/*
 * bigint.h: Implementation of big integers by wrapping GMP in a C++
 * class.
 *
 * (I tried using gmp++, but found it didn't quite give a good enough
 * illusion of bigints being a native type - I forget what, but there
 * was some way in which it did something confusing.)
 */

#include <stdio.h>
#include <gmp.h>

#define BIGINT_PROVIDER "gmp"

class bigint {
  public:
    inline bigint() { mpz_init(z); }
    inline bigint(int x) { mpz_init_set_si(z, x); }
    inline bigint(unsigned x) { mpz_init_set_ui(z, x); }
    inline bigint(mpz_t x) { mpz_init_set(z, x); }
    inline bigint(const bigint &x) { mpz_init_set(z, x.z); }
    inline ~bigint() { mpz_clear(z); }

    inline operator int() const { return mpz_get_si(z); }
    inline operator unsigned() const {
        unsigned ret = mpz_get_ui(z);
        /* mpz_get_ui returns the absolute value, so we might need to negate */
        if (mpz_cmp_si(z, 0) < 0)
            ret = -ret;
        return ret;
    }
    inline operator bool() const { return mpz_cmp_si(z, 0) != 0; }
    inline bigint &operator=(const bigint &a) { mpz_set(z,a.z); return *this; }
    inline bigint &operator+=(const bigint &a) { overwrite_add(*this, a); return *this; }
    inline bigint &operator+=(int a) { overwrite_add(*this, a); return *this; }
    inline bigint &operator-=(const bigint &a) { overwrite_sub(*this, a); return *this; }
    inline bigint &operator-=(int a) { overwrite_sub(*this, a); return *this; }
    inline bigint &operator*=(const bigint &a) { overwrite_mul(*this, a); return *this; }
    inline bigint &operator*=(int a) { overwrite_mul(*this, a); return *this; }
    inline bigint &operator*=(unsigned a) { overwrite_mul(*this, a); return *this; }
    inline bigint &operator/=(const bigint &a) { overwrite_div(*this, a); return *this; }
    inline bigint &operator/=(unsigned a) { overwrite_div(*this, a); return *this; }
    inline bigint &operator%=(const bigint &a) { overwrite_mod(*this, a); return *this; }
    inline bigint &operator%=(unsigned a) { overwrite_mod(*this, a); return *this; }
    inline bigint &operator<<=(unsigned a) { overwrite_shl(*this, a); return *this; }
    inline bigint &operator>>=(unsigned a) { overwrite_shr(*this, a); return *this; }
    inline bigint &operator++() { mpz_add_ui(z,z,1); return *this; }
    inline bigint &operator--() { mpz_sub_ui(z,z,1); return *this; }
  private:
    mpz_t z;

    inline void overwrite_add(const bigint &x, const bigint &a) { mpz_add(z,x.z,a.z); }
    inline void overwrite_add(const bigint &x, int a) { if (a > 0) mpz_add_ui(z,x.z,a); else mpz_sub_ui(z,x.z,-a); }
    inline void overwrite_sub(const bigint &x, const bigint &a) { mpz_sub(z,x.z,a.z); }
    inline void overwrite_sub(const bigint &x, int a) { overwrite_add(x,-a); }
    inline void overwrite_mul(const bigint &x, const bigint &a) { mpz_mul(z,x.z,a.z); }
    inline void overwrite_mul(const bigint &x, int a) { mpz_mul_si(z,x.z,a); }
    inline void overwrite_mul(const bigint &x, unsigned a) { mpz_mul_ui(z,x.z,a); }
    inline void overwrite_div(const bigint &x, const bigint &a) { mpz_tdiv_q(z,x.z,a.z); }
    inline void overwrite_div(const bigint &x, unsigned a) { mpz_tdiv_q_ui(z,x.z,a); }
    inline void overwrite_mod(const bigint &x, const bigint &a) { mpz_tdiv_r(z,x.z,a.z); }
    inline void overwrite_mod(const bigint &x, unsigned a) { mpz_tdiv_r_ui(z,x.z,a); }
    inline void overwrite_shl(const bigint &x, unsigned a) { mpz_mul_2exp(z,x.z,a); }
    inline void overwrite_shr(const bigint &x, unsigned a) { mpz_fdiv_q_2exp(z,x.z,a); }

    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 void bigint_print(const bigint &a);
    friend char *bigint_decstring(const bigint &a);
    friend char *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 bigint operator+(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_add(a,b); return ret; }
inline bigint operator+(const bigint &a, int b) { bigint ret; ret.overwrite_add(a,b); return ret; }
inline bigint operator+(int a, const bigint &b) { bigint ret; ret.overwrite_add(a,b); return ret; }
inline bigint operator-(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_sub(a,b); return ret; }
inline bigint operator-(const bigint &a, int b) { bigint ret; ret.overwrite_sub(a,b); return ret; }
inline bigint operator-(int a, const bigint &b) { bigint ret; mpz_neg(ret.z, b.z); return ret += a; }
inline bigint operator*(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_mul(a,b); return ret; }
inline bigint operator*(const bigint &a, int b) { bigint ret; ret.overwrite_mul(a,b); return ret; }
inline bigint operator*(const bigint &a, unsigned b) { bigint ret; ret.overwrite_mul(a,b); return ret; }
inline bigint operator*(int a, const bigint &b) { bigint ret; ret.overwrite_mul(b,a); return ret; }
inline bigint operator*(unsigned a, const bigint &b) { bigint ret; ret.overwrite_mul(b,a); return ret; }
inline bigint operator/(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_div(a,b); return ret; }
inline bigint operator/(const bigint &a, unsigned b) { bigint ret; ret.overwrite_div(a,b); return ret; }
inline bigint fdiv(const bigint &a, const bigint &b) { bigint ret; mpz_fdiv_q(ret.z, a.z, b.z); return ret; }
inline bigint operator%(const bigint &a, const bigint &b) { bigint ret; ret.overwrite_mod(a,b); return ret; }
inline bigint operator%(const bigint &a, unsigned b) { bigint ret; ret.overwrite_mod(a,b); return ret; }
inline bigint operator<<(const bigint &a, unsigned b) { bigint ret; ret.overwrite_shl(a,b); return ret; }
inline bigint operator>>(const bigint &a, unsigned b) { bigint ret; ret.overwrite_shr(a,b); return ret; }
inline bigint operator-(const bigint &a) { bigint ret; mpz_neg(ret.z,a.z); return ret; }
inline bool operator==(const bigint &a, const bigint &b) { return 0 == mpz_cmp(a.z,b.z); }
inline bool operator!=(const bigint &a, const bigint &b) { return 0 != mpz_cmp(a.z,b.z); }
inline bool operator<(const bigint &a, const bigint &b) { return 0 > mpz_cmp(a.z,b.z); }
inline bool operator>(const bigint &a, const bigint &b) { return 0 < mpz_cmp(a.z,b.z); }
inline bool operator<=(const bigint &a, const bigint &b) { return 0 >= mpz_cmp(a.z,b.z); }
inline bool operator>=(const bigint &a, const bigint &b) { return 0 <= mpz_cmp(a.z,b.z); }
inline bool operator==(const bigint &a, int b) { return 0 == mpz_cmp_si(a.z,b); }
inline bool operator!=(const bigint &a, int b) { return 0 != mpz_cmp_si(a.z,b); }
inline bool operator<(const bigint &a, int b) { return 0 > mpz_cmp_si(a.z,b); }
inline bool operator>(const bigint &a, int b) { return 0 < mpz_cmp_si(a.z,b); }
inline bool operator<=(const bigint &a, int b) { return 0 >= mpz_cmp_si(a.z,b); }
inline bool operator>=(const bigint &a, int b) { return 0 <= mpz_cmp_si(a.z,b); }
inline bool operator==(int a, const bigint &b) { return 0 == mpz_cmp_si(b.z,a); }
inline bool operator!=(int a, const bigint &b) { return 0 != mpz_cmp_si(b.z,a); }
inline bool operator<(int a, const bigint &b) { return 0 < mpz_cmp_si(b.z,a); }
inline bool operator>(int a, const bigint &b) { return 0 > mpz_cmp_si(b.z,a); }
inline bool operator<=(int a, const bigint &b) { return 0 <= mpz_cmp_si(b.z,a); }
inline bool operator>=(int a, const bigint &b) { return 0 >= mpz_cmp_si(b.z,a); }
inline bigint bigint_abs(const bigint &a) { bigint ret; mpz_abs(ret.z,a.z); return ret; }

inline int bigint_sign(const bigint &a) { return mpz_cmp_si(a.z, 0); }
inline bigint bigint_sqrt(const bigint &a) { bigint ret; mpz_sqrt(ret.z,a.z); return ret; }
inline void bigint_print(const bigint &a) { mpz_out_str(stdout,10,a.z); }
inline char *bigint_decstring(const bigint &a) { return mpz_get_str(NULL,10,a.z); }
inline char *bigint_hexstring(const bigint &a) { return mpz_get_str(NULL,16,a.z); }
inline bigint bigint_power(const bigint &x, unsigned y) { bigint ret; mpz_pow_ui(ret.z,x.z,y); return ret; }
/* mpz_sizeinbase with base==2 is documented, rather vaguely, as 'can
 * be used to locate the most significant 1 bit, counting from 1',
 * which I interpret as saying that it returns 1 + floor(log2(x)). We
 * want exactly floor(log2(x)), so subtract 1, clipping at zero. */
inline unsigned bigint_approxlog2(const bigint &a) { unsigned ret = mpz_sizeinbase(a.z, 2); return ret < 1 ? 0 : ret-1; }
inline int bigint_bit(const bigint &a, unsigned index) { return mpz_tstbit(a.z, index); }
