blob: a25ba9e7d208bb7c7a3964c0639193da1650d8ed [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 <string.h>
28#include <assert.h>
29
Damien George51dfcb42015-01-01 20:27:54 +000030#include "py/mpz.h"
Damien George438c88d2014-02-22 19:25:23 +000031
32#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
33
Damien George06201ff2014-03-01 19:50:50 +000034#define DIG_SIZE (MPZ_DIG_SIZE)
stijn0e557fa2014-10-30 14:39:22 +010035#define DIG_MASK ((MPZ_LONG_1 << DIG_SIZE) - 1)
36#define DIG_MSB (MPZ_LONG_1 << (DIG_SIZE - 1))
37#define DIG_BASE (MPZ_LONG_1 << DIG_SIZE)
Damien George438c88d2014-02-22 19:25:23 +000038
39/*
Damien George06201ff2014-03-01 19:50:50 +000040 mpz is an arbitrary precision integer type with a public API.
41
42 mpn functions act on non-negative integers represented by an array of generalised
43 digits (eg a word per digit). You also need to specify separately the length of the
44 array. There is no public API for mpn. Rather, the functions are used by mpz to
45 implement its features.
46
47 Integer values are stored little endian (first digit is first in memory).
48
49 Definition of normalise: ?
Damien George438c88d2014-02-22 19:25:23 +000050*/
51
52/* compares i with j
53 returns sign(i - j)
54 assumes i, j are normalised
55*/
Damien George42f3de92014-10-03 17:44:14 +000056STATIC 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 +000057 if (ilen < jlen) { return -1; }
58 if (ilen > jlen) { return 1; }
59
60 for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
Damien George9a21d2e2014-09-06 17:15:34 +010061 mpz_dbl_dig_signed_t cmp = (mpz_dbl_dig_t)*(--idig) - (mpz_dbl_dig_t)*(--jdig);
Damien George438c88d2014-02-22 19:25:23 +000062 if (cmp < 0) { return -1; }
63 if (cmp > 0) { return 1; }
64 }
65
66 return 0;
67}
68
Damien Georgec5ac2ac2014-02-26 16:56:30 +000069/* computes i = j << n
70 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +000071 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien Georgec5ac2ac2014-02-26 16:56:30 +000072 can have i, j pointing to same memory
73*/
Damien Georgeafb1cf72014-09-05 20:37:06 +010074STATIC mp_uint_t mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, mp_uint_t jlen, mp_uint_t n) {
75 mp_uint_t n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
76 mp_uint_t n_part = n % DIG_SIZE;
Damien Georgebb4a43f2014-03-12 15:36:06 +000077 if (n_part == 0) {
78 n_part = DIG_SIZE;
79 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +000080
Damien George06201ff2014-03-01 19:50:50 +000081 // start from the high end of the digit arrays
82 idig += jlen + n_whole - 1;
83 jdig += jlen - 1;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000084
Damien George06201ff2014-03-01 19:50:50 +000085 // shift the digits
86 mpz_dbl_dig_t d = 0;
Damien Georgeafb1cf72014-09-05 20:37:06 +010087 for (mp_uint_t i = jlen; i > 0; i--, idig--, jdig--) {
Damien George06201ff2014-03-01 19:50:50 +000088 d |= *jdig;
Damien George5d9b8162014-08-07 14:27:48 +000089 *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
Damien George06201ff2014-03-01 19:50:50 +000090 d <<= DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +000091 }
92
Damien George06201ff2014-03-01 19:50:50 +000093 // store remaining bits
Damien George5d9b8162014-08-07 14:27:48 +000094 *idig = (d >> (DIG_SIZE - n_part)) & DIG_MASK;
Damien George06201ff2014-03-01 19:50:50 +000095 idig -= n_whole - 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +000096 memset(idig, 0, (n_whole - 1) * sizeof(mpz_dig_t));
Damien George06201ff2014-03-01 19:50:50 +000097
98 // work out length of result
99 jlen += n_whole;
100 if (idig[jlen - 1] == 0) {
101 jlen--;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000102 }
103
Damien George06201ff2014-03-01 19:50:50 +0000104 // return length of result
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000105 return jlen;
106}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000107
Damien George438c88d2014-02-22 19:25:23 +0000108/* computes i = j >> n
109 returns number of digits in i
Damien George06201ff2014-03-01 19:50:50 +0000110 assumes enough memory in i; assumes normalised j; assumes n > 0
Damien George438c88d2014-02-22 19:25:23 +0000111 can have i, j pointing to same memory
112*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100113STATIC mp_uint_t mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, mp_uint_t jlen, mp_uint_t n) {
114 mp_uint_t n_whole = n / DIG_SIZE;
115 mp_uint_t n_part = n % DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000116
117 if (n_whole >= jlen) {
118 return 0;
119 }
120
121 jdig += n_whole;
122 jlen -= n_whole;
123
Damien Georgeafb1cf72014-09-05 20:37:06 +0100124 for (mp_uint_t i = jlen; i > 0; i--, idig++, jdig++) {
Damien George438c88d2014-02-22 19:25:23 +0000125 mpz_dbl_dig_t d = *jdig;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000126 if (i > 1) {
Damien George9a21d2e2014-09-06 17:15:34 +0100127 d |= (mpz_dbl_dig_t)jdig[1] << DIG_SIZE;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000128 }
Damien George438c88d2014-02-22 19:25:23 +0000129 d >>= n_part;
130 *idig = d & DIG_MASK;
131 }
132
133 if (idig[-1] == 0) {
Damien George06201ff2014-03-01 19:50:50 +0000134 jlen--;
Damien George438c88d2014-02-22 19:25:23 +0000135 }
136
137 return jlen;
138}
139
140/* computes i = j + k
141 returns number of digits in i
142 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
143 can have i, j, k pointing to same memory
144*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100145STATIC 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 +0000146 mpz_dig_t *oidig = idig;
147 mpz_dbl_dig_t carry = 0;
148
149 jlen -= klen;
150
151 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100152 carry += (mpz_dbl_dig_t)*jdig + (mpz_dbl_dig_t)*kdig;
Damien George438c88d2014-02-22 19:25:23 +0000153 *idig = carry & DIG_MASK;
154 carry >>= DIG_SIZE;
155 }
156
157 for (; jlen > 0; --jlen, ++idig, ++jdig) {
158 carry += *jdig;
159 *idig = carry & DIG_MASK;
160 carry >>= DIG_SIZE;
161 }
162
163 if (carry != 0) {
164 *idig++ = carry;
165 }
166
167 return idig - oidig;
168}
169
170/* computes i = j - k
171 returns number of digits in i
172 assumes enough memory in i; assumes normalised j, k; assumes j >= k
173 can have i, j, k pointing to same memory
174*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100175STATIC 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 +0000176 mpz_dig_t *oidig = idig;
177 mpz_dbl_dig_signed_t borrow = 0;
178
179 jlen -= klen;
180
181 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100182 borrow += (mpz_dbl_dig_t)*jdig - (mpz_dbl_dig_t)*kdig;
Damien George438c88d2014-02-22 19:25:23 +0000183 *idig = borrow & DIG_MASK;
184 borrow >>= DIG_SIZE;
185 }
186
Damien Georgeaca14122014-02-24 21:32:52 +0000187 for (; jlen > 0; --jlen, ++idig, ++jdig) {
Damien George438c88d2014-02-22 19:25:23 +0000188 borrow += *jdig;
189 *idig = borrow & DIG_MASK;
190 borrow >>= DIG_SIZE;
191 }
192
193 for (--idig; idig >= oidig && *idig == 0; --idig) {
194 }
195
196 return idig + 1 - oidig;
197}
198
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200199/* computes i = j & k
200 returns number of digits in i
201 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
202 can have i, j, k pointing to same memory
203*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100204STATIC 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 +0200205 mpz_dig_t *oidig = idig;
206
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200207 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
208 *idig = *jdig & *kdig;
209 }
210
Damien George561e4252014-05-12 23:27:29 +0100211 // remove trailing zeros
Damien George51fab282014-05-13 22:58:00 +0100212 for (--idig; idig >= oidig && *idig == 0; --idig) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200213 }
214
Damien George51fab282014-05-13 22:58:00 +0100215 return idig + 1 - oidig;
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200216}
217
Damien Georgef55cf102014-05-29 15:01:49 +0100218/* computes i = j & -k = j & (~k + 1)
219 returns number of digits in i
220 assumes enough memory in i; assumes normalised j, k
221 can have i, j, k pointing to same memory
222*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100223STATIC 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 +0100224 mpz_dig_t *oidig = idig;
225 mpz_dbl_dig_t carry = 1;
226
227 for (; jlen > 0 && klen > 0; --jlen, --klen, ++idig, ++jdig, ++kdig) {
228 carry += *kdig ^ DIG_MASK;
229 *idig = (*jdig & carry) & DIG_MASK;
230 carry >>= DIG_SIZE;
231 }
232
233 for (; jlen > 0; --jlen, ++idig, ++jdig) {
234 carry += DIG_MASK;
235 *idig = (*jdig & carry) & DIG_MASK;
236 carry >>= DIG_SIZE;
237 }
238
239 if (carry != 0) {
240 *idig = carry;
241 } else {
242 // remove trailing zeros
243 for (--idig; idig >= oidig && *idig == 0; --idig) {
244 }
245 }
246
247 return idig + 1 - oidig;
248}
249
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200250/* computes i = j | k
251 returns number of digits in i
252 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
253 can have i, j, k pointing to same memory
254*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100255STATIC 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 +0200256 mpz_dig_t *oidig = idig;
257
258 jlen -= klen;
259
260 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
261 *idig = *jdig | *kdig;
262 }
263
264 for (; jlen > 0; --jlen, ++idig, ++jdig) {
265 *idig = *jdig;
266 }
267
268 return idig - oidig;
269}
270
271/* computes i = j ^ k
272 returns number of digits in i
273 assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
274 can have i, j, k pointing to same memory
275*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100276STATIC 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 +0200277 mpz_dig_t *oidig = idig;
278
279 jlen -= klen;
280
281 for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
282 *idig = *jdig ^ *kdig;
283 }
284
285 for (; jlen > 0; --jlen, ++idig, ++jdig) {
286 *idig = *jdig;
287 }
288
289 return idig - oidig;
290}
291
Damien George438c88d2014-02-22 19:25:23 +0000292/* computes i = i * d1 + d2
293 returns number of digits in i
294 assumes enough memory in i; assumes normalised i; assumes dmul != 0
295*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100296STATIC 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 +0000297 mpz_dig_t *oidig = idig;
298 mpz_dbl_dig_t carry = dadd;
299
300 for (; ilen > 0; --ilen, ++idig) {
Damien George9a21d2e2014-09-06 17:15:34 +0100301 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 +0000302 *idig = carry & DIG_MASK;
303 carry >>= DIG_SIZE;
304 }
305
306 if (carry != 0) {
307 *idig++ = carry;
308 }
309
310 return idig - oidig;
311}
312
313/* computes i = j * k
314 returns number of digits in i
315 assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
316 can have j, k point to same memory
317*/
Damien Georgeafb1cf72014-09-05 20:37:06 +0100318STATIC 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 +0000319 mpz_dig_t *oidig = idig;
Damien Georgeafb1cf72014-09-05 20:37:06 +0100320 mp_uint_t ilen = 0;
Damien George438c88d2014-02-22 19:25:23 +0000321
322 for (; klen > 0; --klen, ++idig, ++kdig) {
323 mpz_dig_t *id = idig;
324 mpz_dbl_dig_t carry = 0;
325
Damien Georgeafb1cf72014-09-05 20:37:06 +0100326 mp_uint_t jl = jlen;
Damien George438c88d2014-02-22 19:25:23 +0000327 for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
Damien George9a21d2e2014-09-06 17:15:34 +0100328 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 +0000329 *id = carry & DIG_MASK;
330 carry >>= DIG_SIZE;
331 }
332
333 if (carry != 0) {
334 *id++ = carry;
335 }
336
337 ilen = id - oidig;
338 }
339
340 return ilen;
341}
342
343/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
344 assumes den != 0
345 assumes num_dig has enough memory to be extended by 1 digit
346 assumes quo_dig has enough memory (as many digits as num)
347 assumes quo_dig is filled with zeros
348 modifies den_dig memory, but restors it to original state at end
349*/
350
Damien George40f3c022014-07-03 13:25:24 +0100351STATIC 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 +0000352 mpz_dig_t *orig_num_dig = num_dig;
353 mpz_dig_t *orig_quo_dig = quo_dig;
354 mpz_dig_t norm_shift = 0;
355 mpz_dbl_dig_t lead_den_digit;
356
357 // handle simple cases
358 {
Damien George42f3de92014-10-03 17:44:14 +0000359 int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
Damien George438c88d2014-02-22 19:25:23 +0000360 if (cmp == 0) {
361 *num_len = 0;
362 quo_dig[0] = 1;
363 *quo_len = 1;
364 return;
365 } else if (cmp < 0) {
366 // numerator remains the same
367 *quo_len = 0;
368 return;
369 }
370 }
371
372 // count number of leading zeros in leading digit of denominator
373 {
374 mpz_dig_t d = den_dig[den_len - 1];
Damien George9a21d2e2014-09-06 17:15:34 +0100375 while ((d & DIG_MSB) == 0) {
Damien George438c88d2014-02-22 19:25:23 +0000376 d <<= 1;
377 ++norm_shift;
378 }
379 }
380
381 // normalise denomenator (leading bit of leading digit is 1)
382 for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
383 mpz_dig_t d = *den;
384 *den = ((d << norm_shift) | carry) & DIG_MASK;
385 carry = d >> (DIG_SIZE - norm_shift);
386 }
387
388 // now need to shift numerator by same amount as denominator
389 // first, increase length of numerator in case we need more room to shift
390 num_dig[*num_len] = 0;
391 ++(*num_len);
392 for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
393 mpz_dig_t n = *num;
394 *num = ((n << norm_shift) | carry) & DIG_MASK;
395 carry = n >> (DIG_SIZE - norm_shift);
396 }
397
398 // cache the leading digit of the denominator
399 lead_den_digit = den_dig[den_len - 1];
400
401 // point num_dig to last digit in numerator
402 num_dig += *num_len - 1;
403
404 // calculate number of digits in quotient
405 *quo_len = *num_len - den_len;
406
407 // point to last digit to store for quotient
408 quo_dig += *quo_len - 1;
409
410 // keep going while we have enough digits to divide
411 while (*num_len > den_len) {
Damien George9a21d2e2014-09-06 17:15:34 +0100412 mpz_dbl_dig_t quo = ((mpz_dbl_dig_t)*num_dig << DIG_SIZE) | num_dig[-1];
Damien George438c88d2014-02-22 19:25:23 +0000413
414 // get approximate quotient
415 quo /= lead_den_digit;
416
Damien George9a21d2e2014-09-06 17:15:34 +0100417 // Multiply quo by den and subtract from num to get remainder.
418 // We have different code here to handle different compile-time
419 // configurations of mpz:
420 //
421 // 1. DIG_SIZE is stricly less than half the number of bits
422 // available in mpz_dbl_dig_t. In this case we can use a
423 // slightly more optimal (in time and space) routine that
424 // uses the extra bits in mpz_dbl_dig_signed_t to store a
425 // sign bit.
426 //
427 // 2. DIG_SIZE is exactly half the number of bits available in
428 // mpz_dbl_dig_t. In this (common) case we need to be careful
429 // not to overflow the borrow variable. And the shifting of
430 // borrow needs some special logic (it's a shift right with
431 // round up).
432
433 if (DIG_SIZE < 8 * sizeof(mpz_dbl_dig_t) / 2) {
Damien George438c88d2014-02-22 19:25:23 +0000434 mpz_dbl_dig_signed_t borrow = 0;
435
436 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
Damien George9a21d2e2014-09-06 17:15:34 +0100437 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 +0000438 *n = borrow & DIG_MASK;
439 borrow >>= DIG_SIZE;
440 }
Damien George9a21d2e2014-09-06 17:15:34 +0100441 borrow += *num_dig; // will overflow if DIG_SIZE >= 8*sizeof(mpz_dbl_dig_t)/2
Damien George438c88d2014-02-22 19:25:23 +0000442 *num_dig = borrow & DIG_MASK;
443 borrow >>= DIG_SIZE;
444
445 // adjust quotient if it is too big
446 for (; borrow != 0; --quo) {
447 mpz_dbl_dig_t carry = 0;
448 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
Damien George9a21d2e2014-09-06 17:15:34 +0100449 carry += (mpz_dbl_dig_t)*n + (mpz_dbl_dig_t)*d;
Damien George438c88d2014-02-22 19:25:23 +0000450 *n = carry & DIG_MASK;
451 carry >>= DIG_SIZE;
452 }
453 carry += *num_dig;
454 *num_dig = carry & DIG_MASK;
455 carry >>= DIG_SIZE;
456
457 borrow += carry;
458 }
Damien George9a21d2e2014-09-06 17:15:34 +0100459 } else { // DIG_SIZE == 8 * sizeof(mpz_dbl_dig_t) / 2
460 mpz_dbl_dig_t borrow = 0;
461
462 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
463 mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (mpz_dbl_dig_t)(*d);
464 if (x >= *n || *n - x <= borrow) {
465 borrow += (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)*n;
466 *n = (-borrow) & DIG_MASK;
467 borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
468 } else {
469 *n = ((mpz_dbl_dig_t)*n - (mpz_dbl_dig_t)x - (mpz_dbl_dig_t)borrow) & DIG_MASK;
470 borrow = 0;
471 }
472 }
473 if (borrow >= *num_dig) {
474 borrow -= (mpz_dbl_dig_t)*num_dig;
475 *num_dig = (-borrow) & DIG_MASK;
476 borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up
477 } else {
478 *num_dig = (*num_dig - borrow) & DIG_MASK;
479 borrow = 0;
480 }
481
482 // adjust quotient if it is too big
483 for (; borrow != 0; --quo) {
484 mpz_dbl_dig_t carry = 0;
485 for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
486 carry += (mpz_dbl_dig_t)*n + (mpz_dbl_dig_t)*d;
487 *n = carry & DIG_MASK;
488 carry >>= DIG_SIZE;
489 }
490 carry += (mpz_dbl_dig_t)*num_dig;
491 *num_dig = carry & DIG_MASK;
492 carry >>= DIG_SIZE;
493
494 //assert(borrow >= carry); // enable this to check the logic
495 borrow -= carry;
496 }
Damien George438c88d2014-02-22 19:25:23 +0000497 }
498
499 // store this digit of the quotient
500 *quo_dig = quo & DIG_MASK;
501 --quo_dig;
502
503 // move down to next digit of numerator
504 --num_dig;
505 --(*num_len);
506 }
507
508 // unnormalise denomenator
509 for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
510 mpz_dig_t d = *den;
511 *den = ((d >> norm_shift) | carry) & DIG_MASK;
512 carry = d << (DIG_SIZE - norm_shift);
513 }
514
515 // unnormalise numerator (remainder now)
516 for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
517 mpz_dig_t n = *num;
518 *num = ((n >> norm_shift) | carry) & DIG_MASK;
519 carry = n << (DIG_SIZE - norm_shift);
520 }
521
522 // strip trailing zeros
523
524 while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
525 --(*quo_len);
526 }
527
528 while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
529 --(*num_len);
530 }
531}
532
Damien George06201ff2014-03-01 19:50:50 +0000533#define MIN_ALLOC (2)
Damien George438c88d2014-02-22 19:25:23 +0000534
Damien George1c70cbf2014-08-30 00:38:16 +0100535STATIC const uint8_t log_base2_floor[] = {
Damien George438c88d2014-02-22 19:25:23 +0000536 0,
537 0, 1, 1, 2,
538 2, 2, 2, 3,
539 3, 3, 3, 3,
540 3, 3, 3, 4,
541 4, 4, 4, 4,
542 4, 4, 4, 4,
543 4, 4, 4, 4,
544 4, 4, 4, 5
545};
546
Damien George438c88d2014-02-22 19:25:23 +0000547void mpz_init_zero(mpz_t *z) {
Damien George438c88d2014-02-22 19:25:23 +0000548 z->neg = 0;
Damien George06201ff2014-03-01 19:50:50 +0000549 z->fixed_dig = 0;
550 z->alloc = 0;
Damien George438c88d2014-02-22 19:25:23 +0000551 z->len = 0;
552 z->dig = NULL;
553}
554
Damien George40f3c022014-07-03 13:25:24 +0100555void mpz_init_from_int(mpz_t *z, mp_int_t val) {
Damien George438c88d2014-02-22 19:25:23 +0000556 mpz_init_zero(z);
557 mpz_set_from_int(z, val);
558}
559
Damien Georgeafb1cf72014-09-05 20:37:06 +0100560void 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 +0000561 z->neg = 0;
562 z->fixed_dig = 1;
563 z->alloc = alloc;
564 z->len = 0;
565 z->dig = dig;
566 mpz_set_from_int(z, val);
567}
568
Damien George438c88d2014-02-22 19:25:23 +0000569void mpz_deinit(mpz_t *z) {
Damien George06201ff2014-03-01 19:50:50 +0000570 if (z != NULL && !z->fixed_dig) {
Damien George438c88d2014-02-22 19:25:23 +0000571 m_del(mpz_dig_t, z->dig, z->alloc);
572 }
573}
574
575mpz_t *mpz_zero(void) {
576 mpz_t *z = m_new_obj(mpz_t);
577 mpz_init_zero(z);
578 return z;
579}
580
Damien George40f3c022014-07-03 13:25:24 +0100581mpz_t *mpz_from_int(mp_int_t val) {
Damien George438c88d2014-02-22 19:25:23 +0000582 mpz_t *z = mpz_zero();
583 mpz_set_from_int(z, val);
584 return z;
585}
586
Damien George95307432014-09-10 22:10:33 +0100587mpz_t *mpz_from_ll(long long val, bool is_signed) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000588 mpz_t *z = mpz_zero();
Damien George95307432014-09-10 22:10:33 +0100589 mpz_set_from_ll(z, val, is_signed);
Damien Georgebb4a43f2014-03-12 15:36:06 +0000590 return z;
591}
592
Damien Georgeafb1cf72014-09-05 20:37:06 +0100593mpz_t *mpz_from_str(const char *str, mp_uint_t len, bool neg, mp_uint_t base) {
Damien George438c88d2014-02-22 19:25:23 +0000594 mpz_t *z = mpz_zero();
595 mpz_set_from_str(z, str, len, neg, base);
596 return z;
597}
598
599void mpz_free(mpz_t *z) {
600 if (z != NULL) {
601 m_del(mpz_dig_t, z->dig, z->alloc);
602 m_del_obj(mpz_t, z);
603 }
604}
605
Damien Georgeafb1cf72014-09-05 20:37:06 +0100606STATIC void mpz_need_dig(mpz_t *z, mp_uint_t need) {
Damien George438c88d2014-02-22 19:25:23 +0000607 if (need < MIN_ALLOC) {
Damien George06201ff2014-03-01 19:50:50 +0000608 need = MIN_ALLOC;
Damien George438c88d2014-02-22 19:25:23 +0000609 }
610
Damien George06201ff2014-03-01 19:50:50 +0000611 if (z->dig == NULL || z->alloc < need) {
612 if (z->fixed_dig) {
613 // cannot reallocate fixed buffers
614 assert(0);
615 return;
616 }
617 z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
618 z->alloc = need;
Damien George438c88d2014-02-22 19:25:23 +0000619 }
620}
621
622mpz_t *mpz_clone(const mpz_t *src) {
623 mpz_t *z = m_new_obj(mpz_t);
Damien George438c88d2014-02-22 19:25:23 +0000624 z->neg = src->neg;
Damien George06201ff2014-03-01 19:50:50 +0000625 z->fixed_dig = 0;
626 z->alloc = src->alloc;
Damien George438c88d2014-02-22 19:25:23 +0000627 z->len = src->len;
628 if (src->dig == NULL) {
629 z->dig = NULL;
630 } else {
631 z->dig = m_new(mpz_dig_t, z->alloc);
632 memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
633 }
634 return z;
635}
636
Damien George06201ff2014-03-01 19:50:50 +0000637/* sets dest = src
638 can have dest, src the same
639*/
Damien George438c88d2014-02-22 19:25:23 +0000640void mpz_set(mpz_t *dest, const mpz_t *src) {
641 mpz_need_dig(dest, src->len);
642 dest->neg = src->neg;
643 dest->len = src->len;
644 memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
645}
646
Damien George40f3c022014-07-03 13:25:24 +0100647void mpz_set_from_int(mpz_t *z, mp_int_t val) {
Damien George06201ff2014-03-01 19:50:50 +0000648 mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
Damien George438c88d2014-02-22 19:25:23 +0000649
Damien George40f3c022014-07-03 13:25:24 +0100650 mp_uint_t uval;
Damien George438c88d2014-02-22 19:25:23 +0000651 if (val < 0) {
652 z->neg = 1;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000653 uval = -val;
Damien George438c88d2014-02-22 19:25:23 +0000654 } else {
655 z->neg = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000656 uval = val;
Damien George438c88d2014-02-22 19:25:23 +0000657 }
658
659 z->len = 0;
Damien Georgebb4a43f2014-03-12 15:36:06 +0000660 while (uval > 0) {
661 z->dig[z->len++] = uval & DIG_MASK;
662 uval >>= DIG_SIZE;
663 }
664}
665
Damien George95307432014-09-10 22:10:33 +0100666void mpz_set_from_ll(mpz_t *z, long long val, bool is_signed) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000667 mpz_need_dig(z, MPZ_NUM_DIG_FOR_LL);
668
669 unsigned long long uval;
Damien George95307432014-09-10 22:10:33 +0100670 if (is_signed && val < 0) {
Damien Georgebb4a43f2014-03-12 15:36:06 +0000671 z->neg = 1;
672 uval = -val;
673 } else {
674 z->neg = 0;
675 uval = val;
676 }
677
678 z->len = 0;
679 while (uval > 0) {
680 z->dig[z->len++] = uval & DIG_MASK;
681 uval >>= DIG_SIZE;
Damien George438c88d2014-02-22 19:25:23 +0000682 }
683}
684
685// returns number of bytes from str that were processed
Damien Georgeafb1cf72014-09-05 20:37:06 +0100686mp_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 +0000687 assert(base < 36);
688
689 const char *cur = str;
690 const char *top = str + len;
691
692 mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
693
694 if (neg) {
695 z->neg = 1;
696 } else {
697 z->neg = 0;
698 }
699
700 z->len = 0;
701 for (; cur < top; ++cur) { // XXX UTF8 next char
Damien Georgeafb1cf72014-09-05 20:37:06 +0100702 //mp_uint_t v = char_to_numeric(cur#); // XXX UTF8 get char
703 mp_uint_t v = *cur;
Damien George438c88d2014-02-22 19:25:23 +0000704 if ('0' <= v && v <= '9') {
705 v -= '0';
706 } else if ('A' <= v && v <= 'Z') {
707 v -= 'A' - 10;
708 } else if ('a' <= v && v <= 'z') {
709 v -= 'a' - 10;
710 } else {
711 break;
712 }
713 if (v >= base) {
714 break;
715 }
716 z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
717 }
718
719 return cur - str;
720}
721
722bool mpz_is_zero(const mpz_t *z) {
723 return z->len == 0;
724}
725
726bool mpz_is_pos(const mpz_t *z) {
727 return z->len > 0 && z->neg == 0;
728}
729
730bool mpz_is_neg(const mpz_t *z) {
731 return z->len > 0 && z->neg != 0;
732}
733
734bool mpz_is_odd(const mpz_t *z) {
735 return z->len > 0 && (z->dig[0] & 1) != 0;
736}
737
738bool mpz_is_even(const mpz_t *z) {
739 return z->len == 0 || (z->dig[0] & 1) == 0;
740}
741
Damien George42f3de92014-10-03 17:44:14 +0000742int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
743 int cmp = (int)z2->neg - (int)z1->neg;
Damien George438c88d2014-02-22 19:25:23 +0000744 if (cmp != 0) {
745 return cmp;
746 }
747 cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
748 if (z1->neg != 0) {
749 cmp = -cmp;
750 }
751 return cmp;
752}
753
Damien George06201ff2014-03-01 19:50:50 +0000754#if 0
755// obsolete
756// compares mpz with an integer that fits within DIG_SIZE bits
Damien Georgeafb1cf72014-09-05 20:37:06 +0100757mp_int_t mpz_cmp_sml_int(const mpz_t *z, mp_int_t sml_int) {
758 mp_int_t cmp;
Damien George438c88d2014-02-22 19:25:23 +0000759 if (z->neg == 0) {
760 if (sml_int < 0) return 1;
761 if (sml_int == 0) {
762 if (z->len == 0) return 0;
763 return 1;
764 }
765 if (z->len == 0) return -1;
766 assert(sml_int < (1 << DIG_SIZE));
767 if (z->len != 1) return 1;
768 cmp = z->dig[0] - sml_int;
769 } else {
770 if (sml_int > 0) return -1;
771 if (sml_int == 0) {
772 if (z->len == 0) return 0;
773 return -1;
774 }
775 if (z->len == 0) return 1;
776 assert(sml_int > -(1 << DIG_SIZE));
777 if (z->len != 1) return -1;
778 cmp = -z->dig[0] - sml_int;
779 }
780 if (cmp < 0) return -1;
781 if (cmp > 0) return 1;
782 return 0;
783}
Damien George06201ff2014-03-01 19:50:50 +0000784#endif
Damien George438c88d2014-02-22 19:25:23 +0000785
Damien George438c88d2014-02-22 19:25:23 +0000786#if 0
787these functions are unused
788
789/* returns abs(z)
790*/
791mpz_t *mpz_abs(const mpz_t *z) {
792 mpz_t *z2 = mpz_clone(z);
793 z2->neg = 0;
794 return z2;
795}
796
797/* returns -z
798*/
799mpz_t *mpz_neg(const mpz_t *z) {
800 mpz_t *z2 = mpz_clone(z);
801 z2->neg = 1 - z2->neg;
802 return z2;
803}
804
805/* returns lhs + rhs
806 can have lhs, rhs the same
807*/
808mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
809 mpz_t *z = mpz_zero();
810 mpz_add_inpl(z, lhs, rhs);
811 return z;
812}
813
814/* returns lhs - rhs
815 can have lhs, rhs the same
816*/
817mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
818 mpz_t *z = mpz_zero();
819 mpz_sub_inpl(z, lhs, rhs);
820 return z;
821}
822
823/* returns lhs * rhs
824 can have lhs, rhs the same
825*/
826mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
827 mpz_t *z = mpz_zero();
828 mpz_mul_inpl(z, lhs, rhs);
829 return z;
830}
831
832/* returns lhs ** rhs
833 can have lhs, rhs the same
834*/
835mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
836 mpz_t *z = mpz_zero();
837 mpz_pow_inpl(z, lhs, rhs);
838 return z;
839}
840#endif
841
842/* computes dest = abs(z)
843 can have dest, z the same
844*/
845void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
846 if (dest != z) {
847 mpz_set(dest, z);
848 }
849 dest->neg = 0;
850}
851
852/* computes dest = -z
853 can have dest, z the same
854*/
855void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
856 if (dest != z) {
857 mpz_set(dest, z);
858 }
859 dest->neg = 1 - dest->neg;
860}
861
Damien George06201ff2014-03-01 19:50:50 +0000862/* computes dest = ~z (= -z - 1)
863 can have dest, z the same
864*/
865void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
866 if (dest != z) {
867 mpz_set(dest, z);
868 }
Damien Georgee0ac1942014-12-31 19:35:01 +0000869 if (dest->len == 0) {
870 mpz_need_dig(dest, 1);
871 dest->dig[0] = 1;
872 dest->len = 1;
873 dest->neg = 1;
874 } else if (dest->neg) {
Damien George06201ff2014-03-01 19:50:50 +0000875 dest->neg = 0;
876 mpz_dig_t k = 1;
877 dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
878 } else {
Damien Georgee0ac1942014-12-31 19:35:01 +0000879 mpz_need_dig(dest, dest->len + 1);
Damien George06201ff2014-03-01 19:50:50 +0000880 mpz_dig_t k = 1;
881 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
882 dest->neg = 1;
883 }
884}
885
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000886/* computes dest = lhs << rhs
887 can have dest, lhs the same
888*/
Damien George40f3c022014-07-03 13:25:24 +0100889void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, mp_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000890 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000891 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000892 } else if (rhs < 0) {
893 mpz_shr_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000894 } else {
Damien George06201ff2014-03-01 19:50:50 +0000895 mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
896 dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
897 dest->neg = lhs->neg;
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000898 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000899}
900
901/* computes dest = lhs >> rhs
902 can have dest, lhs the same
903*/
Damien George40f3c022014-07-03 13:25:24 +0100904void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, mp_int_t rhs) {
Damien George06201ff2014-03-01 19:50:50 +0000905 if (lhs->len == 0 || rhs == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000906 mpz_set(dest, lhs);
Damien George06201ff2014-03-01 19:50:50 +0000907 } else if (rhs < 0) {
908 mpz_shl_inpl(dest, lhs, -rhs);
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000909 } else {
Damien George06201ff2014-03-01 19:50:50 +0000910 mpz_need_dig(dest, lhs->len);
911 dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
912 dest->neg = lhs->neg;
913 if (dest->neg) {
914 // arithmetic shift right, rounding to negative infinity
Damien Georgeafb1cf72014-09-05 20:37:06 +0100915 mp_uint_t n_whole = rhs / DIG_SIZE;
916 mp_uint_t n_part = rhs % DIG_SIZE;
Damien George06201ff2014-03-01 19:50:50 +0000917 mpz_dig_t round_up = 0;
Damien Georgeafb1cf72014-09-05 20:37:06 +0100918 for (mp_uint_t i = 0; i < lhs->len && i < n_whole; i++) {
Damien George06201ff2014-03-01 19:50:50 +0000919 if (lhs->dig[i] != 0) {
920 round_up = 1;
921 break;
922 }
923 }
924 if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
925 round_up = 1;
926 }
927 if (round_up) {
Damien Georgee0ac1942014-12-31 19:35:01 +0000928 if (dest->len == 0) {
929 // dest == 0, so need to add 1 by hand (answer will be -1)
930 dest->dig[0] = 1;
931 dest->len = 1;
932 } else {
933 // dest > 0, so can use mpn_add to add 1
934 dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
935 }
Damien George06201ff2014-03-01 19:50:50 +0000936 }
937 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000938 }
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000939}
Damien Georgec5ac2ac2014-02-26 16:56:30 +0000940
Damien George438c88d2014-02-22 19:25:23 +0000941/* computes dest = lhs + rhs
942 can have dest, lhs, rhs the same
943*/
944void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
945 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
946 const mpz_t *temp = lhs;
947 lhs = rhs;
948 rhs = temp;
949 }
950
951 if (lhs->neg == rhs->neg) {
952 mpz_need_dig(dest, lhs->len + 1);
953 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
954 } else {
955 mpz_need_dig(dest, lhs->len);
956 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
957 }
958
959 dest->neg = lhs->neg;
960}
961
962/* computes dest = lhs - rhs
963 can have dest, lhs, rhs the same
964*/
965void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
966 bool neg = false;
967
968 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
969 const mpz_t *temp = lhs;
970 lhs = rhs;
971 rhs = temp;
972 neg = true;
973 }
974
975 if (lhs->neg != rhs->neg) {
976 mpz_need_dig(dest, lhs->len + 1);
977 dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
978 } else {
979 mpz_need_dig(dest, lhs->len);
980 dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
981 }
982
983 if (neg) {
984 dest->neg = 1 - lhs->neg;
985 } else {
986 dest->neg = lhs->neg;
987 }
988}
989
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200990/* computes dest = lhs & rhs
991 can have dest, lhs, rhs the same
992*/
993void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
Paul Sokolovsky57207b82014-03-23 01:52:36 +0200994 if (lhs->neg == rhs->neg) {
Damien Georgef55cf102014-05-29 15:01:49 +0100995 if (lhs->neg == 0) {
996 // make sure lhs has the most digits
997 if (lhs->len < rhs->len) {
998 const mpz_t *temp = lhs;
999 lhs = rhs;
1000 rhs = temp;
1001 }
1002 // do the and'ing
1003 mpz_need_dig(dest, rhs->len);
1004 dest->len = mpn_and(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1005 dest->neg = 0;
1006 } else {
1007 // TODO both args are negative
1008 assert(0);
1009 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001010 } else {
Damien Georgef55cf102014-05-29 15:01:49 +01001011 // args have different sign
1012 // make sure lhs is the positive arg
1013 if (rhs->neg == 0) {
1014 const mpz_t *temp = lhs;
1015 lhs = rhs;
1016 rhs = temp;
1017 }
1018 mpz_need_dig(dest, lhs->len + 1);
1019 dest->len = mpn_and_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1020 assert(dest->len <= dest->alloc);
1021 dest->neg = 0;
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001022 }
Paul Sokolovsky57207b82014-03-23 01:52:36 +02001023}
1024
1025/* computes dest = lhs | rhs
1026 can have dest, lhs, rhs the same
1027*/
1028void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1029 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
1030 const mpz_t *temp = lhs;
1031 lhs = rhs;
1032 rhs = temp;
1033 }
1034
1035 if (lhs->neg == rhs->neg) {
1036 mpz_need_dig(dest, lhs->len);
1037 dest->len = mpn_or(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1038 } else {
1039 mpz_need_dig(dest, lhs->len);
1040 // TODO
1041 assert(0);
1042// dest->len = mpn_or_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1043 }
1044
1045 dest->neg = lhs->neg;
1046}
1047
1048/* computes dest = lhs ^ rhs
1049 can have dest, lhs, rhs the same
1050*/
1051void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1052 if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
1053 const mpz_t *temp = lhs;
1054 lhs = rhs;
1055 rhs = temp;
1056 }
1057
1058 if (lhs->neg == rhs->neg) {
1059 mpz_need_dig(dest, lhs->len);
1060 dest->len = mpn_xor(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1061 } else {
1062 mpz_need_dig(dest, lhs->len);
1063 // TODO
1064 assert(0);
1065// dest->len = mpn_xor_neg(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1066 }
1067
1068 dest->neg = 0;
1069}
1070
Damien George438c88d2014-02-22 19:25:23 +00001071/* computes dest = lhs * rhs
1072 can have dest, lhs, rhs the same
1073*/
1074void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
1075{
1076 if (lhs->len == 0 || rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001077 mpz_set_from_int(dest, 0);
1078 return;
Damien George438c88d2014-02-22 19:25:23 +00001079 }
1080
1081 mpz_t *temp = NULL;
1082 if (lhs == dest) {
1083 lhs = temp = mpz_clone(lhs);
1084 if (rhs == dest) {
1085 rhs = lhs;
1086 }
1087 } else if (rhs == dest) {
1088 rhs = temp = mpz_clone(rhs);
1089 }
1090
1091 mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
1092 memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
1093 dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
1094
1095 if (lhs->neg == rhs->neg) {
1096 dest->neg = 0;
1097 } else {
1098 dest->neg = 1;
1099 }
1100
1101 mpz_free(temp);
1102}
1103
1104/* computes dest = lhs ** rhs
1105 can have dest, lhs, rhs the same
1106*/
1107void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
1108 if (lhs->len == 0 || rhs->neg != 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001109 mpz_set_from_int(dest, 0);
1110 return;
Damien George438c88d2014-02-22 19:25:23 +00001111 }
1112
1113 if (rhs->len == 0) {
Damien Georgec5ac2ac2014-02-26 16:56:30 +00001114 mpz_set_from_int(dest, 1);
1115 return;
Damien George438c88d2014-02-22 19:25:23 +00001116 }
1117
1118 mpz_t *x = mpz_clone(lhs);
1119 mpz_t *n = mpz_clone(rhs);
1120
1121 mpz_set_from_int(dest, 1);
1122
1123 while (n->len > 0) {
1124 if (mpz_is_odd(n)) {
1125 mpz_mul_inpl(dest, dest, x);
1126 }
Damien George438c88d2014-02-22 19:25:23 +00001127 n->len = mpn_shr(n->dig, n->dig, n->len, 1);
Damien George5bf565e2014-04-04 00:16:32 +01001128 if (n->len == 0) {
1129 break;
1130 }
1131 mpz_mul_inpl(x, x, x);
Damien George438c88d2014-02-22 19:25:23 +00001132 }
1133
1134 mpz_free(x);
1135 mpz_free(n);
1136}
1137
1138/* computes gcd(z1, z2)
1139 based on Knuth's modified gcd algorithm (I think?)
1140 gcd(z1, z2) >= 0
1141 gcd(0, 0) = 0
1142 gcd(z, 0) = abs(z)
1143*/
1144mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
1145 if (z1->len == 0) {
1146 mpz_t *a = mpz_clone(z2);
1147 a->neg = 0;
1148 return a;
1149 } else if (z2->len == 0) {
1150 mpz_t *a = mpz_clone(z1);
1151 a->neg = 0;
1152 return a;
1153 }
1154
1155 mpz_t *a = mpz_clone(z1);
1156 mpz_t *b = mpz_clone(z2);
1157 mpz_t c; mpz_init_zero(&c);
1158 a->neg = 0;
1159 b->neg = 0;
1160
1161 for (;;) {
1162 if (mpz_cmp(a, b) < 0) {
1163 if (a->len == 0) {
1164 mpz_free(a);
1165 mpz_deinit(&c);
1166 return b;
1167 }
1168 mpz_t *t = a; a = b; b = t;
1169 }
1170 if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
1171 break;
1172 }
1173 mpz_set(&c, b);
1174 do {
1175 mpz_add_inpl(&c, &c, &c);
1176 } while (mpz_cmp(&c, a) <= 0);
1177 c.len = mpn_shr(c.dig, c.dig, c.len, 1);
1178 mpz_sub_inpl(a, a, &c);
1179 }
1180
1181 mpz_deinit(&c);
1182
1183 if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
1184 mpz_free(a);
1185 return b;
1186 } else {
1187 mpz_free(b);
1188 return a;
1189 }
1190}
1191
1192/* computes lcm(z1, z2)
1193 = abs(z1) / gcd(z1, z2) * abs(z2)
1194 lcm(z1, z1) >= 0
1195 lcm(0, 0) = 0
1196 lcm(z, 0) = 0
1197*/
Damien George5d9b8162014-08-07 14:27:48 +00001198mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2) {
1199 if (z1->len == 0 || z2->len == 0) {
1200 return mpz_zero();
stijn01d6be42014-05-05 12:18:27 +02001201 }
Damien George438c88d2014-02-22 19:25:23 +00001202
1203 mpz_t *gcd = mpz_gcd(z1, z2);
1204 mpz_t *quo = mpz_zero();
1205 mpz_t *rem = mpz_zero();
1206 mpz_divmod_inpl(quo, rem, z1, gcd);
1207 mpz_mul_inpl(rem, quo, z2);
1208 mpz_free(gcd);
1209 mpz_free(quo);
1210 rem->neg = 0;
1211 return rem;
1212}
1213
1214/* computes new integers in quo and rem such that:
1215 quo * rhs + rem = lhs
1216 0 <= rem < rhs
1217 can have lhs, rhs the same
1218*/
1219void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
1220 *quo = mpz_zero();
1221 *rem = mpz_zero();
1222 mpz_divmod_inpl(*quo, *rem, lhs, rhs);
1223}
1224
1225/* computes new integers in quo and rem such that:
1226 quo * rhs + rem = lhs
1227 0 <= rem < rhs
1228 can have lhs, rhs the same
1229*/
1230void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
1231 if (rhs->len == 0) {
1232 mpz_set_from_int(dest_quo, 0);
1233 mpz_set_from_int(dest_rem, 0);
1234 return;
1235 }
1236
1237 mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
1238 memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
1239 dest_quo->len = 0;
1240 mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
1241 mpz_set(dest_rem, lhs);
1242 //rhs->dig[rhs->len] = 0;
1243 mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
1244
1245 if (lhs->neg != rhs->neg) {
1246 dest_quo->neg = 1;
1247 }
1248}
1249
1250#if 0
1251these functions are unused
1252
1253/* computes floor(lhs / rhs)
1254 can have lhs, rhs the same
1255*/
1256mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
1257 mpz_t *quo = mpz_zero();
1258 mpz_t rem; mpz_init_zero(&rem);
1259 mpz_divmod_inpl(quo, &rem, lhs, rhs);
1260 mpz_deinit(&rem);
1261 return quo;
1262}
1263
1264/* computes lhs % rhs ( >= 0)
1265 can have lhs, rhs the same
1266*/
1267mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
1268 mpz_t quo; mpz_init_zero(&quo);
1269 mpz_t *rem = mpz_zero();
1270 mpz_divmod_inpl(&quo, rem, lhs, rhs);
1271 mpz_deinit(&quo);
1272 return rem;
1273}
1274#endif
1275
Damien Georgeffe911d2014-07-24 14:21:37 +01001276// must return actual int value if it fits in mp_int_t
1277mp_int_t mpz_hash(const mpz_t *z) {
1278 mp_int_t val = 0;
1279 mpz_dig_t *d = z->dig + z->len;
1280
1281 while (--d >= z->dig) {
1282 val = (val << DIG_SIZE) | *d;
1283 }
1284
1285 if (z->neg != 0) {
1286 val = -val;
1287 }
1288
1289 return val;
1290}
1291
Damien George40f3c022014-07-03 13:25:24 +01001292bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) {
1293 mp_int_t val = 0;
Damien George8270e382014-04-03 11:00:54 +00001294 mpz_dig_t *d = i->dig + i->len;
1295
1296 while (--d >= i->dig) {
Damien Georgec9aa58e2014-07-31 13:41:43 +00001297 if (val > (~(WORD_MSBIT_HIGH) >> DIG_SIZE)) {
1298 // will overflow
Damien George8270e382014-04-03 11:00:54 +00001299 return false;
1300 }
Damien Georgec9aa58e2014-07-31 13:41:43 +00001301 val = (val << DIG_SIZE) | *d;
Damien George8270e382014-04-03 11:00:54 +00001302 }
1303
1304 if (i->neg != 0) {
1305 val = -val;
1306 }
1307
1308 *value = val;
1309 return true;
1310}
1311
Damien Georgec9aa58e2014-07-31 13:41:43 +00001312bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) {
1313 if (i->neg != 0) {
1314 // can't represent signed values
1315 return false;
1316 }
1317
1318 mp_uint_t val = 0;
1319 mpz_dig_t *d = i->dig + i->len;
1320
1321 while (--d >= i->dig) {
Damien George9a21d2e2014-09-06 17:15:34 +01001322 if (val > (~(WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) {
Damien Georgec9aa58e2014-07-31 13:41:43 +00001323 // will overflow
1324 return false;
1325 }
1326 val = (val << DIG_SIZE) | *d;
1327 }
1328
1329 *value = val;
1330 return true;
1331}
1332
Damien Georgefb510b32014-06-01 13:32:54 +01001333#if MICROPY_PY_BUILTINS_FLOAT
Damien George52608102014-03-08 15:04:54 +00001334mp_float_t mpz_as_float(const mpz_t *i) {
1335 mp_float_t val = 0;
Damien George438c88d2014-02-22 19:25:23 +00001336 mpz_dig_t *d = i->dig + i->len;
1337
1338 while (--d >= i->dig) {
Damien George9a21d2e2014-09-06 17:15:34 +01001339 val = val * DIG_BASE + *d;
Damien George438c88d2014-02-22 19:25:23 +00001340 }
1341
1342 if (i->neg != 0) {
1343 val = -val;
1344 }
1345
1346 return val;
1347}
Damien George52608102014-03-08 15:04:54 +00001348#endif
Damien George438c88d2014-02-22 19:25:23 +00001349
Damien Georgeafb1cf72014-09-05 20:37:06 +01001350mp_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 +00001351 if (base < 2 || base > 32) {
1352 return 0;
1353 }
1354
Damien Georgeafb1cf72014-09-05 20:37:06 +01001355 mp_uint_t num_digits = i->len * DIG_SIZE / log_base2_floor[base] + 1;
1356 mp_uint_t num_commas = comma ? num_digits / 3: 0;
1357 mp_uint_t prefix_len = prefix ? strlen(prefix) : 0;
Dave Hylandsc4029e52014-04-07 11:19:51 -07001358
1359 return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte
1360}
1361
Damien Georgeafb1cf72014-09-05 20:37:06 +01001362#if 0
1363this function is unused
1364char *mpz_as_str(const mpz_t *i, mp_uint_t base) {
1365 char *s = m_new(char, mpz_as_str_size(i, base, NULL, '\0'));
1366 mpz_as_str_inpl(i, base, NULL, 'a', '\0', s);
Damien George438c88d2014-02-22 19:25:23 +00001367 return s;
1368}
Damien Georgeafb1cf72014-09-05 20:37:06 +01001369#endif
Damien George438c88d2014-02-22 19:25:23 +00001370
1371// assumes enough space as calculated by mpz_as_str_size
1372// returns length of string, not including null byte
Damien Georgeafb1cf72014-09-05 20:37:06 +01001373mp_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 +00001374 if (str == NULL || base < 2 || base > 32) {
1375 str[0] = 0;
1376 return 0;
1377 }
1378
Damien Georgeafb1cf72014-09-05 20:37:06 +01001379 mp_uint_t ilen = i->len;
Damien George438c88d2014-02-22 19:25:23 +00001380
Dave Hylandsc4029e52014-04-07 11:19:51 -07001381 char *s = str;
Damien George438c88d2014-02-22 19:25:23 +00001382 if (ilen == 0) {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001383 if (prefix) {
1384 while (*prefix)
1385 *s++ = *prefix++;
1386 }
1387 *s++ = '0';
1388 *s = '\0';
1389 return s - str;
Damien George438c88d2014-02-22 19:25:23 +00001390 }
1391
Damien Georgeeec91052014-04-08 23:11:00 +01001392 // make a copy of mpz digits, so we can do the div/mod calculation
Damien George438c88d2014-02-22 19:25:23 +00001393 mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
1394 memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
1395
1396 // convert
Dave Hylandsc4029e52014-04-07 11:19:51 -07001397 char *last_comma = str;
Damien George438c88d2014-02-22 19:25:23 +00001398 bool done;
1399 do {
1400 mpz_dig_t *d = dig + ilen;
1401 mpz_dbl_dig_t a = 0;
1402
1403 // compute next remainder
1404 while (--d >= dig) {
1405 a = (a << DIG_SIZE) | *d;
1406 *d = a / base;
1407 a %= base;
1408 }
1409
1410 // convert to character
1411 a += '0';
1412 if (a > '9') {
Dave Hylandsc4029e52014-04-07 11:19:51 -07001413 a += base_char - '9' - 1;
Damien George438c88d2014-02-22 19:25:23 +00001414 }
1415 *s++ = a;
1416
1417 // check if number is zero
1418 done = true;
1419 for (d = dig; d < dig + ilen; ++d) {
1420 if (*d != 0) {
1421 done = false;
1422 break;
1423 }
1424 }
Dave Hylandsc4029e52014-04-07 11:19:51 -07001425 if (comma && (s - last_comma) == 3) {
1426 *s++ = comma;
1427 last_comma = s;
1428 }
1429 }
1430 while (!done);
Damien George438c88d2014-02-22 19:25:23 +00001431
Damien Georgeeec91052014-04-08 23:11:00 +01001432 // free the copy of the digits array
1433 m_del(mpz_dig_t, dig, ilen);
1434
Dave Hylandsc4029e52014-04-07 11:19:51 -07001435 if (prefix) {
1436 const char *p = &prefix[strlen(prefix)];
1437 while (p > prefix) {
1438 *s++ = *--p;
1439 }
1440 }
Damien George438c88d2014-02-22 19:25:23 +00001441 if (i->neg != 0) {
1442 *s++ = '-';
1443 }
1444
1445 // reverse string
1446 for (char *u = str, *v = s - 1; u < v; ++u, --v) {
1447 char temp = *u;
1448 *u = *v;
1449 *v = temp;
1450 }
1451
Dave Hylandsc4029e52014-04-07 11:19:51 -07001452 *s = '\0'; // null termination
Damien George438c88d2014-02-22 19:25:23 +00001453
1454 return s - str;
1455}
1456
1457#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ