#!/usr/bin/env python

import sys


class CType:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name


class GMPAPI:
    def __init__(self, ret_ty, name, *params, **kw):
        out = kw.get("out", [0])
        inout = kw.get("inout", [])
        mixed = kw.get("mixed", False)
        custom = kw.get("custom", False)
        self.name = name
        self.ret_ty = ret_ty
        self.params = params
        self.inout_params = inout
        self.custom_test = custom
        # most functions with return results dont need extra out params
        # set mixed to true to check both the return value and an out param
        if self.ret_ty != void and not mixed:
            self.out_params = []
        else:
            self.out_params = out  # param location of the output result

    def is_write_only(self, pos):
        if pos in self.out_params and pos not in self.inout_params:
            return True
        return False

    def __str__(self):
        return "{} {}({})".format(
            self.ret_ty, self.name, ",".join(map(str, self.params))
        )

    def __repr__(self):
        return str(self)


void = CType("void")
voidp = CType("void *")
charp = CType("char *")
iint = CType("int")
size_t = CType("size_t")
size_tp = CType("size_t*")
ilong = CType("long")
ulong = CType("unsigned long")
mpz_t = CType("mpz_t")
mpq_t = CType("mpq_t")

apis = [
    GMPAPI(void, "mpz_abs", mpz_t, mpz_t),
    GMPAPI(void, "mpz_add", mpz_t, mpz_t, mpz_t),
    GMPAPI(iint, "mpz_cmp_si", mpz_t, ilong),
    GMPAPI(iint, "mpz_cmpabs", mpz_t, mpz_t),
    GMPAPI(iint, "mpz_cmp", mpz_t, mpz_t),
    GMPAPI(void, "mpz_mul", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_neg", mpz_t, mpz_t),
    GMPAPI(void, "mpz_set_si", mpz_t, ilong),
    GMPAPI(void, "mpz_set", mpz_t, mpz_t),
    GMPAPI(void, "mpz_sub", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_swap", mpz_t, mpz_t, out=[0, 1], inout=[0, 1]),
    GMPAPI(iint, "mpz_sgn", mpz_t),
    GMPAPI(void, "mpz_addmul", mpz_t, mpz_t, mpz_t, inout=[0]),
    GMPAPI(void, "mpz_divexact", mpz_t, mpz_t, mpz_t),
    GMPAPI(iint, "mpz_divisible_p", mpz_t, mpz_t),
    GMPAPI(void, "mpz_submul", mpz_t, mpz_t, mpz_t, inout=[0]),
    GMPAPI(void, "mpz_set_ui", mpz_t, ulong),
    GMPAPI(void, "mpz_add_ui", mpz_t, mpz_t, ulong),
    GMPAPI(void, "mpz_divexact_ui", mpz_t, mpz_t, ulong),
    GMPAPI(void, "mpz_mul_ui", mpz_t, mpz_t, ulong),
    GMPAPI(void, "mpz_pow_ui", mpz_t, mpz_t, ulong),
    GMPAPI(void, "mpz_sub_ui", mpz_t, mpz_t, ulong),
    GMPAPI(void, "mpz_cdiv_q", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_fdiv_q", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_fdiv_r", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_tdiv_q", mpz_t, mpz_t, mpz_t),
    GMPAPI(ulong, "mpz_fdiv_q_ui", mpz_t, mpz_t, ulong, out=[0], mixed=True),
    GMPAPI(ilong, "mpz_get_si", mpz_t),
    GMPAPI(ulong, "mpz_get_ui", mpz_t),
    GMPAPI(void, "mpz_gcd", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_lcm", mpz_t, mpz_t, mpz_t),
    GMPAPI(void, "mpz_mul_2exp", mpz_t, mpz_t, ulong),
    GMPAPI(
        void,
        "mpz_export",
        voidp,
        size_tp,
        iint,
        size_t,
        iint,
        size_t,
        mpz_t,
        custom=True,
    ),
    # The mpz_import signature is a bit of a lie, but it is ok because it is custom
    GMPAPI(
        void,
        "mpz_import",
        voidp,
        size_t,
        iint,
        size_t,
        iint,
        size_t,
        mpz_t,
        custom=True,
    ),
    GMPAPI(size_t, "mpz_sizeinbase", mpz_t, iint),
    GMPAPI(charp, "mpz_get_str", charp, iint, mpz_t),
    # mpq functions
    GMPAPI(iint, "mpq_set_str", mpq_t, charp, iint, out=[0], mixed=True),
    GMPAPI(void, "mpq_canonicalize", mpq_t, inout=[0]),
    GMPAPI(iint, "mpq_cmp", mpq_t, mpq_t),
    GMPAPI(void, "mpq_mul", mpq_t, mpq_t, mpq_t),
    GMPAPI(void, "mpq_set", mpq_t, mpq_t),
    GMPAPI(void, "mpq_set_ui", mpq_t, ulong, ulong),
    GMPAPI(iint, "mpq_sgn", mpq_t),
    GMPAPI(charp, "mpq_get_str", charp, iint, mpq_t),
]


def get_api(name):
    for a in apis:
        if a.name == name:
            return a
    raise RuntimeError("Unknown api: {}".format(name))
