kopia lustrzana https://github.com/micropython/micropython
1103 wiersze
27 KiB
C
1103 wiersze
27 KiB
C
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "mpz.h"
|
|
|
|
#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
|
|
|
|
#define DIG_SIZE (MPZ_DIG_SIZE)
|
|
#define DIG_MASK ((1 << DIG_SIZE) - 1)
|
|
|
|
/*
|
|
mpz is an arbitrary precision integer type with a public API.
|
|
|
|
mpn functions act on non-negative integers represented by an array of generalised
|
|
digits (eg a word per digit). You also need to specify separately the length of the
|
|
array. There is no public API for mpn. Rather, the functions are used by mpz to
|
|
implement its features.
|
|
|
|
Integer values are stored little endian (first digit is first in memory).
|
|
|
|
Definition of normalise: ?
|
|
*/
|
|
|
|
/* compares i with j
|
|
returns sign(i - j)
|
|
assumes i, j are normalised
|
|
*/
|
|
STATIC int mpn_cmp(const mpz_dig_t *idig, uint ilen, const mpz_dig_t *jdig, uint jlen) {
|
|
if (ilen < jlen) { return -1; }
|
|
if (ilen > jlen) { return 1; }
|
|
|
|
for (idig += ilen, jdig += ilen; ilen > 0; --ilen) {
|
|
int cmp = *(--idig) - *(--jdig);
|
|
if (cmp < 0) { return -1; }
|
|
if (cmp > 0) { return 1; }
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* computes i = j << n
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes normalised j; assumes n > 0
|
|
can have i, j pointing to same memory
|
|
*/
|
|
STATIC uint mpn_shl(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
|
|
uint n_whole = (n + DIG_SIZE - 1) / DIG_SIZE;
|
|
uint n_part = n % DIG_SIZE;
|
|
|
|
// start from the high end of the digit arrays
|
|
idig += jlen + n_whole - 1;
|
|
jdig += jlen - 1;
|
|
|
|
// shift the digits
|
|
mpz_dbl_dig_t d = 0;
|
|
for (uint i = jlen; i > 0; i--, idig--, jdig--) {
|
|
d |= *jdig;
|
|
*idig = d >> (DIG_SIZE - n_part);
|
|
d <<= DIG_SIZE;
|
|
}
|
|
|
|
// store remaining bits
|
|
*idig = d >> (DIG_SIZE - n_part);
|
|
idig -= n_whole - 1;
|
|
memset(idig, 0, n_whole - 1);
|
|
|
|
// work out length of result
|
|
jlen += n_whole;
|
|
if (idig[jlen - 1] == 0) {
|
|
jlen--;
|
|
}
|
|
|
|
// return length of result
|
|
return jlen;
|
|
}
|
|
|
|
/* computes i = j >> n
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes normalised j; assumes n > 0
|
|
can have i, j pointing to same memory
|
|
*/
|
|
STATIC uint mpn_shr(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, uint n) {
|
|
uint n_whole = n / DIG_SIZE;
|
|
uint n_part = n % DIG_SIZE;
|
|
|
|
if (n_whole >= jlen) {
|
|
return 0;
|
|
}
|
|
|
|
jdig += n_whole;
|
|
jlen -= n_whole;
|
|
|
|
for (uint i = jlen; i > 0; i--, idig++, jdig++) {
|
|
mpz_dbl_dig_t d = *jdig;
|
|
if (i > 1) {
|
|
d |= jdig[1] << DIG_SIZE;
|
|
}
|
|
d >>= n_part;
|
|
*idig = d & DIG_MASK;
|
|
}
|
|
|
|
if (idig[-1] == 0) {
|
|
jlen--;
|
|
}
|
|
|
|
return jlen;
|
|
}
|
|
|
|
/* computes i = j + k
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes normalised j, k; assumes jlen >= klen
|
|
can have i, j, k pointing to same memory
|
|
*/
|
|
STATIC uint mpn_add(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
|
|
mpz_dig_t *oidig = idig;
|
|
mpz_dbl_dig_t carry = 0;
|
|
|
|
jlen -= klen;
|
|
|
|
for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
|
|
carry += *jdig + *kdig;
|
|
*idig = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
}
|
|
|
|
for (; jlen > 0; --jlen, ++idig, ++jdig) {
|
|
carry += *jdig;
|
|
*idig = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
}
|
|
|
|
if (carry != 0) {
|
|
*idig++ = carry;
|
|
}
|
|
|
|
return idig - oidig;
|
|
}
|
|
|
|
/* computes i = j - k
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes normalised j, k; assumes j >= k
|
|
can have i, j, k pointing to same memory
|
|
*/
|
|
STATIC uint mpn_sub(mpz_dig_t *idig, const mpz_dig_t *jdig, uint jlen, const mpz_dig_t *kdig, uint klen) {
|
|
mpz_dig_t *oidig = idig;
|
|
mpz_dbl_dig_signed_t borrow = 0;
|
|
|
|
jlen -= klen;
|
|
|
|
for (; klen > 0; --klen, ++idig, ++jdig, ++kdig) {
|
|
borrow += *jdig - *kdig;
|
|
*idig = borrow & DIG_MASK;
|
|
borrow >>= DIG_SIZE;
|
|
}
|
|
|
|
for (; jlen > 0; --jlen, ++idig, ++jdig) {
|
|
borrow += *jdig;
|
|
*idig = borrow & DIG_MASK;
|
|
borrow >>= DIG_SIZE;
|
|
}
|
|
|
|
for (--idig; idig >= oidig && *idig == 0; --idig) {
|
|
}
|
|
|
|
return idig + 1 - oidig;
|
|
}
|
|
|
|
/* computes i = i * d1 + d2
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes normalised i; assumes dmul != 0
|
|
*/
|
|
STATIC uint mpn_mul_dig_add_dig(mpz_dig_t *idig, uint ilen, mpz_dig_t dmul, mpz_dig_t dadd) {
|
|
mpz_dig_t *oidig = idig;
|
|
mpz_dbl_dig_t carry = dadd;
|
|
|
|
for (; ilen > 0; --ilen, ++idig) {
|
|
carry += *idig * dmul; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
|
|
*idig = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
}
|
|
|
|
if (carry != 0) {
|
|
*idig++ = carry;
|
|
}
|
|
|
|
return idig - oidig;
|
|
}
|
|
|
|
/* computes i = j * k
|
|
returns number of digits in i
|
|
assumes enough memory in i; assumes i is zeroed; assumes normalised j, k
|
|
can have j, k point to same memory
|
|
*/
|
|
STATIC uint mpn_mul(mpz_dig_t *idig, mpz_dig_t *jdig, uint jlen, mpz_dig_t *kdig, uint klen) {
|
|
mpz_dig_t *oidig = idig;
|
|
uint ilen = 0;
|
|
|
|
for (; klen > 0; --klen, ++idig, ++kdig) {
|
|
mpz_dig_t *id = idig;
|
|
mpz_dbl_dig_t carry = 0;
|
|
|
|
uint jl = jlen;
|
|
for (mpz_dig_t *jd = jdig; jl > 0; --jl, ++jd, ++id) {
|
|
carry += *id + *jd * *kdig; // will never overflow so long as DIG_SIZE <= WORD_SIZE / 2
|
|
*id = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
}
|
|
|
|
if (carry != 0) {
|
|
*id++ = carry;
|
|
}
|
|
|
|
ilen = id - oidig;
|
|
}
|
|
|
|
return ilen;
|
|
}
|
|
|
|
/* natural_div - quo * den + new_num = old_num (ie num is replaced with rem)
|
|
assumes den != 0
|
|
assumes num_dig has enough memory to be extended by 1 digit
|
|
assumes quo_dig has enough memory (as many digits as num)
|
|
assumes quo_dig is filled with zeros
|
|
modifies den_dig memory, but restors it to original state at end
|
|
*/
|
|
|
|
STATIC void mpn_div(mpz_dig_t *num_dig, machine_uint_t *num_len, mpz_dig_t *den_dig, machine_uint_t den_len, mpz_dig_t *quo_dig, machine_uint_t *quo_len) {
|
|
mpz_dig_t *orig_num_dig = num_dig;
|
|
mpz_dig_t *orig_quo_dig = quo_dig;
|
|
mpz_dig_t norm_shift = 0;
|
|
mpz_dbl_dig_t lead_den_digit;
|
|
|
|
// handle simple cases
|
|
{
|
|
int cmp = mpn_cmp(num_dig, *num_len, den_dig, den_len);
|
|
if (cmp == 0) {
|
|
*num_len = 0;
|
|
quo_dig[0] = 1;
|
|
*quo_len = 1;
|
|
return;
|
|
} else if (cmp < 0) {
|
|
// numerator remains the same
|
|
*quo_len = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// count number of leading zeros in leading digit of denominator
|
|
{
|
|
mpz_dig_t d = den_dig[den_len - 1];
|
|
while ((d & (1 << (DIG_SIZE - 1))) == 0) {
|
|
d <<= 1;
|
|
++norm_shift;
|
|
}
|
|
}
|
|
|
|
// normalise denomenator (leading bit of leading digit is 1)
|
|
for (mpz_dig_t *den = den_dig, carry = 0; den < den_dig + den_len; ++den) {
|
|
mpz_dig_t d = *den;
|
|
*den = ((d << norm_shift) | carry) & DIG_MASK;
|
|
carry = d >> (DIG_SIZE - norm_shift);
|
|
}
|
|
|
|
// now need to shift numerator by same amount as denominator
|
|
// first, increase length of numerator in case we need more room to shift
|
|
num_dig[*num_len] = 0;
|
|
++(*num_len);
|
|
for (mpz_dig_t *num = num_dig, carry = 0; num < num_dig + *num_len; ++num) {
|
|
mpz_dig_t n = *num;
|
|
*num = ((n << norm_shift) | carry) & DIG_MASK;
|
|
carry = n >> (DIG_SIZE - norm_shift);
|
|
}
|
|
|
|
// cache the leading digit of the denominator
|
|
lead_den_digit = den_dig[den_len - 1];
|
|
|
|
// point num_dig to last digit in numerator
|
|
num_dig += *num_len - 1;
|
|
|
|
// calculate number of digits in quotient
|
|
*quo_len = *num_len - den_len;
|
|
|
|
// point to last digit to store for quotient
|
|
quo_dig += *quo_len - 1;
|
|
|
|
// keep going while we have enough digits to divide
|
|
while (*num_len > den_len) {
|
|
mpz_dbl_dig_t quo = (*num_dig << DIG_SIZE) | num_dig[-1];
|
|
|
|
// get approximate quotient
|
|
quo /= lead_den_digit;
|
|
|
|
// multiply quo by den and subtract from num get remainder
|
|
{
|
|
mpz_dbl_dig_signed_t borrow = 0;
|
|
|
|
for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
|
|
borrow += *n - quo * *d; // will overflow if DIG_SIZE >= 16
|
|
*n = borrow & DIG_MASK;
|
|
borrow >>= DIG_SIZE;
|
|
}
|
|
borrow += *num_dig; // will overflow if DIG_SIZE >= 16
|
|
*num_dig = borrow & DIG_MASK;
|
|
borrow >>= DIG_SIZE;
|
|
|
|
// adjust quotient if it is too big
|
|
for (; borrow != 0; --quo) {
|
|
mpz_dbl_dig_t carry = 0;
|
|
for (mpz_dig_t *n = num_dig - den_len, *d = den_dig; n < num_dig; ++n, ++d) {
|
|
carry += *n + *d;
|
|
*n = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
}
|
|
carry += *num_dig;
|
|
*num_dig = carry & DIG_MASK;
|
|
carry >>= DIG_SIZE;
|
|
|
|
borrow += carry;
|
|
}
|
|
}
|
|
|
|
// store this digit of the quotient
|
|
*quo_dig = quo & DIG_MASK;
|
|
--quo_dig;
|
|
|
|
// move down to next digit of numerator
|
|
--num_dig;
|
|
--(*num_len);
|
|
}
|
|
|
|
// unnormalise denomenator
|
|
for (mpz_dig_t *den = den_dig + den_len - 1, carry = 0; den >= den_dig; --den) {
|
|
mpz_dig_t d = *den;
|
|
*den = ((d >> norm_shift) | carry) & DIG_MASK;
|
|
carry = d << (DIG_SIZE - norm_shift);
|
|
}
|
|
|
|
// unnormalise numerator (remainder now)
|
|
for (mpz_dig_t *num = orig_num_dig + *num_len - 1, carry = 0; num >= orig_num_dig; --num) {
|
|
mpz_dig_t n = *num;
|
|
*num = ((n >> norm_shift) | carry) & DIG_MASK;
|
|
carry = n << (DIG_SIZE - norm_shift);
|
|
}
|
|
|
|
// strip trailing zeros
|
|
|
|
while (*quo_len > 0 && orig_quo_dig[*quo_len - 1] == 0) {
|
|
--(*quo_len);
|
|
}
|
|
|
|
while (*num_len > 0 && orig_num_dig[*num_len - 1] == 0) {
|
|
--(*num_len);
|
|
}
|
|
}
|
|
|
|
#define MIN_ALLOC (2)
|
|
|
|
static const uint log_base2_floor[] = {
|
|
0,
|
|
0, 1, 1, 2,
|
|
2, 2, 2, 3,
|
|
3, 3, 3, 3,
|
|
3, 3, 3, 4,
|
|
4, 4, 4, 4,
|
|
4, 4, 4, 4,
|
|
4, 4, 4, 4,
|
|
4, 4, 4, 5
|
|
};
|
|
|
|
void mpz_init_zero(mpz_t *z) {
|
|
z->neg = 0;
|
|
z->fixed_dig = 0;
|
|
z->alloc = 0;
|
|
z->len = 0;
|
|
z->dig = NULL;
|
|
}
|
|
|
|
void mpz_init_from_int(mpz_t *z, machine_int_t val) {
|
|
mpz_init_zero(z);
|
|
mpz_set_from_int(z, val);
|
|
}
|
|
|
|
void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, uint alloc, machine_int_t val) {
|
|
z->neg = 0;
|
|
z->fixed_dig = 1;
|
|
z->alloc = alloc;
|
|
z->len = 0;
|
|
z->dig = dig;
|
|
mpz_set_from_int(z, val);
|
|
}
|
|
|
|
void mpz_deinit(mpz_t *z) {
|
|
if (z != NULL && !z->fixed_dig) {
|
|
m_del(mpz_dig_t, z->dig, z->alloc);
|
|
}
|
|
}
|
|
|
|
mpz_t *mpz_zero(void) {
|
|
mpz_t *z = m_new_obj(mpz_t);
|
|
mpz_init_zero(z);
|
|
return z;
|
|
}
|
|
|
|
mpz_t *mpz_from_int(machine_int_t val) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_set_from_int(z, val);
|
|
return z;
|
|
}
|
|
|
|
mpz_t *mpz_from_str(const char *str, uint len, bool neg, uint base) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_set_from_str(z, str, len, neg, base);
|
|
return z;
|
|
}
|
|
|
|
void mpz_free(mpz_t *z) {
|
|
if (z != NULL) {
|
|
m_del(mpz_dig_t, z->dig, z->alloc);
|
|
m_del_obj(mpz_t, z);
|
|
}
|
|
}
|
|
|
|
STATIC void mpz_need_dig(mpz_t *z, uint need) {
|
|
if (need < MIN_ALLOC) {
|
|
need = MIN_ALLOC;
|
|
}
|
|
|
|
if (z->dig == NULL || z->alloc < need) {
|
|
if (z->fixed_dig) {
|
|
// cannot reallocate fixed buffers
|
|
assert(0);
|
|
return;
|
|
}
|
|
z->dig = m_renew(mpz_dig_t, z->dig, z->alloc, need);
|
|
z->alloc = need;
|
|
}
|
|
}
|
|
|
|
mpz_t *mpz_clone(const mpz_t *src) {
|
|
mpz_t *z = m_new_obj(mpz_t);
|
|
z->neg = src->neg;
|
|
z->fixed_dig = 0;
|
|
z->alloc = src->alloc;
|
|
z->len = src->len;
|
|
if (src->dig == NULL) {
|
|
z->dig = NULL;
|
|
} else {
|
|
z->dig = m_new(mpz_dig_t, z->alloc);
|
|
memcpy(z->dig, src->dig, src->alloc * sizeof(mpz_dig_t));
|
|
}
|
|
return z;
|
|
}
|
|
|
|
/* sets dest = src
|
|
can have dest, src the same
|
|
*/
|
|
void mpz_set(mpz_t *dest, const mpz_t *src) {
|
|
mpz_need_dig(dest, src->len);
|
|
dest->neg = src->neg;
|
|
dest->len = src->len;
|
|
memcpy(dest->dig, src->dig, src->len * sizeof(mpz_dig_t));
|
|
}
|
|
|
|
void mpz_set_from_int(mpz_t *z, machine_int_t val) {
|
|
mpz_need_dig(z, MPZ_NUM_DIG_FOR_INT);
|
|
|
|
if (val < 0) {
|
|
z->neg = 1;
|
|
val = -val;
|
|
} else {
|
|
z->neg = 0;
|
|
}
|
|
|
|
z->len = 0;
|
|
while (val > 0) {
|
|
z->dig[z->len++] = val & DIG_MASK;
|
|
val >>= DIG_SIZE;
|
|
}
|
|
}
|
|
|
|
// returns number of bytes from str that were processed
|
|
uint mpz_set_from_str(mpz_t *z, const char *str, uint len, bool neg, uint base) {
|
|
assert(base < 36);
|
|
|
|
const char *cur = str;
|
|
const char *top = str + len;
|
|
|
|
mpz_need_dig(z, len * 8 / DIG_SIZE + 1);
|
|
|
|
if (neg) {
|
|
z->neg = 1;
|
|
} else {
|
|
z->neg = 0;
|
|
}
|
|
|
|
z->len = 0;
|
|
for (; cur < top; ++cur) { // XXX UTF8 next char
|
|
//uint v = char_to_numeric(cur#); // XXX UTF8 get char
|
|
uint v = *cur;
|
|
if ('0' <= v && v <= '9') {
|
|
v -= '0';
|
|
} else if ('A' <= v && v <= 'Z') {
|
|
v -= 'A' - 10;
|
|
} else if ('a' <= v && v <= 'z') {
|
|
v -= 'a' - 10;
|
|
} else {
|
|
break;
|
|
}
|
|
if (v >= base) {
|
|
break;
|
|
}
|
|
z->len = mpn_mul_dig_add_dig(z->dig, z->len, base, v);
|
|
}
|
|
|
|
return cur - str;
|
|
}
|
|
|
|
bool mpz_is_zero(const mpz_t *z) {
|
|
return z->len == 0;
|
|
}
|
|
|
|
bool mpz_is_pos(const mpz_t *z) {
|
|
return z->len > 0 && z->neg == 0;
|
|
}
|
|
|
|
bool mpz_is_neg(const mpz_t *z) {
|
|
return z->len > 0 && z->neg != 0;
|
|
}
|
|
|
|
bool mpz_is_odd(const mpz_t *z) {
|
|
return z->len > 0 && (z->dig[0] & 1) != 0;
|
|
}
|
|
|
|
bool mpz_is_even(const mpz_t *z) {
|
|
return z->len == 0 || (z->dig[0] & 1) == 0;
|
|
}
|
|
|
|
int mpz_cmp(const mpz_t *z1, const mpz_t *z2) {
|
|
int cmp = z2->neg - z1->neg;
|
|
if (cmp != 0) {
|
|
return cmp;
|
|
}
|
|
cmp = mpn_cmp(z1->dig, z1->len, z2->dig, z2->len);
|
|
if (z1->neg != 0) {
|
|
cmp = -cmp;
|
|
}
|
|
return cmp;
|
|
}
|
|
|
|
#if 0
|
|
// obsolete
|
|
// compares mpz with an integer that fits within DIG_SIZE bits
|
|
int mpz_cmp_sml_int(const mpz_t *z, machine_int_t sml_int) {
|
|
int cmp;
|
|
if (z->neg == 0) {
|
|
if (sml_int < 0) return 1;
|
|
if (sml_int == 0) {
|
|
if (z->len == 0) return 0;
|
|
return 1;
|
|
}
|
|
if (z->len == 0) return -1;
|
|
assert(sml_int < (1 << DIG_SIZE));
|
|
if (z->len != 1) return 1;
|
|
cmp = z->dig[0] - sml_int;
|
|
} else {
|
|
if (sml_int > 0) return -1;
|
|
if (sml_int == 0) {
|
|
if (z->len == 0) return 0;
|
|
return -1;
|
|
}
|
|
if (z->len == 0) return 1;
|
|
assert(sml_int > -(1 << DIG_SIZE));
|
|
if (z->len != 1) return -1;
|
|
cmp = -z->dig[0] - sml_int;
|
|
}
|
|
if (cmp < 0) return -1;
|
|
if (cmp > 0) return 1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
these functions are unused
|
|
|
|
/* returns abs(z)
|
|
*/
|
|
mpz_t *mpz_abs(const mpz_t *z) {
|
|
mpz_t *z2 = mpz_clone(z);
|
|
z2->neg = 0;
|
|
return z2;
|
|
}
|
|
|
|
/* returns -z
|
|
*/
|
|
mpz_t *mpz_neg(const mpz_t *z) {
|
|
mpz_t *z2 = mpz_clone(z);
|
|
z2->neg = 1 - z2->neg;
|
|
return z2;
|
|
}
|
|
|
|
/* returns lhs + rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_add(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_add_inpl(z, lhs, rhs);
|
|
return z;
|
|
}
|
|
|
|
/* returns lhs - rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_sub(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_sub_inpl(z, lhs, rhs);
|
|
return z;
|
|
}
|
|
|
|
/* returns lhs * rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_mul(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_mul_inpl(z, lhs, rhs);
|
|
return z;
|
|
}
|
|
|
|
/* returns lhs ** rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_pow(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t *z = mpz_zero();
|
|
mpz_pow_inpl(z, lhs, rhs);
|
|
return z;
|
|
}
|
|
#endif
|
|
|
|
/* computes dest = abs(z)
|
|
can have dest, z the same
|
|
*/
|
|
void mpz_abs_inpl(mpz_t *dest, const mpz_t *z) {
|
|
if (dest != z) {
|
|
mpz_set(dest, z);
|
|
}
|
|
dest->neg = 0;
|
|
}
|
|
|
|
/* computes dest = -z
|
|
can have dest, z the same
|
|
*/
|
|
void mpz_neg_inpl(mpz_t *dest, const mpz_t *z) {
|
|
if (dest != z) {
|
|
mpz_set(dest, z);
|
|
}
|
|
dest->neg = 1 - dest->neg;
|
|
}
|
|
|
|
/* computes dest = ~z (= -z - 1)
|
|
can have dest, z the same
|
|
*/
|
|
void mpz_not_inpl(mpz_t *dest, const mpz_t *z) {
|
|
if (dest != z) {
|
|
mpz_set(dest, z);
|
|
}
|
|
if (dest->neg) {
|
|
dest->neg = 0;
|
|
mpz_dig_t k = 1;
|
|
dest->len = mpn_sub(dest->dig, dest->dig, dest->len, &k, 1);
|
|
} else {
|
|
mpz_dig_t k = 1;
|
|
dest->len = mpn_add(dest->dig, dest->dig, dest->len, &k, 1);
|
|
dest->neg = 1;
|
|
}
|
|
}
|
|
|
|
/* computes dest = lhs << rhs
|
|
can have dest, lhs the same
|
|
*/
|
|
void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
|
|
if (lhs->len == 0 || rhs == 0) {
|
|
mpz_set(dest, lhs);
|
|
} else if (rhs < 0) {
|
|
mpz_shr_inpl(dest, lhs, -rhs);
|
|
} else {
|
|
mpz_need_dig(dest, lhs->len + (rhs + DIG_SIZE - 1) / DIG_SIZE);
|
|
dest->len = mpn_shl(dest->dig, lhs->dig, lhs->len, rhs);
|
|
dest->neg = lhs->neg;
|
|
}
|
|
}
|
|
|
|
/* computes dest = lhs >> rhs
|
|
can have dest, lhs the same
|
|
*/
|
|
void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, machine_int_t rhs) {
|
|
if (lhs->len == 0 || rhs == 0) {
|
|
mpz_set(dest, lhs);
|
|
} else if (rhs < 0) {
|
|
mpz_shl_inpl(dest, lhs, -rhs);
|
|
} else {
|
|
mpz_need_dig(dest, lhs->len);
|
|
dest->len = mpn_shr(dest->dig, lhs->dig, lhs->len, rhs);
|
|
dest->neg = lhs->neg;
|
|
if (dest->neg) {
|
|
// arithmetic shift right, rounding to negative infinity
|
|
uint n_whole = rhs / DIG_SIZE;
|
|
uint n_part = rhs % DIG_SIZE;
|
|
mpz_dig_t round_up = 0;
|
|
for (uint i = 0; i < lhs->len && i < n_whole; i++) {
|
|
if (lhs->dig[i] != 0) {
|
|
round_up = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (n_whole < lhs->len && (lhs->dig[n_whole] & ((1 << n_part) - 1)) != 0) {
|
|
round_up = 1;
|
|
}
|
|
if (round_up) {
|
|
dest->len = mpn_add(dest->dig, dest->dig, dest->len, &round_up, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* computes dest = lhs + rhs
|
|
can have dest, lhs, rhs the same
|
|
*/
|
|
void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
|
|
if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
|
|
const mpz_t *temp = lhs;
|
|
lhs = rhs;
|
|
rhs = temp;
|
|
}
|
|
|
|
if (lhs->neg == rhs->neg) {
|
|
mpz_need_dig(dest, lhs->len + 1);
|
|
dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
|
|
} else {
|
|
mpz_need_dig(dest, lhs->len);
|
|
dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
|
|
}
|
|
|
|
dest->neg = lhs->neg;
|
|
}
|
|
|
|
/* computes dest = lhs - rhs
|
|
can have dest, lhs, rhs the same
|
|
*/
|
|
void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
|
|
bool neg = false;
|
|
|
|
if (mpn_cmp(lhs->dig, lhs->len, rhs->dig, rhs->len) < 0) {
|
|
const mpz_t *temp = lhs;
|
|
lhs = rhs;
|
|
rhs = temp;
|
|
neg = true;
|
|
}
|
|
|
|
if (lhs->neg != rhs->neg) {
|
|
mpz_need_dig(dest, lhs->len + 1);
|
|
dest->len = mpn_add(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
|
|
} else {
|
|
mpz_need_dig(dest, lhs->len);
|
|
dest->len = mpn_sub(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
|
|
}
|
|
|
|
if (neg) {
|
|
dest->neg = 1 - lhs->neg;
|
|
} else {
|
|
dest->neg = lhs->neg;
|
|
}
|
|
}
|
|
|
|
/* computes dest = lhs * rhs
|
|
can have dest, lhs, rhs the same
|
|
*/
|
|
void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs)
|
|
{
|
|
if (lhs->len == 0 || rhs->len == 0) {
|
|
mpz_set_from_int(dest, 0);
|
|
return;
|
|
}
|
|
|
|
mpz_t *temp = NULL;
|
|
if (lhs == dest) {
|
|
lhs = temp = mpz_clone(lhs);
|
|
if (rhs == dest) {
|
|
rhs = lhs;
|
|
}
|
|
} else if (rhs == dest) {
|
|
rhs = temp = mpz_clone(rhs);
|
|
}
|
|
|
|
mpz_need_dig(dest, lhs->len + rhs->len); // min mem l+r-1, max mem l+r
|
|
memset(dest->dig, 0, dest->alloc * sizeof(mpz_dig_t));
|
|
dest->len = mpn_mul(dest->dig, lhs->dig, lhs->len, rhs->dig, rhs->len);
|
|
|
|
if (lhs->neg == rhs->neg) {
|
|
dest->neg = 0;
|
|
} else {
|
|
dest->neg = 1;
|
|
}
|
|
|
|
mpz_free(temp);
|
|
}
|
|
|
|
/* computes dest = lhs ** rhs
|
|
can have dest, lhs, rhs the same
|
|
*/
|
|
void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs) {
|
|
if (lhs->len == 0 || rhs->neg != 0) {
|
|
mpz_set_from_int(dest, 0);
|
|
return;
|
|
}
|
|
|
|
if (rhs->len == 0) {
|
|
mpz_set_from_int(dest, 1);
|
|
return;
|
|
}
|
|
|
|
mpz_t *x = mpz_clone(lhs);
|
|
mpz_t *n = mpz_clone(rhs);
|
|
|
|
mpz_set_from_int(dest, 1);
|
|
|
|
while (n->len > 0) {
|
|
if (mpz_is_odd(n)) {
|
|
mpz_mul_inpl(dest, dest, x);
|
|
}
|
|
mpz_mul_inpl(x, x, x);
|
|
n->len = mpn_shr(n->dig, n->dig, n->len, 1);
|
|
}
|
|
|
|
mpz_free(x);
|
|
mpz_free(n);
|
|
}
|
|
|
|
/* computes gcd(z1, z2)
|
|
based on Knuth's modified gcd algorithm (I think?)
|
|
gcd(z1, z2) >= 0
|
|
gcd(0, 0) = 0
|
|
gcd(z, 0) = abs(z)
|
|
*/
|
|
mpz_t *mpz_gcd(const mpz_t *z1, const mpz_t *z2) {
|
|
if (z1->len == 0) {
|
|
mpz_t *a = mpz_clone(z2);
|
|
a->neg = 0;
|
|
return a;
|
|
} else if (z2->len == 0) {
|
|
mpz_t *a = mpz_clone(z1);
|
|
a->neg = 0;
|
|
return a;
|
|
}
|
|
|
|
mpz_t *a = mpz_clone(z1);
|
|
mpz_t *b = mpz_clone(z2);
|
|
mpz_t c; mpz_init_zero(&c);
|
|
a->neg = 0;
|
|
b->neg = 0;
|
|
|
|
for (;;) {
|
|
if (mpz_cmp(a, b) < 0) {
|
|
if (a->len == 0) {
|
|
mpz_free(a);
|
|
mpz_deinit(&c);
|
|
return b;
|
|
}
|
|
mpz_t *t = a; a = b; b = t;
|
|
}
|
|
if (!(b->len >= 2 || (b->len == 1 && b->dig[0] > 1))) { // compute b > 0; could be mpz_cmp_small_int(b, 1) > 0
|
|
break;
|
|
}
|
|
mpz_set(&c, b);
|
|
do {
|
|
mpz_add_inpl(&c, &c, &c);
|
|
} while (mpz_cmp(&c, a) <= 0);
|
|
c.len = mpn_shr(c.dig, c.dig, c.len, 1);
|
|
mpz_sub_inpl(a, a, &c);
|
|
}
|
|
|
|
mpz_deinit(&c);
|
|
|
|
if (b->len == 1 && b->dig[0] == 1) { // compute b == 1; could be mpz_cmp_small_int(b, 1) == 0
|
|
mpz_free(a);
|
|
return b;
|
|
} else {
|
|
mpz_free(b);
|
|
return a;
|
|
}
|
|
}
|
|
|
|
/* computes lcm(z1, z2)
|
|
= abs(z1) / gcd(z1, z2) * abs(z2)
|
|
lcm(z1, z1) >= 0
|
|
lcm(0, 0) = 0
|
|
lcm(z, 0) = 0
|
|
*/
|
|
mpz_t *mpz_lcm(const mpz_t *z1, const mpz_t *z2)
|
|
{
|
|
if (z1->len == 0 || z2->len == 0)
|
|
return mpz_zero();
|
|
|
|
mpz_t *gcd = mpz_gcd(z1, z2);
|
|
mpz_t *quo = mpz_zero();
|
|
mpz_t *rem = mpz_zero();
|
|
mpz_divmod_inpl(quo, rem, z1, gcd);
|
|
mpz_mul_inpl(rem, quo, z2);
|
|
mpz_free(gcd);
|
|
mpz_free(quo);
|
|
rem->neg = 0;
|
|
return rem;
|
|
}
|
|
|
|
/* computes new integers in quo and rem such that:
|
|
quo * rhs + rem = lhs
|
|
0 <= rem < rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
void mpz_divmod(const mpz_t *lhs, const mpz_t *rhs, mpz_t **quo, mpz_t **rem) {
|
|
*quo = mpz_zero();
|
|
*rem = mpz_zero();
|
|
mpz_divmod_inpl(*quo, *rem, lhs, rhs);
|
|
}
|
|
|
|
/* computes new integers in quo and rem such that:
|
|
quo * rhs + rem = lhs
|
|
0 <= rem < rhs
|
|
can have lhs, rhs the same
|
|
*/
|
|
void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs) {
|
|
if (rhs->len == 0) {
|
|
mpz_set_from_int(dest_quo, 0);
|
|
mpz_set_from_int(dest_rem, 0);
|
|
return;
|
|
}
|
|
|
|
mpz_need_dig(dest_quo, lhs->len + 1); // +1 necessary?
|
|
memset(dest_quo->dig, 0, (lhs->len + 1) * sizeof(mpz_dig_t));
|
|
dest_quo->len = 0;
|
|
mpz_need_dig(dest_rem, lhs->len + 1); // +1 necessary?
|
|
mpz_set(dest_rem, lhs);
|
|
//rhs->dig[rhs->len] = 0;
|
|
mpn_div(dest_rem->dig, &dest_rem->len, rhs->dig, rhs->len, dest_quo->dig, &dest_quo->len);
|
|
|
|
if (lhs->neg != rhs->neg) {
|
|
dest_quo->neg = 1;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
these functions are unused
|
|
|
|
/* computes floor(lhs / rhs)
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t *quo = mpz_zero();
|
|
mpz_t rem; mpz_init_zero(&rem);
|
|
mpz_divmod_inpl(quo, &rem, lhs, rhs);
|
|
mpz_deinit(&rem);
|
|
return quo;
|
|
}
|
|
|
|
/* computes lhs % rhs ( >= 0)
|
|
can have lhs, rhs the same
|
|
*/
|
|
mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
|
|
mpz_t quo; mpz_init_zero(&quo);
|
|
mpz_t *rem = mpz_zero();
|
|
mpz_divmod_inpl(&quo, rem, lhs, rhs);
|
|
mpz_deinit(&quo);
|
|
return rem;
|
|
}
|
|
#endif
|
|
|
|
machine_int_t mpz_as_int(const mpz_t *i) {
|
|
machine_int_t val = 0;
|
|
mpz_dig_t *d = i->dig + i->len;
|
|
|
|
while (--d >= i->dig) {
|
|
machine_int_t oldval = val;
|
|
val = (val << DIG_SIZE) | *d;
|
|
if (val < oldval) {
|
|
// TODO need better handling of conversion overflow
|
|
if (i->neg == 0) {
|
|
return 0x7fffffff;
|
|
} else {
|
|
return 0x80000000;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i->neg != 0) {
|
|
val = -val;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
#if MICROPY_ENABLE_FLOAT
|
|
mp_float_t mpz_as_float(const mpz_t *i) {
|
|
mp_float_t val = 0;
|
|
mpz_dig_t *d = i->dig + i->len;
|
|
|
|
while (--d >= i->dig) {
|
|
val = val * (1 << DIG_SIZE) + *d;
|
|
}
|
|
|
|
if (i->neg != 0) {
|
|
val = -val;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
#endif
|
|
|
|
uint mpz_as_str_size(const mpz_t *i, uint base) {
|
|
if (base < 2 || base > 32) {
|
|
return 0;
|
|
}
|
|
|
|
return i->len * DIG_SIZE / log_base2_floor[base] + 2 + 1; // +1 for null byte termination
|
|
}
|
|
|
|
char *mpz_as_str(const mpz_t *i, uint base) {
|
|
char *s = m_new(char, mpz_as_str_size(i, base));
|
|
mpz_as_str_inpl(i, base, s);
|
|
return s;
|
|
}
|
|
|
|
// assumes enough space as calculated by mpz_as_str_size
|
|
// returns length of string, not including null byte
|
|
uint mpz_as_str_inpl(const mpz_t *i, uint base, char *str) {
|
|
if (str == NULL || base < 2 || base > 32) {
|
|
str[0] = 0;
|
|
return 0;
|
|
}
|
|
|
|
uint ilen = i->len;
|
|
|
|
if (ilen == 0) {
|
|
str[0] = '0';
|
|
str[1] = 0;
|
|
return 1;
|
|
}
|
|
|
|
// make a copy of mpz digits
|
|
mpz_dig_t *dig = m_new(mpz_dig_t, ilen);
|
|
memcpy(dig, i->dig, ilen * sizeof(mpz_dig_t));
|
|
|
|
// convert
|
|
char *s = str;
|
|
bool done;
|
|
do {
|
|
mpz_dig_t *d = dig + ilen;
|
|
mpz_dbl_dig_t a = 0;
|
|
|
|
// compute next remainder
|
|
while (--d >= dig) {
|
|
a = (a << DIG_SIZE) | *d;
|
|
*d = a / base;
|
|
a %= base;
|
|
}
|
|
|
|
// convert to character
|
|
a += '0';
|
|
if (a > '9') {
|
|
a += 'a' - '9' - 1;
|
|
}
|
|
*s++ = a;
|
|
|
|
// check if number is zero
|
|
done = true;
|
|
for (d = dig; d < dig + ilen; ++d) {
|
|
if (*d != 0) {
|
|
done = false;
|
|
break;
|
|
}
|
|
}
|
|
} while (!done);
|
|
|
|
if (i->neg != 0) {
|
|
*s++ = '-';
|
|
}
|
|
|
|
// reverse string
|
|
for (char *u = str, *v = s - 1; u < v; ++u, --v) {
|
|
char temp = *u;
|
|
*u = *v;
|
|
*v = temp;
|
|
}
|
|
|
|
s[0] = 0; // null termination
|
|
|
|
return s - str;
|
|
}
|
|
|
|
#endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ
|