001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2023, 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 LONG_BOXED(true, Long.class, 1L, 0L), 063 LONG_ATOMIC(true, AtomicLong.class, 1L, 0), 064 BIG_INTEGER(true, BigInteger.class, BigInteger.ONE, BigInteger.ZERO), 065 066 // rational types 067 RATIONAL(false, RationalNumber.class, RationalNumber.ONE, RationalNumber.ZERO), 068 069 // fractional types 070 FLOAT_BOXED(false, Float.class, 1.f, 0.f), 071 DOUBLE_BOXED(false, Double.class, 1.d, 0.d), 072 BIG_DECIMAL(false, BigDecimal.class, BigDecimal.ONE, BigDecimal.ZERO), 073 074 ; 075 private final boolean integerOnly; 076 private final Class<? extends Number> type; 077 private final Number one; 078 private final Number zero; 079 080 private NumberType(boolean integerOnly, Class<? extends Number> type, 081 Number one, Number zero) { 082 083 this.integerOnly = integerOnly; 084 this.type = type; 085 this.one = one; 086 this.zero = zero; 087 } 088 089 public boolean isIntegerOnly() { 090 return integerOnly; 091 } 092 093 @SuppressWarnings("unused") 094 public Class<? extends Number> getType() { 095 return type; 096 } 097 098 // 'hardcoded' for performance reasons 099 static NumberType valueOf(Number number) { 100 if(number instanceof Long) { 101 return LONG_BOXED; 102 } 103 if(number instanceof AtomicLong) { 104 return LONG_ATOMIC; 105 } 106 if(number instanceof Integer) { 107 return INTEGER_BOXED; 108 } 109 if(number instanceof AtomicInteger) { 110 return INTEGER_ATOMIC; 111 } 112 if(number instanceof Double) { 113 return DOUBLE_BOXED; 114 } 115 if(number instanceof Short) { 116 return SHORT_BOXED; 117 } 118 if(number instanceof Byte) { 119 return BYTE_BOXED; 120 } 121 if(number instanceof Float) { 122 return FLOAT_BOXED; 123 } 124 if(number instanceof BigDecimal) { 125 return BIG_DECIMAL; 126 } 127 if(number instanceof BigInteger) { 128 return BIG_INTEGER; 129 } 130 if(number instanceof RationalNumber) { 131 return RATIONAL; 132 } 133 final String msg = String.format("Unsupported number type '%s'", 134 number.getClass().getName()); 135 throw new IllegalArgumentException(msg); 136 } 137 138 } 139 140 @Override 141 public Number add(Number x, Number y) { 142 143 final NumberType type_x = NumberType.valueOf(x); 144 final NumberType type_y = NumberType.valueOf(y); 145 146 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 147 148 return reorder_args 149 ? addWideAndNarrow(type_y, y, type_x, x) 150 : addWideAndNarrow(type_x, x, type_y, y); 151 } 152 153 @Override 154 public Number subtract(Number x, Number y) { 155 return add(x, negate(y)); 156 } 157 158 @Override 159 public Number multiply(Number x, Number y) { 160 161 final NumberType type_x = NumberType.valueOf(x); 162 final NumberType type_y = NumberType.valueOf(y); 163 164 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 165 166 return reorder_args 167 ? multiplyWideAndNarrow(type_y, y, type_x, x) 168 : multiplyWideAndNarrow(type_x, x, type_y, y); 169 } 170 171 @Override 172 public Number divide(Number x, Number y) { 173 return multiply(x, reciprocal(y)); 174 } 175 176 @Override 177 public Number[] divideAndRemainder(Number x, Number y, boolean roundRemainderTowardsZero) { 178 179 final int sign_x = signum(x); 180 final int sign_y = signum(y); 181 182 final int sign = sign_x * sign_y; 183 // handle corner cases when x or y are zero 184 if(sign == 0) { 185 if(sign_y == 0) { 186 throw new ArithmeticException("division by zero"); 187 } 188 if(sign_x==0) { 189 return new Number[] {0, 0}; 190 } 191 } 192 193 final Number absX = abs(x); 194 final Number absY = abs(y); 195 196 final NumberType type_x = NumberType.valueOf(absX); 197 final NumberType type_y = NumberType.valueOf(absY); 198 199 // if x and y are both integer types than we can calculate integer results, 200 // otherwise we resort to BigDecimal 201 final boolean yieldIntegerResult = type_x.isIntegerOnly() && type_y.isIntegerOnly(); 202 203 if(yieldIntegerResult) { 204 205 final BigInteger integer_x = integerToBigInteger(absX); 206 final BigInteger integer_y = integerToBigInteger(absY); 207 208 final BigInteger[] divAndRemainder = integer_x.divideAndRemainder(integer_y); 209 210 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigInteger)number)); 211 212 } else { 213 214 final MathContext mathContext = 215 new MathContext(Calculus.MATH_CONTEXT.getPrecision(), RoundingMode.FLOOR); 216 217 final BigDecimal decimal_x = (type_x == NumberType.RATIONAL) 218 ? ((RationalNumber) absX).bigDecimalValue() 219 : toBigDecimal(absX); 220 final BigDecimal decimal_y = (type_y == NumberType.RATIONAL) 221 ? ((RationalNumber) absY).bigDecimalValue() 222 : toBigDecimal(absY); 223 224 final BigDecimal[] divAndRemainder = decimal_x.divideAndRemainder(decimal_y, mathContext); 225 226 if(roundRemainderTowardsZero) { 227 return new Number[] { 228 copySignTo(sign, divAndRemainder[0]), 229 copySignTo(sign, divAndRemainder[1].toBigInteger())}; 230 231 } else { 232 return applyToArray(divAndRemainder, number->copySignTo(sign, (BigDecimal)number)); 233 } 234 235 } 236 237 } 238 239 @Override 240 public Number reciprocal(Number number) { 241 if(isIntegerOnly(number)) { 242 return RationalNumber.of(BigInteger.ONE, integerToBigInteger(number)); 243 } 244 if(number instanceof BigDecimal) { 245 return RationalNumber.of((BigDecimal) number).reciprocal(); 246 } 247 if(number instanceof RationalNumber) { 248 return ((RationalNumber) number).reciprocal(); 249 } 250 if(number instanceof Double) { 251 return RationalNumber.of((double)number).reciprocal(); 252 } 253 if(number instanceof Float) { 254 return RationalNumber.of(number.doubleValue()).reciprocal(); 255 } 256 throw unsupportedNumberType(number); 257 } 258 259 @Override 260 public int signum(Number number) { 261 if(number instanceof BigInteger) { 262 return ((BigInteger) number).signum(); 263 } 264 if(number instanceof BigDecimal) { 265 return ((BigDecimal) number).signum(); 266 } 267 if(number instanceof RationalNumber) { 268 return ((RationalNumber) number).signum(); 269 } 270 if(number instanceof Double) { 271 return (int)Math.signum((double)number); 272 } 273 if(number instanceof Float) { 274 return (int)Math.signum((float)number); 275 } 276 if(number instanceof Long || number instanceof AtomicLong) { 277 final long longValue = number.longValue(); 278 return Long.signum(longValue); 279 } 280 if(number instanceof Integer || number instanceof AtomicInteger || 281 number instanceof Short || number instanceof Byte) { 282 final int intValue = number.intValue(); 283 return Integer.signum(intValue); 284 } 285 throw unsupportedNumberType(number); 286 } 287 288 @Override 289 public Number abs(Number number) { 290 if(number instanceof BigInteger) { 291 return ((BigInteger) number).abs(); 292 } 293 if(number instanceof BigDecimal) { 294 return ((BigDecimal) number).abs(); 295 } 296 if(number instanceof RationalNumber) { 297 return ((RationalNumber) number).abs(); 298 } 299 if(number instanceof Double) { 300 return Math.abs((double)number); 301 } 302 if(number instanceof Float) { 303 return Math.abs((float)number); 304 } 305 if(number instanceof Long || number instanceof AtomicLong) { 306 final long longValue = number.longValue(); 307 if(longValue == Long.MIN_VALUE) { 308 return BigInteger.valueOf(longValue).abs(); // widen to BigInteger 309 } 310 return Math.abs(longValue); 311 } 312 if(number instanceof Integer || number instanceof AtomicInteger) { 313 final int intValue = number.intValue(); 314 if(intValue == Integer.MIN_VALUE) { 315 return Math.abs(number.longValue()); // widen to long 316 } 317 return Math.abs(intValue); 318 } 319 if(number instanceof Short || number instanceof Byte) { 320 Math.abs(number.intValue()); // widen to int 321 } 322 throw unsupportedNumberType(number); 323 } 324 325 @Override 326 public Number negate(Number number) { 327 if(number instanceof BigInteger) { 328 return ((BigInteger) number).negate(); 329 } 330 if(number instanceof BigDecimal) { 331 return ((BigDecimal) number).negate(); 332 } 333 if(number instanceof RationalNumber) { 334 return ((RationalNumber) number).negate(); 335 } 336 if(number instanceof Double) { 337 return -((double)number); 338 } 339 if(number instanceof Float) { 340 return -((float)number); 341 } 342 if(number instanceof Long || number instanceof AtomicLong) { 343 final long longValue = number.longValue(); 344 if(longValue == Long.MIN_VALUE) { 345 return BigInteger.valueOf(longValue).negate(); // widen to BigInteger 346 } 347 return -longValue; 348 } 349 if(number instanceof Integer || number instanceof AtomicInteger) { 350 final int intValue = number.intValue(); 351 if(intValue == Integer.MIN_VALUE) { 352 return -number.longValue(); // widen to long 353 } 354 return -intValue; 355 } 356 if(number instanceof Short) { 357 final short shortValue = (short)number; 358 if(shortValue == Short.MIN_VALUE) { 359 return -number.intValue(); // widen to int 360 } 361 return -shortValue; 362 } 363 if(number instanceof Byte) { 364 final short byteValue = (byte)number; 365 if(byteValue == Byte.MIN_VALUE) { 366 return -number.intValue(); // widen to int 367 } 368 return -byteValue; 369 } 370 throw unsupportedNumberType(number); 371 } 372 373 @Override 374 public Number power(Number number, int exponent) { 375 if(exponent==0) { 376 if(isZero(number)) { 377 throw new ArithmeticException("0^0 is not defined"); 378 } 379 return 1; // x^0 == 1, for any x!=0 380 } 381 if(exponent==1) { 382 return number; // x^1 == x, for any x 383 } 384 if(number instanceof BigInteger || 385 number instanceof Long || number instanceof AtomicLong || 386 number instanceof Integer || number instanceof AtomicInteger || 387 number instanceof Short || number instanceof Byte) { 388 final BigInteger bigInt = integerToBigInteger(number); 389 if(exponent>0) { 390 return bigInt.pow(exponent); 391 } 392 return RationalNumber.ofInteger(bigInt).pow(exponent); 393 394 } 395 if(number instanceof BigDecimal) { 396 return ((BigDecimal) number).pow(exponent, Calculus.MATH_CONTEXT); 397 } 398 if(number instanceof RationalNumber) { 399 ((RationalNumber) number).pow(exponent); 400 } 401 if(number instanceof Double || number instanceof Float) { 402 return toBigDecimal(number).pow(exponent, Calculus.MATH_CONTEXT); 403 } 404 throw unsupportedNumberType(number); 405 } 406 407 @Override 408 public Number exp(Number number) { 409 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 410 return Math.exp(number.doubleValue()); 411 } 412 413 @Override 414 public Number log(Number number) { 415 //TODO[220] this is a poor implementation, certainly we can do better using BigDecimal 416 return Math.log(number.doubleValue()); 417 } 418 419 @Override 420 public Number narrow(Number number) { 421 422 //Implementation Note: for performance we stop narrowing down at 'double' or 'integer' level 423 424 if(number instanceof Integer || number instanceof AtomicInteger || 425 number instanceof Short || number instanceof Byte) { 426 return number; 427 } 428 429 if(number instanceof Double || number instanceof Float) { 430 final double doubleValue = number.doubleValue(); 431 if(!Double.isFinite(doubleValue)) { 432 throw unsupportedNumberValue(doubleValue); 433 } 434 if(doubleValue % 1 == 0 && !isZero(number)) { 435 // double represents an integer other than zero 436 return narrow(BigDecimal.valueOf(doubleValue)); 437 } 438 return number; 439 } 440 441 if(isIntegerOnly(number)) { 442 443 // number is one of {BigInteger, Long} 444 445 final int total_bits_required = bitLengthOfInteger(number); 446 447 // check whether we have enough bits to store the result into an int 448 if(total_bits_required<31) { 449 return number.intValue(); 450 } 451 452 // check whether we have enough bits to store the result into a long 453 if(total_bits_required<63) { 454 return number.longValue(); 455 } 456 457 return number; // cannot narrow down 458 459 } 460 461 if(number instanceof BigDecimal) { 462 463 final BigDecimal decimal = ((BigDecimal) number); 464 try { 465 BigInteger integer = decimal.toBigIntegerExact(); 466 return narrow(integer); 467 } catch (ArithmeticException e) { 468 return number; // cannot narrow to integer 469 } 470 } 471 472 if(number instanceof RationalNumber) { 473 474 final RationalNumber rational = ((RationalNumber) number); 475 476 return rational.isInteger() 477 ? narrow(rational.getDividend()) // divisor is ONE 478 : number; // cannot narrow to integer; 479 } 480 481 // for any other number type just do nothing 482 return number; 483 } 484 485 @Override 486 public int compare(Number x, Number y) { 487 488 final NumberType type_x = NumberType.valueOf(x); 489 final NumberType type_y = NumberType.valueOf(y); 490 491 final boolean reorder_args = type_y.ordinal()>type_x.ordinal(); 492 493 return reorder_args 494 ? -compareWideVsNarrow(type_y, y, type_x, x) 495 : compareWideVsNarrow(type_x, x, type_y, y); 496 } 497 498 @Override 499 public boolean isZero(Number number) { 500 NumberType numberType = NumberType.valueOf(number); 501 return compare(numberType.zero, number) == 0; 502 } 503 504 @Override 505 public boolean isOne(Number number) { 506 NumberType numberType = NumberType.valueOf(number); 507 return compare(numberType.one, number) == 0; 508 } 509 510 @Override 511 public boolean isLessThanOne(Number number) { 512 NumberType numberType = NumberType.valueOf(number); 513 return compare(numberType.one, number) > 0; 514 } 515 516 @Override 517 public boolean isInteger(Number number) { 518 NumberType numberType = NumberType.valueOf(number); 519 return isInteger(numberType, number); 520 } 521 522 523 // -- HELPER 524 525 private IllegalArgumentException unsupportedNumberValue(Number number) { 526 final String msg = String.format("Unsupported number value '%s' of type '%s' in number system '%s'", 527 "" + number, 528 number.getClass(), 529 this.getClass().getName()); 530 531 return new IllegalArgumentException(msg); 532 } 533 534 private IllegalArgumentException unsupportedNumberType(Number number) { 535 final String msg = String.format("Unsupported number type '%s' in number system '%s'", 536 number.getClass().getName(), 537 this.getClass().getName()); 538 539 return new IllegalArgumentException(msg); 540 } 541 542 private IllegalStateException unexpectedCodeReach() { 543 final String msg = String.format("Implementation Error: Code was reached that is expected unreachable"); 544 return new IllegalStateException(msg); 545 } 546 547 private boolean isIntegerOnly(Number number) { 548 return NumberType.valueOf(number).isIntegerOnly(); 549 } 550 551 private boolean isInteger(NumberType numberType, Number number) { 552 if(numberType.isIntegerOnly()) { 553 return true; // numberType only allows integer 554 } 555 if(number instanceof RationalNumber) { 556 return ((RationalNumber)number).isInteger(); 557 } 558 559 // remaining types to check: Double, Float, BigDecimal ... 560 561 if(number instanceof BigDecimal) { 562 final BigDecimal decimal = (BigDecimal)number; 563 // see https://stackoverflow.com/questions/1078953/check-if-bigdecimal-is-integer-value 564 if(decimal.scale()<=0) { 565 return true; 566 } 567 try { 568 decimal.toBigIntegerExact(); 569 return true; 570 } catch (ArithmeticException ex) { 571 return false; 572 } 573 } 574 if(number instanceof Double || number instanceof Float) { 575 double doubleValue = number.doubleValue(); 576 // see https://stackoverflow.com/questions/15963895/how-to-check-if-a-double-value-has-no-decimal-part 577 if (isZero(number)) return false; 578 return doubleValue % 1 == 0; 579 } 580 throw unsupportedNumberType(number); 581 } 582 583 private int bitLengthOfInteger(Number number) { 584 if(number instanceof BigInteger) { 585 return ((BigInteger) number).bitLength(); 586 } 587 long long_value = number.longValue(); 588 589 if(long_value == Long.MIN_VALUE) { 590 return 63; 591 } else { 592 int leadingZeros = Long.numberOfLeadingZeros(Math.abs(long_value)); 593 return 64-leadingZeros; 594 } 595 } 596 597 private BigInteger integerToBigInteger(Number number) { 598 if(number instanceof BigInteger) { 599 return (BigInteger) number; 600 } 601 return BigInteger.valueOf(number.longValue()); 602 } 603 604 private BigDecimal toBigDecimal(Number number) { 605 if(number instanceof BigDecimal) { 606 return (BigDecimal) number; 607 } 608 if(number instanceof BigInteger) { 609 return new BigDecimal((BigInteger) number); 610 } 611 if(number instanceof Long || 612 number instanceof AtomicLong || 613 number instanceof Integer || 614 number instanceof AtomicInteger || 615 number instanceof Short || 616 number instanceof Byte) { 617 return BigDecimal.valueOf(number.longValue()); 618 } 619 if(number instanceof Double || number instanceof Float) { 620 return BigDecimal.valueOf(number.doubleValue()); 621 } 622 if(number instanceof RationalNumber) { 623 throw unexpectedCodeReach(); 624 //Note: don't do that (potential precision loss) 625 //return ((RationalNumber) number).bigDecimalValue(); 626 } 627 throw unsupportedNumberType(number); 628 } 629 630 private Number addWideAndNarrow( 631 NumberType wideType, Number wide, 632 NumberType narrowType, Number narrow) { 633 634 // avoid type-check or widening if one of the arguments is zero 635 // https://github.com/unitsofmeasurement/indriya/issues/384 636 if (isZero(wide)) { 637 return narrow; 638 } else if (isZero(narrow)) { 639 return wide; 640 } 641 642 if(wideType.isIntegerOnly()) { 643 // at this point we know, that narrow must also be an integer-only type 644 if(wide instanceof BigInteger) { 645 return ((BigInteger) wide).add(integerToBigInteger(narrow)); 646 } 647 648 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 649 650 // +1 carry, not including sign 651 int total_bits_required = Math.max(bitLengthOfInteger(wide), bitLengthOfInteger(narrow)) + 1; 652 653 // check whether we have enough bits to store the result into a long 654 if(total_bits_required<63) { 655 return wide.longValue() + narrow.longValue(); 656 } 657 658 return integerToBigInteger(wide).add(integerToBigInteger(narrow)); 659 } 660 661 if(wide instanceof RationalNumber) { 662 663 // at this point we know, that narrow must either be rational or an integer-only type 664 if(narrow instanceof RationalNumber) { 665 return ((RationalNumber) wide).add((RationalNumber) narrow); 666 } 667 668 return ((RationalNumber) wide).add( 669 RationalNumber.ofInteger(integerToBigInteger(narrow))); 670 } 671 672 // at this point we know, that wide is one of {BigDecimal, Double, Float} 673 674 if(wide instanceof BigDecimal) { 675 676 if(narrow instanceof BigDecimal) { 677 return ((BigDecimal) wide).add((BigDecimal) narrow, Calculus.MATH_CONTEXT); 678 } 679 680 if(narrow instanceof Double || narrow instanceof Float) { 681 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 682 } 683 684 if(narrow instanceof RationalNumber) { 685 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 686 return ((BigDecimal) wide).add(((RationalNumber) narrow).bigDecimalValue()); 687 } 688 689 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 690 return ((BigDecimal) wide).add(BigDecimal.valueOf(narrow.longValue())); 691 692 } 693 694 // at this point we know, that wide is one of {Double, Float} 695 696 if(narrow instanceof Double || narrow instanceof Float) { 697 //converting to BigDecimal, because especially fractional addition is sensitive to precision loss 698 return BigDecimal.valueOf(wide.doubleValue()) 699 .add(BigDecimal.valueOf(narrow.doubleValue())); 700 } 701 702 if(narrow instanceof RationalNumber) { 703 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 704 return BigDecimal.valueOf(wide.doubleValue()) 705 .add(((RationalNumber) narrow).bigDecimalValue()); 706 } 707 708 if(narrow instanceof BigInteger) { 709 return BigDecimal.valueOf(wide.doubleValue()) 710 .add(new BigDecimal((BigInteger) narrow)); 711 } 712 713 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 714 return BigDecimal.valueOf(wide.doubleValue()) 715 .add(BigDecimal.valueOf(narrow.longValue())); 716 717 } 718 719 private Number multiplyWideAndNarrow( 720 NumberType wideType, Number wide, 721 NumberType narrowType, Number narrow) { 722 723 if(wideType.isIntegerOnly()) { 724 // at this point we know, that narrow must also be an integer-only type 725 if(wide instanceof BigInteger) { 726 return ((BigInteger) wide).multiply(integerToBigInteger(narrow)); 727 } 728 729 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 730 731 int total_bits_required = bitLengthOfInteger(wide) + bitLengthOfInteger(narrow); // not including sign 732 733 // check whether we have enough bits to store the result into a long 734 if(total_bits_required<63) { 735 return wide.longValue() * narrow.longValue(); 736 } 737 738 return integerToBigInteger(wide).multiply(integerToBigInteger(narrow)); 739 } 740 741 if(wide instanceof RationalNumber) { 742 743 // at this point we know, that narrow must either be rational or an integer-only type 744 if(narrow instanceof RationalNumber) { 745 return ((RationalNumber) wide).multiply((RationalNumber) narrow); 746 } 747 748 return ((RationalNumber) wide).multiply( 749 RationalNumber.ofInteger(integerToBigInteger(narrow))); 750 } 751 752 // at this point we know, that wide is one of {BigDecimal, Double, Float} 753 754 if(wide instanceof BigDecimal) { 755 756 if(narrow instanceof BigDecimal) { 757 return ((BigDecimal) wide).multiply((BigDecimal) narrow, Calculus.MATH_CONTEXT); 758 } 759 760 if(narrow instanceof BigInteger) { 761 return ((BigDecimal) wide).multiply(new BigDecimal((BigInteger)narrow), Calculus.MATH_CONTEXT); 762 } 763 764 if(narrow instanceof Double || narrow instanceof Float) { 765 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.doubleValue()), Calculus.MATH_CONTEXT); 766 } 767 768 if(narrow instanceof RationalNumber) { 769 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 770 return ((BigDecimal) wide).multiply(((RationalNumber) narrow).bigDecimalValue()); 771 } 772 773 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 774 return ((BigDecimal) wide).multiply(BigDecimal.valueOf(narrow.longValue())); 775 776 } 777 778 // at this point we know, that wide is one of {Double, Float} 779 780 if(narrow instanceof Double || narrow instanceof Float) { 781 // not converting to BigDecimal, because fractional multiplication is not sensitive to precision loss 782 return wide.doubleValue() * narrow.doubleValue(); 783 } 784 785 if(narrow instanceof RationalNumber) { 786 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 787 return BigDecimal.valueOf(wide.doubleValue()) 788 .multiply(((RationalNumber) narrow).bigDecimalValue()); 789 } 790 791 if(narrow instanceof BigInteger) { 792 return BigDecimal.valueOf(wide.doubleValue()) 793 .multiply(new BigDecimal((BigInteger) narrow)); 794 } 795 796 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 797 return BigDecimal.valueOf(wide.doubleValue()) 798 .multiply(BigDecimal.valueOf(narrow.longValue())); 799 800 } 801 802 803 private int compareWideVsNarrow( 804 NumberType wideType, Number wide, 805 NumberType narrowType, Number narrow) { 806 807 808 if(wideType.isIntegerOnly()) { 809 // at this point we know, that narrow must also be an integer-only type 810 if(wide instanceof BigInteger) { 811 return ((BigInteger) wide).compareTo(integerToBigInteger(narrow)); 812 } 813 814 // at this point we know, that 'wide' and 'narrow' are one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 815 return Long.compare(wide.longValue(), narrow.longValue()); 816 } 817 818 if(wide instanceof RationalNumber) { 819 820 // at this point we know, that narrow must either be rational or an integer-only type 821 if(narrow instanceof RationalNumber) { 822 return ((RationalNumber) wide).compareTo((RationalNumber) narrow); 823 } 824 825 return ((RationalNumber) wide).compareTo( 826 RationalNumber.ofInteger(integerToBigInteger(narrow))); 827 } 828 829 // at this point we know, that wide is one of {BigDecimal, Double, Float} 830 831 if(wide instanceof BigDecimal) { 832 833 if(narrow instanceof BigDecimal) { 834 return ((BigDecimal) wide).compareTo((BigDecimal) narrow); 835 } 836 837 if(narrow instanceof Double || narrow instanceof Float) { 838 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.doubleValue())); 839 } 840 841 if(narrow instanceof RationalNumber) { 842 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 843 return ((BigDecimal) wide).compareTo(((RationalNumber) narrow).bigDecimalValue()); 844 } 845 846 if (narrow instanceof BigInteger) { 847 //TODO for optimization, can this be done without instantiating a new BigDecimal? 848 return ((BigDecimal) wide).compareTo(new BigDecimal((BigInteger) narrow)); 849 } 850 851 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 852 return ((BigDecimal) wide).compareTo(BigDecimal.valueOf(narrow.longValue())); 853 854 } 855 856 // at this point we know, that wide is one of {Double, Float} 857 858 if(narrow instanceof Double || narrow instanceof Float) { 859 return Double.compare(wide.doubleValue(), narrow.doubleValue()); 860 } 861 862 if(narrow instanceof RationalNumber) { 863 //TODO[220] can we do better than that, eg. by converting BigDecimal to RationalNumber 864 return BigDecimal.valueOf(wide.doubleValue()) 865 .compareTo(((RationalNumber) narrow).bigDecimalValue()); 866 } 867 868 if(narrow instanceof BigInteger) { 869 return BigDecimal.valueOf(wide.doubleValue()) 870 .compareTo(new BigDecimal((BigInteger) narrow)); 871 } 872 873 // at this point we know, that 'narrow' is one of {(Atomic)Long, (Atomic)Integer, Short, Byte} 874 return BigDecimal.valueOf(wide.doubleValue()) 875 .compareTo(BigDecimal.valueOf(narrow.longValue())); 876 877 } 878 879 // only for non-zero sign 880 private static BigInteger copySignTo(int sign, BigInteger absNumber) { 881 if(sign==-1) { 882 return absNumber.negate(); 883 } 884 return absNumber; 885 } 886 887 // only for non-zero sign 888 private static BigDecimal copySignTo(int sign, BigDecimal absNumber) { 889 if(sign==-1) { 890 return absNumber.negate(); 891 } 892 return absNumber; 893 } 894 895 private static Number[] applyToArray(Number[] array, UnaryOperator<Number> operator) { 896 // only ever used for length=2 897 return new Number[] { 898 operator.apply(array[0]), 899 operator.apply(array[1]) 900 }; 901 } 902}