/* -*- c++ -*-
 * Internal implementation of bigints, for spigot to use when GMP is
 * not available.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <limits.h>

#include <vector>
#include <string>
#include <utility>
using std::vector;
using std::string;
using std::swap;

#if defined _MSC_VER                   /* no stdint.h, at least in VS2010 */
typedef __int64 signed_dword;
typedef unsigned __int64 dword;
typedef unsigned __int32 word;
#define WORDSIZE 32
#else
#include <stdint.h>
typedef int64_t signed_dword;
typedef uint64_t dword;
typedef uint32_t word;
#define WORDSIZE 32
#endif

#define BIGINT_PROVIDER "internal"

class bigint {
    vector<word> v;
    int sign;                          // must be +1 or -1 at all times

    word word_at(size_t index) const;
    static word add_words(word a, word b, word &carry);
    void ensure_index_exists(size_t index);
    word add_word_at_pos(size_t index, word a, word carry = 0);
    void normalise();
    template<typename Words> void mag_add_in_place(
        Words mag, size_t n, size_t pos = 0);
    template<typename Words> void mag_sub_in_place(
        Words mag, size_t n, size_t pos = 0);
    void add_in_place(const bigint &that, int that_sign);
    void add_word_in_place(word w, int w_sign);
    void add_int_in_place(int i, int i_sign);
    void set_to_int(int i);
    void set_to_word(word w, int input_sign = +1);
    void set_to_unsigned(unsigned u);
    void multiply_word_in_place(word w, int w_sign = +1);
    void multiply_int_in_place(int i, int i_sign = +1);
    void multiply_overwrite_naive(const bigint &a, const bigint &b);
    void multiply_overwrite(const bigint &a, const bigint &b);
    template<typename Words> int compare(
        Words mag, size_t bsize, int bsign) const;
    int compare_bigint(const bigint &b) const;
    int compare_int(int i) const;
    word short_divide(word d);
    template<typename Words> static void debug_pieces(
        int sign, size_t size, Words data, const char *s = "");
    void debug(const char *s = "") const;
    bigint long_divide(const bigint &d);
    word word_at_bit_pos(size_t bitpos) const;
    void shift_right_in_place(size_t sh);
    bigint shift_left(size_t sh) const;

    bigint(const char *s);
    template<char... cs> friend const bigint &operator "" _bi();

  public:
    bigint();
    bigint(int x);
    bigint(unsigned x);
    bigint(const bigint &x);
    bigint(bigint &&x) noexcept;
    ~bigint() = default;

    friend void swap(bigint &a, bigint &b);

    inline operator int() const;
    inline operator unsigned() const;
    inline operator bool() const;
    inline bigint &operator=(bigint a);
    inline bigint &operator+=(const bigint &a);
    inline bigint &operator+=(int a);
    inline bigint &operator-=(const bigint &a);
    inline bigint &operator-=(int a);
    inline bigint &operator*=(const bigint &a);
    inline bigint &operator*=(int a);
    inline bigint &operator*=(unsigned a);
    inline bigint &operator/=(const bigint &a);
    inline bigint &operator/=(unsigned a);
    inline bigint &operator%=(const bigint &a);
    inline bigint &operator%=(unsigned a);
    inline bigint &operator<<=(unsigned a);
    inline bigint &operator>>=(unsigned a);
    inline bigint &operator++();
    inline bigint &operator--();

    friend bigint operator+(const bigint &a, const bigint &b);
    friend bigint operator+(const bigint &a, int b);
    friend bigint operator+(int a, const bigint &b);
    friend bigint operator-(const bigint &a, const bigint &b);
    friend bigint operator-(const bigint &a, int b);
    friend bigint operator-(int a, const bigint &b);
    friend bigint operator*(const bigint &a, const bigint &b);
    friend bigint operator*(const bigint &a, int b);
    friend bigint operator*(const bigint &a, unsigned b);
    friend bigint operator*(int a, const bigint &b);
    friend bigint operator*(unsigned a, const bigint &b);
    friend bigint operator/(const bigint &a, const bigint &b);
    friend bigint operator/(const bigint &a, unsigned b);
    friend bigint fdiv(const bigint &a, const bigint &b);
    friend bigint operator%(const bigint &a, const bigint &b);
    friend bigint operator%(const bigint &a, unsigned b);
    friend bigint operator<<(const bigint &a, unsigned b);
    friend bigint operator>>(const bigint &a, unsigned b);
    friend bigint operator-(const bigint &a);
    friend bool operator==(const bigint &a, const bigint &b);
    friend bool operator!=(const bigint &a, const bigint &b);
    friend bool operator<(const bigint &a, const bigint &b);
    friend bool operator>(const bigint &a, const bigint &b);
    friend bool operator<=(const bigint &a, const bigint &b);
    friend bool operator>=(const bigint &a, const bigint &b);
    friend bool operator==(const bigint &a, int b);
    friend bool operator!=(const bigint &a, int b);
    friend bool operator<(const bigint &a, int b);
    friend bool operator>(const bigint &a, int b);
    friend bool operator<=(const bigint &a, int b);
    friend bool operator>=(const bigint &a, int b);
    friend bool operator==(int a, const bigint &b);
    friend bool operator!=(int a, const bigint &b);
    friend bool operator<(int a, const bigint &b);
    friend bool operator>(int a, const bigint &b);
    friend bool operator<=(int a, const bigint &b);
    friend bool operator>=(int a, const bigint &b);
    friend bigint bigint_abs(const bigint &a);
    friend int bigint_sign(const bigint &a);
    friend bigint bigint_sqrt(const bigint &a);
    friend string bigint_decstring(const bigint &a);
    friend string bigint_hexstring(const bigint &a);
    friend bigint bigint_power(const bigint &x, unsigned y);
    friend unsigned bigint_approxlog2(const bigint &a);
    friend int bigint_bit(const bigint &a, unsigned index);
};

inline void swap(bigint &a, bigint &b)
{
    swap(a.v, b.v);
    swap(a.sign, b.sign);
}

inline bigint::bigint()
    : sign(+1)
{
}

inline bigint::bigint(int x)
    : sign(+1)
{
    set_to_int(x);
}

inline bigint::bigint(unsigned x)
    : sign(+1)
{
    set_to_unsigned(x);
}
inline bigint::bigint(const bigint &x)
    : v(x.v), sign(x.sign)
{
}

inline bigint::bigint(bigint &&x) noexcept
    : bigint()
{
    swap(*this, x);
}

inline word bigint::word_at(size_t index) const
{
    if (index >= v.size())
        return 0;
    return v[index];
}

inline word bigint::add_words(word a, word b, word &carry)
{
    dword sum = (dword)a + b + carry;
    carry = sum >> WORDSIZE;
    return sum;
}

inline void bigint::ensure_index_exists(size_t index)
{
    if (index >= v.size())
        v.resize(index + 1, 0);
}

inline word bigint::add_word_at_pos(size_t index, word a, word carry)
{
    ensure_index_exists(index);
    v[index] = add_words(v[index], a, carry);
    return carry;
}

inline void bigint::normalise()
{
    // Normalise by removing leading zero words.
    size_t index;
    for (index = v.size(); index > 0 && v[index-1] == 0; index--)
        /* do nothing */;
    v.resize(index);
}

template<typename Words>
inline void bigint::mag_add_in_place(Words mag, size_t n, size_t pos)
{
    word carry = 0;
    size_t index = 0;
    ensure_index_exists(n + pos + 1);
    while (index < n) {
        carry = add_word_at_pos(index + pos, mag[index], carry);
        index++;
    }
    while (carry) {
        carry = add_word_at_pos(index + pos, 0, carry);
        index++;
    }
    normalise();
}

template<typename Words>
inline void bigint::mag_sub_in_place(Words mag, size_t n, size_t pos)
{
    word carry = 1;
    size_t index = 0;
    ensure_index_exists(n+1);
    while (index < n) {
        carry = add_word_at_pos(index + pos, ~mag[index], carry);
        index++;
    }
    while (!carry && index < v.size()) {
        carry = add_word_at_pos(index + pos, ~(word)0, carry);
        index++;
    }
    if (!carry) {
        // Sign has flipped.
        sign = -sign;
        carry = 1;
        for (index = 0; index < v.size(); ++index)
            v[index] = add_words(0, ~v[index], carry);
    }

    normalise();
}

inline void bigint::add_in_place(const bigint &that, int that_sign)
{
    if (sign == that.sign * that_sign)
        mag_add_in_place(that.v, that.v.size());
    else
        mag_sub_in_place(that.v, that.v.size());
}

inline void bigint::add_word_in_place(word w, int w_sign)
{
    if (sign == w_sign)
        mag_add_in_place(&w, 1);
    else
        mag_sub_in_place(&w, 1);
}

inline void bigint::add_int_in_place(int i, int i_sign)
{
    // current implementation depends on an int fitting in a word
    enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) };

    if (i < 0)
        add_word_in_place(-i, -i_sign);
    else
        add_word_in_place(+i, +i_sign);
}

inline void bigint::set_to_int(int i)
{
    // current implementation depends on an int fitting in a word
    enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) };

    if (i == 0) {
        v.resize(0);
        sign = +1;
    } else {
        v.resize(1);
        if (i < 0) {
            v[0] = -i;
            sign = -1;
        } else {
            v[0] = +i;
            sign = +1;
        }
    }
}

inline void bigint::set_to_word(word w, int input_sign)
{
    sign = input_sign;
    if (w) {
        v.resize(1);
        v[0] = w;
    } else {
        v.resize(0);
    }
}

inline void bigint::set_to_unsigned(unsigned u)
{
    // current implementation depends on an unsigned fitting in a word
    enum { maxcheck = 1 / (int)(UINT_MAX <= ~(word)0) };
    set_to_word(u);
}

inline void bigint::multiply_word_in_place(word w, int w_sign)
{
    size_t i;

    if (w == 0) {
        v.resize(0);
        return;
    }

    sign *= w_sign;

    word carry = 0;
    for (i = 0; i < v.size(); ++i) {
        dword product = (dword)v[i] * w + carry;
        v[i] = product;
        carry = product >> WORDSIZE;
    }
    if (carry != 0)
        add_word_at_pos(i, carry);
}

inline void bigint::multiply_int_in_place(int i, int i_sign)
{
    if (i < 0)
        multiply_word_in_place(-i, -i_sign);
    else
        multiply_word_in_place(+i, +i_sign);
}

inline void bigint::multiply_overwrite_naive(const bigint &a, const bigint &b)
{
    size_t maxsize, i, j;

    sign = a.sign * b.sign;

    maxsize = a.v.size() + b.v.size() + 1;
    v.resize(maxsize);
    for (i = 0; i < maxsize; i++)
        v[i] = 0;

    for (i = 0; i < a.v.size(); ++i) {
        word carry = 0;
        for (j = 0; j < b.v.size(); ++j) {
            dword product = (dword)a.v[i] * b.v[j];
            carry = ((product >> WORDSIZE) +
                     add_word_at_pos(i+j, product, carry));
        }
        carry = add_word_at_pos(i+j, carry);
        assert(carry == 0);
    }
    normalise();
}

inline void bigint::multiply_overwrite(const bigint &a, const bigint &b)
{
    size_t asize = a.v.size(), bsize = b.v.size();
    /*
     * Deal with really easy cases.
     */
    if (asize < 2 || bsize < 2) {
        if (asize == 0 || bsize == 0) {
            v.resize(0);
            return;
        }
        if (asize == 1) {
            *this = b;
            multiply_word_in_place(a.v[0], a.sign);
            return;
        }
        if (bsize == 1) {
            *this = a;
            multiply_word_in_place(b.v[0], b.sign);
            return;
        }
    }
#if 0
    size_t minsize = (asize < bsize ? asize : bsize);
    if (minsize > 96) {
        /*
         * Karatsuba reduction. Currently conditioned out, because
         * my timings (of 'make test' and 'spigot -d10000 pi')
         * suggested it didn't seem to be helping, no matter what
         * I set the threshold to.
         */
        size_t maxsize = (asize > bsize ? asize : bsize);
        size_t index = maxsize / 2;
        size_t sh = index * WORDSIZE;
        bigint atop = a >> (unsigned)sh, btop = b >> (unsigned)sh;
        bigint abot = a, bbot = b;
        abot.v.resize(index);
        bbot.v.resize(index);
        bigint top = atop * btop;
        bigint bottom = abot * bbot;
        bigint middle = (atop + abot) * (btop + bbot) - top - bottom;
        *this = bottom;
        mag_add_in_place(middle.v, middle.v.size(), index);
        mag_add_in_place(top.v, top.v.size(), index * 2);
    } else
#endif
    {
        multiply_overwrite_naive(a, b);
    }
}

template<typename Words>
inline int bigint::compare(Words mag, size_t bsize, int bsign) const
{
    size_t asize = v.size();
    if (asize == 0 && bsize == 0)
        return 0;
    if (sign != bsign)
        return sign < bsign ? -1 : +1;
    if (asize != bsize)
        return sign * (asize < bsize ? -1 : +1);
    for (size_t index = asize; index-- > 0 ;) {
        if (v[index] != mag[index])
            return sign * (v[index] < mag[index] ? -1 : +1);
    }
    return 0;
}

inline int bigint::compare_bigint(const bigint &b) const
{
    return compare(b.v, b.v.size(), b.sign);
}

inline int bigint::compare_int(int i) const
{
    // current implementation depends on an int fitting in a word
    enum { maxcheck = 1 / (int)(INT_MAX < ~(word)0) };

    word w;

    if (i < 0) {
        w = -i;
        return compare(&w, 1, -1);
    } else {
        w = +i;
        return compare(&w, i > 0, +1);
    }
}

inline word bigint::short_divide(word d)
{
    /*
     * Writes the quotient in place; returns the remainder.
     * Remainder is the absolute value; it should be given the
     * sign of the input.
     */
    assert(d != 0);
    size_t index = v.size();
    word r = 0;
    while (index >= 1) {
        dword n = ((dword)r << WORDSIZE) + word_at(index-1);
        dword q = n / d;
        r = n % d;
        v[index-1] = q;
        index--;
    }
    normalise();
    return r;
}

template<typename Words>
void bigint::debug_pieces(int sign, size_t size, Words data, const char *s)
{
    printf("%s%+d %3d [", s, sign, (int)size);
    for (size_t i = 0; i < size; ++i) {
        printf("%s%08x", i>0 ? " " : "", data[size-1-i]);
    }
    printf("]\n");
}

inline void bigint::debug(const char *s) const
{
    debug_pieces(sign, v.size(), v, s);
}

inline bigint bigint::long_divide(const bigint &d)
{
    /*
     * Writes the remainder in place; returns the quotient.
     */
    assert(d.v.size() != 0);       /* catch div by zero */
    size_t dsize = d.v.size();
    size_t nsize = v.size();
    bigint q;
    if (nsize > dsize)
        q.v.resize(nsize - dsize + 1, 0); /* just to avoid n increments */
    q.sign = sign * d.sign;

    dword dtop = d.v[dsize - 1];
    dtop <<= WORDSIZE;
    if (dsize > 1)
        dtop |= d.v[dsize - 2];
    unsigned dshift = 0;
    for (int sh = WORDSIZE; sh != 0; sh >>= 1) {
        if (!(dtop >> (2*WORDSIZE-sh))) {
            dtop <<= sh;
            dshift += sh;
        }
    }
    dtop >>= WORDSIZE;

    if (nsize + 2 >= dsize) {      /* otherwise skip this loop entirely */
        for (size_t index = nsize - dsize + 2; index-- > 0 ;) {
            dword ntop = word_at(index + dsize);
            ntop <<= WORDSIZE;
            ntop |= word_at(index + dsize - 1);
            ntop <<= dshift;
            if (dshift > 0 && index + dsize >= 2)
                ntop |= word_at(index + dsize - 2) >> (WORDSIZE - dshift);
            dword qword = ntop / dtop;
            if (qword > ~(word)0)
                qword = ~(word)0; /* any apparent overflow will
                                   * not be real */
            dword carry = 1;
            dword product = 0;
            for (size_t j = 0; j < dsize+1 || product; ++j) {
                product += qword * d.word_at(j);
                word prodword = product;
                product >>= WORDSIZE;
                carry = add_word_at_pos(index + j, ~prodword, carry);
            }
            /*
             * We might have overestimated qword by one above -
             * perhaps even by more than one, in some fiddly cases
             * where the digits after ntop are low and those after
             * dtop are high. A simple while loop should suffice
             * to correct it.
             */
            while (!carry) {
                carry = 0;
                for (size_t j = 0; j < dsize+1; ++j) {
                    carry = add_word_at_pos(index + j, d.word_at(j),
                                            carry);
                }
                qword--;
            }
            q.add_word_at_pos(index, qword);
        }
    }

    q.normalise();
    normalise();
    return q;
}

/* bitpos can be as small as -(WORDSIZE-1) and this function will
 * still be expected to work. */
inline word bigint::word_at_bit_pos(size_t bitpos) const
{
    size_t index1 = (bitpos + WORDSIZE) / WORDSIZE;
    size_t sh = (bitpos + WORDSIZE) % WORDSIZE;
    word word0 = index1 > 0 ? word_at(index1 - 1) : 0;
    if (sh == 0)
        return word0;
    word word1 = word_at(index1);
    return (word0 >> sh) | (word1 << (WORDSIZE - sh));
}

inline void bigint::shift_right_in_place(size_t sh)
{
    if (v.size() < sh/WORDSIZE) {
        v.resize(0);
    } else {
        if (sign < 0) {
            // Semantics of shifting a negative number right should be
            // that it rounds towards -inf, i.e. we get floor(n / 2^k)
            //
            // Doing the obvious thing to the magnitude vector will
            // round toward zero, so we start by adding 2^k - 1 to the
            // magnitude.
            word w = (word)1 << (sh % WORDSIZE);
            mag_add_in_place(&w, 1, sh / WORDSIZE);
            w = 1;
            mag_sub_in_place(&w, 1);
        }
        size_t max = v.size() - sh / WORDSIZE;
        for (size_t index = 0; index < max; ++index)
            v[index] = word_at_bit_pos(sh + index * WORDSIZE);
        v.resize(max);
        normalise();
    }
}

inline bigint bigint::shift_left(size_t sh) const
{
    bigint ret;
    ret.sign = sign;
    ret.v.resize(v.size() + (sh + WORDSIZE - 1) / WORDSIZE, 0);
    size_t start = sh / WORDSIZE;
    size_t offset = sh % WORDSIZE;
    for (size_t i = 0; i < v.size() + 1; ++i)
        ret.add_word_at_pos(i + start,
                            word_at_bit_pos(i * WORDSIZE - offset));
    ret.normalise();
    return ret;
}

inline bigint::operator int() const
{
    // current implementation depends on a word fitting in an unsigned
    enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) };
    return (unsigned)word_at(0) * (unsigned)sign;
}

inline bigint::operator unsigned() const
{
    // current implementation depends on a word fitting in an unsigned
    enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) };
    return (unsigned)word_at(0) * (unsigned)sign;
}

inline bigint::operator bool() const
{
    return v.size() != 0;
}

inline bigint &bigint::operator=(bigint a)
{
    swap(*this, a);
    return *this;
}

inline bigint &bigint::operator+=(const bigint &a)
{
    add_in_place(a, +1);
    return *this;
}

inline bigint &bigint::operator+=(int a)
{
    add_int_in_place(a, +1);
    return *this;
}

inline bigint &bigint::operator-=(const bigint &a)
{
    add_in_place(a, -1);
    return *this;
}

inline bigint &bigint::operator-=(int a)
{
    add_int_in_place(a, -1);
    return *this;
}

inline bigint &bigint::operator*=(const bigint &a)
{
    return *this = (*this) * a;
}

inline bigint &bigint::operator*=(int a)
{
    multiply_int_in_place(a);
    return *this;
}

inline bigint &bigint::operator*=(unsigned a)
{
    multiply_word_in_place(a);
    return *this;
}

inline bigint &bigint::operator/=(const bigint &a)
{
    return *this = long_divide(a);
}

inline bigint &bigint::operator/=(unsigned a)
{
    // current implementation depends on a word fitting in an unsigned
    enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) };
    short_divide(a);
    return *this;
}

inline bigint &bigint::operator%=(const bigint &a)
{
    long_divide(a);
    return *this;
}

inline bigint &bigint::operator%=(unsigned a)
{
    // current implementation depends on a word fitting in an unsigned
    enum { maxcheck = 1 / (int)(UINT_MAX >= ~(word)0) };
    set_to_word(short_divide(a), sign);
    return *this;
}

inline bigint &bigint::operator<<=(unsigned a)
{
    return *this = shift_left(a);
}

inline bigint &bigint::operator>>=(unsigned a)
{
    shift_right_in_place(a);
    return *this;
}

inline bigint &bigint::operator++()
{
    add_word_in_place(1, +1);
    return *this;
}

inline bigint &bigint::operator--()
{
    add_word_in_place(1, -1);
    return *this;
}

inline bigint operator+(const bigint &a, const bigint &b)
{
    bigint ret = a;
    return ret += b;
}

inline bigint operator+(const bigint &a, int b)
{
    bigint ret = a;
    return ret += b;
}

inline bigint operator+(int a, const bigint &b)
{
    bigint ret = b;
    return ret += a;
}

inline bigint operator-(const bigint &a, const bigint &b)
{
    bigint ret = a;
    return ret -= b;
}

inline bigint operator-(const bigint &a, int b)
{
    bigint ret = a;
    return ret -= b;
}

inline bigint operator-(int a, const bigint &b)
{
    bigint ret = b;
    ret.sign = -ret.sign;
    return ret += a;
}

inline bigint operator*(const bigint &a, const bigint &b)
{
    bigint ret;
    ret.multiply_overwrite(a, b);
    return ret;
}

inline bigint operator*(const bigint &a, int b)
{
    bigint ret = a;
    return ret *= b;
}

inline bigint operator*(const bigint &a, unsigned b)
{
    bigint ret = a;
    return ret *= b;
}

inline bigint operator*(int a, const bigint &b)
{
    bigint ret = b;
    return ret *= a;
}

inline bigint operator*(unsigned a, const bigint &b)
{
    bigint ret = b;
    return ret *= a;
}

inline bigint operator/(const bigint &a, const bigint &b)
{
    bigint tmp = a;
    return tmp.long_divide(b);
}

inline bigint operator/(const bigint &a, unsigned b)
{
    bigint ret = a;
    return ret /= b;
}

inline bigint fdiv(const bigint &n, const bigint &d) {
    bigint q, r = n;
    q = r.long_divide(d);
    if (bigint_sign(r) * bigint_sign(d) < 0)
        --q;
    return q;
}
inline bigint operator%(const bigint &a, const bigint &b)
{
    bigint ret = a;
    ret.long_divide(b);
    return ret;
}

inline bigint operator%(const bigint &a, unsigned b)
{
    bigint ret = a;
    return ret %= b;
}

inline bigint operator<<(const bigint &a, unsigned b)
{
    return a.shift_left(b);
}

inline bigint operator>>(const bigint &a, unsigned b)
{
    bigint ret = a;
    return ret >>= b;
}

inline bigint operator-(const bigint &a)
{
    bigint ret = a;
    ret.sign = -ret.sign;
    return ret;
}

inline bool operator==(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) == 0;
}

inline bool operator!=(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) != 0;
}

inline bool operator<(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) < 0;
}

inline bool operator>(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) > 0;
}

inline bool operator<=(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) <= 0;
}

inline bool operator>=(const bigint &a, const bigint &b)
{
    return a.compare_bigint(b) >= 0;
}

inline bool operator==(const bigint &a, int b)
{
    return a.compare_int(b) == 0;
}

inline bool operator!=(const bigint &a, int b)
{
    return a.compare_int(b) != 0;
}

inline bool operator<(const bigint &a, int b)
{
    return a.compare_int(b) < 0;
}

inline bool operator>(const bigint &a, int b)
{
    return a.compare_int(b) > 0;
}

inline bool operator<=(const bigint &a, int b)
{
    return a.compare_int(b) <= 0;
}

inline bool operator>=(const bigint &a, int b)
{
    return a.compare_int(b) >= 0;
}

inline bool operator==(int a, const bigint &b)
{
    return b.compare_int(a) == 0;
}

inline bool operator!=(int a, const bigint &b)
{
    return b.compare_int(a) != 0;
}

inline bool operator<(int a, const bigint &b)
{
    return b.compare_int(a) > 0;
}

inline bool operator>(int a, const bigint &b)
{
    return b.compare_int(a) < 0;
}

inline bool operator<=(int a, const bigint &b)
{
    return b.compare_int(a) >= 0;
}

inline bool operator>=(int a, const bigint &b)
{
    return b.compare_int(a) <= 0;
}

inline bigint bigint_abs(const bigint &a)
{
    bigint ret = a;
    ret.sign = +1;
    return ret;
}

inline int bigint_sign(const bigint &a)
{
    return (a.v.size() == 0) ? 0 : a.sign;
}

inline bigint bigint_sqrt(const bigint &n)
{
    bigint d, a, b, di;

    d = n;
    a = 0;
    b = 0;
    b.add_word_at_pos(n.v.size(),
                      (word)1 << ((WORDSIZE-1) & ~1)); /* big power of 4 */
    do {
        a >>= 1;
        di = 2*a + b;
        if (di <= d) {
            d -= di;
            a += b;
        }
        b >>= 2;
    } while (b);

    return a;
}

inline string bigint_decstring(const bigint &a)
{
    if (a.v.size() == 0)
        return "0";

    bigint tmp = a;

    /* 146/485 is an overestimate of log10(2) */
    size_t maxchars = (a.v.size() * WORDSIZE * 146 + 484) / 485;
    string buf(maxchars, 'x');
    size_t index = maxchars;
    while (tmp.v.size() != 0) {
        assert(index > 0);
        buf[--index] = '0' + tmp.short_divide(10);
    }

    return (a.sign < 0 && a.v.size() > 0 ? "-" : "") + buf.substr(index);
}

inline void bigint_print(FILE *fp, const bigint &a)
{
    string buf = bigint_decstring(a);
    fwrite(buf.c_str(), 1, buf.size(), fp);
}

inline string bigint_hexstring(const bigint &a)
{
    size_t asize = a.v.size();
    if (asize == 0)
        return "0";
    size_t nchars = asize * WORDSIZE / 4;

    word top = a.v[asize - 1];
    for (size_t sh = WORDSIZE - 4; sh > 0; sh -= 4) {
        if (!(top >> sh))
            nchars--;
    }

    string buf(nchars, 'x');
    for (size_t digit = 0; digit < nchars; digit++) {
        word w = a.word_at(digit / (WORDSIZE / 4));
        w >>= 4 * (digit % (WORDSIZE / 4));
        buf[nchars - 1 - digit] = "0123456789abcdef"[w & 0xF];
    }
    return (a.sign < 0 && a.v.size() > 0 ? "-" : "") + buf;
}

inline bigint bigint_power(const bigint &x, unsigned y)
{
    bigint ret = 1;
    bigint curr = x;
    unsigned bit = 1;
    while (y) {
        if (y & bit) {
            ret *= curr;
            y &= ~bit;
        }
        bit <<= 1;
        curr *= curr;
    }
    return ret;
}

inline unsigned bigint_approxlog2(const bigint &a)
{
    if (!a.v.size())
        return 0;
    /* The value we will return if the top bit of the topmost word is set */
    unsigned ret = a.v.size() * WORDSIZE - 1;
    /* Correct for the top bit of the topmost bit not being set */
    word w = a.v[a.v.size() - 1];
    for (unsigned sh = WORDSIZE/2; sh != 0; sh >>= 1) {
        if (!(w >> (WORDSIZE - sh))) {
            w <<= sh;
            ret -= sh;
        }
    }
    return ret;
}

inline int bigint_bit(const bigint &a, unsigned index)
{
    word w = a.word_at(index / WORDSIZE);
    w >>= index % WORDSIZE;
    return w & 1;
}

inline bigint::bigint(const char *s) : bigint()
{
    unsigned base = 10;
    if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
        base = 16;
        s += 2;
    } else if (s[0] == '0' && (s[1] == 'b' || s[1] == 'B')) {
        base = 2;
        s += 2;
    } else if (s[0] == '0' && s[1]) {
        base = 8;
        s++;
    }
    while (*s) {
        char c = *s++;
        int digit = (c >= '0' && c <= '9' ? c-'0' :
                     c >= 'A' && c <= 'Z' ? c-'A'+10 :
                     c >= 'a' && c <= 'z' ? c-'a'+10 :
                     -1);
        assert(digit >= 0);
        assert((unsigned)digit < base);
        multiply_word_in_place(base);
        add_int_in_place(digit, +1);
    }
}

template<char... cs>
inline const bigint &operator "" _bi()
{
    static const char literal_string[] = { cs... , '\0' };
    static const bigint z { literal_string };
    return z;
}
