/*++ Copyright (c) 2006 Microsoft Corporation Module Name: mpz.cpp Abstract: Author: Leonardo de Moura (leonardo) 2010-06-17. Revision History: --*/ #include #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 #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 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 mpz_manager::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(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(UINT64_MAX); unsigned max_h = static_cast(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(INT64_MAX % static_cast(UINT_MAX)); max_h = static_cast(INT64_MAX / static_cast(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 mpz_manager::~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 void mpz_manager::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(_v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(_v); digits(c)[1] = static_cast(_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(_v)); mpz_set_ui(m_tmp, static_cast(_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 void mpz_manager::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(v); c.m_ptr->m_size = 1; } else { // 32-bit machine digits(c)[0] = static_cast(v); digits(c)[1] = static_cast(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(v)); mpz_set_ui(m_tmp, static_cast(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 template void mpz_manager::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(m_tmp[IDX]->m_digits[0]) : static_cast(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 void mpz_manager::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 void mpz_manager::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 template void mpz_manager::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(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 void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(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 void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(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 void mpz_manager::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 template void mpz_manager::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 void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { #ifndef _MP_GMP quot_rem_core(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 void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(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 void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(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 void mpz_manager::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(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(UINT_MAX) + 1); SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); SASSERT(b_hat + D <= static_cast(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 unsigned mpz_manager::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 struct mpz_manager::sz_lt { mpz_manager & m; mpz const * m_as; sz_lt(mpz_manager & _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 void mpz_manager::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 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 void mpz_manager::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 void mpz_manager::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 void mpz_manager::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 void mpz_manager::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 void mpz_manager::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 void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { SASSERT(is_nonneg(a)); if (is_small(a) && sz <= 63) { int64 mask = (static_cast(1) << sz) - static_cast(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(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 void mpz_manager::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 int mpz_manager::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 mpz_manager::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 mpz_manager::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(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 uint64 mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) return static_cast(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(digits(a)[1]) << 32) | (static_cast(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(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); uint64 r = static_cast(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(mpz_get_ui(m_tmp)) << static_cast(32); return r; } #endif } template int64 mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) return static_cast(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(num); } return static_cast(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(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); int64 r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); return r; } #endif } template double mpz_manager::get_double(mpz const & a) const { if (is_small(a)) return static_cast(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(digits(a)[i]); if (sizeof(digit_t) == sizeof(uint64)) d *= static_cast(UINT64_MAX); // 64-bit version else d *= static_cast(UINT_MAX); // 32-bit version } return a.m_val < 0 ? -r : r; #else return mpz_get_d(*a.m_ptr); #endif } template void mpz_manager::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 buffer(11*size(a), 0); out << mp_decimal(digits(a), size(a), buffer.begin(), buffer.size(), 0); } else { sbuffer 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 buffer(sz, 0); mpz_get_str(buffer.c_ptr(), 10, *a.m_ptr); out << buffer.c_ptr(); #endif } } template void mpz_manager::display_smt2(std::ostream & out, mpz const & a, bool decimal) const { if (is_neg(a)) { mpz_manager * _this = const_cast*>(this); _scoped_numeral > 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 std::string mpz_manager::to_string(mpz const & a) const { std::ostringstream buffer; display(buffer, a); return buffer.str(); } template unsigned mpz_manager::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(digits(a)[0]); return string_hash(reinterpret_cast(digits(a)), sz * sizeof(digit_t), 17); #else return mpz_get_si(*a.m_ptr); #endif } template void mpz_manager::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 mpz_manager::is_power_of_two(mpz const & a) { unsigned shift; return is_power_of_two(a, shift); } template bool mpz_manager::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 void mpz_manager::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 void mpz_manager::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(ds[0]) : static_cast(ds[0]); del(a); a.m_val = val; return; } // adjust size c->m_size = i; } #endif template void mpz_manager::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 void mpz_manager::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(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 unsigned mpz_manager::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(v) % (static_cast(1) << 32) == 0) { r += 32; v = static_cast(static_cast(v) / (static_cast(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 unsigned mpz_manager::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(ds[sz-1])); #else unsigned r = mpz_sizeinbase(*a.m_ptr, 2); SASSERT(r > 0); return r - 1; #endif } template unsigned mpz_manager::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(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 unsigned mpz_manager::bitsize(mpz const & a) { if (is_nonneg(a)) return log2(a) + 1; else return mlog2(a) + 1; } template bool mpz_manager::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 mpz_manager::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 mpz_manager::decompose(mpz const & a, svector & 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 mpz_manager::divides(mpz const & a, mpz const & b) { _scoped_numeral > 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; template class mpz_manager;