//******************************************************************************
//
// File:    BigRational.java
// Package: edu.rit.numeric
// Unit:    Class edu.rit.numeric.BigRational
//
// This Java source file is copyright (C) 2009 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;

/**
 * Class BigRational provides an arbitrary precision rational number. An
 * arbitrary precision rational number is the ratio of two arbitrary precision
 * integers (type java.math.BigInteger). Operations are provided for exact
 * arithmetic with rational numbers.
 * 

* A rational number is said to be normalized if * GCD(numerator,denominator) = 1. The methods below do not * automatically normalize the rational number. Thus, the numerator and * denominator tend to get larger and larger as operations are performed on the * rational number. To reduce the numerator and denominator to lowest terms * again, call the normalize() method. It is up to you to decide * whether to normalize the rational number after each operation, or after a * series of operations. *

* Class BigRational provides the equals() and hashCode() * methods, and BigRational objects can be used as keys in hashed data * structures. However, BigRational objects are mutable. If a BigRational object * is used as a hash key, be sure not to change its value. *

* Class BigRational is not multiple thread safe. * * @author Alan Kaminsky * @version 31-Dec-2009 */ public class BigRational extends Number implements Comparable { // Hidden data members. private BigInteger numer; private BigInteger denom; // Exported constructors. /** * Construct a new rational number. Its value is 0. */ public BigRational() { numer = BigInteger.ZERO; denom = BigInteger.ONE; } /** * Construct a new rational number. Its value is x. * * @param x Long integer. */ public BigRational (long x) { assign (x); } /** * Construct a new rational number. Its value is n/d. * * @param n Numerator. * @param d Denominator. * * @exception ArithmeticException * (unchecked exception) Thrown if d is 0. */ public BigRational (long n, long d) { assign (n, d); } /** * Construct a new rational number. Its value is x. * * @param x Big integer. */ public BigRational (BigInteger x) { assign (x); } /** * Construct a new rational number. Its value is n/d. * * @param n Numerator. * @param d Denominator. * * @exception ArithmeticException * (unchecked exception) Thrown if d is 0. */ public BigRational (BigInteger n, BigInteger d) { assign (n, d); } /** * Construct a new rational number. Its value is x. * * @param x Rational number. */ public BigRational (BigRational x) { assign (x); } /** * Construct a new rational number. Its value comes from the string * s. The string must obey this syntax: optional minus sign * (-), numerator (decimal integer), slash (/), * denominator (decimal integer). The slash-and-denominator is optional. If * present, the denominator must be greater than 0. There is no whitespace * within the string. * * @param s String. * * @exception NumberFormatException * (unchecked exception) Thrown if s cannot be parsed into a * rational number. */ public BigRational (String s) { assign (s); } // Exported operations. /** * Returns this rational number's numerator. * * @return Numerator. */ public BigInteger numerator() { return numer; } /** * Returns this rational number's denominator. * * @return Denominator. */ public BigInteger denominator() { return denom; } /** * Set this rational number to the given number. * * @param x Long integer. * * @return This rational number, with its value set to x. */ public BigRational assign (long x) { this.numer = BigInteger.valueOf (x); this.denom = BigInteger.ONE; return this; } /** * Set this rational number to the given fraction. * * @param n Numerator. * @param d Denominator. * * @return This rational number, with its value set to n/d. * * @exception ArithmeticException * (unchecked exception) Thrown if d is 0. */ public BigRational assign (long n, long d) { if (d == 0) { throw new ArithmeticException ("BigRational.assign(): Zero denominator"); } this.numer = BigInteger.valueOf (n); this.denom = BigInteger.valueOf (d); return this; } /** * Set this rational number to the given number. * * @param x Big integer. * * @return This rational number, with its value set to x. */ public BigRational assign (BigInteger x) { this.numer = x; this.denom = BigInteger.ONE; return this; } /** * Set this rational number to the given fraction. * * @param n Numerator. * @param d Denominator. * * @return This rational number, with its value set to n/d. * * @exception ArithmeticException * (unchecked exception) Thrown if d is 0. */ public BigRational assign (BigInteger n, BigInteger d) { if (d.equals (BigInteger.ZERO)) { throw new ArithmeticException ("BigRational.assign(): Zero denominator"); } this.numer = n; this.denom = d; return this; } /** * Set this rational number to the given number. * * @param x Rational number. * * @return This rational number, with its value set to x. */ public BigRational assign (BigRational x) { this.numer = x.numer; this.denom = x.denom; return this; } /** * Set this rational number to the value parsed from the given string. The * string must obey this syntax: optional minus sign (-), numerator * (decimal integer), slash (/), denominator (decimal integer). The * slash-and-denominator is optional. If present, the denominator must be * greater than 0. There is no whitespace within the string. * * @param s String. * * @return This rational number, with its value set to s. * * @exception NumberFormatException * (unchecked exception) Thrown if s cannot be parsed into a * rational number. */ public BigRational assign (String s) { BigInteger numer, denom; int iSlash = s.indexOf ('/'); if (iSlash == -1) { // No denominator. numer = new BigInteger (s); denom = BigInteger.ONE; } else if (iSlash+1 < s.length()) { // Slash and denominator. numer = new BigInteger (s.substring (0, iSlash)); denom = new BigInteger (s.substring (iSlash+1)); if (denom.compareTo (BigInteger.ZERO) <= 0) { throw new NumberFormatException ("BigRational.assign(): Negative denominator not allowed"); } } else { // Slash but no denominator. throw new NumberFormatException ("BigRational.assign(): Missing denominator after /"); } this.numer = numer; this.denom = denom; return this; } /** * Set this rational number to the integer part of itself. * * @return This rational number, with its value set to int(this). */ public BigRational intPart() { numer = numer.divide (denom); denom = BigInteger.ONE; return this; } /** * Set this rational number to the fractional part of itself. * * @return This rational number, with its value set to frac(this). */ public BigRational fracPart() { numer = numer.remainder (denom); return this; } /** * Set this rational number to the sum of itself and the given number. * * @param x Rational number. * * @return This rational number, with its value set to this+x. */ public BigRational add (BigRational x) { this.numer = this.numer.multiply(x.denom).add (x.numer.multiply (this.denom)); this.denom = this.denom.multiply (x.denom); return this; } /** * Set this rational number to the difference of itself and the given * number. * * @param x Rational number. * * @return This rational number, with its value set to this-x. */ public BigRational sub (BigRational x) { this.numer = this.numer.multiply(x.denom).subtract (x.numer.multiply (this.denom)); this.denom = this.denom.multiply (x.denom); return this; } /** * Set this rational number to the product of itself and the given number. * * @param x Rational number. * * @return This rational number, with its value set to this*x. */ public BigRational mul (BigRational x) { this.numer = this.numer.multiply (x.numer); this.denom = this.denom.multiply (x.denom); return this; } /** * Set this rational number to the quotient of itself and the given number. * * @param x Rational number. * * @return This rational number, with its value set to this/x. * * @exception ArithmeticException * (unchecked exception) Thrown if x is 0. */ public BigRational div (BigRational x) { if (x.numer.equals (BigInteger.ZERO)) { throw new ArithmeticException ("BigRational.div(): Divide by zero"); } this.numer = this.numer.multiply (x.denom); this.denom = this.denom.multiply (x.numer); return this; } /** * Set this rational number to the remainder when divided by the given * number. * * @param x Rational number. * * @return This rational number, with its value set to * frac(this/x)*x. * * @exception ArithmeticException * (unchecked exception) Thrown if x is 0. */ public BigRational rem (BigRational x) { return div (x) .fracPart() .mul (x); } /** * Increment this rational number. * * @return This rational number, with its value set to this+1. */ public BigRational incr() { this.numer = this.numer.add (this.denom); return this; } /** * Decrement this rational number. * * @return This rational number, with its value set to this-1. */ public BigRational decr() { this.numer = this.numer.subtract (this.denom); return this; } /** * Set this rational number to the absolute value of itself. * * @return This rational number, with its value set to abs(this). */ public BigRational abs() { this.numer = this.numer.abs(); this.denom = this.denom.abs(); return this; } /** * Set this rational number to the negative of itself. * * @return This rational number, with its value set to -this. */ public BigRational negate() { this.numer = this.numer.negate(); return this; } /** * Set this rational number to the reciprocal of itself. * * @return This rational number, with its value set to 1/this. * * @exception ArithmeticException * (unchecked exception) Thrown if this rational number is 0. */ public BigRational recip() { if (this.numer.equals (BigInteger.ZERO)) { throw new ArithmeticException ("BigRational.recip(): Divide by zero"); } BigInteger tmp = this.numer; this.numer = this.denom; this.denom = tmp; return this; } /** * Set this rational number to the smaller of itself and the given number. * * @param x Rational number. * * @return This rational number, with its value set to * min(this,x). */ public BigRational min (BigRational x) { if (this.compareTo (x) > 0) this.assign (x); return this; } /** * Set this rational number to the larger of itself and the given number. * * @param x Rational number. * * @return This rational number, with its value set to * max(this,x). */ public BigRational max (BigRational x) { if (this.compareTo (x) < 0) this.assign (x); return this; } /** * Normalize this rational number. Afterwards, the denominator is greater * than or equal to 1, and the denominator does not divide the numerator; in * other words, GCD(numerator,denominator) = 1. * * @return This rational number, normalized. */ public BigRational normalize() { int sign = numer.signum() * denom.signum(); numer = numer.abs(); denom = denom.abs(); BigInteger gcd = numer.gcd (denom); numer = numer.divide (gcd); denom = denom.divide (gcd); if (sign < 0) numer = numer.negate(); return this; } /** * Converts this rational number to an integer. * * @return Integer value of this rational number. */ public int intValue() { BigDecimal n = new BigDecimal (numer); BigDecimal d = new BigDecimal (denom); return n.divide (d, 1, RoundingMode.HALF_UP) .intValue(); } /** * Converts this rational number to a long integer. * * @return Long integer value of this rational number. */ public long longValue() { BigDecimal n = new BigDecimal (numer); BigDecimal d = new BigDecimal (denom); return n.divide (d, 1, RoundingMode.HALF_UP) .longValue(); } /** * Converts this rational number to a single precision floating point * number. * * @return Single precision floating point value of this rational number. */ public float floatValue() { BigDecimal n = new BigDecimal (numer); BigDecimal d = new BigDecimal (denom); return n.divide (d, MathContext.DECIMAL32) .floatValue(); } /** * Converts this rational number to a double precision floating point * number. * * @return Double precision floating point value of this rational number. */ public double doubleValue() { BigDecimal n = new BigDecimal (numer); BigDecimal d = new BigDecimal (denom); return n.divide (d, MathContext.DECIMAL64) .doubleValue(); } /** * Compare this rational number to the given rational number. * * @param x Rational number to compare. * * @return A number less than, equal to, or greater than 0 if this rational * number is less than, equal to, or greater than x, * respectively. */ public int compareTo (BigRational x) { BigInteger diff = this.numer.multiply(x.denom).subtract (x.numer.multiply (this.denom)); return diff.compareTo (BigInteger.ZERO); } /** * Determine if this rational number equals the given object. *

* Note: Class BigRational provides the equals() and * hashCode() methods, and BigRational objects can be used as keys * in hashed data structures. However, BigRational objects are mutable. If a * BigRational object is used as a hash key, be sure not to change its * value. *

* Note: Two rational numbers are equal only if their numerators and * denominators are equal. Thus, it is possible for equals() to * return false even if the two rational numbers have the same numerical * value. To ensure that equals() will return true if the two * rational numbers have the same numerical value, normalize the two * rational numbers first. * * @param obj Object to compare. * * @return True if this rational number equals obj, false * otherwise. */ public boolean equals (Object obj) { return (obj instanceof BigRational) && (((BigRational) obj).numer.equals (this.numer)) && (((BigRational) obj).denom.equals (this.denom)); } /** * Returns a hash code for this rational number. *

* Note: Class BigRational provides the equals() and * hashCode() methods, and BigRational objects can be used as keys * in hashed data structures. However, BigRational objects are mutable. If a * BigRational object is used as a hash key, be sure not to change its * value. *

* Note: Two rational numbers have the same hash code only if their * numerators and denominators are equal. Thus, it is possible for * hashCode() to return different values even if the two rational * numbers have the same numerical value. To ensure that hashCode() * will return the same value if the two rational numbers have the same * numerical value, normalize the two rational numbers first. * * @return Hash code. */ public int hashCode() { return numer.hashCode()*31 + denom.hashCode(); } /** * Returns a string version of this rational number. If this rational * number's denominator is 1, the string is in the form * "<numer>", otherwise the string is in the form * "<numer>/<denom>". The negative sign, if any, is * attached to the numerator. *

* Note: The toString() method yields the numerator and * denominator as they are, without normalizing this rational number. */ public String toString() { String ns = this.numer.toString(); String ds = this.denom.toString(); if (ns.charAt(0) == '-' && ds.charAt(0) == '-') { ns = ns.substring (1); ds = ds.substring (1); } else if (ns.charAt(0) != '-' && ds.charAt(0) == '-') { ns = "-" + ns; ds = ds.substring (1); } if (ds.equals ("1")) { return ns; } else { return ns + "/" + ds; } } }

Ads help maintain this website.