blob: 9c42878ff8a6899ca3d0abbfa1e127ddd05c3a0f [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
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200210 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
211 *idig = *jdig & *kdig;
212 }
213
Damien George561e4252014-05-12 23:27:29 +0100214 // remove trailing zeros
Damien George51fab282014-05-13 22:58:00 +0100215 for (--idig; idig >= oidig && *idig == 0; --idig) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200216 }
217
Damien George51fab282014-05-13 22:58:00 +0100218 return idig + 1 - oidig;
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200219}
220
Damien Georgef55cf102014-05-29 15:01:49 +0100221/* computes i = j & -k = j & (~k + 1)
222 returns number of digits in i
223 assumes enough memory in i; assumes normalised j, k
224 can have i, j, k pointing to same memory
225*/
226STATIC uint mpn_and_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
227 mpz_dig_t *oidig = idig;
228 mpz_dbl_dig_t carry = 1;
229
230 for (; jlen > 0 && klen > 0; --jlen, --klen, ++idig, ++jdig, ++kdig) {
231 carry += *kdig ^ DIG_MASK;
232 *idig = (*jdig & carry) & DIG_MASK;
233 carry >>= DIG_SIZE;
234 }
235
236 for (; jlen > 0; --jlen, ++idig, ++jdig) {
237 carry += DIG_MASK;
238 *idig = (*jdig & carry) & DIG_MASK;
239 carry >>= DIG_SIZE;
240 }
241
242 if (carry != 0) {
243 *idig = carry;
244 } else {
245 // remove trailing zeros
246 for (--idig; idig >= oidig && *idig == 0; --idig) {
247 }
248 }
249
250 return idig + 1 - oidig;
251}
252
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200253/* computes i = j | k
254 returns number of digits in i
255 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
256 can have i, j, k pointing to same memory
257*/
258STATIC uint mpn_or(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
259 mpz_dig_t *oidig = idig;
260
261 jlen -= klen;
262
263 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
264 *idig = *jdig | *kdig;
265 }
266
267 for (; jlen > 0; --jlen, ++idig, ++jdig) {
268 *idig = *jdig;
269 }
270
271 return idig - oidig;
272}
273
274/* computes i = j ^ k
275 returns number of digits in i
276 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
277 can have i, j, k pointing to same memory
278*/
279STATIC uint mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
280 mpz_dig_t *oidig = idig;
281
282 jlen -= klen;
283
284 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
285 *idig = *jdig ^ *kdig;
286 }
287
288 for (; jlen > 0; --jlen, ++idig, ++jdig) {
289 *idig = *jdig;
290 }
291
292 return idig - oidig;
293}
294
Damien George438c88d2014-02-22 19:25:23 +0000295/* computes i = i * d1 + d2
296 returns number of digits in i
297 assumes enough memory in i; assumes normalised i; assumes dmul != 0
298*/
Damien George06201ff2014-03-01 19:50:50 +0000299STATIC 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 +0000300 mpz_dig_t *oidig = idig;
301 mpz_dbl_dig_t carry = dadd;
302
303 for (; ilen > 0; --ilen, ++idig) {
304 carry += *idig * dmul; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
305 *idig = carry & DIG_MASK;
306 carry >>= DIG_SIZE;
307 }
308
309 if (carry != 0) {
310 *idig++ = carry;
311 }
312
313 return idig - oidig;
314}
315
316/* computes i = j * k
317 returns number of digits in i
318 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
319 can have j, k point to same memory
320*/
Damien George06201ff2014-03-01 19:50:50 +0000321STATIC 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 +0000322 mpz_dig_t *oidig = idig;
323 uint ilen = 0;
324
325 for (; klen > 0; --klen, ++idig, ++kdig) {
326 mpz_dig_t *id = idig;
327 mpz_dbl_dig_t carry = 0;
328
329 uint jl = jlen;
330 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
331 carry += *id + *jd * *kdig; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
332 *id = carry & DIG_MASK;
333 carry >>= DIG_SIZE;
334 }
335
336 if (carry != 0) {
337 *id++ = carry;
338 }
339
340 ilen = id - oidig;
341 }
342
343 return ilen;
344}
345
346/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
347 assumes den != 0
348 assumes num_dig has enough memory to be extended by 1 digit
349 assumes quo_dig has enough memory (as many digits as num)
350 assumes quo_dig is filled with zeros
351 modifies den_dig memory, but restors it to original state at end
352*/
353
Damien George06201ff2014-03-01 19:50:50 +0000354STATIC 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 +0000355 mpz_dig_t *orig_num_dig = num_dig;
356 mpz_dig_t *orig_quo_dig = quo_dig;
357 mpz_dig_t norm_shift = 0;
358 mpz_dbl_dig_t lead_den_digit;
359
360 // handle simple cases
361 {
362 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
363 if (cmp == 0) {
364 *num_len = 0;
365 quo_dig[0] = 1;
366 *quo_len = 1;
367 return;
368 } else if (cmp < 0) {
369 // numerator remains the same
370 *quo_len = 0;
371 return;
372 }
373 }
374
375 // count number of leading zeros in leading digit of denominator
376 {
377 mpz_dig_t d = den_dig[den_len - 1];
378 while ((d & (1 << (DIG_SIZE - 1))) == 0) {
379 d <<= 1;
380 ++norm_shift;
381 }
382 }
383
384 // normalise denomenator (leading bit of leading digit is 1)
385 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
386 mpz_dig_t d = *den;
387 *den = ((d << norm_shift) | carry) & DIG_MASK;
388 carry = d >> (DIG_SIZE - norm_shift);
389 }
390
391 // now need to shift numerator by same amount as denominator
392 // first, increase length of numerator in case we need more room to shift
393 num_dig[*num_len] = 0;
394 ++(*num_len);
395 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
396 mpz_dig_t n = *num;
397 *num = ((n << norm_shift) | carry) & DIG_MASK;
398 carry = n >> (DIG_SIZE - norm_shift);
399 }
400
401 // cache the leading digit of the denominator
402 lead_den_digit = den_dig[den_len - 1];
403
404 // point num_dig to last digit in numerator
405 num_dig += *num_len - 1;
406
407 // calculate number of digits in quotient
408 *quo_len = *num_len - den_len;
409
410 // point to last digit to store for quotient
411 quo_dig += *quo_len - 1;
412
413 // keep going while we have enough digits to divide
414 while (*num_len > den_len) {
415 mpz_dbl_dig_t quo = (*num_dig << DIG_SIZE) | num_dig[-1];
416
417 // get approximate quotient
418 quo /= lead_den_digit;
419
420 // multiply quo by den and subtract from num get remainder
421 {
422 mpz_dbl_dig_signed_t borrow = 0;
423
424 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
425 borrow += *n - quo * *d; // will overflow if DIG_SIZE >= 16
426 *n = borrow & DIG_MASK;
427 borrow >>= DIG_SIZE;
428 }
429 borrow += *num_dig; // will overflow if DIG_SIZE >= 16
430 *num_dig = borrow & DIG_MASK;
431 borrow >>= DIG_SIZE;
432
433 // adjust quotient if it is too big
434 for (; borrow != 0; --quo) {
435 mpz_dbl_dig_t carry = 0;
436 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
437 carry += *n + *d;
438 *n = carry & DIG_MASK;
439 carry >>= DIG_SIZE;
440 }
441 carry += *num_dig;
442 *num_dig = carry & DIG_MASK;
443 carry >>= DIG_SIZE;
444
445 borrow += carry;
446 }
447 }
448
449 // store this digit of the quotient
450 *quo_dig = quo & DIG_MASK;
451 --quo_dig;
452
453 // move down to next digit of numerator
454 --num_dig;
455 --(*num_len);
456 }
457
458 // unnormalise denomenator
459 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
460 mpz_dig_t d = *den;
461 *den = ((d >> norm_shift) | carry) & DIG_MASK;
462 carry = d << (DIG_SIZE - norm_shift);
463 }
464
465 // unnormalise numerator (remainder now)
466 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
467 mpz_dig_t n = *num;
468 *num = ((n >> norm_shift) | carry) & DIG_MASK;
469 carry = n << (DIG_SIZE - norm_shift);
470 }
471
472 // strip trailing zeros
473
474 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
475 --(*quo_len);
476 }
477
478 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
479 --(*num_len);
480 }
481}
482
Damien George06201ff2014-03-01 19:50:50 +0000483#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000484
485static const uint log_base2_floor[] = {
486 0,
487 0, 1, 1, 2,
488 2, 2, 2, 3,
489 3, 3, 3, 3,
490 3, 3, 3, 4,
491 4, 4, 4, 4,
492 4, 4, 4, 4,
493 4, 4, 4, 4,
494 4, 4, 4, 5
495};
496
Damien George438c88d2014-02-22 19:25:23 +0000497void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000498 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000499 z->fixed_dig = 0;
500 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000501 z->len = 0;
502 z->dig = NULL;
503}
504
505void mpz_init_from_int(mpz_t *z, machine_int_t val) {
506 mpz_init_zero(z);
507 mpz_set_from_int(z, val);
508}
509
Damien George06201ff2014-03-01 19:50:50 +0000510void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, uint alloc, machine_int_t val) {
511 z->neg = 0;
512 z->fixed_dig = 1;
513 z->alloc = alloc;
514 z->len = 0;
515 z->dig = dig;
516 mpz_set_from_int(z, val);
517}
518
Damien George438c88d2014-02-22 19:25:23 +0000519void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000520 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000521 m_del(mpz_dig_t, z->dig, z->alloc);
522 }
523}
524
525mpz_t *mpz_zero(void) {
526 mpz_t *z = m_new_obj(mpz_t);
527 mpz_init_zero(z);
528 return z;
529}
530
531mpz_t *mpz_from_int(machine_int_t val) {
532 mpz_t *z = mpz_zero();
533 mpz_set_from_int(z, val);
534 return z;
535}
536
Damien Georgebb4a43f2014-03-12 15:36:06 +0000537mpz_t *mpz_from_ll(long long val) {
538 mpz_t *z = mpz_zero();
539 mpz_set_from_ll(z, val);
540 return z;
541}
542
Damien George438c88d2014-02-22 19:25:23 +0000543mpz_t *mpz_from_str(const char *str, uint len, bool neg, uint base) {
544 mpz_t *z = mpz_zero();
545 mpz_set_from_str(z, str, len, neg, base);
546 return z;
547}
548
549void mpz_free(mpz_t *z) {
550 if (z != NULL) {
551 m_del(mpz_dig_t, z->dig, z->alloc);
552 m_del_obj(mpz_t, z);
553 }
554}
555
556STATIC void mpz_need_dig(mpz_t *z, uint need) {
Damien George438c88d2014-02-22 19:25:23 +0000557 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000558 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000559 }
560
Damien George06201ff2014-03-01 19:50:50 +0000561 if (z->dig == NULL || z->alloc < need) {
562 if (z->fixed_dig) {
563 // cannot reallocate fixed buffers
564 assert(0);
565 return;
566 }
567 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
568 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000569 }
570}
571
572mpz_t *mpz_clone(const mpz_t *src) {
573 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000574 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000575 z->fixed_dig = 0;
576 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000577 z->len = src->len;
578 if (src->dig == NULL) {
579 z->dig = NULL;
580 } else {
581 z->dig = m_new(mpz_dig_t, z->alloc);
582 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
583 }
584 return z;
585}
586
Damien George06201ff2014-03-01 19:50:50 +0000587/* sets dest = src
588 can have dest, src the same
589*/
Damien George438c88d2014-02-22 19:25:23 +0000590void mpz_set(mpz_t *dest, const mpz_t *src) {
591 mpz_need_dig(dest, src->len);
592 dest->neg = src->neg;
593 dest->len = src->len;
594 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
595}
596
597void mpz_set_from_int(mpz_t *z, machine_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000598 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000599
Damien Georgebb4a43f2014-03-12 15:36:06 +0000600 machine_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000601 if (val < 0) {
602 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000603 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000604 } else {
605 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000606 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000607 }
608
609 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000610 while (uval > 0) {
611 z->dig[z->len++] = uval & DIG_MASK;
612 uval >>= DIG_SIZE;
613 }
614}
615
616void mpz_set_from_ll(mpz_t *z, long long val) {
617 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
618
619 unsigned long long uval;
620 if (val < 0) {
621 z->neg = 1;
622 uval = -val;
623 } else {
624 z->neg = 0;
625 uval = val;
626 }
627
628 z->len = 0;
629 while (uval > 0) {
630 z->dig[z->len++] = uval & DIG_MASK;
631 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000632 }
633}
634
635// returns number of bytes from str that were processed
636uint mpz_set_from_str(mpz_t *z, const char *str, uint len, bool neg, uint base) {
637 assert(base < 36);
638
639 const char *cur = str;
640 const char *top = str + len;
641
642 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
643
644 if (neg) {
645 z->neg = 1;
646 } else {
647 z->neg = 0;
648 }
649
650 z->len = 0;
651 for (; cur < top; ++cur) { // XXX UTF8 next char
652 //uint v = char_to_numeric(cur#); // XXX UTF8 get char
653 uint v = *cur;
654 if ('0' <= v && v <= '9') {
655 v -= '0';
656 } else if ('A' <= v && v <= 'Z') {
657 v -= 'A' - 10;
658 } else if ('a' <= v && v <= 'z') {
659 v -= 'a' - 10;
660 } else {
661 break;
662 }
663 if (v >= base) {
664 break;
665 }
666 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
667 }
668
669 return cur - str;
670}
671
672bool mpz_is_zero(const mpz_t *z) {
673 return z->len == 0;
674}
675
676bool mpz_is_pos(const mpz_t *z) {
677 return z->len > 0 && z->neg == 0;
678}
679
680bool mpz_is_neg(const mpz_t *z) {
681 return z->len > 0 && z->neg != 0;
682}
683
684bool mpz_is_odd(const mpz_t *z) {
685 return z->len > 0 && (z->dig[0] & 1) != 0;
686}
687
688bool mpz_is_even(const mpz_t *z) {
689 return z->len == 0 || (z->dig[0] & 1) == 0;
690}
691
692int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
693 int cmp = z2->neg - z1->neg;
694 if (cmp != 0) {
695 return cmp;
696 }
697 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
698 if (z1->neg != 0) {
699 cmp = -cmp;
700 }
701 return cmp;
702}
703
Damien George06201ff2014-03-01 19:50:50 +0000704#if 0
705// obsolete
706// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeaca14122014-02-24 21:32:52 +0000707int mpz_cmp_sml_int(const mpz_t *z, machine_int_t sml_int) {
Damien George438c88d2014-02-22 19:25:23 +0000708 int cmp;
709 if (z->neg == 0) {
710 if (sml_int < 0) return 1;
711 if (sml_int == 0) {
712 if (z->len == 0) return 0;
713 return 1;
714 }
715 if (z->len == 0) return -1;
716 assert(sml_int < (1 << DIG_SIZE));
717 if (z->len != 1) return 1;
718 cmp = z->dig[0] - sml_int;
719 } else {
720 if (sml_int > 0) return -1;
721 if (sml_int == 0) {
722 if (z->len == 0) return 0;
723 return -1;
724 }
725 if (z->len == 0) return 1;
726 assert(sml_int > -(1 << DIG_SIZE));
727 if (z->len != 1) return -1;
728 cmp = -z->dig[0] - sml_int;
729 }
730 if (cmp < 0) return -1;
731 if (cmp > 0) return 1;
732 return 0;
733}
Damien George06201ff2014-03-01 19:50:50 +0000734#endif
Damien George438c88d2014-02-22 19:25:23 +0000735
Damien George438c88d2014-02-22 19:25:23 +0000736#if 0
737these functions are unused
738
739/* returns abs(z)
740*/
741mpz_t *mpz_abs(const mpz_t *z) {
742 mpz_t *z2 = mpz_clone(z);
743 z2->neg = 0;
744 return z2;
745}
746
747/* returns -z
748*/
749mpz_t *mpz_neg(const mpz_t *z) {
750 mpz_t *z2 = mpz_clone(z);
751 z2->neg = 1 - z2->neg;
752 return z2;
753}
754
755/* returns lhs + rhs
756 can have lhs, rhs the same
757*/
758mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
759 mpz_t *z = mpz_zero();
760 mpz_add_inpl(z, lhs, rhs);
761 return z;
762}
763
764/* returns lhs - rhs
765 can have lhs, rhs the same
766*/
767mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
768 mpz_t *z = mpz_zero();
769 mpz_sub_inpl(z, lhs, rhs);
770 return z;
771}
772
773/* returns lhs * rhs
774 can have lhs, rhs the same
775*/
776mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
777 mpz_t *z = mpz_zero();
778 mpz_mul_inpl(z, lhs, rhs);
779 return z;
780}
781
782/* returns lhs ** rhs
783 can have lhs, rhs the same
784*/
785mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
786 mpz_t *z = mpz_zero();
787 mpz_pow_inpl(z, lhs, rhs);
788 return z;
789}
790#endif
791
792/* computes dest = abs(z)
793 can have dest, z the same
794*/
795void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
796 if (dest != z) {
797 mpz_set(dest, z);
798 }
799 dest->neg = 0;
800}
801
802/* computes dest = -z
803 can have dest, z the same
804*/
805void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
806 if (dest != z) {
807 mpz_set(dest, z);
808 }
809 dest->neg = 1 - dest->neg;
810}
811
Damien George06201ff2014-03-01 19:50:50 +0000812/* computes dest = ~z (= -z - 1)
813 can have dest, z the same
814*/
815void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
816 if (dest != z) {
817 mpz_set(dest, z);
818 }
819 if (dest->neg) {
820 dest->neg = 0;
821 mpz_dig_t k = 1;
822 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
823 } else {
824 mpz_dig_t k = 1;
825 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
826 dest->neg = 1;
827 }
828}
829
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000830/* computes dest = lhs << rhs
831 can have dest, lhs the same
832*/
833void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000834 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000835 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000836 } else if (rhs < 0) {
837 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000838 } else {
Damien George06201ff2014-03-01 19:50:50 +0000839 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
840 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
841 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000842 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000843}
844
845/* computes dest = lhs >> rhs
846 can have dest, lhs the same
847*/
848void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000849 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000850 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000851 } else if (rhs < 0) {
852 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000853 } else {
Damien George06201ff2014-03-01 19:50:50 +0000854 mpz_need_dig(dest, lhs->len);
855 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
856 dest->neg = lhs->neg;
857 if (dest->neg) {
858 // arithmetic shift right, rounding to negative infinity
859 uint n_whole = rhs / DIG_SIZE;
860 uint n_part = rhs % DIG_SIZE;
861 mpz_dig_t round_up = 0;
862 for (uint i = 0; i < lhs->len && i < n_whole; i++) {
863 if (lhs->dig[i] != 0) {
864 round_up = 1;
865 break;
866 }
867 }
868 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
869 round_up = 1;
870 }
871 if (round_up) {
872 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
873 }
874 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000875 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000876}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000877
Damien George438c88d2014-02-22 19:25:23 +0000878/* computes dest = lhs + rhs
879 can have dest, lhs, rhs the same
880*/
881void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
882 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
883 const mpz_t *temp = lhs;
884 lhs = rhs;
885 rhs = temp;
886 }
887
888 if (lhs->neg == rhs->neg) {
889 mpz_need_dig(dest, lhs->len + 1);
890 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
891 } else {
892 mpz_need_dig(dest, lhs->len);
893 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
894 }
895
896 dest->neg = lhs->neg;
897}
898
899/* computes dest = lhs - rhs
900 can have dest, lhs, rhs the same
901*/
902void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
903 bool neg = false;
904
905 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
906 const mpz_t *temp = lhs;
907 lhs = rhs;
908 rhs = temp;
909 neg = true;
910 }
911
912 if (lhs->neg != rhs->neg) {
913 mpz_need_dig(dest, lhs->len + 1);
914 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
915 } else {
916 mpz_need_dig(dest, lhs->len);
917 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
918 }
919
920 if (neg) {
921 dest->neg = 1 - lhs->neg;
922 } else {
923 dest->neg = lhs->neg;
924 }
925}
926
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200927/* computes dest = lhs & rhs
928 can have dest, lhs, rhs the same
929*/
930void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200931 if (lhs->neg == rhs->neg) {
Damien Georgef55cf102014-05-29 15:01:49 +0100932 if (lhs->neg == 0) {
933 // make sure lhs has the most digits
934 if (lhs->len < rhs->len) {
935 const mpz_t *temp = lhs;
936 lhs = rhs;
937 rhs = temp;
938 }
939 // do the and'ing
940 mpz_need_dig(dest, rhs->len);
941 dest->len = mpn_and(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
942 dest->neg = 0;
943 } else {
944 // TODO both args are negative
945 assert(0);
946 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200947 } else {
Damien Georgef55cf102014-05-29 15:01:49 +0100948 // args have different sign
949 // make sure lhs is the positive arg
950 if (rhs->neg == 0) {
951 const mpz_t *temp = lhs;
952 lhs = rhs;
953 rhs = temp;
954 }
955 mpz_need_dig(dest, lhs->len + 1);
956 dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
957 assert(dest->len <= dest->alloc);
958 dest->neg = 0;
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200959 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200960}
961
962/* computes dest = lhs | rhs
963 can have dest, lhs, rhs the same
964*/
965void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
966 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
967 const mpz_t *temp = lhs;
968 lhs = rhs;
969 rhs = temp;
970 }
971
972 if (lhs->neg == rhs->neg) {
973 mpz_need_dig(dest, lhs->len);
974 dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
975 } else {
976 mpz_need_dig(dest, lhs->len);
977 // TODO
978 assert(0);
979// dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
980 }
981
982 dest->neg = lhs->neg;
983}
984
985/* computes dest = lhs ^ rhs
986 can have dest, lhs, rhs the same
987*/
988void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
989 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
990 const mpz_t *temp = lhs;
991 lhs = rhs;
992 rhs = temp;
993 }
994
995 if (lhs->neg == rhs->neg) {
996 mpz_need_dig(dest, lhs->len);
997 dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
998 } else {
999 mpz_need_dig(dest, lhs->len);
1000 // TODO
1001 assert(0);
1002// dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1003 }
1004
1005 dest->neg = 0;
1006}
1007
Damien George438c88d2014-02-22 19:25:23 +00001008/* computes dest = lhs * rhs
1009 can have dest, lhs, rhs the same
1010*/
1011void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
1012{
1013 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001014 mpz_set_from_int(dest, 0);
1015 return;
Damien George438c88d2014-02-22 19:25:23 +00001016 }
1017
1018 mpz_t *temp = NULL;
1019 if (lhs == dest) {
1020 lhs = temp = mpz_clone(lhs);
1021 if (rhs == dest) {
1022 rhs = lhs;
1023 }
1024 } else if (rhs == dest) {
1025 rhs = temp = mpz_clone(rhs);
1026 }
1027
1028 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
1029 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
1030 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1031
1032 if (lhs->neg == rhs->neg) {
1033 dest->neg = 0;
1034 } else {
1035 dest->neg = 1;
1036 }
1037
1038 mpz_free(temp);
1039}
1040
1041/* computes dest = lhs ** rhs
1042 can have dest, lhs, rhs the same
1043*/
1044void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1045 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001046 mpz_set_from_int(dest, 0);
1047 return;
Damien George438c88d2014-02-22 19:25:23 +00001048 }
1049
1050 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001051 mpz_set_from_int(dest, 1);
1052 return;
Damien George438c88d2014-02-22 19:25:23 +00001053 }
1054
1055 mpz_t *x = mpz_clone(lhs);
1056 mpz_t *n = mpz_clone(rhs);
1057
1058 mpz_set_from_int(dest, 1);
1059
1060 while (n->len > 0) {
1061 if (mpz_is_odd(n)) {
1062 mpz_mul_inpl(dest, dest, x);
1063 }
Damien George438c88d2014-02-22 19:25:23 +00001064 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
Damien George5bf565e2014-04-04 00:16:32 +01001065 if (n->len == 0) {
1066 break;
1067 }
1068 mpz_mul_inpl(x, x, x);
Damien George438c88d2014-02-22 19:25:23 +00001069 }
1070
1071 mpz_free(x);
1072 mpz_free(n);
1073}
1074
1075/* computes gcd(z1, z2)
1076 based on Knuth's modified gcd algorithm (I think?)
1077 gcd(z1, z2) >= 0
1078 gcd(0, 0) = 0
1079 gcd(z, 0) = abs(z)
1080*/
1081mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
1082 if (z1->len == 0) {
1083 mpz_t *a = mpz_clone(z2);
1084 a->neg = 0;
1085 return a;
1086 } else if (z2->len == 0) {
1087 mpz_t *a = mpz_clone(z1);
1088 a->neg = 0;
1089 return a;
1090 }
1091
1092 mpz_t *a = mpz_clone(z1);
1093 mpz_t *b = mpz_clone(z2);
1094 mpz_t c; mpz_init_zero(&c);
1095 a->neg = 0;
1096 b->neg = 0;
1097
1098 for (;;) {
1099 if (mpz_cmp(a, b) < 0) {
1100 if (a->len == 0) {
1101 mpz_free(a);
1102 mpz_deinit(&c);
1103 return b;
1104 }
1105 mpz_t *t = a; a = b; b = t;
1106 }
1107 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
1108 break;
1109 }
1110 mpz_set(&c, b);
1111 do {
1112 mpz_add_inpl(&c, &c, &c);
1113 } while (mpz_cmp(&c, a) <= 0);
1114 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
1115 mpz_sub_inpl(a, a, &c);
1116 }
1117
1118 mpz_deinit(&c);
1119
1120 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
1121 mpz_free(a);
1122 return b;
1123 } else {
1124 mpz_free(b);
1125 return a;
1126 }
1127}
1128
1129/* computes lcm(z1, z2)
1130 = abs(z1) / gcd(z1, z2) * abs(z2)
1131 lcm(z1, z1) >= 0
1132 lcm(0, 0) = 0
1133 lcm(z, 0) = 0
1134*/
1135mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2)
1136{
stijn01d6be42014-05-05 12:18:27 +02001137 // braces below are required for compilation to succeed with CL, see bug report
1138 // https://connect.microsoft.com/VisualStudio/feedback/details/864169/compilation-error-when-braces-are-left-out-of-single-line-if-statement
1139 if (z1->len == 0 || z2->len == 0) {
1140 return mpz_zero();
1141 }
Damien George438c88d2014-02-22 19:25:23 +00001142
1143 mpz_t *gcd = mpz_gcd(z1, z2);
1144 mpz_t *quo = mpz_zero();
1145 mpz_t *rem = mpz_zero();
1146 mpz_divmod_inpl(quo, rem, z1, gcd);
1147 mpz_mul_inpl(rem, quo, z2);
1148 mpz_free(gcd);
1149 mpz_free(quo);
1150 rem->neg = 0;
1151 return rem;
1152}
1153
1154/* computes new integers in quo and rem such that:
1155 quo * rhs + rem = lhs
1156 0 <= rem < rhs
1157 can have lhs, rhs the same
1158*/
1159void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
1160 *quo = mpz_zero();
1161 *rem = mpz_zero();
1162 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
1163}
1164
1165/* computes new integers in quo and rem such that:
1166 quo * rhs + rem = lhs
1167 0 <= rem < rhs
1168 can have lhs, rhs the same
1169*/
1170void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
1171 if (rhs->len == 0) {
1172 mpz_set_from_int(dest_quo, 0);
1173 mpz_set_from_int(dest_rem, 0);
1174 return;
1175 }
1176
1177 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
1178 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
1179 dest_quo->len = 0;
1180 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
1181 mpz_set(dest_rem, lhs);
1182 //rhs->dig[rhs->len] = 0;
1183 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
1184
1185 if (lhs->neg != rhs->neg) {
1186 dest_quo->neg = 1;
1187 }
1188}
1189
1190#if 0
1191these functions are unused
1192
1193/* computes floor(lhs / rhs)
1194 can have lhs, rhs the same
1195*/
1196mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
1197 mpz_t *quo = mpz_zero();
1198 mpz_t rem; mpz_init_zero(&rem);
1199 mpz_divmod_inpl(quo, &rem, lhs, rhs);
1200 mpz_deinit(&rem);
1201 return quo;
1202}
1203
1204/* computes lhs % rhs ( >= 0)
1205 can have lhs, rhs the same
1206*/
1207mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1208 mpz_t quo; mpz_init_zero(&quo);
1209 mpz_t *rem = mpz_zero();
1210 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1211 mpz_deinit(&quo);
1212 return rem;
1213}
1214#endif
1215
Damien George8270e382014-04-03 11:00:54 +00001216// TODO check that this correctly handles overflow in all cases
Damien Georgeaca14122014-02-24 21:32:52 +00001217machine_int_t mpz_as_int(const mpz_t *i) {
1218 machine_int_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001219 mpz_dig_t *d = i->dig + i->len;
1220
Damien George06201ff2014-03-01 19:50:50 +00001221 while (--d >= i->dig) {
Damien Georgeaca14122014-02-24 21:32:52 +00001222 machine_int_t oldval = val;
Damien George438c88d2014-02-22 19:25:23 +00001223 val = (val << DIG_SIZE) | *d;
Damien George06201ff2014-03-01 19:50:50 +00001224 if (val < oldval) {
Damien George8270e382014-04-03 11:00:54 +00001225 // overflow, return +/- "infinity"
Damien George438c88d2014-02-22 19:25:23 +00001226 if (i->neg == 0) {
Damien George8270e382014-04-03 11:00:54 +00001227 // +infinity
1228 return ~WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001229 } else {
Damien George8270e382014-04-03 11:00:54 +00001230 // -infinity
1231 return WORD_MSBIT_HIGH;
Damien George438c88d2014-02-22 19:25:23 +00001232 }
1233 }
1234 }
1235
1236 if (i->neg != 0) {
1237 val = -val;
1238 }
1239
1240 return val;
1241}
1242
Damien George8270e382014-04-03 11:00:54 +00001243// TODO check that this correctly handles overflow in all cases
1244bool mpz_as_int_checked(const mpz_t *i, machine_int_t *value) {
1245 machine_int_t val = 0;
1246 mpz_dig_t *d = i->dig + i->len;
1247
1248 while (--d >= i->dig) {
1249 machine_int_t oldval = val;
1250 val = (val << DIG_SIZE) | *d;
1251 if (val < oldval) {
1252 // overflow
1253 return false;
1254 }
1255 }
1256
1257 if (i->neg != 0) {
1258 val = -val;
1259 }
1260
1261 *value = val;
1262 return true;
1263}
1264
Damien George52608102014-03-08 15:04:54 +00001265#if MICROPY_ENABLE_FLOAT
1266mp_float_t mpz_as_float(const mpz_t *i) {
1267 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001268 mpz_dig_t *d = i->dig + i->len;
1269
1270 while (--d >= i->dig) {
1271 val = val * (1 << DIG_SIZE) + *d;
1272 }
1273
1274 if (i->neg != 0) {
1275 val = -val;
1276 }
1277
1278 return val;
1279}
Damien George52608102014-03-08 15:04:54 +00001280#endif
Damien George438c88d2014-02-22 19:25:23 +00001281
1282uint mpz_as_str_size(const mpz_t *i, uint base) {
1283 if (base < 2 || base > 32) {
1284 return 0;
1285 }
1286
1287 return i->len * DIG_SIZE / log_base2_floor[base] + 2 + 1; // +1 for null byte termination
1288}
1289
Dave Hylandsc4029e52014-04-07 11:19:51 -07001290uint mpz_as_str_size_formatted(const mpz_t *i, uint base, const char *prefix, char comma) {
1291 if (base < 2 || base > 32) {
1292 return 0;
1293 }
1294
1295 uint num_digits = i->len * DIG_SIZE / log_base2_floor[base] + 1;
1296 uint num_commas = comma ? num_digits / 3: 0;
1297 uint prefix_len = prefix ? strlen(prefix) : 0;
1298
1299 return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte
1300}
1301
Damien George438c88d2014-02-22 19:25:23 +00001302char *mpz_as_str(const mpz_t *i, uint base) {
1303 char *s = m_new(char, mpz_as_str_size(i, base));
Dave Hylandsc4029e52014-04-07 11:19:51 -07001304 mpz_as_str_inpl(i, base, "", 'a', 0, s);
Damien George438c88d2014-02-22 19:25:23 +00001305 return s;
1306}
1307
1308// assumes enough space as calculated by mpz_as_str_size
1309// returns length of string, not including null byte
Dave Hylandsc4029e52014-04-07 11:19:51 -07001310uint 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 +00001311 if (str == NULL || base < 2 || base > 32) {
1312 str[0] = 0;
1313 return 0;
1314 }
1315
1316 uint ilen = i->len;
1317
Dave Hylandsc4029e52014-04-07 11:19:51 -07001318 char *s = str;
Damien George438c88d2014-02-22 19:25:23 +00001319 if (ilen == 0) {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001320 if (prefix) {
1321 while (*prefix)
1322 *s++ = *prefix++;
1323 }
1324 *s++ = '0';
1325 *s = '\0';
1326 return s - str;
Damien George438c88d2014-02-22 19:25:23 +00001327 }
1328
Damien Georgeeec91052014-04-08 23:11:00 +01001329 // make a copy of mpz digits, so we can do the div/mod calculation
Damien George438c88d2014-02-22 19:25:23 +00001330 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1331 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1332
1333 // convert
Dave Hylandsc4029e52014-04-07 11:19:51 -07001334 char *last_comma = str;
Damien George438c88d2014-02-22 19:25:23 +00001335 bool done;
1336 do {
1337 mpz_dig_t *d = dig + ilen;
1338 mpz_dbl_dig_t a = 0;
1339
1340 // compute next remainder
1341 while (--d >= dig) {
1342 a = (a << DIG_SIZE) | *d;
1343 *d = a / base;
1344 a %= base;
1345 }
1346
1347 // convert to character
1348 a += '0';
1349 if (a > '9') {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001350 a += base_char - '9' - 1;
Damien George438c88d2014-02-22 19:25:23 +00001351 }
1352 *s++ = a;
1353
1354 // check if number is zero
1355 done = true;
1356 for (d = dig; d < dig + ilen; ++d) {
1357 if (*d != 0) {
1358 done = false;
1359 break;
1360 }
1361 }
Dave Hylandsc4029e52014-04-07 11:19:51 -07001362 if (comma && (s - last_comma) == 3) {
1363 *s++ = comma;
1364 last_comma = s;
1365 }
1366 }
1367 while (!done);
Damien George438c88d2014-02-22 19:25:23 +00001368
Damien Georgeeec91052014-04-08 23:11:00 +01001369 // free the copy of the digits array
1370 m_del(mpz_dig_t, dig, ilen);
1371
Dave Hylandsc4029e52014-04-07 11:19:51 -07001372 if (prefix) {
1373 const char *p = &prefix[strlen(prefix)];
1374 while (p > prefix) {
1375 *s++ = *--p;
1376 }
1377 }
Damien George438c88d2014-02-22 19:25:23 +00001378 if (i->neg != 0) {
1379 *s++ = '-';
1380 }
1381
1382 // reverse string
1383 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1384 char temp = *u;
1385 *u = *v;
1386 *v = temp;
1387 }
1388
Dave Hylandsc4029e52014-04-07 11:19:51 -07001389 *s = '\0'; // null termination
Damien George438c88d2014-02-22 19:25:23 +00001390
1391 return s - str;
1392}
1393
1394#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ