source: branches/1.3.1/src/org/armedbear/lisp/Ratio.java

Last change on this file was 14466, checked in by rschlatte, 12 years ago

call type_error when possible

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.1 KB
Line 
1/*
2 * Ratio.java
3 *
4 * Copyright (C) 2003-2005 Peter Graves
5 * $Id: Ratio.java 14466 2013-04-24 12:50:40Z rschlatte $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.math.BigInteger;
39
40public final class Ratio extends LispObject
41{
42    private BigInteger numerator;
43    private BigInteger denominator;
44
45    public Ratio(BigInteger numerator, BigInteger denominator)
46    {
47        this.numerator = numerator;
48        this.denominator = denominator;
49    }
50
51    public BigInteger numerator()
52    {
53        return numerator;
54    }
55
56    @Override
57    public LispObject NUMERATOR()
58    {
59        return number(numerator);
60    }
61
62    public BigInteger denominator()
63    {
64        return denominator;
65    }
66
67    @Override
68    public LispObject DENOMINATOR()
69    {
70        return number(denominator);
71    }
72
73    @Override
74    public LispObject typeOf()
75    {
76        return Symbol.RATIO;
77    }
78
79    @Override
80    public LispObject classOf()
81    {
82        return BuiltInClass.RATIO;
83    }
84
85    @Override
86    public LispObject typep(LispObject type)
87    {
88        if (type == Symbol.RATIO)
89            return T;
90        if (type == Symbol.RATIONAL)
91            return T;
92        if (type == Symbol.REAL)
93            return T;
94        if (type == Symbol.NUMBER)
95            return T;
96        if (type == BuiltInClass.RATIO)
97            return T;
98        return super.typep(type);
99    }
100
101    @Override
102    public boolean numberp()
103    {
104        return true;
105    }
106
107    @Override
108    public boolean rationalp()
109    {
110        return true;
111    }
112
113    @Override
114    public boolean realp()
115    {
116        return true;
117    }
118
119    @Override
120    public boolean eql(LispObject obj)
121    {
122        if (this == obj)
123            return true;
124        if (obj instanceof Ratio) {
125            return (numerator.equals(((Ratio)obj).numerator) &&
126                    denominator.equals(((Ratio)obj).denominator));
127        }
128        return false;
129    }
130
131    @Override
132    public boolean equal(LispObject obj)
133    {
134        return eql(obj);
135    }
136
137    @Override
138    public boolean equalp(LispObject obj)
139    {
140        if (obj instanceof Ratio) {
141            return numerator.equals(((Ratio)obj).numerator) &&
142                denominator.equals(((Ratio)obj).denominator);
143        }
144        if (obj instanceof SingleFloat) {
145            return floatValue() == ((SingleFloat)obj).value;
146        }
147        if (obj instanceof DoubleFloat) {
148            return doubleValue() == ((DoubleFloat)obj).value;
149        }
150        return false;
151    }
152
153    @Override
154    public LispObject ABS()
155    {
156        if (numerator.signum() > 0 && denominator.signum() > 0)
157            return this;
158        if (numerator.signum() < 0 && denominator.signum() < 0)
159            return this;
160        return new Ratio(numerator.negate(), denominator);
161    }
162
163    @Override
164    public boolean plusp()
165    {
166        return numerator.signum() == denominator.signum();
167    }
168
169    @Override
170    public boolean minusp()
171    {
172        return numerator.signum() != denominator.signum();
173    }
174
175    @Override
176    public boolean zerop()
177    {
178        return false;
179    }
180
181    @Override
182    public float floatValue()
183    {
184        float result = (float) doubleValue();
185        if (Float.isInfinite(result) && TRAP_OVERFLOW)
186            type_error(this, Symbol.SINGLE_FLOAT);
187
188        return (float) doubleValue();
189    }
190
191    @Override
192    public double doubleValue()
193    {
194        double result = numerator.doubleValue() / denominator.doubleValue();
195        if (result != 0 && !Double.isNaN(result) && !Double.isInfinite(result))
196            return result;
197        final boolean negative = numerator.signum() < 0;
198        final BigInteger num = negative ? numerator.negate() : numerator;
199        final BigInteger den = denominator;
200        final int numLen = num.bitLength();
201        final int denLen = den.bitLength();
202        int length = Math.min(numLen, denLen);
203        if (length <= 1)
204            return result;
205        BigInteger n = num;
206        BigInteger d = den;
207        final int digits = 54;
208        if (length > digits) {
209            n = n.shiftRight(length - digits);
210            d = d.shiftRight(length - digits);
211            length -= digits;
212        } else {
213            n = n.shiftRight(1);
214            d = d.shiftRight(1);
215            --length;
216        }
217        for (int i = 0; i < length; i++) {
218            result = n.doubleValue() / d.doubleValue();
219            if (result != 0 && !Double.isNaN(result) && !Double.isInfinite(result))
220                break;
221            n = n.shiftRight(1);
222            d = d.shiftRight(1);
223        }
224        if (Double.isInfinite(result) && TRAP_OVERFLOW)
225            type_error(this, Symbol.DOUBLE_FLOAT);
226
227        return negative ? -result : result;
228    }
229
230    @Override
231    public final LispObject incr()
232    {
233        return new Ratio(numerator.add(denominator), denominator);
234    }
235
236    @Override
237    public final LispObject decr()
238    {
239        return new Ratio(numerator.subtract(denominator), denominator);
240    }
241
242    @Override
243    public LispObject add(LispObject obj)
244    {
245        if (obj instanceof Fixnum) {
246            BigInteger n =
247                numerator.add(BigInteger.valueOf(((Fixnum)obj).value).multiply(denominator));
248            return number(n, denominator);
249        }
250        if (obj instanceof Bignum) {
251            BigInteger n = ((Bignum)obj).value;
252            return number(numerator.add(n.multiply(denominator)),
253                denominator);
254        }
255        if (obj instanceof Ratio) {
256            BigInteger n = ((Ratio)obj).numerator;
257            BigInteger d = ((Ratio)obj).denominator;
258            if (denominator.equals(d))
259                return number(numerator.add(n), denominator);
260            BigInteger common = denominator.multiply(d);
261            return number(numerator.multiply(d).add(n.multiply(denominator)),
262                common);
263        }
264        if (obj instanceof SingleFloat) {
265            return new SingleFloat(floatValue() + ((SingleFloat)obj).value);
266        }
267        if (obj instanceof DoubleFloat) {
268            return new DoubleFloat(doubleValue() + ((DoubleFloat)obj).value);
269        }
270        if (obj instanceof Complex) {
271            Complex c = (Complex) obj;
272            return Complex.getInstance(add(c.getRealPart()), c.getImaginaryPart());
273        }
274        return type_error(obj, Symbol.NUMBER);
275    }
276
277    @Override
278    public LispObject subtract(LispObject obj)
279    {
280        if (obj instanceof Fixnum) {
281            BigInteger n =
282                numerator.subtract(BigInteger.valueOf(((Fixnum)obj).value).multiply(denominator));
283            return number(n, denominator);
284        }
285        if (obj instanceof Bignum) {
286            BigInteger n = ((Bignum)obj).value;
287            return number(numerator.subtract(n.multiply(denominator)),
288                denominator);
289        }
290        if (obj instanceof Ratio) {
291            BigInteger n = ((Ratio)obj).numerator;
292            BigInteger d = ((Ratio)obj).denominator;
293            if (denominator.equals(d))
294                return number(numerator.subtract(n), denominator);
295            BigInteger common = denominator.multiply(d);
296            return number(numerator.multiply(d).subtract(n.multiply(denominator)),
297                common);
298        }
299        if (obj instanceof SingleFloat) {
300            return new SingleFloat(floatValue() - ((SingleFloat)obj).value);
301        }
302        if (obj instanceof DoubleFloat) {
303            return new DoubleFloat(doubleValue() - ((DoubleFloat)obj).value);
304        }
305        if (obj instanceof Complex) {
306            Complex c = (Complex) obj;
307            return Complex.getInstance(subtract(c.getRealPart()),
308                                       Fixnum.ZERO.subtract(c.getImaginaryPart()));
309        }
310        return type_error(obj, Symbol.NUMBER);
311    }
312
313    @Override
314    public LispObject multiplyBy(LispObject obj)
315    {
316        if (obj instanceof Fixnum) {
317            BigInteger n = ((Fixnum)obj).getBigInteger();
318            return number(numerator.multiply(n), denominator);
319        }
320        if (obj instanceof Bignum) {
321            BigInteger n = ((Bignum)obj).value;
322            return number(numerator.multiply(n), denominator);
323        }
324        if (obj instanceof Ratio) {
325            BigInteger n = ((Ratio)obj).numerator;
326            BigInteger d = ((Ratio)obj).denominator;
327            return number(numerator.multiply(n), denominator.multiply(d));
328        }
329        if (obj instanceof SingleFloat) {
330            return new SingleFloat(floatValue() * ((SingleFloat)obj).value);
331        }
332        if (obj instanceof DoubleFloat) {
333            return new DoubleFloat(doubleValue() * ((DoubleFloat)obj).value);
334        }
335        if (obj instanceof Complex) {
336            Complex c = (Complex) obj;
337            return Complex.getInstance(multiplyBy(c.getRealPart()),
338                                       multiplyBy(c.getImaginaryPart()));
339        }
340        return type_error(obj, Symbol.NUMBER);
341    }
342
343    @Override
344    public LispObject divideBy(LispObject obj)
345    {
346        if (obj instanceof Fixnum) {
347            BigInteger n = ((Fixnum)obj).getBigInteger();
348            return number(numerator, denominator.multiply(n));
349        }
350        if (obj instanceof Bignum) {
351            BigInteger n = ((Bignum)obj).value;
352            return number(numerator, denominator.multiply(n));
353        }
354        if (obj instanceof Ratio) {
355            BigInteger n = ((Ratio)obj).numerator;
356            BigInteger d = ((Ratio)obj).denominator;
357            return number(numerator.multiply(d), denominator.multiply(n));
358        }
359        if (obj instanceof SingleFloat) {
360            if (obj.zerop())
361                return error(new DivisionByZero());
362            return new SingleFloat(floatValue() / ((SingleFloat)obj).value);
363        }
364        if (obj instanceof DoubleFloat) {
365            if (obj.zerop())
366                return error(new DivisionByZero());
367            return new DoubleFloat(doubleValue() / ((DoubleFloat)obj).value);
368        }
369        if (obj instanceof Complex) {
370            Complex c = (Complex) obj;
371            // numerator
372            LispObject realPart = this.multiplyBy(c.getRealPart());
373            LispObject imagPart =
374                Fixnum.ZERO.subtract(this).multiplyBy(c.getImaginaryPart());
375            // denominator
376            LispObject d =
377                c.getRealPart().multiplyBy(c.getRealPart());
378            d = d.add(c.getImaginaryPart().multiplyBy(c.getImaginaryPart()));
379            return Complex.getInstance(realPart.divideBy(d),
380                                       imagPart.divideBy(d));
381        }
382        return type_error(obj, Symbol.NUMBER);
383    }
384
385    @Override
386    public boolean isEqualTo(LispObject obj)
387    {
388        if (obj instanceof Ratio)
389            return (numerator.equals(((Ratio)obj).numerator) &&
390                    denominator.equals(((Ratio)obj).denominator));
391        if (obj instanceof SingleFloat)
392            return isEqualTo(((SingleFloat)obj).rational());
393        if (obj instanceof DoubleFloat)
394            return isEqualTo(((DoubleFloat)obj).rational());
395        if (obj.numberp())
396            return false;
397        type_error(obj, Symbol.NUMBER);
398        // Not reached.
399        return false;
400    }
401
402    @Override
403    public boolean isNotEqualTo(LispObject obj)
404    {
405        return !isEqualTo(obj);
406    }
407
408    @Override
409    public boolean isLessThan(LispObject obj)
410    {
411        if (obj instanceof Fixnum) {
412            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
413            return numerator.compareTo(n2) < 0;
414        }
415        if (obj instanceof Bignum) {
416            BigInteger n = ((Bignum)obj).value.multiply(denominator);
417            return numerator.compareTo(n) < 0;
418        }
419        if (obj instanceof Ratio) {
420            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
421            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
422            return n1.compareTo(n2) < 0;
423        }
424        if (obj instanceof SingleFloat)
425            return isLessThan(((SingleFloat)obj).rational());
426        if (obj instanceof DoubleFloat)
427            return isLessThan(((DoubleFloat)obj).rational());
428        type_error(obj, Symbol.REAL);
429        // Not reached.
430        return false;
431    }
432
433    @Override
434    public boolean isGreaterThan(LispObject obj)
435    {
436        if (obj instanceof Fixnum) {
437            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
438            return numerator.compareTo(n2) > 0;
439        }
440        if (obj instanceof Bignum) {
441            BigInteger n = ((Bignum)obj).value.multiply(denominator);
442            return numerator.compareTo(n) > 0;
443        }
444        if (obj instanceof Ratio) {
445            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
446            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
447            return n1.compareTo(n2) > 0;
448        }
449        if (obj instanceof SingleFloat)
450            return isGreaterThan(((SingleFloat)obj).rational());
451        if (obj instanceof DoubleFloat)
452            return isGreaterThan(((DoubleFloat)obj).rational());
453        type_error(obj, Symbol.REAL);
454        // Not reached.
455        return false;
456    }
457
458    @Override
459    public boolean isLessThanOrEqualTo(LispObject obj)
460    {
461        if (obj instanceof Fixnum) {
462            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
463            return numerator.compareTo(n2) <= 0;
464        }
465        if (obj instanceof Bignum) {
466            BigInteger n = ((Bignum)obj).value.multiply(denominator);
467            return numerator.compareTo(n) <= 0;
468        }
469        if (obj instanceof Ratio) {
470            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
471            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
472            return n1.compareTo(n2) <= 0;
473        }
474        if (obj instanceof SingleFloat)
475            return isLessThanOrEqualTo(((SingleFloat)obj).rational());
476        if (obj instanceof DoubleFloat)
477            return isLessThanOrEqualTo(((DoubleFloat)obj).rational());
478        type_error(obj, Symbol.REAL);
479        // Not reached.
480        return false;
481    }
482
483    @Override
484    public boolean isGreaterThanOrEqualTo(LispObject obj)
485    {
486        if (obj instanceof Fixnum) {
487            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
488            return numerator.compareTo(n2) >= 0;
489        }
490        if (obj instanceof Bignum) {
491            BigInteger n = ((Bignum)obj).value.multiply(denominator);
492            return numerator.compareTo(n) >= 0;
493        }
494        if (obj instanceof Ratio) {
495            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
496            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
497            return n1.compareTo(n2) >= 0;
498        }
499        if (obj instanceof SingleFloat)
500            return isGreaterThanOrEqualTo(((SingleFloat)obj).rational());
501        if (obj instanceof DoubleFloat)
502            return isGreaterThanOrEqualTo(((DoubleFloat)obj).rational());
503        type_error(obj, Symbol.REAL);
504        // Not reached.
505        return false;
506    }
507
508    @Override
509    public LispObject truncate(LispObject obj)
510    {
511        // "When rationals and floats are combined by a numerical function,
512        // the rational is first converted to a float of the same format."
513        // 12.1.4.1
514        if (obj instanceof SingleFloat)
515            return new SingleFloat(floatValue()).truncate(obj);
516        if (obj instanceof DoubleFloat)
517            return new DoubleFloat(doubleValue()).truncate(obj);
518        BigInteger n, d;
519  try {
520    if (obj instanceof Fixnum) {
521            n = ((Fixnum)obj).getBigInteger();
522            d = BigInteger.ONE;
523    } else if (obj instanceof Bignum) {
524            n = ((Bignum)obj).value;
525            d = BigInteger.ONE;
526    } else if (obj instanceof Ratio) {
527            n = ((Ratio)obj).numerator();
528            d = ((Ratio)obj).denominator();
529    } else {
530            return type_error(obj, Symbol.NUMBER);
531    }
532    // Invert and multiply.
533    BigInteger num = numerator.multiply(d);
534    BigInteger den = denominator.multiply(n);
535    BigInteger quotient = num.divide(den);
536    // Multiply quotient by divisor.
537    LispObject product = number(quotient.multiply(n), d);
538    // Subtract to get remainder.
539    LispObject remainder = subtract(product);
540          return LispThread.currentThread().setValues(number(quotient), remainder);
541        }
542        catch (ArithmeticException e) {
543            if (obj.zerop())
544                return error(new DivisionByZero());
545            return error(new ArithmeticError(e.getMessage()));
546        }
547    }
548
549    @Override
550    public int hashCode()
551    {
552        return numerator.hashCode() ^ denominator.hashCode();
553    }
554
555    @Override
556    public String printObject()
557    {
558        final LispThread thread = LispThread.currentThread();
559        int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue(thread));
560        StringBuffer sb = new StringBuffer(numerator.toString(base));
561        sb.append('/');
562        sb.append(denominator.toString(base));
563        String s = sb.toString().toUpperCase();
564        if (Symbol.PRINT_RADIX.symbolValue(thread) != NIL) {
565            sb.setLength(0);
566            switch (base) {
567                case 2:
568                    sb.append("#b");
569                    sb.append(s);
570                    break;
571                case 8:
572                    sb.append("#o");
573                    sb.append(s);
574                    break;
575                case 10:
576                    sb.append("#10r");
577                    sb.append(s);
578                    break;
579                case 16:
580                    sb.append("#x");
581                    sb.append(s);
582                    break;
583                default:
584                    sb.append('#');
585                    sb.append(String.valueOf(base));
586                    sb.append('r');
587                    sb.append(s);
588                    break;
589            }
590            s = sb.toString();
591        }
592        return s;
593    }
594}
Note: See TracBrowser for help on using the repository browser.