/**********************************************************
 * 
 * This is a test file for local_square_classes.m
 * 
 * It checks that given an element a, the coordinates of
 * its local square-class are correctly computed.
 * 
 *********************************************************/

// set the path to the package
import "path.m" : PATH_TO_LSC;
Attach(PATH_TO_LSC);

// how many fields to test for each pair (e,f)
// (provided that enough fields exist)
NUM_OF_FIELDS := 5;

// import the list of dyadic fields from https://www.lmfdb.org/
import "lmfdb_lf_dyadic_fields.m" : data;

// Magma needs a size for Random intrinsic
SIZE := 4;

procedure run_tests( K : exhaustive := true, ntests := 64 )
// This routine tests the correctness of coordinate
// computation for a given field. 
// If exhaustive is set to true (default) it will
// exhaustively check the whole vector space.
// Otherwise. it does only random tests.
// The number of random tests is controlled by ntests.
    printf ".";

    // get the dyadic ideal
    p :=  PrimeFactorization(K, 2)[1][1];
    // compute the group of local square-classes (in two ways)
    G, mG := LocalGroupOfSquaresClasses(p);
    // extract the bases
    B := [ v @ mG : v in Basis(G) ];

    // check whether G -> K -> G is an identity
    if exhaustive then
        // check the whole vector space
        assert forall{ v : v in G | v eq (v @ mG @@ mG ) };
    else
        // check the basis (to make sure all possible quadratic
        // defects appear) and ntests random elements
        G_ := [ Random(G) : j in [1..ntests] ] cat Basis(G);
        assert forall{ v : v in G_ | v eq (v @ mG @@ mG ) };
    end if;

    // check whether the coordinates of two elements in the same
    // local square-class are equal; this test is always randomized
    // first, check the bases (to make sure all possible quadratic
    // defects appear)
    A := [ Random(K, SIZE) : i in [1..ntests] ];
    A := [ a : a in A | not IsZero(a) ];
    assert forall{ b : b in B | [ b @@ mG : a in A] eq [ (b*a^2) @@ mG : a in A ] };
    // now check ntests random elements
    for j in [1..ntests] do
        repeat a := Random(K, SIZE); until not IsZero(a);
        repeat b := Random(K, SIZE); until not IsZero(b);
        assert (a @@ mG) eq ((a*b^2) @@ mG);
    end for;
end procedure;

R<x> := PolynomialRing(Rationals());

// extract all ramification indices and inertia degrees
I := {@ <d[4],d[5]> : d in data @};

// run the tests for all e's and f's
for ef in I do
    printf "e = %o, f = %o:\t", ef[1], ef[2];
    // find all fields with a given ramification index
    // and inertia degree
    fields := [d : d in data | <d[4],d[5]> eq ef];
    // select NUM_OF_FIELDS random elements
    // (or take all if there are not enough)
    if #fields gt NUM_OF_FIELDS then
        inx := { Integers() | };
        while #inx lt NUM_OF_FIELDS do
            Include(~inx, Random([1..#fields]));
        end while;
        fields := fields[ Setseq(inx) ];
    end if;
    // run the tests on the picked fields
    for rec in fields do
        K<t> := NumberField( R!rec[3] );
        degK := Degree(K);
        run_tests(K : exhaustive := (degK lt 10));
    end for;
    printf "\n";
end for;

// a few more hand-picked fields with degree > 30
// (hence, no pre-compution used)

// e = 31, f = 1, degree > 30 (so no precomputation used)
K<t> := NumberField(x^31 + 128);
printf "e = 31, f = 1:\t";
run_tests(K : exhaustive := false);

// e = 1, f = 31, degree > 30 (so no precomputation used)
K<t> := NumberField(x^31 + x^3 + 1);
printf "\ne = 1, f = 31:\t";
run_tests(K : exhaustive := false);

// e = 2, f = 17, degree > 30 (so no precomputation used)
K<t> := NumberField(x^34 + 2*x^20 + 2*x^17 + x^6 + 2*x^3 + 16*x + 1);
printf "\ne = 2, f = 17:\t";
run_tests(K : exhaustive := false);

print "\nAll tests passed.";
quit;
