/******************************************************************************* * Copyright (c) 2011-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * Anya Helene Bagge - initial implementation * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.value.impl.primitive; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.INumber; import org.rascalmpl.value.IRational; import org.rascalmpl.value.IReal; import org.rascalmpl.value.IValue; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.visitors.IValueVisitor; /*package*/ class RationalValue extends AbstractNumberValue implements IRational { public static final Type RATIONAL_TYPE = TypeFactory.getInstance().rationalType(); protected final IInteger num; protected final IInteger denom; /*package*/ static IRational newRational(IInteger a, IInteger b) { return new RationalValue(a, b); } private RationalValue(IInteger num, IInteger denom) { if(denom.signum() < 0) { num = num.negate(); denom = denom.negate(); } // normalize infinites if(denom.signum() == 0) { if(num.signum() > 0) num = intOne(); else if(num.signum() < 0) num = intOne().negate(); else throw new ArithmeticException("Illegal fraction 0/0"); } else if(num.signum() == 0) { denom = intOne(); } else { IInteger gcd = gcd(num, denom); while(gcd.compare(intOne()) != 0) { num = num.divide(gcd); denom = denom.divide(gcd); gcd = gcd(num, denom); } } this.num = num; this.denom = denom; } @Override public IRational add(IRational other) { // (num*other.denom + denom*other.num) / denom*other.denom return toRational( num.multiply(other.denominator()).add(denom.multiply(other.numerator())), denom.multiply(other.denominator())); } @Override public IReal add(IReal other) { return toReal(other.precision()).add(other); } @Override public INumber add(IInteger other) { return toRational(num.add(other.multiply(denom)), denom); } @Override public IRational subtract(IRational other) { // (num*other.denom - denom*other.num) / denom*other.denom return toRational( num.multiply(other.denominator()).subtract(denom.multiply(other.numerator())), denom.multiply(other.denominator())); } @Override public INumber subtract(IReal other) { return toReal(other.precision()).subtract(other); } @Override public INumber subtract(IInteger other) { return toRational(num.subtract(other.multiply(denom)), denom); } @Override public IRational multiply(IRational other) { return toRational(num.multiply(other.numerator()), denom.multiply(other.denominator())); } @Override public IReal multiply(IReal other) { return toReal(other.precision()).multiply(other); } @Override public INumber multiply(IInteger other) { return toRational(num.multiply(other), denom); } // TODO: should we perhaps drop this and only have the other divide? // or vice-versa? @Override public IRational divide(IRational other) { return toRational(num.multiply(other.denominator()), denom.multiply(other.numerator())); } @Override public IReal divide(IReal other, int precision) { return toReal(precision).divide(other, precision); } @Override public IRational divide(IInteger other, int precision) { return divide(other); // forget precision } @Override public IRational divide(IInteger other) { return toRational(num, denom.multiply(other)); } @Override public INumber divide(IRational other, int precision) { return toRational(num.multiply(other.denominator()), denom.multiply(other.numerator())); } @Override public IBool less(IRational other) { return BoolValue.getBoolValue(compare(other) < 0); } @Override public IBool less(IReal other) { return other.greater(this); } @Override public IBool less(IInteger other) { return less(other.toRational()); } @Override public IBool greater(IRational other) { return BoolValue.getBoolValue(compare(other) > 0); } @Override public IBool greater(IReal other) { return other.less(this); } @Override public IBool greater(IInteger other) { return greater(other.toRational()); } @Override public IBool equal(IRational other) { return BoolValue.getBoolValue(compare(other) == 0); } @Override public IBool equal(IReal other) { return other.equal(this); } @Override public IBool equal(IInteger other) { return equal(other.toRational()); } @Override public IBool lessEqual(IRational other) { return BoolValue.getBoolValue(compare(other) <= 0); } @Override public IBool lessEqual(IReal other) { return other.greaterEqual(this); } @Override public IBool lessEqual(IInteger other) { return lessEqual(other.toRational()); } @Override public IBool greaterEqual(IRational other) { return BoolValue.getBoolValue(compare(other) >= 0); } @Override public IBool greaterEqual(IReal other) { return other.lessEqual(this); } @Override public IBool greaterEqual(IInteger other) { return greaterEqual(other.toRational()); } @Override public boolean isEqual(IValue other) { return equals(other); } public boolean equals(Object o) { if(o == null) return false; if(o == this) return true; if(o.getClass() == getClass()){ RationalValue other = (RationalValue) o; return num.equals(other.num) && denom.equals(other.denom); } return false; } @Override public int compare(INumber other) { if(isIntegerType(other)) { IInteger div = num.divide(denom); IInteger rem = num.remainder(denom); if(div.compare(other) != 0) return div.compare(other); else return rem.signum(); } else if(isRationalType(other)){ IRational diff = subtract((IRational)other); return diff.signum(); } else { assert other instanceof IReal; return toReal(((IReal) other).precision()).compare(other); } } @Override public Type getType() { return RATIONAL_TYPE; } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E { return v.visitRational(this); } @Override public IRational negate() { return toRational(num.negate(), denom); } @Override public IReal toReal(int precision) { IReal r1 = num.toReal(precision); IReal r2 = denom.toReal(precision); r1 = r1.divide(r2, precision); return r1; } @Override public IInteger toInteger() { return num.divide(denom); } @Override public String getStringRepresentation() { return num.getStringRepresentation() + "r" + (denom.equals(intOne()) ? "" : denom.getStringRepresentation()); } @Override public int compare(IRational other) { IRational diff = subtract(other); return diff.signum(); } @Override public int signum() { return num.signum(); } @Override public IRational abs() { return toRational(num.abs(), denom); } @Override public IInteger floor() { return num.divide(denom); } @Override public IInteger round() { return toReal(2).round().toInteger(); } @Override public IRational toRational() { return this; } public IRational toRational(IInteger n, IInteger d) { return newRational(n, d); } @Override public IRational remainder(IRational other) { throw new UnsupportedOperationException(); } @Override public int hashCode() { if(denom.equals(intOne())) return num.hashCode(); else { final int prime = 31; int result = 1; result = prime * result + num.hashCode(); result = prime * result + denom.hashCode(); return result; } } @Override public IInteger numerator() { return num; } @Override public IInteger denominator() { return denom; } @Override public IInteger remainder() { return num.remainder(denom); } protected IInteger gcd(IInteger n, IInteger d) { n = n.abs(); d = d.abs(); while(d.signum() > 0) { IInteger tmp = d; d = n.mod(d); n = tmp; } return n; } protected IInteger intOne() { return IntegerValue.INTEGER_ONE; } @Override public double doubleValue() { return num.doubleValue() / denom.doubleValue(); } }