blob: b5cc7d1838baba2ca73f8ab94e4ee34b74eb9a90 [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
Damien George438c88d2014-02-22 19:25:23 +000033#include "mpconfig.h"
Paul Sokolovsky59c675a2014-06-21 22:43:22 +030034#include "misc.h"
Damien George438c88d2014-02-22 19:25:23 +000035#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 George9a21d2e2014-09-06 17:15:34 +010040#define DIG_MASK ((1L << DIG_SIZE) - 1)
41#define DIG_MSB (1L << (DIG_SIZE - 1))
42#define DIG_BASE (1L << DIG_SIZE)
Damien George438c88d2014-02-22 19:25:23 +000043
44/*
Damien George06201ff2014-03-01 19:50:50 +000045 mpz is an arbitrary precision integer type with a public API.
46
47 mpn functions act on non-negative integers represented by an array of generalised
48 digits (eg a word per digit). You also need to specify separately the length of the
49 array. There is no public API for mpn. Rather, the functions are used by mpz to
50 implement its features.
51
52 Integer values are stored little endian (first digit is first in memory).
53
54 Definition of normalise: ?
Damien George438c88d2014-02-22 19:25:23 +000055*/
56
57/* compares i with j
58 returns sign(i - j)
59 assumes i, j are normalised
60*/
Damien George42f3de92014-10-03 17:44:14 +000061STATIC int mpn_cmp(const mpz_dig_t *idig, mp_uint_t ilen, const mpz_dig_t *jdig, mp_uint_t jlen) {
Damien George438c88d2014-02-22 19:25:23 +000062 if (ilen < jlen) { return -1; }
63 if (ilen > jlen) { return 1; }
64
65 for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
Damien George9a21d2e2014-09-06 17:15:34 +010066 mpz_dbl_dig_signed_t cmp = (mpz_dbl_dig_t)*(--idig) - (mpz_dbl_dig_t)*(--jdig);
Damien George438c88d2014-02-22 19:25:23 +000067 if (cmp < 0) { return -1; }
68 if (cmp > 0) { return 1; }
69 }
70
71 return 0;
72}
73
Damien Georgec5ac2ac2014-02-26 16:56:30 +000074/* computes i = j << n
75 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +000076 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien Georgec5ac2ac2014-02-26 16:56:30 +000077 can have i, j pointing to same memory
78*/
Damien Georgeafb1cf72014-09-05 20:37:06 +010079STATIC mp_uint_t mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, mp_uint_t jlen, mp_uint_t n) {
80 mp_uint_t n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
81 mp_uint_t n_part = n % DIG_SIZE;
Damien Georgebb4a43f2014-03-12 15:36:06 +000082 if (n_part == 0) {
83 n_part = DIG_SIZE;
84 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +000085
Damien George06201ff2014-03-01 19:50:50 +000086 // start from the high end of the digit arrays
87 idig += jlen + n_whole - 1;
88 jdig += jlen - 1;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000089
Damien George06201ff2014-03-01 19:50:50 +000090 // shift the digits
91 mpz_dbl_dig_t d = 0;
Damien Georgeafb1cf72014-09-05 20:37:06 +010092 for (mp_uint_t i = jlen; i > 0; i--, idig--, jdig--) {
Damien George06201ff2014-03-01 19:50:50 +000093 d |= *jdig;
Damien George5d9b8162014-08-07 14:27:48 +000094 *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
Damien George06201ff2014-03-01 19:50:50 +000095 d <<= DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000096 }
97
Damien George06201ff2014-03-01 19:50:50 +000098 // store remaining bits
Damien George5d9b8162014-08-07 14:27:48 +000099 *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
Damien George06201ff2014-03-01 19:50:50 +0000100 idig -= n_whole - 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000101 memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t));
Damien George06201ff2014-03-01 19:50:50 +0000102
103 // work out length of result
104 jlen += n_whole;
105 if (idig[jlen - 1] == 0) {
106 jlen--;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000107 }
108
Damien George06201ff2014-03-01 19:50:50 +0000109 // return length of result
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000110 return jlen;
111}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000112
Damien George438c88d2014-02-22 19:25:23 +0000113/* computes i = j >> n
114 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +0000115 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien George438c88d2014-02-22 19:25:23 +0000116 can have i, j pointing to same memory
117*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100118STATIC mp_uint_t mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, mp_uint_t jlen, mp_uint_t n) {
119 mp_uint_t n_whole = n / DIG_SIZE;
120 mp_uint_t n_part = n % DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000121
122 if (n_whole >= jlen) {
123 return 0;
124 }
125
126 jdig += n_whole;
127 jlen -= n_whole;
128
Damien Georgeafb1cf72014-09-05 20:37:06 +0100129 for (mp_uint_t i = jlen; i > 0; i--, idig++, jdig++) {
Damien George438c88d2014-02-22 19:25:23 +0000130 mpz_dbl_dig_t d = *jdig;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000131 if (i > 1) {
Damien George9a21d2e2014-09-06 17:15:34 +0100132 d |= (mpz_dbl_dig_t)jdig[1] << DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000133 }
Damien George438c88d2014-02-22 19:25:23 +0000134 d >>= n_part;
135 *idig = d & DIG_MASK;
136 }
137
138 if (idig[-1] == 0) {
Damien George06201ff2014-03-01 19:50:50 +0000139 jlen--;
Damien George438c88d2014-02-22 19:25:23 +0000140 }
141
142 return jlen;
143}
144
145/* computes i = j + k
146 returns number of digits in i
147 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
148 can have i, j, k pointing to same memory
149*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100150STATIC mp_uint_t mpn_add(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Damien George438c88d2014-02-22 19:25:23 +0000151 mpz_dig_t *oidig = idig;
152 mpz_dbl_dig_t carry = 0;
153
154 jlen -= klen;
155
156 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100157 carry += (mpz_dbl_dig_t)*jdig + (mpz_dbl_dig_t)*kdig;
Damien George438c88d2014-02-22 19:25:23 +0000158 *idig = carry & DIG_MASK;
159 carry >>= DIG_SIZE;
160 }
161
162 for (; jlen > 0; --jlen, ++idig, ++jdig) {
163 carry += *jdig;
164 *idig = carry & DIG_MASK;
165 carry >>= DIG_SIZE;
166 }
167
168 if (carry != 0) {
169 *idig++ = carry;
170 }
171
172 return idig - oidig;
173}
174
175/* computes i = j - k
176 returns number of digits in i
177 assumes enough memory in i; assumes normalised j, k; assumes j >= k
178 can have i, j, k pointing to same memory
179*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100180STATIC mp_uint_t mpn_sub(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Damien George438c88d2014-02-22 19:25:23 +0000181 mpz_dig_t *oidig = idig;
182 mpz_dbl_dig_signed_t borrow = 0;
183
184 jlen -= klen;
185
186 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100187 borrow += (mpz_dbl_dig_t)*jdig - (mpz_dbl_dig_t)*kdig;
Damien George438c88d2014-02-22 19:25:23 +0000188 *idig = borrow & DIG_MASK;
189 borrow >>= DIG_SIZE;
190 }
191
Damien Georgeaca14122014-02-24 21:32:52 +0000192 for (; jlen > 0; --jlen, ++idig, ++jdig) {
Damien George438c88d2014-02-22 19:25:23 +0000193 borrow += *jdig;
194 *idig = borrow & DIG_MASK;
195 borrow >>= DIG_SIZE;
196 }
197
198 for (--idig; idig >= oidig && *idig == 0; --idig) {
199 }
200
201 return idig + 1 - oidig;
202}
203
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200204/* computes i = j & k
205 returns number of digits in i
206 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
207 can have i, j, k pointing to same memory
208*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100209STATIC mp_uint_t mpn_and(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200210 mpz_dig_t *oidig = idig;
211
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200212 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
213 *idig = *jdig & *kdig;
214 }
215
Damien George561e4252014-05-12 23:27:29 +0100216 // remove trailing zeros
Damien George51fab282014-05-13 22:58:00 +0100217 for (--idig; idig >= oidig && *idig == 0; --idig) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200218 }
219
Damien George51fab282014-05-13 22:58:00 +0100220 return idig + 1 - oidig;
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200221}
222
Damien Georgef55cf102014-05-29 15:01:49 +0100223/* computes i = j & -k = j & (~k + 1)
224 returns number of digits in i
225 assumes enough memory in i; assumes normalised j, k
226 can have i, j, k pointing to same memory
227*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100228STATIC mp_uint_t mpn_and_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Damien Georgef55cf102014-05-29 15:01:49 +0100229 mpz_dig_t *oidig = idig;
230 mpz_dbl_dig_t carry = 1;
231
232 for (; jlen > 0 && klen > 0; --jlen, --klen, ++idig, ++jdig, ++kdig) {
233 carry += *kdig ^ DIG_MASK;
234 *idig = (*jdig & carry) & DIG_MASK;
235 carry >>= DIG_SIZE;
236 }
237
238 for (; jlen > 0; --jlen, ++idig, ++jdig) {
239 carry += DIG_MASK;
240 *idig = (*jdig & carry) & DIG_MASK;
241 carry >>= DIG_SIZE;
242 }
243
244 if (carry != 0) {
245 *idig = carry;
246 } else {
247 // remove trailing zeros
248 for (--idig; idig >= oidig && *idig == 0; --idig) {
249 }
250 }
251
252 return idig + 1 - oidig;
253}
254
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200255/* computes i = j | k
256 returns number of digits in i
257 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
258 can have i, j, k pointing to same memory
259*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100260STATIC mp_uint_t mpn_or(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200261 mpz_dig_t *oidig = idig;
262
263 jlen -= klen;
264
265 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
266 *idig = *jdig | *kdig;
267 }
268
269 for (; jlen > 0; --jlen, ++idig, ++jdig) {
270 *idig = *jdig;
271 }
272
273 return idig - oidig;
274}
275
276/* computes i = j ^ k
277 returns number of digits in i
278 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
279 can have i, j, k pointing to same memory
280*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100281STATIC mp_uint_t mpn_xor(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jlen, const mpz_dig_t *kdig, mp_uint_t klen) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200282 mpz_dig_t *oidig = idig;
283
284 jlen -= klen;
285
286 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
287 *idig = *jdig ^ *kdig;
288 }
289
290 for (; jlen > 0; --jlen, ++idig, ++jdig) {
291 *idig = *jdig;
292 }
293
294 return idig - oidig;
295}
296
Damien George438c88d2014-02-22 19:25:23 +0000297/* computes i = i * d1 + d2
298 returns number of digits in i
299 assumes enough memory in i; assumes normalised i; assumes dmul != 0
300*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100301STATIC mp_uint_t mpn_mul_dig_add_dig(mpz_dig_t *idig, mp_uint_t ilen, mpz_dig_t dmul, mpz_dig_t dadd) {
Damien George438c88d2014-02-22 19:25:23 +0000302 mpz_dig_t *oidig = idig;
303 mpz_dbl_dig_t carry = dadd;
304
305 for (; ilen > 0; --ilen, ++idig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100306 carry += (mpz_dbl_dig_t)*idig * (mpz_dbl_dig_t)dmul; // will never overflow so long as DIG_SIZE <= 8*sizeof(mpz_dbl_dig_t)/2
Damien George438c88d2014-02-22 19:25:23 +0000307 *idig = carry & DIG_MASK;
308 carry >>= DIG_SIZE;
309 }
310
311 if (carry != 0) {
312 *idig++ = carry;
313 }
314
315 return idig - oidig;
316}
317
318/* computes i = j * k
319 returns number of digits in i
320 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
321 can have j, k point to same memory
322*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100323STATIC mp_uint_t mpn_mul(mpz_dig_t *idig, mpz_dig_t *jdig, mp_uint_t jlen, mpz_dig_t *kdig, mp_uint_t klen) {
Damien George438c88d2014-02-22 19:25:23 +0000324 mpz_dig_t *oidig = idig;
Damien Georgeafb1cf72014-09-05 20:37:06 +0100325 mp_uint_t ilen = 0;
Damien George438c88d2014-02-22 19:25:23 +0000326
327 for (; klen > 0; --klen, ++idig, ++kdig) {
328 mpz_dig_t *id = idig;
329 mpz_dbl_dig_t carry = 0;
330
Damien Georgeafb1cf72014-09-05 20:37:06 +0100331 mp_uint_t jl = jlen;
Damien George438c88d2014-02-22 19:25:23 +0000332 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
Damien George9a21d2e2014-09-06 17:15:34 +0100333 carry += (mpz_dbl_dig_t)*id + (mpz_dbl_dig_t)*jd * (mpz_dbl_dig_t)*kdig; // will never overflow so long as DIG_SIZE <= 8*sizeof(mpz_dbl_dig_t)/2
Damien George438c88d2014-02-22 19:25:23 +0000334 *id = carry & DIG_MASK;
335 carry >>= DIG_SIZE;
336 }
337
338 if (carry != 0) {
339 *id++ = carry;
340 }
341
342 ilen = id - oidig;
343 }
344
345 return ilen;
346}
347
348/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
349 assumes den != 0
350 assumes num_dig has enough memory to be extended by 1 digit
351 assumes quo_dig has enough memory (as many digits as num)
352 assumes quo_dig is filled with zeros
353 modifies den_dig memory, but restors it to original state at end
354*/
355
Damien George40f3c022014-07-03 13:25:24 +0100356STATIC void mpn_div(mpz_dig_t *num_dig, mp_uint_t *num_len, mpz_dig_t *den_dig, mp_uint_t den_len, mpz_dig_t *quo_dig, mp_uint_t *quo_len) {
Damien George438c88d2014-02-22 19:25:23 +0000357 mpz_dig_t *orig_num_dig = num_dig;
358 mpz_dig_t *orig_quo_dig = quo_dig;
359 mpz_dig_t norm_shift = 0;
360 mpz_dbl_dig_t lead_den_digit;
361
362 // handle simple cases
363 {
Damien George42f3de92014-10-03 17:44:14 +0000364 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
Damien George438c88d2014-02-22 19:25:23 +0000365 if (cmp == 0) {
366 *num_len = 0;
367 quo_dig[0] = 1;
368 *quo_len = 1;
369 return;
370 } else if (cmp < 0) {
371 // numerator remains the same
372 *quo_len = 0;
373 return;
374 }
375 }
376
377 // count number of leading zeros in leading digit of denominator
378 {
379 mpz_dig_t d = den_dig[den_len - 1];
Damien George9a21d2e2014-09-06 17:15:34 +0100380 while ((d & DIG_MSB) == 0) {
Damien George438c88d2014-02-22 19:25:23 +0000381 d <<= 1;
382 ++norm_shift;
383 }
384 }
385
386 // normalise denomenator (leading bit of leading digit is 1)
387 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
388 mpz_dig_t d = *den;
389 *den = ((d << norm_shift) | carry) & DIG_MASK;
390 carry = d >> (DIG_SIZE - norm_shift);
391 }
392
393 // now need to shift numerator by same amount as denominator
394 // first, increase length of numerator in case we need more room to shift
395 num_dig[*num_len] = 0;
396 ++(*num_len);
397 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
398 mpz_dig_t n = *num;
399 *num = ((n << norm_shift) | carry) & DIG_MASK;
400 carry = n >> (DIG_SIZE - norm_shift);
401 }
402
403 // cache the leading digit of the denominator
404 lead_den_digit = den_dig[den_len - 1];
405
406 // point num_dig to last digit in numerator
407 num_dig += *num_len - 1;
408
409 // calculate number of digits in quotient
410 *quo_len = *num_len - den_len;
411
412 // point to last digit to store for quotient
413 quo_dig += *quo_len - 1;
414
415 // keep going while we have enough digits to divide
416 while (*num_len > den_len) {
Damien George9a21d2e2014-09-06 17:15:34 +0100417 mpz_dbl_dig_t quo = ((mpz_dbl_dig_t)*num_dig << DIG_SIZE) | num_dig[-1];
Damien George438c88d2014-02-22 19:25:23 +0000418
419 // get approximate quotient
420 quo /= lead_den_digit;
421
Damien George9a21d2e2014-09-06 17:15:34 +0100422 // Multiply quo by den and subtract from num to get remainder.
423 // We have different code here to handle different compile-time
424 // configurations of mpz:
425 //
426 // 1. DIG_SIZE is stricly less than half the number of bits
427 // available in mpz_dbl_dig_t. In this case we can use a
428 // slightly more optimal (in time and space) routine that
429 // uses the extra bits in mpz_dbl_dig_signed_t to store a
430 // sign bit.
431 //
432 // 2. DIG_SIZE is exactly half the number of bits available in
433 // mpz_dbl_dig_t. In this (common) case we need to be careful
434 // not to overflow the borrow variable. And the shifting of
435 // borrow needs some special logic (it's a shift right with
436 // round up).
437
438 if (DIG_SIZE < 8 * sizeof(mpz_dbl_dig_t) / 2) {
Damien George438c88d2014-02-22 19:25:23 +0000439 mpz_dbl_dig_signed_t borrow = 0;
440
441 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
Damien George9a21d2e2014-09-06 17:15:34 +0100442 borrow += (mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)quo * (mpz_dbl_dig_t)*d; // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2
Damien George438c88d2014-02-22 19:25:23 +0000443 *n = borrow & DIG_MASK;
444 borrow >>= DIG_SIZE;
445 }
Damien George9a21d2e2014-09-06 17:15:34 +0100446 borrow += *num_dig; // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2
Damien George438c88d2014-02-22 19:25:23 +0000447 *num_dig = borrow & DIG_MASK;
448 borrow >>= DIG_SIZE;
449
450 // adjust quotient if it is too big
451 for (; borrow != 0; --quo) {
452 mpz_dbl_dig_t carry = 0;
453 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
Damien George9a21d2e2014-09-06 17:15:34 +0100454 carry += (mpz_dbl_dig_t)*n + (mpz_dbl_dig_t)*d;
Damien George438c88d2014-02-22 19:25:23 +0000455 *n = carry & DIG_MASK;
456 carry >>= DIG_SIZE;
457 }
458 carry += *num_dig;
459 *num_dig = carry & DIG_MASK;
460 carry >>= DIG_SIZE;
461
462 borrow += carry;
463 }
Damien George9a21d2e2014-09-06 17:15:34 +0100464 } else { // DIG_SIZE == 8 * sizeof(mpz_dbl_dig_t) / 2
465 mpz_dbl_dig_t borrow = 0;
466
467 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
468 mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (mpz_dbl_dig_t)(*d);
469 if (x >= *n || *n - x <= borrow) {
470 borrow += (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)*n;
471 *n = (-borrow) & DIG_MASK;
472 borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
473 } else {
474 *n = ((mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)borrow) & DIG_MASK;
475 borrow = 0;
476 }
477 }
478 if (borrow >= *num_dig) {
479 borrow -= (mpz_dbl_dig_t)*num_dig;
480 *num_dig = (-borrow) & DIG_MASK;
481 borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
482 } else {
483 *num_dig = (*num_dig - borrow) & DIG_MASK;
484 borrow = 0;
485 }
486
487 // adjust quotient if it is too big
488 for (; borrow != 0; --quo) {
489 mpz_dbl_dig_t carry = 0;
490 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
491 carry += (mpz_dbl_dig_t)*n + (mpz_dbl_dig_t)*d;
492 *n = carry & DIG_MASK;
493 carry >>= DIG_SIZE;
494 }
495 carry += (mpz_dbl_dig_t)*num_dig;
496 *num_dig = carry & DIG_MASK;
497 carry >>= DIG_SIZE;
498
499 //assert(borrow >= carry); // enable this to check the logic
500 borrow -= carry;
501 }
Damien George438c88d2014-02-22 19:25:23 +0000502 }
503
504 // store this digit of the quotient
505 *quo_dig = quo & DIG_MASK;
506 --quo_dig;
507
508 // move down to next digit of numerator
509 --num_dig;
510 --(*num_len);
511 }
512
513 // unnormalise denomenator
514 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
515 mpz_dig_t d = *den;
516 *den = ((d >> norm_shift) | carry) & DIG_MASK;
517 carry = d << (DIG_SIZE - norm_shift);
518 }
519
520 // unnormalise numerator (remainder now)
521 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
522 mpz_dig_t n = *num;
523 *num = ((n >> norm_shift) | carry) & DIG_MASK;
524 carry = n << (DIG_SIZE - norm_shift);
525 }
526
527 // strip trailing zeros
528
529 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
530 --(*quo_len);
531 }
532
533 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
534 --(*num_len);
535 }
536}
537
Damien George06201ff2014-03-01 19:50:50 +0000538#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000539
Damien George1c70cbf2014-08-30 00:38:16 +0100540STATIC const uint8_t log_base2_floor[] = {
Damien George438c88d2014-02-22 19:25:23 +0000541 0,
542 0, 1, 1, 2,
543 2, 2, 2, 3,
544 3, 3, 3, 3,
545 3, 3, 3, 4,
546 4, 4, 4, 4,
547 4, 4, 4, 4,
548 4, 4, 4, 4,
549 4, 4, 4, 5
550};
551
Damien George438c88d2014-02-22 19:25:23 +0000552void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000553 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000554 z->fixed_dig = 0;
555 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000556 z->len = 0;
557 z->dig = NULL;
558}
559
Damien George40f3c022014-07-03 13:25:24 +0100560void mpz_init_from_int(mpz_t *z, mp_int_t val) {
Damien George438c88d2014-02-22 19:25:23 +0000561 mpz_init_zero(z);
562 mpz_set_from_int(z, val);
563}
564
Damien Georgeafb1cf72014-09-05 20:37:06 +0100565void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, mp_uint_t alloc, mp_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000566 z->neg = 0;
567 z->fixed_dig = 1;
568 z->alloc = alloc;
569 z->len = 0;
570 z->dig = dig;
571 mpz_set_from_int(z, val);
572}
573
Damien George438c88d2014-02-22 19:25:23 +0000574void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000575 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000576 m_del(mpz_dig_t, z->dig, z->alloc);
577 }
578}
579
580mpz_t *mpz_zero(void) {
581 mpz_t *z = m_new_obj(mpz_t);
582 mpz_init_zero(z);
583 return z;
584}
585
Damien George40f3c022014-07-03 13:25:24 +0100586mpz_t *mpz_from_int(mp_int_t val) {
Damien George438c88d2014-02-22 19:25:23 +0000587 mpz_t *z = mpz_zero();
588 mpz_set_from_int(z, val);
589 return z;
590}
591
Damien George95307432014-09-10 22:10:33 +0100592mpz_t *mpz_from_ll(long long val, bool is_signed) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000593 mpz_t *z = mpz_zero();
Damien George95307432014-09-10 22:10:33 +0100594 mpz_set_from_ll(z, val, is_signed);
Damien Georgebb4a43f2014-03-12 15:36:06 +0000595 return z;
596}
597
Damien Georgeafb1cf72014-09-05 20:37:06 +0100598mpz_t *mpz_from_str(const char *str, mp_uint_t len, bool neg, mp_uint_t base) {
Damien George438c88d2014-02-22 19:25:23 +0000599 mpz_t *z = mpz_zero();
600 mpz_set_from_str(z, str, len, neg, base);
601 return z;
602}
603
604void mpz_free(mpz_t *z) {
605 if (z != NULL) {
606 m_del(mpz_dig_t, z->dig, z->alloc);
607 m_del_obj(mpz_t, z);
608 }
609}
610
Damien Georgeafb1cf72014-09-05 20:37:06 +0100611STATIC void mpz_need_dig(mpz_t *z, mp_uint_t need) {
Damien George438c88d2014-02-22 19:25:23 +0000612 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000613 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000614 }
615
Damien George06201ff2014-03-01 19:50:50 +0000616 if (z->dig == NULL || z->alloc < need) {
617 if (z->fixed_dig) {
618 // cannot reallocate fixed buffers
619 assert(0);
620 return;
621 }
622 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
623 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000624 }
625}
626
627mpz_t *mpz_clone(const mpz_t *src) {
628 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000629 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000630 z->fixed_dig = 0;
631 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000632 z->len = src->len;
633 if (src->dig == NULL) {
634 z->dig = NULL;
635 } else {
636 z->dig = m_new(mpz_dig_t, z->alloc);
637 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
638 }
639 return z;
640}
641
Damien George06201ff2014-03-01 19:50:50 +0000642/* sets dest = src
643 can have dest, src the same
644*/
Damien George438c88d2014-02-22 19:25:23 +0000645void mpz_set(mpz_t *dest, const mpz_t *src) {
646 mpz_need_dig(dest, src->len);
647 dest->neg = src->neg;
648 dest->len = src->len;
649 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
650}
651
Damien George40f3c022014-07-03 13:25:24 +0100652void mpz_set_from_int(mpz_t *z, mp_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000653 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000654
Damien George40f3c022014-07-03 13:25:24 +0100655 mp_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000656 if (val < 0) {
657 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000658 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000659 } else {
660 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000661 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000662 }
663
664 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000665 while (uval > 0) {
666 z->dig[z->len++] = uval & DIG_MASK;
667 uval >>= DIG_SIZE;
668 }
669}
670
Damien George95307432014-09-10 22:10:33 +0100671void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000672 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
673
674 unsigned long long uval;
Damien George95307432014-09-10 22:10:33 +0100675 if (is_signed && val < 0) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000676 z->neg = 1;
677 uval = -val;
678 } else {
679 z->neg = 0;
680 uval = val;
681 }
682
683 z->len = 0;
684 while (uval > 0) {
685 z->dig[z->len++] = uval & DIG_MASK;
686 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000687 }
688}
689
690// returns number of bytes from str that were processed
Damien Georgeafb1cf72014-09-05 20:37:06 +0100691mp_uint_t mpz_set_from_str(mpz_t *z, const char *str, mp_uint_t len, bool neg, mp_uint_t base) {
Damien George438c88d2014-02-22 19:25:23 +0000692 assert(base < 36);
693
694 const char *cur = str;
695 const char *top = str + len;
696
697 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
698
699 if (neg) {
700 z->neg = 1;
701 } else {
702 z->neg = 0;
703 }
704
705 z->len = 0;
706 for (; cur < top; ++cur) { // XXX UTF8 next char
Damien Georgeafb1cf72014-09-05 20:37:06 +0100707 //mp_uint_t v = char_to_numeric(cur#); // XXX UTF8 get char
708 mp_uint_t v = *cur;
Damien George438c88d2014-02-22 19:25:23 +0000709 if ('0' <= v && v <= '9') {
710 v -= '0';
711 } else if ('A' <= v && v <= 'Z') {
712 v -= 'A' - 10;
713 } else if ('a' <= v && v <= 'z') {
714 v -= 'a' - 10;
715 } else {
716 break;
717 }
718 if (v >= base) {
719 break;
720 }
721 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
722 }
723
724 return cur - str;
725}
726
727bool mpz_is_zero(const mpz_t *z) {
728 return z->len == 0;
729}
730
731bool mpz_is_pos(const mpz_t *z) {
732 return z->len > 0 && z->neg == 0;
733}
734
735bool mpz_is_neg(const mpz_t *z) {
736 return z->len > 0 && z->neg != 0;
737}
738
739bool mpz_is_odd(const mpz_t *z) {
740 return z->len > 0 && (z->dig[0] & 1) != 0;
741}
742
743bool mpz_is_even(const mpz_t *z) {
744 return z->len == 0 || (z->dig[0] & 1) == 0;
745}
746
Damien George42f3de92014-10-03 17:44:14 +0000747int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
748 int cmp = (int)z2->neg - (int)z1->neg;
Damien George438c88d2014-02-22 19:25:23 +0000749 if (cmp != 0) {
750 return cmp;
751 }
752 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
753 if (z1->neg != 0) {
754 cmp = -cmp;
755 }
756 return cmp;
757}
758
Damien George06201ff2014-03-01 19:50:50 +0000759#if 0
760// obsolete
761// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeafb1cf72014-09-05 20:37:06 +0100762mp_int_t mpz_cmp_sml_int(const mpz_t *z, mp_int_t sml_int) {
763 mp_int_t cmp;
Damien George438c88d2014-02-22 19:25:23 +0000764 if (z->neg == 0) {
765 if (sml_int < 0) return 1;
766 if (sml_int == 0) {
767 if (z->len == 0) return 0;
768 return 1;
769 }
770 if (z->len == 0) return -1;
771 assert(sml_int < (1 << DIG_SIZE));
772 if (z->len != 1) return 1;
773 cmp = z->dig[0] - sml_int;
774 } else {
775 if (sml_int > 0) return -1;
776 if (sml_int == 0) {
777 if (z->len == 0) return 0;
778 return -1;
779 }
780 if (z->len == 0) return 1;
781 assert(sml_int > -(1 << DIG_SIZE));
782 if (z->len != 1) return -1;
783 cmp = -z->dig[0] - sml_int;
784 }
785 if (cmp < 0) return -1;
786 if (cmp > 0) return 1;
787 return 0;
788}
Damien George06201ff2014-03-01 19:50:50 +0000789#endif
Damien George438c88d2014-02-22 19:25:23 +0000790
Damien George438c88d2014-02-22 19:25:23 +0000791#if 0
792these functions are unused
793
794/* returns abs(z)
795*/
796mpz_t *mpz_abs(const mpz_t *z) {
797 mpz_t *z2 = mpz_clone(z);
798 z2->neg = 0;
799 return z2;
800}
801
802/* returns -z
803*/
804mpz_t *mpz_neg(const mpz_t *z) {
805 mpz_t *z2 = mpz_clone(z);
806 z2->neg = 1 - z2->neg;
807 return z2;
808}
809
810/* returns lhs + rhs
811 can have lhs, rhs the same
812*/
813mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
814 mpz_t *z = mpz_zero();
815 mpz_add_inpl(z, lhs, rhs);
816 return z;
817}
818
819/* returns lhs - rhs
820 can have lhs, rhs the same
821*/
822mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
823 mpz_t *z = mpz_zero();
824 mpz_sub_inpl(z, lhs, rhs);
825 return z;
826}
827
828/* returns lhs * rhs
829 can have lhs, rhs the same
830*/
831mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
832 mpz_t *z = mpz_zero();
833 mpz_mul_inpl(z, lhs, rhs);
834 return z;
835}
836
837/* returns lhs ** rhs
838 can have lhs, rhs the same
839*/
840mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
841 mpz_t *z = mpz_zero();
842 mpz_pow_inpl(z, lhs, rhs);
843 return z;
844}
845#endif
846
847/* computes dest = abs(z)
848 can have dest, z the same
849*/
850void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
851 if (dest != z) {
852 mpz_set(dest, z);
853 }
854 dest->neg = 0;
855}
856
857/* computes dest = -z
858 can have dest, z the same
859*/
860void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
861 if (dest != z) {
862 mpz_set(dest, z);
863 }
864 dest->neg = 1 - dest->neg;
865}
866
Damien George06201ff2014-03-01 19:50:50 +0000867/* computes dest = ~z (= -z - 1)
868 can have dest, z the same
869*/
870void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
871 if (dest != z) {
872 mpz_set(dest, z);
873 }
874 if (dest->neg) {
875 dest->neg = 0;
876 mpz_dig_t k = 1;
877 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
878 } else {
879 mpz_dig_t k = 1;
880 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
881 dest->neg = 1;
882 }
883}
884
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000885/* computes dest = lhs << rhs
886 can have dest, lhs the same
887*/
Damien George40f3c022014-07-03 13:25:24 +0100888void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, mp_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000889 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000890 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000891 } else if (rhs < 0) {
892 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000893 } else {
Damien George06201ff2014-03-01 19:50:50 +0000894 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
895 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
896 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000897 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000898}
899
900/* computes dest = lhs >> rhs
901 can have dest, lhs the same
902*/
Damien George40f3c022014-07-03 13:25:24 +0100903void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, mp_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000904 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000905 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000906 } else if (rhs < 0) {
907 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000908 } else {
Damien George06201ff2014-03-01 19:50:50 +0000909 mpz_need_dig(dest, lhs->len);
910 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
911 dest->neg = lhs->neg;
912 if (dest->neg) {
913 // arithmetic shift right, rounding to negative infinity
Damien Georgeafb1cf72014-09-05 20:37:06 +0100914 mp_uint_t n_whole = rhs / DIG_SIZE;
915 mp_uint_t n_part = rhs % DIG_SIZE;
Damien George06201ff2014-03-01 19:50:50 +0000916 mpz_dig_t round_up = 0;
Damien Georgeafb1cf72014-09-05 20:37:06 +0100917 for (mp_uint_t i = 0; i < lhs->len && i < n_whole; i++) {
Damien George06201ff2014-03-01 19:50:50 +0000918 if (lhs->dig[i] != 0) {
919 round_up = 1;
920 break;
921 }
922 }
923 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
924 round_up = 1;
925 }
926 if (round_up) {
927 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
928 }
929 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000930 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000931}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000932
Damien George438c88d2014-02-22 19:25:23 +0000933/* computes dest = lhs + rhs
934 can have dest, lhs, rhs the same
935*/
936void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
937 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
938 const mpz_t *temp = lhs;
939 lhs = rhs;
940 rhs = temp;
941 }
942
943 if (lhs->neg == rhs->neg) {
944 mpz_need_dig(dest, lhs->len + 1);
945 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
946 } else {
947 mpz_need_dig(dest, lhs->len);
948 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
949 }
950
951 dest->neg = lhs->neg;
952}
953
954/* computes dest = lhs - rhs
955 can have dest, lhs, rhs the same
956*/
957void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
958 bool neg = false;
959
960 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
961 const mpz_t *temp = lhs;
962 lhs = rhs;
963 rhs = temp;
964 neg = true;
965 }
966
967 if (lhs->neg != rhs->neg) {
968 mpz_need_dig(dest, lhs->len + 1);
969 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
970 } else {
971 mpz_need_dig(dest, lhs->len);
972 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
973 }
974
975 if (neg) {
976 dest->neg = 1 - lhs->neg;
977 } else {
978 dest->neg = lhs->neg;
979 }
980}
981
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200982/* computes dest = lhs & rhs
983 can have dest, lhs, rhs the same
984*/
985void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200986 if (lhs->neg == rhs->neg) {
Damien Georgef55cf102014-05-29 15:01:49 +0100987 if (lhs->neg == 0) {
988 // make sure lhs has the most digits
989 if (lhs->len < rhs->len) {
990 const mpz_t *temp = lhs;
991 lhs = rhs;
992 rhs = temp;
993 }
994 // do the and'ing
995 mpz_need_dig(dest, rhs->len);
996 dest->len = mpn_and(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
997 dest->neg = 0;
998 } else {
999 // TODO both args are negative
1000 assert(0);
1001 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001002 } else {
Damien Georgef55cf102014-05-29 15:01:49 +01001003 // args have different sign
1004 // make sure lhs is the positive arg
1005 if (rhs->neg == 0) {
1006 const mpz_t *temp = lhs;
1007 lhs = rhs;
1008 rhs = temp;
1009 }
1010 mpz_need_dig(dest, lhs->len + 1);
1011 dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1012 assert(dest->len <= dest->alloc);
1013 dest->neg = 0;
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001014 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001015}
1016
1017/* computes dest = lhs | rhs
1018 can have dest, lhs, rhs the same
1019*/
1020void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1021 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
1022 const mpz_t *temp = lhs;
1023 lhs = rhs;
1024 rhs = temp;
1025 }
1026
1027 if (lhs->neg == rhs->neg) {
1028 mpz_need_dig(dest, lhs->len);
1029 dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1030 } else {
1031 mpz_need_dig(dest, lhs->len);
1032 // TODO
1033 assert(0);
1034// dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1035 }
1036
1037 dest->neg = lhs->neg;
1038}
1039
1040/* computes dest = lhs ^ rhs
1041 can have dest, lhs, rhs the same
1042*/
1043void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1044 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
1045 const mpz_t *temp = lhs;
1046 lhs = rhs;
1047 rhs = temp;
1048 }
1049
1050 if (lhs->neg == rhs->neg) {
1051 mpz_need_dig(dest, lhs->len);
1052 dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1053 } else {
1054 mpz_need_dig(dest, lhs->len);
1055 // TODO
1056 assert(0);
1057// dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1058 }
1059
1060 dest->neg = 0;
1061}
1062
Damien George438c88d2014-02-22 19:25:23 +00001063/* computes dest = lhs * rhs
1064 can have dest, lhs, rhs the same
1065*/
1066void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
1067{
1068 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001069 mpz_set_from_int(dest, 0);
1070 return;
Damien George438c88d2014-02-22 19:25:23 +00001071 }
1072
1073 mpz_t *temp = NULL;
1074 if (lhs == dest) {
1075 lhs = temp = mpz_clone(lhs);
1076 if (rhs == dest) {
1077 rhs = lhs;
1078 }
1079 } else if (rhs == dest) {
1080 rhs = temp = mpz_clone(rhs);
1081 }
1082
1083 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
1084 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
1085 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1086
1087 if (lhs->neg == rhs->neg) {
1088 dest->neg = 0;
1089 } else {
1090 dest->neg = 1;
1091 }
1092
1093 mpz_free(temp);
1094}
1095
1096/* computes dest = lhs ** rhs
1097 can have dest, lhs, rhs the same
1098*/
1099void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1100 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001101 mpz_set_from_int(dest, 0);
1102 return;
Damien George438c88d2014-02-22 19:25:23 +00001103 }
1104
1105 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001106 mpz_set_from_int(dest, 1);
1107 return;
Damien George438c88d2014-02-22 19:25:23 +00001108 }
1109
1110 mpz_t *x = mpz_clone(lhs);
1111 mpz_t *n = mpz_clone(rhs);
1112
1113 mpz_set_from_int(dest, 1);
1114
1115 while (n->len > 0) {
1116 if (mpz_is_odd(n)) {
1117 mpz_mul_inpl(dest, dest, x);
1118 }
Damien George438c88d2014-02-22 19:25:23 +00001119 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
Damien George5bf565e2014-04-04 00:16:32 +01001120 if (n->len == 0) {
1121 break;
1122 }
1123 mpz_mul_inpl(x, x, x);
Damien George438c88d2014-02-22 19:25:23 +00001124 }
1125
1126 mpz_free(x);
1127 mpz_free(n);
1128}
1129
1130/* computes gcd(z1, z2)
1131 based on Knuth's modified gcd algorithm (I think?)
1132 gcd(z1, z2) >= 0
1133 gcd(0, 0) = 0
1134 gcd(z, 0) = abs(z)
1135*/
1136mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
1137 if (z1->len == 0) {
1138 mpz_t *a = mpz_clone(z2);
1139 a->neg = 0;
1140 return a;
1141 } else if (z2->len == 0) {
1142 mpz_t *a = mpz_clone(z1);
1143 a->neg = 0;
1144 return a;
1145 }
1146
1147 mpz_t *a = mpz_clone(z1);
1148 mpz_t *b = mpz_clone(z2);
1149 mpz_t c; mpz_init_zero(&c);
1150 a->neg = 0;
1151 b->neg = 0;
1152
1153 for (;;) {
1154 if (mpz_cmp(a, b) < 0) {
1155 if (a->len == 0) {
1156 mpz_free(a);
1157 mpz_deinit(&c);
1158 return b;
1159 }
1160 mpz_t *t = a; a = b; b = t;
1161 }
1162 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
1163 break;
1164 }
1165 mpz_set(&c, b);
1166 do {
1167 mpz_add_inpl(&c, &c, &c);
1168 } while (mpz_cmp(&c, a) <= 0);
1169 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
1170 mpz_sub_inpl(a, a, &c);
1171 }
1172
1173 mpz_deinit(&c);
1174
1175 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
1176 mpz_free(a);
1177 return b;
1178 } else {
1179 mpz_free(b);
1180 return a;
1181 }
1182}
1183
1184/* computes lcm(z1, z2)
1185 = abs(z1) / gcd(z1, z2) * abs(z2)
1186 lcm(z1, z1) >= 0
1187 lcm(0, 0) = 0
1188 lcm(z, 0) = 0
1189*/
Damien George5d9b8162014-08-07 14:27:48 +00001190mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2) {
1191 if (z1->len == 0 || z2->len == 0) {
1192 return mpz_zero();
stijn01d6be42014-05-05 12:18:27 +02001193 }
Damien George438c88d2014-02-22 19:25:23 +00001194
1195 mpz_t *gcd = mpz_gcd(z1, z2);
1196 mpz_t *quo = mpz_zero();
1197 mpz_t *rem = mpz_zero();
1198 mpz_divmod_inpl(quo, rem, z1, gcd);
1199 mpz_mul_inpl(rem, quo, z2);
1200 mpz_free(gcd);
1201 mpz_free(quo);
1202 rem->neg = 0;
1203 return rem;
1204}
1205
1206/* computes new integers in quo and rem such that:
1207 quo * rhs + rem = lhs
1208 0 <= rem < rhs
1209 can have lhs, rhs the same
1210*/
1211void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
1212 *quo = mpz_zero();
1213 *rem = mpz_zero();
1214 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
1215}
1216
1217/* computes new integers in quo and rem such that:
1218 quo * rhs + rem = lhs
1219 0 <= rem < rhs
1220 can have lhs, rhs the same
1221*/
1222void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
1223 if (rhs->len == 0) {
1224 mpz_set_from_int(dest_quo, 0);
1225 mpz_set_from_int(dest_rem, 0);
1226 return;
1227 }
1228
1229 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
1230 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
1231 dest_quo->len = 0;
1232 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
1233 mpz_set(dest_rem, lhs);
1234 //rhs->dig[rhs->len] = 0;
1235 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
1236
1237 if (lhs->neg != rhs->neg) {
1238 dest_quo->neg = 1;
1239 }
1240}
1241
1242#if 0
1243these functions are unused
1244
1245/* computes floor(lhs / rhs)
1246 can have lhs, rhs the same
1247*/
1248mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
1249 mpz_t *quo = mpz_zero();
1250 mpz_t rem; mpz_init_zero(&rem);
1251 mpz_divmod_inpl(quo, &rem, lhs, rhs);
1252 mpz_deinit(&rem);
1253 return quo;
1254}
1255
1256/* computes lhs % rhs ( >= 0)
1257 can have lhs, rhs the same
1258*/
1259mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1260 mpz_t quo; mpz_init_zero(&quo);
1261 mpz_t *rem = mpz_zero();
1262 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1263 mpz_deinit(&quo);
1264 return rem;
1265}
1266#endif
1267
Damien Georgeffe911d2014-07-24 14:21:37 +01001268// must return actual int value if it fits in mp_int_t
1269mp_int_t mpz_hash(const mpz_t *z) {
1270 mp_int_t val = 0;
1271 mpz_dig_t *d = z->dig + z->len;
1272
1273 while (--d >= z->dig) {
1274 val = (val << DIG_SIZE) | *d;
1275 }
1276
1277 if (z->neg != 0) {
1278 val = -val;
1279 }
1280
1281 return val;
1282}
1283
Damien George40f3c022014-07-03 13:25:24 +01001284bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) {
1285 mp_int_t val = 0;
Damien George8270e382014-04-03 11:00:54 +00001286 mpz_dig_t *d = i->dig + i->len;
1287
1288 while (--d >= i->dig) {
Damien Georgec9aa58e2014-07-31 13:41:43 +00001289 if (val > (~(WORD_MSBIT_HIGH) >> DIG_SIZE)) {
1290 // will overflow
Damien George8270e382014-04-03 11:00:54 +00001291 return false;
1292 }
Damien Georgec9aa58e2014-07-31 13:41:43 +00001293 val = (val << DIG_SIZE) | *d;
Damien George8270e382014-04-03 11:00:54 +00001294 }
1295
1296 if (i->neg != 0) {
1297 val = -val;
1298 }
1299
1300 *value = val;
1301 return true;
1302}
1303
Damien Georgec9aa58e2014-07-31 13:41:43 +00001304bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) {
1305 if (i->neg != 0) {
1306 // can't represent signed values
1307 return false;
1308 }
1309
1310 mp_uint_t val = 0;
1311 mpz_dig_t *d = i->dig + i->len;
1312
1313 while (--d >= i->dig) {
Damien George9a21d2e2014-09-06 17:15:34 +01001314 if (val > (~(WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) {
Damien Georgec9aa58e2014-07-31 13:41:43 +00001315 // will overflow
1316 return false;
1317 }
1318 val = (val << DIG_SIZE) | *d;
1319 }
1320
1321 *value = val;
1322 return true;
1323}
1324
Damien Georgefb510b32014-06-01 13:32:54 +01001325#if MICROPY_PY_BUILTINS_FLOAT
Damien George52608102014-03-08 15:04:54 +00001326mp_float_t mpz_as_float(const mpz_t *i) {
1327 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001328 mpz_dig_t *d = i->dig + i->len;
1329
1330 while (--d >= i->dig) {
Damien George9a21d2e2014-09-06 17:15:34 +01001331 val = val * DIG_BASE + *d;
Damien George438c88d2014-02-22 19:25:23 +00001332 }
1333
1334 if (i->neg != 0) {
1335 val = -val;
1336 }
1337
1338 return val;
1339}
Damien George52608102014-03-08 15:04:54 +00001340#endif
Damien George438c88d2014-02-22 19:25:23 +00001341
Damien Georgeafb1cf72014-09-05 20:37:06 +01001342mp_uint_t mpz_as_str_size(const mpz_t *i, mp_uint_t base, const char *prefix, char comma) {
Damien George438c88d2014-02-22 19:25:23 +00001343 if (base < 2 || base > 32) {
1344 return 0;
1345 }
1346
Damien Georgeafb1cf72014-09-05 20:37:06 +01001347 mp_uint_t num_digits = i->len * DIG_SIZE / log_base2_floor[base] + 1;
1348 mp_uint_t num_commas = comma ? num_digits / 3: 0;
1349 mp_uint_t prefix_len = prefix ? strlen(prefix) : 0;
Dave Hylandsc4029e52014-04-07 11:19:51 -07001350
1351 return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte
1352}
1353
Damien Georgeafb1cf72014-09-05 20:37:06 +01001354#if 0
1355this function is unused
1356char *mpz_as_str(const mpz_t *i, mp_uint_t base) {
1357 char *s = m_new(char, mpz_as_str_size(i, base, NULL, '\0'));
1358 mpz_as_str_inpl(i, base, NULL, 'a', '\0', s);
Damien George438c88d2014-02-22 19:25:23 +00001359 return s;
1360}
Damien Georgeafb1cf72014-09-05 20:37:06 +01001361#endif
Damien George438c88d2014-02-22 19:25:23 +00001362
1363// assumes enough space as calculated by mpz_as_str_size
1364// returns length of string, not including null byte
Damien Georgeafb1cf72014-09-05 20:37:06 +01001365mp_uint_t mpz_as_str_inpl(const mpz_t *i, mp_uint_t base, const char *prefix, char base_char, char comma, char *str) {
Damien George438c88d2014-02-22 19:25:23 +00001366 if (str == NULL || base < 2 || base > 32) {
1367 str[0] = 0;
1368 return 0;
1369 }
1370
Damien Georgeafb1cf72014-09-05 20:37:06 +01001371 mp_uint_t ilen = i->len;
Damien George438c88d2014-02-22 19:25:23 +00001372
Dave Hylandsc4029e52014-04-07 11:19:51 -07001373 char *s = str;
Damien George438c88d2014-02-22 19:25:23 +00001374 if (ilen == 0) {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001375 if (prefix) {
1376 while (*prefix)
1377 *s++ = *prefix++;
1378 }
1379 *s++ = '0';
1380 *s = '\0';
1381 return s - str;
Damien George438c88d2014-02-22 19:25:23 +00001382 }
1383
Damien Georgeeec91052014-04-08 23:11:00 +01001384 // make a copy of mpz digits, so we can do the div/mod calculation
Damien George438c88d2014-02-22 19:25:23 +00001385 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1386 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1387
1388 // convert
Dave Hylandsc4029e52014-04-07 11:19:51 -07001389 char *last_comma = str;
Damien George438c88d2014-02-22 19:25:23 +00001390 bool done;
1391 do {
1392 mpz_dig_t *d = dig + ilen;
1393 mpz_dbl_dig_t a = 0;
1394
1395 // compute next remainder
1396 while (--d >= dig) {
1397 a = (a << DIG_SIZE) | *d;
1398 *d = a / base;
1399 a %= base;
1400 }
1401
1402 // convert to character
1403 a += '0';
1404 if (a > '9') {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001405 a += base_char - '9' - 1;
Damien George438c88d2014-02-22 19:25:23 +00001406 }
1407 *s++ = a;
1408
1409 // check if number is zero
1410 done = true;
1411 for (d = dig; d < dig + ilen; ++d) {
1412 if (*d != 0) {
1413 done = false;
1414 break;
1415 }
1416 }
Dave Hylandsc4029e52014-04-07 11:19:51 -07001417 if (comma && (s - last_comma) == 3) {
1418 *s++ = comma;
1419 last_comma = s;
1420 }
1421 }
1422 while (!done);
Damien George438c88d2014-02-22 19:25:23 +00001423
Damien Georgeeec91052014-04-08 23:11:00 +01001424 // free the copy of the digits array
1425 m_del(mpz_dig_t, dig, ilen);
1426
Dave Hylandsc4029e52014-04-07 11:19:51 -07001427 if (prefix) {
1428 const char *p = &prefix[strlen(prefix)];
1429 while (p > prefix) {
1430 *s++ = *--p;
1431 }
1432 }
Damien George438c88d2014-02-22 19:25:23 +00001433 if (i->neg != 0) {
1434 *s++ = '-';
1435 }
1436
1437 // reverse string
1438 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1439 char temp = *u;
1440 *u = *v;
1441 *v = temp;
1442 }
1443
Dave Hylandsc4029e52014-04-07 11:19:51 -07001444 *s = '\0'; // null termination
Damien George438c88d2014-02-22 19:25:23 +00001445
1446 return s - str;
1447}
1448
1449#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ