blob: 16198730aface9f9b858a3824e94683c36551fed [file] [log] [blame]
Damien George438c88d2014-02-22 19:25:23 +00001#include <stdint.h>
2#include <stdbool.h>
3#include <stdlib.h>
4#include <string.h>
5#include <assert.h>
6
7#include "misc.h"
8#include "mpconfig.h"
9#include "mpz.h"
10
11#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
12
Damien George06201ff2014-03-01 19:50:50 +000013#define DIG_SIZE (MPZ_DIG_SIZE)
Damien George438c88d2014-02-22 19:25:23 +000014#define DIG_MASK ((1 << DIG_SIZE) - 1)
15
16/*
Damien George06201ff2014-03-01 19:50:50 +000017 mpz is an arbitrary precision integer type with a public API.
18
19 mpn functions act on non-negative integers represented by an array of generalised
20 digits (eg a word per digit). You also need to specify separately the length of the
21 array. There is no public API for mpn. Rather, the functions are used by mpz to
22 implement its features.
23
24 Integer values are stored little endian (first digit is first in memory).
25
26 Definition of normalise: ?
Damien George438c88d2014-02-22 19:25:23 +000027*/
28
29/* compares i with j
30 returns sign(i - j)
31 assumes i, j are normalised
32*/
Damien George06201ff2014-03-01 19:50:50 +000033STATIC int mpn_cmp(const mpz_dig_t *idig, uint ilen, const mpz_dig_t *jdig, uint jlen) {
Damien George438c88d2014-02-22 19:25:23 +000034 if (ilen < jlen) { return -1; }
35 if (ilen > jlen) { return 1; }
36
37 for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
38 int cmp = *(--idig) - *(--jdig);
39 if (cmp < 0) { return -1; }
40 if (cmp > 0) { return 1; }
41 }
42
43 return 0;
44}
45
Damien Georgec5ac2ac2014-02-26 16:56:30 +000046/* computes i = j << n
47 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +000048 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien Georgec5ac2ac2014-02-26 16:56:30 +000049 can have i, j pointing to same memory
50*/
Damien George06201ff2014-03-01 19:50:50 +000051STATIC uint mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
52 uint n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000053 uint n_part = n % DIG_SIZE;
Damien Georgebb4a43f2014-03-12 15:36:06 +000054 if (n_part == 0) {
55 n_part = DIG_SIZE;
56 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +000057
Damien George06201ff2014-03-01 19:50:50 +000058 // start from the high end of the digit arrays
59 idig += jlen + n_whole - 1;
60 jdig += jlen - 1;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000061
Damien George06201ff2014-03-01 19:50:50 +000062 // shift the digits
63 mpz_dbl_dig_t d = 0;
64 for (uint i = jlen; i > 0; i--, idig--, jdig--) {
65 d |= *jdig;
66 *idig = d >> (DIG_SIZE - n_part);
67 d <<= DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000068 }
69
Damien George06201ff2014-03-01 19:50:50 +000070 // store remaining bits
71 *idig = d >> (DIG_SIZE - n_part);
72 idig -= n_whole - 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +000073 memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t));
Damien George06201ff2014-03-01 19:50:50 +000074
75 // work out length of result
76 jlen += n_whole;
77 if (idig[jlen - 1] == 0) {
78 jlen--;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000079 }
80
Damien George06201ff2014-03-01 19:50:50 +000081 // return length of result
Damien Georgec5ac2ac2014-02-26 16:56:30 +000082 return jlen;
83}
Damien Georgec5ac2ac2014-02-26 16:56:30 +000084
Damien George438c88d2014-02-22 19:25:23 +000085/* computes i = j >> n
86 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +000087 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien George438c88d2014-02-22 19:25:23 +000088 can have i, j pointing to same memory
89*/
Damien George06201ff2014-03-01 19:50:50 +000090STATIC uint mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
Damien George438c88d2014-02-22 19:25:23 +000091 uint n_whole = n / DIG_SIZE;
92 uint n_part = n % DIG_SIZE;
93
94 if (n_whole >= jlen) {
95 return 0;
96 }
97
98 jdig += n_whole;
99 jlen -= n_whole;
100
Damien George06201ff2014-03-01 19:50:50 +0000101 for (uint i = jlen; i > 0; i--, idig++, jdig++) {
Damien George438c88d2014-02-22 19:25:23 +0000102 mpz_dbl_dig_t d = *jdig;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000103 if (i > 1) {
Damien George438c88d2014-02-22 19:25:23 +0000104 d |= jdig[1] << DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000105 }
Damien George438c88d2014-02-22 19:25:23 +0000106 d >>= n_part;
107 *idig = d & DIG_MASK;
108 }
109
110 if (idig[-1] == 0) {
Damien George06201ff2014-03-01 19:50:50 +0000111 jlen--;
Damien George438c88d2014-02-22 19:25:23 +0000112 }
113
114 return jlen;
115}
116
117/* computes i = j + k
118 returns number of digits in i
119 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
120 can have i, j, k pointing to same memory
121*/
Damien George06201ff2014-03-01 19:50:50 +0000122STATIC uint mpn_add(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
Damien George438c88d2014-02-22 19:25:23 +0000123 mpz_dig_t *oidig = idig;
124 mpz_dbl_dig_t carry = 0;
125
126 jlen -= klen;
127
128 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
129 carry += *jdig + *kdig;
130 *idig = carry & DIG_MASK;
131 carry >>= DIG_SIZE;
132 }
133
134 for (; jlen > 0; --jlen, ++idig, ++jdig) {
135 carry += *jdig;
136 *idig = carry & DIG_MASK;
137 carry >>= DIG_SIZE;
138 }
139
140 if (carry != 0) {
141 *idig++ = carry;
142 }
143
144 return idig - oidig;
145}
146
147/* computes i = j - k
148 returns number of digits in i
149 assumes enough memory in i; assumes normalised j, k; assumes j >= k
150 can have i, j, k pointing to same memory
151*/
Damien George06201ff2014-03-01 19:50:50 +0000152STATIC uint mpn_sub(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
Damien George438c88d2014-02-22 19:25:23 +0000153 mpz_dig_t *oidig = idig;
154 mpz_dbl_dig_signed_t borrow = 0;
155
156 jlen -= klen;
157
158 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
159 borrow += *jdig - *kdig;
160 *idig = borrow & DIG_MASK;
161 borrow >>= DIG_SIZE;
162 }
163
Damien Georgeaca14122014-02-24 21:32:52 +0000164 for (; jlen > 0; --jlen, ++idig, ++jdig) {
Damien George438c88d2014-02-22 19:25:23 +0000165 borrow += *jdig;
166 *idig = borrow & DIG_MASK;
167 borrow >>= DIG_SIZE;
168 }
169
170 for (--idig; idig >= oidig && *idig == 0; --idig) {
171 }
172
173 return idig + 1 - oidig;
174}
175
176/* computes i = i * d1 + d2
177 returns number of digits in i
178 assumes enough memory in i; assumes normalised i; assumes dmul != 0
179*/
Damien George06201ff2014-03-01 19:50:50 +0000180STATIC uint mpn_mul_dig_add_dig(mpz_dig_t *idig, uint ilen, mpz_dig_t dmul, mpz_dig_t dadd) {
Damien George438c88d2014-02-22 19:25:23 +0000181 mpz_dig_t *oidig = idig;
182 mpz_dbl_dig_t carry = dadd;
183
184 for (; ilen > 0; --ilen, ++idig) {
185 carry += *idig * dmul; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
186 *idig = carry & DIG_MASK;
187 carry >>= DIG_SIZE;
188 }
189
190 if (carry != 0) {
191 *idig++ = carry;
192 }
193
194 return idig - oidig;
195}
196
197/* computes i = j * k
198 returns number of digits in i
199 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
200 can have j, k point to same memory
201*/
Damien George06201ff2014-03-01 19:50:50 +0000202STATIC uint mpn_mul(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, mpz_dig_t *kdig, uint klen) {
Damien George438c88d2014-02-22 19:25:23 +0000203 mpz_dig_t *oidig = idig;
204 uint ilen = 0;
205
206 for (; klen > 0; --klen, ++idig, ++kdig) {
207 mpz_dig_t *id = idig;
208 mpz_dbl_dig_t carry = 0;
209
210 uint jl = jlen;
211 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
212 carry += *id + *jd * *kdig; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
213 *id = carry & DIG_MASK;
214 carry >>= DIG_SIZE;
215 }
216
217 if (carry != 0) {
218 *id++ = carry;
219 }
220
221 ilen = id - oidig;
222 }
223
224 return ilen;
225}
226
227/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
228 assumes den != 0
229 assumes num_dig has enough memory to be extended by 1 digit
230 assumes quo_dig has enough memory (as many digits as num)
231 assumes quo_dig is filled with zeros
232 modifies den_dig memory, but restors it to original state at end
233*/
234
Damien George06201ff2014-03-01 19:50:50 +0000235STATIC void mpn_div(mpz_dig_t *num_dig, machine_uint_t *num_len, mpz_dig_t *den_dig, machine_uint_t den_len, mpz_dig_t *quo_dig, machine_uint_t *quo_len) {
Damien George438c88d2014-02-22 19:25:23 +0000236 mpz_dig_t *orig_num_dig = num_dig;
237 mpz_dig_t *orig_quo_dig = quo_dig;
238 mpz_dig_t norm_shift = 0;
239 mpz_dbl_dig_t lead_den_digit;
240
241 // handle simple cases
242 {
243 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
244 if (cmp == 0) {
245 *num_len = 0;
246 quo_dig[0] = 1;
247 *quo_len = 1;
248 return;
249 } else if (cmp < 0) {
250 // numerator remains the same
251 *quo_len = 0;
252 return;
253 }
254 }
255
256 // count number of leading zeros in leading digit of denominator
257 {
258 mpz_dig_t d = den_dig[den_len - 1];
259 while ((d & (1 << (DIG_SIZE - 1))) == 0) {
260 d <<= 1;
261 ++norm_shift;
262 }
263 }
264
265 // normalise denomenator (leading bit of leading digit is 1)
266 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
267 mpz_dig_t d = *den;
268 *den = ((d << norm_shift) | carry) & DIG_MASK;
269 carry = d >> (DIG_SIZE - norm_shift);
270 }
271
272 // now need to shift numerator by same amount as denominator
273 // first, increase length of numerator in case we need more room to shift
274 num_dig[*num_len] = 0;
275 ++(*num_len);
276 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
277 mpz_dig_t n = *num;
278 *num = ((n << norm_shift) | carry) & DIG_MASK;
279 carry = n >> (DIG_SIZE - norm_shift);
280 }
281
282 // cache the leading digit of the denominator
283 lead_den_digit = den_dig[den_len - 1];
284
285 // point num_dig to last digit in numerator
286 num_dig += *num_len - 1;
287
288 // calculate number of digits in quotient
289 *quo_len = *num_len - den_len;
290
291 // point to last digit to store for quotient
292 quo_dig += *quo_len - 1;
293
294 // keep going while we have enough digits to divide
295 while (*num_len > den_len) {
296 mpz_dbl_dig_t quo = (*num_dig << DIG_SIZE) | num_dig[-1];
297
298 // get approximate quotient
299 quo /= lead_den_digit;
300
301 // multiply quo by den and subtract from num get remainder
302 {
303 mpz_dbl_dig_signed_t borrow = 0;
304
305 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
306 borrow += *n - quo * *d; // will overflow if DIG_SIZE >= 16
307 *n = borrow & DIG_MASK;
308 borrow >>= DIG_SIZE;
309 }
310 borrow += *num_dig; // will overflow if DIG_SIZE >= 16
311 *num_dig = borrow & DIG_MASK;
312 borrow >>= DIG_SIZE;
313
314 // adjust quotient if it is too big
315 for (; borrow != 0; --quo) {
316 mpz_dbl_dig_t carry = 0;
317 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
318 carry += *n + *d;
319 *n = carry & DIG_MASK;
320 carry >>= DIG_SIZE;
321 }
322 carry += *num_dig;
323 *num_dig = carry & DIG_MASK;
324 carry >>= DIG_SIZE;
325
326 borrow += carry;
327 }
328 }
329
330 // store this digit of the quotient
331 *quo_dig = quo & DIG_MASK;
332 --quo_dig;
333
334 // move down to next digit of numerator
335 --num_dig;
336 --(*num_len);
337 }
338
339 // unnormalise denomenator
340 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
341 mpz_dig_t d = *den;
342 *den = ((d >> norm_shift) | carry) & DIG_MASK;
343 carry = d << (DIG_SIZE - norm_shift);
344 }
345
346 // unnormalise numerator (remainder now)
347 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
348 mpz_dig_t n = *num;
349 *num = ((n >> norm_shift) | carry) & DIG_MASK;
350 carry = n << (DIG_SIZE - norm_shift);
351 }
352
353 // strip trailing zeros
354
355 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
356 --(*quo_len);
357 }
358
359 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
360 --(*num_len);
361 }
362}
363
Damien George06201ff2014-03-01 19:50:50 +0000364#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000365
366static const uint log_base2_floor[] = {
367 0,
368 0, 1, 1, 2,
369 2, 2, 2, 3,
370 3, 3, 3, 3,
371 3, 3, 3, 4,
372 4, 4, 4, 4,
373 4, 4, 4, 4,
374 4, 4, 4, 4,
375 4, 4, 4, 5
376};
377
Damien George438c88d2014-02-22 19:25:23 +0000378void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000379 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000380 z->fixed_dig = 0;
381 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000382 z->len = 0;
383 z->dig = NULL;
384}
385
386void mpz_init_from_int(mpz_t *z, machine_int_t val) {
387 mpz_init_zero(z);
388 mpz_set_from_int(z, val);
389}
390
Damien George06201ff2014-03-01 19:50:50 +0000391void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, uint alloc, machine_int_t val) {
392 z->neg = 0;
393 z->fixed_dig = 1;
394 z->alloc = alloc;
395 z->len = 0;
396 z->dig = dig;
397 mpz_set_from_int(z, val);
398}
399
Damien George438c88d2014-02-22 19:25:23 +0000400void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000401 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000402 m_del(mpz_dig_t, z->dig, z->alloc);
403 }
404}
405
406mpz_t *mpz_zero(void) {
407 mpz_t *z = m_new_obj(mpz_t);
408 mpz_init_zero(z);
409 return z;
410}
411
412mpz_t *mpz_from_int(machine_int_t val) {
413 mpz_t *z = mpz_zero();
414 mpz_set_from_int(z, val);
415 return z;
416}
417
Damien Georgebb4a43f2014-03-12 15:36:06 +0000418mpz_t *mpz_from_ll(long long val) {
419 mpz_t *z = mpz_zero();
420 mpz_set_from_ll(z, val);
421 return z;
422}
423
Damien George438c88d2014-02-22 19:25:23 +0000424mpz_t *mpz_from_str(const char *str, uint len, bool neg, uint base) {
425 mpz_t *z = mpz_zero();
426 mpz_set_from_str(z, str, len, neg, base);
427 return z;
428}
429
430void mpz_free(mpz_t *z) {
431 if (z != NULL) {
432 m_del(mpz_dig_t, z->dig, z->alloc);
433 m_del_obj(mpz_t, z);
434 }
435}
436
437STATIC void mpz_need_dig(mpz_t *z, uint need) {
Damien George438c88d2014-02-22 19:25:23 +0000438 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000439 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000440 }
441
Damien George06201ff2014-03-01 19:50:50 +0000442 if (z->dig == NULL || z->alloc < need) {
443 if (z->fixed_dig) {
444 // cannot reallocate fixed buffers
445 assert(0);
446 return;
447 }
448 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
449 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000450 }
451}
452
453mpz_t *mpz_clone(const mpz_t *src) {
454 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000455 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000456 z->fixed_dig = 0;
457 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000458 z->len = src->len;
459 if (src->dig == NULL) {
460 z->dig = NULL;
461 } else {
462 z->dig = m_new(mpz_dig_t, z->alloc);
463 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
464 }
465 return z;
466}
467
Damien George06201ff2014-03-01 19:50:50 +0000468/* sets dest = src
469 can have dest, src the same
470*/
Damien George438c88d2014-02-22 19:25:23 +0000471void mpz_set(mpz_t *dest, const mpz_t *src) {
472 mpz_need_dig(dest, src->len);
473 dest->neg = src->neg;
474 dest->len = src->len;
475 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
476}
477
478void mpz_set_from_int(mpz_t *z, machine_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000479 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000480
Damien Georgebb4a43f2014-03-12 15:36:06 +0000481 machine_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000482 if (val < 0) {
483 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000484 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000485 } else {
486 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000487 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000488 }
489
490 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000491 while (uval > 0) {
492 z->dig[z->len++] = uval & DIG_MASK;
493 uval >>= DIG_SIZE;
494 }
495}
496
497void mpz_set_from_ll(mpz_t *z, long long val) {
498 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
499
500 unsigned long long uval;
501 if (val < 0) {
502 z->neg = 1;
503 uval = -val;
504 } else {
505 z->neg = 0;
506 uval = val;
507 }
508
509 z->len = 0;
510 while (uval > 0) {
511 z->dig[z->len++] = uval & DIG_MASK;
512 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000513 }
514}
515
516// returns number of bytes from str that were processed
517uint mpz_set_from_str(mpz_t *z, const char *str, uint len, bool neg, uint base) {
518 assert(base < 36);
519
520 const char *cur = str;
521 const char *top = str + len;
522
523 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
524
525 if (neg) {
526 z->neg = 1;
527 } else {
528 z->neg = 0;
529 }
530
531 z->len = 0;
532 for (; cur < top; ++cur) { // XXX UTF8 next char
533 //uint v = char_to_numeric(cur#); // XXX UTF8 get char
534 uint v = *cur;
535 if ('0' <= v && v <= '9') {
536 v -= '0';
537 } else if ('A' <= v && v <= 'Z') {
538 v -= 'A' - 10;
539 } else if ('a' <= v && v <= 'z') {
540 v -= 'a' - 10;
541 } else {
542 break;
543 }
544 if (v >= base) {
545 break;
546 }
547 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
548 }
549
550 return cur - str;
551}
552
553bool mpz_is_zero(const mpz_t *z) {
554 return z->len == 0;
555}
556
557bool mpz_is_pos(const mpz_t *z) {
558 return z->len > 0 && z->neg == 0;
559}
560
561bool mpz_is_neg(const mpz_t *z) {
562 return z->len > 0 && z->neg != 0;
563}
564
565bool mpz_is_odd(const mpz_t *z) {
566 return z->len > 0 && (z->dig[0] & 1) != 0;
567}
568
569bool mpz_is_even(const mpz_t *z) {
570 return z->len == 0 || (z->dig[0] & 1) == 0;
571}
572
573int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
574 int cmp = z2->neg - z1->neg;
575 if (cmp != 0) {
576 return cmp;
577 }
578 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
579 if (z1->neg != 0) {
580 cmp = -cmp;
581 }
582 return cmp;
583}
584
Damien George06201ff2014-03-01 19:50:50 +0000585#if 0
586// obsolete
587// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeaca14122014-02-24 21:32:52 +0000588int mpz_cmp_sml_int(const mpz_t *z, machine_int_t sml_int) {
Damien George438c88d2014-02-22 19:25:23 +0000589 int cmp;
590 if (z->neg == 0) {
591 if (sml_int < 0) return 1;
592 if (sml_int == 0) {
593 if (z->len == 0) return 0;
594 return 1;
595 }
596 if (z->len == 0) return -1;
597 assert(sml_int < (1 << DIG_SIZE));
598 if (z->len != 1) return 1;
599 cmp = z->dig[0] - sml_int;
600 } else {
601 if (sml_int > 0) return -1;
602 if (sml_int == 0) {
603 if (z->len == 0) return 0;
604 return -1;
605 }
606 if (z->len == 0) return 1;
607 assert(sml_int > -(1 << DIG_SIZE));
608 if (z->len != 1) return -1;
609 cmp = -z->dig[0] - sml_int;
610 }
611 if (cmp < 0) return -1;
612 if (cmp > 0) return 1;
613 return 0;
614}
Damien George06201ff2014-03-01 19:50:50 +0000615#endif
Damien George438c88d2014-02-22 19:25:23 +0000616
Damien George438c88d2014-02-22 19:25:23 +0000617#if 0
618these functions are unused
619
620/* returns abs(z)
621*/
622mpz_t *mpz_abs(const mpz_t *z) {
623 mpz_t *z2 = mpz_clone(z);
624 z2->neg = 0;
625 return z2;
626}
627
628/* returns -z
629*/
630mpz_t *mpz_neg(const mpz_t *z) {
631 mpz_t *z2 = mpz_clone(z);
632 z2->neg = 1 - z2->neg;
633 return z2;
634}
635
636/* returns lhs + rhs
637 can have lhs, rhs the same
638*/
639mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
640 mpz_t *z = mpz_zero();
641 mpz_add_inpl(z, lhs, rhs);
642 return z;
643}
644
645/* returns lhs - rhs
646 can have lhs, rhs the same
647*/
648mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
649 mpz_t *z = mpz_zero();
650 mpz_sub_inpl(z, lhs, rhs);
651 return z;
652}
653
654/* returns lhs * rhs
655 can have lhs, rhs the same
656*/
657mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
658 mpz_t *z = mpz_zero();
659 mpz_mul_inpl(z, lhs, rhs);
660 return z;
661}
662
663/* returns lhs ** rhs
664 can have lhs, rhs the same
665*/
666mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
667 mpz_t *z = mpz_zero();
668 mpz_pow_inpl(z, lhs, rhs);
669 return z;
670}
671#endif
672
673/* computes dest = abs(z)
674 can have dest, z the same
675*/
676void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
677 if (dest != z) {
678 mpz_set(dest, z);
679 }
680 dest->neg = 0;
681}
682
683/* computes dest = -z
684 can have dest, z the same
685*/
686void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
687 if (dest != z) {
688 mpz_set(dest, z);
689 }
690 dest->neg = 1 - dest->neg;
691}
692
Damien George06201ff2014-03-01 19:50:50 +0000693/* computes dest = ~z (= -z - 1)
694 can have dest, z the same
695*/
696void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
697 if (dest != z) {
698 mpz_set(dest, z);
699 }
700 if (dest->neg) {
701 dest->neg = 0;
702 mpz_dig_t k = 1;
703 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
704 } else {
705 mpz_dig_t k = 1;
706 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
707 dest->neg = 1;
708 }
709}
710
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000711/* computes dest = lhs << rhs
712 can have dest, lhs the same
713*/
714void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000715 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000716 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000717 } else if (rhs < 0) {
718 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000719 } else {
Damien George06201ff2014-03-01 19:50:50 +0000720 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
721 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
722 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000723 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000724}
725
726/* computes dest = lhs >> rhs
727 can have dest, lhs the same
728*/
729void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000730 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000731 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000732 } else if (rhs < 0) {
733 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000734 } else {
Damien George06201ff2014-03-01 19:50:50 +0000735 mpz_need_dig(dest, lhs->len);
736 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
737 dest->neg = lhs->neg;
738 if (dest->neg) {
739 // arithmetic shift right, rounding to negative infinity
740 uint n_whole = rhs / DIG_SIZE;
741 uint n_part = rhs % DIG_SIZE;
742 mpz_dig_t round_up = 0;
743 for (uint i = 0; i < lhs->len && i < n_whole; i++) {
744 if (lhs->dig[i] != 0) {
745 round_up = 1;
746 break;
747 }
748 }
749 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
750 round_up = 1;
751 }
752 if (round_up) {
753 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
754 }
755 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000756 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000757}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000758
Damien George438c88d2014-02-22 19:25:23 +0000759/* computes dest = lhs + rhs
760 can have dest, lhs, rhs the same
761*/
762void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
763 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
764 const mpz_t *temp = lhs;
765 lhs = rhs;
766 rhs = temp;
767 }
768
769 if (lhs->neg == rhs->neg) {
770 mpz_need_dig(dest, lhs->len + 1);
771 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
772 } else {
773 mpz_need_dig(dest, lhs->len);
774 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
775 }
776
777 dest->neg = lhs->neg;
778}
779
780/* computes dest = lhs - rhs
781 can have dest, lhs, rhs the same
782*/
783void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
784 bool neg = false;
785
786 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
787 const mpz_t *temp = lhs;
788 lhs = rhs;
789 rhs = temp;
790 neg = true;
791 }
792
793 if (lhs->neg != rhs->neg) {
794 mpz_need_dig(dest, lhs->len + 1);
795 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
796 } else {
797 mpz_need_dig(dest, lhs->len);
798 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
799 }
800
801 if (neg) {
802 dest->neg = 1 - lhs->neg;
803 } else {
804 dest->neg = lhs->neg;
805 }
806}
807
808/* computes dest = lhs * rhs
809 can have dest, lhs, rhs the same
810*/
811void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
812{
813 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000814 mpz_set_from_int(dest, 0);
815 return;
Damien George438c88d2014-02-22 19:25:23 +0000816 }
817
818 mpz_t *temp = NULL;
819 if (lhs == dest) {
820 lhs = temp = mpz_clone(lhs);
821 if (rhs == dest) {
822 rhs = lhs;
823 }
824 } else if (rhs == dest) {
825 rhs = temp = mpz_clone(rhs);
826 }
827
828 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
829 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
830 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
831
832 if (lhs->neg == rhs->neg) {
833 dest->neg = 0;
834 } else {
835 dest->neg = 1;
836 }
837
838 mpz_free(temp);
839}
840
841/* computes dest = lhs ** rhs
842 can have dest, lhs, rhs the same
843*/
844void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
845 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000846 mpz_set_from_int(dest, 0);
847 return;
Damien George438c88d2014-02-22 19:25:23 +0000848 }
849
850 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000851 mpz_set_from_int(dest, 1);
852 return;
Damien George438c88d2014-02-22 19:25:23 +0000853 }
854
855 mpz_t *x = mpz_clone(lhs);
856 mpz_t *n = mpz_clone(rhs);
857
858 mpz_set_from_int(dest, 1);
859
860 while (n->len > 0) {
861 if (mpz_is_odd(n)) {
862 mpz_mul_inpl(dest, dest, x);
863 }
864 mpz_mul_inpl(x, x, x);
865 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
866 }
867
868 mpz_free(x);
869 mpz_free(n);
870}
871
872/* computes gcd(z1, z2)
873 based on Knuth's modified gcd algorithm (I think?)
874 gcd(z1, z2) >= 0
875 gcd(0, 0) = 0
876 gcd(z, 0) = abs(z)
877*/
878mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
879 if (z1->len == 0) {
880 mpz_t *a = mpz_clone(z2);
881 a->neg = 0;
882 return a;
883 } else if (z2->len == 0) {
884 mpz_t *a = mpz_clone(z1);
885 a->neg = 0;
886 return a;
887 }
888
889 mpz_t *a = mpz_clone(z1);
890 mpz_t *b = mpz_clone(z2);
891 mpz_t c; mpz_init_zero(&c);
892 a->neg = 0;
893 b->neg = 0;
894
895 for (;;) {
896 if (mpz_cmp(a, b) < 0) {
897 if (a->len == 0) {
898 mpz_free(a);
899 mpz_deinit(&c);
900 return b;
901 }
902 mpz_t *t = a; a = b; b = t;
903 }
904 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
905 break;
906 }
907 mpz_set(&c, b);
908 do {
909 mpz_add_inpl(&c, &c, &c);
910 } while (mpz_cmp(&c, a) <= 0);
911 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
912 mpz_sub_inpl(a, a, &c);
913 }
914
915 mpz_deinit(&c);
916
917 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
918 mpz_free(a);
919 return b;
920 } else {
921 mpz_free(b);
922 return a;
923 }
924}
925
926/* computes lcm(z1, z2)
927 = abs(z1) / gcd(z1, z2) * abs(z2)
928 lcm(z1, z1) >= 0
929 lcm(0, 0) = 0
930 lcm(z, 0) = 0
931*/
932mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2)
933{
934 if (z1->len == 0 || z2->len == 0)
935 return mpz_zero();
936
937 mpz_t *gcd = mpz_gcd(z1, z2);
938 mpz_t *quo = mpz_zero();
939 mpz_t *rem = mpz_zero();
940 mpz_divmod_inpl(quo, rem, z1, gcd);
941 mpz_mul_inpl(rem, quo, z2);
942 mpz_free(gcd);
943 mpz_free(quo);
944 rem->neg = 0;
945 return rem;
946}
947
948/* computes new integers in quo and rem such that:
949 quo * rhs + rem = lhs
950 0 <= rem < rhs
951 can have lhs, rhs the same
952*/
953void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
954 *quo = mpz_zero();
955 *rem = mpz_zero();
956 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
957}
958
959/* computes new integers in quo and rem such that:
960 quo * rhs + rem = lhs
961 0 <= rem < rhs
962 can have lhs, rhs the same
963*/
964void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
965 if (rhs->len == 0) {
966 mpz_set_from_int(dest_quo, 0);
967 mpz_set_from_int(dest_rem, 0);
968 return;
969 }
970
971 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
972 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
973 dest_quo->len = 0;
974 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
975 mpz_set(dest_rem, lhs);
976 //rhs->dig[rhs->len] = 0;
977 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
978
979 if (lhs->neg != rhs->neg) {
980 dest_quo->neg = 1;
981 }
982}
983
984#if 0
985these functions are unused
986
987/* computes floor(lhs / rhs)
988 can have lhs, rhs the same
989*/
990mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
991 mpz_t *quo = mpz_zero();
992 mpz_t rem; mpz_init_zero(&rem);
993 mpz_divmod_inpl(quo, &rem, lhs, rhs);
994 mpz_deinit(&rem);
995 return quo;
996}
997
998/* computes lhs % rhs ( >= 0)
999 can have lhs, rhs the same
1000*/
1001mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1002 mpz_t quo; mpz_init_zero(&quo);
1003 mpz_t *rem = mpz_zero();
1004 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1005 mpz_deinit(&quo);
1006 return rem;
1007}
1008#endif
1009
Damien Georgeaca14122014-02-24 21:32:52 +00001010machine_int_t mpz_as_int(const mpz_t *i) {
1011 machine_int_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001012 mpz_dig_t *d = i->dig + i->len;
1013
Damien George06201ff2014-03-01 19:50:50 +00001014 while (--d >= i->dig) {
Damien Georgeaca14122014-02-24 21:32:52 +00001015 machine_int_t oldval = val;
Damien George438c88d2014-02-22 19:25:23 +00001016 val = (val << DIG_SIZE) | *d;
Damien George06201ff2014-03-01 19:50:50 +00001017 if (val < oldval) {
1018 // TODO need better handling of conversion overflow
Damien George438c88d2014-02-22 19:25:23 +00001019 if (i->neg == 0) {
1020 return 0x7fffffff;
1021 } else {
1022 return 0x80000000;
1023 }
1024 }
1025 }
1026
1027 if (i->neg != 0) {
1028 val = -val;
1029 }
1030
1031 return val;
1032}
1033
Damien George52608102014-03-08 15:04:54 +00001034#if MICROPY_ENABLE_FLOAT
1035mp_float_t mpz_as_float(const mpz_t *i) {
1036 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001037 mpz_dig_t *d = i->dig + i->len;
1038
1039 while (--d >= i->dig) {
1040 val = val * (1 << DIG_SIZE) + *d;
1041 }
1042
1043 if (i->neg != 0) {
1044 val = -val;
1045 }
1046
1047 return val;
1048}
Damien George52608102014-03-08 15:04:54 +00001049#endif
Damien George438c88d2014-02-22 19:25:23 +00001050
1051uint mpz_as_str_size(const mpz_t *i, uint base) {
1052 if (base < 2 || base > 32) {
1053 return 0;
1054 }
1055
1056 return i->len * DIG_SIZE / log_base2_floor[base] + 2 + 1; // +1 for null byte termination
1057}
1058
1059char *mpz_as_str(const mpz_t *i, uint base) {
1060 char *s = m_new(char, mpz_as_str_size(i, base));
1061 mpz_as_str_inpl(i, base, s);
1062 return s;
1063}
1064
1065// assumes enough space as calculated by mpz_as_str_size
1066// returns length of string, not including null byte
1067uint mpz_as_str_inpl(const mpz_t *i, uint base, char *str) {
1068 if (str == NULL || base < 2 || base > 32) {
1069 str[0] = 0;
1070 return 0;
1071 }
1072
1073 uint ilen = i->len;
1074
1075 if (ilen == 0) {
1076 str[0] = '0';
1077 str[1] = 0;
1078 return 1;
1079 }
1080
1081 // make a copy of mpz digits
1082 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1083 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1084
1085 // convert
1086 char *s = str;
1087 bool done;
1088 do {
1089 mpz_dig_t *d = dig + ilen;
1090 mpz_dbl_dig_t a = 0;
1091
1092 // compute next remainder
1093 while (--d >= dig) {
1094 a = (a << DIG_SIZE) | *d;
1095 *d = a / base;
1096 a %= base;
1097 }
1098
1099 // convert to character
1100 a += '0';
1101 if (a > '9') {
1102 a += 'a' - '9' - 1;
1103 }
1104 *s++ = a;
1105
1106 // check if number is zero
1107 done = true;
1108 for (d = dig; d < dig + ilen; ++d) {
1109 if (*d != 0) {
1110 done = false;
1111 break;
1112 }
1113 }
1114 } while (!done);
1115
1116 if (i->neg != 0) {
1117 *s++ = '-';
1118 }
1119
1120 // reverse string
1121 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1122 char temp = *u;
1123 *u = *v;
1124 *v = temp;
1125 }
1126
1127 s[0] = 0; // null termination
1128
1129 return s - str;
1130}
1131
1132#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ