1 /*
   2  * Copyright (c) 2016, 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
  27  * @summary Tests of BigDecimal.sqrt().
  28  */
  29 
  30 import java.math.*;
  31 import java.util.*;
  32 
  33 public class SquareRootTests {
  34 
  35     public static void main(String... args) {
  36         int failures = 0;
  37 
  38         failures += negativeTests();
  39         failures += zeroTests();
  40         failures += evenPowersOfTenTests();
  41         failures += squareRootTwoTests();
  42         failures += lowPrecisionPerfectSquares();
  43 
  44         if (failures > 0 ) {
  45             throw new RuntimeException("Incurred " + failures + " failures" +
  46                                        " testing BigDecimal.sqrt().");
  47         }
  48     }
  49 
  50     private static int negativeTests() {
  51         int failures = 0;
  52 
  53         for (long i = -10; i < 0; i++) {
  54             for (int j = -5; j < 5; j++) {
  55                 try {
  56                     BigDecimal input = BigDecimal.valueOf(i, j);
  57                     BigDecimal result = input.sqrt(MathContext.DECIMAL64);
  58                     System.err.println("Unexpected sqrt of negative: (" +
  59                                        input + ").sqrt()  = " + result );
  60                     failures += 1;
  61                 } catch (ArithmeticException e) {
  62                     ; // Expected
  63                 }
  64             }
  65         }
  66         
  67         return failures;
  68     }
  69 
  70     private static int zeroTests() {
  71         int failures = 0;
  72 
  73         for (int i = -100; i < 100; i++) {
  74             BigDecimal expected = BigDecimal.valueOf(0L, i/2);
  75             // These results are independent of rounding mode
  76             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.UNLIMITED),
  77                                 expected, true, "zeros");
  78 
  79             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.DECIMAL64),
  80                                 expected, true, "zeros");
  81         }
  82 
  83         return failures;
  84     }
  85 
  86     /**
  87      * sqrt(10^2N) is 10^N
  88      * Both numerical value and representation should be verified
  89      */
  90     private static int evenPowersOfTenTests() {
  91         int failures = 0;
  92         MathContext oneDigitExactly = new MathContext(1, RoundingMode.UNNECESSARY);
  93 
  94         for (int scale = -100; scale <= 100; scale++) {
  95             BigDecimal testValue       = BigDecimal.valueOf(1, 2*scale);
  96             BigDecimal expectedNumericalResult = BigDecimal.valueOf(1, scale);
  97 
  98             BigDecimal result;
  99 
 100 
 101             failures += equalNumerically(expectedNumericalResult,
 102                                            result = testValue.sqrt(MathContext.DECIMAL64),
 103                                            "Even powers of 10, DECIMAL64");
 104 
 105             // Can round to one digit of precision exactly
 106             failures += equalNumerically(expectedNumericalResult,
 107                                            result = testValue.sqrt(oneDigitExactly),
 108                                            "even powers of 10, 1 digit");
 109             if (result.precision() > 1) {
 110                 failures += 1;
 111                 System.err.println("Excess precision for " + result);
 112             }
 113                 
 114 
 115             // If rounding to more than one digit, do precision / scale checking...
 116 
 117         }
 118 
 119         return failures;
 120     }
 121 
 122     private static int squareRootTwoTests() {
 123         int failures = 0;
 124         BigDecimal TWO = new BigDecimal(2);
 125 
 126         // Square root of 2 truncated to 65 digits
 127         BigDecimal highPrecisionRoot2 =
 128             new BigDecimal("1.41421356237309504880168872420969807856967187537694807317667973799");
 129 
 130 
 131         RoundingMode[] modes = {
 132             RoundingMode.UP,       RoundingMode.DOWN,
 133             RoundingMode.CEILING, RoundingMode.FLOOR,
 134             RoundingMode.HALF_UP, RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN
 135         };
 136 
 137         // For each iteresting rounding mode, for precisions 1 to, say
 138         // 63 numerically compare TWO.sqrt(mc) to
 139         // highPrecisionRoot2.round(mc)
 140         
 141         for (RoundingMode mode : modes) {
 142             for (int precision = 1; precision < 63; precision++) {
 143                 MathContext mc = new MathContext(precision, mode);
 144                 BigDecimal expected = highPrecisionRoot2.round(mc); 
 145                 BigDecimal computed = TWO.sqrt(mc);
 146 
 147                 equalNumerically(expected, computed, "sqrt(2)");
 148             }
 149         }
 150 
 151         return failures;
 152     }
 153 
 154     private static int lowPrecisionPerfectSquares() {
 155         int failures = 0;
 156 
 157         // For 5^2 through 9^2, if the input is rounded to one digit
 158         // first before the root is computed, the wrong answer will
 159         // result. Verify results and scale for different rounding
 160         // modes and precisions.
 161         long[][] squaresWithOneDigitRoot = {{ 4, 2},
 162                                             { 9, 3},
 163                                             {25, 5},
 164                                             {36, 6},
 165                                             {49, 7},
 166                                             {64, 8},
 167                                             {81, 9}};
 168 
 169         for (long[] squareAndRoot : squaresWithOneDigitRoot) {
 170             BigDecimal square     = new BigDecimal(squareAndRoot[0]);
 171             BigDecimal expected   = new BigDecimal(squareAndRoot[1]);
 172             
 173             for (int scale = 0; scale <= 4; scale++) {
 174                 BigDecimal scaledSquare = square.setScale(scale, RoundingMode.UNNECESSARY);
 175                 int expectedScale = scale/2;
 176                 for (int precision = 0; precision <= 5; precision++) {
 177                     for (RoundingMode rm : RoundingMode.values()) {
 178                         MathContext mc = new MathContext(precision, rm);
 179                         BigDecimal computedRoot = scaledSquare.sqrt(mc);
 180                         failures += equalNumerically(expected, computedRoot, "simple squares");
 181                         int computedScale = computedRoot.scale();
 182                         if (precision >=  expectedScale + 1 &&
 183                             computedScale != expectedScale) {
 184                         System.err.printf("%s\tprecision=%d\trm=%s%n",
 185                                           computedRoot.toString(), precision, rm);
 186                             failures++;
 187                             System.err.printf("\t%s does not have expected scale of %d%n.",
 188                                               computedRoot, expectedScale);
 189                         }
 190                     }
 191                 }
 192             }
 193         }
 194 
 195         return failures;
 196     }
 197 
 198     private static int compare(BigDecimal a, BigDecimal b, boolean expected, String prefix) {
 199         boolean result = a.equals(b);
 200         int failed = (result==expected) ? 0 : 1;
 201         if (failed == 1) {
 202             System.err.println("Testing " + prefix + 
 203                                "(" + a + ").compareTo(" + b + ") => " + result +
 204                                "\n\tExpected " + expected);
 205         }
 206         return failed;
 207     }
 208 
 209     private static int equalNumerically(BigDecimal a, BigDecimal b,
 210                                         String prefix) {
 211         return compareNumerically(a, b, 0, prefix);
 212     }
 213 
 214 
 215     private static int compareNumerically(BigDecimal a, BigDecimal b,
 216                                           int expected, String prefix) {
 217         int result = a.compareTo(b);
 218         int failed = (result==expected) ? 0 : 1;
 219         if (failed == 1) {
 220             System.err.println("Testing " + prefix + 
 221                                "(" + a + ").compareTo(" + b + ") => " + result +
 222                                "\n\tExpected " + expected);
 223         }
 224         return failed;
 225     }
 226 
 227 }