/*
    Copyright (C) 2017 Fredrik Johansson

    This file is part of Arb.

    Arb is free software: you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License (LGPL) as published
    by the Free Software Foundation; either version 2.1 of the License, or
    (at your option) any later version.  See <http://www.gnu.org/licenses/>.
*/

#include "arb_fmpz_poly.h"
#include "acb_dirichlet.h"

int main()
{
    slong iter;
    flint_rand_t state;

    flint_printf("gauss_period_minpoly....");
    fflush(stdout);

    flint_randinit(state);

    {
        slong prec;
        ulong n, q;
        fmpz_poly_t pol;
        fmpz_poly_init(pol);

        /* test q = 0 separately; see issue #194 */
        for (n = 0; n < 100; n++)
        {
            fmpz_poly_one(pol);
            arb_fmpz_poly_gauss_period_minpoly(pol, 0, n);
            if (!fmpz_poly_is_zero(pol))
            {
                flint_printf("FAIL (q = 0)\n");
                flint_abort();
            }
        }

        for (q = 1; q < 1000; q++)
        {
            acb_dirichlet_roots_t zeta;
            prec = 100 + n_randint(state, 500);

            if (n_is_prime(q))
                acb_dirichlet_roots_init(zeta, q, 30, prec);

            for (n = 0; n < 1000; n++)
            {
                arb_fmpz_poly_gauss_period_minpoly(pol, q, n);

                if (!fmpz_poly_is_zero(pol))
                {
                    ulong k, g, gk, e, d;
                    acb_t t, u;

                    acb_init(t);
                    acb_init(u);
                    d = (q - 1) / n;
                    g = n_primitive_root_prime(q);

                    for (iter = 0; iter < 3; iter++)
                    {
                        k = n_randint(state, n);
                        gk = n_powmod2(g, k, 2 * q);
                        acb_zero(u);

                        for (e = 0; e < d; e++)
                        {
                            acb_dirichlet_root(t, zeta, n_mulmod2_preinv(gk,
                                n_powmod2(g, n * e, 2 * q), 2 * q, n_preinvert_limb(2 * q)), prec);
                            acb_add(u, u, t, prec);
                        }

                        arb_fmpz_poly_evaluate_acb(t, pol, u, prec);

                        if (!acb_contains_zero(t) || fmpz_poly_degree(pol) != n)
                        {
                            flint_printf("FAIL\n");
                            flint_printf("q = %wu, n = %wu, k = %wu\n\n", q, n, k);
                            fmpz_poly_print(pol); flint_printf("\n\n");
                            acb_printn(u, 30, 0); flint_printf("\n\n");
                            acb_printn(t, 30, 0); flint_printf("\n\n");
                            flint_abort();
                        }
                    }

                    acb_clear(t);
                    acb_clear(u);
                }
            }

            if (n_is_prime(q))
                acb_dirichlet_roots_clear(zeta);
        }

        fmpz_poly_clear(pol);
    }

    flint_randclear(state);
    flint_cleanup();
    flint_printf("PASS\n");
    return EXIT_SUCCESS;
}

