blob: 8eed283f04e27c997c2f0325d04b206e683665e0 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Damien George438c88d2014-02-22 19:25:23 +000027#include <stdint.h>
28#include <stdbool.h>
29#include <stdlib.h>
30#include <string.h>
31#include <assert.h>
32
33#include "misc.h"
34#include "mpconfig.h"
35#include "mpz.h"
36
37#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
38
Damien George06201ff2014-03-01 19:50:50 +000039#define DIG_SIZE (MPZ_DIG_SIZE)
Damien George438c88d2014-02-22 19:25:23 +000040#define DIG_MASK ((1 << DIG_SIZE) - 1)
41
42/*
Damien George06201ff2014-03-01 19:50:50 +000043 mpz is an arbitrary precision integer type with a public API.
44
45 mpn functions act on non-negative integers represented by an array of generalised
46 digits (eg a word per digit). You also need to specify separately the length of the
47 array. There is no public API for mpn. Rather, the functions are used by mpz to
48 implement its features.
49
50 Integer values are stored little endian (first digit is first in memory).
51
52 Definition of normalise: ?
Damien George438c88d2014-02-22 19:25:23 +000053*/
54
55/* compares i with j
56 returns sign(i - j)
57 assumes i, j are normalised
58*/
Damien George06201ff2014-03-01 19:50:50 +000059STATIC int mpn_cmp(const mpz_dig_t *idig, uint ilen, const mpz_dig_t *jdig, uint jlen) {
Damien George438c88d2014-02-22 19:25:23 +000060 if (ilen < jlen) { return -1; }
61 if (ilen > jlen) { return 1; }
62
63 for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
64 int cmp = *(--idig) - *(--jdig);
65 if (cmp < 0) { return -1; }
66 if (cmp > 0) { return 1; }
67 }
68
69 return 0;
70}
71
Damien Georgec5ac2ac2014-02-26 16:56:30 +000072/* computes i = j << n
73 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +000074 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien Georgec5ac2ac2014-02-26 16:56:30 +000075 can have i, j pointing to same memory
76*/
Damien George06201ff2014-03-01 19:50:50 +000077STATIC uint mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
78 uint n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000079 uint n_part = n % DIG_SIZE;
Damien Georgebb4a43f2014-03-12 15:36:06 +000080 if (n_part == 0) {
81 n_part = DIG_SIZE;
82 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +000083
Damien George06201ff2014-03-01 19:50:50 +000084 // start from the high end of the digit arrays
85 idig += jlen + n_whole - 1;
86 jdig += jlen - 1;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000087
Damien George06201ff2014-03-01 19:50:50 +000088 // shift the digits
89 mpz_dbl_dig_t d = 0;
90 for (uint i = jlen; i > 0; i--, idig--, jdig--) {
91 d |= *jdig;
92 *idig = d >> (DIG_SIZE - n_part);
93 d <<= DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000094 }
95
Damien George06201ff2014-03-01 19:50:50 +000096 // store remaining bits
97 *idig = d >> (DIG_SIZE - n_part);
98 idig -= n_whole - 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +000099 memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t));
Damien George06201ff2014-03-01 19:50:50 +0000100
101 // work out length of result
102 jlen += n_whole;
103 if (idig[jlen - 1] == 0) {
104 jlen--;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000105 }
106
Damien George06201ff2014-03-01 19:50:50 +0000107 // return length of result
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000108 return jlen;
109}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000110
Damien George438c88d2014-02-22 19:25:23 +0000111/* computes i = j >> n
112 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +0000113 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien George438c88d2014-02-22 19:25:23 +0000114 can have i, j pointing to same memory
115*/
Damien George06201ff2014-03-01 19:50:50 +0000116STATIC uint mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
Damien George438c88d2014-02-22 19:25:23 +0000117 uint n_whole = n / DIG_SIZE;
118 uint n_part = n % DIG_SIZE;
119
120 if (n_whole >= jlen) {
121 return 0;
122 }
123
124 jdig += n_whole;
125 jlen -= n_whole;
126
Damien George06201ff2014-03-01 19:50:50 +0000127 for (uint i = jlen; i > 0; i--, idig++, jdig++) {
Damien George438c88d2014-02-22 19:25:23 +0000128 mpz_dbl_dig_t d = *jdig;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000129 if (i > 1) {
Damien George438c88d2014-02-22 19:25:23 +0000130 d |= jdig[1] << DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000131 }
Damien George438c88d2014-02-22 19:25:23 +0000132 d >>= n_part;
133 *idig = d & DIG_MASK;
134 }
135
136 if (idig[-1] == 0) {
Damien George06201ff2014-03-01 19:50:50 +0000137 jlen--;
Damien George438c88d2014-02-22 19:25:23 +0000138 }
139
140 return jlen;
141}
142
143/* computes i = j + k
144 returns number of digits in i
145 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
146 can have i, j, k pointing to same memory
147*/
Damien George06201ff2014-03-01 19:50:50 +0000148STATIC 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 +0000149 mpz_dig_t *oidig = idig;
150 mpz_dbl_dig_t carry = 0;
151
152 jlen -= klen;
153
154 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
155 carry += *jdig + *kdig;
156 *idig = carry & DIG_MASK;
157 carry >>= DIG_SIZE;
158 }
159
160 for (; jlen > 0; --jlen, ++idig, ++jdig) {
161 carry += *jdig;
162 *idig = carry & DIG_MASK;
163 carry >>= DIG_SIZE;
164 }
165
166 if (carry != 0) {
167 *idig++ = carry;
168 }
169
170 return idig - oidig;
171}
172
173/* computes i = j - k
174 returns number of digits in i
175 assumes enough memory in i; assumes normalised j, k; assumes j >= k
176 can have i, j, k pointing to same memory
177*/
Damien George06201ff2014-03-01 19:50:50 +0000178STATIC 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 +0000179 mpz_dig_t *oidig = idig;
180 mpz_dbl_dig_signed_t borrow = 0;
181
182 jlen -= klen;
183
184 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
185 borrow += *jdig - *kdig;
186 *idig = borrow & DIG_MASK;
187 borrow >>= DIG_SIZE;
188 }
189
Damien Georgeaca14122014-02-24 21:32:52 +0000190 for (; jlen > 0; --jlen, ++idig, ++jdig) {
Damien George438c88d2014-02-22 19:25:23 +0000191 borrow += *jdig;
192 *idig = borrow & DIG_MASK;
193 borrow >>= DIG_SIZE;
194 }
195
196 for (--idig; idig >= oidig && *idig == 0; --idig) {
197 }
198
199 return idig + 1 - oidig;
200}
201
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200202/* computes i = j & k
203 returns number of digits in i
204 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
205 can have i, j, k pointing to same memory
206*/
207STATIC uint mpn_and(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
208 mpz_dig_t *oidig = idig;
209
210 jlen -= klen;
211
212 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
213 *idig = *jdig & *kdig;
214 }
215
216 for (; jlen > 0; --jlen, ++idig) {
217 *idig = 0;
218 }
219
220 return idig - oidig;
221}
222
223/* computes i = j | k
224 returns number of digits in i
225 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
226 can have i, j, k pointing to same memory
227*/
228STATIC uint mpn_or(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
229 mpz_dig_t *oidig = idig;
230
231 jlen -= klen;
232
233 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
234 *idig = *jdig | *kdig;
235 }
236
237 for (; jlen > 0; --jlen, ++idig, ++jdig) {
238 *idig = *jdig;
239 }
240
241 return idig - oidig;
242}
243
244/* computes i = j ^ k
245 returns number of digits in i
246 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
247 can have i, j, k pointing to same memory
248*/
249STATIC uint mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
250 mpz_dig_t *oidig = idig;
251
252 jlen -= klen;
253
254 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
255 *idig = *jdig ^ *kdig;
256 }
257
258 for (; jlen > 0; --jlen, ++idig, ++jdig) {
259 *idig = *jdig;
260 }
261
262 return idig - oidig;
263}
264
Damien George438c88d2014-02-22 19:25:23 +0000265/* computes i = i * d1 + d2
266 returns number of digits in i
267 assumes enough memory in i; assumes normalised i; assumes dmul != 0
268*/
Damien George06201ff2014-03-01 19:50:50 +0000269STATIC 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 +0000270 mpz_dig_t *oidig = idig;
271 mpz_dbl_dig_t carry = dadd;
272
273 for (; ilen > 0; --ilen, ++idig) {
274 carry += *idig * dmul; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
275 *idig = carry & DIG_MASK;
276 carry >>= DIG_SIZE;
277 }
278
279 if (carry != 0) {
280 *idig++ = carry;
281 }
282
283 return idig - oidig;
284}
285
286/* computes i = j * k
287 returns number of digits in i
288 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
289 can have j, k point to same memory
290*/
Damien George06201ff2014-03-01 19:50:50 +0000291STATIC 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 +0000292 mpz_dig_t *oidig = idig;
293 uint ilen = 0;
294
295 for (; klen > 0; --klen, ++idig, ++kdig) {
296 mpz_dig_t *id = idig;
297 mpz_dbl_dig_t carry = 0;
298
299 uint jl = jlen;
300 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
301 carry += *id + *jd * *kdig; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
302 *id = carry & DIG_MASK;
303 carry >>= DIG_SIZE;
304 }
305
306 if (carry != 0) {
307 *id++ = carry;
308 }
309
310 ilen = id - oidig;
311 }
312
313 return ilen;
314}
315
316/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
317 assumes den != 0
318 assumes num_dig has enough memory to be extended by 1 digit
319 assumes quo_dig has enough memory (as many digits as num)
320 assumes quo_dig is filled with zeros
321 modifies den_dig memory, but restors it to original state at end
322*/
323
Damien George06201ff2014-03-01 19:50:50 +0000324STATIC 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 +0000325 mpz_dig_t *orig_num_dig = num_dig;
326 mpz_dig_t *orig_quo_dig = quo_dig;
327 mpz_dig_t norm_shift = 0;
328 mpz_dbl_dig_t lead_den_digit;
329
330 // handle simple cases
331 {
332 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
333 if (cmp == 0) {
334 *num_len = 0;
335 quo_dig[0] = 1;
336 *quo_len = 1;
337 return;
338 } else if (cmp < 0) {
339 // numerator remains the same
340 *quo_len = 0;
341 return;
342 }
343 }
344
345 // count number of leading zeros in leading digit of denominator
346 {
347 mpz_dig_t d = den_dig[den_len - 1];
348 while ((d & (1 << (DIG_SIZE - 1))) == 0) {
349 d <<= 1;
350 ++norm_shift;
351 }
352 }
353
354 // normalise denomenator (leading bit of leading digit is 1)
355 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
356 mpz_dig_t d = *den;
357 *den = ((d << norm_shift) | carry) & DIG_MASK;
358 carry = d >> (DIG_SIZE - norm_shift);
359 }
360
361 // now need to shift numerator by same amount as denominator
362 // first, increase length of numerator in case we need more room to shift
363 num_dig[*num_len] = 0;
364 ++(*num_len);
365 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
366 mpz_dig_t n = *num;
367 *num = ((n << norm_shift) | carry) & DIG_MASK;
368 carry = n >> (DIG_SIZE - norm_shift);
369 }
370
371 // cache the leading digit of the denominator
372 lead_den_digit = den_dig[den_len - 1];
373
374 // point num_dig to last digit in numerator
375 num_dig += *num_len - 1;
376
377 // calculate number of digits in quotient
378 *quo_len = *num_len - den_len;
379
380 // point to last digit to store for quotient
381 quo_dig += *quo_len - 1;
382
383 // keep going while we have enough digits to divide
384 while (*num_len > den_len) {
385 mpz_dbl_dig_t quo = (*num_dig << DIG_SIZE) | num_dig[-1];
386
387 // get approximate quotient
388 quo /= lead_den_digit;
389
390 // multiply quo by den and subtract from num get remainder
391 {
392 mpz_dbl_dig_signed_t borrow = 0;
393
394 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
395 borrow += *n - quo * *d; // will overflow if DIG_SIZE >= 16
396 *n = borrow & DIG_MASK;
397 borrow >>= DIG_SIZE;
398 }
399 borrow += *num_dig; // will overflow if DIG_SIZE >= 16
400 *num_dig = borrow & DIG_MASK;
401 borrow >>= DIG_SIZE;
402
403 // adjust quotient if it is too big
404 for (; borrow != 0; --quo) {
405 mpz_dbl_dig_t carry = 0;
406 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
407 carry += *n + *d;
408 *n = carry & DIG_MASK;
409 carry >>= DIG_SIZE;
410 }
411 carry += *num_dig;
412 *num_dig = carry & DIG_MASK;
413 carry >>= DIG_SIZE;
414
415 borrow += carry;
416 }
417 }
418
419 // store this digit of the quotient
420 *quo_dig = quo & DIG_MASK;
421 --quo_dig;
422
423 // move down to next digit of numerator
424 --num_dig;
425 --(*num_len);
426 }
427
428 // unnormalise denomenator
429 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
430 mpz_dig_t d = *den;
431 *den = ((d >> norm_shift) | carry) & DIG_MASK;
432 carry = d << (DIG_SIZE - norm_shift);
433 }
434
435 // unnormalise numerator (remainder now)
436 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
437 mpz_dig_t n = *num;
438 *num = ((n >> norm_shift) | carry) & DIG_MASK;
439 carry = n << (DIG_SIZE - norm_shift);
440 }
441
442 // strip trailing zeros
443
444 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
445 --(*quo_len);
446 }
447
448 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
449 --(*num_len);
450 }
451}
452
Damien George06201ff2014-03-01 19:50:50 +0000453#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000454
455static const uint log_base2_floor[] = {
456 0,
457 0, 1, 1, 2,
458 2, 2, 2, 3,
459 3, 3, 3, 3,
460 3, 3, 3, 4,
461 4, 4, 4, 4,
462 4, 4, 4, 4,
463 4, 4, 4, 4,
464 4, 4, 4, 5
465};
466
Damien George438c88d2014-02-22 19:25:23 +0000467void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000468 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000469 z->fixed_dig = 0;
470 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000471 z->len = 0;
472 z->dig = NULL;
473}
474
475void mpz_init_from_int(mpz_t *z, machine_int_t val) {
476 mpz_init_zero(z);
477 mpz_set_from_int(z, val);
478}
479
Damien George06201ff2014-03-01 19:50:50 +0000480void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, uint alloc, machine_int_t val) {
481 z->neg = 0;
482 z->fixed_dig = 1;
483 z->alloc = alloc;
484 z->len = 0;
485 z->dig = dig;
486 mpz_set_from_int(z, val);
487}
488
Damien George438c88d2014-02-22 19:25:23 +0000489void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000490 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000491 m_del(mpz_dig_t, z->dig, z->alloc);
492 }
493}
494
495mpz_t *mpz_zero(void) {
496 mpz_t *z = m_new_obj(mpz_t);
497 mpz_init_zero(z);
498 return z;
499}
500
501mpz_t *mpz_from_int(machine_int_t val) {
502 mpz_t *z = mpz_zero();
503 mpz_set_from_int(z, val);
504 return z;
505}
506
Damien Georgebb4a43f2014-03-12 15:36:06 +0000507mpz_t *mpz_from_ll(long long val) {
508 mpz_t *z = mpz_zero();
509 mpz_set_from_ll(z, val);
510 return z;
511}
512
Damien George438c88d2014-02-22 19:25:23 +0000513mpz_t *mpz_from_str(const char *str, uint len, bool neg, uint base) {
514 mpz_t *z = mpz_zero();
515 mpz_set_from_str(z, str, len, neg, base);
516 return z;
517}
518
519void mpz_free(mpz_t *z) {
520 if (z != NULL) {
521 m_del(mpz_dig_t, z->dig, z->alloc);
522 m_del_obj(mpz_t, z);
523 }
524}
525
526STATIC void mpz_need_dig(mpz_t *z, uint need) {
Damien George438c88d2014-02-22 19:25:23 +0000527 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000528 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000529 }
530
Damien George06201ff2014-03-01 19:50:50 +0000531 if (z->dig == NULL || z->alloc < need) {
532 if (z->fixed_dig) {
533 // cannot reallocate fixed buffers
534 assert(0);
535 return;
536 }
537 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
538 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000539 }
540}
541
542mpz_t *mpz_clone(const mpz_t *src) {
543 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000544 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000545 z->fixed_dig = 0;
546 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000547 z->len = src->len;
548 if (src->dig == NULL) {
549 z->dig = NULL;
550 } else {
551 z->dig = m_new(mpz_dig_t, z->alloc);
552 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
553 }
554 return z;
555}
556
Damien George06201ff2014-03-01 19:50:50 +0000557/* sets dest = src
558 can have dest, src the same
559*/
Damien George438c88d2014-02-22 19:25:23 +0000560void mpz_set(mpz_t *dest, const mpz_t *src) {
561 mpz_need_dig(dest, src->len);
562 dest->neg = src->neg;
563 dest->len = src->len;
564 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
565}
566
567void mpz_set_from_int(mpz_t *z, machine_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000568 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000569
Damien Georgebb4a43f2014-03-12 15:36:06 +0000570 machine_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000571 if (val < 0) {
572 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000573 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000574 } else {
575 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000576 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000577 }
578
579 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000580 while (uval > 0) {
581 z->dig[z->len++] = uval & DIG_MASK;
582 uval >>= DIG_SIZE;
583 }
584}
585
586void mpz_set_from_ll(mpz_t *z, long long val) {
587 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
588
589 unsigned long long uval;
590 if (val < 0) {
591 z->neg = 1;
592 uval = -val;
593 } else {
594 z->neg = 0;
595 uval = val;
596 }
597
598 z->len = 0;
599 while (uval > 0) {
600 z->dig[z->len++] = uval & DIG_MASK;
601 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000602 }
603}
604
605// returns number of bytes from str that were processed
606uint mpz_set_from_str(mpz_t *z, const char *str, uint len, bool neg, uint base) {
607 assert(base < 36);
608
609 const char *cur = str;
610 const char *top = str + len;
611
612 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
613
614 if (neg) {
615 z->neg = 1;
616 } else {
617 z->neg = 0;
618 }
619
620 z->len = 0;
621 for (; cur < top; ++cur) { // XXX UTF8 next char
622 //uint v = char_to_numeric(cur#); // XXX UTF8 get char
623 uint v = *cur;
624 if ('0' <= v && v <= '9') {
625 v -= '0';
626 } else if ('A' <= v && v <= 'Z') {
627 v -= 'A' - 10;
628 } else if ('a' <= v && v <= 'z') {
629 v -= 'a' - 10;
630 } else {
631 break;
632 }
633 if (v >= base) {
634 break;
635 }
636 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
637 }
638
639 return cur - str;
640}
641
642bool mpz_is_zero(const mpz_t *z) {
643 return z->len == 0;
644}
645
646bool mpz_is_pos(const mpz_t *z) {
647 return z->len > 0 && z->neg == 0;
648}
649
650bool mpz_is_neg(const mpz_t *z) {
651 return z->len > 0 && z->neg != 0;
652}
653
654bool mpz_is_odd(const mpz_t *z) {
655 return z->len > 0 && (z->dig[0] & 1) != 0;
656}
657
658bool mpz_is_even(const mpz_t *z) {
659 return z->len == 0 || (z->dig[0] & 1) == 0;
660}
661
662int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
663 int cmp = z2->neg - z1->neg;
664 if (cmp != 0) {
665 return cmp;
666 }
667 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
668 if (z1->neg != 0) {
669 cmp = -cmp;
670 }
671 return cmp;
672}
673
Damien George06201ff2014-03-01 19:50:50 +0000674#if 0
675// obsolete
676// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeaca14122014-02-24 21:32:52 +0000677int mpz_cmp_sml_int(const mpz_t *z, machine_int_t sml_int) {
Damien George438c88d2014-02-22 19:25:23 +0000678 int cmp;
679 if (z->neg == 0) {
680 if (sml_int < 0) return 1;
681 if (sml_int == 0) {
682 if (z->len == 0) return 0;
683 return 1;
684 }
685 if (z->len == 0) return -1;
686 assert(sml_int < (1 << DIG_SIZE));
687 if (z->len != 1) return 1;
688 cmp = z->dig[0] - sml_int;
689 } else {
690 if (sml_int > 0) return -1;
691 if (sml_int == 0) {
692 if (z->len == 0) return 0;
693 return -1;
694 }
695 if (z->len == 0) return 1;
696 assert(sml_int > -(1 << DIG_SIZE));
697 if (z->len != 1) return -1;
698 cmp = -z->dig[0] - sml_int;
699 }
700 if (cmp < 0) return -1;
701 if (cmp > 0) return 1;
702 return 0;
703}
Damien George06201ff2014-03-01 19:50:50 +0000704#endif
Damien George438c88d2014-02-22 19:25:23 +0000705
Damien George438c88d2014-02-22 19:25:23 +0000706#if 0
707these functions are unused
708
709/* returns abs(z)
710*/
711mpz_t *mpz_abs(const mpz_t *z) {
712 mpz_t *z2 = mpz_clone(z);
713 z2->neg = 0;
714 return z2;
715}
716
717/* returns -z
718*/
719mpz_t *mpz_neg(const mpz_t *z) {
720 mpz_t *z2 = mpz_clone(z);
721 z2->neg = 1 - z2->neg;
722 return z2;
723}
724
725/* returns lhs + rhs
726 can have lhs, rhs the same
727*/
728mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
729 mpz_t *z = mpz_zero();
730 mpz_add_inpl(z, lhs, rhs);
731 return z;
732}
733
734/* returns lhs - rhs
735 can have lhs, rhs the same
736*/
737mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
738 mpz_t *z = mpz_zero();
739 mpz_sub_inpl(z, lhs, rhs);
740 return z;
741}
742
743/* returns lhs * rhs
744 can have lhs, rhs the same
745*/
746mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
747 mpz_t *z = mpz_zero();
748 mpz_mul_inpl(z, lhs, rhs);
749 return z;
750}
751
752/* returns lhs ** rhs
753 can have lhs, rhs the same
754*/
755mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
756 mpz_t *z = mpz_zero();
757 mpz_pow_inpl(z, lhs, rhs);
758 return z;
759}
760#endif
761
762/* computes dest = abs(z)
763 can have dest, z the same
764*/
765void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
766 if (dest != z) {
767 mpz_set(dest, z);
768 }
769 dest->neg = 0;
770}
771
772/* computes dest = -z
773 can have dest, z the same
774*/
775void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
776 if (dest != z) {
777 mpz_set(dest, z);
778 }
779 dest->neg = 1 - dest->neg;
780}
781
Damien George06201ff2014-03-01 19:50:50 +0000782/* computes dest = ~z (= -z - 1)
783 can have dest, z the same
784*/
785void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
786 if (dest != z) {
787 mpz_set(dest, z);
788 }
789 if (dest->neg) {
790 dest->neg = 0;
791 mpz_dig_t k = 1;
792 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
793 } else {
794 mpz_dig_t k = 1;
795 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
796 dest->neg = 1;
797 }
798}
799
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000800/* computes dest = lhs << rhs
801 can have dest, lhs the same
802*/
803void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000804 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000805 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000806 } else if (rhs < 0) {
807 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000808 } else {
Damien George06201ff2014-03-01 19:50:50 +0000809 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
810 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
811 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000812 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000813}
814
815/* computes dest = lhs >> rhs
816 can have dest, lhs the same
817*/
818void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000819 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000820 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000821 } else if (rhs < 0) {
822 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000823 } else {
Damien George06201ff2014-03-01 19:50:50 +0000824 mpz_need_dig(dest, lhs->len);
825 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
826 dest->neg = lhs->neg;
827 if (dest->neg) {
828 // arithmetic shift right, rounding to negative infinity
829 uint n_whole = rhs / DIG_SIZE;
830 uint n_part = rhs % DIG_SIZE;
831 mpz_dig_t round_up = 0;
832 for (uint i = 0; i < lhs->len && i < n_whole; i++) {
833 if (lhs->dig[i] != 0) {
834 round_up = 1;
835 break;
836 }
837 }
838 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
839 round_up = 1;
840 }
841 if (round_up) {
842 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
843 }
844 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000845 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000846}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000847
Damien George438c88d2014-02-22 19:25:23 +0000848/* computes dest = lhs + rhs
849 can have dest, lhs, rhs the same
850*/
851void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
852 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
853 const mpz_t *temp = lhs;
854 lhs = rhs;
855 rhs = temp;
856 }
857
858 if (lhs->neg == rhs->neg) {
859 mpz_need_dig(dest, lhs->len + 1);
860 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
861 } else {
862 mpz_need_dig(dest, lhs->len);
863 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
864 }
865
866 dest->neg = lhs->neg;
867}
868
869/* computes dest = lhs - rhs
870 can have dest, lhs, rhs the same
871*/
872void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
873 bool neg = false;
874
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 neg = true;
880 }
881
882 if (lhs->neg != rhs->neg) {
883 mpz_need_dig(dest, lhs->len + 1);
884 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
885 } else {
886 mpz_need_dig(dest, lhs->len);
887 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
888 }
889
890 if (neg) {
891 dest->neg = 1 - lhs->neg;
892 } else {
893 dest->neg = lhs->neg;
894 }
895}
896
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200897/* computes dest = lhs & rhs
898 can have dest, lhs, rhs the same
899*/
900void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
901 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
902 const mpz_t *temp = lhs;
903 lhs = rhs;
904 rhs = temp;
905 }
906
907 if (lhs->neg == rhs->neg) {
908 mpz_need_dig(dest, lhs->len);
909 dest->len = mpn_and(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
910 } else {
911 mpz_need_dig(dest, lhs->len);
912 // TODO
913 assert(0);
914// dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
915 }
916
917 dest->neg = lhs->neg;
918}
919
920/* computes dest = lhs | rhs
921 can have dest, lhs, rhs the same
922*/
923void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
924 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
925 const mpz_t *temp = lhs;
926 lhs = rhs;
927 rhs = temp;
928 }
929
930 if (lhs->neg == rhs->neg) {
931 mpz_need_dig(dest, lhs->len);
932 dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
933 } else {
934 mpz_need_dig(dest, lhs->len);
935 // TODO
936 assert(0);
937// dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
938 }
939
940 dest->neg = lhs->neg;
941}
942
943/* computes dest = lhs ^ rhs
944 can have dest, lhs, rhs the same
945*/
946void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
947 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
948 const mpz_t *temp = lhs;
949 lhs = rhs;
950 rhs = temp;
951 }
952
953 if (lhs->neg == rhs->neg) {
954 mpz_need_dig(dest, lhs->len);
955 dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
956 } else {
957 mpz_need_dig(dest, lhs->len);
958 // TODO
959 assert(0);
960// dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
961 }
962
963 dest->neg = 0;
964}
965
Damien George438c88d2014-02-22 19:25:23 +0000966/* computes dest = lhs * rhs
967 can have dest, lhs, rhs the same
968*/
969void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
970{
971 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000972 mpz_set_from_int(dest, 0);
973 return;
Damien George438c88d2014-02-22 19:25:23 +0000974 }
975
976 mpz_t *temp = NULL;
977 if (lhs == dest) {
978 lhs = temp = mpz_clone(lhs);
979 if (rhs == dest) {
980 rhs = lhs;
981 }
982 } else if (rhs == dest) {
983 rhs = temp = mpz_clone(rhs);
984 }
985
986 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
987 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
988 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
989
990 if (lhs->neg == rhs->neg) {
991 dest->neg = 0;
992 } else {
993 dest->neg = 1;
994 }
995
996 mpz_free(temp);
997}
998
999/* computes dest = lhs ** rhs
1000 can have dest, lhs, rhs the same
1001*/
1002void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1003 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001004 mpz_set_from_int(dest, 0);
1005 return;
Damien George438c88d2014-02-22 19:25:23 +00001006 }
1007
1008 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001009 mpz_set_from_int(dest, 1);
1010 return;
Damien George438c88d2014-02-22 19:25:23 +00001011 }
1012
1013 mpz_t *x = mpz_clone(lhs);
1014 mpz_t *n = mpz_clone(rhs);
1015
1016 mpz_set_from_int(dest, 1);
1017
1018 while (n->len > 0) {
1019 if (mpz_is_odd(n)) {
1020 mpz_mul_inpl(dest, dest, x);
1021 }
Damien George438c88d2014-02-22 19:25:23 +00001022 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
Damien George5bf565e2014-04-04 00:16:32 +01001023 if (n->len == 0) {
1024 break;
1025 }
1026 mpz_mul_inpl(x, x, x);
Damien George438c88d2014-02-22 19:25:23 +00001027 }
1028
1029 mpz_free(x);
1030 mpz_free(n);
1031}
1032
1033/* computes gcd(z1, z2)
1034 based on Knuth's modified gcd algorithm (I think?)
1035 gcd(z1, z2) >= 0
1036 gcd(0, 0) = 0
1037 gcd(z, 0) = abs(z)
1038*/
1039mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
1040 if (z1->len == 0) {
1041 mpz_t *a = mpz_clone(z2);
1042 a->neg = 0;
1043 return a;
1044 } else if (z2->len == 0) {
1045 mpz_t *a = mpz_clone(z1);
1046 a->neg = 0;
1047 return a;
1048 }
1049
1050 mpz_t *a = mpz_clone(z1);
1051 mpz_t *b = mpz_clone(z2);
1052 mpz_t c; mpz_init_zero(&c);
1053 a->neg = 0;
1054 b->neg = 0;
1055
1056 for (;;) {
1057 if (mpz_cmp(a, b) < 0) {
1058 if (a->len == 0) {
1059 mpz_free(a);
1060 mpz_deinit(&c);
1061 return b;
1062 }
1063 mpz_t *t = a; a = b; b = t;
1064 }
1065 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
1066 break;
1067 }
1068 mpz_set(&c, b);
1069 do {
1070 mpz_add_inpl(&c, &c, &c);
1071 } while (mpz_cmp(&c, a) <= 0);
1072 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
1073 mpz_sub_inpl(a, a, &c);
1074 }
1075
1076 mpz_deinit(&c);
1077
1078 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
1079 mpz_free(a);
1080 return b;
1081 } else {
1082 mpz_free(b);
1083 return a;
1084 }
1085}
1086
1087/* computes lcm(z1, z2)
1088 = abs(z1) / gcd(z1, z2) * abs(z2)
1089 lcm(z1, z1) >= 0
1090 lcm(0, 0) = 0
1091 lcm(z, 0) = 0
1092*/
1093mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2)
1094{
stijn01d6be42014-05-05 12:18:27 +02001095 // braces below are required for compilation to succeed with CL, see bug report
1096 // https://connect.microsoft.com/VisualStudio/feedback/details/864169/compilation-error-when-braces-are-left-out-of-single-line-if-statement
1097 if (z1->len == 0 || z2->len == 0) {
1098 return mpz_zero();
1099 }
Damien George438c88d2014-02-22 19:25:23 +00001100
1101 mpz_t *gcd = mpz_gcd(z1, z2);
1102 mpz_t *quo = mpz_zero();
1103 mpz_t *rem = mpz_zero();
1104 mpz_divmod_inpl(quo, rem, z1, gcd);
1105 mpz_mul_inpl(rem, quo, z2);
1106 mpz_free(gcd);
1107 mpz_free(quo);
1108 rem->neg = 0;
1109 return rem;
1110}
1111
1112/* computes new integers in quo and rem such that:
1113 quo * rhs + rem = lhs
1114 0 <= rem < rhs
1115 can have lhs, rhs the same
1116*/
1117void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
1118 *quo = mpz_zero();
1119 *rem = mpz_zero();
1120 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
1121}
1122
1123/* computes new integers in quo and rem such that:
1124 quo * rhs + rem = lhs
1125 0 <= rem < rhs
1126 can have lhs, rhs the same
1127*/
1128void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
1129 if (rhs->len == 0) {
1130 mpz_set_from_int(dest_quo, 0);
1131 mpz_set_from_int(dest_rem, 0);
1132 return;
1133 }
1134
1135 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
1136 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
1137 dest_quo->len = 0;
1138 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
1139 mpz_set(dest_rem, lhs);
1140 //rhs->dig[rhs->len] = 0;
1141 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
1142
1143 if (lhs->neg != rhs->neg) {
1144 dest_quo->neg = 1;
1145 }
1146}
1147
1148#if 0
1149these functions are unused
1150
1151/* computes floor(lhs / rhs)
1152 can have lhs, rhs the same
1153*/
1154mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
1155 mpz_t *quo = mpz_zero();
1156 mpz_t rem; mpz_init_zero(&rem);
1157 mpz_divmod_inpl(quo, &rem, lhs, rhs);
1158 mpz_deinit(&rem);
1159 return quo;
1160}
1161
1162/* computes lhs % rhs ( >= 0)
1163 can have lhs, rhs the same
1164*/
1165mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1166 mpz_t quo; mpz_init_zero(&quo);
1167 mpz_t *rem = mpz_zero();
1168 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1169 mpz_deinit(&quo);
1170 return rem;
1171}
1172#endif
1173
Damien George8270e382014-04-03 11:00:54 +00001174// TODO check that this correctly handles overflow in all cases
Damien Georgeaca14122014-02-24 21:32:52 +00001175machine_int_t mpz_as_int(const mpz_t *i) {
1176 machine_int_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001177 mpz_dig_t *d = i->dig + i->len;
1178
Damien George06201ff2014-03-01 19:50:50 +00001179 while (--d >= i->dig) {
Damien Georgeaca14122014-02-24 21:32:52 +00001180 machine_int_t oldval = val;
Damien George438c88d2014-02-22 19:25:23 +00001181 val = (val << DIG_SIZE) | *d;
Damien George06201ff2014-03-01 19:50:50 +00001182 if (val < oldval) {
Damien George8270e382014-04-03 11:00:54 +00001183 // overflow, return +/- "infinity"
Damien George438c88d2014-02-22 19:25:23 +00001184 if (i->neg == 0) {
Damien George8270e382014-04-03 11:00:54 +00001185 // +infinity
1186 return ~WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001187 } else {
Damien George8270e382014-04-03 11:00:54 +00001188 // -infinity
1189 return WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001190 }
1191 }
1192 }
1193
1194 if (i->neg != 0) {
1195 val = -val;
1196 }
1197
1198 return val;
1199}
1200
Damien George8270e382014-04-03 11:00:54 +00001201// TODO check that this correctly handles overflow in all cases
1202bool mpz_as_int_checked(const mpz_t *i, machine_int_t *value) {
1203 machine_int_t val = 0;
1204 mpz_dig_t *d = i->dig + i->len;
1205
1206 while (--d >= i->dig) {
1207 machine_int_t oldval = val;
1208 val = (val << DIG_SIZE) | *d;
1209 if (val < oldval) {
1210 // overflow
1211 return false;
1212 }
1213 }
1214
1215 if (i->neg != 0) {
1216 val = -val;
1217 }
1218
1219 *value = val;
1220 return true;
1221}
1222
Damien George52608102014-03-08 15:04:54 +00001223#if MICROPY_ENABLE_FLOAT
1224mp_float_t mpz_as_float(const mpz_t *i) {
1225 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001226 mpz_dig_t *d = i->dig + i->len;
1227
1228 while (--d >= i->dig) {
1229 val = val * (1 << DIG_SIZE) + *d;
1230 }
1231
1232 if (i->neg != 0) {
1233 val = -val;
1234 }
1235
1236 return val;
1237}
Damien George52608102014-03-08 15:04:54 +00001238#endif
Damien George438c88d2014-02-22 19:25:23 +00001239
1240uint mpz_as_str_size(const mpz_t *i, uint base) {
1241 if (base < 2 || base > 32) {
1242 return 0;
1243 }
1244
1245 return i->len * DIG_SIZE / log_base2_floor[base] + 2 + 1; // +1 for null byte termination
1246}
1247
Dave Hylandsc4029e52014-04-07 11:19:51 -07001248uint mpz_as_str_size_formatted(const mpz_t *i, uint base, const char *prefix, char comma) {
1249 if (base < 2 || base > 32) {
1250 return 0;
1251 }
1252
1253 uint num_digits = i->len * DIG_SIZE / log_base2_floor[base] + 1;
1254 uint num_commas = comma ? num_digits / 3: 0;
1255 uint prefix_len = prefix ? strlen(prefix) : 0;
1256
1257 return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte
1258}
1259
Damien George438c88d2014-02-22 19:25:23 +00001260char *mpz_as_str(const mpz_t *i, uint base) {
1261 char *s = m_new(char, mpz_as_str_size(i, base));
Dave Hylandsc4029e52014-04-07 11:19:51 -07001262 mpz_as_str_inpl(i, base, "", 'a', 0, s);
Damien George438c88d2014-02-22 19:25:23 +00001263 return s;
1264}
1265
1266// assumes enough space as calculated by mpz_as_str_size
1267// returns length of string, not including null byte
Dave Hylandsc4029e52014-04-07 11:19:51 -07001268uint mpz_as_str_inpl(const mpz_t *i, uint base, const char *prefix, char base_char, char comma, char *str) {
Damien George438c88d2014-02-22 19:25:23 +00001269 if (str == NULL || base < 2 || base > 32) {
1270 str[0] = 0;
1271 return 0;
1272 }
1273
1274 uint ilen = i->len;
1275
Dave Hylandsc4029e52014-04-07 11:19:51 -07001276 char *s = str;
Damien George438c88d2014-02-22 19:25:23 +00001277 if (ilen == 0) {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001278 if (prefix) {
1279 while (*prefix)
1280 *s++ = *prefix++;
1281 }
1282 *s++ = '0';
1283 *s = '\0';
1284 return s - str;
Damien George438c88d2014-02-22 19:25:23 +00001285 }
1286
Damien Georgeeec91052014-04-08 23:11:00 +01001287 // make a copy of mpz digits, so we can do the div/mod calculation
Damien George438c88d2014-02-22 19:25:23 +00001288 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1289 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1290
1291 // convert
Dave Hylandsc4029e52014-04-07 11:19:51 -07001292 char *last_comma = str;
Damien George438c88d2014-02-22 19:25:23 +00001293 bool done;
1294 do {
1295 mpz_dig_t *d = dig + ilen;
1296 mpz_dbl_dig_t a = 0;
1297
1298 // compute next remainder
1299 while (--d >= dig) {
1300 a = (a << DIG_SIZE) | *d;
1301 *d = a / base;
1302 a %= base;
1303 }
1304
1305 // convert to character
1306 a += '0';
1307 if (a > '9') {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001308 a += base_char - '9' - 1;
Damien George438c88d2014-02-22 19:25:23 +00001309 }
1310 *s++ = a;
1311
1312 // check if number is zero
1313 done = true;
1314 for (d = dig; d < dig + ilen; ++d) {
1315 if (*d != 0) {
1316 done = false;
1317 break;
1318 }
1319 }
Dave Hylandsc4029e52014-04-07 11:19:51 -07001320 if (comma && (s - last_comma) == 3) {
1321 *s++ = comma;
1322 last_comma = s;
1323 }
1324 }
1325 while (!done);
Damien George438c88d2014-02-22 19:25:23 +00001326
Damien Georgeeec91052014-04-08 23:11:00 +01001327 // free the copy of the digits array
1328 m_del(mpz_dig_t, dig, ilen);
1329
Dave Hylandsc4029e52014-04-07 11:19:51 -07001330 if (prefix) {
1331 const char *p = &prefix[strlen(prefix)];
1332 while (p > prefix) {
1333 *s++ = *--p;
1334 }
1335 }
Damien George438c88d2014-02-22 19:25:23 +00001336 if (i->neg != 0) {
1337 *s++ = '-';
1338 }
1339
1340 // reverse string
1341 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1342 char temp = *u;
1343 *u = *v;
1344 *v = temp;
1345 }
1346
Dave Hylandsc4029e52014-04-07 11:19:51 -07001347 *s = '\0'; // null termination
Damien George438c88d2014-02-22 19:25:23 +00001348
1349 return s - str;
1350}
1351
1352#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ