hash_base = 2^64;
init_h = 5381;

split_Z(n) =
{
    my (bits = 8, base = 2^bits, sgn = sign(n) % base, res = []);
    n = abs(n);
    while (n != 0,
           res = concat(res, bitand(n, base - 1));
           n = shift(n, -bits));
    res = concat(res, sgn);
}

glue(h, a) = bitand((((h << 5) + h) + a), hash_base - 1);

hash_Z(n) =
{
    my (v = split_Z(n), h = init_h);
    for (i = 1, #v, h = glue(h, v[i]));
    h;
}

hash_ZX(pol) =
{
    my (v = Vec(pol), h = init_h);
    for (i = 1, #v, h = glue(h, hash_Z(v[i])));
    h;
}

hash_ZXX(pol) =
{
    my (v = [Vec(c) | c <- Vec(pol)], h = init_h);
    for (i = 1, #v, h = glue(h, hash_ZX(v[i])));
    h;
}

{
lvl_idx = [0, 1, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 6, 0, 0, 0, 7, 0, 8, 0, 0, 0, 9];
modpoly_hashes = [
953115400354185,
619732354788530567,
7671381920119322245,
1662362517513198972,
11499552816775494464,
10945716853871337038,
1858790070632847848,
16279119036202003022,
9091292905489559584
];

}

check_modpoly(L) =
{
    if (hash_ZXX(polmodular(L)) != modpoly_hashes[lvl_idx[L]],
        error("Bad modpoly"));
}

{
MAX_LEVEL = 23;  \\ This already gives 89% coverage in 1.2s
forprime(L = 2, MAX_LEVEL, check_modpoly(L));

\\ Check that specifying variables works
my (phi7 = polmodular(7));
if (phi7 != polmodular(7, 'x, 'y) || phi7 != polmodular(7, 'x)
    || polmodular(7, 's, 't) != substvec(phi7, ['x, 'y], ['s, 't]),
    error("Bad variables"));

\\ Check argument checking
my (got_err);
iferr (polmodular(7, "I am the queen of France", 'x),
       err, got_err = 1, errname(err) == "e_TYPE");
if ( ! got_err, error("No type error from bad param"));

got_err = 0;
iferr (polmodular(7, ffgen(2^3), 'x),
       err, got_err = 1, errname(err) == "e_DOMAIN");
if ( ! got_err, error("No domain error from non-prime field arg"));

got_err = 0;
iferr (polmodular(1), err, got_err = 1, errname(err) == "e_DOMAIN");
if ( ! got_err, error("No error from level 1"));

got_err = 0;
iferr (polmodular(6), err, got_err = 1, errname(err) == "e_IMPL");
if ( ! got_err, error("No error from composite level"));

got_err = 0;
iferr (polmodular(nextprime(5000)), err, got_err = 1, errname(err) == "e_IMPL");
if ( ! got_err, error("No error from huge level"));

got_err = 0;
iferr (polmodular(7, 'x, 'y, 1), err, got_err = 1, errname(err) == "e_FLAG");
if ( ! got_err, error("No error from inappropriate flag"));

got_err = 0;
iferr (polmodular(7, 'x, 'x), err, got_err = 1, errname(err) == "e_PRIORITY");
if ( ! got_err, error("No error from same variables"));

got_err = 0;
iferr (polmodular(7, 'y, 'x), err, got_err = 1, errname(err) == "e_PRIORITY");
if ( ! got_err, error("No error from bad variables"));
}

all(v) = { my (r = 1); for (i = 1, #v, r = r && v[i]); r; }
poloftype(f, tp) =
{
    type(f) == "t_POL" && all([type(polcoeff(f, d)) == tp | d <- [0 .. poldegree(f)]]);
}

lift_ffx(f) =
{
    my (v = Vec(f));
    if ( ! all([poldegree(c.pol) == 0 | c <- v]),
        error("Polynomial has coeffs in extension"));
    Pol([polcoeff(c.pol, 0) | c <- Vec(f)], variable(f));
}

check_eval_modpoly(L, j, p, expected) =
{
    my (jm = Mod(j, p),
        jf = j * ffgen(p)^0,
        um = polmodular(L, jm, 'y, 0),
        uf = polmodular(L, jf, 'y, 0),
        vm = polmodular(L, jm, 'y, 1),
        vf = polmodular(L, jf, 'y, 1));
    if ( ! poloftype(um, "t_INTMOD") || ! poloftype(uf, "t_FFELT")
        || type(vm) != "t_VEC" || #vm != 3 || type(vf) != "t_VEC" || #vf != 3,
        error("Invalid return type"));
    if ( ! all([poloftype(v, "t_INTMOD") | v <- vm])
        || ! all([poloftype(v, "t_FFELT") | v <- vf]),
        error("Invalid coefficients"));

    if (um != vm[1] || uf != vf[1] || lift(um) != lift_ffx(uf) || hash_ZX(lift(um)) != expected[1],
        error("Wrong result for modpoly eval"));
    if (hash_ZX(lift(vm[2])) != expected[2],
        error("Wrong derivative"));
    if (hash_ZX(lift(vm[3])) != expected[3],
        error("Wrong second derivative"));
}

{
my (p = nextprime(2^40));
check_eval_modpoly( 5, 7, 151, [8033941431460000, 243641761686181, 243612090562303]);
check_eval_modpoly(19, 7, 151, [11844895572672018496, 369501438945078285, 13082720985735388448]);
\\check_eval_modpoly( 5, 7, factorial(12), XXXX);
check_eval_modpoly( 5, 7, p, [3901199766181530739, 4054334766401667256, 16751141247645108349]);
\\check_eval_modpoly(23, 7, factorial(12), XXXX);
check_eval_modpoly(23, 7, p, [2360118342899681926, 2787294817779511277, 18359991236545579908]);
}
