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}