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.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         * <dl>
151     * <dt><span class="strong">Implementation Note:</span></dt><dd>this implementation stores dividend and divisor after canceling
152         *           down from given parameters</dd>
153     * </dl>
154         * 
155         * @param dividend the dividend
156         * @param divisor the divisor
157         * @return dividend/divisor
158         * @throws IllegalArgumentException if <code>divisor = 0</code>
159         * @throws NullPointerException     - if dividend is {@code null} or divisor is
160         *                                  {@code null}
161         */
162        public static RationalNumber of(BigInteger dividend, BigInteger divisor) {
163                Objects.requireNonNull(dividend);
164                Objects.requireNonNull(divisor);
165
166                if (BigInteger.ONE.equals(divisor)) {
167                        return ofInteger(dividend);
168                }
169
170                if (BigInteger.ZERO.equals(divisor)) {
171                        throw new IllegalArgumentException("cannot initalize a rational number with divisor equal to ZERO");
172                }
173
174                final int signumDividend = dividend.signum();
175                final int signumDivisor = divisor.signum();
176                final int signum = signumDividend * signumDivisor;
177
178                if (signum == 0) {
179                        return ZERO;
180                }
181
182                final BigInteger absDividend = dividend.abs();
183                final BigInteger absDivisor = divisor.abs();
184
185                // cancel down
186                final BigInteger gcd = absDividend.gcd(absDivisor);
187                return new RationalNumber(signum, absDividend.divide(gcd), absDivisor.divide(gcd));
188        }
189
190        // hidden constructor, that expects non-negative dividend and positive divisor,
191        // these already canceled down
192        private RationalNumber(int signum, BigInteger absDividend, BigInteger absDivisor) {
193                this.signum = signum;
194                this.absDividend = absDividend;
195                this.absDivisor = absDivisor;
196                this.hashCode = Objects.hash(signum, absDividend, absDivisor);
197                this.isInteger = BigInteger.ONE.equals(absDivisor);
198        }
199
200        /**
201         * For a non-negative rational number, returns a non-negative dividend.
202         * Otherwise returns a negative <i>dividend</i>. In other words, by convention,
203         * the integer returned includes the sign of this {@code RationalNumber},
204         * whereas @link {@link #getDivisor()} does not and is always non-negative.
205         * 
206         * @return sign(a/b) * abs(a), (given rational number a/b)
207         */
208        public BigInteger getDividend() {
209                return signum < 0 ? absDividend.negate() : absDividend;
210        }
211
212        /**
213         * By convention, returns a non-negative <i>divisor</i>.
214         * 
215         * @return abs(b), (given rational number a/b)
216         */
217        public BigInteger getDivisor() {
218                return absDivisor;
219        }
220
221        /**
222         * @return whether this {@code RationalNumber} represents an integer number
223         */
224        public boolean isInteger() {
225                return isInteger;
226        }
227
228        /**
229         * 
230         * @return the sign of this {@code RationalNumber}: -1, 0 or +1
231         */
232        public int signum() {
233                return signum;
234        }
235
236        /**
237         * The {@link BigDecimal} representation of this {@code RationalNumber}.
238         * <dl>
239     * <dt><span class="strong">Implementation Note:</span></dt><dd>the conversion calculation is done lazily and thread-safe</dd>           
240     * </dl>
241     * @return this {@code RationalNumber} converted to {@link BigDecimal}
242         *         representation 
243         */
244        public BigDecimal bigDecimalValue() {
245                synchronized ($lock1) {
246                        if (divisionResult == null) {
247                                divisionResult = new BigDecimal(absDividend).divide(new BigDecimal(absDivisor), Calculus.MATH_CONTEXT);
248                                if (signum < 0) {
249                                        divisionResult = divisionResult.negate();
250                                }
251                        }
252                }
253                return divisionResult;
254        }
255
256        /**
257         * Returns a new instance of {@code RationalNumber} representing the addition
258         * {@code this + that}.
259         * 
260         * @param that
261         * @return this + that
262         */
263        public RationalNumber add(RationalNumber that) {
264
265                // a/b + c/d = (ad + bc) / bd
266                BigInteger a = this.absDividend;
267                BigInteger b = this.absDivisor;
268                BigInteger c = that.absDividend;
269                BigInteger d = that.absDivisor;
270
271                if (this.signum < 0) {
272                        a = a.negate();
273                }
274                if (that.signum < 0) {
275                        c = c.negate();
276                }
277
278                return of(a.multiply(d).add(b.multiply(c)), // (ad + bc)
279                                b.multiply(d) // bd
280                );
281        }
282
283        /**
284         * Returns a new instance of {@code RationalNumber} representing the subtraction
285         * {@code this - that}.
286         * 
287         * @param that
288         * @return this - that
289         */
290        public RationalNumber subtract(RationalNumber that) {
291                return add(that.negate());
292        }
293
294        /**
295         * Returns a new instance of {@code RationalNumber} representing the
296         * multiplication {@code this * that}.
297         * 
298         * @param that
299         * @return this * that
300         */
301        public RationalNumber multiply(RationalNumber that) {
302
303                final int productSignum = this.signum * that.signum;
304                if (productSignum == 0) {
305                        return ZERO;
306                }
307
308                // a/b * c/d = ac / bd
309                final BigInteger a = this.absDividend;
310                final BigInteger b = this.absDivisor;
311                final BigInteger c = that.absDividend;
312                final BigInteger d = that.absDivisor;
313
314                final BigInteger ac = a.multiply(c);
315                final BigInteger bd = b.multiply(d);
316
317                // cancel down
318                final BigInteger gcd = ac.gcd(bd);
319
320                return new RationalNumber(productSignum, ac.divide(gcd), bd.divide(gcd));
321        }
322
323        /**
324         * Returns a new instance of {@code RationalNumber} representing the division
325         * {@code this / that}.
326         * 
327         * @param that
328         * @return this / that
329         */
330        public RationalNumber divide(RationalNumber that) {
331                return multiply(that.reciprocal());
332        }
333
334        /**
335         * Returns a new instance of {@code RationalNumber} representing the negation of
336         * {@code this}.
337         * 
338         * @return -this
339         */
340        public RationalNumber negate() {
341                return new RationalNumber(-signum, absDividend, absDivisor);
342        }
343
344        /**
345         * Returns a new instance of {@code RationalNumber} representing the reciprocal
346         * of {@code this}.
347         * 
348         * @return 1/this
349         */
350        public RationalNumber reciprocal() {
351                return new RationalNumber(signum, absDivisor, absDividend);
352        }
353
354        /**
355         * Returns a new instance of {@code RationalNumber} representing the reciprocal
356         * of {@code this}.
357         * 
358         * @param exponent
359         * @return this^exponent
360         */
361        public RationalNumber pow(int exponent) {
362                if (exponent == 0) {
363                        if (signum == 0) {
364                                throw new ArithmeticException("0^0 is not defined");
365                        }
366                        return ONE; // x^0 == 1, for any x!=0
367                }
368                if (signum == 0) {
369                        return ZERO;
370                }
371
372                final boolean isExponentEven = (exponent & 1) == 0;
373                final int newSignum;
374                if (signum < 0) {
375                        newSignum = isExponentEven ? 1 : -1;
376                } else {
377                        newSignum = 1;
378                }
379
380                if (exponent > 0) {
381                        return new RationalNumber(newSignum, absDividend.pow(exponent), absDivisor.pow(exponent));
382                } else {
383                        return new RationalNumber(newSignum, absDivisor.pow(exponent), absDividend.pow(exponent));
384                }
385
386        }
387
388        /**
389         * Returns a {@code RationalNumber} whose value is the absolute value of this
390         * {@code RationalNumber}.
391         *
392         * @return {@code abs(this)}
393         */
394        public RationalNumber abs() {
395                return signum < 0 ? new RationalNumber(1, absDividend, absDivisor) : this;
396        }
397
398        /**
399         * Compares two {@code RationalNumber} values numerically.
400         *
401         * @param that
402         * @return the value {@code 0} if {@code this} equals (numerically)
403         *         {@code that}; a value less than {@code 0} if {@code this < that}; and
404         *         a value greater than {@code 0} if {@code this > that}
405         */
406        public int compareTo(RationalNumber that) {
407
408                final int comp = Integer.compare(this.signum, that.signum);
409                if (comp != 0) {
410                        return comp;
411                }
412                if (comp == 0 && this.signum == 0) {
413                        return 0; // both are ZERO
414                }
415
416                // we have same signum
417
418                // a/b > c/d <=> ad > bc
419
420                final BigInteger a = this.absDividend;
421                final BigInteger b = this.absDivisor;
422                final BigInteger c = that.absDividend;
423                final BigInteger d = that.absDivisor;
424
425                final BigInteger ad = a.multiply(d);
426                final BigInteger bc = b.multiply(c);
427
428                final int absCompare = ad.compareTo(bc);
429
430                return this.signum > 0 ? absCompare : -absCompare;
431        }
432
433        // -- NUMBER IMPLEMENTATION
434
435        @Override
436        public int intValue() {
437                return (int) longValue();
438        }
439
440        @Override
441        public long longValue() {
442                // performance optimized version, rounding mode is FLOOR
443                // equivalent to 'bigDecimalValue().longValue()';
444                synchronized ($lock2) {
445                        if (longValue == null) {
446                                longValue = signum() < 0 ? absDividend.negate().divide(absDivisor).longValue()
447                                                : absDividend.divide(absDivisor).longValue();
448                        }
449                }
450                return longValue;
451        }
452
453        @Override
454        public float floatValue() {
455                return (float) doubleValue();
456        }
457
458        @Override
459        public double doubleValue() {
460                return bigDecimalValue().doubleValue();
461        }
462
463        /**
464         * Lay out this {@code RationalNumber} into a {@code String}.
465         *
466         * @param useFractionalRepresentation {@code true} for fractional representation {@code 5÷3}; 
467         *         {@code false} for decimal {@code 1.66667}.
468         *         
469         * @return string with canonical string representation of this
470         *         {@code RationalNumber}
471         */
472        private String layoutChars(boolean useFractionalRepresentation, char divisionCharacter) {
473                if (signum == 0) {
474                        return "0";
475                }
476                if (isInteger) {
477                        return getDividend().toString(); // already includes the sign
478                }
479                if (useFractionalRepresentation) {
480                        return getDividend().toString() + divisionCharacter + absDivisor;
481                } else {
482                        return String.valueOf(bigDecimalValue());
483                }
484        }
485
486        @Override
487        public String toString() {
488                return layoutChars(false, DIVISION_CHARACTER);
489        }
490
491        /**
492         * Returns a string representation of this {@code RationalNumber}, using
493         * fractional notation, eg. {@code 5÷3} or {@code -5÷3}.
494         *
495         * @return string representation of this {@code RationalNumber}, using
496         *         fractional notation.
497         * @since 2.0
498         */
499        public String toRationalString() {
500                return layoutChars(true, DIVISION_CHARACTER);
501        }
502        
503        /**
504     * Returns a string representation of this {@code RationalNumber}, using
505     * fractional notation, eg. {@code 5÷3} or {@code -5÷3}.
506     * 
507     * @param divisionCharacter the character to use instead of the default {@code ÷}
508     * @return string representation of this {@code RationalNumber}, using
509     *         fractional notation.
510     * @since 2.0
511     */
512        public String toRationalString(char divisionCharacter) {
513            return layoutChars(true, divisionCharacter);
514    }
515
516        @Override
517        public int hashCode() {
518                return hashCode;
519        }
520
521        /**
522     * Compares this RationalNumber with the specified Object for equality.
523     *
524     * @param  x Object to which this RationalNumber is to be compared.
525     * @return {@code true} if and only if the specified Object is a
526     *         RationalNumber whose value is numerically equal to this RationalNumber.
527     */
528        @Override
529    public boolean equals(Object x) {
530        // This test is just an optimization, which may or may not help
531        if (x == this) {
532            return true;
533        }
534
535        // no explicit null check required
536        if (!(x instanceof RationalNumber)) { 
537            return false; // will also return here if x is null 
538        }
539
540        final RationalNumber other = (RationalNumber) x;
541        
542//        // null checks not needed, since the constructor guards against dividend or divisor being null
543//        boolean result = (
544//                this.signum == other.signum &&
545//                Objects.equals(this.absDividend, other.absDividend) && 
546//                Objects.equals(this.absDivisor, other.absDivisor));
547//        return result; This is still broken
548        
549        return Objects.equals(this.bigDecimalValue(), other.bigDecimalValue());
550    }
551        
552
553}