/* -*- c++ -*-
 * 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 <stdlib.h>
#include <string>
#include <memory>

using std::string;
using std::unique_ptr;

#include <gmp.h>

#define BIGINT_PROVIDER "gmp"

class bigint {
    mpz_t z;

    void overwrite_add(const bigint &x, const bigint &a);
    void overwrite_add(const bigint &x, int a);
    void overwrite_sub(const bigint &x, const bigint &a);
    void overwrite_sub(const bigint &x, int a);
    void overwrite_mul(const bigint &x, const bigint &a);
    void overwrite_mul(const bigint &x, int a);
    void overwrite_mul(const bigint &x, unsigned a);
    void overwrite_div(const bigint &x, const bigint &a);
    void overwrite_div(const bigint &x, unsigned a);
    void overwrite_mod(const bigint &x, const bigint &a);
    void overwrite_mod(const bigint &x, unsigned a);
    void overwrite_shl(const bigint &x, unsigned a);
    void overwrite_shr(const bigint &x, unsigned a);

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

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

    operator int() const;
    operator unsigned() const;
    operator bool() const;
    bigint &operator=(const bigint &a);
    bigint &operator=(bigint &&a);
    bigint &operator+=(const bigint &a);
    bigint &operator+=(int a);
    bigint &operator-=(const bigint &a);
    bigint &operator-=(int a);
    bigint &operator*=(const bigint &a);
    bigint &operator*=(int a);
    bigint &operator*=(unsigned a);
    bigint &operator/=(const bigint &a);
    bigint &operator/=(unsigned a);
    bigint &operator%=(const bigint &a);
    bigint &operator%=(unsigned a);
    bigint &operator<<=(unsigned a);
    bigint &operator>>=(unsigned a);
    bigint &operator++();
    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 void bigint_print(FILE *fp, 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 bigint::bigint()
{
    mpz_init(z);
}

inline bigint::bigint(int x)
{
    mpz_init_set_si(z, x);
}

inline bigint::bigint(unsigned x)
{
    mpz_init_set_ui(z, x);
}

inline bigint::bigint(mpz_t x)
{
    mpz_init_set(z, x);
}

inline bigint::bigint(const bigint &x)
{
    mpz_init_set(z, x.z);
}

inline bigint::bigint(const char *s)
{
    mpz_init_set_str(z, s, 0);
}

inline bigint::bigint(bigint &&x) noexcept
{
    mpz_init(z);
    mpz_swap(z, x.z);
}

inline bigint::~bigint()
{
    mpz_clear(z);
}

inline bigint::operator int() const
{
    return mpz_get_si(z);
}

inline bigint::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 bigint::operator bool() const
{
    return mpz_cmp_si(z, 0) != 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

inline void bigint::overwrite_add(const bigint &x, const bigint &a)
{
    mpz_add(z, x.z, a.z);
}

inline void bigint::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 bigint::overwrite_sub(const bigint &x, const bigint &a)
{
    mpz_sub(z, x.z, a.z);
}

inline void bigint::overwrite_sub(const bigint &x, int a)
{
    overwrite_add(x, -a);
}

inline void bigint::overwrite_mul(const bigint &x, const bigint &a)
{
    mpz_mul(z, x.z, a.z);
}

inline void bigint::overwrite_mul(const bigint &x, int a)
{
    mpz_mul_si(z, x.z, a);
}

inline void bigint::overwrite_mul(const bigint &x, unsigned a)
{
    mpz_mul_ui(z, x.z, a);
}

inline void bigint::overwrite_div(const bigint &x, const bigint &a)
{
    mpz_tdiv_q(z, x.z, a.z);
}

inline void bigint::overwrite_div(const bigint &x, unsigned a)
{
    mpz_tdiv_q_ui(z, x.z, a);
}

inline void bigint::overwrite_mod(const bigint &x, const bigint &a)
{
    mpz_tdiv_r(z, x.z, a.z);
}

inline void bigint::overwrite_mod(const bigint &x, unsigned a)
{
    mpz_tdiv_r_ui(z, x.z, a);
}

inline void bigint::overwrite_shl(const bigint &x, unsigned a)
{
    mpz_mul_2exp(z, x.z, a);
}

inline void bigint::overwrite_shr(const bigint &x, unsigned a)
{
    mpz_fdiv_q_2exp(z, x.z, a);
}

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)
{
    int retd = mpz_cmp_si(a.z, 0);
    return retd < 0 ? -1 : retd > 0 ? +1 : 0;
}

inline bigint bigint_sqrt(const bigint &a)
{
    bigint ret;
    mpz_sqrt(ret.z, a.z);
    return ret;
}

inline void bigint_print(FILE *fp, const bigint &a)
{
    mpz_out_str(fp, 10, a.z);
}

inline string bigint_decstring(const bigint &a)
{
    return unique_ptr<char, void (*)(void *)>(
        mpz_get_str(nullptr, 10, a.z), free).get();
}

inline string bigint_hexstring(const bigint &a)
{
    return unique_ptr<char, void (*)(void *)>(
        mpz_get_str(nullptr, 16, a.z), free).get();
}

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);
}

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