/*
Matrices test.
AKS, Nov 05.
*/

ClearVerbose();

pp := func<q |
    l and e gt 1 select Sprintf("%o^%o", p, e) else q
    where l, p, e := IsPrimePower(q)>;


///////////////

procedure test_field(K)
    q := #K;

    printf "\n*******\ntest_field; q: %o\n", pp(q);
    ZEIT := Cputime();
    IndentPush();

    if q le (IsPrime(q) select 2^23.5 else 2^20) then
	L := [
	    <10, 1000>, <17, 500>, <37, 500>, <101, 10>, <503, 1>
	];
//L cat:= [<i, 1> : i in [10 .. 200 by 10]];
//L cat:= [<i, 1> : i in [1024 .. 1030]];
//L cat:= [<i, 1> : i in [1100 .. 2000 by 100]];
	if q le 16 then
	    L cat:= [<677, 1>, <1173, 1>];
	    //L cat:= [<i, 1> : i in [2000..5000 by 1000]];
	end if;
    else
	L := [
	    <10, 100>, <17, 10>, <37, 5>, <50, 3>, <101, 2>, <157, 1>,
	    <300, 1>
	];
    end if;

    for t in L do
	n, count := Explode(t);

	printf "q = %o, n = %o, count = %o, Seed: ", pp(q), n, count;
	GetSeed();
	verb := false;

	if count eq 1 then
	    SetVerbose("User1", 1);
	    IndentPush();
	else
	    SetVerbose("User1", 0);
	end if;

	M := MatrixRing(K, n);
	time for ci := 1 to count do
	    X := Random(M);
	    Y := Random(M);
	    vprint User1: "Product";
	    vtime User1: P1 := X*Y;
	    vprint User1: "Expansion";
	    vtime User1: P1 := (X + Y)^2;
	    vtime User1: P2 := (X^2 + Y^2 + X*Y + Y*X);
	    assert P1 eq P2;
	    vprint User1: "Rank";
	    vtime User1: r := Rank(X);
	    if r eq n then
		vprint User1: "Inverse";
		vtime User1: I := X^-1;
		vprint User1: "Inv prod";
//X: Magma;
		vtime User1: P1 := X*I;
P2 := I*X;
if P2 ne P1 then
    d := P2 - P1;
    "BAD";
    //Sort(Setseq(Support(d)));
IsOne(P1);
IsOne(P2);
end if;
		assert IsOne(P1);
	    end if;

	    vprint User1: "Nullspace";
	    e := 3;
	    X := Random(RMatrixSpace(K, n + e, n));
	    vtime User1: N := BasisMatrix(Nullspace(X));
	    assert Nrows(N) ge e and IsZero(N*X);

	    X := RandomSparseMatrix(K, n + e, n, 0.01);
	    vprintf User1: "Nullspace (density %.2o)\n", Density(X);
	    vtime User1: N := BasisMatrix(Nullspace(X));
	    assert Nrows(N) ge e and IsZero(N*X);

	    a := Random(1, n);
	    b := Random(1, n);
	    c := Random(1, n);
/*
K<w> := K;
a := 1;
b := 30;
c := 1;
*/
//GetSeed();
//a,b,c;
	    A := Random(RMatrixSpace(K, a, b));
	    B := Random(RMatrixSpace(K, a, b));
	    C := Random(RMatrixSpace(K, b, c));
	    D := Random(RMatrixSpace(K, b, c));
/*
printf "A := %m;\n", A;
printf "B := %m;\n", B;
printf "C := %m;\n", C;
printf "D := %m;\n", D;
*/
	    vprint User1: "Product (A - B)*(C - D)";
	    vtime User1: P1 := (A - B)*(C - D);
	    vprint User1: "Expansion A*C - A*D - B*C + B*D";
/*
"A*C:", A*C;
"A*D:", A*D;
"B*C:", B*C;
"B*D:", B*D;
*/
	    vtime User1: P2 := A*C - A*D - B*C + B*D;
/*
"P1:", P1;
"P2:", P2;
"P1-P2:", P1-P2;
*/
	    assert P1 eq P2;
	end for;

	if count eq 1 then
	    IndentPop();
	end if;

    end for;
    IndentPop();
    printf "Total field test time: %o\n", Cputime(ZEIT);
end procedure;

test_field(GF(21362167));

///////////////

procedure test_mult(q, n, count)

    SCL := 0.9;
    SCH := 1.1;

    K := GF(q);
    printf "\n*******\ntest_mult; q: %o, n: %o, count: %o\n", pp(q), n, count;
    time for i := 1 to count do

	if i eq 1 then
	    a := n; b := n; c := n;
	else
	    a := Random(Round(SCL * n), Round(SCH * n));
	    b := Random(Round(SCL * n), Round(SCH * n));
	    c := Random(Round(SCL * n), Round(SCH * n));
	end if;

	X := Random(RMatrixSpace(K, a, b));
	Y := Random(RMatrixSpace(K, b, c));

	p := X*Y;
	for j := 1 to 5 do
	    r := Random(1, Nrows(X));
	    assert X[r]*Y eq p[r];
	end for;
	Xs := Parent(X[1])!0;
	ps := Parent(p[1])!0;
	for r := 1 to Nrows(X) do
	    Xs +:= X[r];
	    ps +:= p[r];
	end for;
	assert Xs*Y eq ps;

	X := Transpose(X);
	Y := Transpose(Y);
	p := Transpose(p); // Now p = Y*X
	for j := 1 to 5 do
	    r := Random(1, Nrows(Y));
	    assert Y[r]*X eq p[r];
	end for;
	Ys := Parent(Y[1])!0;
	ps := Parent(p[1])!0;
	for r := 1 to Nrows(Y) do
	    Ys +:= Y[r];
	    ps +:= p[r];
	end for;
	assert Ys*X eq ps;

    end for;

end procedure;

test_mult(2, 1000, 50);
test_mult(2, 5000, 10);
test_mult(2, 10000, 3);
//test_mult(2, 20000, 1);

test_mult(3, 1000, 10);
test_mult(3, 5000, 2);
test_mult(3, 8000, 1);

test_mult(4, 1000, 10);
test_mult(4, 5000, 3);

test_mult(5, 500, 10);
test_mult(5, 1000, 3);
test_mult(5, 5000, 1);

test_mult(7, 500, 5);
test_mult(7, 1000, 3);
test_mult(7, 3000, 1);

for d := 2 to 10 do
    test_mult(3^d, 100, 10);
    test_mult(3^d, 500, 2);
    test_mult(3^d, 800, 1);
end for;

for p in [23, 257, 32003, 11863279] do

    test_mult(p, 500, 5);
    test_mult(p, 1000, 3);
    test_mult(p, 3000, 1);
    //test_mult(p, 5000, 1);

end for;

///////////////

//test_field(GF(3));

test_field(GF(257, 6));
test_field(GF(NextPrime(2^20), 2));
test_field(GF(2965819, 5));


///////////////

test_field(ExtensionField<GF(2), x | x^3 + x^2 + 1>);
test_field(ExtensionField<GF(2), x | x^4 + x^3 + 1>);
test_field(ExtensionField<GF(2), x | x^4 + x^3 + x^2 + x + 1>);

///////////////

QLIST :=
[q: q in [2 .. 32] | IsPrimePower(q)] cat
[
    5^3, 13^2, 257, 509, 521,
    65537, 2965819, 11863279,
    33554393,
    PreviousPrime(2^30), NextPrime(2^30), NextPrime(10^10), NextPrime(10^50),
    3^14, 43^4, 23^5, 19^8, 21362167
];

for q in QLIST do
    test_field(GF(q));
end for;

///////////////

s := [1 .. 250] cat [252, 254, 258, 260, 268, 271, 280, 281 ];

for q in QLIST do
    "Echelon Test:", q;
    K := GF(21362167);
    E := ZeroMatrix(K, #s, Max(s));
    ss := {1 .. Ncols(E)} diff Set(s);
    for i := 1 to #s do
	E[i,s[i]]:=1;
	for c in ss do if c gt s[i] then E[i,c]:=Random(K); end if; end for;
    end for;
    r := Nrows(E);
    X := Random(MatrixRing(K,r))*E;
    EE := EchelonForm(X);
    assert EE eq E;
end for;

///////////////
// Oct 21

"Test InvariantFactors";

for q in [2, 3, 4, 5, 7, 9, 11, 16, 23, 32, 128] do
    q;

    //q, c := Explode(t);
    c := 10000;

    F := GF(q);
    K<X> := PolynomialRing(F);

    k := 3;
    n := 7;

    H := Random(MatrixAlgebra(F, n));
    N := KroneckerProduct(H, MatrixAlgebra(F, k)!1);

    time inv := {InvariantFactors(N) : i in [1..c]};
    //#inv; continue;
    assert #inv eq 1;

    L := Representative(inv);
    for i := 2 to #L do
        if L[i] mod L[i - 1] ne 0 then
            "BAD F:", L;
            "i:", i;
            assert false;
        end if;
    end for;
end for;

///////////////
// May 22

k := FiniteField(2);
R<t_1,t_2,t_3> := PolynomialRing(k,3);
K<t_1,t_2,t_3> := FieldOfFractions(R);
K;
FSd :=
Matrix(K,[[1,t_1,0,0,0,0],[0,1,0,0,0,0],[0,0,0,0,1,t_1],[0,1,0,1,0,t_2],[0,0,1,0
,0,t_1],[0,0,0,0,0,1]]);
FDelta :=
Matrix(K,[[1,t_1,0,0,0,t_3],[0,1,0,0,0,0],[0,0,0,0,1,t_1],[0,1,0,1,0,t_2],[0,0,1
,0,0,t_1],[0,0,0,0,0,1]]);
A, B, C := JordanForm(FSd);
a, b, c := JordanForm(FDelta);

assert IsOne(B*B^-1);
assert IsOne(B^-1*B);
assert B*FSd*B^-1 eq A;

assert IsOne(b*b^-1);
assert IsOne(b^-1*b);
assert b*FDelta*b^-1 eq a;


X :=
MatrixAlgebra(IntegerRing(), 10) ! Matrix(10, 10, \[ 2, 1, 0, 0, 0, 0, -1, -1,
-1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ]);
f<x> := CharacteristicPolynomial(X);
L := Factorization(f);
// "Fact of CharacteristicPolynomial:", L;
FCP := FactoredCharacteristicPolynomial(X);
// "FactoredCharacteristicPolynomial:", FCP;
assert L eq FCP;


// Bug 173:
F := GF(2);
m := Matrix([[Random(F) : i in [1..5]] : j in [1..5]]);
e := Eltseq(m);
t := 1 - m;
assert Eltseq(m) eq e;
