001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2020, Units of Measurement project.
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.util.Objects;
035
036/**
037 * Represents a rational number {@code dividend/divisor} with {@code dividend}
038 * and {@code divisor} being integer numbers.
039 * <p>
040 * @implSpec
041 * This implementation uses {@link BigInteger} to represent 'dividend' and
042 * 'divisor'.
043 * 
044 * @author Andi Huber
045 * @author Werner Keil
046 * @version 1.2, June 21, 2019
047 * @since 2.0
048 */
049public final class RationalNumber extends Number {
050
051        private static final long serialVersionUID = 1L;
052        private final Object $lock1 = new Object[0]; // serializable lock for 'divisionResult'
053        private final Object $lock2 = new Object[0]; // serializable lock for 'longValue'
054
055        private final int signum;
056        private final BigInteger absDividend;
057        private final BigInteger absDivisor;
058        private final int hashCode;
059        private final boolean isInteger;
060
061        private transient BigDecimal divisionResult;
062        private transient Long longValue;
063
064        /**
065         * The default {@code DIVISION_CHARACTER} is ÷ which (on Windows) can by typed
066         * using Alt+ 246.
067         * <p>
068         * Note: Number parsing will fail if this is a white-space character.
069         */
070        public static char DIVISION_CHARACTER = '÷'; // Alt+ 246
071
072        public final static RationalNumber ZERO = ofInteger(BigInteger.ZERO);
073        public final static RationalNumber ONE = ofInteger(BigInteger.ONE);
074
075        /**
076         * Returns a {@code RationalNumber} with divisor <i>ONE</i>. In other words,
077         * returns a {@code RationalNumber} that represents given integer
078         * {@code number}.
079         * 
080         * @param number
081         * @return number/1
082         * @throws NullPointerException - if number is {@code null}
083         */
084        public static RationalNumber ofInteger(long number) {
085                return ofInteger(BigInteger.valueOf(number));
086        }
087
088        /**
089         * Returns a {@code RationalNumber} with divisor <i>ONE</i>. In other words,
090         * returns a {@code RationalNumber} that represents given integer
091         * {@code number}.
092         * 
093         * @param number
094         * @return number/1
095         * @throws NullPointerException - if number is {@code null}
096         */
097        public static RationalNumber ofInteger(BigInteger number) {
098                Objects.requireNonNull(number);
099                return new RationalNumber(number.signum(), number.abs(), BigInteger.ONE);
100        }
101
102        /**
103         * Returns a {@code RationalNumber} that represents the division
104         * {@code dividend/divisor}.
105         * 
106         * @param dividend
107         * @param divisor
108         * @return dividend/divisor
109         * @throws IllegalArgumentException if <code>divisor = 0</code>
110         */
111        public static RationalNumber of(long dividend, long divisor) {
112                return of(BigInteger.valueOf(dividend), BigInteger.valueOf(divisor));
113        }
114        
115        /**
116     * Returns a {@code RationalNumber} that represents the given double precision 
117     * {@code number}, with an accuracy equivalent to {@link BigDecimal#valueOf(double)}.
118     * 
119         * @param number
120         */
121        public static RationalNumber of(double number) {
122            final BigDecimal decimalValue = BigDecimal.valueOf(number);
123            return of(decimalValue);
124        }
125
126        /**
127         * Returns a {@code RationalNumber} that represents the given BigDecimal decimalValue.
128         * 
129         * @param decimalValue
130         */
131        public static RationalNumber of(BigDecimal decimalValue) {
132            Objects.requireNonNull(decimalValue);
133            
134            final int scale = decimalValue.scale();
135        
136        if(scale<=0) {
137            return ofInteger(decimalValue.toBigIntegerExact()); 
138        }
139        
140        final BigInteger dividend = decimalValue.unscaledValue();
141        final BigInteger divisor = BigInteger.TEN.pow(scale);
142        
143        return of(dividend, divisor);
144        }
145
146        /**
147         * Returns a {@code RationalNumber} that represents the division
148         * {@code dividend/divisor}.
149         * 
150         * @param dividend
151         * @param divisor
152         * @return dividend/divisor
153         * @throws IllegalArgumentException if <code>divisor = 0</code>
154         * @throws NullPointerException     - if dividend is {@code null} or divisor is
155         *                                  {@code null}
156         * 
157         * @implNote this implementation stores dividend and divisor after canceling
158         *           down from given parameters
159         */
160        public static RationalNumber of(BigInteger dividend, BigInteger divisor) {
161                Objects.requireNonNull(dividend);
162                Objects.requireNonNull(divisor);
163
164                if (BigInteger.ONE.equals(divisor)) {
165                        return ofInteger(dividend);
166                }
167
168                if (BigInteger.ZERO.equals(divisor)) {
169                        throw new IllegalArgumentException("cannot initalize a rational number with divisor equal to ZERO");
170                }
171
172                final int signumDividend = dividend.signum();
173                final int signumDivisor = divisor.signum();
174                final int signum = signumDividend * signumDivisor;
175
176                if (signum == 0) {
177                        return ZERO;
178                }
179
180                final BigInteger absDividend = dividend.abs();
181                final BigInteger absDivisor = divisor.abs();
182
183                // cancel down
184                final BigInteger gcd = absDividend.gcd(absDivisor);
185                return new RationalNumber(signum, absDividend.divide(gcd), absDivisor.divide(gcd));
186        }
187
188        // hidden constructor, that expects non-negative dividend and positive divisor,
189        // these already canceled down
190        private RationalNumber(int signum, BigInteger absDividend, BigInteger absDivisor) {
191                this.signum = signum;
192                this.absDividend = absDividend;
193                this.absDivisor = absDivisor;
194                this.hashCode = Objects.hash(signum, absDividend, absDivisor);
195                this.isInteger = BigInteger.ONE.equals(absDivisor);
196        }
197
198        /**
199         * For a non-negative rational number, returns a non-negative dividend.
200         * Otherwise returns a negative <i>dividend</i>. In other words, by convention,
201         * the integer returned includes the sign of this {@code RationalNumber},
202         * whereas @link {@link #getDivisor()} does not and is always non-negative.
203         * 
204         * @return sign(a/b) * abs(a), (given rational number a/b)
205         */
206        public BigInteger getDividend() {
207                return signum < 0 ? absDividend.negate() : absDividend;
208        }
209
210        /**
211         * By convention, returns a non-negative <i>divisor</i>.
212         * 
213         * @return abs(b), (given rational number a/b)
214         */
215        public BigInteger getDivisor() {
216                return absDivisor;
217        }
218
219        /**
220         * @return whether this {@code RationalNumber} represents an integer number
221         */
222        public boolean isInteger() {
223                return isInteger;
224        }
225
226        /**
227         * 
228         * @return the sign of this {@code RationalNumber}: -1, 0 or +1
229         */
230        public int signum() {
231                return signum;
232        }
233
234        /**
235         * @return this {@code RationalNumber} converted to {@link BigDecimal}
236         *         representation
237         * @implNote the conversion calculation is done lazily and thread-safe
238         */
239        public BigDecimal bigDecimalValue() {
240                synchronized ($lock1) {
241                        if (divisionResult == null) {
242                                divisionResult = new BigDecimal(absDividend).divide(new BigDecimal(absDivisor), Calculus.MATH_CONTEXT);
243                                if (signum < 0) {
244                                        divisionResult = divisionResult.negate();
245                                }
246                        }
247                }
248                return divisionResult;
249        }
250
251        /**
252         * Returns a new instance of {@code RationalNumber} representing the addition
253         * {@code this + that}.
254         * 
255         * @param that
256         * @return this + that
257         */
258        public RationalNumber add(RationalNumber that) {
259
260                // a/b + c/d = (ad + bc) / bd
261                BigInteger a = this.absDividend;
262                BigInteger b = this.absDivisor;
263                BigInteger c = that.absDividend;
264                BigInteger d = that.absDivisor;
265
266                if (this.signum < 0) {
267                        a = a.negate();
268                }
269                if (that.signum < 0) {
270                        c = c.negate();
271                }
272
273                return of(a.multiply(d).add(b.multiply(c)), // (ad + bc)
274                                b.multiply(d) // bd
275                );
276        }
277
278        /**
279         * Returns a new instance of {@code RationalNumber} representing the subtraction
280         * {@code this - that}.
281         * 
282         * @param that
283         * @return this - that
284         */
285        public RationalNumber subtract(RationalNumber that) {
286                return add(that.negate());
287        }
288
289        /**
290         * Returns a new instance of {@code RationalNumber} representing the
291         * multiplication {@code this * that}.
292         * 
293         * @param that
294         * @return this * that
295         */
296        public RationalNumber multiply(RationalNumber that) {
297
298                final int productSignum = this.signum * that.signum;
299                if (productSignum == 0) {
300                        return ZERO;
301                }
302
303                // a/b * c/d = ac / bd
304                final BigInteger a = this.absDividend;
305                final BigInteger b = this.absDivisor;
306                final BigInteger c = that.absDividend;
307                final BigInteger d = that.absDivisor;
308
309                final BigInteger ac = a.multiply(c);
310                final BigInteger bd = b.multiply(d);
311
312                // cancel down
313                final BigInteger gcd = ac.gcd(bd);
314
315                return new RationalNumber(productSignum, ac.divide(gcd), bd.divide(gcd));
316        }
317
318        /**
319         * Returns a new instance of {@code RationalNumber} representing the division
320         * {@code this / that}.
321         * 
322         * @param that
323         * @return this / that
324         */
325        public RationalNumber divide(RationalNumber that) {
326                return multiply(that.reciprocal());
327        }
328
329        /**
330         * Returns a new instance of {@code RationalNumber} representing the negation of
331         * {@code this}.
332         * 
333         * @return -this
334         */
335        public RationalNumber negate() {
336                return new RationalNumber(-signum, absDividend, absDivisor);
337        }
338
339        /**
340         * Returns a new instance of {@code RationalNumber} representing the reciprocal
341         * of {@code this}.
342         * 
343         * @return 1/this
344         */
345        public RationalNumber reciprocal() {
346                return new RationalNumber(signum, absDivisor, absDividend);
347        }
348
349        /**
350         * Returns a new instance of {@code RationalNumber} representing the reciprocal
351         * of {@code this}.
352         * 
353         * @param exponent
354         * @return this^exponent
355         */
356        public RationalNumber pow(int exponent) {
357                if (exponent == 0) {
358                        if (signum == 0) {
359                                throw new ArithmeticException("0^0 is not defined");
360                        }
361                        return ONE; // x^0 == 1, for any x!=0
362                }
363                if (signum == 0) {
364                        return ZERO;
365                }
366
367                final boolean isExponentEven = (exponent & 1) == 0;
368                final int newSignum;
369                if (signum < 0) {
370                        newSignum = isExponentEven ? 1 : -1;
371                } else {
372                        newSignum = 1;
373                }
374
375                if (exponent > 0) {
376                        return new RationalNumber(newSignum, absDividend.pow(exponent), absDivisor.pow(exponent));
377                } else {
378                        return new RationalNumber(newSignum, absDivisor.pow(exponent), absDividend.pow(exponent));
379                }
380
381        }
382
383        /**
384         * Returns a {@code RationalNumber} whose value is the absolute value of this
385         * {@code RationalNumber}.
386         *
387         * @return {@code abs(this)}
388         */
389        public RationalNumber abs() {
390                return signum < 0 ? new RationalNumber(1, absDividend, absDivisor) : this;
391        }
392
393        /**
394         * Compares two {@code RationalNumber} values numerically.
395         *
396         * @param that
397         * @return the value {@code 0} if {@code this} equals (numerically)
398         *         {@code that}; a value less than {@code 0} if {@code this < that}; and
399         *         a value greater than {@code 0} if {@code this > that}
400         */
401        public int compareTo(RationalNumber that) {
402
403                final int comp = Integer.compare(this.signum, that.signum);
404                if (comp != 0) {
405                        return comp;
406                }
407                if (comp == 0 && this.signum == 0) {
408                        return 0; // both are ZERO
409                }
410
411                // we have same signum
412
413                // a/b > c/d <=> ad > bc
414
415                final BigInteger a = this.absDividend;
416                final BigInteger b = this.absDivisor;
417                final BigInteger c = that.absDividend;
418                final BigInteger d = that.absDivisor;
419
420                final BigInteger ad = a.multiply(d);
421                final BigInteger bc = b.multiply(c);
422
423                final int absCompare = ad.compareTo(bc);
424
425                return this.signum > 0 ? absCompare : -absCompare;
426        }
427
428        // -- NUMBER IMPLEMENTATION
429
430        @Override
431        public int intValue() {
432                return (int) longValue();
433        }
434
435        @Override
436        public long longValue() {
437                // performance optimized version, rounding mode is FLOOR
438                // equivalent to 'bigDecimalValue().longValue()';
439                synchronized ($lock2) {
440                        if (longValue == null) {
441                                longValue = signum() < 0 ? absDividend.negate().divide(absDivisor).longValue()
442                                                : absDividend.divide(absDivisor).longValue();
443                        }
444                }
445                return longValue;
446        }
447
448        @Override
449        public float floatValue() {
450                return (float) doubleValue();
451        }
452
453        @Override
454        public double doubleValue() {
455                return bigDecimalValue().doubleValue();
456        }
457
458        /**
459         * Lay out this {@code RationalNumber} into a {@code String}.
460         *
461         * @param useFractionalRepresentation {@code true} for fractional representation {@code 5÷3}; 
462         *         {@code false} for decimal {@code 1.66667}.
463         *         
464         * @return string with canonical string representation of this
465         *         {@code RationalNumber}
466         */
467        private String layoutChars(boolean useFractionalRepresentation, char divisionCharacter) {
468                if (signum == 0) {
469                        return "0";
470                }
471                if (isInteger) {
472                        return getDividend().toString(); // already includes the sign
473                }
474                if (useFractionalRepresentation) {
475                        return getDividend().toString() + divisionCharacter + absDivisor;
476                } else {
477                        return String.valueOf(bigDecimalValue());
478                }
479        }
480
481        @Override
482        public String toString() {
483                return layoutChars(false, DIVISION_CHARACTER);
484        }
485
486        /**
487         * Returns a string representation of this {@code RationalNumber}, using
488         * fractional notation, eg. {@code 5÷3} or {@code -5÷3}.
489         *
490         * @return string representation of this {@code RationalNumber}, using
491         *         fractional notation.
492         * @since 2.0
493         */
494        public String toRationalString() {
495                return layoutChars(true, DIVISION_CHARACTER);
496        }
497        
498        /**
499     * Returns a string representation of this {@code RationalNumber}, using
500     * fractional notation, eg. {@code 5÷3} or {@code -5÷3}.
501     * 
502     * @param divisionCharacter the character to use instead of the default {@code ÷}
503     * @return string representation of this {@code RationalNumber}, using
504     *         fractional notation.
505     * @since 2.0
506     */
507        public String toRationalString(char divisionCharacter) {
508            return layoutChars(true, divisionCharacter);
509    }
510
511        @Override
512        public int hashCode() {
513                return hashCode;
514        }
515
516        /**
517     * Compares this RationalNumber with the specified Object for equality.
518     *
519     * @param  x Object to which this RationalNumber is to be compared.
520     * @return {@code true} if and only if the specified Object is a
521     *         RationalNumber whose value is numerically equal to this RationalNumber.
522     */
523        @Override
524    public boolean equals(Object x) {
525        // This test is just an optimization, which may or may not help
526        if (x == this) {
527            return true;
528        }
529
530        // no explicit null check required
531        if (!(x instanceof RationalNumber)) { 
532            return false; // will also return here if x is null 
533        }
534
535        final RationalNumber other = (RationalNumber) x;
536        
537//        // null checks not needed, since the constructor guards against dividend or divisor being null
538//        boolean result = (
539//                this.signum == other.signum &&
540//                Objects.equals(this.absDividend, other.absDividend) && 
541//                Objects.equals(this.absDivisor, other.absDivisor));
542//        return result; This is still broken
543        
544        return Objects.equals(this.bigDecimalValue(), other.bigDecimalValue());
545    }
546        
547
548}