BigDecimalIntervalNumber.java

/*
 * Created on 2017/01/24
 * Copyright (C) 2017 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.cga.interval.scalar;

import java.math.BigDecimal;
import java.math.RoundingMode;

import org.mklab.nfc.util.RoundMode;
import org.mklab.nfc.util.RoundModeManager;

/**
 * @author kenbishi
 * @version $Revision$, 2017/03/01
 */
public class BigDecimalIntervalNumber {

  /** 区間の下限 */
  private BigDecimal infimum;

  /** 区間の上限 */
  private BigDecimal supremum;

  /** 区間の中心 */
  private BigDecimal middle;

  /** 区間の半径 */
  private BigDecimal radius;

  /**
   * コンストラクタ。 入力値を下限と上限に設定します。
   * 
   * @param value 浮動小数点数
   */
  public BigDecimalIntervalNumber(final String value) {
    this.infimum = new BigDecimal(value);
    this.supremum = new BigDecimal(value);
    this.middle = mid();
    this.radius = rad();
  }

  /**
   * コンストラクタ。 下限と上限を設定します。
   * 
   * @param inf 下限
   * @param sup 上限
   */
  public BigDecimalIntervalNumber(final String inf, final String sup) {
    this.infimum = new BigDecimal(inf);
    this.supremum = new BigDecimal(sup);
    this.middle = mid();
    this.radius = rad();
  }

  /**
   * 数値入力による誤差を考慮して区間を作ります。
   * 
   * @param value 点
   * @param flag フラグ
   */
  /*public BigDecimalCreateIntervalNumber(final double value, final boolean flag) {
    if (flag) {
      this.infimum = Math.nextAfter(value, Double.NEGATIVE_INFINITY);
      this.supremum = Math.nextUp(value);
    } else {
      this.infimum = value;
      this.supremum = value;
    }
    this.middle = mid();
    this.radius = rad();
  }*/

  /**
   * 数値入力による誤差を考慮して区間を作ります。
   * 
   * @param inf 下限
   * @param sup 上限
   */
  /*public BigDecimalCreateIntervalNumber(final double inf, final double sup, final boolean flag) {
    if (flag) {
      this.infimum = Math.nextAfter(inf, Double.NEGATIVE_INFINITY);
      this.supremum = Math.nextUp(sup);
    } else {
      this.infimum = inf;
      this.supremum = sup;
    }
    this.middle = mid();
    this.radius = rad();
  }*/
  public BigDecimalIntervalNumber(BigDecimal inf, BigDecimal sup) {
    this.infimum = inf;
    this.supremum = sup;
    this.middle = mid();
    this.radius = rad();
  }

  /**
   * 区間の中心を求めます。
   * 
   * @return 区間の中心。
   */
  private BigDecimal mid() {
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal mid = this.infimum.add((this.supremum.subtract(this.infimum)).divide(new BigDecimal(2)));

    manager.setRoundMode(oldRoundMode);
    return mid;
  }

  /**
   * 区間の半径を求めます。
   * 
   * @return 区間の半径。
   */
  private BigDecimal rad() {
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal mid = mid();
    BigDecimal rad = mid.subtract(this.infimum);

    manager.setRoundMode(oldRoundMode);
    return rad;
  }
  
  /*public DoubleNumber getError() {
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal ans = this.supremum.subtract(this.infimum);

    manager.setRoundMode(oldRoundMode);
    return new DoubleNumber(ans);
  }*/

  /**
   * @see org.mklab.cga.interval.scalar.IntervalScalar#printInfSup()
   */
  public void printInfSup() {
    printInfSup("ans"); //$NON-NLS-1$
  }

  /**
   * @param name input
   * @see org.mklab.cga.interval.scalar.IntervalScalar#printInfSup(java.lang.String)
   */
  public void printInfSup(final String name) {
    System.out.println(name + Messages.getString("RealInterval.0") + this.infimum); //$NON-NLS-1$
    System.out.println(name + Messages.getString("RealInterval.1") + this.supremum); //$NON-NLS-1$
    System.out.println();
  }

  /**
   * 自身と実数区間との和を求めます。
   * 
   * @param interval 実数区間
   * @return 和。
   */
  public BigDecimalIntervalNumber add(final BigDecimalIntervalNumber interval) {// IR+IR
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = this.infimum.add(interval.getInfimum());

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = this.supremum.add(interval.getSupremum());

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }

  /**
   * @param d BigDecimalNumber
   * @return BigDecimalIntervalNumber
   * 
   */
  public BigDecimalIntervalNumber add(final BigDecimal d) {// IR+R
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = this.infimum.add(d);

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = this.supremum.add(d);

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }

  /**
   * 自身と実数区間との差を求めます。
   * 
   * @param interval 実数区間
   * @return 差。
   */
  public BigDecimalIntervalNumber subtract(final BigDecimalIntervalNumber interval) {// IR-IR
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = this.infimum.subtract(interval.getSupremum());

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = this.supremum.subtract(interval.getInfimum());

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }

  /**
   * @param d BigDecimalNumber
   * @return BigDecimalIntervalNumber
   * @see org.mklab.nfc.scalar.Scalar#subtract(double)
   */
  public BigDecimalIntervalNumber subtract(final BigDecimal d) {
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = this.infimum.subtract(d);

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = this.supremum.subtract(d);

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }
  
  /**
   * 自身と実数区間との積を求めます。
   * 
   * @param interval 実数区間
   * @return 積
   */
  public BigDecimalIntervalNumber multiply(final BigDecimalIntervalNumber interval) {// IR*IR
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    BigDecimal xi = this.infimum;
    BigDecimal xs = this.supremum;

    BigDecimal yi = interval.getInfimum();
    BigDecimal ys = interval.getSupremum();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = min(xi.multiply(yi), xi.multiply(ys));
    inf = min(inf, xs.multiply(yi));
    inf = min(inf, xs.multiply(ys));

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = max(xi.multiply(yi), xi.multiply(ys));
    sup = max(sup, xs.multiply(yi));
    sup = max(sup, xs.multiply(ys));

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }
  
  /**
   * @param d BigDecimalNumber
   * @return BigDecimalIntervalNumber
   * @see org.mklab.nfc.scalar.Scalar#multiply(double)
   */
  public BigDecimalIntervalNumber multiply(final BigDecimal d) {// IR*R
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    BigDecimal xi = this.infimum;
    BigDecimal xs = this.supremum;

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = min(xi.multiply(d), xi.multiply(d));
    inf = min(inf, xs.multiply(d));
    inf = min(inf, xs.multiply(d));

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = max(xi.multiply(d), xi.multiply(d));
    sup = max(sup, xs.multiply(d));
    sup = max(sup, xs.multiply(d));

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }
  
  /**
   * 自身と実数区間との商を求めます。
   * 
   * @param y 実数区間
   * @return 商
   */
  public BigDecimalIntervalNumber divide(final BigDecimalIntervalNumber y) {// IR/IR
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    final BigDecimal yi = y.getInfimum();
    final BigDecimal ys = y.getSupremum();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = min(this.infimum .divide(yi), this.infimum.divide(ys));
    inf = min(inf, this.supremum.divide(yi));
    inf = min(inf, this.supremum.divide(ys));

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = max(this.infimum.divide(yi), this.infimum.divide(ys));
    sup = max(sup, this.supremum.divide(yi));
    sup = max(sup, this.supremum.divide(ys));

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }
  
  /**
   * 自身と実数区間との商を求めます。
   * 
   * @param y 実数区間
   * @param scala 精度
   * @return 商
   */
  public BigDecimalIntervalNumber divide(final BigDecimalIntervalNumber y, final int scala) {// IR/IR
    RoundModeManager manager = RoundModeManager.getManager();
    RoundMode oldRoundMode = manager.getRoundMode();

    final BigDecimal yi = y.getInfimum();
    final BigDecimal ys = y.getSupremum();

    manager.setRoundMode(RoundMode.ROUND_DOWN);
    BigDecimal inf = min(this.infimum .divide(yi, yi.scale()+scala, RoundingMode.HALF_UP), this.infimum.divide(ys, ys.scale()+scala, RoundingMode.HALF_UP));
    inf = min(inf, this.supremum.divide(yi, yi.scale()+scala, RoundingMode.HALF_UP));
    inf = min(inf, this.supremum.divide(ys, ys.scale()+scala, RoundingMode.HALF_UP));

    manager.setRoundMode(RoundMode.ROUND_UP);
    BigDecimal sup = max(this.infimum.divide(yi, yi.scale()+scala, RoundingMode.HALF_UP), this.infimum.divide(ys, ys.scale()+scala, RoundingMode.HALF_UP));
    sup = max(sup, this.supremum.divide(yi, yi.scale()+scala, RoundingMode.HALF_UP));
    sup = max(sup, this.supremum.divide(ys, ys.scale()+scala, RoundingMode.HALF_UP));

    manager.setRoundMode(oldRoundMode);
    return new BigDecimalIntervalNumber(inf, sup);
  }
  
  /**
   * @param a BigDecimalNumber
   * @param b BigDecimalNumber
   * @return BigDecimal
   */
  public static BigDecimal min(BigDecimal a, BigDecimal b) {
    return (a.doubleValue() <= b.doubleValue()) ? a : b;
  }
  
  /**
   * @param a BigDecimalNumber
   * @param b BigDecimalNumber
   * @return BigDecimal
   */
  public static BigDecimal max(BigDecimal a, BigDecimal b) {
    return (a.doubleValue() >= b.doubleValue()) ? a : b;
}
  
  /**
   * @return BigDecimalNumber
   * @see org.mklab.cga.interval.scalar.IntervalScalar#getInfimum()
   */
  public BigDecimal getInfimum() {
    return this.infimum;
  }

  /**
   * @return BigDecimalNumber
   * @see org.mklab.cga.interval.scalar.IntervalScalar#getMiddle()
   */
  public BigDecimal getMiddle() {
    return this.middle;
  }

  /**
   * @return BigDecimalNumber
   */
  public BigDecimal getRadius() {
    return this.radius;
  }

  /**
   * @return BigDecimalNumber
   * @see org.mklab.cga.interval.scalar.IntervalScalar#getSupremum()
   */
  public BigDecimal getSupremum() {
    return this.supremum;
}
  /**
   * @param scalar power
   * @return BigDecimalIntervalNumber
   */
  public BigDecimalIntervalNumber power(int scalar) {
    BigDecimalIntervalNumber value = this.create("1"); //$NON-NLS-1$
    if (scalar == 0) {
      return value;
    }
    for (int i = 0; i < scalar; i++) {
      value = value.multiply(this);
    }
    return value;
  }

  /**
   * @param value String
   * @return BigDecimalIntervalNumber
   */
  public BigDecimalIntervalNumber create(final String value) {
    return new BigDecimalIntervalNumber(value);
  }
  
}