dafny.BigRational Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of DafnyRuntime Show documentation
Show all versions of DafnyRuntime Show documentation
Runtime for Dafny programs compiled to Java
The newest version!
// Copyright by the contributors to the Dafny Project
// SPDX-License-Identifier: MIT
package dafny;
import java.math.BigInteger;
public class BigRational {
//TODO: Implement default method, and disallow 0 for den
public static final BigRational ZERO = new BigRational(0);
BigInteger num, den; // invariant 1 <= den || (num == 0 && den == 0)
@Override
public String toString() {
if (den.equals(BigInteger.ONE) || num.equals(BigInteger.ZERO)) {
return num + ".0";
} else {
Tuple3 t = dividesAPowerOf10(den);
Integer log10 = t.dtor__2();
if (t.dtor__0()) {
BigInteger n = num.multiply(t.dtor__1());
String sign;
String digits;
if (num.signum() < 0) {
sign = "-";
digits = (n.negate()).toString();
} else {
sign = "";
digits = n.toString();
}
if (log10 < digits.length()) {
int digitCount = digits.length() - log10;
return sign + digits.substring(0, digitCount) + "." + digits.substring(digitCount);
} else {
int z = log10 - digits.length();
StringBuffer outputBuffer = new StringBuffer(z);
for (int i = 0; i < z; i++) {
outputBuffer.append("0");
}
return sign + "0." + outputBuffer.toString() + digits;
}
} else {
return "(" + num + ".0 / " + den + ".0)";
}
}
}
public static Tuple2 isPowerOf10(BigInteger x) {
int log10 = 0;
if (x.equals(BigInteger.ZERO)) {
return new Tuple2<>(false, log10);
}
while (true) {
// invariant: x != 0 && x * 10^log10 == old(x)
if (x.equals(BigInteger.ONE)) {
return new Tuple2<>(true, log10);
} else if (x.mod(BigInteger.TEN).equals(BigInteger.ZERO)) {
log10++;
x = x.divide(BigInteger.TEN);
} else {
return new Tuple2<>(false, log10);
}
}
}
public static Tuple3 dividesAPowerOf10(BigInteger i) {
BigInteger factor = BigInteger.ONE;
int log10 = 0;
if (i.compareTo(BigInteger.ZERO) <= 0) {
return new Tuple3<>(false, factor, log10);
}
// invariant: 1 <= i && i * 10^log10 == factor * old(i)
while (i.mod(BigInteger.TEN).equals(BigInteger.ZERO)) {
i = i.divide(BigInteger.TEN);
log10++;
}
BigInteger two = BigInteger.valueOf(2); // note, in Java 9, one can use BigInteger.TWO
BigInteger five = BigInteger.valueOf(5);
while (i.mod(five).equals(BigInteger.ZERO)) {
i = i.divide(five);
factor = factor.multiply(two);
log10++;
}
while (i.mod(two).equals(BigInteger.ZERO)) {
i = i.divide(two);
factor = factor.multiply(five);
log10++;
}
return new Tuple3<>(i.equals(BigInteger.ONE), factor, log10);
}
public BigRational() {
this(0, 1);
}
public BigRational(int n) {
this(n, 1);
}
public BigRational(int n, int d) {
this(BigInteger.valueOf(n), BigInteger.valueOf(d));
}
public BigRational(BigInteger n, BigInteger d) {
assert !d.equals(BigInteger.ZERO) : "Precondition Failure";
// ensures 1 <= den
if (d.compareTo(BigInteger.ZERO) < 0) {
num = n.negate();
den = d.negate();
} else {
num = n;
den = d;
}
}
public BigInteger ToBigInteger() {
if (num.equals(BigInteger.ZERO) || den.equals(BigInteger.ONE)) {
return num;
} else if (0 < num.signum()) {
return num.divide(den);
} else {
// Dafny uses Euclidean Division, so divide will not always satisfy the preconditions of
// num = (den * BigInteger) + remainder
// when the numerator is negative.
return (num.subtract(den).add(BigInteger.ONE)).divide(den);
}
}
public boolean isInteger() {
BigRational floored = new BigRational(this.ToBigInteger(), BigInteger.ONE);
return this.equals(floored);
}
// In order to compare, add, and subtract fractions, they must have the same denominator. This computes the
// common denominator of the fractions, and returns a tuple containing:
// aa: the numerator for a when multiplied by the common denominator
// bb: the numerator for b when multiplied by the common denominator
// dd: the common denominator
private static Tuple3 Normalize(BigRational a, BigRational b) {
BigInteger aa, bb, dd;
if (a.num.equals(BigInteger.ZERO)) {
aa = a.num;
bb = b.num;
dd = b.den;
} else if (b.num.equals(BigInteger.ZERO)) {
aa = a.num;
dd = a.den;
bb = b.num;
} else {
BigInteger gcd = a.den.gcd(b.den);
BigInteger xx = a.den.divide(gcd);
BigInteger yy = b.den.divide(gcd);
// We now have a == a.num / (xx * gcd) and b == b.num / (yy * gcd).
aa = a.num.multiply(yy);
bb = b.num.multiply(xx);
// 1 <= a.den * yy -> 1 <= dd
dd = a.den.multiply(yy);
}
return new Tuple3<>(aa, bb, dd);
}
public BigRational reduce() {
BigInteger gcd = num.abs().gcd(den);
if (gcd.equals(BigInteger.ONE)) return this;
return new BigRational(num.divide(gcd), den.divide(gcd));
}
public int compareTo(BigRational that) {
// simple things first
int asign = this.num.signum();
int bsign = that.num.signum();
if (asign < 0 && 0 <= bsign) {
return -1;
} else if (asign <= 0 && 0 < bsign) {
return -1;
} else if (bsign < 0 && 0 <= asign) {
return 1;
} else if (bsign <= 0 && 0 < asign) {
return 1;
}
BigInteger aa, bb, dd;
Tuple3 n = Normalize(this, that);
return n.dtor__0().compareTo(n.dtor__1());
}
public int signum() {
return this.num.signum();
}
@Override
public int hashCode() {
return num.hashCode() + 29 * den.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
BigRational o = (BigRational) obj;
Tuple3 t = Normalize(this, o);
return t.dtor__0().equals(t.dtor__1());
}
public static BigRational add(BigRational a, BigRational b) {
Tuple3 t = Normalize(a, b);
return new BigRational(t.dtor__0().add(t.dtor__1()), t.dtor__2());
}
public static BigRational subtract(BigRational a, BigRational b) {
Tuple3 t = Normalize(a, b);
return new BigRational(t.dtor__0().subtract(t.dtor__1()), t.dtor__2());
}
public static BigRational negate(BigRational a) {
return new BigRational(a.num.negate(), a.den);
}
public static BigRational multiply(BigRational a, BigRational b) {
return new BigRational(a.num.multiply(b.num), a.den.multiply(b.den));
}
public static BigRational divide(BigRational a, BigRational b) {
BigRational bReciprocal;
if (0 < b.num.signum()) {
bReciprocal = new BigRational(b.den, b.num);
} else {
// We track the sign of the rational in the numerator, so ensure that the numerator of the reciprocal
// has the sign of rational.
bReciprocal = new BigRational(b.den.negate(), b.num.negate());
}
return multiply(a, bReciprocal);
}
public BigRational add(BigRational b) {
return add(this, b);
}
public BigRational subtract(BigRational b) {
return subtract(this, b);
}
public BigRational negate() {
return new BigRational(num.negate(), den);
}
public BigRational multiply(BigRational b) {
return multiply(this, b);
}
public BigRational divide(BigRational b) {
return divide(this, b);
}
}