//
// First we store all possible equations (thought of as row vectors)
// in a big matrix
//
EQS := Matrix(IntegerRing(),45,5,[
2,1,1,0,0,
2,1,0,1,0,
2,1,0,0,1,
2,0,1,1,0,
2,0,1,0,1,
2,0,0,1,1,
1,2,1,0,0,
1,2,0,1,0,
1,2,0,0,1,
1,1,2,0,0,
1,1,0,2,0,
1,1,0,0,2,
1,0,2,1,0,
1,0,2,0,1,
1,0,1,2,0,
1,0,1,0,2,
1,0,0,2,1,
1,0,0,1,2,
0,2,1,1,0,
0,2,1,0,1,
0,2,0,1,1,
0,1,2,1,0,
0,1,2,0,1,
0,1,1,2,0,
0,1,1,0,2,
0,1,0,2,1,
0,1,0,1,2,
0,0,2,1,1,
0,0,1,2,1,
0,0,1,1,2,
1,1,0,0,0,
1,0,1,0,0,
1,0,0,1,0,
1,0,0,0,1,
0,1,1,0,0,
0,1,0,1,0,
0,1,0,0,1,
0,0,1,1,0,
0,0,1,0,1,
0,0,0,1,1,
1,1,1,1,0,
1,1,1,0,1,
1,1,0,1,1,
1,0,1,1,1,
0,1,1,1,1
]);

//
// The next function takes an integral matrix of size Nx5. First it is checked
// if there is a zero column; if this happens the matrix is said to be invalid.
// Next we test: if the equation 1,1,0,0,0 does not occur, then an equation
// of the type 2,1,*,*,* or 1,2,*,*,* must occur. Same with all permutations
// of this. The output is 'true' if (and only if) these conditions are satisfied.
//
IsValid := function(M)
//
// In SpecialEQS we store all equations of type 1,1,0,0,0 + permutations
//
SpecialEQS := Matrix(IntegerRing(),10,5,[
1,1,0,0,0,
1,0,1,0,0,
1,0,0,1,0,
1,0,0,0,1,
0,1,1,0,0,
0,1,0,1,0,
0,1,0,0,1,
0,0,1,1,0,
0,0,1,0,1,
0,0,0,1,1]);
stillvalid := true;
//
// We first check if the zero column occurs. If so, the matrix is invalid.
//
nilcolumn := ZeroMatrix(IntegerRing(),NumberOfRows(M),1);
for j in [1..NumberOfColumns(M)] do
  if (ExtractBlock(M,1,j,NumberOfRows(M),1) eq nilcolumn) then
    stillvalid := false;
    break;
  end if;
end for;
if stillvalid then
  for i in [1..4] do
    for j in [i+1..5] do
      case i:
        when 1: specialindex := j-1;
        when 2: specialindex := j+2;
        when 3: specialindex := j+4;
        when 4: specialindex := j+5;
      end case;
      specialeq := ExtractBlock(SpecialEQS,specialindex,1,1,5);
//
// specialindex is the row number (in the matrix SpecialEQS) of the equation of
// type 1,1,0,0,0 with i=j=1, and specialeq is then that equation.
//
      specialfound := false;
      for r in [1..NumberOfRows(M)] do
        if (ExtractBlock(M,r,1,1,5) eq specialeq) then
          specialfound := true;
          break r;
        end if;
      end for;
      if (not specialfound) then
        OneTwoFound := false;
        for r in [1..NumberOfRows(M)] do
          if ((M[r,i] * M[r,j]) eq 2) then
            OneTwoFound := true;
            break;
          end if;
        end for;
        if (not OneTwoFound) then
          stillvalid := false;
          break i;
        end if;
      end if;
    end for;
  end for;
end if;
return stillvalid;
end function;
//
// end of function IsValid
//


//
DoubleEigenval := function(A)
B := Transpose(A);
D := 1;
for i := 1 to 5 do
  for j := i+1 to 5 do
    D := D *(B[i]-B[j]);
  end for; //end of j-loop
end for; //end of i-loop
return (D eq 0);
end function;


Primes := {5,7,11,13};
//
// It turns out these are the only primes where something happens. If another
// prime pops up in the calculations, we will write output to a separate file.
// (But it turns out that this does not occur.)
//

IND := {1..45};

SolsFive := {};
SolsSeven := {};
SolsEleven := {};
SolsThirteen := {};

for Iset in Subsets(IND,5) do
  Iseq := [x: x in Iset];
  M := Submatrix(EQS,Iseq,[1..5]);
  d := AbsoluteValue(Determinant(M));
  if d gt 1 then
    for p in Primes do
      if IsDivisibleBy(d,p) then
        Mmodp := ChangeRing(M,GF(p));
        A := KernelMatrix(Transpose(Mmodp));
        if DoubleEigenval(A) then
          ARows := {};
          for i in [1..45] do
            row := ExtractBlock(EQS,i,1,1,5);
            rowmodp := ChangeRing(row,GF(p));
            if (A * Transpose(rowmodp) eq 0) then
              Include(~ARows,i);
            end if;
          end for; //end of i in [1..45]
          QuadricMatrix := Submatrix(EQS,[x: x in ARows],[1,2,3,4,5]);
//
// Now we check if QuadricMatrix is valid. If it is, we put the vector A
// (or rather its transpose) in a set, taking into account the value of p.
//
          if IsValid(QuadricMatrix) then
            case p:
              when 5:
                Include(~SolsFive,Transpose(A));
              when 7:
                Include(~SolsSeven,Transpose(A));
              when 11:
                Include(~SolsEleven,Transpose(A));
              when 13:
                Include(~SolsThirteen,Transpose(A));
            end case;
          end if; //end of IsValid
        end if; //end of DoubleEigenval
      end if; //end of IsDivisibleBy
    end for; //end of p in Primes
  end if; //end of d gt 1
end for; //end of Iset

//
// At this stage, each of the sets SolsXXX contains a list of all A = diag(-a1,...,-a5)
// for which there is a candidate family of quadrics.
//

//
// Now we start cleaning up: we are still allowed to permute coordinates and to
// rescale the vectors A. The next function outputs a (much smaller) set of solutions
// that has one representative for each equivalence class.
//
Cleanup := function(S,p)
CleanSet := {};
for A in S do
  newclass := true;
  for s in Sym(5) do
    for c in {c : c in GF(p) | c ne (GF(p) ! 0)} do
      if ((c * PermutationMatrix(GF(p),s) * A) in CleanSet) then
        newclass := false;
        break s;
      end if;
    end for;
  end for;
  if newclass then
    Include(~CleanSet,A);
  end if;
end for;
return CleanSet;
end function;
//
// end of function Cleanup
//

SolsFiveClean := Cleanup(SolsFive,5);
SolsSevenClean := Cleanup(SolsSeven,7);
SolsElevenClean := Cleanup(SolsEleven,11);
SolsThirteenClean := Cleanup(SolsThirteen,13);

//
// Finally we have a procedure that prints the matrices QuadricMatrix
// corresponding to the solutions we have found.
//
PrintMatrices := procedure(S,p)
for A in S do
  print A;
  print "";
  ARows := {};
  for i in [1..45] do
    row := ExtractBlock(EQS,i,1,1,5);
    rowmodp := ChangeRing(row,GF(p));
    if (rowmodp * A eq 0) then
      Include(~ARows,i);
    end if;
  end for;
  QuadricMatrix := Submatrix(EQS,[x: x in ARows],[1,2,3,4,5]);
  print QuadricMatrix;
  print "";
end for;
end procedure;


print "============================================================";
print "";
print "Solutions with p=5";
print "";
PrintMatrices(SolsFiveClean,5);
print "";

print "============================================================";
print "";
print "Solutions with p=7";
print "";
PrintMatrices(SolsSevenClean,7);
print "";

print "============================================================";
print "";
print "Solutions with p=11";
print "";
PrintMatrices(SolsElevenClean,11);
print "";

print "============================================================";
print "";
print "Solutions with p=13";
print "";
PrintMatrices(SolsThirteenClean,13);
print "";
print "============================================================";
