1 /*
   2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4851777 8233452
  27  * @summary Tests of BigDecimal.sqrt().
  28  */
  29 
  30 import java.math.*;
  31 import java.util.*;
  32 
  33 import static java.math.BigDecimal.ONE;
  34 import static java.math.BigDecimal.TEN;
  35 import static java.math.BigDecimal.ZERO;
  36 import static java.math.BigDecimal.valueOf;
  37 
  38 public class SquareRootTests {
  39     private static BigDecimal TWO = new BigDecimal(2);
  40 
  41     /**
  42      * The value 0.1, with a scale of 1.
  43      */
  44     private static final BigDecimal ONE_TENTH = valueOf(1L, 1);
  45 
  46     public static void main(String... args) {
  47         int failures = 0;
  48 
  49         failures += negativeTests();
  50         failures += zeroTests();
  51         failures += oneDigitTests();
  52         failures += evenPowersOfTenTests();
  53         failures += squareRootTwoTests();
  54         failures += lowPrecisionPerfectSquares();
  55         failures += almostFourRoundingDown();
  56         failures += almostFourRoundingUp();
  57         failures += nearTen();
  58         failures += nearOne();
  59         failures += halfWay();
  60 
  61         if (failures > 0 ) {
  62             throw new RuntimeException("Incurred " + failures + " failures" +
  63                                        " testing BigDecimal.sqrt().");
  64         }
  65     }
  66 
  67     private static int negativeTests() {
  68         int failures = 0;
  69 
  70         for (long i = -10; i < 0; i++) {
  71             for (int j = -5; j < 5; j++) {
  72                 try {
  73                     BigDecimal input = BigDecimal.valueOf(i, j);
  74                     BigDecimal result = input.sqrt(MathContext.DECIMAL64);
  75                     System.err.println("Unexpected sqrt of negative: (" +
  76                                        input + ").sqrt()  = " + result );
  77                     failures += 1;
  78                 } catch (ArithmeticException e) {
  79                     ; // Expected
  80                 }
  81             }
  82         }
  83 
  84         return failures;
  85     }
  86 
  87     private static int zeroTests() {
  88         int failures = 0;
  89 
  90         for (int i = -100; i < 100; i++) {
  91             BigDecimal expected = BigDecimal.valueOf(0L, i/2);
  92             // These results are independent of rounding mode
  93             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.UNLIMITED),
  94                                 expected, true, "zeros");
  95 
  96             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.DECIMAL64),
  97                                 expected, true, "zeros");
  98         }
  99 
 100         return failures;
 101     }
 102 
 103     /**
 104      * Probe inputs with one digit of precision, 1 .... 9 and those
 105      * values scaled by 10^-1.
 106      */
 107     private static int oneDigitTests() {
 108         int failures = 0;
 109 
 110         List<BigDecimal> oneToNine =
 111             List.of(ONE,        valueOf(2), valueOf(3),
 112                     valueOf(4), valueOf(5), valueOf(6),
 113                     valueOf(7), valueOf(8), valueOf(9));
 114 
 115         List<RoundingMode> modes =
 116             List.of(RoundingMode.UP,       RoundingMode.DOWN,
 117                     RoundingMode.CEILING, RoundingMode.FLOOR,
 118                     RoundingMode.HALF_UP, RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN);
 119 
 120         for (int i = 1; i < 20; i++) {
 121             for (RoundingMode rm : modes) {
 122                 for (BigDecimal bd  : oneToNine) {
 123                     MathContext mc = new MathContext(i, rm);
 124 
 125                     failures += equalNumerically(BigSquareRoot.sqrt(bd, mc),
 126                                                  bd.sqrt(mc), "sqrt(" + bd + ") under " + mc);
 127                     bd = bd.multiply(ONE_TENTH);
 128                     failures += equalNumerically(BigSquareRoot.sqrt(bd, mc),
 129                                                  bd.sqrt(mc), "sqrt(" + bd + ") under " + mc);
 130                 }
 131             }
 132         }
 133 
 134         return failures;
 135     }
 136 
 137     /**
 138      * sqrt(10^2N) is 10^N
 139      * Both numerical value and representation should be verified
 140      */
 141     private static int evenPowersOfTenTests() {
 142         int failures = 0;
 143         MathContext oneDigitExactly = new MathContext(1, RoundingMode.UNNECESSARY);
 144 
 145         for (int scale = -100; scale <= 100; scale++) {
 146             BigDecimal testValue       = BigDecimal.valueOf(1, 2*scale);
 147             BigDecimal expectedNumericalResult = BigDecimal.valueOf(1, scale);
 148 
 149             BigDecimal result;
 150 
 151             failures += equalNumerically(expectedNumericalResult,
 152                                            result = testValue.sqrt(MathContext.DECIMAL64),
 153                                            "Even powers of 10, DECIMAL64");
 154 
 155             // Can round to one digit of precision exactly
 156             failures += equalNumerically(expectedNumericalResult,
 157                                            result = testValue.sqrt(oneDigitExactly),
 158                                            "even powers of 10, 1 digit");
 159             if (result.precision() > 1) {
 160                 failures += 1;
 161                 System.err.println("Excess precision for " + result);
 162             }
 163 
 164             // If rounding to more than one digit, do precision / scale checking...
 165         }
 166 
 167         return failures;
 168     }
 169 
 170     private static int squareRootTwoTests() {
 171         int failures = 0;
 172 
 173         // Square root of 2 truncated to 65 digits
 174         BigDecimal highPrecisionRoot2 =
 175             new BigDecimal("1.41421356237309504880168872420969807856967187537694807317667973799");
 176 
 177         RoundingMode[] modes = {
 178             RoundingMode.UP,       RoundingMode.DOWN,
 179             RoundingMode.CEILING, RoundingMode.FLOOR,
 180             RoundingMode.HALF_UP, RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN
 181         };
 182 
 183 
 184         // For each interesting rounding mode, for precisions 1 to, say,
 185         // 63 numerically compare TWO.sqrt(mc) to
 186         // highPrecisionRoot2.round(mc) and the alternative internal high-precision
 187         // implementation of square root.
 188         for (RoundingMode mode : modes) {
 189             for (int precision = 1; precision < 63; precision++) {
 190                 MathContext mc = new MathContext(precision, mode);
 191                 BigDecimal expected = highPrecisionRoot2.round(mc);
 192                 BigDecimal computed = TWO.sqrt(mc);
 193                 BigDecimal altComputed = BigSquareRoot.sqrt(TWO, mc);
 194 
 195                 failures += equalNumerically(expected, computed, "sqrt(2)");
 196                 failures += equalNumerically(computed, altComputed, "computed & altComputed");
 197             }
 198         }
 199 
 200         return failures;
 201     }
 202 
 203     private static int lowPrecisionPerfectSquares() {
 204         int failures = 0;
 205 
 206         // For 5^2 through 9^2, if the input is rounded to one digit
 207         // first before the root is computed, the wrong answer will
 208         // result. Verify results and scale for different rounding
 209         // modes and precisions.
 210         long[][] squaresWithOneDigitRoot = {{ 4, 2},
 211                                             { 9, 3},
 212                                             {25, 5},
 213                                             {36, 6},
 214                                             {49, 7},
 215                                             {64, 8},
 216                                             {81, 9}};
 217 
 218         for (long[] squareAndRoot : squaresWithOneDigitRoot) {
 219             BigDecimal square     = new BigDecimal(squareAndRoot[0]);
 220             BigDecimal expected   = new BigDecimal(squareAndRoot[1]);
 221 
 222             for (int scale = 0; scale <= 4; scale++) {
 223                 BigDecimal scaledSquare = square.setScale(scale, RoundingMode.UNNECESSARY);
 224                 int expectedScale = scale/2;
 225                 for (int precision = 0; precision <= 5; precision++) {
 226                     for (RoundingMode rm : RoundingMode.values()) {
 227                         MathContext mc = new MathContext(precision, rm);
 228                         BigDecimal computedRoot = scaledSquare.sqrt(mc);
 229                         failures += equalNumerically(expected, computedRoot, "simple squares");
 230                         int computedScale = computedRoot.scale();
 231                         if (precision >=  expectedScale + 1 &&
 232                             computedScale != expectedScale) {
 233                             System.err.printf("%s\tprecision=%d\trm=%s%n",
 234                                               computedRoot.toString(), precision, rm);
 235                             failures++;
 236                             System.err.printf("\t%s does not have expected scale of %d%n.",
 237                                               computedRoot, expectedScale);
 238                         }
 239                     }
 240                 }
 241             }
 242         }
 243 
 244         return failures;
 245     }
 246 
 247     /**
 248      * Test around 3.9999 that the result doesn't not improperly
 249      * round-up to a numerical value of 2.
 250      */
 251     private static int almostFourRoundingDown() {
 252         int failures = 0;
 253         BigDecimal nearFour = new BigDecimal("3.999999999999999999999999999999");
 254 
 255         // Sqrt root is 1.9999...
 256         
 257         for (int i = 1; i < 64; i++) {
 258             MathContext mc = new MathContext(i, RoundingMode.FLOOR);
 259             BigDecimal result = nearFour.sqrt(mc);
 260             BigDecimal expected = BigSquareRoot.sqrt(nearFour, mc);
 261             failures += equalNumerically(expected, result, "near four rounding down");
 262             failures += (result.compareTo(TWO) < 0) ? 0  : 1 ;
 263         }
 264 
 265         return failures;
 266     }
 267 
 268     /**
 269      * Test around 4.000...1 that the result doesn't not improperly
 270      * round-down to a numerical value of 2.
 271      */
 272     private static int almostFourRoundingUp() {
 273         int failures = 0;
 274         BigDecimal nearFour = new BigDecimal("4.000000000000000000000000000001");
 275 
 276         // Sqrt root is 2.0000....<non-zero digits>
 277         
 278         for (int i = 1; i < 64; i++) {
 279             MathContext mc = new MathContext(i, RoundingMode.CEILING);
 280             BigDecimal result = nearFour.sqrt(mc);
 281             BigDecimal expected = BigSquareRoot.sqrt(nearFour, mc);
 282             failures += equalNumerically(expected, result, "near four rounding down");
 283             failures += (result.compareTo(TWO) > 0) ? 0  : 1 ;
 284         }
 285 
 286         return failures;
 287     }
 288 
 289     private static int nearTen() {
 290         int failures = 0;
 291 
 292          BigDecimal near10 = new BigDecimal("9.99999999999999999999");
 293 
 294          BigDecimal near10sq = near10.multiply(near10);
 295 
 296          BigDecimal near10sq_ulp = near10sq.add(near10sq.ulp());
 297 
 298         for (int i = 10; i < 23; i++) {
 299             MathContext mc = new MathContext(i, RoundingMode.HALF_EVEN);
 300 
 301             failures += equalNumerically(BigSquareRoot.sqrt(near10sq_ulp, mc),
 302                                          near10sq_ulp.sqrt(mc),
 303                                          "near 10 rounding down");
 304         }
 305 
 306         return failures;
 307     }
 308 
 309 
 310     /*
 311      * Probe for rounding failures near a power of ten, 1 = 10^0,
 312      * where an ulp has a different size above and below the value.
 313      */
 314     private static int nearOne() {
 315         int failures = 0;
 316 
 317          BigDecimal near1 = new BigDecimal(".999999999999999999999");
 318          BigDecimal near1sq = near1.multiply(near1);
 319          BigDecimal near1sq_ulp = near1sq.add(near1sq.ulp());
 320 
 321          for (int i = 10; i < 23; i++) {
 322              for (RoundingMode rm : List.of(RoundingMode.HALF_EVEN,
 323                                             RoundingMode.UP,
 324                                             RoundingMode.DOWN )) {
 325                  MathContext mc = new MathContext(i, rm);
 326                  failures += equalNumerically(BigSquareRoot.sqrt(near1sq_ulp, mc),
 327                                               near1sq_ulp.sqrt(mc),
 328                                               "near 1 half even");
 329              }
 330          }
 331 
 332          return failures;
 333     }
 334 
 335 
 336     private static int halfWay() {
 337         int failures = 0;
 338 
 339         /*
 340          * Use enough digits that the exact result cannot be computed
 341          * from the sqrt of a double.
 342          */
 343         BigDecimal[] halfWayCases = {
 344             // Odd next digit, truncate on HALF_EVEN
 345             new BigDecimal("123456789123456789.5"),
 346 
 347              // Even next digit, round up on HALF_EVEN
 348             new BigDecimal("123456789123456788.5"),
 349         };
 350 
 351         for (BigDecimal halfWayCase : halfWayCases) {
 352             // Round result to next-to-last place
 353             int precision = halfWayCase.precision() - 1;
 354             BigDecimal square = halfWayCase.multiply(halfWayCase);
 355 
 356             for (RoundingMode rm : List.of(RoundingMode.HALF_EVEN,
 357                                            RoundingMode.HALF_UP,
 358                                            RoundingMode.HALF_DOWN)) {
 359                 MathContext mc = new MathContext(precision, rm);
 360 
 361                 System.out.println("\nRounding mode " + rm);
 362                 System.out.println("\t" + halfWayCase.round(mc) + "\t" + halfWayCase);
 363                 /*System.out.println("\t" + square.sqrt(mc));*/
 364                 System.out.println("\t" + BigSquareRoot.sqrt(square, mc));
 365 
 366                 failures += equalNumerically(/*square.sqrt(mc),*/
 367                                              BigSquareRoot.sqrt(square, mc),
 368                                              halfWayCase.round(mc),
 369                                              "Rounding halway " + rm);
 370             }
 371         }
 372 
 373         return failures;
 374     }
 375 
 376     private static int compare(BigDecimal a, BigDecimal b, boolean expected, String prefix) {
 377         boolean result = a.equals(b);
 378         int failed = (result==expected) ? 0 : 1;
 379         if (failed == 1) {
 380             System.err.println("Testing " + prefix +
 381                                "(" + a + ").compareTo(" + b + ") => " + result +
 382                                "\n\tExpected " + expected);
 383         }
 384         return failed;
 385     }
 386 
 387     private static int equalNumerically(BigDecimal a, BigDecimal b,
 388                                         String prefix) {
 389         return compareNumerically(a, b, 0, prefix);
 390     }
 391 
 392 
 393     private static int compareNumerically(BigDecimal a, BigDecimal b,
 394                                           int expected, String prefix) {
 395         int result = a.compareTo(b);
 396         int failed = (result==expected) ? 0 : 1;
 397         if (failed == 1) {
 398             System.err.println("Testing " + prefix +
 399                                "(" + a + ").compareTo(" + b + ") => " + result +
 400                                "\n\tExpected " + expected);
 401         }
 402         return failed;
 403     }
 404 
 405     /**
 406      * Alternative implementation of BigDecimal square root which uses
 407      * higher-precision for a simpler set of termination conditions
 408      * for the Newton iteration.
 409      */
 410     private static class BigSquareRoot {
 411 
 412         /**
 413          * The value 0.5, with a scale of 1.
 414          */
 415         private static final BigDecimal ONE_HALF = valueOf(5L, 1);
 416 
 417         public static boolean isPowerOfTen(BigDecimal bd) {
 418             return BigInteger.ONE.equals(bd.unscaledValue());
 419         }
 420 
 421         public static BigDecimal sqrt(BigDecimal bd, MathContext mc) {
 422             int signum = bd.signum();
 423             if (signum == 1) {
 424                 /*
 425                  * The following code draws on the algorithm presented in
 426                  * "Properly Rounded Variable Precision Square Root," Hull and
 427                  * Abrham, ACM Transactions on Mathematical Software, Vol 11,
 428                  * No. 3, September 1985, Pages 229-237.
 429                  *
 430                  * The BigDecimal computational model differs from the one
 431                  * presented in the paper in several ways: first BigDecimal
 432                  * numbers aren't necessarily normalized, second many more
 433                  * rounding modes are supported, including UNNECESSARY, and
 434                  * exact results can be requested.
 435                  *
 436                  * The main steps of the algorithm below are as follows,
 437                  * first argument reduce the value to the numerical range
 438                  * [1, 10) using the following relations:
 439                  *
 440                  * x = y * 10 ^ exp
 441                  * sqrt(x) = sqrt(y) * 10^(exp / 2) if exp is even
 442                  * sqrt(x) = sqrt(y/10) * 10 ^((exp+1)/2) is exp is odd
 443                  *
 444                  * Then use Newton's iteration on the reduced value to compute
 445                  * the numerical digits of the desired result.
 446                  *
 447                  * Finally, scale back to the desired exponent range and
 448                  * perform any adjustment to get the preferred scale in the
 449                  * representation.
 450                  */
 451 
 452                 // The code below favors relative simplicity over checking
 453                 // for special cases that could run faster.
 454 
 455                 int preferredScale = bd.scale()/2;
 456                 BigDecimal zeroWithFinalPreferredScale =
 457                     BigDecimal.valueOf(0L, preferredScale);
 458 
 459                 // First phase of numerical normalization, strip trailing
 460                 // zeros and check for even powers of 10.
 461                 BigDecimal stripped = bd.stripTrailingZeros();
 462                 int strippedScale = stripped.scale();
 463 
 464                 // Numerically sqrt(10^2N) = 10^N
 465                 if (isPowerOfTen(stripped) &&
 466                     strippedScale % 2 == 0) {
 467                     BigDecimal result = BigDecimal.valueOf(1L, strippedScale/2);
 468                     if (result.scale() != preferredScale) {
 469                         // Adjust to requested precision and preferred
 470                         // scale as appropriate.
 471                         result = result.add(zeroWithFinalPreferredScale, mc);
 472                     }
 473                     return result;
 474                 }
 475 
 476                 // After stripTrailingZeros, the representation is normalized as
 477                 //
 478                 // unscaledValue * 10^(-scale)
 479                 //
 480                 // where unscaledValue is an integer with the mimimum
 481                 // precision for the cohort of the numerical value. To
 482                 // allow binary floating-point hardware to be used to get
 483                 // approximately a 15 digit approximation to the square
 484                 // root, it is helpful to instead normalize this so that
 485                 // the significand portion is to right of the decimal
 486                 // point by roughly (scale() - precision() +1).
 487 
 488                 // Now the precision / scale adjustment
 489                 int scaleAdjust = 0;
 490                 int scale = stripped.scale() - stripped.precision() + 1;
 491                 if (scale % 2 == 0) {
 492                     scaleAdjust = scale;
 493                 } else {
 494                     scaleAdjust = scale - 1;
 495                 }
 496 
 497                 BigDecimal working = stripped.scaleByPowerOfTen(scaleAdjust);
 498 
 499                 assert  // Verify 0.1 <= working < 10
 500                     ONE_TENTH.compareTo(working) <= 0 && working.compareTo(TEN) < 0;
 501 
 502                 // Use good ole' Math.sqrt to get the initial guess for
 503                 // the Newton iteration, good to at least 15 decimal
 504                 // digits. This approach does incur the cost of a
 505                 //
 506                 // BigDecimal -> double -> BigDecimal
 507                 //
 508                 // conversion cycle, but it avoids the need for several
 509                 // Newton iterations in BigDecimal arithmetic to get the
 510                 // working answer to 15 digits of precision. If many fewer
 511                 // than 15 digits were needed, it might be faster to do
 512                 // the loop entirely in BigDecimal arithmetic.
 513                 //
 514                 // (A double value might have as much many as 17 decimal
 515                 // digits of precision; it depends on the relative density
 516                 // of binary and decimal numbers at different regions of
 517                 // the number line.)
 518                 //
 519                 // (It would be possible to check for certain special
 520                 // cases to avoid doing any Newton iterations. For
 521                 // example, if the BigDecimal -> double conversion was
 522                 // known to be exact and the rounding mode had a
 523                 // low-enough precision, the post-Newton rounding logic
 524                 // could be applied directly.)
 525 
 526                 BigDecimal guess = new BigDecimal(Math.sqrt(working.doubleValue()));
 527                 int guessPrecision = 15;
 528                 int originalPrecision = mc.getPrecision();
 529                 int targetPrecision;
 530 
 531                 // If an exact value is requested, it must only need about
 532                 // half of the input digits to represent since multiplying
 533                 // an N digit number by itself yield a 2N-1 digit or 2N
 534                 // digit result.
 535                 if (originalPrecision == 0) {
 536                     targetPrecision = stripped.precision()/2 + 1;
 537                 } else {
 538                     targetPrecision = originalPrecision;
 539                 }
 540 
 541                 // When setting the precision to use inside the Newton
 542                 // iteration loop, take care to avoid the case where the
 543                 // precision of the input exceeds the requested precision
 544                 // and rounding the input value too soon.
 545                 BigDecimal approx = guess;
 546                 int workingPrecision = working.precision();
 547                 // Use "2p + 2" property to guarantee enough
 548                 // intermediate precision so that a double-rounding
 549                 // error does not occur when rounded to the final
 550                 // destination precision.
 551                 int loopPrecision = Math.max(Math.max(2 * targetPrecision + 2,
 552                                                       workingPrecision),
 553                                              34); // Force at least
 554                                                   // two Netwon
 555                                                   // iterations on the
 556                                                   // Math.sqrt result.
 557                 do {
 558                     int tmpPrecision = Math.max(Math.max(guessPrecision, targetPrecision + 2),
 559                                                 workingPrecision);
 560                     MathContext mcTmp = new MathContext(loopPrecision, RoundingMode.HALF_EVEN);
 561                     // approx = 0.5 * (approx + fraction / approx)
 562                     approx = ONE_HALF.multiply(approx.add(working.divide(approx, mcTmp), mcTmp));
 563                     guessPrecision *= 2;
 564                 } while (guessPrecision < loopPrecision);
 565 
 566                 BigDecimal result;
 567                 RoundingMode targetRm = mc.getRoundingMode();
 568                 if (targetRm == RoundingMode.UNNECESSARY || originalPrecision == 0) {
 569                     RoundingMode tmpRm =
 570                         (targetRm == RoundingMode.UNNECESSARY) ? RoundingMode.DOWN : targetRm;
 571                     MathContext mcTmp = new MathContext(targetPrecision, tmpRm);
 572                     result = approx.scaleByPowerOfTen(-scaleAdjust/2).round(mcTmp);
 573 
 574                     // If result*result != this numerically, the square
 575                     // root isn't exact
 576                     if (bd.subtract(result.multiply(result)).compareTo(ZERO) != 0) {
 577                         throw new ArithmeticException("Computed square root not exact.");
 578                     }
 579                 } else {
 580                     result = approx.scaleByPowerOfTen(-scaleAdjust/2).round(mc);
 581                 }
 582 
 583                 assert squareRootResultAssertions(bd, result, mc);
 584                 if (result.scale() != preferredScale) {
 585                     // The preferred scale of an add is
 586                     // max(addend.scale(), augend.scale()). Therefore, if
 587                     // the scale of the result is first minimized using
 588                     // stripTrailingZeros(), adding a zero of the
 589                     // preferred scale rounding the correct precision will
 590                     // perform the proper scale vs precision tradeoffs.
 591                     result = result.stripTrailingZeros().
 592                         add(zeroWithFinalPreferredScale,
 593                             new MathContext(originalPrecision, RoundingMode.UNNECESSARY));
 594                 }
 595                 return result;
 596             } else {
 597                 switch (signum) {
 598                 case -1:
 599                     throw new ArithmeticException("Attempted square root " +
 600                                                   "of negative BigDecimal");
 601                 case 0:
 602                     return valueOf(0L, bd.scale()/2);
 603 
 604                 default:
 605                     throw new AssertionError("Bad value from signum");
 606                 }
 607             }
 608         }
 609 
 610         /**
 611          * For nonzero values, check numerical correctness properties of
 612          * the computed result for the chosen rounding mode.
 613          *
 614          * For the directed roundings, for DOWN and FLOOR, result^2 must
 615          * be {@code <=} the input and (result+ulp)^2 must be {@code >} the
 616          * input. Conversely, for UP and CEIL, result^2 must be {@code >=} the
 617          * input and (result-ulp)^2 must be {@code <} the input.
 618          */
 619         private static boolean squareRootResultAssertions(BigDecimal input, BigDecimal result, MathContext mc) {
 620             if (result.signum() == 0) {
 621                 return squareRootZeroResultAssertions(input, result, mc);
 622             } else {
 623                 RoundingMode rm = mc.getRoundingMode();
 624                 BigDecimal ulp = result.ulp();
 625                 BigDecimal neighborUp   = result.add(ulp);
 626                 // Make neighbor down accurate even for powers of ten
 627                 if (isPowerOfTen(result)) {
 628                     ulp = ulp.divide(TEN);
 629                 }
 630                 BigDecimal neighborDown = result.subtract(ulp);
 631 
 632                 // Both the starting value and result should be nonzero and positive.
 633                 if (result.signum() != 1 ||
 634                     input.signum() != 1) {
 635                     return false;
 636                 }
 637 
 638                 switch (rm) {
 639                 case DOWN:
 640                 case FLOOR:
 641                     assert
 642                         result.multiply(result).compareTo(input)         <= 0 &&
 643                         neighborUp.multiply(neighborUp).compareTo(input) > 0:
 644                     "Square of result out for bounds rounding " + rm;
 645                     return true;
 646 
 647                 case UP:
 648                 case CEILING:
 649                     assert
 650                         result.multiply(result).compareTo(input) >= 0 :
 651                     "Square of result too small rounding " + rm;
 652 
 653                     assert
 654                         neighborDown.multiply(neighborDown).compareTo(input) < 0 :
 655                     "Square of down neighbor too large rounding  " + rm + "\n" +
 656                         "\t input: " + input + "\t neighborDown: " +  neighborDown +"\t sqrt: " + result +
 657                         "\t" + mc;
 658                     return true;
 659 
 660 
 661                 case HALF_DOWN:
 662                 case HALF_EVEN:
 663                 case HALF_UP:
 664                     BigDecimal err = result.multiply(result).subtract(input).abs();
 665                     BigDecimal errUp = neighborUp.multiply(neighborUp).subtract(input);
 666                     BigDecimal errDown =  input.subtract(neighborDown.multiply(neighborDown));
 667                     // All error values should be positive so don't need to
 668                     // compare absolute values.
 669 
 670                     int err_comp_errUp = err.compareTo(errUp);
 671                     int err_comp_errDown = err.compareTo(errDown);
 672 
 673                     assert
 674                         errUp.signum()   == 1 &&
 675                         errDown.signum() == 1 :
 676                     "Errors of neighbors squared don't have correct signs";
 677 
 678                     assert
 679                         err_comp_errUp   <= 0 : "Upper neighbor is closer than result: " + rm +
 680                         "\t" + input + "\t result" + result;
 681                     assert
 682                         err_comp_errDown <= 0 : "Lower neighbor is closer than result: " + rm +
 683                         "\t" + input + "\t result " + result + "\t lower neighbor: " + neighborDown;
 684                     
 685 
 686                     assert
 687                         ((err_comp_errUp   == 0 ) ? err_comp_errDown < 0 : true) &&
 688                         ((err_comp_errDown == 0 ) ? err_comp_errUp   < 0 : true) :
 689                             "Incorrect error relationships";
 690                         // && could check for digit conditions for ties too
 691                         return true;
 692 
 693                 default: // Definition of UNNECESSARY already verified.
 694                     return true;
 695                 }
 696             }
 697         }
 698 
 699         private static boolean squareRootZeroResultAssertions(BigDecimal input,
 700                                                               BigDecimal result,
 701                                                               MathContext mc) {
 702             return input.compareTo(ZERO) == 0;
 703         }
 704     }
 705 }
 706