SetEchoInput(true);

H := HadamardDatabase();
assert Degrees(H) eq [1, 2] cat [d : d in [4 .. 256] | d mod 4 eq 0];
assert [NumberOfMatrices(H, d) : d in Degrees(H)] eq 
[1, 1, 1, 1, 1, 5, 3, 60, 487, 23, 218, 20, 500, 55, 562, 2, 3, 1, 2, 1, 2, 1, 3, 2, 1, 1, 1, 1, 2, 2, 1, 3, 1, 1, 2, 2] cat [1 : i in [1 .. 9]] cat [2] cat [1 : i in [1 .. 20]];
l, h := DegreeRange(H);
assert l eq 1; assert h eq 256;

SetEchoInput(false);
load "~magma/dbgen/hadamard/had32.mat";
S32 := S;
load "~magma/dbgen/hadamard/had36.mat";
S36 := S;
load "~magma/dbgen/hadamard/had44.mat";
S44 := S;
load "~magma/dbgen/hadamard/had52sk.mat";
S52 := S;
load "~magma/dbgen/hadamard/had36sk.mat";
S36k := S;
load "~magma/dbgen/hadamard/had44sk.mat";
S44k := S;
load "~magma/dbgen/hadamard/hadamard.out";
Append(~S, S32);
Append(~S, S36);
Append(~S, S36k);
load "~magma/dbgen/hadamard/had40.mat";
Append(~S, h);
Append(~S, S44);
Append(~S, S44k);
load "~magma/dbgen/hadamard/had48.mat";
Append(~S, h);
Append(~S, S52);
SetEchoInput(true);

degrees := [Isqrt(#T[1]) : T in S];
deg_sort := Sort(degrees);
for i in [1 .. #deg_sort] do
    p := Position(degrees[i .. #degrees], deg_sort[i]);
    p +:= i - 1;
    tmp := S[i];
    tmpdeg := degrees[i];
    S[i] := S[p];
    degrees[i] := degrees[p];
    S[p] := tmp;
    degrees[p] := tmpdeg;
end for;
assert degrees eq deg_sort;

/*
Remove duplicates in degrees and S
*/

D := Degrees(H);
assert D eq Sort(Setseq(Seqset(degrees)));

dup :=  {* d : d in degrees *};
dup := {d : d in dup | Multiplicity(dup, d) gt 1};
for d in dup do
    pos := Position(degrees, d);
    assert degrees[pos + 1] eq d;
    while degrees[pos + 1] eq d do
	S[pos] := S[pos] cat S[pos + 1];
	Remove(~S, pos + 1);
	Remove(~degrees, pos + 1);
    end while;
end for;
assert D eq degrees;

D1 := [d : d in D | NumberOfMatrices(H, d) eq 1];
r := Setseq({Random(1, #D1) : i in [1 .. Ceiling(#D1/4)]});
while 0 in r do
    Exclude(~r, 0);
end while;

if #r gt 0 then
    r := [r[Random(1, #r)]];
end if;

for d in D1[r] do
d;
    md := Matrix(H, d, 1);
    ms := S[Position(D, d)][1];
    assert #ms eq d^2;
    assert Eltseq(md) eq ms or 
		HadamardCanonicalForm(MatrixAlgebra(Integers(), d)!ms) eq md;
end for;

Dm := [d : d in D | NumberOfMatrices(H, d) gt 1];
r := Setseq({Random(1, #Dm) : i in [1 .. Ceiling(#Dm/4)]});
while 0 in r do
    Exclude(~r, 0);
end while;

if #r gt 0 then
    r := [r[Random(1, #r)]];
end if;

for d in Dm[r] do
d;
    mds := Matrices(H, d);
    ms := S[Position(D, d)];
    assert #ms ge #mds; /* ms MAY have duplicates */
    P := {Position(mds, HadamardCanonicalForm(MatrixAlgebra(Integers(), d)!mms))
								    : mms in ms}; 
    assert not 0 in P;
    assert #P eq #mds;
    assert Minimum(P) eq 1;
    assert Maximum(P) eq #P;
    assert P eq {1 .. #P};
end for;


H := SkewHadamardDatabase();
assert Degrees(H) eq [36, 44, 52];
assert [NumberOfMatrices(H, d) : d in Degrees(H)] eq [18, 59, 561];
assert DegreeRange(H) eq Explode([36, 52]);

SetEchoInput(false);
SS := [];
load "~magma/dbgen/hadamard/had36sk.mat";
Append(~SS, S);
load "~magma/dbgen/hadamard/had44sk.mat";
Append(~SS, S);
load "~magma/dbgen/hadamard/had52sk.mat";
Append(~SS, S);
S := SS;
SetEchoInput(true);

D := Degrees(H);

D1 := [d : d in D | NumberOfMatrices(H, d) eq 1];
r := Setseq({Random(1, #D1) : i in [1 .. Ceiling(#D1/4)]});
if #r gt 0 then
    r := [r[Random(1, #r)]];
end if;

for d in D1[r] do
d;
    md := Matrix(H, d, 1);
    ms := S[Position(D, d)][1];
    assert #ms eq d^2;
    assert Eltseq(md) eq ms;
end for;

Dm := [d : d in D | NumberOfMatrices(H, d) gt 1];
r := Setseq({Random(1, #Dm) : i in [1 .. Ceiling(#Dm/4)]});
if #r gt 0 then
    r := [r[Random(1, #r)]];
end if;

for d in Dm[r] do
d;
    mds := Matrices(H, d);
    ms := S[Position(D, d)];
    assert #ms ge #mds; /* ms MAY have duplicates */

    /* ensures everthing in database is in generation file */
    P := {Position(ms, Eltseq(mmd)) : mmd in mds}; 
    assert not 0 in P;
    assert #P eq #mds;

    /* ensures everything in generation file is equivalent to something in
    the database */
    Cmd := [HadamardCanonicalForm(mmd) : mmd in mds];
    P := {Position(Cmd, HadamardCanonicalForm(Matrix(Integers(), d, d, m))) 
						: m in ms[1 .. Min(#ms, 100)]};
    assert 0 notin P;
    assert #P le #mds;
    assert P subset {1 .. #mds};
end for;

