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}