001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2024, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tech.units.indriya.function; 031 032import java.math.BigDecimal; 033import java.math.BigInteger; 034import java.math.MathContext; 035import java.math.RoundingMode; 036import java.util.concurrent.atomic.AtomicInteger; 037import java.util.concurrent.atomic.AtomicLong; 038import java.util.function.UnaryOperator; 039 040import tech.units.indriya.spi.NumberSystem; 041 042/** 043 * {@link NumberSystem} implementation to support Java's built-in {@link Number}s and the 044 * {@link RationalNumber} type. 045 * 046 * @author Andi Huber 047 * @author Werner Keil 048 * @since 2.0 049 */ 050public class DefaultNumberSystem implements NumberSystem { 051 052 /** 053 * In order of increasing number type 'widening'. 054 */ 055 private enum NumberType { 056 057 // integer types 058 BYTE_BOXED(true, Byte.class, (byte)1, (byte)0), 059 SHORT_BOXED(true, Short.class, (short)1, (short)0), 060 INTEGER_BOXED(true, Integer.class, 1, 0), 061 INTEGER_ATOMIC(true, AtomicInteger.class, 1, 0) { 062 @Override boolean isZero(Number number) { 063 return ((AtomicInteger) number).intValue() == 0; 064 } 065 }, 066 LONG_BOXED(true, Long.class, 1L, 0L), 067 LONG_ATOMIC(true, AtomicLong.class, 1L, 0) { 068 @Override boolean isZero(Number number) { 069 return ((AtomicLong) number).longValue() == 0L; 070 } 071 }, 072 BIG_INTEGER(true, BigInteger.class, BigInteger.ONE, BigInteger.ZERO) { 073 @Override boolean isZero(Number number) { 074 return ((BigInteger) number).signum() == 0; 075 } 076 }, 077 078 // rational types 079 RATIONAL(false, RationalNumber.class, RationalNumber.ONE, RationalNumber.ZERO) { 080 @Override boolean isZero(Number number) { 081 return ((RationalNumber) number).signum() == 0; 082 } 083 }, 084 085 // fractional types 086 FLOAT_BOXED(false, Float.class, 1.f, 0.f), 087 DOUBLE_BOXED(false, Double.class, 1.d, 0.d), 088 BIG_DECIMAL(false, BigDecimal.class, BigDecimal.ONE, BigDecimal.ZERO) { 089 @Override boolean isZero(Number number) { 090 return ((BigDecimal) number).signum() == 0; 091 } 092 }, 093 094 ; 095 private final boolean integerOnly; 096 private final Class<? extends Number> type; 097 private final Number one; 098 private final Number zero; 099 100 private NumberType(final boolean integerOnly, final Class<? extends Number> type, 101 final Number one, final Number zero) { 102 103 this.integerOnly = integerOnly; 104 this.type = type; 105 this.one = one; 106 this.zero = zero; 107 } 108 109 /** 110 * Whether the underlying number type can only represent integers. 111 * <p> 112 * If <code>false</code> it can also represent fractional numbers. 113 */ 114 public boolean isIntegerOnly() { 115 return integerOnly; 116 } 117 118 @SuppressWarnings("unused") 119 public Class<? extends Number> getType() { 120 return type; 121 } 122 123 // 'hardcoded' for performance reasons 124 static NumberType valueOf(final Number number) { 125 if(number instanceof Long) { 126 return LONG_BOXED; 127 } 128 if(number instanceof AtomicLong) { 129 return LONG_ATOMIC; 130 } 131 if(number instanceof Integer) { 132 return INTEGER_BOXED; 133 } 134 if(number instanceof AtomicInteger) { 135 return INTEGER_ATOMIC; 136 } 137 if(number instanceof Double) { 138 return DOUBLE_BOXED; 139 } 140 if(number instanceof Short) { 141 return SHORT_BOXED; 142 } 143 if(number instanceof Byte) { 144 return BYTE_BOXED; 145 } 146 if(number instanceof Float) { 147 return FLOAT_BOXED; 148 } 149 if(number instanceof BigDecimal) { 150 return BIG_DECIMAL; 151 } 152 if(number instanceof BigInteger) { 153 return BIG_INTEGER; 154 } 155 if(number instanceof RationalNumber) { 156 return RATIONAL; 157 } 158 final String msg = String.format("Unsupported number type '%s'", 159 number.getClass().getName()); 160 throw new IllegalArgumentException(msg); 161 } 162 163 /** 164 * Whether given {@link Number} is ZERO. 165 * @param number - must be of type {@link #getType()} 166 * @apiNote For class internal use only, 167 * such that we have control over the number's type that gets passed in. 168 */ 169 boolean isZero(Number number) { 170 return zero.equals(number); 171 } 172 173 } 174 175 @Override 176 public Number add(final Number x, final Number y) { 177 178 final NumberType type_x = NumberType.valueOf(x); 179 final NumberType type_y = NumberType.valueOf(y); 180 181 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 182 183 return reorder_args 184 ? addWideAndNarrow(type_y, y, type_x, x) 185 : addWideAndNarrow(type_x, x, type_y, y); 186 } 187 188 @Override 189 public Number subtract(final Number x, final Number y) { 190 return add(x, negate(y)); 191 } 192 193 @Override 194 public Number multiply(final Number x, final Number y) { 195 196 final NumberType type_x = NumberType.valueOf(x); 197 final NumberType type_y = NumberType.valueOf(y); 198 199 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 200 201 return reorder_args 202 ? multiplyWideAndNarrow(type_y, y, type_x, x) 203 : multiplyWideAndNarrow(type_x, x, type_y, y); 204 } 205 206 @Override 207 public Number divide(final Number x, final Number y) { 208 return multiply(x, reciprocal(y)); 209 } 210 211 @Override 212 public Number[] divideAndRemainder(final Number x, final Number y, final boolean roundRemainderTowardsZero) { 213 214 final int sign_x = signum(x); 215 final int sign_y = signum(y); 216 217 final int sign = sign_x * sign_y; 218 // handle corner cases when x or y are zero 219 if(sign == 0) { 220 if(sign_y == 0) { 221 throw new ArithmeticException("division by zero"); 222 } 223 if(sign_x==0) { 224 return new Number[] {0, 0}; 225 } 226 } 227 228 final Number absX = abs(x); 229 final Number absY = abs(y); 230 231 final NumberType type_x = NumberType.valueOf(absX); 232 final NumberType type_y = NumberType.valueOf(absY); 233 234 // if x and y are both integer types than we can calculate integer results, 235 // otherwise we resort to BigDecimal 236 final boolean yieldIntegerResult = type_x.isIntegerOnly() && type_y.isIntegerOnly(); 237 238 if(yieldIntegerResult) { 239 240 final BigInteger integer_x = integerToBigInteger(absX); 241 final BigInteger integer_y = integerToBigInteger(absY); 242 243 final BigInteger[] divAndRemainder = integer_x.divideAndRemainder(integer_y); 244 245 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigInteger)number)); 246 247 } else { 248 249 final MathContext mathContext = 250 new MathContext(Calculus.MATH_CONTEXT.getPrecision(), RoundingMode.FLOOR); 251 252 final BigDecimal decimal_x = (type_x == NumberType.RATIONAL) 253 ? ((RationalNumber) absX).bigDecimalValue() 254 : toBigDecimal(absX); 255 final BigDecimal decimal_y = (type_y == NumberType.RATIONAL) 256 ? ((RationalNumber) absY).bigDecimalValue() 257 : toBigDecimal(absY); 258 259 final BigDecimal[] divAndRemainder = decimal_x.divideAndRemainder(decimal_y, mathContext); 260 261 if(roundRemainderTowardsZero) { 262 return new Number[] { 263 copySignTo(sign, divAndRemainder[0]), 264 copySignTo(sign, divAndRemainder[1].toBigInteger())}; 265 266 } else { 267 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigDecimal)number)); 268 } 269 270 } 271 272 } 273 274 @Override 275 public Number reciprocal(final Number number) { 276 if(isIntegerOnly(number)) { 277 return RationalNumber.of(BigInteger.ONE, integerToBigInteger(number)); 278 } 279 if(number instanceof BigDecimal) { 280 return RationalNumber.of((BigDecimal) number).reciprocal(); 281 } 282 if(number instanceof RationalNumber) { 283 return ((RationalNumber) number).reciprocal(); 284 } 285 if(number instanceof Double) { 286 return RationalNumber.of((double)number).reciprocal(); 287 } 288 if(number instanceof Float) { 289 return RationalNumber.of(number.doubleValue()).reciprocal(); 290 } 291 throw unsupportedNumberType(number); 292 } 293 294 @Override 295 public int signum(final Number number) { 296 if(number instanceof BigInteger) { 297 return ((BigInteger) number).signum(); 298 } 299 if(number instanceof BigDecimal) { 300 return ((BigDecimal) number).signum(); 301 } 302 if(number instanceof RationalNumber) { 303 return ((RationalNumber) number).signum(); 304 } 305 if(number instanceof Double) { 306 return (int)Math.signum((double)number); 307 } 308 if(number instanceof Float) { 309 return (int)Math.signum((float)number); 310 } 311 if(number instanceof Long || number instanceof AtomicLong) { 312 final long longValue = number.longValue(); 313 return Long.signum(longValue); 314 } 315 if(number instanceof Integer || number instanceof AtomicInteger || 316 number instanceof Short || number instanceof Byte) { 317 final int intValue = number.intValue(); 318 return Integer.signum(intValue); 319 } 320 throw unsupportedNumberType(number); 321 } 322 323 @Override 324 public Number abs(final Number number) { 325 if(number instanceof BigInteger) { 326 return ((BigInteger) number).abs(); 327 } 328 if(number instanceof BigDecimal) { 329 return ((BigDecimal) number).abs(); 330 } 331 if(number instanceof RationalNumber) { 332 return ((RationalNumber) number).abs(); 333 } 334 if(number instanceof Double) { 335 return Math.abs((double)number); 336 } 337 if(number instanceof Float) { 338 return Math.abs((float)number); 339 } 340 if(number instanceof Long || number instanceof AtomicLong) { 341 final long longValue = number.longValue(); 342 if(longValue == Long.MIN_VALUE) { 343 return BigInteger.valueOf(longValue).abs(); // widen to BigInteger 344 } 345 return Math.abs(longValue); 346 } 347 if(number instanceof Integer || number instanceof AtomicInteger) { 348 final int intValue = number.intValue(); 349 if(intValue == Integer.MIN_VALUE) { 350 return Math.abs(number.longValue()); // widen to long 351 } 352 return Math.abs(intValue); 353 } 354 if(number instanceof Short || number instanceof Byte) { 355 Math.abs(number.intValue()); // widen to int 356 } 357 throw unsupportedNumberType(number); 358 } 359 360 @Override 361 public Number negate(final Number number) { 362 if(number instanceof BigInteger) { 363 return ((BigInteger) number).negate(); 364 } 365 if(number instanceof BigDecimal) { 366 return ((BigDecimal) number).negate(); 367 } 368 if(number instanceof RationalNumber) { 369 return ((RationalNumber) number).negate(); 370 } 371 if(number instanceof Double) { 372 return -((double)number); 373 } 374 if(number instanceof Float) { 375 return -((float)number); 376 } 377 if(number instanceof Long || number instanceof AtomicLong) { 378 final long longValue = number.longValue(); 379 if(longValue == Long.MIN_VALUE) { 380 return BigInteger.valueOf(longValue).negate(); // widen to BigInteger 381 } 382 return -longValue; 383 } 384 if(number instanceof Integer || number instanceof AtomicInteger) { 385 final int intValue = number.intValue(); 386 if(intValue == Integer.MIN_VALUE) { 387 return -number.longValue(); // widen to long 388 } 389 return -intValue; 390 } 391 if(number instanceof Short) { 392 final short shortValue = (short)number; 393 if(shortValue == Short.MIN_VALUE) { 394 return -number.intValue(); // widen to int 395 } 396 return -shortValue; 397 } 398 if(number instanceof Byte) { 399 final short byteValue = (byte)number; 400 if(byteValue == Byte.MIN_VALUE) { 401 return -number.intValue(); // widen to int 402 } 403 return -byteValue; 404 } 405 throw unsupportedNumberType(number); 406 } 407 408 @Override 409 public Number power(final Number number, final int exponent) { 410 if(exponent==0) { 411 if(isZero(number)) { 412 throw new ArithmeticException("0^0 is not defined"); 413 } 414 return 1; // x^0 == 1, for any x!=0 415 } 416 if(exponent==1) { 417 return number; // x^1 == x, for any x 418 } 419 if(number instanceof BigInteger || 420 number instanceof Long || number instanceof AtomicLong || 421 number instanceof Integer || number instanceof AtomicInteger || 422 number instanceof Short || number instanceof Byte) { 423 final BigInteger bigInt = integerToBigInteger(number); 424 if(exponent>0) { 425 return bigInt.pow(exponent); 426 } 427 return RationalNumber.ofInteger(bigInt).pow(exponent); 428 429 } 430 if(number instanceof BigDecimal) { 431 return ((BigDecimal) number).pow(exponent, Calculus.MATH_CONTEXT); 432 } 433 if(number instanceof RationalNumber) { 434 ((RationalNumber) number).pow(exponent); 435 } 436 if(number instanceof Double || number instanceof Float) { 437 return toBigDecimal(number).pow(exponent, Calculus.MATH_CONTEXT); 438 } 439 throw unsupportedNumberType(number); 440 } 441 442 @Override 443 public Number exp(final Number number) { 444 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 445 return Math.exp(number.doubleValue()); 446 } 447 448 @Override 449 public Number log(final Number number) { 450 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 451 return Math.log(number.doubleValue()); 452 } 453 454 @Override 455 public Number narrow(final Number number) { 456 457 //Implementation Note: for performance we stop narrowing down at 'double' or 'integer' level 458 459 if(number instanceof Integer || number instanceof AtomicInteger || 460 number instanceof Short || number instanceof Byte) { 461 return number; 462 } 463 464 if(number instanceof Double || number instanceof Float) { 465 final double doubleValue = number.doubleValue(); 466 if(!Double.isFinite(doubleValue)) { 467 throw unsupportedNumberValue(doubleValue); 468 } 469 if(doubleValue % 1 == 0 && !isZero(number)) { 470 // double represents an integer other than zero 471 return narrow(BigDecimal.valueOf(doubleValue)); 472 } 473 return number; 474 } 475 476 if(isIntegerOnly(number)) { 477 478 // number is one of {BigInteger, Long} 479 480 final int total_bits_required = bitLengthOfInteger(number); 481 482 // check whether we have enough bits to store the result into an int 483 if(total_bits_required<31) { 484 return number.intValue(); 485 } 486 487 // check whether we have enough bits to store the result into a long 488 if(total_bits_required<63) { 489 return number.longValue(); 490 } 491 492 return number; // cannot narrow down 493 494 } 495 496 if(number instanceof BigDecimal) { 497 498 final BigDecimal decimal = (BigDecimal) number; 499 // educated guess: it is more likely for the given decimal to have fractional parts, than not; 500 // hence in order to avoid the expensive conversion attempt decimal.toBigIntegerExact() below, 501 // we do a less expensive check first 502 if(isFractional(decimal)) { 503 return number; // cannot narrow to integer 504 } 505 try { 506 BigInteger integer = decimal.toBigIntegerExact(); 507 return narrow(integer); 508 } catch (ArithmeticException e) { 509 return number; // cannot narrow to integer (unexpected code reach, due to isFractional(decimal) guard above) 510 } 511 } 512 513 if(number instanceof RationalNumber) { 514 515 final RationalNumber rational = ((RationalNumber) number); 516 517 return rational.isInteger() 518 ? narrow(rational.getDividend()) // divisor is ONE 519 : number; // cannot narrow to integer; 520 } 521 522 // for any other number type just do nothing 523 return number; 524 } 525 526 @Override 527 public int compare(final Number x, final Number y) { 528 529 final NumberType type_x = NumberType.valueOf(x); 530 final NumberType type_y = NumberType.valueOf(y); 531 532 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 533 534 return reorder_args 535 ? -compareWideVsNarrow(type_y, y, type_x, x) 536 : compareWideVsNarrow(type_x, x, type_y, y); 537 } 538 539 @Override 540 public boolean isZero(final Number number) { 541 NumberType numberType = NumberType.valueOf(number); 542 return numberType.isZero(number); 543 } 544 545 @Override 546 public boolean isOne(final Number number) { 547 NumberType numberType = NumberType.valueOf(number); 548 return compare(numberType.one, number) == 0; 549 } 550 551 @Override 552 public boolean isLessThanOne(final Number number) { 553 NumberType numberType = NumberType.valueOf(number); 554 return compare(numberType.one, number) > 0; 555 } 556 557 @Override 558 public boolean isInteger(final Number number) { 559 NumberType numberType = NumberType.valueOf(number); 560 return isInteger(numberType, number); 561 } 562 563 564 // -- HELPER 565 566 private IllegalArgumentException unsupportedNumberValue(final Number number) { 567 final String msg = String.format("Unsupported number value '%s' of type '%s' in number system '%s'", 568 "" + number, 569 number.getClass(), 570 this.getClass().getName()); 571 572 return new IllegalArgumentException(msg); 573 } 574 575 private IllegalArgumentException unsupportedNumberType(final Number number) { 576 final String msg = String.format("Unsupported number type '%s' in number system '%s'", 577 number.getClass().getName(), 578 this.getClass().getName()); 579 580 return new IllegalArgumentException(msg); 581 } 582 583 private IllegalStateException unexpectedCodeReach() { 584 final String msg = String.format("Implementation Error: Code was reached that is expected unreachable"); 585 return new IllegalStateException(msg); 586 } 587 588 /** 589 * Whether the {@link Number}'s type can only represent integers. 590 * <p> 591 * If <code>false</code> it can also represent fractional numbers. 592 * <p> 593 * Note: this does not check whether given number represents an integer. 594 */ 595 private boolean isIntegerOnly(final Number number) { 596 return NumberType.valueOf(number).isIntegerOnly(); 597 } 598 599 /** 600 * Whether given {@link BigDecimal} has (non-zero) fractional parts. 601 * When <code>false</code>, given {@link BigDecimal} can be converted to a {@link BigInteger}. 602 * @implNote {@link BigDecimal#stripTrailingZeros()} creates a new {@link BigDecimal} just to do the check. 603 * @see https://stackoverflow.com/questions/1078953/check-if-bigdecimal-is-integer-value 604 */ 605 static boolean isFractional(final BigDecimal decimal) { 606 // check if is ZERO first 607 if(decimal.signum() == 0) { 608 return false; 609 } 610 // check if scale <= 0; if it is, then decimal definitely has no fractional parts 611 if(decimal.scale()<=0) { 612 return false; 613 } 614 // Note: this creates a new BigDecimal instance just to check for fractional parts 615 // (perhaps we can improve that in the future) 616 return decimal.stripTrailingZeros().scale() > 0; 617 } 618 619 /** 620 * Whether given {@link Number} represents an integer. 621 * Optimized for when we know the {@link NumberType} in advance. 622 */ 623 private boolean isInteger(final NumberType numberType, final Number number) { 624 if(numberType.isIntegerOnly()) { 625 return true; // numberType only allows integer 626 } 627 if(number instanceof RationalNumber) { 628 return ((RationalNumber)number).isInteger(); 629 } 630 631 // remaining types to check: Double, Float, BigDecimal ... 632 633 if(number instanceof BigDecimal) { 634 return !isFractional((BigDecimal)number); 635 } 636 if(number instanceof Double || number instanceof Float) { 637 double doubleValue = number.doubleValue(); 638 // see https://stackoverflow.com/questions/15963895/how-to-check-if-a-double-value-has-no-decimal-part 639 if (numberType.isZero(number)) return false; 640 return doubleValue % 1 == 0; 641 } 642 throw unsupportedNumberType(number); 643 } 644 645 private int bitLengthOfInteger(final Number number) { 646 if(number instanceof BigInteger) { 647 return ((BigInteger) number).bitLength(); 648 } 649 long long_value = number.longValue(); 650 651 if(long_value == Long.MIN_VALUE) { 652 return 63; 653 } else { 654 int leadingZeros = Long.numberOfLeadingZeros(Math.abs(long_value)); 655 return 64-leadingZeros; 656 } 657 } 658 659 private BigInteger integerToBigInteger(final Number number) { 660 if(number instanceof BigInteger) { 661 return (BigInteger) number; 662 } 663 return BigInteger.valueOf(number.longValue()); 664 } 665 666 private BigDecimal toBigDecimal(final Number number) { 667 if(number instanceof BigDecimal) { 668 return (BigDecimal) number; 669 } 670 if(number instanceof BigInteger) { 671 return new BigDecimal((BigInteger) number); 672 } 673 if(number instanceof Long || 674 number instanceof AtomicLong || 675 number instanceof Integer || 676 number instanceof AtomicInteger || 677 number instanceof Short || 678 number instanceof Byte) { 679 return BigDecimal.valueOf(number.longValue()); 680 } 681 if(number instanceof Double || number instanceof Float) { 682 return BigDecimal.valueOf(number.doubleValue()); 683 } 684 if(number instanceof RationalNumber) { 685 throw unexpectedCodeReach(); 686 //Note: don't do that (potential precision loss) 687 //return ((RationalNumber) number).bigDecimalValue(); 688 } 689 throw unsupportedNumberType(number); 690 } 691 692 private Number addWideAndNarrow( 693 final NumberType wideType, final Number wide, 694 final NumberType narrowType, final Number narrow) { 695 696 // avoid type-check or widening if one of the arguments is zero 697 // https://github.com/unitsofmeasurement/indriya/issues/384 698 if (wideType.isZero(wide)) { 699 return narrow; 700 } else if (narrowType.isZero(narrow)) { 701 return wide; 702 } 703 704 if(wideType.isIntegerOnly()) { 705 // at this point we know, that narrow must also be an integer-only type 706 if(wide instanceof BigInteger) { 707 return ((BigInteger) wide).add(integerToBigInteger(narrow)); 708 } 709 710 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 711 712 // +1 carry, not including sign 713 int total_bits_required = Math.max(bitLengthOfInteger(wide), bitLengthOfInteger(narrow)) + 1; 714 715 // check whether we have enough bits to store the result into a long 716 if(total_bits_required<63) { 717 return wide.longValue() + narrow.longValue(); 718 } 719 720 return integerToBigInteger(wide).add(integerToBigInteger(narrow)); 721 } 722 723 if(wide instanceof RationalNumber) { 724 725 // at this point we know, that narrow must either be rational or an integer-only type 726 if(narrow instanceof RationalNumber) { 727 return ((RationalNumber) wide).add((RationalNumber) narrow); 728 } 729 730 return ((RationalNumber) wide).add( 731 RationalNumber.ofInteger(integerToBigInteger(narrow))); 732 } 733 734 // at this point we know, that wide is one of {BigDecimal, Double, Float} 735 736 if(wide instanceof BigDecimal) { 737 738 if(narrow instanceof BigDecimal) { 739 return ((BigDecimal) wide).add((BigDecimal) narrow, Calculus.MATH_CONTEXT); 740 } 741 742 if(narrow instanceof Double || narrow instanceof Float) { 743 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 744 } 745 746 if(narrow instanceof RationalNumber) { 747 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 748 return ((BigDecimal) wide).add(((RationalNumber) narrow).bigDecimalValue()); 749 } 750 751 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 752 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.longValue())); 753 754 } 755 756 // at this point we know, that wide is one of {Double, Float} 757 758 if(narrow instanceof Double || narrow instanceof Float) { 759 //converting to BigDecimal, because especially fractional addition is sensitive to precision loss 760 return BigDecimal.valueOf(wide.doubleValue()) 761 .add(BigDecimal.valueOf(narrow.doubleValue())); 762 } 763 764 if(narrow instanceof RationalNumber) { 765 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 766 return BigDecimal.valueOf(wide.doubleValue()) 767 .add(((RationalNumber) narrow).bigDecimalValue()); 768 } 769 770 if(narrow instanceof BigInteger) { 771 return BigDecimal.valueOf(wide.doubleValue()) 772 .add(new BigDecimal((BigInteger) narrow)); 773 } 774 775 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 776 return BigDecimal.valueOf(wide.doubleValue()) 777 .add(BigDecimal.valueOf(narrow.longValue())); 778 779 } 780 781 private Number multiplyWideAndNarrow( 782 final NumberType wideType, final Number wide, 783 final NumberType narrowType, final Number narrow) { 784 785 // shortcut if any of the operands is zero. 786 if (wideType.isZero(wide) 787 || narrowType.isZero(narrow)) { 788 return 0; 789 } 790 791 if(wideType.isIntegerOnly()) { 792 // at this point we know, that narrow must also be an integer-only type 793 if(wide instanceof BigInteger) { 794 return ((BigInteger) wide).multiply(integerToBigInteger(narrow)); 795 } 796 797 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 798 799 int total_bits_required = bitLengthOfInteger(wide) + bitLengthOfInteger(narrow); // not including sign 800 801 // check whether we have enough bits to store the result into a long 802 if(total_bits_required<63) { 803 return wide.longValue() * narrow.longValue(); 804 } 805 806 return integerToBigInteger(wide).multiply(integerToBigInteger(narrow)); 807 } 808 809 if(wide instanceof RationalNumber) { 810 811 // at this point we know, that narrow must either be rational or an integer-only type 812 if(narrow instanceof RationalNumber) { 813 return ((RationalNumber) wide).multiply((RationalNumber) narrow); 814 } 815 816 return ((RationalNumber) wide).multiply( 817 RationalNumber.ofInteger(integerToBigInteger(narrow))); 818 } 819 820 // at this point we know, that wide is one of {BigDecimal, Double, Float} 821 822 if(wide instanceof BigDecimal) { 823 824 if(narrow instanceof BigDecimal) { 825 return ((BigDecimal) wide).multiply((BigDecimal) narrow, Calculus.MATH_CONTEXT); 826 } 827 828 if(narrow instanceof BigInteger) { 829 return ((BigDecimal) wide).multiply(new BigDecimal((BigInteger)narrow), Calculus.MATH_CONTEXT); 830 } 831 832 if(narrow instanceof Double || narrow instanceof Float) { 833 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 834 } 835 836 if(narrow instanceof RationalNumber) { 837 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 838 return ((BigDecimal) wide).multiply(((RationalNumber) narrow).bigDecimalValue()); 839 } 840 841 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 842 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.longValue())); 843 844 } 845 846 // at this point we know, that wide is one of {Double, Float} 847 848 if(narrow instanceof Double || narrow instanceof Float) { 849 // not converting to BigDecimal, because fractional multiplication is not sensitive to precision loss 850 return wide.doubleValue() * narrow.doubleValue(); 851 } 852 853 if(narrow instanceof RationalNumber) { 854 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 855 return BigDecimal.valueOf(wide.doubleValue()) 856 .multiply(((RationalNumber) narrow).bigDecimalValue()); 857 } 858 859 if(narrow instanceof BigInteger) { 860 return BigDecimal.valueOf(wide.doubleValue()) 861 .multiply(new BigDecimal((BigInteger) narrow)); 862 } 863 864 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 865 return BigDecimal.valueOf(wide.doubleValue()) 866 .multiply(BigDecimal.valueOf(narrow.longValue())); 867 868 } 869 870 /** 871 * @param unusedNarrowType - currently unused (but future refactoring might use it) 872 */ 873 private int compareWideVsNarrow( 874 final NumberType wideType, final Number wide, 875 final NumberType unusedNarrowType, final Number narrow) { 876 877 if(wideType.isIntegerOnly()) { 878 // at this point we know, that narrow must also be an integer-only type 879 if(wide instanceof BigInteger) { 880 return ((BigInteger) wide).compareTo(integerToBigInteger(narrow)); 881 } 882 883 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 884 return Long.compare(wide.longValue(), narrow.longValue()); 885 } 886 887 if(wide instanceof RationalNumber) { 888 889 // at this point we know, that narrow must either be rational or an integer-only type 890 if(narrow instanceof RationalNumber) { 891 return ((RationalNumber) wide).compareTo((RationalNumber) narrow); 892 } 893 894 return ((RationalNumber) wide).compareTo( 895 RationalNumber.ofInteger(integerToBigInteger(narrow))); 896 } 897 898 // at this point we know, that wide is one of {BigDecimal, Double, Float} 899 900 if(wide instanceof BigDecimal) { 901 902 if(narrow instanceof BigDecimal) { 903 return ((BigDecimal) wide).compareTo((BigDecimal) narrow); 904 } 905 906 if(narrow instanceof Double || narrow instanceof Float) { 907 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.doubleValue())); 908 } 909 910 if(narrow instanceof RationalNumber) { 911 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 912 return ((BigDecimal) wide).compareTo(((RationalNumber) narrow).bigDecimalValue()); 913 } 914 915 if (narrow instanceof BigInteger) { 916 //TODO for optimization, can this be done without instantiating a new BigDecimal? 917 return ((BigDecimal) wide).compareTo(new BigDecimal((BigInteger) narrow)); 918 } 919 920 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 921 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.longValue())); 922 923 } 924 925 // at this point we know, that wide is one of {Double, Float} 926 927 if(narrow instanceof Double || narrow instanceof Float) { 928 return Double.compare(wide.doubleValue(), narrow.doubleValue()); 929 } 930 931 if(narrow instanceof RationalNumber) { 932 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 933 return BigDecimal.valueOf(wide.doubleValue()) 934 .compareTo(((RationalNumber) narrow).bigDecimalValue()); 935 } 936 937 if(narrow instanceof BigInteger) { 938 return BigDecimal.valueOf(wide.doubleValue()) 939 .compareTo(new BigDecimal((BigInteger) narrow)); 940 } 941 942 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 943 return BigDecimal.valueOf(wide.doubleValue()) 944 .compareTo(BigDecimal.valueOf(narrow.longValue())); 945 946 } 947 948 // only for non-zero sign 949 private static BigInteger copySignTo(final int sign, final BigInteger absNumber) { 950 if(sign==-1) { 951 return absNumber.negate(); 952 } 953 return absNumber; 954 } 955 956 // only for non-zero sign 957 private static BigDecimal copySignTo(final int sign, final BigDecimal absNumber) { 958 if(sign==-1) { 959 return absNumber.negate(); 960 } 961 return absNumber; 962 } 963 964 private static Number[] applyToArray(final Number[] array, final UnaryOperator<Number> operator) { 965 // only ever used for length=2 966 return new Number[] { 967 operator.apply(array[0]), 968 operator.apply(array[1]) 969 }; 970 } 971}