from sage.libs.ntl.ntl_ZZ_p_decl cimport ZZ_p_c
from sage.libs.ntl.ntl_vec_ZZ_p_decl cimport vec_ZZ_p_c
from sage.libs.ntl.ntl_ZZ_decl cimport ZZ_c
from sage.libs.ntl.ntl_ZZX_decl cimport ZZX_c
from sage.libs.ntl.ntl_ZZ_pContext_decl cimport ZZ_pContext_c


cdef extern from "ntl_wrap.h":
    #### ZZ_pX_c
    ctypedef struct ZZ_pX_c "struct ZZ_pX":
        void *rep
        void (* SetMaxLength)(long n)

    ZZ_pX_c* ZZ_pX_new "New<ZZ_pX>"()
    ZZ_pX_c* ZZ_pX_construct "Construct<ZZ_pX>"(void *mem)
    void ZZ_pX_destruct "Destruct<ZZ_pX>"(ZZ_pX_c *mem)
    void ZZ_pX_delete "Delete<ZZ_pX>"(ZZ_pX_c *mem)
    void ZZ_pX_from_str "_from_str<ZZ_pX>"(ZZ_pX_c* dest, char* s)
    object ZZ_pX_to_PyString "_to_PyString<ZZ_pX>"(ZZ_pX_c *x)
    #int ZZ_pX_equal "_equal<ZZ_pX>"(ZZ_pX_c x, ZZ_pX_c y)

    long ZZ_pX_equal "operator=="(ZZ_pX_c a, ZZ_pX_c b)
    long ZZ_pX_IsZero "IsZero"(ZZ_pX_c a)
    long ZZ_pX_IsOne "IsOne"(ZZ_pX_c a)

    void ZZ_pX_add "add"( ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_add_long "add"(ZZ_pX_c x, ZZ_pX_c a, long b)
    void ZZ_pX_sub "sub"( ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_sub_long "sub"(ZZ_pX_c x, long a, ZZ_pX_c b)
    void ZZ_pX_negate "negate"(ZZ_pX_c x, ZZ_pX_c a)

    void ZZ_pX_mul "mul"( ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_mul_long "mul"( ZZ_pX_c x, ZZ_pX_c a, long b)
    void ZZ_pX_mul_ZZ_p "mul"( ZZ_pX_c x, ZZ_pX_c a, ZZ_p_c b)
    void ZZ_pX_rmul "mul"( ZZ_pX_c x, ZZ_pX_c a, ZZ_p_c b)
    void ZZ_pX_sqr "sqr"( ZZ_pX_c x, ZZ_pX_c a)
    long ZZ_pX_power "power"( ZZ_pX_c x, ZZ_pX_c a, long e)

    void ZZ_pX_LeftShift "LeftShift"(ZZ_pX_c x, ZZ_pX_c a, long n)
    void ZZ_pX_RightShift "RightShift"(ZZ_pX_c x, ZZ_pX_c a, long n)

    void ZZ_pX_DivRem "DivRem"(ZZ_pX_c q, ZZ_pX_c r, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_div_ZZ "div"( ZZ_pX_c q, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_div "div"( ZZ_pX_c q, ZZ_pX_c a, ZZ_pX_c b)
    long ZZ_pX_divide "divide"( ZZ_pX_c q, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_rem "rem"(ZZ_pX_c r, ZZ_pX_c a, ZZ_pX_c b)

    void ZZ_pX_GCD "GCD"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_PlainXGCD "PlainXGCD"(ZZ_pX_c d, ZZ_pX_c s, ZZ_pX_c t, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_XGCD "XGCD"(ZZ_pX_c d, ZZ_pX_c s, ZZ_pX_c t, ZZ_pX_c a, ZZ_pX_c b)

    long ZZ_pX_deg "deg"( ZZ_pX_c x )
    ZZ_p_c ZZ_pX_coeff "coeff"(ZZ_pX_c a, long i)
    ZZ_p_c ZZ_pX_LeadCoeff "LeadCoeff"(ZZ_pX_c x)
    ZZ_p_c ZZ_pX_ConstTerm "ConstTerm"(ZZ_pX_c x)
    void ZZ_pX_SetCoeff "SetCoeff"(ZZ_pX_c x, long i, ZZ_p_c a)
    void ZZ_pX_SetCoeff_long "SetCoeff"(ZZ_pX_c x, long i, long a)
    void ZZ_pX_SetX "SetX"(ZZ_pX_c x)
    long ZZ_pX_IsX "IsX"(ZZ_pX_c a)
    void ZZ_pX_diff "diff"(ZZ_pX_c x, ZZ_pX_c a)
    void ZZ_pX_MakeMonic "MakeMonic"(ZZ_pX_c x)
    void ZZ_pX_reverse_hi "reverse"(ZZ_pX_c x, ZZ_pX_c a, long hi)
    void ZZ_pX_reverse "reverse"(ZZ_pX_c x, ZZ_pX_c a)
    void ZZ_pX_VectorCopy "VectorCopy"(vec_ZZ_p_c x, ZZ_pX_c a, long n)

    void ZZ_pX_random "random"(ZZ_pX_c x, long n)

    void ZZ_pX_BuildFromRoots "BuildFromRoots"(ZZ_pX_c x, vec_ZZ_p_c a)
    void ZZ_pX_eval "eval"(ZZ_p_c fa, ZZ_pX_c f, ZZ_p_c a)
    void ZZ_pX_eval_vec "eval"(vec_ZZ_p_c fa, ZZ_pX_c f, vec_ZZ_p_c a)
    void ZZ_pX_interpolate "interpolate"(ZZ_pX_c f, vec_ZZ_p_c a, vec_ZZ_p_c b)

    void ZZ_pX_trunc "trunc"(ZZ_pX_c x, ZZ_pX_c a, long n)
    void ZZ_pX_MulTrunc "MulTrunc"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b, long n)
    void ZZ_pX_SqrTrunc "SqrTrunc"(ZZ_pX_c x, ZZ_pX_c a, long n)
    void ZZ_pX_InvTrunc "InvTrunc"(ZZ_pX_c x, ZZ_pX_c a, long n)

    void ZZ_pX_MulMod "MulMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b, ZZ_pX_c f)
    void ZZ_pX_SqrMod "SqrMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c f)
    void ZZ_pX_MulByXMod "MulByXMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c f)
    void ZZ_pX_InvMod "InvMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c f)
    long ZZ_pX_InvModStatus "InvModStatus"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c f)

    ctypedef struct ZZ_pX_Modulus_c "struct ZZ_pXModulus":
        ZZ_pX_c (* val) ( )

    ZZ_pX_Modulus_c* ZZ_pX_Modulus_new "New<ZZ_pXModulus>"()
    ZZ_pX_Modulus_c* ZZ_pX_Modulus_construct "Construct<ZZ_pXModulus>"(void *mem)
    void ZZ_pX_Modulus_destruct "Destruct<ZZ_pXModulus>"(ZZ_pX_Modulus_c *mem)
    void ZZ_pX_Modulus_delete "Delete<ZZ_pXModulus>"(ZZ_pX_Modulus_c *mem)
    void ZZ_pX_Modulus_from_str "_from_str<ZZ_pXModulus>"(ZZ_pX_Modulus_c* dest, char* s)
    void ZZ_pX_Modulus_build "build"(ZZ_pX_Modulus_c F, ZZ_pX_c f) # MUST be called before using the modulus
    long ZZ_pX_Modulus_deg "deg"(ZZ_pX_Modulus_c F)

    void ZZ_pX_MulMod_pre "MulMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_c b, ZZ_pX_Modulus_c F)
    void ZZ_pX_SqrMod_pre "SqrMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerMod_pre "PowerMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_c e, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerMod_long_pre "PowerMod"(ZZ_pX_c x, ZZ_pX_c a, long e, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerXMod_pre "PowerXMod"(ZZ_pX_c x, ZZ_c e, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerXMod_long_pre "PowerXMod"(ZZ_pX_c x, long e, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerXPlusAMod_pre "PowerXPlusAMod"(ZZ_pX_c x, ZZ_p_c a, ZZ_c e, ZZ_pX_Modulus_c F)
    void ZZ_pX_PowerXPlusAMod_long_pre "PowerXPlusAMod"(ZZ_pX_c x, ZZ_p_c a, long e, ZZ_pX_Modulus_c F)
    void ZZ_pX_rem_pre "rem"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F)
    void ZZ_pX_DivRem_pre "DivRem"(ZZ_pX_c q, ZZ_pX_c r, ZZ_pX_c a, ZZ_pX_Modulus_c F)
    void ZZ_pX_div_pre "div"(ZZ_pX_c q, ZZ_pX_c a, ZZ_pX_Modulus_c F)
    void ZZ_pX_InvMod_pre "InvMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F)

    ctypedef struct ZZ_pX_Multiplier_c "struct ZZ_pXMultiplier":
        ZZ_pX_c (* val) ( )
    ZZ_pX_Multiplier_c* ZZ_pX_Multiplier_new "New<ZZ_pXMultiplier>"()
    ZZ_pX_Multiplier_c* ZZ_pX_Multiplier_construct "Construct<ZZ_pXMultiplier>"(void *mem)
    void ZZ_pX_Multiplier_destruct "Destruct<ZZ_pXMultiplier>"(ZZ_pX_Multiplier_c *mem)
    void ZZ_pX_Multiplier_delete "Delete<ZZ_pXMultiplier>"(ZZ_pX_Multiplier_c *mem)
    void ZZ_pX_Multiplier_from_str "_from_str<ZZ_pXMultiplier>"(ZZ_pX_Multiplier_c* dest, char* s)
    void ZZ_pX_Multiplier_build "build"(ZZ_pX_Multiplier_c F, ZZ_pX_c b, ZZ_pX_Modulus_c F) # MUST be called before using the multiplier
    void ZZ_pX_MulMod_premul "MulMod"(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Multiplier_c B, ZZ_pX_Modulus_c F)

    void ZZ_pX_CompMod "CompMod"(ZZ_pX_c x, ZZ_pX_c g, ZZ_pX_c h, ZZ_pX_Modulus_c F)
    # Add other composition functions here
    # Add power projection routines
    # Add minimum polynomials of recurrence sequences
    void ZZ_pX_MinPolyMod "MinPolyMod"(ZZ_pX_c h, ZZ_pX_c g, ZZ_pX_c f)
    void ZZ_pX_MinPolyMod_pre "MinPolyMod"(ZZ_pX_c h, ZZ_pX_c g, ZZ_pX_Modulus_c F)

    void ZZ_pX_TraceMod "TraceMod"(ZZ_p_c x, ZZ_pX_c a, ZZ_pX_c f)
    void ZZ_pX_TraceMod_pre "TraceMod"(ZZ_p_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F)
    void ZZ_pX_TraceVec "TraceVec"(vec_ZZ_p_c S, ZZ_pX_c f)
    void ZZ_pX_NormMod "NormMod"(ZZ_p_c x, ZZ_pX_c a, ZZ_pX_c f)
    void ZZ_pX_resultant "resultant"(ZZ_p_c x, ZZ_pX_c a, ZZ_pX_c b)
    void ZZ_pX_CharPolyMod "CharPolyMod"(ZZ_pX_c g, ZZ_pX_c a, ZZ_pX_c f)

    void ZZ_pX_clear "clear"(ZZ_pX_c x)
    void ZZ_pX_set "set"(ZZ_pX_c x)

    void ZZ_pX_to_ZZX "conv"(ZZX_c x, ZZ_pX_c a)
    void ZZX_to_ZZ_pX "conv"(ZZ_pX_c x, ZZX_c a)

    #char* ZZ_pX_repr(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_copy(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_mod(ZZ_pX_c* x, ZZ_pX_c* y)
    #void ZZ_pX_quo_rem(ZZ_pX_c* x, ZZ_pX_c* other, ZZ_pX_c** r, ZZ_pX_c** q)
    #ZZ_pX_c* ZZ_pX_square(ZZ_pX_c* x)
    #int ZZ_pX_is_monic(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_neg(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_left_shift(ZZ_pX_c* x, long n)
    #ZZ_pX_c* ZZ_pX_right_shift(ZZ_pX_c* x, long n)
    #ZZ_pX_c* ZZ_pX_gcd(ZZ_pX_c* x, ZZ_pX_c* y)
    #ZZ_pX_c* ZZ_pX_xgcd(ZZ_pX_c** d, ZZ_pX_c** s, ZZ_pX_c** t, ZZ_pX_c* a, ZZ_pX_c* b)
    #ZZ_pX_c* ZZ_pX_plain_xgcd(ZZ_pX_c** d, ZZ_pX_c** s, ZZ_pX_c** t, ZZ_pX_c* a, ZZ_pX_c* b)
    #void ZZ_pX_set_x(ZZ_pX_c* x)
    #int ZZ_pX_is_x(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_derivative(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_reverse(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_reverse_hi(ZZ_pX_c* x, long hi)
    #ZZ_pX_c* ZZ_pX_truncate(ZZ_pX_c* x, long m)
    #ZZ_pX_c* ZZ_pX_multiply_and_truncate(ZZ_pX_c* x, ZZ_pX_c* y, long m)
    #ZZ_pX_c* ZZ_pX_square_and_truncate(ZZ_pX_c* x, long m)
    #ZZ_pX_c* ZZ_pX_invert_and_truncate(ZZ_pX_c* x, long m)
    #ZZ_pX_c* ZZ_pX_multiply_mod(ZZ_pX_c* x, ZZ_pX_c* y,  ZZ_pX_c* modulus)
    #ZZ_p_c* ZZ_pX_trace_mod(ZZ_pX_c* x, ZZ_pX_c* y)
    char* ZZ_pX_trace_list(ZZ_pX_c* x)
    #ZZ_p_c* ZZ_pX_resultant(ZZ_pX_c* x, ZZ_pX_c* y)
    #ZZ_p_c* ZZ_pX_norm_mod(ZZ_pX_c* x, ZZ_pX_c* y)
    #ZZ_p_c* ZZ_pX_discriminant(ZZ_pX_c* x)
    #ZZ_pX_c* ZZ_pX_charpoly_mod(ZZ_pX_c* x, ZZ_pX_c* y)
    #ZZ_pX_c* ZZ_pX_minpoly_mod(ZZ_pX_c* x, ZZ_pX_c* y)
    #void ZZ_pX_preallocate_space(ZZ_pX_c* x, long n)

    void ZZ_pX_factor(ZZ_pX_c*** v, long** e, long* n, ZZ_pX_c* x, long verbose)
    void ZZ_pX_linear_roots(ZZ_p_c*** v, long* n, ZZ_pX_c* x)

    # The following are ZZ_pX functions written in ntl_wrap, used for padics.

    void ZZ_pX_conv_modulus(ZZ_pX_c fout, ZZ_pX_c fin, ZZ_pContext_c c)
    void ZZ_pX_min_val_coeff(long valuation, long index, ZZ_pX_c f, ZZ_c p)
    long ZZ_pX_get_val_coeff(ZZ_pX_c f, ZZ_c p, long i)
    void ZZ_pX_left_pshift(ZZ_pX_c x, ZZ_pX_c a, ZZ_c pn, ZZ_pContext_c c)
    void ZZ_pX_right_pshift(ZZ_pX_c x, ZZ_pX_c a, ZZ_c pn, ZZ_pContext_c c)
    void ZZ_pX_InvMod_newton_unram(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F, ZZ_pContext_c cpn, ZZ_pContext_c cp)
    void ZZ_pX_InvMod_newton_ram(ZZ_pX_c x, ZZ_pX_c a, ZZ_pX_Modulus_c F, ZZ_pContext_c cpn)

