freeze;
 
/****-*-magma-* EXPORT DATE: 2004-11-23 ************************************
                                                                            
                     MODFORM: Modular Forms in MAGMA
 
                              William A. Stein        
                         
   FILE: creation.m


   2004-10-24: (was)  Changed CoerceModFrmElt(M,x) so coercion
                      into the parent of an element is the identity:

   Revision 1.13  2002/09/12 15:13:41  was
   changed k to 2 in a comment.

   Revision 1.12  2002/09/11 18:18:07  was
   Changed back to keeping bad characters, so construction of zero space is possible.

   Revision 1.11  2002/09/11 18:12:09  was
   Throw out characters that don't satisfy eps(-1) = (-1)^k in modular forms constructor.

   Revision 1.10  2002/05/04 18:12:19  was
   Added CuspForms.

   Revision 1.9  2002/04/13 07:26:38  was
   *** empty log message ***

   Revision 1.8  2002/03/11 23:06:07  was
   improved intrinsic ModularForms(M::ModFrm, N::RngIntElt).

   Revision 1.7  2002/03/11 23:01:36  was
   Added intrinsic ModularForms(M::ModFrm, N::RngIntElt) -> ModFrm.

   Revision 1.6  2001/11/22 19:28:20  was
   Added some more constructors for "ModularForms".

   Revision 1.5  2001/05/30 18:54:19  was
   Created.

   Revision 1.4  2001/05/16 04:11:48  was
   *** empty log message ***

   Revision 1.3  2001/05/16 04:01:01  was
   *** empty log message ***

   Revision 1.2  2001/05/16 03:56:48  was
   *** empty log message ***

   Revision 1.1  2001/05/16 03:51:41  was
   Initial revision

      
 ***************************************************************************/

import "categories.m" : CopyOfModFrmElt;

import "predicates.m" : SpaceType,
                        SpaceTypeParam,
                        AssociatedSpaceOverZ;

intrinsic ModularForms(R::Rng) -> ModFrm
{The space spanned by all modular forms of all weights over the ring R.}
   M := HackobjCreateRaw(ModFrm);
   M`base_ring := R;
   M`is_complex := true;
   M`is_padic := false;
   M`is_modp := false;
   M`dimension := Infinity();
   M`type := "ring";
   return M;
end intrinsic;

intrinsic ModularForms(N::RngIntElt) -> ModFrm
{The space of weight 2 modular forms on Gamma_0(N) over the integers.}
   require N ge 1: "The first argument must be at least 1.";
   return ModularForms(N,2);
end intrinsic; 

intrinsic ModularForms(N::RngIntElt, k::RngIntElt) -> ModFrm
{The space of weight k modular forms on Gamma_0(N) over the integers.}
   require N ge 1: "The first argument must be at least 1.";
   requirege k,1;
   return ModularForms([DirichletGroup(N)!1], k);
end intrinsic;

function ModularFormsGamma1(N, k)
   assert Type(N) eq RngIntElt;
   assert Type(k) eq RngIntElt;
   M := HackobjCreateRaw(ModFrm);
   M`base_ring := Integers();
   M`is_gamma1 := true;
   M`level := N;
   M`weight := k;
   M`type := "full";
   return M;
end function;

intrinsic ModularForms(G::GrpPSL2) -> ModFrm
{The space of weight 2 modular forms on G over the integers.}
   require IsGamma1(G) or IsGamma0(G) : "Argument 1 must be Gamma_0(N) or Gamma_1(N).";
   return ModularForms(G,2);
end intrinsic;

intrinsic ModularForms(G::GrpPSL2, k::RngIntElt) -> ModFrm
{The space of weight k modular forms on G over the integers.}
   requirege k,1;
   if IsGamma1(G) then
      return ModularFormsGamma1(Level(G),k);
   elif IsGamma0(G) then
      return ModularForms(Level(G),k);
   else
      require false : "Argument 1 must be Gamma_0(N) or Gamma_1(N).";
   end if;
end intrinsic;

/*intrinsic ModularForms(eps::GrpDrchElt, k::RngIntElt) -> ModFrm
{The space M_k(N,eps) over Z.}
   require Type(BaseRing(Parent(eps))) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   return ModularForms([eps],k); 
end intrinsic;
*/

intrinsic ModularForms(chars::[GrpDrchElt], k::RngIntElt) -> ModFrm
{The direct sum of the spaces ModularForms(eps,k), where eps runs through
 representatives of the Galois orbits of the characters in the
 sequence chars.}
   requirege k,1;
   require #chars gt 0 : "Argument 1 must have length at least 1.";
   require Type(BaseRing(Parent(chars[1]))) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   
   M := HackobjCreateRaw(ModFrm);
   M`base_ring := Integers();
   M`dirichlet_character := GaloisConjugacyRepresentatives(chars);
   M`is_gamma1 := false;
   M`weight := k;
   M`type := "full";
   return M;
end intrinsic;

intrinsic ModularForms(chars::[GrpDrchElt]) -> ModFrm
{The direct sum of the spaces ModularForms(eps,2), where eps runs through
representatives of the Galois orbits of the characters in the
sequence chars.}
   require #chars gt 0 : "Argument 1 must have length at least 1.";
   require Type(BaseRing(Parent(chars[1]))) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   return ModularForms(chars,2);
end intrinsic;


intrinsic ModularForms(eps::GrpDrchElt, k::RngIntElt) -> ModFrm
{The direct sum of the spaces ModularForms(eps,k), where eps runs through
representatives of the Galois orbit of eps.}
   requirege k,2;
   require Type(BaseRing(Parent(eps))) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   return ModularForms([eps],k);  
end intrinsic;

intrinsic ModularForms(eps::GrpDrchElt) -> ModFrm
{The direct sum of the spaces ModularForms(eps,2), where eps runs through
representatives of the Galois orbit of eps.}
   require Type(BaseRing(Parent(eps))) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   return ModularForms([eps],2);  
end intrinsic;

// The following two intrinsic are cheats, because they don't
// have proper require statements. 
intrinsic CuspForms(x::.) -> .  
{}
   return CuspidalSubspace(ModularForms(x));
end intrinsic;

intrinsic CuspForms(x::., y::.) -> .
{}
   return CuspidalSubspace(ModularForms(x,y));
end intrinsic;

// Just copy the most very basic defining properties of M, not 
// things like hecke operators and so on. 
function CopyOfDefiningModularFormsObject(M)
   C := HackobjCreateRaw(ModFrm);  // C for "copy"
   if assigned M`dimension then
      C`dimension := M`dimension;  
   end if;
   C`base_ring := BaseRing(M);
   C`level := Level(M);
   C`weight := Weight(M);
   C`type := SpaceType(M);
   C`type_param := SpaceTypeParam(M);
   C`is_gamma1 := IsGamma1(M);
   if assigned M`made_from_newform then
      C`made_from_newform := M`made_from_newform;
   end if;
   if assigned M`dirichlet_character then
      C`dirichlet_character := M`dirichlet_character;  
   end if;
   if assigned M`ambient_space then
      C`ambient_space := M`ambient_space;
   end if;
   if assigned M`associated_space_over_z then
      C`associated_space_over_z := M`associated_space_over_z;
   end if;
   if assigned M`default_precision then
      C`default_precision := M`default_precision;
   end if;
   return C;
end function;

intrinsic ModularForms(M::ModFrm, N::RngIntElt) -> ModFrm
{The full space of modular forms with the same defining 
weight, base ring, etc. as M, but with level N.  
If M is defined by nontrivial dirichlet characters, then
N must be a multiple at least one of the conductors 
of the characters.}
   T := HackobjCreateRaw(ModFrm);  
   T`base_ring := BaseRing(M);
   T`level := N;
   T`weight := Weight(M);
   T`is_gamma1 := IsGamma1(M);
   T`type := "full";
   if not IsGamma1(M) and assigned M`dirichlet_character then 
      chars := [];
      E := M`dirichlet_character;
      G := DirichletGroup(N,BaseRing(Parent(E[1])));
      for eps in [chi : chi in E |  N mod Conductor(chi) eq 0] do
         e1 := Restrict(eps,Conductor(eps));
         e2 := N ne Conductor(eps) select Extend(e1,N) else e1;
         Append(~chars, e2);
      end for;
      require #chars gt 0: "If M is defined by nontrivial dirichlet characters, then N must be a multiple at least one of the conductors of the characters.";
      T`dirichlet_character := chars;
   end if;
   if assigned M`default_precision then
      T`default_precision := M`default_precision;
   end if;
   return T;
end intrinsic;


/*
intrinsic ModularForms(eps::GrpDrchElt, k::RngIntElt) -> ModFrm
{This returns M_k(eps), which is the direct sum of the e parts 
of M_k(Gamma_1(N)) as e varies over the Galois conjugates of eps.} 
   requirege k,2;
   require Type(BaseRing(eps)) in {FldCyc, FldRat} : 
       "The base ring of argument 1 must be the rationals or cyclotomic.";
   return ModularForms([eps],k);
end intrinsic;
*/



function CreateModularFormFromElement(parent, element)
   assert Type(parent) eq ModFrm;
   assert Type(element) in {ModTupRngElt, ModTupFldElt};
   y := HackobjCreateRaw(ModFrmElt);
   y`parent := parent;
   y`element := element;
   return y;
end function;


function CoerceRngIntElt(M,x)
   assert Type(M) eq ModFrm;
   assert Type(x) eq RngIntElt;

   if x eq 0 then
      return true, CreateModularFormFromElement(M, 
                 RSpace(BaseRing(M), Dimension(M))!0);
   else
      return false, "Can't coerce nonzero integer in.";
   end if;
  
end function;

function CoerceRngSerPowElt(M,x)
   assert Type(M) eq ModFrm;
   assert Type(x) eq RngSerPowElt;

   /* If x agrees with the q-expansion of some element of M, then
      this function returns that element. Otherwise, there is an error. */
   error_message := "The series does not define a modular form in the space.";

   if Dimension(M) eq 0 and x ne 0 then
      return false, error_message;
   end if;

   if RelativePrecision(x) eq -1 then
      return false, "Illegal coercision -- power series must have absolute precision.";
   end if;
   prec := AbsolutePrecision(x);
   Q := [PowerSeries(f,prec) : f in Basis(M)];
   elt := [BaseRing(M)|];
   f := Parent(Q[1])!0;
   zero := BaseRing(M)!0;
   for n in [1..#Q] do
      //v := Valuation(Q[n]);   // this doesn't work right anymore, because 
                                // of how the new p-adics are implemented.
      seq := [Integers() | i : i in [0..prec-1] | zero ne Coefficient(Q[n],i)];
      if #seq eq 0 then
         v := prec;
      else
         v := Min(seq);
      end if;
         
      if v ge AbsolutePrecision(x-f) then
         Append(~elt,0);
         continue;
      end if;
      /* It's now no longer possible to work in K[x]/(f(x)) when f is irreducible.  
         Until this changes, working with modular forms over the p-adics in MAGMA 
         is essentially impossible, since generic extensions of the p-adics can't
         be created.   The problem is the XGCD doesn't work over p-adics, but I'm 
         sure this'll be fixed soon.  (WAS, 06/16/03).
       */
      coef := Coefficient(x-f,v) / Coefficient(Q[n],v);
      if not (coef in BaseRing(M)) then
         return false, error_message;
      end if;
      coef := BaseRing(M)!coef;
      f +:= coef*Q[n];
      Append(~elt,coef);
   end for;
   g := M!elt;
   if x ne PowerSeries(g,prec) then 
      // the above test doesn't work over rings like C or Qp.
      if Type(BaseRing(M)) in {FldRat, RngInt, FldNum, FldCyc, FldFin} then
//         print "0 =/= coerced - x = ", x - PowerSeries(g,prec);
         return false, error_message;
      end if;
   end if;
   return true, g;
end function;

function CoerceSeqEnum(M,x)
    val, el := IsCoercible(RSpace(M),x);
    if val eq false then
       return false, 
           "Illegal coercion.";
    end if;
    return true, CreateModularFormFromElement(M, el);
end function;


function CoerceModFrmElt(M,x)
   if BaseRing(Parent(x)) cmpne BaseRing(M) then
      return false, "Incompatible base rings.";
   end if;
   if IsRingOfAllModularForms(M) then
      y := CopyOfModFrmElt(x);
      y`parent := M;
      delete y`element;
      return true, y;
   end if;  
   if Parent(x) eq M then
      return true, x;
   end if;
   if Parent(x) subset M then
      g := PowerSeries(x,PrecisionBound(M : Exact := false));
      return IsCoercible(M,g);
   end if;
   if IsRingOfAllModularForms(Parent(x)) then
      return false, "Illegal coercion.";
   end if;   

   if Weight(M) ne Weight(Parent(x)) then
      return false, "Incompatible weights.";
   end if;
   N := LCM(Level(M),Level(Parent(x)));
   if IsGamma0(M) and IsGamma0(Parent(x)) then  
      G := Gamma0(N); 
   else
      G := Gamma1(N);
   end if;
   Mbig := BaseExtend(ModularForms(G,Weight(M)), BaseRing(M));
   prec := PrecisionBound(Mbig : Exact := false);
   t, z := IsCoercible(M, PowerSeries(x,prec));
   if not t then
      return false, "Cannot coerce modular form.";
   else
      return t, z;
   end if;
end function;

intrinsic HackobjCoerceModFrm(M::ModFrm,x::.) -> BoolElt, ModFrmElt
{Coerce x into M.}
   case Type(x):
      when RngIntElt:
         return CoerceRngIntElt(M,x);
      when ModTupRngElt, ModTupFldElt:
         return CoerceSeqEnum(M,Eltseq(x));
      when SeqEnum:
         return CoerceSeqEnum(M,x);
      when RngSerPowElt:
         return CoerceRngSerPowElt(M,x);
      when ModFrmElt:
         return CoerceModFrmElt(M,x);
      else
         return false, "Illegal coercion";
   end case;
end intrinsic;


intrinsic BaseExtend(M::ModFrm, R::Rng) -> ModFrm, Map
{The base extension of M to the commutative ring R and the induced map
from M to BaseExtend(M,R).  The only requirement on R is that
there is a natural coercion map from BaseRing(M) to R.    For 
example, when BaseRing(M) is the integers then any commutative
ring R is allowed.}
   require IsCommutative(R) : "Argument 2 must be commutative.";
   if R cmpeq BaseRing(M) then
      return M;
   end if;
   phi := hom<BaseRing(M) -> R | x :-> R!x>;
   return BaseExtend(M,phi);
end intrinsic;


intrinsic BaseExtend(M::ModFrm, phi::Map) -> ModFrm, Map
{The base extension of M to R using the map phi:BaseRing(M) --> R, 
and the induced map from M to BaseExtend(M,R).}
   require IsCommutative(Codomain(phi)) : "Argument 2 must be commutative.";
   M_R := CopyOfDefiningModularFormsObject(M);
   if Characteristic(Codomain(phi)) ne 0 then
      delete M_R`default_precision;
   end if;
   M_R`base_ring := Codomain(phi);
   M_R`base_extend_map := phi;
   if Type(BaseRing(M_R)) ne RngInt then
      M_R`associated_space_over_z := AssociatedSpaceOverZ(M);
   end if;
   if not IsAmbientSpace(M) then
      M_R`ambient_space := BaseExtend(AmbientSpace(M),phi);
   end if;
   psi := hom<M -> M_R | x :-> M_R![phi(a) : a in Eltseq(x)]>;
   return M_R, psi;
end intrinsic;

intrinsic DisownChildren(M::ModFrm) 
{Ambient modular symbols spaces M can create circular references, which 
cause the memory manager to never de-allocate M.  
Calling DisownChildren forces the circular references created
by M to be deleted.}
   if assigned M`ambient_space then
      delete M`ambient_space;
   end if;
   
   if assigned M`cusp then 
      delete M`cusp;
   end if;

   if assigned M`eis then 
      delete M`eis;
   end if;

   if assigned M`new then 
      delete M`new;
   end if;

   if assigned M`cusp_new then 
      delete M`cusp_new;
   end if;

   if assigned M`eis_new then 
      delete M`eis_new;
   end if;

   if assigned M`cuspidal_subspace then 
        delete M`cuspidal_subspace;
   end if;

   if assigned M`new_subspace then 
        delete M`new_subspace;
   end if;

   if assigned M`eisenstein_subspace then 
        delete M`eisenstein_subspace;
   end if;

   if assigned M`mf_modular_symbols then 
      for x in M`mf_modular_symbols do
         if Type(x) ne BoolElt then
            for y in x do
               DisownChildren(y);
            end for;
         end if;
      end for;
   end if;

   if assigned M`newforms then 
      for group in M`newforms do
         for f in group do
            DisownChildren(Parent(f));
         end for;
      end for;
   end if;
   
   if assigned M`basis then
      delete M`basis;
   end if;

   if not IsAmbientSpace(M) then
      DisownChildren(AmbientSpace(M));
   end if;
end intrinsic;
