blob: 4a8941e2988e7e79ae6a15467c0dd565884190df [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
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200176/* computes i = j & k
177 returns number of digits in i
178 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
179 can have i, j, k pointing to same memory
180*/
181STATIC uint mpn_and(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
182 mpz_dig_t *oidig = idig;
183
184 jlen -= klen;
185
186 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
187 *idig = *jdig & *kdig;
188 }
189
190 for (; jlen > 0; --jlen, ++idig) {
191 *idig = 0;
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 normalised j, k; assumes jlen >= klen
200 can have i, j, k pointing to same memory
201*/
202STATIC uint mpn_or(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
203 mpz_dig_t *oidig = idig;
204
205 jlen -= klen;
206
207 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
208 *idig = *jdig | *kdig;
209 }
210
211 for (; jlen > 0; --jlen, ++idig, ++jdig) {
212 *idig = *jdig;
213 }
214
215 return idig - oidig;
216}
217
218/* computes i = j ^ k
219 returns number of digits in i
220 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
221 can have i, j, k pointing to same memory
222*/
223STATIC uint mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
224 mpz_dig_t *oidig = idig;
225
226 jlen -= klen;
227
228 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
229 *idig = *jdig ^ *kdig;
230 }
231
232 for (; jlen > 0; --jlen, ++idig, ++jdig) {
233 *idig = *jdig;
234 }
235
236 return idig - oidig;
237}
238
Damien George438c88d2014-02-22 19:25:23 +0000239/* computes i = i * d1 + d2
240 returns number of digits in i
241 assumes enough memory in i; assumes normalised i; assumes dmul != 0
242*/
Damien George06201ff2014-03-01 19:50:50 +0000243STATIC 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 +0000244 mpz_dig_t *oidig = idig;
245 mpz_dbl_dig_t carry = dadd;
246
247 for (; ilen > 0; --ilen, ++idig) {
248 carry += *idig * dmul; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
249 *idig = carry & DIG_MASK;
250 carry >>= DIG_SIZE;
251 }
252
253 if (carry != 0) {
254 *idig++ = carry;
255 }
256
257 return idig - oidig;
258}
259
260/* computes i = j * k
261 returns number of digits in i
262 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
263 can have j, k point to same memory
264*/
Damien George06201ff2014-03-01 19:50:50 +0000265STATIC 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 +0000266 mpz_dig_t *oidig = idig;
267 uint ilen = 0;
268
269 for (; klen > 0; --klen, ++idig, ++kdig) {
270 mpz_dig_t *id = idig;
271 mpz_dbl_dig_t carry = 0;
272
273 uint jl = jlen;
274 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
275 carry += *id + *jd * *kdig; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
276 *id = carry & DIG_MASK;
277 carry >>= DIG_SIZE;
278 }
279
280 if (carry != 0) {
281 *id++ = carry;
282 }
283
284 ilen = id - oidig;
285 }
286
287 return ilen;
288}
289
290/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
291 assumes den != 0
292 assumes num_dig has enough memory to be extended by 1 digit
293 assumes quo_dig has enough memory (as many digits as num)
294 assumes quo_dig is filled with zeros
295 modifies den_dig memory, but restors it to original state at end
296*/
297
Damien George06201ff2014-03-01 19:50:50 +0000298STATIC 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 +0000299 mpz_dig_t *orig_num_dig = num_dig;
300 mpz_dig_t *orig_quo_dig = quo_dig;
301 mpz_dig_t norm_shift = 0;
302 mpz_dbl_dig_t lead_den_digit;
303
304 // handle simple cases
305 {
306 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
307 if (cmp == 0) {
308 *num_len = 0;
309 quo_dig[0] = 1;
310 *quo_len = 1;
311 return;
312 } else if (cmp < 0) {
313 // numerator remains the same
314 *quo_len = 0;
315 return;
316 }
317 }
318
319 // count number of leading zeros in leading digit of denominator
320 {
321 mpz_dig_t d = den_dig[den_len - 1];
322 while ((d & (1 << (DIG_SIZE - 1))) == 0) {
323 d <<= 1;
324 ++norm_shift;
325 }
326 }
327
328 // normalise denomenator (leading bit of leading digit is 1)
329 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
330 mpz_dig_t d = *den;
331 *den = ((d << norm_shift) | carry) & DIG_MASK;
332 carry = d >> (DIG_SIZE - norm_shift);
333 }
334
335 // now need to shift numerator by same amount as denominator
336 // first, increase length of numerator in case we need more room to shift
337 num_dig[*num_len] = 0;
338 ++(*num_len);
339 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
340 mpz_dig_t n = *num;
341 *num = ((n << norm_shift) | carry) & DIG_MASK;
342 carry = n >> (DIG_SIZE - norm_shift);
343 }
344
345 // cache the leading digit of the denominator
346 lead_den_digit = den_dig[den_len - 1];
347
348 // point num_dig to last digit in numerator
349 num_dig += *num_len - 1;
350
351 // calculate number of digits in quotient
352 *quo_len = *num_len - den_len;
353
354 // point to last digit to store for quotient
355 quo_dig += *quo_len - 1;
356
357 // keep going while we have enough digits to divide
358 while (*num_len > den_len) {
359 mpz_dbl_dig_t quo = (*num_dig << DIG_SIZE) | num_dig[-1];
360
361 // get approximate quotient
362 quo /= lead_den_digit;
363
364 // multiply quo by den and subtract from num get remainder
365 {
366 mpz_dbl_dig_signed_t borrow = 0;
367
368 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
369 borrow += *n - quo * *d; // will overflow if DIG_SIZE >= 16
370 *n = borrow & DIG_MASK;
371 borrow >>= DIG_SIZE;
372 }
373 borrow += *num_dig; // will overflow if DIG_SIZE >= 16
374 *num_dig = borrow & DIG_MASK;
375 borrow >>= DIG_SIZE;
376
377 // adjust quotient if it is too big
378 for (; borrow != 0; --quo) {
379 mpz_dbl_dig_t carry = 0;
380 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
381 carry += *n + *d;
382 *n = carry & DIG_MASK;
383 carry >>= DIG_SIZE;
384 }
385 carry += *num_dig;
386 *num_dig = carry & DIG_MASK;
387 carry >>= DIG_SIZE;
388
389 borrow += carry;
390 }
391 }
392
393 // store this digit of the quotient
394 *quo_dig = quo & DIG_MASK;
395 --quo_dig;
396
397 // move down to next digit of numerator
398 --num_dig;
399 --(*num_len);
400 }
401
402 // unnormalise denomenator
403 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
404 mpz_dig_t d = *den;
405 *den = ((d >> norm_shift) | carry) & DIG_MASK;
406 carry = d << (DIG_SIZE - norm_shift);
407 }
408
409 // unnormalise numerator (remainder now)
410 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
411 mpz_dig_t n = *num;
412 *num = ((n >> norm_shift) | carry) & DIG_MASK;
413 carry = n << (DIG_SIZE - norm_shift);
414 }
415
416 // strip trailing zeros
417
418 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
419 --(*quo_len);
420 }
421
422 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
423 --(*num_len);
424 }
425}
426
Damien George06201ff2014-03-01 19:50:50 +0000427#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000428
429static const uint log_base2_floor[] = {
430 0,
431 0, 1, 1, 2,
432 2, 2, 2, 3,
433 3, 3, 3, 3,
434 3, 3, 3, 4,
435 4, 4, 4, 4,
436 4, 4, 4, 4,
437 4, 4, 4, 4,
438 4, 4, 4, 5
439};
440
Damien George438c88d2014-02-22 19:25:23 +0000441void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000442 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000443 z->fixed_dig = 0;
444 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000445 z->len = 0;
446 z->dig = NULL;
447}
448
449void mpz_init_from_int(mpz_t *z, machine_int_t val) {
450 mpz_init_zero(z);
451 mpz_set_from_int(z, val);
452}
453
Damien George06201ff2014-03-01 19:50:50 +0000454void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, uint alloc, machine_int_t val) {
455 z->neg = 0;
456 z->fixed_dig = 1;
457 z->alloc = alloc;
458 z->len = 0;
459 z->dig = dig;
460 mpz_set_from_int(z, val);
461}
462
Damien George438c88d2014-02-22 19:25:23 +0000463void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000464 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000465 m_del(mpz_dig_t, z->dig, z->alloc);
466 }
467}
468
469mpz_t *mpz_zero(void) {
470 mpz_t *z = m_new_obj(mpz_t);
471 mpz_init_zero(z);
472 return z;
473}
474
475mpz_t *mpz_from_int(machine_int_t val) {
476 mpz_t *z = mpz_zero();
477 mpz_set_from_int(z, val);
478 return z;
479}
480
Damien Georgebb4a43f2014-03-12 15:36:06 +0000481mpz_t *mpz_from_ll(long long val) {
482 mpz_t *z = mpz_zero();
483 mpz_set_from_ll(z, val);
484 return z;
485}
486
Damien George438c88d2014-02-22 19:25:23 +0000487mpz_t *mpz_from_str(const char *str, uint len, bool neg, uint base) {
488 mpz_t *z = mpz_zero();
489 mpz_set_from_str(z, str, len, neg, base);
490 return z;
491}
492
493void mpz_free(mpz_t *z) {
494 if (z != NULL) {
495 m_del(mpz_dig_t, z->dig, z->alloc);
496 m_del_obj(mpz_t, z);
497 }
498}
499
500STATIC void mpz_need_dig(mpz_t *z, uint need) {
Damien George438c88d2014-02-22 19:25:23 +0000501 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000502 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000503 }
504
Damien George06201ff2014-03-01 19:50:50 +0000505 if (z->dig == NULL || z->alloc < need) {
506 if (z->fixed_dig) {
507 // cannot reallocate fixed buffers
508 assert(0);
509 return;
510 }
511 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
512 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000513 }
514}
515
516mpz_t *mpz_clone(const mpz_t *src) {
517 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000518 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000519 z->fixed_dig = 0;
520 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000521 z->len = src->len;
522 if (src->dig == NULL) {
523 z->dig = NULL;
524 } else {
525 z->dig = m_new(mpz_dig_t, z->alloc);
526 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
527 }
528 return z;
529}
530
Damien George06201ff2014-03-01 19:50:50 +0000531/* sets dest = src
532 can have dest, src the same
533*/
Damien George438c88d2014-02-22 19:25:23 +0000534void mpz_set(mpz_t *dest, const mpz_t *src) {
535 mpz_need_dig(dest, src->len);
536 dest->neg = src->neg;
537 dest->len = src->len;
538 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
539}
540
541void mpz_set_from_int(mpz_t *z, machine_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000542 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000543
Damien Georgebb4a43f2014-03-12 15:36:06 +0000544 machine_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000545 if (val < 0) {
546 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000547 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000548 } else {
549 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000550 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000551 }
552
553 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000554 while (uval > 0) {
555 z->dig[z->len++] = uval & DIG_MASK;
556 uval >>= DIG_SIZE;
557 }
558}
559
560void mpz_set_from_ll(mpz_t *z, long long val) {
561 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
562
563 unsigned long long uval;
564 if (val < 0) {
565 z->neg = 1;
566 uval = -val;
567 } else {
568 z->neg = 0;
569 uval = val;
570 }
571
572 z->len = 0;
573 while (uval > 0) {
574 z->dig[z->len++] = uval & DIG_MASK;
575 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000576 }
577}
578
579// returns number of bytes from str that were processed
580uint mpz_set_from_str(mpz_t *z, const char *str, uint len, bool neg, uint base) {
581 assert(base < 36);
582
583 const char *cur = str;
584 const char *top = str + len;
585
586 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
587
588 if (neg) {
589 z->neg = 1;
590 } else {
591 z->neg = 0;
592 }
593
594 z->len = 0;
595 for (; cur < top; ++cur) { // XXX UTF8 next char
596 //uint v = char_to_numeric(cur#); // XXX UTF8 get char
597 uint v = *cur;
598 if ('0' <= v && v <= '9') {
599 v -= '0';
600 } else if ('A' <= v && v <= 'Z') {
601 v -= 'A' - 10;
602 } else if ('a' <= v && v <= 'z') {
603 v -= 'a' - 10;
604 } else {
605 break;
606 }
607 if (v >= base) {
608 break;
609 }
610 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
611 }
612
613 return cur - str;
614}
615
616bool mpz_is_zero(const mpz_t *z) {
617 return z->len == 0;
618}
619
620bool mpz_is_pos(const mpz_t *z) {
621 return z->len > 0 && z->neg == 0;
622}
623
624bool mpz_is_neg(const mpz_t *z) {
625 return z->len > 0 && z->neg != 0;
626}
627
628bool mpz_is_odd(const mpz_t *z) {
629 return z->len > 0 && (z->dig[0] & 1) != 0;
630}
631
632bool mpz_is_even(const mpz_t *z) {
633 return z->len == 0 || (z->dig[0] & 1) == 0;
634}
635
636int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
637 int cmp = z2->neg - z1->neg;
638 if (cmp != 0) {
639 return cmp;
640 }
641 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
642 if (z1->neg != 0) {
643 cmp = -cmp;
644 }
645 return cmp;
646}
647
Damien George06201ff2014-03-01 19:50:50 +0000648#if 0
649// obsolete
650// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeaca14122014-02-24 21:32:52 +0000651int mpz_cmp_sml_int(const mpz_t *z, machine_int_t sml_int) {
Damien George438c88d2014-02-22 19:25:23 +0000652 int cmp;
653 if (z->neg == 0) {
654 if (sml_int < 0) return 1;
655 if (sml_int == 0) {
656 if (z->len == 0) return 0;
657 return 1;
658 }
659 if (z->len == 0) return -1;
660 assert(sml_int < (1 << DIG_SIZE));
661 if (z->len != 1) return 1;
662 cmp = z->dig[0] - sml_int;
663 } else {
664 if (sml_int > 0) return -1;
665 if (sml_int == 0) {
666 if (z->len == 0) return 0;
667 return -1;
668 }
669 if (z->len == 0) return 1;
670 assert(sml_int > -(1 << DIG_SIZE));
671 if (z->len != 1) return -1;
672 cmp = -z->dig[0] - sml_int;
673 }
674 if (cmp < 0) return -1;
675 if (cmp > 0) return 1;
676 return 0;
677}
Damien George06201ff2014-03-01 19:50:50 +0000678#endif
Damien George438c88d2014-02-22 19:25:23 +0000679
Damien George438c88d2014-02-22 19:25:23 +0000680#if 0
681these functions are unused
682
683/* returns abs(z)
684*/
685mpz_t *mpz_abs(const mpz_t *z) {
686 mpz_t *z2 = mpz_clone(z);
687 z2->neg = 0;
688 return z2;
689}
690
691/* returns -z
692*/
693mpz_t *mpz_neg(const mpz_t *z) {
694 mpz_t *z2 = mpz_clone(z);
695 z2->neg = 1 - z2->neg;
696 return z2;
697}
698
699/* returns lhs + rhs
700 can have lhs, rhs the same
701*/
702mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
703 mpz_t *z = mpz_zero();
704 mpz_add_inpl(z, lhs, rhs);
705 return z;
706}
707
708/* returns lhs - rhs
709 can have lhs, rhs the same
710*/
711mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
712 mpz_t *z = mpz_zero();
713 mpz_sub_inpl(z, lhs, rhs);
714 return z;
715}
716
717/* returns lhs * rhs
718 can have lhs, rhs the same
719*/
720mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
721 mpz_t *z = mpz_zero();
722 mpz_mul_inpl(z, lhs, rhs);
723 return z;
724}
725
726/* returns lhs ** rhs
727 can have lhs, rhs the same
728*/
729mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
730 mpz_t *z = mpz_zero();
731 mpz_pow_inpl(z, lhs, rhs);
732 return z;
733}
734#endif
735
736/* computes dest = abs(z)
737 can have dest, z the same
738*/
739void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
740 if (dest != z) {
741 mpz_set(dest, z);
742 }
743 dest->neg = 0;
744}
745
746/* computes dest = -z
747 can have dest, z the same
748*/
749void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
750 if (dest != z) {
751 mpz_set(dest, z);
752 }
753 dest->neg = 1 - dest->neg;
754}
755
Damien George06201ff2014-03-01 19:50:50 +0000756/* computes dest = ~z (= -z - 1)
757 can have dest, z the same
758*/
759void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
760 if (dest != z) {
761 mpz_set(dest, z);
762 }
763 if (dest->neg) {
764 dest->neg = 0;
765 mpz_dig_t k = 1;
766 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
767 } else {
768 mpz_dig_t k = 1;
769 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
770 dest->neg = 1;
771 }
772}
773
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000774/* computes dest = lhs << rhs
775 can have dest, lhs the same
776*/
777void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000778 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000779 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000780 } else if (rhs < 0) {
781 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000782 } else {
Damien George06201ff2014-03-01 19:50:50 +0000783 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
784 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
785 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000786 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000787}
788
789/* computes dest = lhs >> rhs
790 can have dest, lhs the same
791*/
792void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000793 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000794 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000795 } else if (rhs < 0) {
796 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000797 } else {
Damien George06201ff2014-03-01 19:50:50 +0000798 mpz_need_dig(dest, lhs->len);
799 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
800 dest->neg = lhs->neg;
801 if (dest->neg) {
802 // arithmetic shift right, rounding to negative infinity
803 uint n_whole = rhs / DIG_SIZE;
804 uint n_part = rhs % DIG_SIZE;
805 mpz_dig_t round_up = 0;
806 for (uint i = 0; i < lhs->len && i < n_whole; i++) {
807 if (lhs->dig[i] != 0) {
808 round_up = 1;
809 break;
810 }
811 }
812 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
813 round_up = 1;
814 }
815 if (round_up) {
816 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
817 }
818 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000819 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000820}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000821
Damien George438c88d2014-02-22 19:25:23 +0000822/* computes dest = lhs + rhs
823 can have dest, lhs, rhs the same
824*/
825void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
826 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
827 const mpz_t *temp = lhs;
828 lhs = rhs;
829 rhs = temp;
830 }
831
832 if (lhs->neg == rhs->neg) {
833 mpz_need_dig(dest, lhs->len + 1);
834 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
835 } else {
836 mpz_need_dig(dest, lhs->len);
837 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
838 }
839
840 dest->neg = lhs->neg;
841}
842
843/* computes dest = lhs - rhs
844 can have dest, lhs, rhs the same
845*/
846void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
847 bool neg = false;
848
849 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
850 const mpz_t *temp = lhs;
851 lhs = rhs;
852 rhs = temp;
853 neg = true;
854 }
855
856 if (lhs->neg != rhs->neg) {
857 mpz_need_dig(dest, lhs->len + 1);
858 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
859 } else {
860 mpz_need_dig(dest, lhs->len);
861 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
862 }
863
864 if (neg) {
865 dest->neg = 1 - lhs->neg;
866 } else {
867 dest->neg = lhs->neg;
868 }
869}
870
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200871/* computes dest = lhs & rhs
872 can have dest, lhs, rhs the same
873*/
874void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
875 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
876 const mpz_t *temp = lhs;
877 lhs = rhs;
878 rhs = temp;
879 }
880
881 if (lhs->neg == rhs->neg) {
882 mpz_need_dig(dest, lhs->len);
883 dest->len = mpn_and(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
884 } else {
885 mpz_need_dig(dest, lhs->len);
886 // TODO
887 assert(0);
888// dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
889 }
890
891 dest->neg = lhs->neg;
892}
893
894/* computes dest = lhs | rhs
895 can have dest, lhs, rhs the same
896*/
897void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
898 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
899 const mpz_t *temp = lhs;
900 lhs = rhs;
901 rhs = temp;
902 }
903
904 if (lhs->neg == rhs->neg) {
905 mpz_need_dig(dest, lhs->len);
906 dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
907 } else {
908 mpz_need_dig(dest, lhs->len);
909 // TODO
910 assert(0);
911// dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
912 }
913
914 dest->neg = lhs->neg;
915}
916
917/* computes dest = lhs ^ rhs
918 can have dest, lhs, rhs the same
919*/
920void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
921 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
922 const mpz_t *temp = lhs;
923 lhs = rhs;
924 rhs = temp;
925 }
926
927 if (lhs->neg == rhs->neg) {
928 mpz_need_dig(dest, lhs->len);
929 dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
930 } else {
931 mpz_need_dig(dest, lhs->len);
932 // TODO
933 assert(0);
934// dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
935 }
936
937 dest->neg = 0;
938}
939
Damien George438c88d2014-02-22 19:25:23 +0000940/* computes dest = lhs * rhs
941 can have dest, lhs, rhs the same
942*/
943void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
944{
945 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000946 mpz_set_from_int(dest, 0);
947 return;
Damien George438c88d2014-02-22 19:25:23 +0000948 }
949
950 mpz_t *temp = NULL;
951 if (lhs == dest) {
952 lhs = temp = mpz_clone(lhs);
953 if (rhs == dest) {
954 rhs = lhs;
955 }
956 } else if (rhs == dest) {
957 rhs = temp = mpz_clone(rhs);
958 }
959
960 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
961 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
962 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
963
964 if (lhs->neg == rhs->neg) {
965 dest->neg = 0;
966 } else {
967 dest->neg = 1;
968 }
969
970 mpz_free(temp);
971}
972
973/* computes dest = lhs ** rhs
974 can have dest, lhs, rhs the same
975*/
976void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
977 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000978 mpz_set_from_int(dest, 0);
979 return;
Damien George438c88d2014-02-22 19:25:23 +0000980 }
981
982 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000983 mpz_set_from_int(dest, 1);
984 return;
Damien George438c88d2014-02-22 19:25:23 +0000985 }
986
987 mpz_t *x = mpz_clone(lhs);
988 mpz_t *n = mpz_clone(rhs);
989
990 mpz_set_from_int(dest, 1);
991
992 while (n->len > 0) {
993 if (mpz_is_odd(n)) {
994 mpz_mul_inpl(dest, dest, x);
995 }
996 mpz_mul_inpl(x, x, x);
997 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
998 }
999
1000 mpz_free(x);
1001 mpz_free(n);
1002}
1003
1004/* computes gcd(z1, z2)
1005 based on Knuth's modified gcd algorithm (I think?)
1006 gcd(z1, z2) >= 0
1007 gcd(0, 0) = 0
1008 gcd(z, 0) = abs(z)
1009*/
1010mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
1011 if (z1->len == 0) {
1012 mpz_t *a = mpz_clone(z2);
1013 a->neg = 0;
1014 return a;
1015 } else if (z2->len == 0) {
1016 mpz_t *a = mpz_clone(z1);
1017 a->neg = 0;
1018 return a;
1019 }
1020
1021 mpz_t *a = mpz_clone(z1);
1022 mpz_t *b = mpz_clone(z2);
1023 mpz_t c; mpz_init_zero(&c);
1024 a->neg = 0;
1025 b->neg = 0;
1026
1027 for (;;) {
1028 if (mpz_cmp(a, b) < 0) {
1029 if (a->len == 0) {
1030 mpz_free(a);
1031 mpz_deinit(&c);
1032 return b;
1033 }
1034 mpz_t *t = a; a = b; b = t;
1035 }
1036 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
1037 break;
1038 }
1039 mpz_set(&c, b);
1040 do {
1041 mpz_add_inpl(&c, &c, &c);
1042 } while (mpz_cmp(&c, a) <= 0);
1043 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
1044 mpz_sub_inpl(a, a, &c);
1045 }
1046
1047 mpz_deinit(&c);
1048
1049 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
1050 mpz_free(a);
1051 return b;
1052 } else {
1053 mpz_free(b);
1054 return a;
1055 }
1056}
1057
1058/* computes lcm(z1, z2)
1059 = abs(z1) / gcd(z1, z2) * abs(z2)
1060 lcm(z1, z1) >= 0
1061 lcm(0, 0) = 0
1062 lcm(z, 0) = 0
1063*/
1064mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2)
1065{
1066 if (z1->len == 0 || z2->len == 0)
1067 return mpz_zero();
1068
1069 mpz_t *gcd = mpz_gcd(z1, z2);
1070 mpz_t *quo = mpz_zero();
1071 mpz_t *rem = mpz_zero();
1072 mpz_divmod_inpl(quo, rem, z1, gcd);
1073 mpz_mul_inpl(rem, quo, z2);
1074 mpz_free(gcd);
1075 mpz_free(quo);
1076 rem->neg = 0;
1077 return rem;
1078}
1079
1080/* computes new integers in quo and rem such that:
1081 quo * rhs + rem = lhs
1082 0 <= rem < rhs
1083 can have lhs, rhs the same
1084*/
1085void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
1086 *quo = mpz_zero();
1087 *rem = mpz_zero();
1088 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
1089}
1090
1091/* computes new integers in quo and rem such that:
1092 quo * rhs + rem = lhs
1093 0 <= rem < rhs
1094 can have lhs, rhs the same
1095*/
1096void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
1097 if (rhs->len == 0) {
1098 mpz_set_from_int(dest_quo, 0);
1099 mpz_set_from_int(dest_rem, 0);
1100 return;
1101 }
1102
1103 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
1104 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
1105 dest_quo->len = 0;
1106 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
1107 mpz_set(dest_rem, lhs);
1108 //rhs->dig[rhs->len] = 0;
1109 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
1110
1111 if (lhs->neg != rhs->neg) {
1112 dest_quo->neg = 1;
1113 }
1114}
1115
1116#if 0
1117these functions are unused
1118
1119/* computes floor(lhs / rhs)
1120 can have lhs, rhs the same
1121*/
1122mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
1123 mpz_t *quo = mpz_zero();
1124 mpz_t rem; mpz_init_zero(&rem);
1125 mpz_divmod_inpl(quo, &rem, lhs, rhs);
1126 mpz_deinit(&rem);
1127 return quo;
1128}
1129
1130/* computes lhs % rhs ( >= 0)
1131 can have lhs, rhs the same
1132*/
1133mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1134 mpz_t quo; mpz_init_zero(&quo);
1135 mpz_t *rem = mpz_zero();
1136 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1137 mpz_deinit(&quo);
1138 return rem;
1139}
1140#endif
1141
Damien George8270e382014-04-03 11:00:54 +00001142// TODO check that this correctly handles overflow in all cases
Damien Georgeaca14122014-02-24 21:32:52 +00001143machine_int_t mpz_as_int(const mpz_t *i) {
1144 machine_int_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001145 mpz_dig_t *d = i->dig + i->len;
1146
Damien George06201ff2014-03-01 19:50:50 +00001147 while (--d >= i->dig) {
Damien Georgeaca14122014-02-24 21:32:52 +00001148 machine_int_t oldval = val;
Damien George438c88d2014-02-22 19:25:23 +00001149 val = (val << DIG_SIZE) | *d;
Damien George06201ff2014-03-01 19:50:50 +00001150 if (val < oldval) {
Damien George8270e382014-04-03 11:00:54 +00001151 // overflow, return +/- "infinity"
Damien George438c88d2014-02-22 19:25:23 +00001152 if (i->neg == 0) {
Damien George8270e382014-04-03 11:00:54 +00001153 // +infinity
1154 return ~WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001155 } else {
Damien George8270e382014-04-03 11:00:54 +00001156 // -infinity
1157 return WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001158 }
1159 }
1160 }
1161
1162 if (i->neg != 0) {
1163 val = -val;
1164 }
1165
1166 return val;
1167}
1168
Damien George8270e382014-04-03 11:00:54 +00001169// TODO check that this correctly handles overflow in all cases
1170bool mpz_as_int_checked(const mpz_t *i, machine_int_t *value) {
1171 machine_int_t val = 0;
1172 mpz_dig_t *d = i->dig + i->len;
1173
1174 while (--d >= i->dig) {
1175 machine_int_t oldval = val;
1176 val = (val << DIG_SIZE) | *d;
1177 if (val < oldval) {
1178 // overflow
1179 return false;
1180 }
1181 }
1182
1183 if (i->neg != 0) {
1184 val = -val;
1185 }
1186
1187 *value = val;
1188 return true;
1189}
1190
Damien George52608102014-03-08 15:04:54 +00001191#if MICROPY_ENABLE_FLOAT
1192mp_float_t mpz_as_float(const mpz_t *i) {
1193 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001194 mpz_dig_t *d = i->dig + i->len;
1195
1196 while (--d >= i->dig) {
1197 val = val * (1 << DIG_SIZE) + *d;
1198 }
1199
1200 if (i->neg != 0) {
1201 val = -val;
1202 }
1203
1204 return val;
1205}
Damien George52608102014-03-08 15:04:54 +00001206#endif
Damien George438c88d2014-02-22 19:25:23 +00001207
1208uint mpz_as_str_size(const mpz_t *i, uint base) {
1209 if (base < 2 || base > 32) {
1210 return 0;
1211 }
1212
1213 return i->len * DIG_SIZE / log_base2_floor[base] + 2 + 1; // +1 for null byte termination
1214}
1215
1216char *mpz_as_str(const mpz_t *i, uint base) {
1217 char *s = m_new(char, mpz_as_str_size(i, base));
1218 mpz_as_str_inpl(i, base, s);
1219 return s;
1220}
1221
1222// assumes enough space as calculated by mpz_as_str_size
1223// returns length of string, not including null byte
1224uint mpz_as_str_inpl(const mpz_t *i, uint base, char *str) {
1225 if (str == NULL || base < 2 || base > 32) {
1226 str[0] = 0;
1227 return 0;
1228 }
1229
1230 uint ilen = i->len;
1231
1232 if (ilen == 0) {
1233 str[0] = '0';
1234 str[1] = 0;
1235 return 1;
1236 }
1237
1238 // make a copy of mpz digits
1239 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1240 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1241
1242 // convert
1243 char *s = str;
1244 bool done;
1245 do {
1246 mpz_dig_t *d = dig + ilen;
1247 mpz_dbl_dig_t a = 0;
1248
1249 // compute next remainder
1250 while (--d >= dig) {
1251 a = (a << DIG_SIZE) | *d;
1252 *d = a / base;
1253 a %= base;
1254 }
1255
1256 // convert to character
1257 a += '0';
1258 if (a > '9') {
1259 a += 'a' - '9' - 1;
1260 }
1261 *s++ = a;
1262
1263 // check if number is zero
1264 done = true;
1265 for (d = dig; d < dig + ilen; ++d) {
1266 if (*d != 0) {
1267 done = false;
1268 break;
1269 }
1270 }
1271 } while (!done);
1272
1273 if (i->neg != 0) {
1274 *s++ = '-';
1275 }
1276
1277 // reverse string
1278 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1279 char temp = *u;
1280 *u = *v;
1281 *v = temp;
1282 }
1283
1284 s[0] = 0; // null termination
1285
1286 return s - str;
1287}
1288
1289#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ