1 /*
2  * Arithmetic.cpp -
3  *
4  *   Copyright (c) 2008  Higepon(Taro Minowa)  <higepon@users.sourceforge.jp>
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above copyright
14  *      notice, this list of conditions and the following disclaimer in the
15  *      documentation and/or other materials provided with the distribution.
16  *
17  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  $Id: Arithmetic.cpp 183 2008-07-04 06:19:28Z higepon $
30  */
31 
32 #include "Arithmetic.h"
33 #include "Object.h"
34 #include "Object-inl.h"
35 #include "Pair.h"
36 #include "Pair-inl.h"
37 #include "Bignum.h"
38 #include "Ratnum.h"
39 #include "Flonum.h"
40 #include "Fixnum.h"
41 #include "Compnum.h"
42 #include "SString.h"
43 #include "VM.h"
44 #include "ErrorProcedures.h"
45 #include "StringProcedures.h"
46 #include "ProcedureMacro.h"
47 #include "TextualOutputPort.h"
48 
49 #ifdef _WIN32
50     #define snprintf _snprintf
51 #endif
52 using namespace scheme;
53 
numberToString(Object n,int radix)54 Object Arithmetic::numberToString(Object n, int radix)
55 {
56     MOSH_ASSERT(n.isNumber());
57     MOSH_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16);
58     if (n.isFixnum()) {
59         const int fn = n.toFixnum();
60         char buf[64];
61         char* start = buf;
62         if (fn < 0) {
63             *start++ = '-';
64         }
65         long unsigned ufn = ::abs(fn);
66         switch(radix) {
67         case 16:
68             snprintf(start, 64, "%lx", ufn);
69             break;
70         case 8:
71             snprintf(start, 64, "%lo", ufn);
72             break;
73         case 2:
74         {
75             Bignum* const b = new Bignum(ufn);
76             snprintf(start, 64, "%s", b->toString(2));
77             break;
78         }
79         case 10: // fallthrough
80         default:
81             snprintf(start, 64, "%ld", ufn);
82             break;
83         }
84         return Object::makeString(buf);
85     } else if (n.isFlonum()) {
86         MOSH_ASSERT(radix == 10);
87         const double value = n.toFlonum()->value();
88 
89         if (n.toFlonum()->isNan()) {
90             return Object::makeString("+nan.0");
91         } else if (n.toFlonum()->isInfinite()) {
92             return Object::makeString((value > 0) ? "+inf.0" : "-inf.0");
93         } else {
94         return Object::makeString(FlonumUtil::flonumToUcs4String(value, false));
95 //             char buf[256];
96 //             snprintf(buf, 256, "%f", value);
97 //             return Object::makeString(buf);
98         }
99     } else if (n.isBignum()) {
100         return Object::makeString(n.toBignum()->toString(radix));
101     } else if (n.isRatnum()) {
102         Ratnum* const r = n.toRatnum();
103         return Object::makeString(r->toString(radix));
104     } else if (n.isCompnum()) {
105         Compnum* const c = n.toCompnum();
106         return format(NULL, UC("~d~a~di"), Pair::list3(numberToString(c->real(), radix),
107                                                       lt(c->imag(), Object::makeFixnum(0)) ? UC("") : UC("+"),
108                                                       numberToString(c->imag(), radix)));
109     } else {
110         MOSH_ASSERT(false);
111         return Object::Undef;
112     }
113 }
114 
real(Object n)115 Object Arithmetic::real(Object n)
116 {
117     MOSH_ASSERT(n.isNumber());
118     if (n.isCompnum()) {
119         return n.toCompnum()->real();
120     } else {
121         return n;
122     }
123 }
124 
imag(Object n)125 Object Arithmetic::imag(Object n)
126 {
127     if (n.isCompnum()) {
128         return n.toCompnum()->imag();
129     } else {
130         return Object::makeFixnum(0);
131     }
132 }
133 
expt(Object n1,Object n2)134 Object Arithmetic::expt(Object n1, Object n2)
135 {
136     MOSH_ASSERT(n1.isNumber());
137     MOSH_ASSERT(n2.isNumber());
138     MOSH_ASSERT(!n2.isBignum()); // too big.
139     if (n1.isFixnum()) {
140         if (n2.isFixnum()) {
141             const int fn2 = n2.toFixnum();
142             if (fn2 > 0) {
143                 return Bignum::pow(n1.toFixnum(), fn2);
144             } else if (fn2 == 0) {
145                 return Object::makeFixnum(1);
146             } else {
147                 const int fn1 = n1.toFixnum();
148                 if (0 == fn1) {
149                     return Object::Undef;
150                 }
151                 // inverse
152                 Object ret = n1;
153                 for (int i = 0; i < -fn2 - 1; i++) {
154                     ret = Arithmetic::mul(ret, n1);
155                 }
156                 bool isDiv0Error = false;
157                 const Object result = Arithmetic::div(Object::makeFixnum(1), ret, isDiv0Error);
158                 // isDiv0Error never occurs, so ignore.
159                 MOSH_ASSERT(!isDiv0Error);
160                 return result;
161             }
162         } else if (n2.isFlonum()) {
163             const double fn1 = static_cast<double>(n1.toFixnum());
164             const double fn2 = n2.toFlonum()->value();
165             return Object::makeFlonum(::pow(fn1, fn2));
166         } else if (n2.isBignum()) {
167             MOSH_FATAL("not reached");
168         } else if (n2.isRatnum()) {
169             const double fn1 = static_cast<double>(n1.toFixnum());
170             const double fn2 = n2.toRatnum()->toDouble();
171             return Object::makeFlonum(::pow(fn1, fn2));
172         } else if (n2.isCompnum()) {
173             const int fn1 = n1.toFixnum();
174             Compnum* const compnum = n2.toCompnum();
175             if (0 == fn1) {
176                 if (Arithmetic::isNegative(compnum->real())) {
177                     return Object::Undef;
178                 } else {
179                     return Object::makeFixnum(0);
180                 }
181             } else {
182                 return Compnum::expt(n1, n2);
183             }
184         } else {
185             MOSH_ASSERT(false);
186             return Object::Undef;
187         }
188     } else if (n1.isFlonum()) {
189         if (n2.isFixnum()) {
190             const double fn1 = n1.toFlonum()->value();
191             const double fn2 = static_cast<double>(n2.toFixnum());
192             return Object::makeFlonum(::pow(fn1, fn2));
193         } else if (n2.isFlonum()) {
194             const double fn1 = n1.toFlonum()->value();
195             const double fn2 = n2.toFlonum()->value();
196             return Object::makeFlonum(::pow(fn1, fn2));
197         } else if (n2.isBignum()) {
198             MOSH_FATAL("not reached");
199         } else if (n2.isRatnum()) {
200             const double fn1 = n1.toFlonum()->value();
201             const double fn2 = n2.toRatnum()->toDouble();
202             return Object::makeFlonum(::pow(fn1, fn2));
203         } else if (n2.isCompnum()) {
204             const double fn1 = n1.toFlonum()->value();
205             Compnum* const compnum = n2.toCompnum();
206             if (0.0 == fn1) {
207                 if (Arithmetic::isNegative(compnum->real())) {
208                     return Object::Undef;
209                 } else {
210                     return Object::makeFlonum(0.0);
211                 }
212             } else {
213                 return Compnum::expt(n1, n2);
214             }
215         } else {
216             MOSH_ASSERT(false);
217             return Object::Undef;
218         }
219     } else if (n1.isBignum()) {
220         if (n2.isFixnum()) {
221             const int fn2 = n2.toFixnum();
222             if (fn2 > 0) {
223                 // can be much faster using gmp
224                 Object ret = n1;
225                 for (int i = 0; i < fn2 - 1; i++) {
226                     ret = Arithmetic::mul(ret, n1);
227                 }
228                 return ret;
229             } else if (fn2 == 0) {
230                 return Object::makeFixnum(1);
231             } else {
232                 // inverse
233                 Object ret = n1;
234                 for (int i = 0; i < -fn2 - 1; i++) {
235                     ret = Arithmetic::mul(ret, n1);
236                 }
237                 bool isDiv0Error = false;
238                 const Object result = Arithmetic::div(Object::makeFixnum(1), ret, isDiv0Error);
239                 MOSH_ASSERT(!isDiv0Error);
240                 return result;
241             }
242         } else if (n2.isFlonum()) {
243             const double fn1 = n1.toBignum()->toDouble();
244             const double fn2 = n2.toFlonum()->value();
245             return Object::makeFlonum(::pow(fn1, fn2));
246         } else if (n2.isBignum()) {
247             MOSH_FATAL("not reached");
248         } else if (n2.isRatnum()) {
249             const double fn1 = n1.toBignum()->toDouble();
250             const double fn2 = n2.toRatnum()->toDouble();
251             return Object::makeFlonum(::pow(fn1, fn2));
252         } else if (n2.isCompnum()) {
253             return Compnum::expt(n1, n2);
254         } else {
255             MOSH_ASSERT(false);
256             return Object::Undef;
257         }
258     } else if (n1.isRatnum()) {
259         if (n2.isFixnum()) {
260             const int fn2 = n2.toFixnum();
261             if (fn2 > 0) {
262                 // can be much faster using gmp
263                 Object ret = n1;
264                 for (int i = 0; i < fn2 - 1; i++) {
265                     ret = Arithmetic::mul(ret, n1);
266                 }
267                 return ret;
268             } else if (fn2 == 0) {
269                 return Object::makeFixnum(1);
270             } else {
271                 // inverse
272                 Object ret = n1;
273                 for (int i = 0; i < -fn2 - 1; i++) {
274                     ret = Arithmetic::mul(ret, n1);
275                 }
276                 bool isDiv0Error = false;
277                 const Object result = Arithmetic::div(Object::makeFixnum(1), ret, isDiv0Error);
278                 MOSH_ASSERT(!isDiv0Error);
279                 return result;
280             }
281         } else if (n2.isFlonum()) {
282             const double fn1 = n1.toRatnum()->toDouble();
283             const double fn2 = n2.toFlonum()->value();
284             return Object::makeFlonum(::pow(fn1, fn2));
285         } else if (n2.isBignum()) {
286             MOSH_FATAL("not reached");
287         } else if (n2.isRatnum()) {
288             const double fn1 = n1.toRatnum()->toDouble();
289             const double fn2 = n2.toRatnum()->toDouble();
290             return Object::makeFlonum(::pow(fn1, fn2));
291         } else if (n2.isCompnum()) {
292             return Compnum::expt(n1, n2);
293         } else {
294             MOSH_ASSERT(false);
295             return Object::Undef;
296         }
297     } else if (n1.isCompnum()) {
298         return Compnum::expt(n1, n2);
299     } else {
300         MOSH_ASSERT(false);
301         return Object::Undef;
302     }
303     MOSH_FATAL("not reached");
304     return Object::Undef;
305 }
306 
sqrt(Object n)307 Object Arithmetic::sqrt(Object n)
308 {
309     MOSH_ASSERT(n.isNumber());
310     if (n.isFixnum()) {
311         return Fixnum::sqrt(n);
312     } else if (n.isBignum()) {
313         return n.toBignum()->sqrt();
314     } else if (n.isFlonum()) {
315         return n.toFlonum()->sqrt();
316     } else if (n.isRatnum()) {
317         return n.toRatnum()->sqrt();
318     } else if (n.isCompnum()) {
319         return n.toCompnum()->sqrt();
320     }
321     MOSH_ASSERT(false);
322     return Object::Undef;
323 }
324 
asin(Object n)325 Object Arithmetic::asin(Object n)
326 {
327     MOSH_ASSERT(n.isNumber());
328     if (n.isFixnum()) {
329         return Fixnum::asin(n.toFixnum());
330     } else if (n.isCompnum()) {
331         return Compnum::asin(n);
332     } else {
333         const double value = realToDouble(n);
334         return Object::makeFlonum(::asin(value));
335     }
336 }
337 
acos(Object n)338 Object Arithmetic::acos(Object n)
339 {
340     MOSH_ASSERT(n.isNumber());
341     if (n.isFixnum()) {
342         return Fixnum::acos(n.toFixnum());
343     } else if (n.isCompnum()) {
344         return Compnum::acos(n);
345     } else {
346         const double value = realToDouble(n);
347         return Object::makeFlonum(::acos(value));
348     }
349 }
350 
atan(Object n,bool & isDiv0Error)351 Object Arithmetic::atan(Object n, bool& isDiv0Error)
352 {
353     MOSH_ASSERT(n.isNumber());
354     if (n.isFixnum()) {
355         return Fixnum::atan(n.toFixnum());
356     } else if (n.isCompnum()) {
357         return Compnum::atan(n, isDiv0Error);
358     } else {
359         const double value = realToDouble(n);
360         return Object::makeFlonum(::atan(value));
361     }
362 }
363 
atan2(Object n1,Object n2)364 Object Arithmetic::atan2(Object n1, Object n2)
365 {
366     MOSH_ASSERT(n1.isReal());
367     MOSH_ASSERT(n2.isReal());
368     if (Arithmetic::isExactZero(n1)) {
369         return Object::makeFixnum(0);
370     }
371     return Object::makeFlonum(::atan2(realToDouble(n1), realToDouble(n2)));
372 }
373 
tan(Object n,bool & isDiv0Error)374 Object Arithmetic::tan(Object n, bool& isDiv0Error)
375 {
376     MOSH_ASSERT(n.isNumber());
377     if (n.isFixnum()) {
378         return Fixnum::tan(n.toFixnum());
379     } else if (n.isCompnum()) {
380         return n.toCompnum()->tan(isDiv0Error);
381     } else {
382         const double value = realToDouble(n);
383         return Object::makeFlonum(::tan(value));
384     }
385 }
386 
cos(Object n)387 Object Arithmetic::cos(Object n)
388 {
389     MOSH_ASSERT(n.isNumber());
390     if (n.isFixnum()) {
391         return Fixnum::cos(n.toFixnum());
392     } else if (n.isFlonum()) {
393         return Object::makeFlonum(::cos(n.toFlonum()->value()));
394     } else if (n.isCompnum()) {
395         return n.toCompnum()->cos();
396     } else if (n.isReal()) {
397         const double value = realToDouble(n);
398         return Object::makeFlonum(::cos(value));
399     } else {
400         return Object::Undef;
401     }
402 }
403 
sin(Object n)404 Object Arithmetic::sin(Object n)
405 {
406     MOSH_ASSERT(n.isNumber());
407     if (n.isFixnum()) {
408         return Fixnum::sin(n.toFixnum());
409     } else if (n.isFlonum()) {
410         return Object::makeFlonum(::sin(n.toFlonum()->value()));
411     } else if (n.isCompnum()) {
412         return n.toCompnum()->sin();
413     } else if (n.isReal()) {
414         const double value = realToDouble(n);
415         return Object::makeFlonum(::sin(value));
416     } else {
417         return Object::Undef;
418     }
419 }
420 
log(Object n)421 Object Arithmetic::log(Object n)
422 {
423     MOSH_ASSERT(n.isNumber());
424     if (n.isFixnum()) {
425         return Fixnum::log(n.toFixnum());
426     } else if (n.isCompnum()) {
427         return n.toCompnum()->log();
428     } else {
429         const double value = realToDouble(n);
430         if (isinf(value) && n.isBignum() && Arithmetic::gt(n, Object::makeFixnum(0))) {
431             Object ret = n.toBignum()->sqrt();
432             return Arithmetic::add(Arithmetic::log(ret), Arithmetic::log(ret));
433         } else if (value >= 0) {
434             return Object::makeFlonum(::log(realToDouble(n)));
435         } else {
436             return Object::makeCompnum(Object::makeFlonum(::log(-value)), Object::makeFlonum(::atan2(0.0, value)));
437         }
438     }
439 }
440 
log(Object n1,Object n2,bool & isDiv0Error)441 Object Arithmetic::log(Object n1, Object n2, bool& isDiv0Error)
442 {
443     return Arithmetic::div(log(n1), log(n2), isDiv0Error);
444 }
445 
446 
exp(Object n)447 Object Arithmetic::exp(Object n)
448 {
449     MOSH_ASSERT(n.isNumber());
450     if (n.isFixnum()) {
451         return Fixnum::exp(n.toFixnum());
452     } else if (n.isCompnum()) {
453         return n.toCompnum()->exp();
454     } else {
455         return Object::makeFlonum(::exp(realToDouble(n)));
456     }
457 }
458 
floor(Object n)459 Object Arithmetic::floor(Object n)
460 {
461     MOSH_ASSERT(n.isReal());
462     if (n.isFixnum() || n.isBignum()) {
463         return n;
464     } else if (n.isFlonum()) {
465         return n.toFlonum()->floor();
466     } else if (n.isRatnum()) {
467         return n.toRatnum()->floor();
468     }
469     MOSH_ASSERT(false);
470     return Object::Undef;
471 }
472 
ceiling(Object n)473 Object Arithmetic::ceiling(Object n)
474 {
475     MOSH_ASSERT(n.isReal());
476     if (n.isFixnum() || n.isBignum()) {
477         return n;
478     } else if (n.isFlonum()) {
479         return n.toFlonum()->ceiling();
480     } else if (n.isRatnum()) {
481         return n.toRatnum()->ceiling();
482     }
483     MOSH_ASSERT(false);
484     return Object::Undef;
485 }
486 
truncate(Object n)487 Object Arithmetic::truncate(Object n)
488 {
489     MOSH_ASSERT(n.isReal());
490     if (n.isFixnum() || n.isBignum()) {
491         return n;
492     } else if (n.isFlonum()) {
493         return n.toFlonum()->truncate();
494     } else if (n.isRatnum()) {
495         return n.toRatnum()->truncate();
496     }
497     MOSH_ASSERT(false);
498     return Object::Undef;
499 }
500 
round(Object n)501 Object Arithmetic::round(Object n)
502 {
503     MOSH_ASSERT(n.isReal());
504     if (n.isFixnum() || n.isBignum()) {
505         return n;
506     } else if (n.isFlonum()) {
507         return n.toFlonum()->round();
508     } else if (n.isRatnum()) {
509         return n.toRatnum()->round();
510     }
511     MOSH_ASSERT(false);
512     return Object::Undef;
513 }
514 
abs(Object n)515 Object Arithmetic::abs(Object n)
516 {
517     MOSH_ASSERT(n.isReal());
518     if (n.isFixnum()) {
519         return Fixnum::abs(n.toFixnum());
520     } else if (n.isBignum()) {
521         return n.toBignum()->abs();
522     } else if (n.isFlonum()) {
523         return n.toFlonum()->abs();
524     } else if (n.isRatnum()) {
525         return n.toRatnum()->abs();
526     }
527 
528     MOSH_ASSERT(false);
529     return Object::Undef;
530 }
531 
negate(Object n)532 Object Arithmetic::negate(Object n)
533 {
534     MOSH_ASSERT(n.isReal());
535     return Arithmetic::mul(n, Object::makeFixnum(-1));
536 }
537 
isNegative(Object n)538 bool Arithmetic::isNegative(Object n)
539 {
540     MOSH_ASSERT(n.isReal());
541     return Arithmetic::lt(n, Object::makeFixnum(0));
542 }
543 
angle(Object n)544 Object Arithmetic::angle(Object n)
545 {
546     MOSH_ASSERT(n.isNumber());
547     if (n.isReal()) {
548         if (isNegative(n)) {
549             // pi
550             return Object::makeFlonum(::acos(-1.0));
551         } else {
552             if (n.isFlonum()) {
553                 return Object::makeFlonum(0.0);
554             } else {
555                 return Object::makeFixnum(0);
556             }
557         }
558     } else if (n.isCompnum()) {
559         return n.toCompnum()->angle();
560     } else {
561         MOSH_ASSERT(false);
562         return Object::Undef;
563     }
564 }
565 
magnitude(Object n)566 Object Arithmetic::magnitude(Object n)
567 {
568     MOSH_ASSERT(n.isNumber());
569     if (n.isReal()) {
570         if (isNegative(n)) {
571             return negate(n);
572         } else {
573             return n;
574         }
575     } else if (n.isCompnum()) {
576         return n.toCompnum()->magnitude();
577     } else {
578         MOSH_ASSERT(false);
579         return Object::Undef;
580     }
581 }
582 
fitsU32(Object n)583 bool Arithmetic::fitsU32(Object n)
584 {
585     MOSH_ASSERT(n.isExactInteger());
586     if (n.isFixnum()) {
587         return n.toFixnum() >= 0;
588     } else if (n.isBignum()) {
589         return n.toBignum()->fitsU32();
590     } else {
591         MOSH_ASSERT(false);
592         return false;
593     }
594 }
595 
toU32(Object n)596 uint32_t Arithmetic::toU32(Object n)
597 {
598     MOSH_ASSERT(fitsU32(n));
599     if (n.isFixnum()) {
600         return n.toFixnum();
601     } else if (n.isBignum()) {
602         return n.toBignum()->toU32();
603     } else {
604         MOSH_ASSERT(false);
605         return 0;
606     }
607 }
608 
fitsS32(Object n)609 bool Arithmetic::fitsS32(Object n)
610 {
611     MOSH_ASSERT(n.isExactInteger());
612     if (n.isFixnum()) {
613         return true;
614     } else if (n.isBignum()) {
615         return n.toBignum()->fitsS32();
616     } else {
617         MOSH_ASSERT(false);
618         return false;
619     }
620 }
621 
toS32(Object n)622 int32_t Arithmetic::toS32(Object n)
623 {
624     MOSH_ASSERT(fitsS32(n));
625     if (n.isFixnum()) {
626         return n.toFixnum();
627     } else if (n.isBignum()) {
628         return n.toBignum()->toS32();
629     } else {
630         MOSH_ASSERT(false);
631         return 0;
632     }
633 }
634 
fitsU64(Object n)635 bool Arithmetic::fitsU64(Object n)
636 {
637     MOSH_ASSERT(n.isExactInteger());
638     if (n.isFixnum()) {
639         return n.toFixnum() >= 0;
640     } else if (n.isBignum()) {
641         return n.toBignum()->fitsU64();
642     } else {
643         MOSH_ASSERT(false);
644         return false;
645     }
646 }
647 
toU64(Object n)648 uint64_t Arithmetic::toU64(Object n)
649 {
650     MOSH_ASSERT(fitsU64(n));
651     if (n.isFixnum()) {
652         return n.toFixnum();
653     } else if (n.isBignum()) {
654         return n.toBignum()->toU64();
655     } else {
656         MOSH_ASSERT(false);
657         return 0;
658     }
659 }
660 
fitsS64(Object n)661 bool Arithmetic::fitsS64(Object n)
662 {
663     MOSH_ASSERT(n.isExactInteger());
664     if (n.isFixnum()) {
665         return true;
666     } else if (n.isBignum()) {
667         return n.toBignum()->fitsS64();
668     } else {
669         MOSH_ASSERT(false);
670         return false;
671     }
672 }
673 
toS64(Object n)674 int64_t Arithmetic::toS64(Object n)
675 {
676     MOSH_ASSERT(fitsS64(n));
677     if (n.isFixnum()) {
678         return n.toFixnum();
679     } else if (n.isBignum()) {
680         return n.toBignum()->toS64();
681     } else {
682         MOSH_ASSERT(false);
683         return 0;
684     }
685 }
686 
realToDouble(Object n)687 double Arithmetic::realToDouble(Object n)
688 {
689     MOSH_ASSERT(isRealValued(n));
690     if (n.isFixnum()) {
691         return n.toFixnum();
692     } else if (n.isBignum()) {
693         return n.toBignum()->toDouble();
694     } else if (n.isFlonum()) {
695         return n.toFlonum()->value();
696     } else if (n.isRatnum()) {
697         return n.toRatnum()->toDouble();
698     } else {
699         MOSH_ASSERT(false);
700         return 0.0;
701     }
702 }
703 
makePolar(Object n1,Object n2)704 Object Arithmetic::makePolar(Object n1, Object n2)
705 {
706     MOSH_ASSERT(isRealValued(n1));
707     MOSH_ASSERT(isRealValued(n2));
708     const Object real = n1.isCompnum() ? n1.toCompnum()->real() : n1;
709     const Object imag = n2.isCompnum() ? n2.toCompnum()->real() : n2;
710     if (eq(imag, Object::makeFixnum(0))) {
711         return real;
712     }
713     const double r = realToDouble(real);
714     const double a = realToDouble(imag);
715     return Object::makeCompnum(Object::makeFlonum(r * ::cos(a)), Object::makeFlonum(r * ::sin(a)));
716 }
717 
bitwiseNot(Object e)718 Object Arithmetic::bitwiseNot(Object e)
719 {
720     MOSH_ASSERT(e.isExactInteger());
721     if (e.isFixnum()) {
722         return Bignum::makeInteger(Fixnum::fxnot(e.toFixnum()));
723     } else if (e.isBignum()) {
724         return e.toBignum()->bitwiseNot();
725     }
726     MOSH_ASSERT(false);
727     return Object::Undef;
728 }
729 
bitwiseAnd(Object e1,Object e2)730 Object Arithmetic::bitwiseAnd(Object e1, Object e2)
731 {
732     MOSH_ASSERT(e1.isExactInteger());
733     MOSH_ASSERT(e2.isExactInteger());
734     if (e1.isFixnum()) {
735         if (e2.isFixnum()) {
736             return Object::makeFixnum(Fixnum::fxand(e1.toFixnum(), e2.toFixnum()));
737         } else if (e2.isBignum()) {
738             return e2.toBignum()->bitwiseAnd(e1.toFixnum());
739         }
740     } else if (e1.isBignum()) {
741         if (e2.isFixnum()) {
742             return e1.toBignum()->bitwiseAnd(e2.toFixnum());
743         } else if (e2.isBignum()) {
744             return e1.toBignum()->bitwiseAnd(e2.toBignum());
745         }
746     }
747     MOSH_ASSERT(false);
748     return Object::Undef;
749 }
750 
bitwiseIor(Object e1,Object e2)751 Object Arithmetic::bitwiseIor(Object e1, Object e2)
752 {
753     MOSH_ASSERT(e1.isExactInteger());
754     MOSH_ASSERT(e2.isExactInteger());
755     if (e1.isFixnum()) {
756         if (e2.isFixnum()) {
757             return Object::makeFixnum(Fixnum::fxior(e1.toFixnum(), e2.toFixnum()));
758         } else if (e2.isBignum()) {
759             return e2.toBignum()->bitwiseIor(e1.toFixnum());
760         }
761     } else if (e1.isBignum()) {
762         if (e2.isFixnum()) {
763             return e1.toBignum()->bitwiseIor(e2.toFixnum());
764         } else if (e2.isBignum()) {
765             return e1.toBignum()->bitwiseIor(e2.toBignum());
766         }
767     }
768     MOSH_ASSERT(false);
769     return Object::Undef;
770 }
771 
bitwiseXor(Object e1,Object e2)772 Object Arithmetic::bitwiseXor(Object e1, Object e2)
773 {
774     MOSH_ASSERT(e1.isExactInteger());
775     MOSH_ASSERT(e2.isExactInteger());
776     if (e1.isFixnum()) {
777         if (e2.isFixnum()) {
778             return Object::makeFixnum(Fixnum::fxxor(e1.toFixnum(), e2.toFixnum()));
779         } else if (e2.isBignum()) {
780             return e2.toBignum()->bitwiseXor(e1.toFixnum());
781         }
782     } else if (e1.isBignum()) {
783         if (e2.isFixnum()) {
784             return e1.toBignum()->bitwiseXor(e2.toFixnum());
785         } else if (e2.isBignum()) {
786             return e1.toBignum()->bitwiseXor(e2.toBignum());
787         }
788     }
789     MOSH_ASSERT(false);
790     return Object::Undef;
791 }
792 
bitwiseBitCount(Object e)793 Object Arithmetic::bitwiseBitCount(Object e)
794 {
795     MOSH_ASSERT(e.isExactInteger());
796     if (e.isFixnum()) {
797         const int n = e.toFixnum();
798         return Object::makeFixnum(Fixnum::fxbitCount(n));
799     } else if (e.isBignum()){
800         return e.toBignum()->bitwiseBitCount();
801     }
802     MOSH_ASSERT(false);
803     return Object::Undef;
804 }
805 
bitwiseLength(Object e)806 Object Arithmetic::bitwiseLength(Object e)
807 {
808     MOSH_ASSERT(e.isExactInteger());
809     if (e.isFixnum()) {
810         const int n = e.toFixnum();
811         return Object::makeFixnum(Fixnum::fxlength(n));
812     } else if (e.isBignum()){
813         return e.toBignum()->bitwiseLength();
814     }
815     MOSH_ASSERT(false);
816     return Object::Undef;
817 }
818 
bitwiseFirstBitSet(Object e)819 Object Arithmetic::bitwiseFirstBitSet(Object e)
820 {
821     MOSH_ASSERT(e.isExactInteger());
822     if (e.isFixnum()) {
823         const int n = e.toFixnum();
824         return Object::makeFixnum(Fixnum::fxfirstBitSet(n));
825     } else if (e.isBignum()){
826         return e.toBignum()->bitwiseFirstBitSet();
827     }
828     MOSH_ASSERT(false);
829     return Object::False;
830 }
831 
bitwiseShiftLeft(Object e1,unsigned long e2)832 Object Arithmetic::bitwiseShiftLeft(Object e1, unsigned long e2)
833 {
834     MOSH_ASSERT(e1.isExactInteger());
835     if (e1.isFixnum()) {
836         return Bignum::bitwiseShiftLeft(e1.toFixnum(), e2);
837     } else if (e1.isBignum()) {
838         return Bignum::bitwiseShiftLeft(e1.toBignum(), e2);
839     }
840     MOSH_ASSERT(false);
841     return Object::False;
842 }
843 
bitwiseShiftRight(Object e1,unsigned long e2)844 Object Arithmetic::bitwiseShiftRight(Object e1, unsigned long e2)
845 {
846     MOSH_ASSERT(e1.isExactInteger());
847     if (e1.isFixnum()) {
848         return Bignum::bitwiseShiftRight(e1.toFixnum(), e2);
849     } else if (e1.isBignum()) {
850         return Bignum::bitwiseShiftRight(e1.toBignum(), e2);
851     }
852     MOSH_ASSERT(false);
853     return Object::False;
854 }
855 
toFlonum(Object real)856 Object Arithmetic::toFlonum(Object real)
857 {
858     MOSH_ASSERT(real.isReal());
859     if (real.isFlonum()) {
860         return real;
861     } else if (real.isFixnum()) {
862         return Object::makeFlonum(real.toFixnum());
863     } else if (real.isBignum()) {
864         return Object::makeFlonum(real.toBignum()->toDouble());
865     } else if (real.isRatnum()) {
866         return Object::makeFlonum(real.toRatnum()->toDouble());
867     } else {
868         MOSH_ASSERT(false);
869         return Object::Nil;
870     }
871 }
872 
numerator(Object n)873 Object Arithmetic::numerator(Object n)
874 {
875     MOSH_ASSERT(n.isRational());
876     if (n.isRatnum()) {
877         return n.toRatnum()->numerator();
878     } else if (n.isFlonum()) {
879         Object m = n.toFlonum()->toExact();
880         return inexact(numerator(m));
881     } else {
882         return n;
883     }
884 }
885 
886 #include "ProcedureMacro.h"
887 #include "TextualOutputPort.h"
888 
denominator(Object n)889 Object Arithmetic::denominator(Object n)
890 {
891 //    VM_LOG1("n=~a", n);
892     MOSH_ASSERT(n.isRational());
893     if (n.isRatnum()) {
894         return n.toRatnum()->denominator();
895     } else if (n.isFlonum()) {
896         Object m = n.toFlonum()->toExact();
897         return inexact(denominator(m));
898     } else {
899         return Object::makeFixnum(1);
900     }
901 }
902 
903 // http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_idx_448
isInteger(Object n)904 bool Arithmetic::isInteger(Object n)
905 {
906     MOSH_ASSERT(n.isNumber());
907     if (n.isFlonum()) {
908         if (n.toFlonum()->isNan() ||
909             n.toFlonum()->isInfinite()) {
910             return false;
911         }
912     } else if (n.isCompnum()) {
913         Compnum* const c = n.toCompnum();
914         return isExactZero(c->imag()) && isInteger(c->real());
915     }
916     return Arithmetic::eq(denominator(n), Object::makeFixnum(1));
917 }
918 
isRealValued(Object n)919 bool Arithmetic::isRealValued(Object n)
920 {
921     MOSH_ASSERT(n.isNumber());
922     if (n.isReal()) {
923         return true;
924     } else if (n.isCompnum()) {
925         if (isZero(n.toCompnum()->imag())) {
926             return true;
927         } else {
928             return false;
929         }
930     }
931     MOSH_ASSERT(false);
932     return false;
933 }
934 
isRationalValued(Object n)935 bool Arithmetic::isRationalValued(Object n)
936 {
937     MOSH_ASSERT(n.isNumber());
938     if (n.isRational()) {
939         return true;
940     } else if (n.isCompnum()) {
941         Compnum* const c = n.toCompnum();
942         return isZero(c->imag()) && isRationalValued(c->real());
943     } else {
944         // +nan.0 +inf.0
945         return false;
946     }
947 }
948 
isEven(Object n)949 bool Arithmetic::isEven(Object n)
950 {
951     MOSH_ASSERT(isIntegerValued(n));
952     if (n.isFixnum()) {
953         return Fixnum::isEven(n.toFixnum());
954     } else if (n.isBignum()) {
955         return n.toBignum()->isEven();
956     } else if (n.isFlonum()) {
957         return n.toFlonum()->isEven();
958     } else if (n.isCompnum()) {
959         return isEven(n.toCompnum()->real());
960     }
961     MOSH_ASSERT(false);
962     return false;
963 }
964 
isIntegerValued(Object n)965 bool Arithmetic::isIntegerValued(Object n)
966 {
967     MOSH_ASSERT(n.isNumber());
968     if (n.isInteger()) {
969         return true;
970     } else if (n.isCompnum()) {
971         Compnum* const c = n.toCompnum();
972         return isZero(c->imag()) && isIntegerValued(c->real());
973     } else {
974         return false;
975     }
976 }
977 
isExactZero(Object n)978 bool Arithmetic::isExactZero(Object n)
979 {
980     return isZero(n) && isExact(n);
981 }
982 
isZero(Object n)983 bool Arithmetic::isZero(Object n)
984 {
985     return Arithmetic::eq(Object::makeFixnum(0), n);
986 }
987 
exact(Object n)988 Object Arithmetic::exact(Object n)
989 {
990     MOSH_ASSERT(n.isNumber());
991     if (n.isFixnum() || n.isBignum() || n.isRatnum()) {
992         return n;
993     } else if (n.isFlonum()) {
994         return n.toFlonum()->toExact();
995     } else if (n.isCompnum()) {
996         Compnum* const c = n.toCompnum();
997         return Object::makeCompnum(exact(c->real()), exact(c->imag()));
998     } else {
999         MOSH_ASSERT(false);
1000     }
1001     return Object::Undef;
1002 }
1003 
inexact(Object n)1004 Object Arithmetic::inexact(Object n)
1005 {
1006     MOSH_ASSERT(n.isNumber());
1007     if (n.isFixnum()) {
1008         return Object::makeFlonum(n.toFixnum());
1009     } else if (n.isBignum()) {
1010         return Object::makeFlonum(n.toBignum()->toDouble());
1011     } else if (n.isFlonum()) {
1012         return n;
1013     } else if (n.isRatnum()) {
1014         return Object::makeFlonum(n.toRatnum()->toDouble());
1015     } else if (n.isCompnum()) {
1016         Compnum* const c = n.toCompnum();
1017         return Object::makeCompnum(inexact(c->real()), inexact(c->imag()));
1018     } else {
1019         MOSH_ASSERT(false);
1020     }
1021     return Object::Undef;
1022 }
1023 
isExact(Object n)1024 bool Arithmetic::isExact(Object n)
1025 {
1026     MOSH_ASSERT(n.isNumber());
1027     if (n.isFixnum() || n.isBignum() || n.isRatnum()) {
1028         return true;
1029     } else if (n.isFlonum()) {
1030         return false;
1031     } else if (n.isCompnum()) {
1032         return
1033             isExact(n.toCompnum()->real()) &&
1034             isExact(n.toCompnum()->imag());
1035     } else {
1036         MOSH_ASSERT(false);
1037         return false;
1038     }
1039 }
1040 
1041 #define MAKE_REAL_COMPARE_FUNC(compare, symbol)          \
1042     bool Arithmetic::compare(Object n1, Object n2)    \
1043     { \
1044         if (n1.isFixnum()) {\
1045             if (n2.isFixnum()) {\
1046                 return Fixnum::compare(n1.toFixnum(), n2.toFixnum()); \
1047             } else if (n2.isFlonum()) {\
1048                 return Flonum::compare(n1.toFixnum(), n2.toFlonum());\
1049             } else if (n2.isRatnum()) {\
1050                 return Ratnum::compare(n1.toFixnum(), n2.toRatnum());\
1051             } else if (n2.isBignum()) {\
1052                 return Bignum::compare(n1.toFixnum(), n2.toBignum());\
1053             }\
1054         } else if (n1.isFlonum()) {\
1055             if (n2.isFixnum()) {\
1056                 return Flonum::compare(n1.toFlonum(), n2.toFixnum()); \
1057             } else if (n2.isFlonum()) {\
1058                 return Flonum::compare(n1.toFlonum(), n2.toFlonum());\
1059             } else if (n2.isRatnum()) {\
1060                 return Flonum::compare(n1.toFlonum(), n2.toRatnum()); \
1061             } else if (n2.isBignum()) {\
1062                 return Flonum::compare(n1.toFlonum(), n2.toBignum());\
1063             }\
1064         } else if (n1.isBignum()) {\
1065             if (n2.isFixnum()) {\
1066                 return Bignum::compare(n1.toBignum(), n2.toFixnum()); \
1067             } else if (n2.isFlonum()) {\
1068                 return Flonum::compare(n1.toBignum(), n2.toFlonum());\
1069             } else if (n2.isRatnum()) {\
1070                 return Ratnum::compare(n1.toBignum(), n2.toRatnum()); \
1071             } else if (n2.isBignum()) {\
1072                 return Bignum::compare(n1.toBignum(), n2.toBignum());\
1073             }\
1074         } else if (n1.isRatnum()) {\
1075             if (n2.isFixnum()) {\
1076                 return Ratnum::compare(n1.toRatnum(), n2.toFixnum());\
1077             } else if (n2.isFlonum()) {\
1078                 return Flonum::compare(n1.toRatnum(), n2.toFlonum());\
1079             } else if (n2.isRatnum()) {\
1080                 return Ratnum::compare(n1.toRatnum(), n2.toRatnum());\
1081             } else if (n2.isBignum()) {\
1082                 return Ratnum::compare(n1.toRatnum(), n2.toBignum());\
1083             }\
1084         }\
1085         return false;\
1086     }
1087 
1088 MAKE_REAL_COMPARE_FUNC(lt, <)
1089 MAKE_REAL_COMPARE_FUNC(le, <=)
1090 MAKE_REAL_COMPARE_FUNC(gt, >)
1091 MAKE_REAL_COMPARE_FUNC(ge, >=)
1092 
eq(Object n1,Object n2)1093 bool Arithmetic::eq(Object n1, Object n2)
1094 {
1095     MOSH_ASSERT(n1.isNumber());
1096     MOSH_ASSERT(n2.isNumber());
1097     if (n1.isFixnum()) {
1098         if (n2.isFixnum()) {
1099             return Fixnum::eq(n1.toFixnum(), n2.toFixnum());
1100         } else if (n2.isFlonum()) {
1101             return Flonum::eq(n1.toFixnum(), n2.toFlonum());
1102         } else if (n2.isRatnum()) {
1103             return Ratnum::eq(n1.toFixnum(), n2.toRatnum());
1104         } else if (n2.isBignum()) {
1105             return Bignum::eq(n1.toFixnum(), n2.toBignum());
1106         } else if (n2.isCompnum()) {
1107             return Compnum::eq(n1, n2.toCompnum());
1108         }
1109     } else if (n1.isFlonum()) {
1110         if (n2.isFixnum()) {
1111             return Flonum::eq(n1.toFlonum(), n2.toFixnum());
1112         } else if (n2.isFlonum()) {
1113             return Flonum::eq(n1.toFlonum(), n2.toFlonum());
1114         } else if (n2.isRatnum()) {
1115             return Flonum::eq(n1.toFlonum(), n2.toRatnum());
1116         } else if (n2.isBignum()) {
1117             return Flonum::eq(n1.toFlonum(), n2.toBignum());
1118         } else if (n2.isCompnum()) {
1119             return Compnum::eq(n1, n2.toCompnum());
1120         }
1121     } else if (n1.isBignum()) {
1122         if (n2.isFixnum()) {
1123             return Bignum::eq(n1.toBignum(), n2.toFixnum());
1124         } else if (n2.isFlonum()) {
1125             return Flonum::eq(n1.toBignum(), n2.toFlonum());
1126         } else if (n2.isRatnum()) {
1127             return Ratnum::eq(n1.toBignum(), n2.toRatnum());
1128         } else if (n2.isBignum()) {
1129             return Bignum::eq(n1.toBignum(), n2.toBignum());
1130         } else if (n2.isCompnum()) {
1131             return Compnum::eq(n1, n2.toCompnum());
1132         }
1133     } else if (n1.isRatnum()) {
1134         if (n2.isFixnum()) {
1135             return Ratnum::eq(n1.toRatnum(), n2.toFixnum());
1136         } else if (n2.isFlonum()) {
1137             return Flonum::eq(n1.toRatnum(), n2.toFlonum());
1138         } else if (n2.isRatnum()) {
1139             return Ratnum::eq(n1.toRatnum(), n2.toRatnum());
1140         } else if (n2.isBignum()) {
1141             return Ratnum::eq(n1.toRatnum(), n2.toBignum());
1142         } else if (n2.isCompnum()) {
1143             return Compnum::eq(n1, n2.toCompnum());
1144         }
1145     } else if (n1.isCompnum()) {
1146         if (n2.isFixnum()) {
1147             return Compnum::eq(n1.toCompnum(), n2);
1148         } else if (n2.isRatnum()) {
1149             return Compnum::eq(n1.toCompnum(), n2);
1150         } else if (n2.isFlonum()) {
1151             return Compnum::eq(n1.toCompnum(), n2);
1152         } else if (n2.isBignum()) {
1153             return Compnum::eq(n1.toCompnum(), n2);
1154         } else if (n2.isCompnum()) {
1155             return Compnum::eq(n1.toCompnum(), n2.toCompnum());
1156         }
1157     }
1158     return false;
1159 }
1160 
1161 #define MAKE_OP_FUNC(op, symbol)\
1162     Object Arithmetic::op(Object n1, Object n2)   \
1163     {\
1164         if (n1.isFixnum()) {\
1165             if (n2.isFixnum()) {\
1166                 return Bignum::op(n1.toFixnum(), n2.toFixnum());\
1167             } else if (n2.isRatnum()) {\
1168                 return Ratnum::op(n1.toFixnum(), n2.toRatnum());\
1169             } else if (n2.isFlonum()) {\
1170                 return Flonum::op(n1.toFixnum(), n2.toFlonum());\
1171             } else if (n2.isBignum()) {\
1172                 return Bignum::op(n1.toFixnum(), n2.toBignum());\
1173             } else if (n2.isCompnum()) {\
1174                 return Compnum::op(n1, n2.toCompnum());   \
1175             }\
1176         } else if (n1.isBignum()) {\
1177             if (n2.isFixnum()) {\
1178                 return Bignum::op(n1.toBignum(), n2.toFixnum()); \
1179             } else if (n2.isFlonum()) {\
1180                 return Flonum::op(n1.toBignum(), n2.toFlonum());\
1181             } else if (n2.isBignum()) {\
1182                 return Bignum::op(n1.toBignum(), n2.toBignum());\
1183             } else if (n2.isRatnum()) {\
1184                 return Ratnum::op(n1.toBignum(), n2.toRatnum()); \
1185             } else if (n2.isCompnum()) {\
1186                 return Compnum::op(n1, n2.toCompnum());   \
1187             }\
1188         } else if (n1.isRatnum()) {\
1189             if (n2.isFixnum()) {\
1190                 return Ratnum::op(n1.toRatnum(), new Ratnum(n2.toFixnum(), 1));\
1191             } else if (n2.isRatnum()) {\
1192                 return Ratnum::op(n1.toRatnum(), n2.toRatnum());\
1193             } else if (n2.isFlonum()) {\
1194                 return Flonum::op(n1.toRatnum(), n2.toFlonum());\
1195             } else if (n2.isBignum()) {\
1196                 return Ratnum::op(n1.toRatnum(), n2.toBignum());\
1197             } else if (n2.isCompnum()) { \
1198                 return Compnum::op(n1, n2.toCompnum());   \
1199             }\
1200         } else if (n1.isFlonum()) {\
1201             if (n2.isFixnum()) {\
1202                 return Flonum::op(n1.toFlonum(), n2.toFixnum()); \
1203             } else if (n2.isRatnum()) {\
1204                 return Flonum::op(n1.toFlonum(), n2.toRatnum()); \
1205             } else if (n2.isFlonum()) {\
1206                 return Flonum::op(n1.toFlonum(), n2.toFlonum());\
1207             } else if (n2.isBignum()) {\
1208                 return Flonum::op(n1.toFlonum(), n2.toBignum());\
1209             } else if (n2.isCompnum()) { \
1210                 return Compnum::op(n1, n2.toCompnum());   \
1211             }\
1212         } else if (n1.isCompnum()) {\
1213             if (n2.isFixnum() || n2.isBignum() || n2.isRatnum() || n2.isFlonum()) { \
1214                 return Compnum::op(n1.toCompnum(), n2);           \
1215             } else if (n2.isCompnum()) {\
1216                 return Compnum::op(n1.toCompnum(), n2.toCompnum()); \
1217             }\
1218         }\
1219         return Object::False;\
1220     }
1221 
1222 MAKE_OP_FUNC(add, +)
1223 MAKE_OP_FUNC(sub, -)
1224 //MAKE_OP_FUNC(mul, *)
1225 
mul(Object n1,Object n2)1226     Object Arithmetic::mul(Object n1, Object n2)
1227     {
1228         if (n1.isFixnum()) {
1229             if (n2.isFixnum()) {
1230                 return Bignum::mul(n1.toFixnum(), n2.toFixnum());
1231             } else if (n2.isRatnum()) {
1232                 return Ratnum::mul(n1.toFixnum(), n2.toRatnum());
1233             } else if (n2.isFlonum()) {
1234                 return Flonum::mul(n1.toFixnum(), n2.toFlonum());
1235             } else if (n2.isBignum()) {
1236                 return Bignum::mul(n1.toFixnum(), n2.toBignum());
1237             } else if (n2.isCompnum()) {
1238                 return Compnum::mul(n1, n2.toCompnum());
1239             }
1240         } else if (n1.isBignum()) {
1241             if (n2.isFixnum()) {
1242                 return Bignum::mul(n1.toBignum(), n2.toFixnum());
1243             } else if (n2.isFlonum()) {
1244                 return Flonum::mul(n1.toBignum(), n2.toFlonum());
1245             } else if (n2.isBignum()) {
1246                 return Bignum::mul(n1.toBignum(), n2.toBignum());
1247             } else if (n2.isRatnum()) {
1248                 return Ratnum::mul(n1.toBignum(), n2.toRatnum());
1249             } else if (n2.isCompnum()) {
1250                 return Compnum::mul(n1, n2.toCompnum());
1251             }
1252         } else if (n1.isRatnum()) {
1253             if (n2.isFixnum()) {
1254                 return Ratnum::mul(n1.toRatnum(), new Ratnum(n2.toFixnum(), 1));
1255             } else if (n2.isRatnum()) {
1256                 return Ratnum::mul(n1.toRatnum(), n2.toRatnum());
1257             } else if (n2.isFlonum()) {
1258                 return Flonum::mul(n1.toRatnum(), n2.toFlonum());
1259             } else if (n2.isBignum()) {
1260                 return Ratnum::mul(n1.toRatnum(), n2.toBignum());
1261             } else if (n2.isCompnum()) {
1262                 return Compnum::mul(n1, n2.toCompnum());
1263             }
1264         } else if (n1.isFlonum()) {
1265             if (n2.isFixnum()) {
1266                 return Flonum::mul(n1.toFlonum(), n2.toFixnum());
1267             } else if (n2.isRatnum()) {
1268                 return Flonum::mul(n1.toFlonum(), n2.toRatnum());
1269             } else if (n2.isFlonum()) {
1270                 return Flonum::mul(n1.toFlonum(), n2.toFlonum());
1271             } else if (n2.isBignum()) {
1272                 return Flonum::mul(n1.toFlonum(), n2.toBignum());
1273             } else if (n2.isCompnum()) {
1274                 return Compnum::mul(n1, n2.toCompnum());
1275             }
1276         } else if (n1.isCompnum()) {
1277             if (n2.isFixnum() || n2.isBignum() || n2.isRatnum() || n2.isFlonum()) {
1278                 return Compnum::mul(n1.toCompnum(), n2);
1279             } else if (n2.isCompnum()) {
1280                 return Compnum::mul(n1.toCompnum(), n2.toCompnum());
1281             }
1282         }
1283         return Object::False;
1284     }
1285 
1286 
mul(int number1,Object number2)1287 Object Arithmetic::mul(int number1, Object number2)
1288 {
1289     return mul(Object::makeFixnum(number1), number2);
1290 }
1291 
1292 #include "ProcedureMacro.h"
1293 #include "TextualOutputPort.h"
div(Object n1,Object n2,bool & isDiv0Error)1294 Object Arithmetic::div(Object n1, Object n2, bool& isDiv0Error)
1295 {
1296     if (n1.isFixnum()) {
1297         if (n2.isFixnum()) {
1298             if (isExactZero(n2)) {
1299                 isDiv0Error = true;
1300                 return Object::makeFixnum(0);
1301             } else {
1302                 if (n2.toFixnum() == 1) {
1303                     return Object::makeFixnum(n1.toFixnum());
1304                 } else {
1305                     return Object::makeRatnum(n1.toFixnum(), n2.toFixnum());
1306                 }
1307             }
1308         } else if (n2.isRatnum()) {
1309             if (isExactZero(n2)) {
1310                 isDiv0Error = true;
1311                 return Object::makeFixnum(0);
1312             } else {
1313                 return Ratnum::div(n1.toFixnum(), n2.toRatnum());
1314             }
1315         } else if (n2.isFlonum()) {
1316             return Flonum::div(n1.toFixnum(), n2.toFlonum());
1317         } else if (n2.isBignum()) {
1318             if (isExactZero(n2)) {
1319                 isDiv0Error = true;
1320                 return Object::makeFixnum(0);
1321             } else {
1322                 return Ratnum::div(n1.toFixnum(), n2.toBignum());
1323             }
1324         } else if (n2.isCompnum()) {
1325             if (isExactZero(n2)) {
1326                 isDiv0Error = true;
1327                 return Object::makeFixnum(0);
1328             } else {
1329                 return Compnum::div(n1, n2.toCompnum(), isDiv0Error);
1330             }
1331         }
1332     } else if (n1.isRatnum()) {
1333         if (n2.isFixnum()) {
1334             if (isExactZero(n2)) {
1335                 isDiv0Error = true;
1336                 return Object::makeFixnum(0);
1337             } else {
1338                 return Ratnum::div(n1.toRatnum(), n2.toFixnum());
1339             }
1340         } else if (n2.isRatnum()) {
1341             return Ratnum::div(n1.toRatnum(), n2.toRatnum());
1342         } else if (n2.isFlonum()) {
1343             return Flonum::div(n1.toRatnum(), n2.toFlonum());
1344         } else if (n2.isBignum()) {
1345             if (isExactZero(n2)) {
1346                 isDiv0Error = true;
1347                 return Object::makeFixnum(0);
1348             } else {
1349                 return Ratnum::div(n1.toRatnum(), n2.toBignum());
1350             }
1351         } else if (n2.isCompnum()) {
1352             if (isExactZero(n2)) {
1353                 isDiv0Error = true;
1354                 return Object::makeFixnum(0);
1355             } else {
1356                 return Compnum::div(n1, n2.toCompnum(), isDiv0Error);
1357             }
1358         }
1359     } else if (n1.isFlonum()) {
1360         if (n2.isFixnum()) {
1361             return Flonum::div(n1.toFlonum(), n2.toFixnum());
1362         } else if (n2.isRatnum()) {
1363             return Flonum::div(n1.toFlonum(), n2.toRatnum());
1364         } else if (n2.isFlonum()) {
1365             // we don't check division by zero.
1366             return Flonum::div(n1.toFlonum(), n2.toFlonum());
1367         } else if (n2.isBignum()) {
1368             if (isExactZero(n2)) {
1369                 isDiv0Error = true;
1370                 return Object::makeFixnum(0);
1371             } else {
1372                 return Flonum::div(n1.toFlonum(), n2.toBignum());
1373             }
1374         } else if (n2.isCompnum()) {
1375             if (isExactZero(n2)) {
1376                 isDiv0Error = true;
1377                 return Object::makeFixnum(0);
1378             } else {
1379                 return Compnum::div(n1, n2.toCompnum(), isDiv0Error);
1380             }
1381         }
1382     } else if (n1.isBignum()) {
1383         if (n2.isFixnum()) {
1384             if (isExactZero(n2)) {
1385                 isDiv0Error = true;
1386                 return Object::makeFixnum(0);
1387             } else {
1388                 return Ratnum::div(n1.toBignum(), n2.toFixnum());
1389             }
1390         } else if (n2.isRatnum()) {
1391             return Ratnum::div(n1.toBignum(), n2.toRatnum());
1392         } else if (n2.isFlonum()) {
1393             return Flonum::div(n1.toBignum(), n2.toFlonum());
1394         } else if (n2.isBignum()) {
1395             if (isExactZero(n2)) {
1396                 isDiv0Error = true;
1397                 return Object::makeFixnum(0);
1398             } else {
1399                 return Ratnum::div(n1.toBignum(), n2.toBignum());
1400             }
1401         } else if (n2.isCompnum()) {
1402             if (isExactZero(n2)) {
1403                 isDiv0Error = true;
1404                 return Object::makeFixnum(0);
1405             } else {
1406                 return Compnum::div(n1, n2.toCompnum(), isDiv0Error);
1407             }
1408         }
1409     } else if (n1.isCompnum()) {
1410         if (n2.isFixnum() || n2.isBignum() || n2.isRatnum() || n2.isFlonum()) {
1411             if (isExactZero(n2)) {
1412                 isDiv0Error = true;
1413                 return Object::makeFixnum(0);
1414             } else {
1415                 return Compnum::div(n1.toCompnum(), n2, isDiv0Error);
1416             }
1417         } else if (n2.isCompnum()) {
1418             return Compnum::div(n1.toCompnum(), n2.toCompnum(), isDiv0Error);
1419         }
1420     }
1421     return Object::False;
1422 }
1423 
1424 //  Reference:
1425 //  William D. Clinger.
1426 //  How to read floating point numbers accurately
1427 //  Proceedings of the ACM SIGPLAN 1990 conference on Programming language design and implementation, p.92-101, June 1990
algorithmR(Object f,const int e,const double z0)1428 double FlonumUtil::algorithmR(Object f, const int e, const double z0)
1429 {
1430     double z = z0;
1431     Object x0;
1432     Object pow10e;
1433     if (e >= 0) {
1434         x0 = Arithmetic::mul(f, Arithmetic::expt(Object::makeFixnum(10), Object::makeFixnum(e)));
1435         pow10e = Object::Undef;
1436     } else {
1437         x0 = Object::Undef;
1438         pow10e = Arithmetic::expt(Object::makeFixnum(10), Object::makeFixnum(-e));
1439     }
1440     while (1) {
1441         if (isinf(z)) return z;
1442         int k;
1443         int sign;
1444         int64_t m = decode_double(z, &k, &sign);
1445         MOSH_ASSERT(sign >= 0);
1446         Object x;
1447         Object y;
1448         if (e >= 0) {
1449             if (k >= 0) {
1450                 x = x0;
1451                 y = Bignum::makeIntegerFromS64(m);
1452                 y = Arithmetic::bitwiseShiftLeft(y, k);
1453             } else {
1454                 x = Arithmetic::bitwiseShiftLeft(x0, -k);
1455                 y = Bignum::makeIntegerFromS64(m);
1456             }
1457         } else {
1458             if (k >= 0) {
1459                 x = f;
1460                 y = Bignum::makeIntegerFromS64(m);
1461                 y = Arithmetic::bitwiseShiftLeft(y, k);
1462                 y = Arithmetic::mul(y, pow10e);
1463             } else {
1464                 x = Arithmetic::bitwiseShiftLeft(f, -k);
1465 
1466                 y = Arithmetic::mul(Bignum::makeIntegerFromS64(m), pow10e);
1467             }
1468         }
1469         Object D = Arithmetic::sub(x, y);
1470         Object D2 = Arithmetic::mul(Bignum::makeIntegerFromS64(m + m), D);
1471         bool negD = Arithmetic::isNegative(D);
1472         if (negD) {
1473             if (D2.isBignum()) {
1474                 D2 = Arithmetic::mul(D2, Object::makeFixnum(-1));
1475             } else {
1476                 D2 = Object::makeFixnum(-1 * D2.toFixnum());
1477             }
1478         }
1479         if (Arithmetic::lt(D2, y)) {
1480             if (negD && m == iexpt_2n52 &&
1481                 Arithmetic::gt(Arithmetic::bitwiseShiftLeft(D2, 1), y) && k > -1074) {
1482                 z = prevfloat(z);
1483                 continue;
1484             }
1485             return z;
1486         }
1487         if (Arithmetic::eq(D2, y)) {
1488             if ((m & 1) == 0) {
1489                 if (negD && m == iexpt_2n52) {
1490                     z = prevfloat(z);
1491                     continue;
1492                 }
1493                 return z;
1494             }
1495             return negD ? prevfloat(z) : nextfloat(z);
1496         }
1497         z = negD ? prevfloat(z) : nextfloat(z);
1498     }
1499 }
1500 
nextfloat(double z)1501 double FlonumUtil::nextfloat(double z)
1502 {
1503     int k;
1504     int sign;
1505     int64_t m = decode_double(z, &k, &sign);
1506     MOSH_ASSERT(sign >= 0);
1507     if (m == iexpt_2n53 - 1) return ldexp((double)iexpt_2n52, k + 1);
1508     return ldexp((double)(m + 1), k);
1509 }
1510 
prevfloat(double z)1511 double FlonumUtil::prevfloat(double z)
1512 {
1513     int k;
1514     int sign;
1515     int64_t m = decode_double(z, &k, &sign);
1516     MOSH_ASSERT(sign >= 0);
1517     if (m == iexpt_2n52) return ldexp((double)(iexpt_2n53 - 1), k - 1);
1518     return ldexp((double)(m - 1), k);
1519 }
1520 
decode_double(double n,int * exp,int * sign)1521 int64_t FlonumUtil::decode_double(double n, int* exp, int* sign)
1522 {
1523     union { double f64; uint64_t u64; } datum;
1524     datum.f64 = n;
1525     uint64_t bits = datum.u64;
1526     uint64_t mant_bits = bits & (iexpt_2n52 - 1);
1527     uint32_t sign_bits = bits >> 63;
1528     uint32_t exp_bits = (bits >> 52) & 0x7ff;
1529     if (n == 0.0) {
1530         *exp = 0;
1531         *sign = sign_bits ? -1 : 1;
1532         return 0;
1533     }
1534     if (isnan(n)) {
1535         *exp = 972;
1536         *sign = 1;
1537         return 0x18000000000000LL; // (uint64_t)0x180000 << 32;
1538     }
1539     if (isinf(n)) {
1540         *exp = 972;
1541         *sign = sign_bits ? -1 : 1;
1542         return 0x10000000000000LL; // (uint64_t)0x100000 << 32;
1543     }
1544     MOSH_ASSERT(exp_bits != 0x7ff);
1545     *exp = (exp_bits ? (int)exp_bits - 1023 : -1022) - 52;
1546     *sign = sign_bits ? -1 : 1;
1547     if (exp_bits) mant_bits |= iexpt_2n52;
1548     return mant_bits;
1549 }
1550 
1551 //  Robert G. Burger and R. Kent Dybvig.
1552 //  Printing floatingpoint numbers quickly and accurately.
1553 //  In Proceedings of the ACM SIGPLAN '96 Conference on Programming Language Design and Implementation, pages 108--116.
1554 //
1555 //  Originally from Ypsilon Scheme
1556 #define array_sizeof(a) ((int)(sizeof(a)/sizeof(a[0])))
flonumToUcs4String(double v,bool no_exponential)1557 ucs4string FlonumUtil::flonumToUcs4String(double v, bool no_exponential)
1558 {
1559     char digits[32];
1560     int digit_count = 0;
1561     int exponent;
1562     int e;
1563     int sign;
1564     int64_t f = decode_double(v, &e, &sign);
1565     if (v == 0.0) return (sign > 0) ? UC("0.0") : UC("-0.0");
1566     if (isnan(v)) return (sign > 0) ? UC("+nan.0") : UC("-nan.0");
1567     if (isinf(v)) return (sign > 0) ? UC("+inf.0") : UC("-inf.0");
1568     if (sign == -1) v = -v;
1569     bool meven = ((f & 1) == 0);
1570     bool eq_mp_mm = true;
1571     Object r;
1572     Object s;
1573     Object mp;
1574     Object mm;
1575     if (e >= 0) {
1576         Object be = Arithmetic::expt(Object::makeFixnum(2), Object::makeFixnum(e));
1577         if (f != iexpt_2n52) {
1578             r = Bignum::makeIntegerFromS64(f);
1579             r = Arithmetic::bitwiseShiftLeft(r, e + 1);
1580             s = Object::makeFixnum(2);
1581             mp = be;
1582             mm = be;
1583         } else {
1584             Object be1 = Arithmetic::expt(Object::makeFixnum(2), Object::makeFixnum(e + 1));
1585             r = Bignum::makeIntegerFromS64(f);
1586             r = Arithmetic::bitwiseShiftLeft(r, e + 2);
1587             s = Object::makeFixnum(4);
1588             mp = be1;
1589             mm = be;
1590             eq_mp_mm = false;
1591         }
1592     } else {
1593         if (e == -1023 || f != iexpt_2n52) {
1594             r = Bignum::makeIntegerFromS64(f << 1);
1595             s = Arithmetic::expt(Object::makeFixnum(2), Object::makeFixnum(1 - e));
1596             mp = Object::makeFixnum(1);
1597             mm = Object::makeFixnum(1);
1598         } else {
1599             r = Bignum::makeIntegerFromS64(f << 2);
1600             s = Arithmetic::expt(Object::makeFixnum(2), Object::makeFixnum(2 - e));
1601             mp = Object::makeFixnum(2);
1602             mm = Object::makeFixnum(1);
1603             eq_mp_mm = false;
1604         }
1605     }
1606     // scale
1607     int est = (int)(ceil(log10(v) - 0.1));
1608     if (est > 0) {
1609         s = Arithmetic::mul(s, Arithmetic::expt(Object::makeFixnum(10), Object::makeFixnum(est)));
1610     } else {
1611         Object scale10 = Arithmetic::expt(Object::makeFixnum(10), Object::makeFixnum(-est));
1612         r = Arithmetic::mul(r, scale10);
1613         mp = Arithmetic::mul(mp, scale10);
1614         mm = eq_mp_mm ? mp : Arithmetic::mul(mm, scale10);
1615     }
1616     // fixup
1617     if (Arithmetic::gt(Arithmetic::add(r, mp), s) || (meven && Arithmetic::eq(Arithmetic::add(r, mp), s))) {
1618         s = Arithmetic::mul(s, Object::makeFixnum(10));
1619         exponent = est + 1;
1620     } else {
1621         exponent = est;
1622     }
1623     // generate
1624 //     BN_TEMPORARY(bn_q);
1625 //     BN_TEMPORARY(bn_s);
1626 //     BN_ALLOC_FIXNUM(bn_q);
1627 //     BN_ALLOC_FIXNUM(bn_s);
1628 loop:
1629     mp = Arithmetic::mul(mp, Object::makeFixnum(10));
1630     mm = eq_mp_mm ? mp : Arithmetic::mul(mm, Object::makeFixnum(10));
1631     r = Arithmetic::mul(r, Object::makeFixnum(10));
1632     intptr_t dig = '0';
1633     if (Arithmetic::eq(r, s)) {
1634         dig += 1;
1635         r = Object::makeFixnum(0);
1636     } else if (Arithmetic::gt(r, s)) {
1637 
1638         bool isDiv0Error = false;
1639         Object nq = Arithmetic::floor(Arithmetic::div(r, s, isDiv0Error)); // floor is correct?
1640         Object nr = Arithmetic::sub(r, Arithmetic::mul(nq, s));
1641         MOSH_ASSERT(nq.isFixnum());
1642         dig += nq.toFixnum();
1643         r = nr;
1644 //         if (r.isFixnum()) {
1645 //             assert(s.isFixnum());
1646 //             intptr_t nq = r.toFixnum() / s.toFixnum();
1647 //             intptr_t nr = r.toFixnum() - (nq * s.toFixnum());
1648 //             dig += nq;
1649 //             r = Object::makeFixnum(nr);
1650 //         } else {
1651 //             bn_div_ans_t ans;
1652 //             ans.quotient = &bn_q;
1653 //             ans.remainder = r;
1654 //             if (BIGNUMP(s)) {
1655 //                 bn_div(heap, &ans, (scm_bignum_t)r, (scm_bignum_t)s);
1656 //             } else {
1657 //                 bn_let(&bn_s, (scm_fixnum_t)s);
1658 //                 bn_div(heap, &ans, (scm_bignum_t)r, &bn_s);
1659 //             }
1660 //             bn_set_sign((scm_bignum_t)ans.quotient, 1);
1661 //             bn_set_sign((scm_bignum_t)ans.remainder, 1);
1662 //             dig += FIXNUM(bn_demote((scm_bignum_t)ans.quotient));
1663 //             r = bn_demote((scm_bignum_t)ans.remainder);
1664 //         }
1665     }
1666     bool tc1 = (Arithmetic::lt(r, mm) || (meven && Arithmetic::eq(r, mm)));
1667     bool tc2 = ((Arithmetic::gt(Arithmetic::add(r, mp), s)) || (meven && Arithmetic::eq(Arithmetic::add(r, mp), s)));
1668     if (!tc1) {
1669         if (!tc2) {
1670             digits[digit_count++] = dig;
1671             if (digit_count >= array_sizeof(digits)) {
1672                 MOSH_FATAL("something wrong");
1673             }
1674             goto loop;
1675         } else {
1676             digits[digit_count++] = dig + 1;
1677         }
1678     } else {
1679         if (!tc2) {
1680             digits[digit_count++] = dig;
1681         } else {
1682             if (Arithmetic::lt(Arithmetic::add(r, r), s)) {
1683                 digits[digit_count++] = dig;
1684             } else {
1685                 digits[digit_count++] = dig + 1;
1686             }
1687         }
1688     }
1689     // todo: support misc format
1690     char out[512];
1691     int out_count = 0;
1692     digits[digit_count] = 0;
1693     if (no_exponential || (exponent >= -10 && exponent <= 10)) {
1694         if (sign == -1) out[out_count++] = '-';
1695         if (exponent <= 0) {
1696             out[out_count++] = '0';
1697             out[out_count++] = '.';
1698             while (++exponent <= 0) out[out_count++] = '0';
1699             for (int i = 0; digits[i] != 0; i++) out[out_count++] = digits[i];
1700         } else {
1701             for (int i = 0; digits[i] != 0; i++) {
1702                 out[out_count++] = digits[i];
1703                 if (--exponent == 0) out[out_count++] = '.';
1704             }
1705             if (exponent >= 0) {
1706                 if (exponent == 0) {
1707                     out[out_count++] = '0';
1708                 } else {
1709                     while (exponent > 0) {
1710                         out[out_count++] = '0';
1711                         exponent--;
1712                     }
1713                     out[out_count++] = '.';
1714                     out[out_count++] = '0';
1715                 }
1716             }
1717         }
1718         out[out_count] = 0;
1719     } else {
1720         if (sign == -1) out[out_count++] = '-';
1721         out[out_count++] = digits[0];
1722         if (digits[1]) out[out_count++] = '.';
1723         for (int i = 1; digits[i] != 0; i++) out[out_count++] = digits[i];
1724         out[out_count] = 0;
1725         snprintf(&out[out_count], sizeof(out) - out_count, "e%d", exponent-1);
1726     }
1727     return ucs4string::from_c_str(out);
1728 }
1729