/*
* Copyright (C) 2009-2012 University of Freiburg
*
* This file is part of SMTInterpol.
*
* SMTInterpol is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SMTInterpol 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>.
*/
package de.uni_freiburg.informatik.ultimate.logic;
import java.math.BigInteger;
import de.uni_freiburg.informatik.ultimate.logic.Rational.BigRational;
/**
* Mutable version of the {@link Rational} class. All arithmetic
* operations change the value of this object.
*
* This class is intended to save some unneeded temporary objects in bigger
* calculations. This should reduce the number of garbage collections such that
* the program should run faster.
*
* @author Juergen Christ
*/
public class MutableRational implements Comparable<MutableRational> {
int mNum;
int mDenom;
BigInteger mBignum;
BigInteger mBigdenom;
/**
* Create a new rational representing num/denom.
* @param num the numerator.
* @param denom the denominator.
*/
public MutableRational(int num, int denom) {
setValue(num, denom);
}
/**
* Create a new rational representing num/denom.
* @param num the numerator.
* @param denom the denominator.
*/
public MutableRational(BigInteger num, BigInteger denom) {
mBignum = num;
mBigdenom = denom;
normalize();
}
/**
* Create a new mutable rational from a rational.
* @param r the rational.
*/
public MutableRational(Rational r) {
mNum = r.mNum;
mDenom = r.mDenom;
if (r instanceof Rational.BigRational) {
mBignum = r.numerator();
mBigdenom = r.denominator();
}
}
/**
* Create a new copy of a mutable rational.
* @param r the original rational.
*/
public MutableRational(MutableRational r) {
mNum = r.mNum;
mDenom = r.mDenom;
mBignum = r.mBignum;
mBigdenom = r.mBigdenom;
}
/**
* Set the value of this rational to value.
* @param value the value to set to.
*/
public void setValue(Rational value) {
mNum = value.mNum;
mDenom = value.mDenom;
if (value instanceof Rational.BigRational) {
mBignum = value.numerator();
mBigdenom = value.denominator();
} else {
mBignum = mBigdenom = null;
}
}
/**
* Set the value of this rational to newnum/newdenom.
* @param newnum the new numerator.
* @param newdenom the new denominator.
*/
public void setValue(long newnum, long newdenom) {
long gcd2 = Rational.gcd(Math.abs(newnum), Math.abs(newdenom));
if (newdenom < 0) {
gcd2 = -gcd2;
}
if (gcd2 != 0) {
newnum /= gcd2;
newdenom /= gcd2;
}
if (Integer.MIN_VALUE <= newnum && newnum <= Integer.MAX_VALUE
&& newdenom <= Integer.MAX_VALUE) {
mNum = (int) newnum;
mDenom = (int) newdenom;
mBignum = mBigdenom = null;
} else {
mBignum = BigInteger.valueOf(newnum);
mBigdenom = BigInteger.valueOf(newdenom);
}
}
/**
* Normalize the rational by dividing through the gcd. This is
* called after every operation.
*/
private void normalize() {
if (mBignum == null) {
final int norm = Rational.gcd(mNum, mDenom);
if (norm != 0 && norm != 1) {
mNum /= norm;
mDenom /= norm;
}
if (mDenom < 0) {
mNum = -mNum;
mDenom = -mDenom;
}
} else {
if (!mBigdenom.equals(BigInteger.ONE)) {
BigInteger norm = Rational.gcd(mBignum, mBigdenom).abs();
if (mBigdenom.signum() < 0) {
norm = norm.negate();
}
if (!norm.equals(BigInteger.ZERO)
&& !norm.equals(BigInteger.ONE)) {
mBignum = mBignum.divide(norm);
mBigdenom = mBigdenom.divide(norm);
}
}
if (mBigdenom.bitLength() < 32 && mBignum.bitLength() < 32) { // NOCHECKSTYLE
mNum = mBignum.intValue();
mDenom = mBigdenom.intValue();
mBignum = mBigdenom = null;
}
}
}
/**
* Add the rational other to this rational.
* @param other the rational to add.
* @return this mutable rational.
*/
public MutableRational add(Rational other) {
/* fast path */
if (other == Rational.ZERO) {
return this;
}
if (mBignum == null && !(other instanceof Rational.BigRational)) {
if (mDenom == other.mDenom) {
/* handle gcd = 0 correctly
* two INFINITYs with same sign give INFINITY,
* otherwise it gives NAN.
*/
if (mDenom == 0) {
if (mNum != other.mNum) {
mNum = 0;
}
} else {
/* a common, very simple case, e.g. for integers */
setValue((long) mNum + other.mNum, mDenom);
}
} else {
final int gcd = Rational.gcd(mDenom, other.mDenom);
final long denomgcd = mDenom / gcd;
final long otherdenomgcd = other.mDenom / gcd;
final long newdenom = denomgcd * other.mDenom;
final long newnum = otherdenomgcd * mNum + denomgcd * other.mNum;
setValue(newnum, newdenom);
}
return this;
}
if (mBignum == null && mNum == 0 && mDenom == 1) {
/* This is zero; set result to other */
mBignum = other.numerator();
mBigdenom = other.denominator();
return this;
}
final BigInteger tdenom = denominator();
final BigInteger odenom = other.denominator();
if (tdenom.equals(odenom)) {
mBignum = numerator().add(other.numerator());
mBigdenom = tdenom;
} else {
final BigInteger gcd = Rational.gcd(tdenom, odenom);
final BigInteger tdenomgcd = tdenom.divide(gcd);
final BigInteger odenomgcd = odenom.divide(gcd);
mBignum = numerator().multiply(odenomgcd)
.add(other.numerator().multiply(tdenomgcd));
mBigdenom = tdenom.multiply(odenomgcd);
}
normalize();
return this;
}
/**
* Negate this rational, i.e., this = -this.
* @return this mutable rational.
*/
public MutableRational negate() {
if (mBignum == null) {
if (mNum == Integer.MIN_VALUE) {
setValue(-(long)Integer.MIN_VALUE, mDenom);
} else {
mNum = -mNum;
}
} else {
mBignum = mBignum.negate();
}
return this;
}
/**
* Subtract the other rational from this.
* @param other the rational to subtract.
* @return this mutable rational.
*/
public MutableRational sub(Rational other) {
return add(other.negate());
}
/**
* Multiply this rational with other and store the result in this.
* @param other the rational to multiply with.
* @return this mutable rational.
*/
public MutableRational mul(Rational other) {
/* fast path */
if (other == Rational.ONE) {
return this;
}
if (other == Rational.MONE) {
return negate();
}
if (mBignum == null && !(other instanceof Rational.BigRational)) {
final long newnum = (long)mNum * other.mNum;
final long newdenom = (long)mDenom * other.mDenom;
setValue(newnum, newdenom);
return this;
}
mBignum = numerator().multiply(other.numerator());
mBigdenom = denominator().multiply(other.denominator());
normalize();
return this;
}
/**
* Divide this rational by the other and store the result in this.
* @param other the divisor.
* @return this mutable rational.
*/
public MutableRational div(Rational other) {
/* fast path */
if (other == Rational.ZERO) {
throw new ArithmeticException("Division by ZERO");
}
if (mBignum == null && mNum == 0) {
return this;
}
if (other == Rational.ONE) {
return this;
}
if (other == Rational.MONE) {
return negate();
}
if (mBignum == null && !(other instanceof Rational.BigRational)) {
long newnum = (long)mNum * other.mDenom;
final long newdenom = (long)mDenom * other.mNum;
// +-inf : -c = -+inf
if (newdenom == 0 && other.mNum < 0) {
newnum = -newnum;
}
setValue(newnum, newdenom);
return this;
}
mBignum = numerator().multiply(other.denominator());
mBigdenom = denominator().multiply(other.numerator());
// +-inf : -c = -+inf
if (mBigdenom.equals(BigInteger.ZERO)
&& other.numerator().signum() == -1) {
mBignum = mBignum.negate();
}
normalize();
return this;
}
/**
* Compute the multiplicative inverse of this rational and store
* it in this.
* @return this mutable rational.
*/
public MutableRational inverse() {
if (mBignum == null) {
setValue(mDenom, mNum);
} else {
final BigInteger tmp = mBigdenom;
if (mBignum.signum() < 0) {
mBigdenom = mBignum.negate();
mBignum = tmp.negate();
} else {
mBigdenom = mBignum;
mBignum = tmp;
}
}
return this;
}
/**
* Check if this rational is negative.
* @return true iff {@code this < 0}.
*/
public boolean isNegative() {
return numerator().signum() < 0;
}
/**
* Computes {@code this += (fac1*fac2)}.
* @param fac1 one of the factors.
* @param fac2 the other factor.
* @return this mutable rational.
*/
public MutableRational addmul(Rational fac1,Rational fac2) {
return add(fac1.mul(fac2));
}
/**
* Computes {@code this += (fac1*fac2)}.
* @param fac1 one of the factors.
* @param fac2 the other factor.
* @return this mutable rational.
*/
public MutableRational addmul(Rational fac1,BigInteger fac2) {
return add(fac1.mul(fac2));
}
/**
* Computes {@code this = (this - s) / d}.
* @param s the rational to subtract.
* @param d the divisor.
* @return this mutable rational.
*/
public MutableRational subdiv(Rational s,Rational d) {
return sub(s).div(d);
}
@Override
/**
* Compares this mutable rational with the other.
* @param o the other mutable rational.
* @return -1, if this < o; 1, if this > o; 0 if they are equal.
*/
public int compareTo(MutableRational o) {
/* fast path */
if (mBignum == null && o.mBignum == null) {
/* handle infinities and nan */
if (o.mDenom == mDenom) {
return mNum < o.mNum ? -1 : mNum == o.mNum ? 0 : 1;
}
final long valt = (long)mNum * o.mDenom;
final long valo = (long)o.mNum * mDenom;
return valt < valo ? -1 : valt == valo ? 0 : 1;
}
final BigInteger valthis = numerator().multiply(o.denominator());
final BigInteger valo = o.numerator().multiply(denominator());
return valthis.compareTo(valo);
}
/**
* Compares this mutable rational with the other.
* @param o the other rational.
* @return -1, if this < o; 1, if this > o; 0 if they are equal.
*/
public int compareTo(Rational o) {
/* fast path */
if (mBignum == null && !(o instanceof BigRational)) {
/* handle infinities and nan */
if (o.mDenom == mDenom) {
return mNum < o.mNum ? -1 : mNum == o.mNum ? 0 : 1;
}
final long valt = (long)mNum * o.mDenom;
final long valo = (long)o.mNum * mDenom;
return valt < valo ? -1 : valt == valo ? 0 : 1;
}
final BigInteger valthis = numerator().multiply(o.denominator());
final BigInteger valo = o.numerator().multiply(denominator());
return valthis.compareTo(valo);
}
/**
* Compares this mutable rational with the other. This works with
* MutableRational and Rational.
* @param o the other rational.
* @return true if this equals o, false otherwise.
*/
@Override
public boolean equals(Object o) {
if (o instanceof Rational) {
final Rational r = (Rational) o;
// Works thanks to normalization!!!
return mBignum == null
? !(r instanceof Rational.BigRational)
&& mNum == r.mNum && mDenom == r.mDenom
: mBignum.equals(r.numerator())
&& mBigdenom.equals(r.denominator());
}
if (o instanceof MutableRational) {
final MutableRational r = (MutableRational) o;
// Works thanks to normalization!!!
return mBignum == null
? r.mBignum == null && mNum == r.mNum && mDenom == r.mDenom
: mBignum.equals(r.mBignum) && mBigdenom.equals(r.mBigdenom);
}
return false;
}
/**
* Get the numerator of this rational.
* @return the numerator.
*/
public BigInteger numerator() {
return mBignum == null ? BigInteger.valueOf(mNum) : mBignum;
}
/**
* Get the denominator of this rational.
* @return the denominator.
*/
public BigInteger denominator() {
return mBigdenom == null ? BigInteger.valueOf(mDenom) : mBigdenom;
}
/**
* Computes a hashcode. The hashcode is computed as
* {@code 257 * numerator + denominator} if both fit into an integer and
* {@code 257 * numerator().hashCode() + denominator().hashCode()} if big
* integers are necessary.
* @return the hashcode.
*/
@Override
public int hashCode() {
if (mBignum == null) {
return mNum * 257 + mDenom;
} else {
return mBignum.hashCode() * 257 + mBigdenom.hashCode();
}
}
/**
* Get a string representation of this number. This is
* {@code numerator()+ "/" + denominator()} except for
* infinity ({@code "inf"}), nan ({@code "nan"}), or minus
* infinity ({@code "-inf"}).
* @return the string representation.
*/
@Override
public String toString() {
/* fast path */
if (mBignum == null) {
if (mDenom == 0) {
return mNum > 0 ? "inf" : mNum == 0 ? "nan" : "-inf";
}
if (mDenom == 1) {
return String.valueOf(mNum);
}
return mNum + "/" + mDenom;
} else {
if (mBigdenom.equals(BigInteger.ONE)) {
return mBignum.toString();
}
return mBignum + "/" + mBigdenom;
}
}
/**
* Check whether this rational represents an integral value. Both infinity
* values are treated as integral.
* @return {@code true} iff value is integral.
*/
public boolean isIntegral() {
return (mBignum == null)
? mDenom <= 1 : mBigdenom.equals(BigInteger.ONE);
}
/**
* Convert this mutable rational into a immutable rational.
* @return the rational.
*/
public Rational toRational() {
if (mBignum == null) {
return Rational.valueOf(mNum, mDenom);
}
return Rational.valueOf(numerator(), denominator());
}
/**
* Compute sign of this rational. This is equivalent to
* {@code compare(Rational.ZERO)}.
* @return the sign of the rational, +1 for positive, 0 for 0 and
* -1 for negative.
*/
public int signum() {
if (mBignum == null) {
return mNum < 0 ? -1 : mNum == 0 ? 0 : 1;
}
return mBignum.signum();
}
}