// ex: se sts=4 sw=4 expandtab: /** * Yeti core library - rational numbers. * * Copyright (c) 2007,2008 Madis Janson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package yeti.lang; import java.math.BigInteger; public final class RatNum extends Num { private final long numerator; private final long denominator; public RatNum(int numerator, int denominator) { if (denominator == 0) { throw new IllegalArgumentException(numerator + "/0"); } if (denominator < 0) { this.numerator = -(long) numerator; this.denominator = -(long) denominator; } else { this.numerator = numerator; this.denominator = denominator; } } private RatNum(long numerator, long denominator) { this.numerator = numerator; this.denominator = denominator; } public Num add(Num num) { return num.add(this); } private static long gcd(long a, long b) { long c; while (b != 0) { b = a % (c = b); a = c; } return a; } public Num add(long num) { long a, c, gcd; if (num > 0x7fffffffL || num < -0x7fffffffL || (a = num * denominator) > 0x7fffffff7fffffffL || a < -0x7fffffff7fffffffL) { return new FloatNum((double) numerator / denominator + num); } if ((c = a + numerator) > 0x7fffffffL || c < -0x7fffffff) { long d = denominator / (gcd = gcd(c < 0 ? -c : c, denominator)); if ((c /= gcd) > 0x7fffffffL || c < -0x7fffffffL) { return new FloatNum((double) c / d); } return new RatNum(c, d); } return new RatNum(c, denominator); } public Num add(RatNum num) { long a, b = numerator * num.denominator, c, gcd; if ((a = num.numerator * denominator) > 0 ? a > 0x3fffffffffffffffL || b > 0x3fffffffffffffffL : a < -0x3fffffffffffffffL || b < -0x3fffffffffffffffL) { return new FloatNum((double) numerator / denominator + (double) num.numerator / num.denominator); } long d = denominator * num.denominator; if ((c = a + b) > 0x7fffffffL || c < -0x7fffffff || d > 0x7ffffffffL || d < -0x7fffffffL) { d /= gcd = gcd(c < 0 ? -c : c, d); if ((c /= gcd) > 0x7fffffffL || c < -0x7fffffffL || d > 0x7fffffffL || d < -0x7fffffffL) { return new FloatNum((double) c / d); } } return new RatNum(c, d); } public Num add(BigInteger num) { return new FloatNum((double) numerator / denominator + num.doubleValue()); } public Num mul(Num num) { return num.mul(this); } public Num mul(long num) { long a; if (num > 0x7fffffffL || num < -0x7fffffffL) { return new FloatNum((double) numerator / denominator * num); } if ((a = numerator * num) > 0x7fffffffL || a < -0x7fffffffL) { long gcd, b = denominator / (gcd = gcd(a, denominator)); if ((a /= gcd) > 0x7fffffffL || a < -0x7fffffffL) { return new FloatNum((double) a / b); } return new RatNum(a, b); } return new RatNum(a, denominator); } public Num mul(RatNum num) { long a, b = denominator * num.denominator, gcd; if ((a = numerator * num.numerator) > 0x7fffffffL || a < -0x7fffffffL || b > 0x7fffffffL || b < -0x7fffffff) { b /= gcd = gcd(a, b); if ((a /= gcd) > 0x7fffffffL || a < -0x7fffffffL || b > 0x7fffffffL || b < -0x7fffffffL) { return new FloatNum((double) a / b); } } return new RatNum(a, b); } public Num mul(BigInteger num) { return new FloatNum((double) numerator / denominator * num.doubleValue()); } public Num div(Num num) { return num.divFrom(this); } public Num div(long num) { long a; if (num > 0x7fffffffL || num < -0x7fffffffL) { return new FloatNum((double) numerator / ((double) denominator * num)); } if ((a = denominator * num) > 0x7fffffffL || a < -0x7fffffffL) { long gcd, b = numerator / (gcd = gcd(a, numerator)); if ((a /= gcd) > 0x7fffffffL || a < -0x7fffffffL) { return new FloatNum((double) b / a); } return new RatNum(b, a); } return new RatNum(numerator, a); } // num / this public Num divFrom(long num) { long a; if (num > 0x7fffffffL || num < -0x7fffffffL) { return new FloatNum((double) num / numerator * denominator); } if ((a = denominator * num) > 0x7fffffffL || a < -0x7fffffffL) { long gcd, b = numerator / (gcd = gcd(a, numerator)); if ((a /= gcd) > 0x7fffffffL || a < -0x7fffffffL) { return new FloatNum((double) a / b); } return new RatNum(a, b); } return new RatNum(a, numerator); } public Num divFrom(RatNum num) { long a, b = numerator * num.denominator, gcd; if ((a = denominator * num.numerator) > 0x7fffffffL || a < -0x7fffffffL || b > 0x7fffffffL || b < -0x7fffffff) { b /= gcd = gcd(a, b); if ((a /= gcd) > 0x7fffffffL || a < -0x7fffffffL || b > 0x7fffffffL || b < -0x7fffffffL) { return new FloatNum((double) a / b); } } return new RatNum(a, b); } public Num intDiv(Num num) { return num.intDivFrom(numerator / denominator); } public Num intDiv(int num) { return new IntNum(numerator / denominator / num); } public Num intDivFrom(long num) { return new IntNum(num / (numerator / denominator)); } public Num intDivFrom(BigInteger num) { return new BigNum(num.divide( BigInteger.valueOf(numerator / denominator))); } public Num rem(Num num) { return num.remFrom(numerator / denominator); } public Num rem(int num) { return new IntNum((numerator / denominator) % num); } public Num remFrom(long num) { return new IntNum(num % (numerator / denominator)); } public Num remFrom(BigInteger num) { return new BigNum(num.remainder( BigInteger.valueOf(numerator / denominator))); } public Num sub(Num num) { return num.subFrom(this); } public Num sub(long num) { return add(-num); } public Num subFrom(long num) { long a, c, gcd; if (num > 0x7fffffffL || num < -0x7fffffffL || (a = num * denominator) > 0x7fffffff7fffffffL || a < -0x7fffffff7fffffffL) { return new FloatNum((double) num - (double) numerator / denominator); } if ((c = a - numerator) > 0x7fffffffL || c < -0x7fffffff) { long d = denominator / (gcd = gcd(c < 0 ? -c : c, denominator)); if ((c /= gcd) > 0x7fffffffL || c < -0x7fffffffL) { return new FloatNum((double) c / d); } return new RatNum(c, d); } return new RatNum(c, denominator); } public Num subFrom(RatNum num) { long a, b = numerator * num.denominator, c, gcd; if ((a = num.numerator * denominator) > 0 ? a > 0x3fffffffffffffffL || b < -0x3fffffffffffffffL : a < -0x3fffffffffffffffL || b > 0x3fffffffffffffffL) { return new FloatNum((double) numerator / denominator + (double) num.numerator / num.denominator); } long d = denominator * num.denominator; if ((c = a - b) > 0x7fffffffL || c < -0x7fffffff || d > 0x7ffffffffL || d < -0x7fffffffL) { d /= gcd = gcd(c < 0 ? -c : c, d); if ((c /= gcd) > 0x7fffffffL || c < -0x7fffffffL || d > 0x7fffffffL || d < -0x7fffffffL) { return new FloatNum((double) c / d); } } return new RatNum(c, d); } public Num and(Num num) { return new IntNum(num.longValue() & (numerator / denominator)); } public Num and(BigInteger num) { return new IntNum(num.longValue() & (numerator / denominator)); } public Num or(Num num) { return num.or(numerator / denominator); } public Num or(long num) { return new IntNum(num | (numerator / denominator)); } public Num xor(Num num) { return num.xor(numerator / denominator); } public Num xor(long num) { return new IntNum(num ^ (numerator / denominator)); } public RatNum reduce() { long gcd = gcd(numerator, denominator); return new RatNum(numerator / gcd, denominator / gcd); } public byte byteValue() { return (byte) (numerator / denominator); } public short shortValue() { return (short) (numerator / denominator); } public int intValue() { return (int) (numerator / denominator); } public long longValue() { return numerator / denominator; } public float floatValue() { return (float) ((double) numerator / denominator); } public double doubleValue() { return (double) numerator / (double) denominator; } public static Num div(long numerator, long denominator) { if (denominator == 0) { throw new IllegalArgumentException("division by zero"); } if (numerator > 0x7fffffff || numerator < -0x7fffffff || denominator > 0x7fffffff || denominator < -0x7fffffff) { long gcd; denominator /= gcd = gcd(numerator, denominator); if ((numerator /= gcd) > 0x7fffffff || numerator < -0x7fffffff || denominator > 0x7fffffff || denominator < -0x7fffffff) { return new FloatNum((double) numerator / denominator); } } return denominator < 0 ? new RatNum(-numerator, -denominator) : new RatNum(numerator, denominator); } public Num subFrom(BigInteger num) { return new FloatNum((double) numerator / denominator - num.doubleValue()); } public int numerator() { return (int) numerator; } public int denominator() { return (int) denominator; } public int compareTo(Object num) { return ((Num) num).rCompare(this); } public int rCompare(long num) { if (-0x7fffffff <= num && num <= 0x7fffffff) { long x = num * denominator; return numerator < x ? 1 : numerator > x ? -1 : 0; } return (double) numerator / denominator < (double) num ? 1 : -1; } public int rCompare(RatNum num) { long a = numerator * num.denominator; long b = num.numerator * denominator; return a < b ? 1 : a > b ? -1 : 0; } public int rCompare(BigInteger num) { if (numerator % denominator == 0 && BigInteger.valueOf(numerator / denominator).equals(num)) { return 0; } double a = (double) numerator / denominator, b = num.doubleValue(); return a < b ? 1 : a > b ? -1 : 0; } public String toString() { if (numerator % denominator == 0) { return Integer.toString((int) numerator / (int) denominator); } return Double.toString((double) numerator / (double) denominator); } public int hashCode() { // compatibility with IntNum and FloatNum... long x = numerator / denominator; long d = Double.doubleToLongBits((double) numerator / denominator - x); if (d != 0x8000000000000000L) { x ^= d; } return (int) (x ^ (x >>> 32)); } }