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