Further cleanup and code reorganization.
[picobit.git] / bignums.c
blob921ac3695aa3fd0907a143052702484b6cb0281f
1 /* file: "bignums.c" */
3 /*
4 * Copyright 2004-2009 by Marc Feeley and Vincent St-Amour, All Rights Reserved.
5 */
7 #include "picobit-vm.h"
9 #ifdef INFINITE_PRECISION_BIGNUMS
11 // TODO clean up all this
13 integer make_integer (digit lo, integer hi) {
14 /* if(!hi && lo <= MAX_FIXNUM && lo >= MIN_FIXNUM) */
15 /* return ENCODE_FIXNUM(lo);o */
16 // TODO won't work, and the bignum functions are unaware of fixnums
17 // TODO if I uncomment, segfaults every time this is called. no ill effect is noticed without it, but kept just in case
18 return alloc_ram_cell_init (BIGNUM_FIELD0 | (hi >> 8), hi, lo >> 8, lo);
21 integer integer_hi (integer x) {
22 if (IN_RAM(x))
23 return ram_get_car (x);
24 else if (IN_ROM(x))
25 return rom_get_car (x);
26 else if (x < (MIN_FIXNUM_ENCODING - MIN_FIXNUM))
27 return NEG1; /* negative fixnum */
28 else
29 return ZERO; /* nonnegative fixnum */
32 digit integer_lo (integer x) {
33 if (IN_RAM(x))
34 return (((digit)ram_get_field2 (x)) << 8) + ram_get_field3 (x);
35 else if (IN_ROM(x))
36 return (((digit)rom_get_field2 (x)) << 8) + rom_get_field3 (x);
37 else
38 return DECODE_FIXNUM(x);
41 integer norm (obj prefix, integer n) {
42 /* norm(prefix,n) returns a normalized integer whose value is the
43 integer n prefixed with the digits in prefix (a list of digits) */
45 while (prefix != NIL) {
46 digit d = integer_lo (prefix);
47 obj temp = prefix;
49 prefix = integer_hi (temp);
51 if (obj_eq (n, ZERO)) {
52 if (d <= MAX_FIXNUM) {
53 n = ENCODE_FIXNUM ((uint8)d);
54 continue; // TODO with cast to unsigned, will it work for negative numbers ? or is it only handled in the next branch ?
57 else if (obj_eq (n, NEG1)) {
58 if (d >= (1<<digit_width) + MIN_FIXNUM) {
59 n = ENCODE_FIXNUM (d - (1<<digit_width)); // TODO had a cast, origianlly to int8, changed to uint8 which didn't work (obviously, we use -1 here), is a cast necessary at all ?
60 continue;
64 integer_hi_set (temp, n);
65 n = temp;
68 return n;
71 uint8 negp (integer x) {
72 /* negp(x) returns true iff x is negative */
74 do {
75 x = integer_hi (x);
76 if (obj_eq (x, ZERO)) return false;
77 } while (!obj_eq (x, NEG1));
79 return true;
82 int8 cmp (integer x, integer y) {
83 /* cmp(x,y) return -1 when x<y, 1 when x>y, and 0 when x=y */
85 int8 result = 0;
86 digit xlo;
87 digit ylo;
89 for (;;) {
90 if (obj_eq (x, ZERO) || obj_eq (x, NEG1)) {
91 if (!obj_eq (x, y))
92 { if (negp (y)) result = 1; else result = -1; }
93 break;
96 if (obj_eq (y, ZERO) || obj_eq (y, NEG1)) {
97 if (negp (x)) result = -1; else result = 1;
98 break;
101 xlo = integer_lo (x);
102 ylo = integer_lo (y);
103 x = integer_hi (x);
104 y = integer_hi (y);
105 if (xlo != ylo)
106 { if (xlo < ylo) result = -1; else result = 1; }
108 return result;
111 uint16 integer_length (integer x) {
112 /* integer_length(x) returns the number of bits in the binary
113 representation of the nonnegative integer x */
115 uint16 result = 0;
116 integer next;
117 digit d;
119 while (!obj_eq ((next = integer_hi (x)), ZERO)) {
120 result += digit_width;
121 x = next;
124 d = integer_lo (x);
126 while (d > 0) {
127 result++;
128 d >>= 1;
131 return result;
134 integer shr (integer x) { // TODO have shift_right
135 /* shr(x) returns the integer x shifted one bit to the right */
137 obj result = NIL;
138 digit d;
140 for (;;) {
141 if (obj_eq (x, ZERO) || obj_eq (x, NEG1)) {
142 result = norm (result, x);
143 break;
146 d = integer_lo (x);
147 x = integer_hi (x);
148 result = make_integer ((d >> 1) |
149 ((integer_lo (x) & 1) ? (1<<(digit_width-1)) : 0),
150 result);
153 return result;
156 integer negative_carry (integer carry) {
157 if (carry)
158 return NEG1;
159 else
160 return ZERO;
163 integer shl (integer x) {
164 /* shl(x) returns the integer x shifted one bit to the left */
166 integer negc = ZERO; /* negative carry */
167 integer temp;
168 obj result = NIL;
169 digit d;
171 for (;;) {
172 if (obj_eq (x, negc)) {
173 result = norm (result, x);
174 break;
177 d = integer_lo (x);
178 x = integer_hi (x);
179 temp = negc;
180 negc = negative_carry (d & (1<<(digit_width-1))); // TODO right side is constant, and sixpic has no constant folding
181 result = make_integer ((d << 1) | obj_eq (temp, NEG1), result);
184 return result;
187 integer shift_left (integer x, uint16 n) { // TODO have the primitves been changed for this and right ?
188 /* shift_left(x,n) returns the integer x shifted n bits to the left */
190 if (obj_eq (x, ZERO))
191 return x;
193 while (n & (digit_width-1)) {
194 x = shl (x);
195 n--;
198 while (n > 0) {
199 x = make_integer (0, x);
200 n -= digit_width;
203 return x;
206 integer add (integer x, integer y) {
207 /* add(x,y) returns the sum of the integers x and y */
209 integer negc = ZERO; /* negative carry */
210 obj result = NIL; /* nil terminated for the norm function */
211 digit dx;
212 digit dy;
214 for (;;) {
215 if (obj_eq (x, negc)) {
216 result = norm (result, y);
217 break;
220 if (obj_eq (y, negc)) {
221 result = norm (result, x);
222 break;
225 dx = integer_lo (x);
226 dy = integer_lo (y);
227 dx = dx + dy; /* may wrap around */
229 if (obj_eq (negc, ZERO))
230 negc = negative_carry (dx < dy);
231 else {
232 dx++; /* may wrap around */
233 negc = negative_carry (dx <= dy);
236 x = integer_hi (x);
237 y = integer_hi (y);
239 result = make_integer (dx, result);
242 return result;
245 integer invert (integer x) {
246 if (obj_eq (x, ZERO))
247 return NEG1;
248 else
249 return ZERO;
252 integer sub (integer x, integer y) {
253 /* sub(x,y) returns the difference of the integers x and y */
254 integer negc = NEG1; /* negative carry */
255 obj result = NIL;
256 digit dx;
257 digit dy;
259 for (;;) {
260 if (obj_eq (x, negc) && (obj_eq (y, ZERO) || obj_eq (y, NEG1))) {
261 result = norm (result, invert (y));
262 break;
265 if (obj_eq (y, invert (negc))) {
266 result = norm (result, x);
267 break;
270 dx = integer_lo (x);
271 dy = ~integer_lo (y);
272 dx = dx + dy; /* may wrap around */
274 if (obj_eq (negc, ZERO))
275 negc = negative_carry (dx < dy);
276 else {
277 dx++; /* may wrap around */
278 negc = negative_carry (dx <= dy);
281 x = integer_hi (x);
282 y = integer_hi (y);
284 result = make_integer (dx, result);
287 return result;
290 integer neg (integer x) {
291 /* neg(x) returns the integer -x */
293 return sub (ZERO, x);
296 integer scale (digit n, integer x) {
297 /* scale(n,x) returns the integer n*x */
299 obj result;
300 digit carry;
301 two_digit m;
303 if ((n == 0) || obj_eq (x, ZERO))
304 return ZERO;
306 if (n == 1)
307 return x;
309 result = NIL;
310 carry = 0;
312 for (;;) {
313 if (obj_eq (x, ZERO)){
314 if (carry <= MAX_FIXNUM)
315 result = norm (result, ENCODE_FIXNUM ((uint8)carry));
316 else
317 result = norm (result, make_integer (carry, ZERO));
318 break;
321 if (obj_eq (x, NEG1)) {
322 carry = carry - n;
323 if (carry >= ((1<<digit_width) + MIN_FIXNUM))
324 result = norm (result, ENCODE_FIXNUM ((uint8)carry));
325 else
326 result = norm (result, make_integer (carry, NEG1));
327 break;
330 m = (two_digit)integer_lo (x) * n + carry;
332 x = integer_hi (x);
333 carry = m >> digit_width;
334 result = make_integer ((digit)m, result);
337 return result;
340 integer mulnonneg (integer x, integer y) {
341 /* mulnonneg(x,y) returns the product of the integers x and y
342 where x is nonnegative */
344 obj result = NIL;
345 integer s = scale (integer_lo (x), y);
347 for (;;) {
348 result = make_integer (integer_lo (s), result);
349 s = integer_hi (s);
350 x = integer_hi (x);
352 if (obj_eq (x, ZERO))
353 break;
355 s = add (s, scale (integer_lo (x), y));
358 return norm (result, s);
361 // TODO have functions mul and div that handle negative arguments ? currently, the logic is in prim_mul and prim_div
362 integer divnonneg (integer x, integer y) {
363 /* divnonneg(x,y) returns the quotient and remainder of
364 the integers x and y where x and y are nonnegative */
366 integer result = ZERO;
367 uint16 lx = integer_length (x);
368 uint16 ly = integer_length (y);
370 if (lx >= ly) {
371 lx = lx - ly;
373 y = shift_left (y, lx);
375 do {
376 result = shl (result);
377 if (cmp (x, y) >= 0) {
378 x = sub (x, y);
379 result = add (POS1, result);
381 y = shr (y);
382 } while (lx-- != 0);
385 return result;
388 // used only in primitives that use small numbers only
389 // for example, vector primitives
390 int32 decode_int (obj o) {
391 int8 result;
392 if (o < MIN_FIXNUM_ENCODING)
393 TYPE_ERROR("decode_int.0", "integer");
395 if (o <= (MIN_FIXNUM_ENCODING + (MAX_FIXNUM - MIN_FIXNUM)))
396 return DECODE_FIXNUM(o);
398 if (IN_RAM(o)) {
399 if (!RAM_BIGNUM(o))
400 TYPE_ERROR("decode_int.1", "integer");
401 return ram_get_field3 (o);
403 else if (IN_ROM(o)) {
404 if (!ROM_BIGNUM(o))
405 TYPE_ERROR("decode_int.2", "integer");
406 return rom_get_field3 (o);
408 else
409 TYPE_ERROR("decode_int.3", "integer");
412 // same purpose as decode_int
413 obj encode_int (int32 n) {
414 if (n >= MIN_FIXNUM && n <= MAX_FIXNUM) {
415 return ENCODE_FIXNUM(n);
418 return alloc_ram_cell_init (BIGNUM_FIELD0, ENCODE_FIXNUM(0), n >> 8, n);
421 #endif