Java Code Examples for java.math.BigDecimal#subtract()

The following examples show how to use java.math.BigDecimal#subtract() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SolarEventCalculator.java    From Beauty-Compass with Apache License 2.0 6 votes vote down vote up
/**
 * Computes the suns right ascension, RA in the algorithm, adjusting for the quadrant of L and turning it
 * into degree-hours. Will be in the range [0,360].
 *
 * @param sunTrueLong
 *            Suns true longitude, in <code>BigDecimal</code>
 * @return suns right ascension in degree-hours, in <code>BigDecimal</code> form.
 */
private BigDecimal getRightAscension(BigDecimal sunTrueLong) {
    BigDecimal tanL = new BigDecimal(Math.tan(convertDegreesToRadians(sunTrueLong).doubleValue()));

    BigDecimal innerParens = multiplyBy(convertRadiansToDegrees(tanL), new BigDecimal("0.91764"));
    BigDecimal rightAscension = new BigDecimal(Math.atan(convertDegreesToRadians(innerParens).doubleValue()));
    rightAscension = setScale(convertRadiansToDegrees(rightAscension));

    if (rightAscension.doubleValue() < 0) {
        rightAscension = rightAscension.add(BigDecimal.valueOf(360));
    } else if (rightAscension.doubleValue() > 360) {
        rightAscension = rightAscension.subtract(BigDecimal.valueOf(360));
    }

    BigDecimal ninety = BigDecimal.valueOf(90);
    BigDecimal longitudeQuadrant = sunTrueLong.divide(ninety, 0, RoundingMode.FLOOR);
    longitudeQuadrant = longitudeQuadrant.multiply(ninety);

    BigDecimal rightAscensionQuadrant = rightAscension.divide(ninety, 0, RoundingMode.FLOOR);
    rightAscensionQuadrant = rightAscensionQuadrant.multiply(ninety);

    BigDecimal augend = longitudeQuadrant.subtract(rightAscensionQuadrant);
    return divideBy(rightAscension.add(augend), BigDecimal.valueOf(15));
}
 
Example 2
Source File: AccountServiceTest.java    From Spring-Framework-Essentials with MIT License 6 votes vote down vote up
@Test
public void testTransfer() throws Exception {
    BigDecimal acct1start = service.getBalance(1L);
    BigDecimal acct2start = service.getBalance(2L);

    BigDecimal amount = new BigDecimal("50.0");
    service.transfer(1L, 2L, amount);

    BigDecimal acct1finish = acct1start.subtract(amount);
    BigDecimal acct2finish = acct2start.add(amount);

    assertThat(acct1finish, is(closeTo(service.getBalance(1L),
            new BigDecimal("0.01"))));
    assertThat(acct2finish, is(closeTo(service.getBalance(2L),
            new BigDecimal("0.01"))));
}
 
Example 3
Source File: Calculator.java    From dctb-utfpr-2018-1 with Apache License 2.0 6 votes vote down vote up
public void operation( char operator, BigDecimal leftOperand, BigDecimal rightOperand )
{
    switch ( operator )
    {
        case '+':
            current = leftOperand.add( rightOperand );
            break;
        case '-':
            current = rightOperand.subtract( leftOperand );
            break;
        case '/':
            current = rightOperand.divide(leftOperand);
            break;
        case '*':
            current = leftOperand.multiply( rightOperand );
            break;
        default:
            break;
    }
}
 
Example 4
Source File: ParseFloat.java    From openjdk-8-source with GNU General Public License v2.0 5 votes vote down vote up
/**
 * For each power of two, test at boundaries of
 * region that should convert to that value.
 */
private static void testPowers() {
    for(int i = -149; i <= +127; i++) {
        float f = Math.scalb(1.0f, i);
        BigDecimal f_BD = new BigDecimal(f);

        BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF));
        BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF));

        check(lowerBound.toString());
        check(upperBound.toString());
    }
    check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString());
}
 
Example 5
Source File: TestSuite063.java    From openjdk-systemtest with Apache License 2.0 5 votes vote down vote up
public void testItem_0053()
{
    BigDecimal alpha = new BigDecimal(new BigInteger("1000"), 0);
    BigDecimal beta = new BigDecimal(new BigInteger("70"), 2);
    BigDecimal gamma = alpha.subtract(beta);
    gamma.setScale(2, BigDecimal.ROUND_DOWN);
    Assert.assertEquals("999.30", gamma.toString());
}
 
Example 6
Source File: ParseFloat.java    From openjdk-jdk8u with GNU General Public License v2.0 5 votes vote down vote up
/**
 * For each power of two, test at boundaries of
 * region that should convert to that value.
 */
private static void testPowers() {
    for(int i = -149; i <= +127; i++) {
        float f = Math.scalb(1.0f, i);
        BigDecimal f_BD = new BigDecimal(f);

        BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF));
        BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF));

        check(lowerBound.toString());
        check(upperBound.toString());
    }
    check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString());
}
 
Example 7
Source File: ReservedQtyServiceImpl.java    From axelor-open-suite with GNU Affero General Public License v3.0 5 votes vote down vote up
@Override
public BigDecimal updateRequestedReservedQuantityInStockMoveLines(
    SaleOrderLine saleOrderLine, Product product, BigDecimal newReservedQty)
    throws AxelorException {
  if (product == null || !product.getStockManaged()) {
    return BigDecimal.ZERO;
  }
  List<StockMoveLine> stockMoveLineList = getPlannedStockMoveLines(saleOrderLine);
  BigDecimal deliveredQty = saleOrderLine.getDeliveredQty();
  BigDecimal allocatedRequestedQty = newReservedQty.subtract(deliveredQty);
  if (allocatedRequestedQty.signum() < 0) {
    throw new AxelorException(
        TraceBackRepository.CATEGORY_INCONSISTENCY,
        I18n.get(IExceptionMessage.SALE_ORDER_LINE_REQUESTED_QTY_TOO_LOW));
  }
  for (StockMoveLine stockMoveLine : stockMoveLineList) {
    BigDecimal stockMoveRequestedQty =
        convertUnitWithProduct(
            saleOrderLine.getUnit(), stockMoveLine.getUnit(), allocatedRequestedQty, product);
    BigDecimal requestedQtyInStockMoveLine = stockMoveLine.getQty().min(stockMoveRequestedQty);
    stockMoveLine.setRequestedReservedQty(requestedQtyInStockMoveLine);
    BigDecimal saleOrderRequestedQtyInStockMoveLine =
        convertUnitWithProduct(
            stockMoveLine.getUnit(),
            saleOrderLine.getUnit(),
            requestedQtyInStockMoveLine,
            product);
    allocatedRequestedQty = allocatedRequestedQty.subtract(saleOrderRequestedQtyInStockMoveLine);
  }
  saleOrderLine.setRequestedReservedQty(newReservedQty.subtract(allocatedRequestedQty));
  return saleOrderLine.getRequestedReservedQty().subtract(deliveredQty);
}
 
Example 8
Source File: WithinCaseCoalescent.java    From beast-mcmc with GNU Lesser General Public License v2.1 5 votes vote down vote up
private static double handleDenominatorUnderflow(double input){
    BigDecimal bigDec = new BigDecimal(input);
    BigDecimal expBigDec = BigDecimalUtils.exp(bigDec, bigDec.scale());
    BigDecimal one = new BigDecimal(1.0);
    BigDecimal oneMinusExpBigDec = one.subtract(expBigDec);
    BigDecimal logOneMinusExpBigDec = BigDecimalUtils.ln(oneMinusExpBigDec, oneMinusExpBigDec.scale());
    return logOneMinusExpBigDec.doubleValue();
}
 
Example 9
Source File: YearServiceAccountImpl.java    From axelor-open-suite with GNU Affero General Public License v3.0 5 votes vote down vote up
@Deprecated
public BigDecimal computeReportedBalance2(
    LocalDate fromDate, LocalDate toDate, Partner partner, Account account) {

  MoveLineRepository moveLineRepo = Beans.get(MoveLineRepository.class);

  List<? extends MoveLine> moveLineList =
      moveLineRepo
          .all()
          .filter(
              "self.partner = ?1 AND self.ignoreInAccountingOk = 'false' AND self.date >= ?2 AND self.date <= ?3 AND self.account = ?4",
              partner,
              fromDate,
              toDate,
              account)
          .fetch();

  BigDecimal reportedBalanceAmount = BigDecimal.ZERO;
  for (MoveLine moveLine : moveLineList) {
    if (moveLine.getDebit().compareTo(BigDecimal.ZERO) > 0) {
      reportedBalanceAmount = reportedBalanceAmount.subtract(moveLine.getAmountRemaining());
    } else if (moveLine.getCredit().compareTo(BigDecimal.ZERO) > 0) {
      reportedBalanceAmount = reportedBalanceAmount.add(moveLine.getAmountRemaining());
    }
  }
  if (log.isDebugEnabled()) {
    log.debug("Solde rapporté : {}", reportedBalanceAmount);
  }
  return reportedBalanceAmount;
}
 
Example 10
Source File: BigDecimalUtils.java    From zheshiyigeniubidexiangmu with MIT License 5 votes vote down vote up
/**
 * 减
 */

public static BigDecimal sub(Double v1, Double v2) {
    BigDecimal b1 = new BigDecimal(v1.toString());
    BigDecimal b2 = new BigDecimal(v2.toString());
    return b1.subtract(b2);
}
 
Example 11
Source File: SqlFunctions.java    From calcite with Apache License 2.0 5 votes vote down vote up
public static BigDecimal ceil(BigDecimal b0, BigDecimal b1) {
  final BigDecimal[] bigDecimals = b0.divideAndRemainder(b1);
  BigDecimal r = bigDecimals[1];
  if (r.signum() > 0) {
    r = r.subtract(b1);
  }
  return b0.subtract(r);
}
 
Example 12
Source File: ParseFloat.java    From TencentKona-8 with GNU General Public License v2.0 5 votes vote down vote up
/**
 * For each power of two, test at boundaries of
 * region that should convert to that value.
 */
private static void testPowers() {
    for(int i = -149; i <= +127; i++) {
        float f = Math.scalb(1.0f, i);
        BigDecimal f_BD = new BigDecimal(f);

        BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF));
        BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF));

        check(lowerBound.toString());
        check(upperBound.toString());
    }
    check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString());
}
 
Example 13
Source File: MoneyUtils.java    From yes-cart with Apache License 2.0 5 votes vote down vote up
/**
 * Determine NET (without tax) money amount.
 *
 * @param money to calculate tax.
 * @param taxRate tax rate.
 * @param taxIncluded tax is included in price.
 *
 * @return tax.
 */
public static BigDecimal getNetAmount(final BigDecimal money, final BigDecimal taxRate, final boolean taxIncluded) {
    if (money == null) {
        return Total.ZERO;
    }
    if (taxIncluded) {
        return money.subtract(getTaxAmount(money, taxRate, taxIncluded));
    }
    // taxes excluded so this is net
    return money;
}
 
Example 14
Source File: InvoiceWorker.java    From scipio-erp with Apache License 2.0 5 votes vote down vote up
/**
 * SCIPIO: Gets included tax amount out of Order Adjustments (either from TaxAuthority Services or OrderAdjustment)
 *
 * @param adjustments
 * @return Tax Amount, Zero if there are no adjustments
 * @throws GenericEntityException
 */
public static BigDecimal getTaxAmountIncluded(List<GenericValue> adjustments) throws GenericEntityException {
    BigDecimal taxAmountIncluded = BigDecimal.ZERO;
    for (GenericValue adjustment : adjustments) {
        BigDecimal amountAlreadyIncluded = adjustment.getBigDecimal("amountAlreadyIncluded");
        taxAmountIncluded = taxAmountIncluded.add(amountAlreadyIncluded);
        BigDecimal exemptAmount = adjustment.getBigDecimal("exemptAmount");
        if (exemptAmount != null) {
            taxAmountIncluded = taxAmountIncluded.subtract(exemptAmount);
        }
    }
    return taxAmountIncluded.setScale(2, RoundingMode.HALF_UP);
}
 
Example 15
Source File: ReservedQtyServiceImpl.java    From axelor-open-suite with GNU Affero General Public License v3.0 4 votes vote down vote up
@Override
public void updateRequestedQuantityInFromStockLocation(
    StockMoveLine stockMoveLine,
    StockLocation stockLocation,
    Product product,
    int toStatus,
    BigDecimal requestedReservedQty)
    throws AxelorException {
  if (product == null || !product.getStockManaged()) {
    return;
  }
  Unit stockMoveLineUnit = stockMoveLine.getUnit();

  StockLocationLine stockLocationLine =
      stockLocationLineService.getStockLocationLine(stockLocation, product);
  if (stockLocationLine == null) {
    return;
  }
  Unit stockLocationLineUnit = stockLocationLine.getUnit();
  // the quantity that will be allocated in stock location line
  BigDecimal realReservedQty;

  // the quantity that will be allocated in stock move line
  BigDecimal realReservedStockMoveQty;

  // if we cancel, subtract the quantity using the previously allocated quantity.
  if (toStatus == StockMoveRepository.STATUS_CANCELED
      || toStatus == StockMoveRepository.STATUS_REALIZED) {
    realReservedStockMoveQty = stockMoveLine.getReservedQty();

    // convert the quantity for stock location line

    realReservedQty =
        convertUnitWithProduct(
            stockMoveLineUnit,
            stockLocationLineUnit,
            realReservedStockMoveQty,
            stockMoveLine.getProduct());

    // reallocate quantity in other stock move lines
    if (isReallocatingQtyOnCancel(stockMoveLine)) {
      reallocateQty(stockMoveLine, stockLocation, stockLocationLine, product, realReservedQty);
    }

    // no more reserved qty in stock move and sale order lines
    updateReservedQuantityFromStockMoveLine(
        stockMoveLine, product, stockMoveLine.getReservedQty().negate());

    // update requested quantity in sale order line
    SaleOrderLine saleOrderLine = stockMoveLine.getSaleOrderLine();
    if (saleOrderLine != null) {
      // requested quantity should never be below delivered quantity.
      if (toStatus == StockMoveRepository.STATUS_REALIZED) {
        saleOrderLine.setRequestedReservedQty(
            saleOrderLine.getRequestedReservedQty().max(saleOrderLine.getDeliveredQty()));
      } else if (!saleOrderLine.getIsQtyRequested()) {
        // if we cancel and do not want to request quantity, the requested quantity become the new
        // delivered quantity.
        saleOrderLine.setRequestedReservedQty(saleOrderLine.getDeliveredQty());
      }
    }

  } else {
    BigDecimal requestedReservedQtyInLocation =
        convertUnitWithProduct(
            stockMoveLineUnit, stockLocationLine.getUnit(), requestedReservedQty, product);
    realReservedQty = computeRealReservedQty(stockLocationLine, requestedReservedQtyInLocation);
    // convert back the quantity for the stock move line
    realReservedStockMoveQty =
        convertUnitWithProduct(
            stockLocationLineUnit,
            stockMoveLineUnit,
            realReservedQty,
            stockMoveLine.getProduct());
    updateReservedQuantityFromStockMoveLine(stockMoveLine, product, realReservedStockMoveQty);

    // reallocate quantity in other stock move lines
    if (supplychainConfigService
        .getSupplyChainConfig(stockLocation.getCompany())
        .getAutoAllocateOnAllocation()) {
      BigDecimal availableQuantityInLocation =
          stockLocationLine.getCurrentQty().subtract(stockLocationLine.getReservedQty());
      availableQuantityInLocation =
          convertUnitWithProduct(
              stockLocationLineUnit, stockMoveLineUnit, availableQuantityInLocation, product);
      BigDecimal qtyRemainingToAllocate =
          availableQuantityInLocation.subtract(realReservedStockMoveQty);
      reallocateQty(
          stockMoveLine, stockLocation, stockLocationLine, product, qtyRemainingToAllocate);
    }
  }

  updateReservedQty(stockLocationLine);
  updateRequestedReservedQty(stockLocationLine);
  checkReservedQtyStocks(stockLocationLine, stockMoveLine, toStatus);
}
 
Example 16
Source File: SaleOrderInvoiceServiceImpl.java    From axelor-open-suite with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * Return the remaining amount to invoice for the saleOrder in parameter
 *
 * @param saleOrder
 * @param currentInvoiceId In the case of invoice ventilation or cancellation, the invoice status
 *     isn't modify in database but it will be integrated in calculation For ventilation, the
 *     invoice should be integrated in calculation For cancellation, the invoice shouldn't be
 *     integrated in calculation
 * @param includeInvoice To know if the invoice should be or not integrated in calculation
 */
@Override
public BigDecimal getInvoicedAmount(
    SaleOrder saleOrder, Long currentInvoiceId, boolean excludeCurrentInvoice) {

  BigDecimal invoicedAmount = BigDecimal.ZERO;

  BigDecimal saleAmount =
      this.getAmountVentilated(
          saleOrder,
          currentInvoiceId,
          excludeCurrentInvoice,
          InvoiceRepository.OPERATION_TYPE_CLIENT_SALE);
  BigDecimal refundAmount =
      this.getAmountVentilated(
          saleOrder,
          currentInvoiceId,
          excludeCurrentInvoice,
          InvoiceRepository.OPERATION_TYPE_CLIENT_REFUND);

  if (saleAmount != null) {
    invoicedAmount = invoicedAmount.add(saleAmount);
  }
  if (refundAmount != null) {
    invoicedAmount = invoicedAmount.subtract(refundAmount);
  }

  if (!saleOrder.getCurrency().equals(saleOrder.getCompany().getCurrency())
      && saleOrder.getCompanyExTaxTotal().compareTo(BigDecimal.ZERO) != 0) {
    BigDecimal rate =
        invoicedAmount.divide(saleOrder.getCompanyExTaxTotal(), 4, RoundingMode.HALF_UP);
    invoicedAmount = saleOrder.getExTaxTotal().multiply(rate);
  }

  log.debug(
      "Compute the invoiced amount ({}) of the sale order : {}",
      invoicedAmount,
      saleOrder.getSaleOrderSeq());

  return invoicedAmount;
}
 
Example 17
Source File: PointDecreaseStrategy.java    From hyena with Apache License 2.0 4 votes vote down vote up
@Override
public PointOpResult processPoint(PointUsage usage, PointCache pointCache) {
    PointPo curPoint = pointCache.getPoint();
    log.debug("curPoint = {}", curPoint);

    HyenaAssert.notNull(curPoint.getAvailable(), HyenaConstants.RES_CODE_PARAMETER_ERROR,
            "can't find point to the uid: " + usage.getUid(), Level.WARN);

    if (usage.getRecId() != null && usage.getRecId() > 0L) {
        checkPointRec(usage, pointCache);   // 校验失败会抛出异常
    }

    curPoint.setSeqNum(curPoint.getSeqNum() + 1)
            .setPoint(curPoint.getPoint().subtract(usage.getPoint()))
            .setAvailable(curPoint.getAvailable().subtract(usage.getPoint()))
            .setUsed(curPoint.getUsed().add(usage.getPoint()));
    var point2Update = new PointPo();
    point2Update.setPoint(curPoint.getPoint())
            .setAvailable(curPoint.getAvailable())
            .setUsed(curPoint.getUsed()).setSeqNum(curPoint.getSeqNum())
            .setId(curPoint.getId());

    PointLogPo pointLog = this.pointBuilder.buildPointLog(PointOpType.DECREASE, usage, curPoint);

    BigDecimal gap = usage.getPoint();
    BigDecimal cost = DecimalUtils.ZERO;
    List<PointRecLogDto> recLogs = new ArrayList<>();

    var recLogsRet = this.decreasePointLoop(usage.getType(),
            pointCache,
            pointLog, gap, usage.getRecId());
    gap = gap.subtract(recLogsRet.getDelta());
    cost = cost.add(recLogsRet.getDeltaCost());
    recLogs.addAll(recLogsRet.getRecLogs());
    log.debug("gap = {}", gap);

    if (DecimalUtils.gt(cost, DecimalUtils.ZERO)) {
        pointLog.setDeltaCost(cost).setCost(pointLog.getCost().subtract(cost));
        curPoint.setCost(curPoint.getCost().subtract(cost));
        point2Update.setCost(curPoint.getCost());
    }

    pointFlowService.updatePoint(usage.getType(), point2Update);
    pointFlowService.updatePointRec(usage.getType(), recLogsRet.getRecList4Update());
    pointFlowService.addFlow(usage, pointLog, recLogs);
    //return curPoint;
    PointOpResult ret = new PointOpResult();
    BeanUtils.copyProperties(curPoint, ret);
    ret.setOpPoint(recLogsRet.getDelta())
            .setOpCost(recLogsRet.getDeltaCost())
            .setRecLogList(recLogs)
            .setLogs(List.of(PointLogDto.build(pointLog)));
    return ret;
}
 
Example 18
Source File: JulianDateStamp.java    From common-utils with GNU General Public License v2.0 4 votes vote down vote up
public JulianDateStamp(BigDecimal bd) {
    double d = bd.doubleValue();
    integer = (int) d;
    bd = bd.subtract(new BigDecimal(integer));
    fraction = bd.doubleValue();
}
 
Example 19
Source File: TemplateAdviser.java    From cryptotrader with GNU Affero General Public License v3.0 2 votes vote down vote up
@VisibleForTesting
BigDecimal calculateBuyBoundaryPrice(Context context, Request request, BigDecimal basis) {

    Key key = Key.from(request);

    BigDecimal ask0 = context.getBestAskPrice(key);

    if (ask0 == null) {
        return null;
    }

    BigDecimal ask1 = ask0.subtract(EPSILON);

    BigDecimal recent = ofNullable(calculateRecentPrice(context, request, SIGNUM_SELL, request.getAversionProducts()))
            .map(r -> r.multiply(ONE.subtract(trimToZero(basis)))).orElse(ask0);

    BigDecimal bid0 = trim(context.getBestBidPrice(key), ask0);

    BigDecimal bid1 = bid0;

    if (trimToEmpty(context.listActiveOrders(key)).stream()
            .filter(Objects::nonNull)
            .filter(o -> o.getOrderQuantity() != null)
            .filter(o -> o.getOrderQuantity().signum() == SIGNUM_BUY)
            .filter(o -> o.getOrderPrice() != null)
            .filter(o -> o.getOrderPrice().compareTo(bid0) == 0)
            .count() == 0) {

        bid1 = ofNullable(context.roundTickSize(key, bid0.add(EPSILON), UP)).orElse(bid0);

    }

    BigDecimal price = ask1.min(bid1).min(recent);

    BigDecimal result = adjustBuyBoundaryPrice(context, request, context.roundTickSize(key, price, DOWN));

    log.trace("Buy boundary : {} (Ask=[{}] Bid=[{}] Recent=[{}])", result, ask0, bid1, recent);

    return result;

}
 
Example 20
Source File: DurationImpl.java    From openjdk-jdk8u with GNU General Public License v2.0 2 votes vote down vote up
/**
 * Computes a new duration whose value is <code>factor</code> times
 * longer than the value of this duration.
 *
 * <p>
 * For example,
 * <pre>
 * "P1M" (1 month) * "12" = "P12M" (12 months)
 * "PT1M" (1 min) * "0.3" = "PT18S" (18 seconds)
 * "P1M" (1 month) * "1.5" = IllegalStateException
 * </pre>
 *
 * <p>
 * Since the {@link Duration} class is immutable, this method
 * doesn't change the value of this object. It simply computes
 * a new Duration object and returns it.
 *
 * <p>
 * The operation will be performed field by field with the precision
 * of {@link BigDecimal}. Since all the fields except seconds are
 * restricted to hold integers,
 * any fraction produced by the computation will be
 * carried down toward the next lower unit. For example,
 * if you multiply "P1D" (1 day) with "0.5", then it will be 0.5 day,
 * which will be carried down to "PT12H" (12 hours).
 * When fractions of month cannot be meaningfully carried down
 * to days, or year to months, this will cause an
 * {@link IllegalStateException} to be thrown.
 * For example if you multiple one month by 0.5.</p>
 *
 * <p>
 * To avoid {@link IllegalStateException}, use
 * the {@link #normalizeWith(Calendar)} method to remove the years
 * and months fields.
 *
 * @param factor to multiply by
 *
 * @return
 *      returns a non-null valid {@link Duration} object
 *
 * @throws IllegalStateException if operation produces fraction in
 * the months field.
 *
 * @throws NullPointerException if the <code>factor</code> parameter is
 * <code>null</code>.
 *
 */
public Duration multiply(BigDecimal factor) {
    BigDecimal carry = ZERO;
    int factorSign = factor.signum();
    factor = factor.abs();

    BigDecimal[] buf = new BigDecimal[6];

    for (int i = 0; i < 5; i++) {
        BigDecimal bd = getFieldAsBigDecimal(FIELDS[i]);
        bd = bd.multiply(factor).add(carry);

        buf[i] = bd.setScale(0, BigDecimal.ROUND_DOWN);

        bd = bd.subtract(buf[i]);
        if (i == 1) {
            if (bd.signum() != 0) {
                throw new IllegalStateException(); // illegal carry-down
            } else {
                carry = ZERO;
            }
        } else {
            carry = bd.multiply(FACTORS[i]);
        }
    }

    if (seconds != null) {
        buf[5] = seconds.multiply(factor).add(carry);
    } else {
        buf[5] = carry;
    }

    return new DurationImpl(
        this.signum * factorSign >= 0,
        toBigInteger(buf[0], null == years),
        toBigInteger(buf[1], null == months),
        toBigInteger(buf[2], null == days),
        toBigInteger(buf[3], null == hours),
        toBigInteger(buf[4], null == minutes),
        (buf[5].signum() == 0 && seconds == null) ? null : buf[5]);
}