001: // Copyright (c) 1997 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.math;
005:
006: /** The abstract class of rational numbers. */
007:
008: public abstract class RatNum extends RealNum {
009: public abstract IntNum numerator();
010:
011: public abstract IntNum denominator();
012:
013: public static RatNum make(IntNum num, IntNum den) {
014: IntNum g = IntNum.gcd(num, den);
015: if (den.isNegative())
016: g = IntNum.neg(g);
017: if (!g.isOne()) {
018: num = IntNum.quotient(num, g);
019: den = IntNum.quotient(den, g);
020: }
021: return den.isOne() ? (RatNum) num : (RatNum) (new IntFraction(
022: num, den));
023: }
024:
025: public boolean isExact() {
026: return true;
027: }
028:
029: public boolean isZero() {
030: return numerator().isZero();
031: }
032:
033: /** Return exact "rational" infinity.
034: * @param sign either 1 or -1 for positive or negative infinity */
035: public static RatNum infinity(int sign) {
036: return new IntFraction(IntNum.make(sign), IntNum.zero());
037: }
038:
039: public static int compare(RatNum x, RatNum y) {
040: return IntNum.compare(IntNum.times(x.numerator(), y
041: .denominator()), IntNum.times(y.numerator(), x
042: .denominator()));
043: }
044:
045: /* Assumes x and y are both canonicalized. */
046: public static boolean equals(RatNum x, RatNum y) {
047: return IntNum.equals(x.numerator(), y.numerator())
048: && IntNum.equals(x.denominator(), y.denominator());
049: }
050:
051: /* Assumes this and obj are both canonicalized. */
052: public boolean equals(Object obj) {
053: if (obj == null || !(obj instanceof RatNum))
054: return false;
055: return RatNum.equals(this , (RatNum) obj);
056: }
057:
058: public static RatNum add(RatNum x, RatNum y, int k) {
059: IntNum x_num = x.numerator();
060: IntNum x_den = x.denominator();
061: IntNum y_num = y.numerator();
062: IntNum y_den = y.denominator();
063: if (IntNum.equals(x_den, y_den))
064: return RatNum.make(IntNum.add(x_num, y_num, k), x_den);
065: return RatNum.make(IntNum.add(IntNum.times(y_den, x_num),
066: IntNum.times(y_num, x_den), k), IntNum.times(x_den,
067: y_den));
068: }
069:
070: public static RatNum times(RatNum x, RatNum y) {
071: return RatNum.make(IntNum.times(x.numerator(), y.numerator()),
072: IntNum.times(x.denominator(), y.denominator()));
073: }
074:
075: public static RatNum divide(RatNum x, RatNum y) {
076: return RatNum.make(
077: IntNum.times(x.numerator(), y.denominator()), IntNum
078: .times(x.denominator(), y.numerator()));
079: }
080:
081: public Numeric power(IntNum y) {
082: boolean inv;
083: if (y.isNegative()) {
084: inv = true;
085: y = IntNum.neg(y);
086: } else
087: inv = false;
088: if (y.words == null) {
089: IntNum num = IntNum.power(numerator(), y.ival);
090: IntNum den = IntNum.power(denominator(), y.ival);
091: return inv ? RatNum.make(den, num) : RatNum.make(num, den);
092: }
093: double d = doubleValue();
094: boolean neg = d < 0.0 && y.isOdd();
095: d = Math.pow(d, y.doubleValue());
096: if (inv)
097: d = 1.0 / d;
098: return new DFloNum(neg ? -d : d);
099: }
100:
101: public final RatNum toExact() {
102: return this ;
103: }
104:
105: public RealNum toInt(int rounding_mode) {
106: return IntNum.quotient(numerator(), denominator(),
107: rounding_mode);
108: }
109:
110: public IntNum toExactInt(int rounding_mode) {
111: return IntNum.quotient(numerator(), denominator(),
112: rounding_mode);
113: }
114:
115: /** Calcaulte the simplest rational between two reals. */
116: public static RealNum rationalize(RealNum x, RealNum y) {
117: // This algorithm is by Alan Bawden. It has been transcribed
118: // with permission from C-Gambit, copyright Marc Feeley.
119: if (x.grt(y))
120: return simplest_rational2(y, x);
121: else if (!(y.grt(x)))
122: return x;
123: else if (x.sign() > 0)
124: return simplest_rational2(x, y);
125: else if (y.isNegative())
126: return (RealNum) (simplest_rational2((RealNum) y.neg(),
127: (RealNum) x.neg())).neg();
128: else
129: return IntNum.zero();
130: }
131:
132: private static RealNum simplest_rational2(RealNum x, RealNum y) {
133: RealNum fx = x.toInt(FLOOR);
134: RealNum fy = y.toInt(FLOOR);
135: if (!x.grt(fx))
136: return fx;
137: else if (fx.equals(fy)) {
138: RealNum n = (RealNum) IntNum.one().div(y.sub(fy));
139: RealNum d = (RealNum) IntNum.one().div(x.sub(fx));
140: return (RealNum) fx.add(IntNum.one().div(
141: simplest_rational2(n, d)), 1);
142: } else
143: return (RealNum) fx.add(IntNum.one(), 1);
144: }
145: }
|