1 /*
   2  * Copyright 2018-2020 Raffaello Giulietti
   3  *
   4  * Permission is hereby granted, free of charge, to any person obtaining a copy
   5  * of this software and associated documentation files (the "Software"), to deal
   6  * in the Software without restriction, including without limitation the rights
   7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8  * copies of the Software, and to permit persons to whom the Software is
   9  * furnished to do so, subject to the following conditions:
  10  *
  11  * The above copyright notice and this permission notice shall be included in
  12  * all copies or substantial portions of the Software.
  13  *
  14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20  * THE SOFTWARE.
  21  */
  22 
  23 package jdk.internal.math;
  24 
  25 import java.io.IOException;
  26 import java.io.StringReader;
  27 import java.math.BigDecimal;
  28 import java.math.BigInteger;
  29 
  30 import static java.math.BigInteger.*;
  31 
  32 /*
  33 A checker for the Javadoc specification.
  34 It just relies on straightforward use of (expensive) BigDecimal arithmetic,
  35 not optimized at all.
  36  */
  37 abstract class ToDecimalChecker extends BasicChecker {
  38 
  39     // The string to check
  40     private final String s;
  41 
  42     // The decimal parsed from s is c 10^q
  43     private long c;
  44     private int q;
  45 
  46     // The number of digits parsed from s: 10^(len10-1) <= c < 10^len10
  47     private int len10;
  48 
  49     ToDecimalChecker(String s) {
  50         this.s = s;
  51     }
  52 
  53     /*
  54     Returns e be such that 10^(e-1) <= v < 10^e.
  55      */
  56     static int e(double v) {
  57         // log10(v) + 1 is a first good approximation of e
  58         int e = (int) Math.floor(Math.log10(v)) + 1;
  59 
  60         // Full precision search for e such that 10^(e-1) <= c 2^q < 10^e.
  61         BigDecimal vp = new BigDecimal(v);
  62         BigDecimal low = new BigDecimal(BigInteger.ONE, -(e - 1));
  63         while (low.compareTo(vp) > 0) {
  64             e -= 1;
  65             low = new BigDecimal(BigInteger.ONE, -(e - 1));
  66         }
  67         BigDecimal high = new BigDecimal(BigInteger.ONE, -e);
  68         while (vp.compareTo(high) >= 0) {
  69             e += 1;
  70             high = new BigDecimal(BigInteger.ONE, -e);
  71         }
  72         return e;
  73     }
  74 
  75     static long cTiny(int qMin, int kMin) {
  76         BigInteger[] qr = ONE.shiftLeft(-qMin)
  77                 .divideAndRemainder(TEN.pow(-(kMin + 1)));
  78         BigInteger cTiny = qr[1].signum() > 0 ? qr[0].add(ONE) : qr[0];
  79         assertTrue(cTiny.bitLength() < Long.SIZE, "C_TINY");
  80         return cTiny.longValue();
  81     }
  82 
  83     void assertTrue() {
  84         if (isOK()) {
  85             return;
  86         }
  87         String msg = "toString applied to the bits " +
  88                 hexBits() +
  89                 " returns " +
  90                 "\"" + s + "\"" +
  91                 ", which is not correct according to the specification.";
  92         if (FAILURE_THROWS_EXCEPTION) {
  93             throw new RuntimeException(msg);
  94         }
  95         System.err.println(msg);
  96     }
  97 
  98     /*
  99     Returns whether s syntactically meets the expected output of
 100     toString. It is restricted to finite positive outputs.
 101     It is an unusually long method but rather straightforward, too.
 102     Many conditionals could be merged, but KISS here.
 103      */
 104     private boolean parse(String t) {
 105         try {
 106             // first determine interesting boundaries in the string
 107             StringReader r = new StringReader(t);
 108             int ch = r.read();
 109 
 110             int i = 0;
 111             while (ch == '0') {
 112                 ++i;
 113                 ch = r.read();
 114             }
 115             // i is just after zeroes starting the integer
 116 
 117             int p = i;
 118             while ('0' <= ch && ch <= '9') {
 119                 c = 10 * c + (ch - '0');
 120                 if (c < 0) {
 121                     return false;
 122                 }
 123                 ++len10;
 124                 ++p;
 125                 ch = r.read();
 126             }
 127             // p is just after digits ending the integer
 128 
 129             int fz = p;
 130             if (ch == '.') {
 131                 ++fz;
 132                 ch = r.read();
 133             }
 134             // fz is just after a decimal '.'
 135 
 136             int f = fz;
 137             while (ch == '0') {
 138                 c = 10 * c + (ch - '0');
 139                 if (c < 0) {
 140                     return false;
 141                 }
 142                 ++len10;
 143                 ++f;
 144                 ch = r.read();
 145             }
 146             // f is just after zeroes starting the fraction
 147 
 148             if (c == 0) {
 149                 len10 = 0;
 150             }
 151             int x = f;
 152             while ('0' <= ch && ch <= '9') {
 153                 c = 10 * c + (ch - '0');
 154                 if (c < 0) {
 155                     return false;
 156                 }
 157                 ++len10;
 158                 ++x;
 159                 ch = r.read();
 160             }
 161             // x is just after digits ending the fraction
 162 
 163             int g = x;
 164             if (ch == 'E') {
 165                 ++g;
 166                 ch = r.read();
 167             }
 168             // g is just after an exponent indicator 'E'
 169 
 170             int ez = g;
 171             if (ch == '-') {
 172                 ++ez;
 173                 ch = r.read();
 174             }
 175             // ez is just after a '-' sign in the exponent
 176 
 177             int e = ez;
 178             while (ch == '0') {
 179                 ++e;
 180                 ch = r.read();
 181             }
 182             // e is just after zeroes starting the exponent
 183 
 184             int z = e;
 185             while ('0' <= ch && ch <= '9') {
 186                 q = 10 * q + (ch - '0');
 187                 if (q < 0) {
 188                     return false;
 189                 }
 190                 ++z;
 191                 ch = r.read();
 192             }
 193             // z is just after digits ending the exponent
 194 
 195             // No other char after the number
 196             if (z != t.length()) {
 197                 return false;
 198             }
 199 
 200             // The integer must be present
 201             if (p == 0) {
 202                 return false;
 203             }
 204 
 205             // The decimal '.' must be present
 206             if (fz == p) {
 207                 return false;
 208             }
 209 
 210             // The fraction must be present
 211             if (x == fz) {
 212                 return false;
 213             }
 214 
 215             // The fraction is not 0 or it consists of exactly one 0
 216             if (f == x && f - fz > 1) {
 217                 return false;
 218             }
 219 
 220             // Plain notation, no exponent
 221             if (x == z) {
 222                 // At most one 0 starting the integer
 223                 if (i > 1) {
 224                     return false;
 225                 }
 226 
 227                 // If the integer is 0, at most 2 zeroes start the fraction
 228                 if (i == 1 && f - fz > 2) {
 229                     return false;
 230                 }
 231 
 232                 // The integer cannot have more than 7 digits
 233                 if (p > 7) {
 234                     return false;
 235                 }
 236 
 237                 q = fz - x;
 238 
 239                 // OK for plain notation
 240                 return true;
 241             }
 242 
 243             // Computerized scientific notation
 244 
 245             // The integer has exactly one nonzero digit
 246             if (i != 0 || p != 1) {
 247                 return false;
 248             }
 249 
 250             //
 251             // There must be an exponent indicator
 252             if (x == g) {
 253                 return false;
 254             }
 255 
 256             // There must be an exponent
 257             if (ez == z) {
 258                 return false;
 259             }
 260 
 261             // The exponent must not start with zeroes
 262             if (ez != e) {
 263                 return false;
 264             }
 265 
 266             if (g != ez) {
 267                 q = -q;
 268             }
 269 
 270             // The exponent must not lie in [-3, 7)
 271             if (-3 <= q && q < 7) {
 272                 return false;
 273             }
 274 
 275             q += fz - x;
 276 
 277             // OK for computerized scientific notation
 278             return true;
 279         } catch (IOException ex) {
 280             // An IOException on a StringReader??? Please...
 281             return false;
 282         }
 283     }
 284 
 285     private boolean isOK() {
 286         if (isNaN()) {
 287             return s.equals("NaN");
 288         }
 289         String t = s;
 290         if (isNegative()) {
 291             if (s.isEmpty() || s.charAt(0) != '-') {
 292                 return false;
 293             }
 294             negate();
 295             t = s.substring(1);
 296         }
 297         if (isInfinity()) {
 298             return t.equals("Infinity");
 299         }
 300         if (isZero()) {
 301             return t.equals("0.0");
 302         }
 303         if (!parse(t)) {
 304             return false;
 305         }
 306         if (len10 < 2) {
 307             c *= 10;
 308             q -= 1;
 309             len10 += 1;
 310         }
 311         if (2 > len10 || len10 > maxLen10()) {
 312             return false;
 313         }
 314 
 315         // The exponent is bounded
 316         if (minExp() > q + len10 || q + len10 > maxExp()) {
 317             return false;
 318         }
 319 
 320         // s must recover v
 321         try {
 322             if (!recovers(t)) {
 323                 return false;
 324             }
 325         } catch (NumberFormatException e) {
 326             return false;
 327         }
 328 
 329         // Get rid of trailing zeroes, still ensuring at least 2 digits
 330         while (len10 > 2 && c % 10 == 0) {
 331             c /= 10;
 332             q += 1;
 333             len10 -= 1;
 334         }
 335 
 336         if (len10 > 2) {
 337             // Try with a shorter number less than v...
 338             if (recovers(BigDecimal.valueOf(c / 10, -q - 1))) {
 339                 return false;
 340             }
 341 
 342             // ... and with a shorter number greater than v
 343             if (recovers(BigDecimal.valueOf(c / 10 + 1, -q - 1))) {
 344                 return false;
 345             }
 346         }
 347 
 348         // Try with the decimal predecessor...
 349         BigDecimal dp = c == 10 ?
 350                 BigDecimal.valueOf(99, -q + 1) :
 351                 BigDecimal.valueOf(c - 1, -q);
 352         if (recovers(dp)) {
 353             BigDecimal bv = toBigDecimal();
 354             BigDecimal deltav = bv.subtract(BigDecimal.valueOf(c, -q));
 355             if (deltav.signum() >= 0) {
 356                 return true;
 357             }
 358             BigDecimal delta = dp.subtract(bv);
 359             if (delta.signum() >= 0) {
 360                 return false;
 361             }
 362             int cmp = deltav.compareTo(delta);
 363             return cmp > 0 || cmp == 0 && (c & 0x1) == 0;
 364         }
 365 
 366         // ... and with the decimal successor
 367         BigDecimal ds = BigDecimal.valueOf(c + 1, -q);
 368         if (recovers(ds)) {
 369             BigDecimal bv = toBigDecimal();
 370             BigDecimal deltav = bv.subtract(BigDecimal.valueOf(c, -q));
 371             if (deltav.signum() <= 0) {
 372                 return true;
 373             }
 374             BigDecimal delta = ds.subtract(bv);
 375             if (delta.signum() <= 0) {
 376                 return false;
 377             }
 378             int cmp = deltav.compareTo(delta);
 379             return cmp < 0 || cmp == 0 && (c & 0x1) == 0;
 380         }
 381 
 382         return true;
 383     }
 384 
 385     abstract BigDecimal toBigDecimal();
 386 
 387     abstract boolean recovers(BigDecimal b);
 388 
 389     abstract boolean recovers(String s);
 390 
 391     abstract String hexBits();
 392 
 393     abstract int minExp();
 394 
 395     abstract int maxExp();
 396 
 397     abstract int maxLen10();
 398 
 399     abstract boolean isZero();
 400 
 401     abstract boolean isInfinity();
 402 
 403     abstract void negate();
 404 
 405     abstract boolean isNegative();
 406 
 407     abstract boolean isNaN();
 408 
 409 }