mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 18:05:21 +00:00
2117 lines
56 KiB
C++
2117 lines
56 KiB
C++
/*++
|
|
Copyright (c) 2006 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
mpz.cpp
|
|
|
|
Abstract:
|
|
|
|
<abstract>
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2010-06-17.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include<sstream>
|
|
#include"mpz.h"
|
|
#include"buffer.h"
|
|
#include"trace.h"
|
|
#include"hash.h"
|
|
#include"bit_util.h"
|
|
|
|
#if defined(_MP_MSBIGNUM)
|
|
#define COMPILER COMPILER_VC
|
|
#ifndef NDEBUG
|
|
#define NDEBUG
|
|
#endif
|
|
#ifdef ARRAYSIZE
|
|
#undef ARRAYSIZE
|
|
#endif
|
|
#include "..\msbig_rational\msbignum.h"
|
|
#elif defined(_MP_INTERNAL)
|
|
#include"mpn.h"
|
|
#elif defined(_MP_GMP)
|
|
#include<gmp.h>
|
|
#else
|
|
#error No multi-precision library selected.
|
|
#endif
|
|
|
|
// Available GCD algorithms
|
|
// #define EUCLID_GCD
|
|
// #define BINARY_GCD
|
|
// #define LS_BINARY_GCD
|
|
// #define LEHMER_GCD
|
|
|
|
#if defined(_MP_GMP) || (defined(_MP_MSBIGNUM) && defined(_AMD64_))
|
|
// Use LEHMER only if not using GMP
|
|
// LEHMER assumes 32-bit digits, so it cannot be used with MSBIGNUM library + 64-bit binary
|
|
#define EUCLID_GCD
|
|
#else
|
|
#define LEHMER_GCD
|
|
#endif
|
|
|
|
template<typename T>
|
|
static T gcd_core(T u, T v) {
|
|
if (u == 0)
|
|
return v;
|
|
if (v == 0)
|
|
return u;
|
|
|
|
int k;
|
|
|
|
for (k = 0; ((u | v) & 1) == 0; ++k) {
|
|
u >>= 1;
|
|
v >>= 1;
|
|
}
|
|
|
|
while ((u & 1) == 0)
|
|
u >>= 1;
|
|
|
|
do {
|
|
while ((v & 1) == 0)
|
|
v >>= 1;
|
|
|
|
if (u < v) {
|
|
v -= u;
|
|
}
|
|
else {
|
|
T diff = u - v;
|
|
u = v;
|
|
v = diff;
|
|
}
|
|
v >>= 1;
|
|
} while (v != 0);
|
|
|
|
return u << k;
|
|
}
|
|
|
|
unsigned u_gcd(unsigned u, unsigned v) { return gcd_core(u, v); }
|
|
uint64 u64_gcd(uint64 u, uint64 v) { return gcd_core(u, v); }
|
|
|
|
template<bool SYNCH>
|
|
mpz_manager<SYNCH>::mpz_manager():
|
|
m_allocator("mpz_manager") {
|
|
if (SYNCH)
|
|
omp_init_nest_lock(&m_lock);
|
|
#ifndef _MP_GMP
|
|
if (sizeof(digit_t) == sizeof(uint64)) {
|
|
// 64-bit machine
|
|
m_init_cell_capacity = 4;
|
|
}
|
|
else {
|
|
m_init_cell_capacity = 6;
|
|
}
|
|
for (unsigned i = 0; i < 2; i++) {
|
|
m_tmp[i] = allocate(m_init_cell_capacity);
|
|
m_arg[i] = allocate(m_init_cell_capacity);
|
|
m_arg[i]->m_size = 1;
|
|
}
|
|
set(m_int_min, -static_cast<int64>(INT_MIN));
|
|
#else
|
|
// GMP
|
|
mpz_init(m_tmp);
|
|
mpz_init(m_tmp2);
|
|
mpz_init(m_two32);
|
|
mpz_set_ui(m_two32, UINT_MAX);
|
|
mpz_set_ui(m_tmp, 1);
|
|
mpz_add(m_two32, m_two32, m_tmp);
|
|
m_arg[0] = allocate();
|
|
m_arg[1] = allocate();
|
|
mpz_init(m_uint64_max);
|
|
unsigned max_l = static_cast<unsigned>(UINT64_MAX);
|
|
unsigned max_h = static_cast<unsigned>(UINT64_MAX >> 32);
|
|
mpz_set_ui(m_uint64_max, max_h);
|
|
mpz_mul(m_uint64_max, m_two32, m_uint64_max);
|
|
mpz_set_ui(m_tmp, max_l);
|
|
mpz_add(m_uint64_max, m_uint64_max, m_tmp);
|
|
mpz_init(m_int64_max);
|
|
|
|
max_l = static_cast<unsigned>(INT64_MAX % static_cast<int64>(UINT_MAX));
|
|
max_h = static_cast<unsigned>(INT64_MAX / static_cast<int64>(UINT_MAX));
|
|
mpz_set_ui(m_int64_max, max_h);
|
|
mpz_set_ui(m_tmp, UINT_MAX);
|
|
mpz_mul(m_int64_max, m_tmp, m_int64_max);
|
|
mpz_set_ui(m_tmp, max_l);
|
|
mpz_add(m_int64_max, m_tmp, m_int64_max);
|
|
#endif
|
|
|
|
mpz one(1);
|
|
set(m_two64, UINT64_MAX);
|
|
add(m_two64, one, m_two64);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
mpz_manager<SYNCH>::~mpz_manager() {
|
|
del(m_two64);
|
|
#ifndef _MP_GMP
|
|
del(m_int_min);
|
|
for (unsigned i = 0; i < 2; i++) {
|
|
deallocate(m_tmp[i]);
|
|
deallocate(m_arg[i]);
|
|
}
|
|
#else
|
|
mpz_clear(m_tmp);
|
|
mpz_clear(m_tmp2);
|
|
mpz_clear(m_two32);
|
|
deallocate(m_arg[0]);
|
|
deallocate(m_arg[1]);
|
|
mpz_clear(m_uint64_max);
|
|
mpz_clear(m_int64_max);
|
|
#endif
|
|
if (SYNCH)
|
|
omp_destroy_nest_lock(&m_lock);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::set_big_i64(mpz & c, int64 v) {
|
|
#ifndef _MP_GMP
|
|
if (is_small(c)) {
|
|
c.m_ptr = allocate(m_init_cell_capacity);
|
|
}
|
|
SASSERT(capacity(c) >= m_init_cell_capacity);
|
|
uint64 _v;
|
|
if (v < 0) {
|
|
_v = -v;
|
|
c.m_val = -1;
|
|
}
|
|
else {
|
|
_v = v;
|
|
c.m_val = 1;
|
|
}
|
|
if (sizeof(digit_t) == sizeof(uint64)) {
|
|
// 64-bit machine
|
|
digits(c)[0] = static_cast<digit_t>(_v);
|
|
c.m_ptr->m_size = 1;
|
|
}
|
|
else {
|
|
// 32-bit machine
|
|
digits(c)[0] = static_cast<unsigned>(_v);
|
|
digits(c)[1] = static_cast<unsigned>(_v >> 32);
|
|
c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2;
|
|
}
|
|
#else
|
|
if (is_small(c)) {
|
|
c.m_ptr = allocate();
|
|
}
|
|
uint64 _v;
|
|
bool sign;
|
|
if (v < 0) {
|
|
_v = -v;
|
|
sign = true;
|
|
}
|
|
else {
|
|
_v = v;
|
|
sign = false;
|
|
}
|
|
mpz_set_ui(*c.m_ptr, static_cast<unsigned>(_v));
|
|
mpz_set_ui(m_tmp, static_cast<unsigned>(_v >> 32));
|
|
mpz_mul(m_tmp, m_tmp, m_two32);
|
|
mpz_add(*c.m_ptr, *c.m_ptr, m_tmp);
|
|
if (sign)
|
|
mpz_neg(*c.m_ptr, *c.m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::set_big_ui64(mpz & c, uint64 v) {
|
|
#ifndef _MP_GMP
|
|
if (is_small(c)) {
|
|
c.m_ptr = allocate(m_init_cell_capacity);
|
|
}
|
|
SASSERT(capacity(c) >= m_init_cell_capacity);
|
|
c.m_val = 1;
|
|
if (sizeof(digit_t) == sizeof(uint64)) {
|
|
// 64-bit machine
|
|
digits(c)[0] = static_cast<digit_t>(v);
|
|
c.m_ptr->m_size = 1;
|
|
}
|
|
else {
|
|
// 32-bit machine
|
|
digits(c)[0] = static_cast<unsigned>(v);
|
|
digits(c)[1] = static_cast<unsigned>(v >> 32);
|
|
c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2;
|
|
}
|
|
#else
|
|
if (is_small(c)) {
|
|
c.m_ptr = allocate();
|
|
}
|
|
mpz_set_ui(*c.m_ptr, static_cast<unsigned>(v));
|
|
mpz_set_ui(m_tmp, static_cast<unsigned>(v >> 32));
|
|
mpz_mul(m_tmp, m_tmp, m_two32);
|
|
mpz_add(*c.m_ptr, *c.m_ptr, m_tmp);
|
|
#endif
|
|
}
|
|
|
|
#ifndef _MP_GMP
|
|
template<bool SYNCH>
|
|
template<int IDX>
|
|
void mpz_manager<SYNCH>::set(mpz & a, int sign, unsigned sz) {
|
|
#if 0
|
|
static unsigned max_sz = 0;
|
|
if (sz > max_sz) {
|
|
max_sz = sz;
|
|
verbose_stream() << "max_sz: " << max_sz << "\n";
|
|
}
|
|
#endif
|
|
unsigned i = sz;
|
|
for (; i > 0; --i) {
|
|
if (m_tmp[IDX]->m_digits[i-1] != 0)
|
|
break;
|
|
}
|
|
|
|
if (i == 0) {
|
|
// m_tmp[IDX] is zero
|
|
reset(a);
|
|
return;
|
|
}
|
|
|
|
if (i == 1 && m_tmp[IDX]->m_digits[0] <= INT_MAX) {
|
|
// m_tmp[IDX] fits is a fixnum
|
|
del(a);
|
|
a.m_val = sign < 0 ? -static_cast<int>(m_tmp[IDX]->m_digits[0]) : static_cast<int>(m_tmp[IDX]->m_digits[0]);
|
|
return;
|
|
}
|
|
|
|
a.m_val = sign;
|
|
std::swap(a.m_ptr, m_tmp[IDX]);
|
|
a.m_ptr->m_size = i;
|
|
if (!m_tmp[IDX]) // 'a' was a small number
|
|
m_tmp[IDX] = allocate(m_init_cell_capacity);
|
|
}
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::set(mpz & a, char const * val) {
|
|
reset(a);
|
|
mpz ten(10);
|
|
mpz tmp;
|
|
char const * str = val;
|
|
bool sign = false;
|
|
while (str[0] == ' ') ++str;
|
|
if (str[0] == '-')
|
|
sign = true;
|
|
while (str[0]) {
|
|
if ('0' <= str[0] && str[0] <= '9') {
|
|
SASSERT(str[0] - '0' <= 9);
|
|
mul(a, ten, tmp);
|
|
add(tmp, mk_z(str[0] - '0'), a);
|
|
}
|
|
++str;
|
|
}
|
|
del(tmp);
|
|
if (sign)
|
|
neg(a);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::set(mpz & target, unsigned sz, digit_t const * digits) {
|
|
// remove zero digits
|
|
while (sz > 0 && digits[sz - 1] == 0)
|
|
sz--;
|
|
if (sz == 0)
|
|
reset(target);
|
|
else if (sz == 1)
|
|
set(target, digits[0]);
|
|
else {
|
|
#ifndef _MP_GMP
|
|
target.m_val = 1; // number is positive.
|
|
if (is_small(target)) {
|
|
unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz;
|
|
target.m_ptr = allocate(c);
|
|
target.m_ptr->m_size = sz;
|
|
target.m_ptr->m_capacity = c;
|
|
memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz);
|
|
}
|
|
else {
|
|
if (capacity(target) < sz) {
|
|
SASSERT(sz > m_init_cell_capacity);
|
|
deallocate(target.m_ptr);
|
|
target.m_ptr = allocate(sz);
|
|
target.m_ptr->m_size = sz;
|
|
target.m_ptr->m_capacity = sz;
|
|
memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz);
|
|
}
|
|
else {
|
|
target.m_ptr->m_size = sz;
|
|
memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz);
|
|
}
|
|
}
|
|
#else
|
|
mk_big(target);
|
|
// reset
|
|
mpz_set_ui(*target.m_ptr, digits[sz - 1]);
|
|
SASSERT(sz > 0);
|
|
unsigned i = sz - 1;
|
|
while (i > 0) {
|
|
--i;
|
|
mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32);
|
|
mpz_set_ui(m_tmp, digits[i]);
|
|
mpz_add(*target.m_ptr, *target.m_ptr, m_tmp);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifndef _MP_GMP
|
|
template<bool SYNCH>
|
|
template<bool SUB>
|
|
void mpz_manager<SYNCH>::big_add_sub(mpz const & a, mpz const & b, mpz & c) {
|
|
int sign_a;
|
|
int sign_b;
|
|
mpz_cell * cell_a;
|
|
mpz_cell * cell_b;
|
|
get_sign_cell<0>(a, sign_a, cell_a);
|
|
get_sign_cell<1>(b, sign_b, cell_b);
|
|
if (SUB)
|
|
sign_b = -sign_b;
|
|
size_t real_sz;
|
|
if (sign_a == sign_b) {
|
|
unsigned sz = max(cell_a->m_size, cell_b->m_size)+1;
|
|
ensure_tmp_capacity<0>(sz);
|
|
add_full(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size,
|
|
m_tmp[0]->m_digits,
|
|
sz,
|
|
&real_sz,
|
|
0);
|
|
SASSERT(real_sz <= sz);
|
|
set<0>(c, sign_a, static_cast<unsigned>(real_sz));
|
|
}
|
|
else {
|
|
digit_t borrow;
|
|
int r = compare_diff(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size);
|
|
if (r == 0) {
|
|
reset(c);
|
|
}
|
|
else if (r < 0) {
|
|
// a < b
|
|
unsigned sz = cell_b->m_size;
|
|
ensure_tmp_capacity<0>(sz);
|
|
sub_diff(cell_b->m_digits,
|
|
cell_b->m_size,
|
|
cell_a->m_digits,
|
|
cell_a->m_size,
|
|
m_tmp[0]->m_digits,
|
|
&borrow,
|
|
0);
|
|
SASSERT(borrow == 0);
|
|
set<0>(c, sign_b, sz);
|
|
}
|
|
else {
|
|
// a > b
|
|
unsigned sz = cell_a->m_size;
|
|
ensure_tmp_capacity<0>(sz);
|
|
sub_diff(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size,
|
|
m_tmp[0]->m_digits,
|
|
&borrow,
|
|
0);
|
|
SASSERT(borrow == 0);
|
|
set<0>(c, sign_a, sz);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_add(mpz const & a, mpz const & b, mpz & c) {
|
|
#ifndef _MP_GMP
|
|
big_add_sub<false>(a, b, c);
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_add(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_sub(mpz const & a, mpz const & b, mpz & c) {
|
|
#ifndef _MP_GMP
|
|
big_add_sub<true>(a, b, c);
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_sub(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_mul(mpz const & a, mpz const & b, mpz & c) {
|
|
#ifndef _MP_GMP
|
|
int sign_a;
|
|
int sign_b;
|
|
mpz_cell * cell_a;
|
|
mpz_cell * cell_b;
|
|
get_sign_cell<0>(a, sign_a, cell_a);
|
|
get_sign_cell<1>(b, sign_b, cell_b);
|
|
unsigned sz = cell_a->m_size + cell_b->m_size;
|
|
ensure_tmp_capacity<0>(sz);
|
|
multiply(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size,
|
|
m_tmp[0]->m_digits,
|
|
0);
|
|
set<0>(c, sign_a == sign_b ? 1 : -1, sz);
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_mul(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
#ifndef _MP_GMP
|
|
template<bool SYNCH>
|
|
template<qr_mode MODE>
|
|
void mpz_manager<SYNCH>::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) {
|
|
/*
|
|
+26 / +7 = +3, remainder is +5
|
|
-26 / +7 = -3, remainder is -5
|
|
+26 / -7 = -3, remainder is +5
|
|
-26 / -7 = +3, remainder is -5
|
|
*/
|
|
int sign_a;
|
|
int sign_b;
|
|
mpz_cell * cell_a;
|
|
mpz_cell * cell_b;
|
|
get_sign_cell<0>(a, sign_a, cell_a);
|
|
get_sign_cell<1>(b, sign_b, cell_b);
|
|
if (cell_b->m_size > cell_a->m_size) {
|
|
if (MODE == REM_ONLY || MODE == QUOT_AND_REM)
|
|
set(r, a);
|
|
if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM)
|
|
reset(q);
|
|
return;
|
|
}
|
|
unsigned q_sz = cell_a->m_size - cell_b->m_size + 1;
|
|
unsigned r_sz = cell_b->m_size;
|
|
ensure_tmp_capacity<0>(q_sz);
|
|
ensure_tmp_capacity<1>(r_sz);
|
|
divide(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size,
|
|
reciprocal_1_NULL,
|
|
m_tmp[0]->m_digits,
|
|
m_tmp[1]->m_digits,
|
|
0);
|
|
if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM)
|
|
set<0>(q, sign_a == sign_b ? 1 : -1, q_sz);
|
|
if (MODE == REM_ONLY || MODE == QUOT_AND_REM)
|
|
set<1>(r, sign_a, r_sz);
|
|
}
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) {
|
|
#ifndef _MP_GMP
|
|
quot_rem_core<QUOT_AND_REM>(a, b, q, r);
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(q);
|
|
mk_big(r);
|
|
mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_div(mpz const & a, mpz const & b, mpz & c) {
|
|
#ifndef _MP_GMP
|
|
mpz dummy;
|
|
quot_rem_core<QUOT_ONLY>(a, b, c, dummy);
|
|
SASSERT(is_zero(dummy));
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_tdiv_q(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_rem(mpz const & a, mpz const & b, mpz & c) {
|
|
#ifndef _MP_GMP
|
|
mpz dummy;
|
|
quot_rem_core<REM_ONLY>(a, b, dummy, c);
|
|
SASSERT(is_zero(dummy));
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_tdiv_r(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::gcd(mpz const & a, mpz const & b, mpz & c) {
|
|
if (is_small(a) && is_small(b)) {
|
|
int _a = a.m_val;
|
|
int _b = b.m_val;
|
|
if (_a < 0) _a = -_a;
|
|
if (_b < 0) _b = -_b;
|
|
unsigned r = u_gcd(_a, _b);
|
|
// Remark: r is (INT_MAX + 1)
|
|
// If a == b == INT_MIN
|
|
set(c, r);
|
|
}
|
|
else {
|
|
#ifdef _MP_GMP
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_gcd(*c.m_ptr, *arg0, *arg1);
|
|
return;
|
|
#endif
|
|
if (is_zero(a)) {
|
|
set(c, b);
|
|
abs(c);
|
|
return;
|
|
}
|
|
if (is_zero(b)) {
|
|
set(c, a);
|
|
abs(c);
|
|
return;
|
|
}
|
|
#ifdef BINARY_GCD
|
|
// Binary GCD for big numbers
|
|
// - It doesn't use division
|
|
// - The initial experiments, don't show any performance improvement
|
|
// - It only works with _MP_INTERNAL and _MP_MSBIGNUM
|
|
mpz u, v, diff;
|
|
set(u, a);
|
|
set(v, b);
|
|
abs(u);
|
|
abs(v);
|
|
|
|
unsigned k_u = power_of_two_multiple(u);
|
|
unsigned k_v = power_of_two_multiple(v);
|
|
unsigned k = k_u < k_v ? k_u : k_v;
|
|
|
|
machine_div2k(u, k_u);
|
|
|
|
while (true) {
|
|
machine_div2k(v, k_v);
|
|
|
|
if (lt(u, v)) {
|
|
sub(v, u, v);
|
|
}
|
|
else {
|
|
sub(u, v, diff);
|
|
swap(u, v);
|
|
swap(v, diff);
|
|
}
|
|
|
|
if (is_zero(v) || is_one(v))
|
|
break;
|
|
|
|
// reset least significant bit
|
|
if (is_small(v))
|
|
v.m_val &= ~1;
|
|
else
|
|
v.m_ptr->m_digits[0] &= ~static_cast<digit_t>(1);
|
|
k_v = power_of_two_multiple(v);
|
|
}
|
|
|
|
mul2k(u, k, c);
|
|
del(u); del(v); del(diff);
|
|
#endif // BINARY_GCD
|
|
|
|
#ifdef EUCLID_GCD
|
|
mpz tmp1;
|
|
mpz tmp2;
|
|
mpz aux;
|
|
set(tmp1, a);
|
|
set(tmp2, b);
|
|
abs(tmp1);
|
|
abs(tmp2);
|
|
if (lt(tmp1, tmp2))
|
|
swap(tmp1, tmp2);
|
|
if (is_zero(tmp2)) {
|
|
swap(c, tmp1);
|
|
}
|
|
else {
|
|
while (true) {
|
|
if (is_uint64(tmp1) && is_uint64(tmp2)) {
|
|
set(c, u64_gcd(get_uint64(tmp1), get_uint64(tmp2)));
|
|
break;
|
|
}
|
|
rem(tmp1, tmp2, aux);
|
|
if (is_zero(aux)) {
|
|
swap(c, tmp2);
|
|
break;
|
|
}
|
|
swap(tmp1, tmp2);
|
|
swap(tmp2, aux);
|
|
}
|
|
}
|
|
del(tmp1); del(tmp2); del(aux);
|
|
#endif // EUCLID_GCD
|
|
|
|
#ifdef LS_BINARY_GCD
|
|
mpz u, v, t, u1, u2;
|
|
set(u, a);
|
|
set(v, b);
|
|
abs(u);
|
|
abs(v);
|
|
if (lt(u, v))
|
|
swap(u, v);
|
|
while (!is_zero(v)) {
|
|
// Basic idea:
|
|
// compute t = 2^e*v such that t <= u < 2t
|
|
// u := min{u - t, 2t - u}
|
|
//
|
|
// The assignment u := min{u - t, 2t - u}
|
|
// can be replaced with u := u - t
|
|
//
|
|
// Since u and v are positive, we have:
|
|
// 2^{log2(u)} <= u < 2^{(log2(u) + 1)}
|
|
// 2^{log2(v)} <= v < 2^{(log2(v) + 1)}
|
|
// -->
|
|
// 2^{log2(v)}*2^{log2(u)-log2(v)} <= v*2^{log2(u)-log2(v)} < 2^{log2(v) + 1}*2^{log2(u)-log2(v)}
|
|
// -->
|
|
// 2^{log2(u)} <= v*2^{log2(u)-log2(v)} < 2^{log2(u) + 1}
|
|
//
|
|
// Now, let t be v*2^{log2(u)-log2(v)}
|
|
// If t <= u, then we found t
|
|
// Otherwise t = t div 2
|
|
unsigned k_u = log2(u);
|
|
unsigned k_v = log2(v);
|
|
SASSERT(k_v <= k_u);
|
|
unsigned e = k_u - k_v;
|
|
mul2k(v, e, t);
|
|
sub(u, t, u1);
|
|
if (is_neg(u1)) {
|
|
// t is too big
|
|
machine_div2k(t, 1);
|
|
// Now, u1 contains u - 2t
|
|
neg(u1);
|
|
// Now, u1 contains 2t - u
|
|
sub(u, t, u2); // u2 := u - t
|
|
}
|
|
else {
|
|
// u1 contains u - t
|
|
mul2k(t, 1);
|
|
sub(t, u, u2);
|
|
// u2 contains 2t - u
|
|
}
|
|
SASSERT(is_nonneg(u1));
|
|
SASSERT(is_nonneg(u2));
|
|
if (lt(u1, u2))
|
|
swap(u, u1);
|
|
else
|
|
swap(u, u2);
|
|
if (lt(u, v))
|
|
swap(u,v);
|
|
}
|
|
swap(u, c);
|
|
del(u); del(v); del(t); del(u1); del(u2);
|
|
#endif // LS_BINARY_GCD
|
|
|
|
#ifdef LEHMER_GCD
|
|
// For now, it only works if sizeof(digit_t) == sizeof(unsigned)
|
|
COMPILE_TIME_ASSERT(sizeof(digit_t) == sizeof(unsigned));
|
|
|
|
int64 a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz;
|
|
mpz a1, b1, t, r, tmp;
|
|
set(a1, a);
|
|
set(b1, b);
|
|
abs(a1);
|
|
abs(b1);
|
|
if (lt(a1, b1))
|
|
swap(a1, b1);
|
|
while (true) {
|
|
SASSERT(ge(a1, b1));
|
|
if (is_small(b1)) {
|
|
if (is_small(a1)) {
|
|
unsigned r = u_gcd(a1.m_val, b1.m_val);
|
|
set(c, r);
|
|
break;
|
|
}
|
|
else {
|
|
while (!is_zero(b1)) {
|
|
SASSERT(ge(a1, b1));
|
|
rem(a1, b1, tmp);
|
|
swap(a1, b1);
|
|
swap(b1, tmp);
|
|
}
|
|
swap(c, a1);
|
|
break;
|
|
}
|
|
}
|
|
SASSERT(!is_small(a1));
|
|
SASSERT(!is_small(b1));
|
|
a_sz = a1.m_ptr->m_size;
|
|
b_sz = b1.m_ptr->m_size;
|
|
SASSERT(b_sz <= a_sz);
|
|
a_hat = a1.m_ptr->m_digits[a_sz - 1];
|
|
b_hat = (b_sz == a_sz) ? b1.m_ptr->m_digits[b_sz - 1] : 0;
|
|
A = 1;
|
|
B = 0;
|
|
C = 0;
|
|
D = 1;
|
|
while (true) {
|
|
// Loop invariants
|
|
SASSERT(a_hat + A <= static_cast<int64>(UINT_MAX) + 1);
|
|
SASSERT(a_hat + B < static_cast<int64>(UINT_MAX) + 1);
|
|
SASSERT(b_hat + C < static_cast<int64>(UINT_MAX) + 1);
|
|
SASSERT(b_hat + D <= static_cast<int64>(UINT_MAX) + 1);
|
|
// overflows can't happen since I'm using int64
|
|
if (b_hat + C == 0 || b_hat + D == 0)
|
|
break;
|
|
q = (a_hat + A)/(b_hat + C);
|
|
if (q != (a_hat + B)/(b_hat + D))
|
|
break;
|
|
T = A - q*C;
|
|
A = C;
|
|
C = T;
|
|
T = B - q*D;
|
|
B = D;
|
|
D = T;
|
|
T = a_hat - q*b_hat;
|
|
a_hat = b_hat;
|
|
b_hat = T;
|
|
}
|
|
SASSERT(ge(a1, b1));
|
|
if (B == 0) {
|
|
rem(a1, b1, t);
|
|
swap(a1, b1);
|
|
swap(b1, t);
|
|
SASSERT(ge(a1, b1));
|
|
}
|
|
else {
|
|
// t <- A*a1
|
|
set(tmp, A);
|
|
mul(a1, tmp, t);
|
|
// t <- t + B*b1
|
|
set(tmp, B);
|
|
addmul(t, tmp, b1, t);
|
|
// r <- C*a1
|
|
set(tmp, C);
|
|
mul(a1, tmp, r);
|
|
// r <- r + D*b1
|
|
set(tmp, D);
|
|
addmul(r, tmp, b1, r);
|
|
// a <- t
|
|
swap(a1, t);
|
|
// b <- r
|
|
swap(b1, r);
|
|
SASSERT(ge(a1, b1));
|
|
}
|
|
}
|
|
del(a1); del(b1); del(r); del(t); del(tmp);
|
|
#endif // LEHMER_GCD
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::size_info(mpz const & a) {
|
|
if (is_small(a))
|
|
return 1;
|
|
#ifndef _MP_GMP
|
|
return a.m_ptr->m_size + 1;
|
|
#else
|
|
return mpz_size(*a.m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
struct mpz_manager<SYNCH>::sz_lt {
|
|
mpz_manager<SYNCH> & m;
|
|
mpz const * m_as;
|
|
|
|
sz_lt(mpz_manager<SYNCH> & _m, mpz const * as):m(_m), m_as(as) {}
|
|
|
|
bool operator()(unsigned p1, unsigned p2) {
|
|
return m.size_info(m_as[p1]) < m.size_info(m_as[p2]);
|
|
}
|
|
};
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::gcd(unsigned sz, mpz const * as, mpz & g) {
|
|
#if 0
|
|
// Optimization: sort numbers by size. Motivation: compute the gcd of the small ones first.
|
|
// The optimization did not really help.
|
|
switch (sz) {
|
|
case 0:
|
|
reset(g);
|
|
return;
|
|
case 1:
|
|
set(g, as[0]);
|
|
abs(g);
|
|
return;
|
|
case 2:
|
|
gcd(as[0], as[1], g);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
unsigned i;
|
|
for (i = 0; i < sz; i++) {
|
|
if (!is_small(as[i]))
|
|
break;
|
|
}
|
|
if (i != sz) {
|
|
// array has big numbers
|
|
sbuffer<unsigned, 1024> p;
|
|
for (i = 0; i < sz; i++)
|
|
p.push_back(i);
|
|
sz_lt lt(*this, as);
|
|
std::sort(p.begin(), p.end(), lt);
|
|
TRACE("mpz_gcd", for (unsigned i = 0; i < sz; i++) tout << p[i] << ":" << size_info(as[p[i]]) << " "; tout << "\n";);
|
|
gcd(as[p[0]], as[p[1]], g);
|
|
for (i = 2; i < sz; i++) {
|
|
if (is_one(g))
|
|
return;
|
|
gcd(g, as[p[i]], g);
|
|
}
|
|
return;
|
|
}
|
|
else {
|
|
gcd(as[0], as[1], g);
|
|
for (unsigned i = 2; i < sz; i++) {
|
|
if (is_one(g))
|
|
return;
|
|
gcd(g, as[i], g);
|
|
}
|
|
}
|
|
#else
|
|
// Vanilla implementation
|
|
switch (sz) {
|
|
case 0:
|
|
reset(g);
|
|
return;
|
|
case 1:
|
|
set(g, as[0]);
|
|
abs(g);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
gcd(as[0], as[1], g);
|
|
for (unsigned i = 2; i < sz; i++) {
|
|
if (is_one(g))
|
|
return;
|
|
gcd(g, as[i], g);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::gcd(mpz const & r1, mpz const & r2, mpz & a, mpz & b, mpz & r) {
|
|
mpz tmp1, tmp2;
|
|
mpz aux, quot;
|
|
set(tmp1, r1);
|
|
set(tmp2, r2);
|
|
set(a, 1);
|
|
set(b, 0);
|
|
mpz nexta, nextb;
|
|
set(nexta, 0);
|
|
set(nextb, 1);
|
|
|
|
abs(tmp1);
|
|
abs(tmp2);
|
|
if (lt(tmp1, tmp2)) {
|
|
swap(tmp1, tmp2);
|
|
swap(nexta, nextb);
|
|
swap(a, b);
|
|
}
|
|
|
|
// tmp1 >= tmp2 >= 0
|
|
// quot_rem in one function would be faster.
|
|
while (is_pos(tmp2)) {
|
|
SASSERT(ge(tmp1, tmp2));
|
|
|
|
// aux = tmp2
|
|
set(aux, tmp2);
|
|
// quot = div(tmp1, tmp2);
|
|
machine_div(tmp1, tmp2, quot);
|
|
// tmp2 = tmp1 % tmp2
|
|
rem(tmp1, tmp2, tmp2);
|
|
// tmp1 = aux
|
|
set(tmp1, aux);
|
|
// aux = nexta
|
|
set(aux, nexta);
|
|
// nexta = a - (quot*nexta)
|
|
mul(quot, nexta, nexta);
|
|
sub(a, nexta, nexta);
|
|
// a = axu
|
|
set(a, aux);
|
|
// aux = nextb
|
|
set(aux, nextb);
|
|
// nextb = b - (quot*nextb)
|
|
mul(nextb, quot, nextb);
|
|
sub(b, nextb, nextb);
|
|
// b = aux
|
|
set(b, aux);
|
|
}
|
|
|
|
if (is_neg(r1))
|
|
neg(a);
|
|
if (is_neg(r2))
|
|
neg(b);
|
|
|
|
// SASSERT((a*r1) + (b*r2) == tmp1);
|
|
#ifdef Z3DEBUG
|
|
mul(a, r1, nexta);
|
|
mul(b, r2, nextb);
|
|
add(nexta, nextb, nexta);
|
|
SASSERT(eq(nexta, tmp1));
|
|
#endif
|
|
set(r, tmp1);
|
|
del(tmp1);
|
|
del(tmp2);
|
|
del(aux);
|
|
del(quot);
|
|
del(nexta);
|
|
del(nextb);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::lcm(mpz const & a, mpz const & b, mpz & c) {
|
|
if (is_one(b)) {
|
|
set(c, a);
|
|
TRACE("lcm_bug", tout << "1. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";);
|
|
}
|
|
else if (is_one(a) || eq(a, b)) {
|
|
set(c, b);
|
|
TRACE("lcm_bug", tout << "2. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";);
|
|
}
|
|
else {
|
|
mpz r;
|
|
gcd(a, b, r);
|
|
TRACE("lcm_bug", tout << "gcd(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(r) << "\n";);
|
|
if (eq(r, a)) {
|
|
set(c, b);
|
|
TRACE("lcm_bug", tout << "3. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";);
|
|
}
|
|
else if (eq(r, b)) {
|
|
set(c, a);
|
|
TRACE("lcm_bug", tout << "4. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";);
|
|
}
|
|
else {
|
|
// c contains gcd(a, b)
|
|
// so c divides a, and machine_div(a, c) is equal to div(a, c)
|
|
machine_div(a, r, r);
|
|
mul(r, b, c);
|
|
TRACE("lcm_bug", tout << "5. lcm(" << to_string(a) << ", " << to_string(b) << ") = " << to_string(c) << "\n";);
|
|
}
|
|
del(r);
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::bitwise_or(mpz const & a, mpz const & b, mpz & c) {
|
|
SASSERT(is_nonneg(a));
|
|
SASSERT(is_nonneg(b));
|
|
TRACE("mpz", tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";);
|
|
if (is_small(a) && is_small(b)) {
|
|
del(c);
|
|
c.m_val = a.m_val | b.m_val;
|
|
}
|
|
else {
|
|
#ifndef _MP_GMP
|
|
mpz a1, b1, a2, b2, m, tmp;
|
|
set(a1, a);
|
|
set(b1, b);
|
|
set(m, 1);
|
|
reset(c);
|
|
while (!is_zero(a1) && !is_zero(b1)) {
|
|
TRACE("mpz", tout << "a1: " << to_string(a1) << ", b1: " << to_string(b1) << "\n";);
|
|
mod(a1, m_two64, a2);
|
|
mod(b1, m_two64, b2);
|
|
TRACE("mpz", tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << "\n";);
|
|
uint64 v = get_uint64(a2) | get_uint64(b2);
|
|
TRACE("mpz", tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << "\n";);
|
|
set(tmp, v);
|
|
mul(tmp, m, tmp);
|
|
add(c, tmp, c); // c += m * v
|
|
mul(m, m_two64, m);
|
|
div(a1, m_two64, a1);
|
|
div(b1, m_two64, b1);
|
|
}
|
|
if (!is_zero(a1)) {
|
|
mul(a1, m, a1);
|
|
add(c, a1, c);
|
|
}
|
|
if (!is_zero(b1)) {
|
|
mul(b1, m, b1);
|
|
add(c, b1, c);
|
|
}
|
|
del(a1); del(b1); del(a2); del(b2); del(m); del(tmp);
|
|
#else
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_ior(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::bitwise_and(mpz const & a, mpz const & b, mpz & c) {
|
|
SASSERT(is_nonneg(a));
|
|
SASSERT(is_nonneg(b));
|
|
if (is_small(a) && is_small(b)) {
|
|
del(c);
|
|
c.m_val = a.m_val & b.m_val;
|
|
}
|
|
else {
|
|
#ifndef _MP_GMP
|
|
mpz a1, b1, a2, b2, m, tmp;
|
|
set(a1, a);
|
|
set(b1, b);
|
|
set(m, 1);
|
|
reset(c);
|
|
while (!is_zero(a1) && !is_zero(b1)) {
|
|
mod(a1, m_two64, a2);
|
|
mod(b1, m_two64, b2);
|
|
uint64 v = get_uint64(a2) & get_uint64(b2);
|
|
set(tmp, v);
|
|
mul(tmp, m, tmp);
|
|
add(c, tmp, c); // c += m * v
|
|
mul(m, m_two64, m);
|
|
div(a1, m_two64, a1);
|
|
div(b1, m_two64, b1);
|
|
}
|
|
del(a1); del(b1); del(a2); del(b2); del(m); del(tmp);
|
|
#else
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_and(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::bitwise_xor(mpz const & a, mpz const & b, mpz & c) {
|
|
SASSERT(is_nonneg(a));
|
|
SASSERT(is_nonneg(b));
|
|
if (is_small(a) && is_small(b)) {
|
|
set_i64(c, i64(a) ^ i64(b));
|
|
}
|
|
else {
|
|
#ifndef _MP_GMP
|
|
mpz a1, b1, a2, b2, m, tmp;
|
|
set(a1, a);
|
|
set(b1, b);
|
|
set(m, 1);
|
|
reset(c);
|
|
while (!is_zero(a1) && !is_zero(b1)) {
|
|
mod(a1, m_two64, a2);
|
|
mod(b1, m_two64, b2);
|
|
uint64 v = get_uint64(a2) ^ get_uint64(b2);
|
|
set(tmp, v);
|
|
mul(tmp, m, tmp);
|
|
add(c, tmp, c); // c += m * v
|
|
mul(m, m_two64, m);
|
|
div(a1, m_two64, a1);
|
|
div(b1, m_two64, b1);
|
|
}
|
|
if (!is_zero(a1)) {
|
|
mul(a1, m, a1);
|
|
add(c, a1, c);
|
|
}
|
|
if (!is_zero(b1)) {
|
|
mul(b1, m, b1);
|
|
add(c, b1, c);
|
|
}
|
|
del(a1); del(b1); del(a2); del(b2); del(m); del(tmp);
|
|
#else
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
mk_big(c);
|
|
mpz_xor(*c.m_ptr, *arg0, *arg1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::bitwise_not(unsigned sz, mpz const & a, mpz & c) {
|
|
SASSERT(is_nonneg(a));
|
|
if (is_small(a) && sz <= 63) {
|
|
int64 mask = (static_cast<int64>(1) << sz) - static_cast<int64>(1);
|
|
set_i64(c, (~ i64(a)) & mask);
|
|
}
|
|
else {
|
|
mpz a1, a2, m, tmp;
|
|
set(a1, a);
|
|
set(m, 1);
|
|
set(c, 0);
|
|
while (sz > 0) {
|
|
mod(a1, m_two64, a2);
|
|
uint64 n = get_uint64(a2);
|
|
uint64 v = ~n;
|
|
SASSERT(~v == n);
|
|
if (sz < 64) {
|
|
uint64 mask = (1ull << static_cast<uint64>(sz)) - 1ull;
|
|
v = mask & v;
|
|
}
|
|
TRACE("bitwise_not", tout << "sz: " << sz << ", v: " << v << ", n: " << n << "\n";);
|
|
set(tmp, v);
|
|
SASSERT(get_uint64(tmp) == v);
|
|
mul(tmp, m, tmp);
|
|
add(c, tmp, c); // c += m * v
|
|
mul(m, m_two64, m);
|
|
div(a1, m_two64, a1);
|
|
sz -= (sz<64) ? sz : 64;
|
|
}
|
|
del(a1); del(a2); del(m); del(tmp);
|
|
TRACE("bitwise_not", tout << "sz: " << sz << " a: " << to_string(a) << " c: " << to_string(c) << "\n";);
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::big_set(mpz & target, mpz const & source) {
|
|
#ifndef _MP_GMP
|
|
if (&target == &source)
|
|
return;
|
|
target.m_val = source.m_val;
|
|
if (is_small(target)) {
|
|
target.m_ptr = allocate(capacity(source));
|
|
target.m_ptr->m_size = size(source);
|
|
target.m_ptr->m_capacity = capacity(source);
|
|
memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source));
|
|
}
|
|
else {
|
|
if (capacity(target) < size(source)) {
|
|
deallocate(target.m_ptr);
|
|
target.m_ptr = allocate(capacity(source));
|
|
target.m_ptr->m_size = size(source);
|
|
target.m_ptr->m_capacity = capacity(source);
|
|
memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source));
|
|
}
|
|
else {
|
|
target.m_ptr->m_size = size(source);
|
|
memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source));
|
|
}
|
|
}
|
|
#else
|
|
// GMP version
|
|
mk_big(target);
|
|
mpz_set(*target.m_ptr, *source.m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
int mpz_manager<SYNCH>::big_compare(mpz const & a, mpz const & b) {
|
|
#ifndef _MP_GMP
|
|
int sign_a;
|
|
int sign_b;
|
|
mpz_cell * cell_a;
|
|
mpz_cell * cell_b;
|
|
get_sign_cell<0>(a, sign_a, cell_a);
|
|
get_sign_cell<1>(b, sign_b, cell_b);
|
|
|
|
if (sign_a > 0) {
|
|
// a is positive
|
|
if (sign_b > 0) {
|
|
// a & b are positive
|
|
return compare_diff(cell_a->m_digits,
|
|
cell_a->m_size,
|
|
cell_b->m_digits,
|
|
cell_b->m_size);
|
|
}
|
|
else {
|
|
// b is negative
|
|
return 1; // a > b
|
|
}
|
|
}
|
|
else {
|
|
// a is negative
|
|
if (sign_b > 0) {
|
|
// b is positive
|
|
return -1; // a < b
|
|
}
|
|
else {
|
|
// a & b are negative
|
|
return compare_diff(cell_b->m_digits,
|
|
cell_b->m_size,
|
|
cell_a->m_digits,
|
|
cell_a->m_size);
|
|
}
|
|
}
|
|
#else
|
|
// GMP version
|
|
mpz_t * arg0;
|
|
mpz_t * arg1;
|
|
get_arg<0>(a, arg0);
|
|
get_arg<1>(b, arg1);
|
|
return mpz_cmp(*arg0, *arg1);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::is_uint64(mpz const & a) const {
|
|
#ifndef _MP_GMP
|
|
if (a.m_val < 0)
|
|
return false;
|
|
if (is_small(a))
|
|
return true;
|
|
if (sizeof(digit_t) == sizeof(uint64)) {
|
|
return size(a) <= 1;
|
|
}
|
|
else {
|
|
return size(a) <= 2;
|
|
}
|
|
#else
|
|
// GMP version
|
|
if (is_small(a))
|
|
return a.m_val >= 0;
|
|
return is_nonneg(a) && mpz_cmp(*a.m_ptr, m_uint64_max) <= 0;
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::is_int64(mpz const & a) const {
|
|
if (is_small(a))
|
|
return true;
|
|
#ifndef _MP_GMP
|
|
if (!is_uint64(a))
|
|
return false;
|
|
uint64 num = get_uint64(a);
|
|
uint64 msb = static_cast<uint64>(1) << 63;
|
|
uint64 msb_val = msb & num;
|
|
if (a.m_val >= 0) {
|
|
// non-negative number.
|
|
return (0 == msb_val);
|
|
}
|
|
else {
|
|
// negative number.
|
|
// either the high bit is 0, or
|
|
// the number is 2^64 which can be represented.
|
|
//
|
|
return 0 == msb_val || (msb_val == num);
|
|
}
|
|
#else
|
|
// GMP version
|
|
return mpz_cmp(*a.m_ptr, m_int64_max) <= 0;
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
uint64 mpz_manager<SYNCH>::get_uint64(mpz const & a) const {
|
|
if (is_small(a))
|
|
return static_cast<uint64>(a.m_val);
|
|
#ifndef _MP_GMP
|
|
SASSERT(a.m_ptr->m_size > 0);
|
|
if (a.m_ptr->m_size == 1)
|
|
return digits(a)[0];
|
|
if (sizeof(digit_t) == sizeof(uint64))
|
|
// 64-bit machine
|
|
return digits(a)[0];
|
|
else
|
|
// 32-bit machine
|
|
return ((static_cast<uint64>(digits(a)[1]) << 32) | (static_cast<uint64>(digits(a)[0])));
|
|
#else
|
|
// GMP version
|
|
if (sizeof(uint64) == sizeof(unsigned long)) {
|
|
return mpz_get_ui(*a.m_ptr);
|
|
}
|
|
else {
|
|
mpz_manager * _this = const_cast<mpz_manager*>(this);
|
|
mpz_set(_this->m_tmp, *a.m_ptr);
|
|
mpz_mod(_this->m_tmp, m_tmp, m_two32);
|
|
uint64 r = static_cast<uint64>(mpz_get_ui(m_tmp));
|
|
mpz_set(_this->m_tmp, *a.m_ptr);
|
|
mpz_div(_this->m_tmp, m_tmp, m_two32);
|
|
r += static_cast<uint64>(mpz_get_ui(m_tmp)) << static_cast<uint64>(32);
|
|
return r;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
int64 mpz_manager<SYNCH>::get_int64(mpz const & a) const {
|
|
if (is_small(a))
|
|
return static_cast<int64>(a.m_val);
|
|
#ifndef _MP_GMP
|
|
SASSERT(is_int64(a));
|
|
uint64 num = get_uint64(a);
|
|
if (a.m_val < 0) {
|
|
if (num != 0 && (num << 1) == 0)
|
|
return INT64_MIN;
|
|
return -static_cast<int64>(num);
|
|
}
|
|
return static_cast<int64>(num);
|
|
#else
|
|
// GMP
|
|
if (sizeof(int64) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) {
|
|
return mpz_get_si(*a.m_ptr);
|
|
}
|
|
else {
|
|
mpz_manager * _this = const_cast<mpz_manager*>(this);
|
|
mpz_mod(_this->m_tmp, *a.m_ptr, m_two32);
|
|
int64 r = static_cast<int64>(mpz_get_ui(m_tmp));
|
|
mpz_div(_this->m_tmp, *a.m_ptr, m_two32);
|
|
r += static_cast<int64>(mpz_get_si(m_tmp)) << static_cast<int64>(32);
|
|
return r;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
double mpz_manager<SYNCH>::get_double(mpz const & a) const {
|
|
if (is_small(a))
|
|
return static_cast<double>(a.m_val);
|
|
#ifndef _MP_GMP
|
|
double r = 0.0;
|
|
double d = 1.0;
|
|
unsigned sz = size(a);
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
r += d * static_cast<double>(digits(a)[i]);
|
|
if (sizeof(digit_t) == sizeof(uint64))
|
|
d *= static_cast<double>(UINT64_MAX); // 64-bit version
|
|
else
|
|
d *= static_cast<double>(UINT_MAX); // 32-bit version
|
|
}
|
|
return a.m_val < 0 ? -r : r;
|
|
#else
|
|
return mpz_get_d(*a.m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::display(std::ostream & out, mpz const & a) const {
|
|
if (is_small(a)) {
|
|
out << a.m_val;
|
|
}
|
|
else {
|
|
#ifndef _MP_GMP
|
|
if (a.m_val < 0)
|
|
out << "-";
|
|
if (sizeof(digit_t) == 4) {
|
|
sbuffer<char, 1024> buffer(11*size(a), 0);
|
|
out << mp_decimal(digits(a), size(a), buffer.begin(), buffer.size(), 0);
|
|
}
|
|
else {
|
|
sbuffer<char, 1024> buffer(21*size(a), 0);
|
|
out << mp_decimal(digits(a), size(a), buffer.begin(), buffer.size(), 0);
|
|
}
|
|
#else
|
|
// GMP version
|
|
size_t sz = mpz_sizeinbase(*a.m_ptr, 10) + 2;
|
|
sbuffer<char, 1024> buffer(sz, 0);
|
|
mpz_get_str(buffer.c_ptr(), 10, *a.m_ptr);
|
|
out << buffer.c_ptr();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::display_smt2(std::ostream & out, mpz const & a, bool decimal) const {
|
|
if (is_neg(a)) {
|
|
mpz_manager<SYNCH> * _this = const_cast<mpz_manager<SYNCH>*>(this);
|
|
_scoped_numeral<mpz_manager<SYNCH> > tmp(*_this);
|
|
_this->set(tmp, a);
|
|
_this->neg(tmp);
|
|
out << "(- ";
|
|
display(out, tmp);
|
|
if (decimal)
|
|
out << ".0";
|
|
out << ")";
|
|
}
|
|
else {
|
|
display(out, a);
|
|
if (decimal)
|
|
out << ".0";
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
std::string mpz_manager<SYNCH>::to_string(mpz const & a) const {
|
|
std::ostringstream buffer;
|
|
display(buffer, a);
|
|
return buffer.str();
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::hash(mpz const & a) {
|
|
if (is_small(a))
|
|
return a.m_val;
|
|
#ifndef _MP_GMP
|
|
unsigned sz = size(a);
|
|
if (sz == 1)
|
|
return static_cast<unsigned>(digits(a)[0]);
|
|
return string_hash(reinterpret_cast<char*>(digits(a)), sz * sizeof(digit_t), 17);
|
|
#else
|
|
return mpz_get_si(*a.m_ptr);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::power(mpz const & a, unsigned p, mpz & b) {
|
|
#ifdef _MP_GMP
|
|
if (!is_small(a)) {
|
|
mk_big(b);
|
|
mpz_pow_ui(*b.m_ptr, *a.m_ptr, p);
|
|
return;
|
|
}
|
|
#endif
|
|
#ifndef _MP_GMP
|
|
if (is_small(a)) {
|
|
if (a.m_val == 2) {
|
|
if (p < 8 * sizeof(int) - 1) {
|
|
del(b);
|
|
b.m_val = 1 << p;
|
|
}
|
|
else {
|
|
unsigned sz = p/(8 * sizeof(digit_t)) + 1;
|
|
unsigned shift = p%(8 * sizeof(digit_t));
|
|
SASSERT(sz > 0);
|
|
allocate_if_needed(b, sz);
|
|
SASSERT(b.m_ptr->m_capacity >= sz);
|
|
b.m_ptr->m_size = sz;
|
|
for (unsigned i = 0; i < sz - 1; i++)
|
|
b.m_ptr->m_digits[i] = 0;
|
|
b.m_ptr->m_digits[sz-1] = 1 << shift;
|
|
b.m_val = 1;
|
|
}
|
|
return;
|
|
}
|
|
if (a.m_val == 0) {
|
|
SASSERT(p != 0);
|
|
reset(b);
|
|
return;
|
|
}
|
|
if (a.m_val == 1) {
|
|
set(b, 1);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
// general purpose
|
|
unsigned mask = 1;
|
|
mpz power;
|
|
set(power, a);
|
|
set(b, 1);
|
|
while (mask <= p) {
|
|
if (mask & p)
|
|
mul(b, power, b);
|
|
mul(power, power, power);
|
|
mask = mask << 1;
|
|
}
|
|
del(power);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::is_power_of_two(mpz const & a) {
|
|
unsigned shift;
|
|
return is_power_of_two(a, shift);
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::is_power_of_two(mpz const & a, unsigned & shift) {
|
|
if (is_nonpos(a))
|
|
return false;
|
|
if (is_small(a)) {
|
|
if (::is_power_of_two(a.m_val)) {
|
|
shift = ::log2(a.m_val);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
#ifndef _MP_GMP
|
|
mpz_cell * c = a.m_ptr;
|
|
unsigned sz = c->m_size;
|
|
digit_t * ds = c->m_digits;
|
|
for (unsigned i = 0; i < sz - 1; i++) {
|
|
if (ds[i] != 0)
|
|
return false;
|
|
}
|
|
digit_t v = ds[sz-1];
|
|
if (!(v & (v - 1)) && v) {
|
|
shift = log2(a);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
#else
|
|
if (mpz_popcount(*a.m_ptr) == 1) {
|
|
shift = log2(a);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Expand capacity of a
|
|
#ifndef _MP_GMP
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::ensure_capacity(mpz & a, unsigned capacity) {
|
|
if (capacity <= 1)
|
|
return;
|
|
if (capacity < m_init_cell_capacity)
|
|
capacity = m_init_cell_capacity;
|
|
if (is_small(a)) {
|
|
a.m_ptr = allocate(capacity);
|
|
SASSERT(a.m_ptr->m_capacity == capacity);
|
|
if (a.m_val == INT_MIN) {
|
|
unsigned intmin_sz = m_int_min.m_ptr->m_size;
|
|
for (unsigned i = 0; i < intmin_sz; i++)
|
|
a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i];
|
|
a.m_val = -1;
|
|
a.m_ptr->m_size = m_int_min.m_ptr->m_size;
|
|
}
|
|
else if (a.m_val < 0) {
|
|
a.m_ptr->m_digits[0] = -a.m_val;
|
|
a.m_val = -1;
|
|
a.m_ptr->m_size = 1;
|
|
}
|
|
else {
|
|
a.m_ptr->m_digits[0] = a.m_val;
|
|
a.m_val = 1;
|
|
a.m_ptr->m_size = 1;
|
|
}
|
|
}
|
|
else {
|
|
if (a.m_ptr->m_capacity >= capacity)
|
|
return;
|
|
mpz_cell * new_cell = allocate(capacity);
|
|
SASSERT(new_cell->m_capacity == capacity);
|
|
unsigned old_sz = a.m_ptr->m_size;
|
|
new_cell->m_size = old_sz;
|
|
for (unsigned i = 0; i < old_sz; i++)
|
|
new_cell->m_digits[i] = a.m_ptr->m_digits[i];
|
|
deallocate(a.m_ptr);
|
|
a.m_ptr = new_cell;
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::normalize(mpz & a) {
|
|
mpz_cell * c = a.m_ptr;
|
|
digit_t * ds = c->m_digits;
|
|
unsigned i = c->m_size;
|
|
for (; i > 0; --i) {
|
|
if (ds[i-1] != 0)
|
|
break;
|
|
}
|
|
|
|
if (i == 0) {
|
|
// a is zero...
|
|
reset(a);
|
|
return;
|
|
}
|
|
|
|
if (i == 1 && ds[0] <= INT_MAX) {
|
|
// a is small
|
|
int val = a.m_val < 0 ? -static_cast<int>(ds[0]) : static_cast<int>(ds[0]);
|
|
del(a);
|
|
a.m_val = val;
|
|
return;
|
|
}
|
|
// adjust size
|
|
c->m_size = i;
|
|
}
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::machine_div2k(mpz & a, unsigned k) {
|
|
if (k == 0 || is_zero(a))
|
|
return;
|
|
if (is_small(a)) {
|
|
if (k < 32) {
|
|
int twok = 1 << k;
|
|
a.m_val /= twok;
|
|
}
|
|
else {
|
|
a.m_val = 0;
|
|
}
|
|
return;
|
|
}
|
|
#ifndef _MP_GMP
|
|
unsigned digit_shift = k / (8 * sizeof(digit_t));
|
|
mpz_cell * c = a.m_ptr;
|
|
unsigned sz = c->m_size;
|
|
if (digit_shift >= sz) {
|
|
reset(a);
|
|
return;
|
|
}
|
|
unsigned bit_shift = k % (8 * sizeof(digit_t));
|
|
unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift;
|
|
unsigned new_sz = sz - digit_shift;
|
|
SASSERT(new_sz >= 1);
|
|
digit_t * ds = c->m_digits;
|
|
TRACE("mpz_2k", tout << "bit_shift: " << bit_shift << ", comp_shift: " << comp_shift << ", new_sz: " << new_sz << ", sz: " << sz << "\n";);
|
|
if (new_sz < sz) {
|
|
unsigned i = 0;
|
|
unsigned j = digit_shift;
|
|
if (bit_shift != 0) {
|
|
for (; i < new_sz - 1; i++, j++) {
|
|
ds[i] = ds[j];
|
|
ds[i] >>= bit_shift;
|
|
ds[i] |= (ds[j+1] << comp_shift);
|
|
}
|
|
ds[i] = ds[j];
|
|
ds[i] >>= bit_shift;
|
|
}
|
|
else {
|
|
for (; i < new_sz; i++, j++) {
|
|
ds[i] = ds[j];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
SASSERT(new_sz == sz);
|
|
SASSERT(bit_shift != 0);
|
|
unsigned i = 0;
|
|
for (; i < new_sz - 1; i++) {
|
|
ds[i] >>= bit_shift;
|
|
ds[i] |= (ds[i+1] << comp_shift);
|
|
}
|
|
ds[i] >>= bit_shift;
|
|
}
|
|
|
|
c->m_size = new_sz;
|
|
normalize(a);
|
|
#else
|
|
MPZ_BEGIN_CRITICAL();
|
|
mpz_t * arg0;
|
|
get_arg<0>(a, arg0);
|
|
mpz_tdiv_q_2exp(m_tmp, *arg0, k);
|
|
mk_big(a);
|
|
mpz_swap(*a.m_ptr, m_tmp);
|
|
MPZ_END_CRITICAL();
|
|
#endif
|
|
}
|
|
|
|
#ifndef _MP_GMP
|
|
static void display_bits(std::ostream & out, digit_t a) {
|
|
for (unsigned i = 0; i < sizeof(digit_t) * 8; i++) {
|
|
if (a % 2 == 0)
|
|
out << "0";
|
|
else
|
|
out << "1";
|
|
a /= 2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
void mpz_manager<SYNCH>::mul2k(mpz & a, unsigned k) {
|
|
if (k == 0 || is_zero(a))
|
|
return;
|
|
if (is_small(a) && k < 32) {
|
|
set_i64(a, i64(a) * (static_cast<int64>(1) << k));
|
|
return;
|
|
}
|
|
#ifndef _MP_GMP
|
|
TRACE("mpz_mul2k", tout << "mul2k\na: " << to_string(a) << "\nk: " << k << "\n";);
|
|
unsigned word_shift = k / (8 * sizeof(digit_t));
|
|
unsigned bit_shift = k % (8 * sizeof(digit_t));
|
|
unsigned old_sz = is_small(a) ? 1 : a.m_ptr->m_size;
|
|
unsigned new_sz = old_sz + word_shift + 1;
|
|
ensure_capacity(a, new_sz);
|
|
TRACE("mpz_mul2k", tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz
|
|
<< "\na after ensure capacity:\n" << to_string(a) << "\n";);
|
|
SASSERT(!is_small(a));
|
|
mpz_cell * cell_a = a.m_ptr;
|
|
old_sz = cell_a->m_size;
|
|
digit_t * ds = cell_a->m_digits;
|
|
for (unsigned i = old_sz; i < new_sz; i++)
|
|
ds[i] = 0;
|
|
cell_a->m_size = new_sz;
|
|
|
|
if (word_shift > 0) {
|
|
unsigned j = old_sz;
|
|
unsigned i = old_sz + word_shift;
|
|
while (j > 0) {
|
|
--j; --i;
|
|
ds[i] = ds[j];
|
|
}
|
|
while (i > 0) {
|
|
--i;
|
|
ds[i] = 0;
|
|
}
|
|
}
|
|
if (bit_shift > 0) {
|
|
DEBUG_CODE({
|
|
for (unsigned i = 0; i < word_shift; i++) {
|
|
SASSERT(ds[i] == 0);
|
|
}
|
|
});
|
|
unsigned comp_shift = (8 * sizeof(digit_t)) - bit_shift;
|
|
digit_t prev = 0;
|
|
for (unsigned i = word_shift; i < new_sz; i++) {
|
|
digit_t new_prev = (ds[i] >> comp_shift);
|
|
ds[i] <<= bit_shift;
|
|
ds[i] |= prev;
|
|
prev = new_prev;
|
|
}
|
|
}
|
|
normalize(a);
|
|
TRACE("mpz_mul2k", tout << "mul2k result:\n" << to_string(a) << "\n";);
|
|
#else
|
|
mpz_t * arg0;
|
|
get_arg<0>(a, arg0);
|
|
mk_big(a);
|
|
mpz_mul_2exp(*a.m_ptr, *arg0, k);
|
|
#endif
|
|
}
|
|
|
|
#ifndef _MP_GMP
|
|
COMPILE_TIME_ASSERT(sizeof(digit_t) == 4 || sizeof(digit_t) == 8);
|
|
#endif
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::power_of_two_multiple(mpz const & a) {
|
|
if (is_zero(a))
|
|
return 0;
|
|
if (is_small(a)) {
|
|
unsigned r = 0;
|
|
int v = a.m_val;
|
|
#define COUNT_DIGIT_RIGHT_ZEROS() \
|
|
if (v % (1 << 16) == 0) { \
|
|
r += 16; \
|
|
v /= (1 << 16); \
|
|
} \
|
|
if (v % (1 << 8) == 0) { \
|
|
r += 8; \
|
|
v /= (1 << 8); \
|
|
} \
|
|
if (v % (1 << 4) == 0) { \
|
|
r += 4; \
|
|
v /= (1 << 4); \
|
|
} \
|
|
if (v % (1 << 2) == 0) { \
|
|
r += 2; \
|
|
v /= (1 << 2); \
|
|
} \
|
|
if (v % 2 == 0) { \
|
|
r++; \
|
|
}
|
|
COUNT_DIGIT_RIGHT_ZEROS();
|
|
return r;
|
|
}
|
|
#ifndef _MP_GMP
|
|
mpz_cell * c = a.m_ptr;
|
|
unsigned sz = c->m_size;
|
|
unsigned r = 0;
|
|
digit_t * source = c->m_digits;
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
if (source[i] != 0) {
|
|
digit_t v = source[i];
|
|
if (sizeof(digit_t) == 8) {
|
|
// TODO: we can remove this if after we move to MPN
|
|
// In MPN the digit_t is always an unsigned integer
|
|
if (static_cast<uint64>(v) % (static_cast<uint64>(1) << 32) == 0) {
|
|
r += 32;
|
|
v = static_cast<digit_t>(static_cast<uint64>(v) / (static_cast<uint64>(1) << 32));
|
|
}
|
|
}
|
|
COUNT_DIGIT_RIGHT_ZEROS();
|
|
return r;
|
|
}
|
|
r += (8 * sizeof(digit_t));
|
|
}
|
|
return r;
|
|
#else
|
|
return mpz_scan1(*a.m_ptr, 0);
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::log2(mpz const & a) {
|
|
if (is_nonpos(a))
|
|
return 0;
|
|
if (is_small(a))
|
|
return ::log2(a.m_val);
|
|
#ifndef _MP_GMP
|
|
COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4);
|
|
mpz_cell * c = a.m_ptr;
|
|
unsigned sz = c->m_size;
|
|
digit_t * ds = c->m_digits;
|
|
if (sizeof(digit_t) == 8)
|
|
return (sz - 1)*64 + uint64_log2(ds[sz-1]);
|
|
else
|
|
return (sz - 1)*32 + ::log2(static_cast<unsigned>(ds[sz-1]));
|
|
#else
|
|
unsigned r = mpz_sizeinbase(*a.m_ptr, 2);
|
|
SASSERT(r > 0);
|
|
return r - 1;
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::mlog2(mpz const & a) {
|
|
if (is_nonneg(a))
|
|
return 0;
|
|
if (is_small(a))
|
|
return ::log2(-a.m_val);
|
|
#ifndef _MP_GMP
|
|
COMPILE_TIME_ASSERT(sizeof(digit_t) == 8 || sizeof(digit_t) == 4);
|
|
mpz_cell * c = a.m_ptr;
|
|
unsigned sz = c->m_size;
|
|
digit_t * ds = c->m_digits;
|
|
if (sizeof(digit_t) == 8)
|
|
return (sz - 1)*64 + uint64_log2(ds[sz-1]);
|
|
else
|
|
return (sz - 1)*32 + ::log2(static_cast<unsigned>(ds[sz-1]));
|
|
#else
|
|
mpz_neg(m_tmp, *a.m_ptr);
|
|
unsigned r = mpz_sizeinbase(m_tmp, 2);
|
|
SASSERT(r > 0);
|
|
return r - 1;
|
|
#endif
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
unsigned mpz_manager<SYNCH>::bitsize(mpz const & a) {
|
|
if (is_nonneg(a))
|
|
return log2(a) + 1;
|
|
else
|
|
return mlog2(a) + 1;
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::is_perfect_square(mpz const & a, mpz & root) {
|
|
if (is_neg(a))
|
|
return false;
|
|
reset(root);
|
|
if (is_zero(a)) {
|
|
return true;
|
|
}
|
|
if (is_one(a)) {
|
|
set(root, 1);
|
|
return true;
|
|
}
|
|
|
|
mpz lo, hi, mid, sq_lo, sq_hi, sq_mid;
|
|
set(lo, 1);
|
|
set(hi, a);
|
|
set(sq_lo, 1);
|
|
mul(hi, hi, sq_hi);
|
|
bool result;
|
|
// lo*lo <= *this < hi*hi
|
|
while (true) {
|
|
SASSERT(lt(lo, hi));
|
|
SASSERT(le(sq_lo, a) && lt(a, sq_hi));
|
|
if (eq(sq_lo, a)) {
|
|
set(root, lo);
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
mpz & tmp = mid;
|
|
add(lo, mpz(1), tmp);
|
|
if (eq(tmp, hi)) {
|
|
set(root, hi);
|
|
result = false;
|
|
break;
|
|
}
|
|
|
|
add(hi, lo, tmp);
|
|
div(tmp, mpz(2), mid);
|
|
|
|
SASSERT(lt(lo, mid) && lt(mid, hi));
|
|
|
|
mul(mid, mid, sq_mid);
|
|
|
|
if (gt(sq_mid, a)) {
|
|
set(hi, mid);
|
|
set(sq_hi, sq_mid);
|
|
}
|
|
else {
|
|
set(lo, mid);
|
|
set(sq_lo, sq_mid);
|
|
}
|
|
}
|
|
del(lo);
|
|
del(hi);
|
|
del(mid);
|
|
del(sq_lo);
|
|
del(sq_hi);
|
|
del(sq_mid);
|
|
return result;
|
|
}
|
|
|
|
|
|
static unsigned div_l(unsigned k, unsigned n) {
|
|
return k/n;
|
|
}
|
|
|
|
static unsigned div_u(unsigned k, unsigned n) {
|
|
return k%n == 0 ? k/n : k/n + 1;
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::root(mpz & a, unsigned n) {
|
|
SASSERT(n % 2 != 0 || is_nonneg(a));
|
|
if (is_zero(a)) {
|
|
return true; // precise
|
|
}
|
|
|
|
// Initial approximation
|
|
//
|
|
// We have that:
|
|
// a > 0 -> 2^{log2(a)} <= a <= 2^{(log2(a) + 1)}
|
|
// a < 0 -> -2^{log2(a) + 1} <= a <= -2^{log2(a)}
|
|
//
|
|
// Thus
|
|
// a > 0 -> 2^{div_l(log2(a), n)} <= a^{1/n} <= 2^{div_u(log2(a) + 1, n)}
|
|
// a < 0 -> -2^{div_u(log2(a) + 1, n)} <= a^{1/n} <= -2^{div_l(log2(a), n)}
|
|
//
|
|
mpz lower;
|
|
mpz upper;
|
|
mpz mid;
|
|
mpz mid_n;
|
|
|
|
if (is_pos(a)) {
|
|
unsigned k = log2(a);
|
|
power(mpz(2), div_l(k, n), lower);
|
|
power(mpz(2), div_u(k + 1, n), upper);
|
|
}
|
|
else {
|
|
unsigned k = mlog2(a);
|
|
power(mpz(2), div_u(k + 1, n), lower);
|
|
power(mpz(2), div_l(k, n), upper);
|
|
neg(lower);
|
|
neg(upper);
|
|
}
|
|
|
|
bool result;
|
|
SASSERT(le(lower, upper));
|
|
if (eq(lower, upper)) {
|
|
swap(a, lower);
|
|
result = true;
|
|
}
|
|
else {
|
|
// Refine using bisection. TODO: use Newton's method if this is a bottleneck
|
|
while (true) {
|
|
add(upper, lower, mid);
|
|
machine_div2k(mid, 1);
|
|
TRACE("mpz", tout << "upper: "; display(tout, upper); tout << "\nlower: "; display(tout, lower); tout << "\nmid: "; display(tout, mid); tout << "\n";);
|
|
power(mid, n, mid_n);
|
|
if (eq(mid_n, a)) {
|
|
swap(a, mid);
|
|
result = true;
|
|
break;
|
|
}
|
|
if (eq(mid, lower) || eq(mid, upper)) {
|
|
swap(a, upper);
|
|
result = false;
|
|
break;
|
|
}
|
|
if (lt(mid_n, a)) {
|
|
// new lower bound
|
|
swap(mid, lower);
|
|
}
|
|
else {
|
|
SASSERT(lt(a, mid_n));
|
|
// new upper bound
|
|
swap(mid, upper);
|
|
}
|
|
}
|
|
}
|
|
del(lower);
|
|
del(upper);
|
|
del(mid);
|
|
del(mid_n);
|
|
return result;
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::decompose(mpz const & a, svector<digit_t> & digits) {
|
|
digits.reset();
|
|
if (is_small(a)) {
|
|
if (a.m_val < 0) {
|
|
digits.push_back(-a.m_val);
|
|
return true;
|
|
}
|
|
else {
|
|
digits.push_back(a.m_val);
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
#ifndef _MP_GMP
|
|
mpz_cell * cell_a = a.m_ptr;
|
|
unsigned sz = cell_a->m_size;
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
digits.push_back(cell_a->m_digits[i]);
|
|
}
|
|
return a.m_val < 0;
|
|
#else
|
|
bool r = is_neg(a);
|
|
mpz_set(m_tmp, *a.m_ptr);
|
|
mpz_abs(m_tmp, m_tmp);
|
|
while (mpz_sgn(m_tmp) != 0) {
|
|
mpz_tdiv_r_2exp(m_tmp2, m_tmp, 32);
|
|
unsigned v = mpz_get_ui(m_tmp2);
|
|
digits.push_back(v);
|
|
mpz_tdiv_q_2exp(m_tmp, m_tmp, 32);
|
|
}
|
|
return r;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
template<bool SYNCH>
|
|
bool mpz_manager<SYNCH>::divides(mpz const & a, mpz const & b) {
|
|
_scoped_numeral<mpz_manager<SYNCH> > tmp(*this);
|
|
bool r;
|
|
if (is_zero(a)) {
|
|
// I assume 0 | 0.
|
|
// Remark a|b is a shorthand for (exists x. a x = b)
|
|
// If b is zero, any x will do. If b != 0, then a does not divide b
|
|
r = is_zero(b);
|
|
}
|
|
else {
|
|
rem(b, a, tmp);
|
|
r = is_zero(tmp);
|
|
}
|
|
STRACE("divides", tout << "[mpz] Divisible["; display(tout, b); tout << ", "; display(tout, a); tout << "] == " << (r?"True":"False") << "\n";);
|
|
TRACE("divides_bug", tout << "tmp: "; display(tout, tmp); tout << "\n";);
|
|
return r;
|
|
}
|
|
|
|
template class mpz_manager<true>;
|
|
template class mpz_manager<false>;
|