private void test(
      final BigDecimal valueToSplit, final int childrenCount, final MathContext mathContext) {
    final String info =
        "ValueToSplit="
            + valueToSplit
            + ", childrenCount="
            + childrenCount
            + ", mathContext="
            + mathContext;
    final int precision = mathContext.getPrecision();

    final BigDecimal splitValueExpected =
        valueToSplit.divide(BigDecimal.valueOf(childrenCount), mathContext);

    // Delta: tolerated difference between calculated split value and actual split value
    // i.e. 1^(-precision) = 1/(1^precision) ... because BigDecimal does not support negative powers
    final BigDecimal splitValueExpectedDelta = BigDecimal.ONE.divide(BigDecimal.ONE.pow(precision));

    // Example: For ValueToSplit=100, childrenCount=33, precision=2
    // splitValueExpected = 0.03
    // splitValueExpectedDelta = 1^(-2) = 0.01

    final MutableAttributeSplitRequest request = createRequest(valueToSplit, childrenCount);

    BigDecimal splitValueSUM = BigDecimal.ZERO;
    for (int index = 0; index < childrenCount; index++) {
      //
      // Update request current index
      request.setAttributeStorageCurrent(request.getAttributeStorages().get(index));
      request.setAttributeStorageCurrentIndex(index);

      //
      // Execute splitting
      final IAttributeSplitResult result = splitter.split(request);

      //
      // Get and check split value
      final BigDecimal splitValue = (BigDecimal) result.getSplitValue();
      assertEquals(
          "Invalid split value on index=" + index + "\n" + info,
          splitValueExpected, // expected
          splitValue, // actual
          splitValueExpectedDelta // delta
          );

      //
      // Update SUM of split values
      splitValueSUM = splitValueSUM.add(splitValue);

      //
      // Update: request's ValueToSplit = last split remaining value
      final BigDecimal remainingValue = (BigDecimal) result.getRemainingValue();
      final BigDecimal remainingValueExpected = valueToSplit.subtract(splitValueSUM);
      assertEquals(
          "Invalid remaining value on index=" + index + "\n" + info,
          remainingValueExpected, // expected
          remainingValue, // actual
          BigDecimal.ZERO // delta=exact
          );
      request.setValueToSplit(remainingValue);
    }

    //
    // Assume: sum of all split values shall be the same as initial value to split
    Assert.assertThat(
        "Invalid splitValues SUM" + "\n" + info,
        splitValueSUM, // actual
        Matchers.comparesEqualTo(valueToSplit) // expected
        );
  }
Ejemplo n.º 2
0
  public static BigDecimal sqrt(BigDecimal squarD, int scalar) {
    // Static constants - perhaps initialize in class Vladimir!
    BigDecimal TWO = new BigDecimal(2);
    double SQRT_10 = 3.162277660168379332;
    MathContext rootMC = new MathContext(scalar);

    // General number and precision checking
    int sign = squarD.signum();
    if (sign == -1) throw new ArithmeticException("\nSquare root of a negative number: " + squarD);
    else if (sign == 0) return squarD.round(rootMC);

    int prec = rootMC.getPrecision(); // the requested precision
    if (prec == 0)
      throw new IllegalArgumentException("\nMost roots won't have infinite precision = 0");

    // Initial precision is that of double numbers 2^63/2 ~ 4E18
    int BITS = 62; // 63-1 an even number of number bits
    int nInit = 16; // precision seems 16 to 18 digits
    MathContext nMC = new MathContext(18, RoundingMode.HALF_UP);

    // Iteration variables, for the square root x and the reciprocal v
    BigDecimal x = null, e = null; // initial x: x0 ~ sqrt()
    BigDecimal v = null, g = null; // initial v: v0 = 1/(2*x)

    // Estimate the square root with the foremost 62 bits of squarD
    BigInteger bi = squarD.unscaledValue(); // bi and scale are a tandem
    int biLen = bi.bitLength();
    int shift = Math.max(0, biLen - BITS + (biLen % 2 == 0 ? 0 : 1)); // even
    // shift..
    bi = bi.shiftRight(shift); // ..floors to 62 or 63 bit BigInteger

    double root = Math.sqrt(bi.doubleValue());
    BigDecimal halfBack = new BigDecimal(BigInteger.ONE.shiftLeft(shift / 2));

    int scale = squarD.scale();
    if (scale % 2 == 1) // add half scales of the root to odds..
    root *= SQRT_10; // 5 -> 2, -5 -> -3 need half a scale more..
    scale = (int) Math.floor(scale / 2.); // ..where 100 -> 10 shifts the
    // scale

    // Initial x - use double root - multiply by halfBack to unshift - set
    // new scale
    x = new BigDecimal(root, nMC);
    x = x.multiply(halfBack, nMC); // x0 ~ sqrt()
    if (scale != 0) x = x.movePointLeft(scale);

    if (prec < nInit) // for prec 15 root x0 must surely be OK
    return x.round(rootMC); // return small prec roots without
    // iterations

    // Initial v - the reciprocal
    v = BigDecimal.ONE.divide(TWO.multiply(x), nMC); // v0 = 1/(2*x)

    // Collect iteration precisions beforehand
    ArrayList<Integer> nPrecs = new ArrayList<Integer>();

    assert nInit > 3 : "Never ending loop!"; // assume nInit = 16 <= prec

    // Let m be the exact digits precision in an earlier! loop
    for (int m = prec + 1; m > nInit; m = m / 2 + (m > 100 ? 1 : 2)) nPrecs.add(m);

    // The loop of "Square Root by Coupled Newton Iteration" for simpletons
    for (int i = nPrecs.size() - 1; i > -1; i--) {
      // Increase precision - next iteration supplies n exact digits
      nMC =
          new MathContext(
              nPrecs.get(i), (i % 2 == 1) ? RoundingMode.HALF_UP : RoundingMode.HALF_DOWN);

      // Next x // e = d - x^2
      e = squarD.subtract(x.multiply(x, nMC), nMC);
      if (i != 0) x = x.add(e.multiply(v, nMC)); // x += e*v ~ sqrt()
      else {
        x = x.add(e.multiply(v, rootMC), rootMC); // root x is ready!
        break;
      }

      // Next v // g = 1 - 2*x*v
      g = BigDecimal.ONE.subtract(TWO.multiply(x).multiply(v, nMC));

      v = v.add(g.multiply(v, nMC)); // v += g*v ~ 1/2/sqrt()
    }

    return x; // return sqrt(squarD) with precision of rootMC
  }