source: branches/streams/abcl/src/org/armedbear/lisp/Ratio.java

Last change on this file was 14757, checked in by Mark Evenson, 10 years ago

Futher fix for EQUALP on numeric tower

This fixes the following case

(let ((h1 (make-hash-table :test 'equalp))

(h2 (make-hash-table :test 'equalp))
(h (make-hash-table :test 'equalp)))

(setf (gethash 1 h1) 2

(gethash 2 h2) 1
(gethash h1 h) h2
(gethash h2 h) h1)

h)

See <https://mailman.common-lisp.net/pipermail/armedbear-devel/2015-April/003452.html>.
See <http://abcl.org/trac/ticket/388>.

Thanks to Massimiliano Ghilardi.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.8 KB
Line 
1/*
2 * Ratio.java
3 *
4 * Copyright (C) 2003-2005 Peter Graves
5 * $Id: Ratio.java 14757 2015-04-11 07:44:42Z mevenson $
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 != null && obj.numberp())
141            return isEqualTo(obj);
142        return false;
143    }
144
145    @Override
146    public LispObject ABS()
147    {
148        if (numerator.signum() > 0 && denominator.signum() > 0)
149            return this;
150        if (numerator.signum() < 0 && denominator.signum() < 0)
151            return this;
152        return new Ratio(numerator.negate(), denominator);
153    }
154
155    @Override
156    public boolean plusp()
157    {
158        return numerator.signum() == denominator.signum();
159    }
160
161    @Override
162    public boolean minusp()
163    {
164        return numerator.signum() != denominator.signum();
165    }
166
167    @Override
168    public boolean zerop()
169    {
170        return false;
171    }
172
173    @Override
174    public float floatValue()
175    {
176        float result = (float) doubleValue();
177        if (Float.isInfinite(result) && TRAP_OVERFLOW)
178            type_error(this, Symbol.SINGLE_FLOAT);
179
180        return (float) doubleValue();
181    }
182
183    @Override
184    public double doubleValue()
185    {
186        double result = numerator.doubleValue() / denominator.doubleValue();
187        if (result != 0 && !Double.isNaN(result) && !Double.isInfinite(result))
188            return result;
189        final boolean negative = numerator.signum() < 0;
190        final BigInteger num = negative ? numerator.negate() : numerator;
191        final BigInteger den = denominator;
192        final int numLen = num.bitLength();
193        final int denLen = den.bitLength();
194        int length = Math.min(numLen, denLen);
195        if (length <= 1)
196            return result;
197        BigInteger n = num;
198        BigInteger d = den;
199        final int digits = 54;
200        if (length > digits) {
201            n = n.shiftRight(length - digits);
202            d = d.shiftRight(length - digits);
203            length -= digits;
204        } else {
205            n = n.shiftRight(1);
206            d = d.shiftRight(1);
207            --length;
208        }
209        for (int i = 0; i < length; i++) {
210            result = n.doubleValue() / d.doubleValue();
211            if (result != 0 && !Double.isNaN(result) && !Double.isInfinite(result))
212                break;
213            n = n.shiftRight(1);
214            d = d.shiftRight(1);
215        }
216        if (Double.isInfinite(result) && TRAP_OVERFLOW)
217            type_error(this, Symbol.DOUBLE_FLOAT);
218
219        return negative ? -result : result;
220    }
221
222    @Override
223    public final LispObject incr()
224    {
225        return new Ratio(numerator.add(denominator), denominator);
226    }
227
228    @Override
229    public final LispObject decr()
230    {
231        return new Ratio(numerator.subtract(denominator), denominator);
232    }
233
234    @Override
235    public LispObject add(LispObject obj)
236    {
237        if (obj instanceof Fixnum) {
238            BigInteger n =
239                numerator.add(BigInteger.valueOf(((Fixnum)obj).value).multiply(denominator));
240            return number(n, denominator);
241        }
242        if (obj instanceof Bignum) {
243            BigInteger n = ((Bignum)obj).value;
244            return number(numerator.add(n.multiply(denominator)),
245                denominator);
246        }
247        if (obj instanceof Ratio) {
248            BigInteger n = ((Ratio)obj).numerator;
249            BigInteger d = ((Ratio)obj).denominator;
250            if (denominator.equals(d))
251                return number(numerator.add(n), denominator);
252            BigInteger common = denominator.multiply(d);
253            return number(numerator.multiply(d).add(n.multiply(denominator)),
254                common);
255        }
256        if (obj instanceof SingleFloat) {
257            return new SingleFloat(floatValue() + ((SingleFloat)obj).value);
258        }
259        if (obj instanceof DoubleFloat) {
260            return new DoubleFloat(doubleValue() + ((DoubleFloat)obj).value);
261        }
262        if (obj instanceof Complex) {
263            Complex c = (Complex) obj;
264            return Complex.getInstance(add(c.getRealPart()), c.getImaginaryPart());
265        }
266        return type_error(obj, Symbol.NUMBER);
267    }
268
269    @Override
270    public LispObject subtract(LispObject obj)
271    {
272        if (obj instanceof Fixnum) {
273            BigInteger n =
274                numerator.subtract(BigInteger.valueOf(((Fixnum)obj).value).multiply(denominator));
275            return number(n, denominator);
276        }
277        if (obj instanceof Bignum) {
278            BigInteger n = ((Bignum)obj).value;
279            return number(numerator.subtract(n.multiply(denominator)),
280                denominator);
281        }
282        if (obj instanceof Ratio) {
283            BigInteger n = ((Ratio)obj).numerator;
284            BigInteger d = ((Ratio)obj).denominator;
285            if (denominator.equals(d))
286                return number(numerator.subtract(n), denominator);
287            BigInteger common = denominator.multiply(d);
288            return number(numerator.multiply(d).subtract(n.multiply(denominator)),
289                common);
290        }
291        if (obj instanceof SingleFloat) {
292            return new SingleFloat(floatValue() - ((SingleFloat)obj).value);
293        }
294        if (obj instanceof DoubleFloat) {
295            return new DoubleFloat(doubleValue() - ((DoubleFloat)obj).value);
296        }
297        if (obj instanceof Complex) {
298            Complex c = (Complex) obj;
299            return Complex.getInstance(subtract(c.getRealPart()),
300                                       Fixnum.ZERO.subtract(c.getImaginaryPart()));
301        }
302        return type_error(obj, Symbol.NUMBER);
303    }
304
305    @Override
306    public LispObject multiplyBy(LispObject obj)
307    {
308        if (obj instanceof Fixnum) {
309            BigInteger n = ((Fixnum)obj).getBigInteger();
310            return number(numerator.multiply(n), denominator);
311        }
312        if (obj instanceof Bignum) {
313            BigInteger n = ((Bignum)obj).value;
314            return number(numerator.multiply(n), denominator);
315        }
316        if (obj instanceof Ratio) {
317            BigInteger n = ((Ratio)obj).numerator;
318            BigInteger d = ((Ratio)obj).denominator;
319            return number(numerator.multiply(n), denominator.multiply(d));
320        }
321        if (obj instanceof SingleFloat) {
322            return new SingleFloat(floatValue() * ((SingleFloat)obj).value);
323        }
324        if (obj instanceof DoubleFloat) {
325            return new DoubleFloat(doubleValue() * ((DoubleFloat)obj).value);
326        }
327        if (obj instanceof Complex) {
328            Complex c = (Complex) obj;
329            return Complex.getInstance(multiplyBy(c.getRealPart()),
330                                       multiplyBy(c.getImaginaryPart()));
331        }
332        return type_error(obj, Symbol.NUMBER);
333    }
334
335    @Override
336    public LispObject divideBy(LispObject obj)
337    {
338        if (obj instanceof Fixnum) {
339            BigInteger n = ((Fixnum)obj).getBigInteger();
340            return number(numerator, denominator.multiply(n));
341        }
342        if (obj instanceof Bignum) {
343            BigInteger n = ((Bignum)obj).value;
344            return number(numerator, denominator.multiply(n));
345        }
346        if (obj instanceof Ratio) {
347            BigInteger n = ((Ratio)obj).numerator;
348            BigInteger d = ((Ratio)obj).denominator;
349            return number(numerator.multiply(d), denominator.multiply(n));
350        }
351        if (obj instanceof SingleFloat) {
352            if (obj.zerop())
353                return error(new DivisionByZero());
354            return new SingleFloat(floatValue() / ((SingleFloat)obj).value);
355        }
356        if (obj instanceof DoubleFloat) {
357            if (obj.zerop())
358                return error(new DivisionByZero());
359            return new DoubleFloat(doubleValue() / ((DoubleFloat)obj).value);
360        }
361        if (obj instanceof Complex) {
362            Complex c = (Complex) obj;
363            // numerator
364            LispObject realPart = this.multiplyBy(c.getRealPart());
365            LispObject imagPart =
366                Fixnum.ZERO.subtract(this).multiplyBy(c.getImaginaryPart());
367            // denominator
368            LispObject d =
369                c.getRealPart().multiplyBy(c.getRealPart());
370            d = d.add(c.getImaginaryPart().multiplyBy(c.getImaginaryPart()));
371            return Complex.getInstance(realPart.divideBy(d),
372                                       imagPart.divideBy(d));
373        }
374        return type_error(obj, Symbol.NUMBER);
375    }
376
377    @Override
378    public boolean isEqualTo(LispObject obj)
379    {
380        if (obj instanceof Ratio)
381            return (numerator.equals(((Ratio)obj).numerator) &&
382                    denominator.equals(((Ratio)obj).denominator));
383        if (obj instanceof SingleFloat)
384            return isEqualTo(((SingleFloat)obj).rational());
385        if (obj instanceof DoubleFloat)
386            return isEqualTo(((DoubleFloat)obj).rational());
387        if (obj.numberp())
388            return false;
389        type_error(obj, Symbol.NUMBER);
390        // Not reached.
391        return false;
392    }
393
394    @Override
395    public boolean isNotEqualTo(LispObject obj)
396    {
397        return !isEqualTo(obj);
398    }
399
400    @Override
401    public boolean isLessThan(LispObject obj)
402    {
403        if (obj instanceof Fixnum) {
404            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
405            return numerator.compareTo(n2) < 0;
406        }
407        if (obj instanceof Bignum) {
408            BigInteger n = ((Bignum)obj).value.multiply(denominator);
409            return numerator.compareTo(n) < 0;
410        }
411        if (obj instanceof Ratio) {
412            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
413            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
414            return n1.compareTo(n2) < 0;
415        }
416        if (obj instanceof SingleFloat)
417            return isLessThan(((SingleFloat)obj).rational());
418        if (obj instanceof DoubleFloat)
419            return isLessThan(((DoubleFloat)obj).rational());
420        type_error(obj, Symbol.REAL);
421        // Not reached.
422        return false;
423    }
424
425    @Override
426    public boolean isGreaterThan(LispObject obj)
427    {
428        if (obj instanceof Fixnum) {
429            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
430            return numerator.compareTo(n2) > 0;
431        }
432        if (obj instanceof Bignum) {
433            BigInteger n = ((Bignum)obj).value.multiply(denominator);
434            return numerator.compareTo(n) > 0;
435        }
436        if (obj instanceof Ratio) {
437            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
438            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
439            return n1.compareTo(n2) > 0;
440        }
441        if (obj instanceof SingleFloat)
442            return isGreaterThan(((SingleFloat)obj).rational());
443        if (obj instanceof DoubleFloat)
444            return isGreaterThan(((DoubleFloat)obj).rational());
445        type_error(obj, Symbol.REAL);
446        // Not reached.
447        return false;
448    }
449
450    @Override
451    public boolean isLessThanOrEqualTo(LispObject obj)
452    {
453        if (obj instanceof Fixnum) {
454            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
455            return numerator.compareTo(n2) <= 0;
456        }
457        if (obj instanceof Bignum) {
458            BigInteger n = ((Bignum)obj).value.multiply(denominator);
459            return numerator.compareTo(n) <= 0;
460        }
461        if (obj instanceof Ratio) {
462            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
463            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
464            return n1.compareTo(n2) <= 0;
465        }
466        if (obj instanceof SingleFloat)
467            return isLessThanOrEqualTo(((SingleFloat)obj).rational());
468        if (obj instanceof DoubleFloat)
469            return isLessThanOrEqualTo(((DoubleFloat)obj).rational());
470        type_error(obj, Symbol.REAL);
471        // Not reached.
472        return false;
473    }
474
475    @Override
476    public boolean isGreaterThanOrEqualTo(LispObject obj)
477    {
478        if (obj instanceof Fixnum) {
479            BigInteger n2 = ((Fixnum)obj).getBigInteger().multiply(denominator);
480            return numerator.compareTo(n2) >= 0;
481        }
482        if (obj instanceof Bignum) {
483            BigInteger n = ((Bignum)obj).value.multiply(denominator);
484            return numerator.compareTo(n) >= 0;
485        }
486        if (obj instanceof Ratio) {
487            BigInteger n1 = numerator.multiply(((Ratio)obj).denominator);
488            BigInteger n2 = ((Ratio)obj).numerator.multiply(denominator);
489            return n1.compareTo(n2) >= 0;
490        }
491        if (obj instanceof SingleFloat)
492            return isGreaterThanOrEqualTo(((SingleFloat)obj).rational());
493        if (obj instanceof DoubleFloat)
494            return isGreaterThanOrEqualTo(((DoubleFloat)obj).rational());
495        type_error(obj, Symbol.REAL);
496        // Not reached.
497        return false;
498    }
499
500    @Override
501    public LispObject truncate(LispObject obj)
502    {
503        // "When rationals and floats are combined by a numerical function,
504        // the rational is first converted to a float of the same format."
505        // 12.1.4.1
506        if (obj instanceof SingleFloat)
507            return new SingleFloat(floatValue()).truncate(obj);
508        if (obj instanceof DoubleFloat)
509            return new DoubleFloat(doubleValue()).truncate(obj);
510        BigInteger n, d;
511  try {
512    if (obj instanceof Fixnum) {
513            n = ((Fixnum)obj).getBigInteger();
514            d = BigInteger.ONE;
515    } else if (obj instanceof Bignum) {
516            n = ((Bignum)obj).value;
517            d = BigInteger.ONE;
518    } else if (obj instanceof Ratio) {
519            n = ((Ratio)obj).numerator();
520            d = ((Ratio)obj).denominator();
521    } else {
522            return type_error(obj, Symbol.NUMBER);
523    }
524    // Invert and multiply.
525    BigInteger num = numerator.multiply(d);
526    BigInteger den = denominator.multiply(n);
527    BigInteger quotient = num.divide(den);
528    // Multiply quotient by divisor.
529    LispObject product = number(quotient.multiply(n), d);
530    // Subtract to get remainder.
531    LispObject remainder = subtract(product);
532          return LispThread.currentThread().setValues(number(quotient), remainder);
533        }
534        catch (ArithmeticException e) {
535            if (obj.zerop())
536                return error(new DivisionByZero());
537            return error(new ArithmeticError(e.getMessage()));
538        }
539    }
540
541    @Override
542    public int hashCode()
543    {
544        return numerator.hashCode() ^ denominator.hashCode();
545    }
546
547    @Override
548    public String printObject()
549    {
550        final LispThread thread = LispThread.currentThread();
551        int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue(thread));
552        StringBuffer sb = new StringBuffer(numerator.toString(base));
553        sb.append('/');
554        sb.append(denominator.toString(base));
555        String s = sb.toString().toUpperCase();
556        if (Symbol.PRINT_RADIX.symbolValue(thread) != NIL) {
557            sb.setLength(0);
558            switch (base) {
559                case 2:
560                    sb.append("#b");
561                    sb.append(s);
562                    break;
563                case 8:
564                    sb.append("#o");
565                    sb.append(s);
566                    break;
567                case 10:
568                    sb.append("#10r");
569                    sb.append(s);
570                    break;
571                case 16:
572                    sb.append("#x");
573                    sb.append(s);
574                    break;
575                default:
576                    sb.append('#');
577                    sb.append(String.valueOf(base));
578                    sb.append('r');
579                    sb.append(s);
580                    break;
581            }
582            s = sb.toString();
583        }
584        return s;
585    }
586}
Note: See TracBrowser for help on using the repository browser.