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}