intrinsic MerelNumberOfIsogenyClasses(MOG::SeqEnum, p::RngIntElt) -> RngIntElt
{Returns the number of isogeny classes at level p.}
   return #MOG[p]`Eigenvectors;
end intrinsic;

intrinsic MerelNumberOfEmbeddings(MOG::SeqEnum,
                       p::RngIntElt, 
                       IsogenyClass::RngIntElt
                       : Factors := true) -> RngIntElt
{Number of factors mod p of the polynomial defining IsogenyClass i.}
   R := PolynomialRing(GF(p));
   f := R ! MOG[p]`Eigenvectors[IsogenyClass]`DefiningPolynomial;
   if Factors then
      return #Factorization(f);
   end if;
   return &+[Degree(a[1]) : a in Factorization(f)];  
end intrinsic;

intrinsic MerelDimension(MOG::SeqEnum,
                         p::RngIntElt, 
                         IsogenyClass::RngIntElt) -> RngIntElt
{Number of factors mod p of the polynomial defining IsogenyClass i.}
   return Degree(MOG[p]`Eigenvectors[IsogenyClass]`DefiningPolynomial);
end intrinsic;

intrinsic MerelIota(MOG::SeqEnum,
                      p::RngIntElt, 
           IsogenyClass::RngIntElt, 
                      u::RngIntElt,
              Embedding::RngIntElt
                           : Factors := true) -> .
{Given a level p <= 997, an IsogenyClass, an integer u,
 and an Embedding number <= MerelNumberOfEmbeddings(p,IsogenyClass), 
 return the rational function sum n_E/((j-j(E))^u.  This function
 is defined in section 1.4 of Merel's paper "Sur la nature  
 non-cyclotomique des points d'ordre fini des courbes elliptiques."  
 The optional parameter Factors controls how the Embedding number
 is defined.}
   R   := PolynomialRing(GF(p));
   v   := MOG[p]`Eigenvectors[IsogenyClass];
   fac := Factorization(R!v`DefiningPolynomial);
   if Factors then
      f := R!fac[Embedding][1];
      i := 1;
   else
      i   := 1;
      while Embedding gt Degree(fac[i][1]) do
         Embedding -:= Degree(fac[i][1]);
         i         +:= 1;
      end while;
      f   := R!fac[i][1];
      i   := Embedding;
   end if;
   Fq<a>:= GF(p^LCM(2,Degree(f)));    // need the s.s j-invariants.
   RR  := PolynomialRing(Fq);
   alp := Roots(RR!f)[i][1];            // assuming output of Roots sorted.
   ss_f:= MOG[p]`SupersingularBasis`DefiningPolynomial;
   bet := Roots(RR!ss_f)[1][1];         // first is good enough.
   coor:=MOG[p]`SupersingularBasis`Coordinates;
   ss_j:= [ Evaluate(RR!coor[n],bet) : n in [1..#coor] ];
   FF<j>:= FieldOfFractions(RR);   
   return &+ [
      FF!Evaluate(RR!v`Coordinates[n],alp)           // n_e
       / (FF!ss_j[n]-j)^u  : n in [1..#ss_j]
   ];
end intrinsic;

////////////////////////////////////////////////////////////
// Criterion when p = 1 (mod 4):
function MerelCriterion1Degree(MOG, LChiZero, Anomolous, p, d)
   assert p mod 4 eq 1;
   assert d gt 0 and (p-1) mod d eq 0;
   assert d ne 2;

   NumberOfClasses := MerelNumberOfIsogenyClasses(MOG,p);
   Anom            := Anomolous[p];
   for iso1 in [1..NumberOfClasses] do
      for iso2 in [iso1..NumberOfClasses] do
         if Index(LChiZero[p][iso1],d) eq 0 and
            Index(LChiZero[p][iso2],d) eq 0 then  
            for embedding1 in 
              [1..MerelNumberOfEmbeddings(MOG,p,iso1 : Factors:=false)] do
               for embedding2 in 
                  [1..MerelNumberOfEmbeddings(MOG,p,iso2 : Factors:=false)] do
                  f11   := MerelIota(MOG, p, iso1, 1, embedding1 : Factors := false);
                  f12   := MerelIota(MOG, p, iso1, 2, embedding1  : Factors := false);
                  f21   := MerelIota(MOG, p, iso2, 1, embedding2 : Factors := false);
                  f22   := MerelIota(MOG, p, iso2, 2, embedding2 : Factors := false);
                  // Magma is clever enough to force the following to
                  // to take place in a common finite extension of
                  // the finite fields over which each fij is defined. 
                  f     := f11*f22 - f12*f21; 
//"f11=",f11;
//"f12=",f12;
//"f21=",f21;
//"f22=",f22;
//"f=",f;
                  vals  := [Evaluate(f,j) : j in Anom];
                  if Index(vals,0) eq 0 then
                     printf "verified with A=%o%o(dim=%o), B=%o%o(dim=%o)\n",  
                       p, ToIsogenyCode(iso1), MerelDimension(MOG,p,iso1),
                       p, ToIsogenyCode(iso2), MerelDimension(MOG,p,iso2);
                     return true;
                  end if;
               end for;
            end for;
         end if;
      end for;
   end for;
   return false;
end function;

function MerelCriterion1(MOG, LChiZero, Anomolous, p)
   assert p mod 4 eq 1;
   "--------->>>>>>  p =",p, " <<<<<<<---------";
   for d in [e : e in Divisors(p-1) | e ne 2] do
      printf "deg(chi)=%3o   ",d;
      if not MerelCriterion1Degree(MOG, LChiZero, Anomolous,p,d) then
         "FAILED!";
         return false;
      end if;
   end for;
   "TRUE.";
   return true;
end function;


////////////////////////////////////////////////////////////
// Criterion when p = 3 (mod 4):
function MerelCriterion3Degree(MOG, LChiZero, Anomolous, p, d)
   assert p mod 4 eq 3;
   assert d gt 0 and (p-1) mod d eq 0;
   NumberOfClasses := MerelNumberOfIsogenyClasses(MOG,p);
   Anom            := Anomolous[p];
   for iso in [1..NumberOfClasses] do
      if Index(LChiZero[p][iso],d) eq 0 then   // L(iso,[d],1) =/= 0.
         for embedding in [1..MerelNumberOfEmbeddings(MOG,p,iso)] do
            f    := MerelIota(MOG, p, iso, 1, embedding);
            vals := [Evaluate(f,j) : j in Anom];
            if Index(vals,0) eq 0 then
               printf "verified with %o%o, (dim=%o)\n",       
                       p, ToIsogenyCode(iso), 
                       MerelDimension(MOG,p,iso);
               return true;
            end if;
         end for;
      end if;
   end for;
   return false;
end function;

function MerelCriterion3(MOG, LChiZero, Anomolous, p)
   assert p mod 4 eq 3;
   "--------->>>>>>  p =",p, " <<<<<<<---------";
   for d in Divisors(p-1) do
      printf "deg(chi)=%3o   ",d;
      if not MerelCriterion3Degree(MOG, LChiZero, Anomolous,p,d) then
         "FAILED!";
         return false;
      end if;
   end for;
   "TRUE.";
   return true;
end function;

intrinsic MerelCriterion(MOG::SeqEnum, LChiZero::SeqEnum,
                               Anomolous::SeqEnum,  p::RngIntElt) -> BoolElt
{Verifies the criterion of Merel for the prime number p.
 The sequence MOG, LChiZero, and Anomolous are all defined
 in the file data.m}
   assert IsPrime(p) and p ge 11;
   case p mod 4:
      when 1: return MerelCriterion1(MOG,LChiZero,Anomolous,p);
      when 3: return MerelCriterion3(MOG,LChiZero,Anomolous,p);
   end case;
end intrinsic;










