1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Base type for a all MDMagma modular curves
declare type MDCrvMod;
declare attributes MDCrvMod: level, curve, _E;

intrinsic Print(X::MDCrvMod)
{ Print X }
    printf "The MDCrvMod class shouldn't be used directly";
end intrinsic;

intrinsic _InitMDCrvMod(X::MDCrvMod, level::RngIntElt, curve::CrvPln, _E)
{Initialize an MDCrvMod object}
   char := Characteristic(BaseRing(curve));
   if char gt 0 then
     assert GCD(level, char) eq 1;
   end if;
   X`level := level;
   X`curve := curve;
   X`_E := _E;
end intrinsic;

intrinsic Level(X::MDCrvMod) -> RngIntElt
{ Return the level }
    return X`level;
end intrinsic;

intrinsic Curve(X::MDCrvMod) -> CrvPln
{ Return the underlying CrvPln object used for calculations with this modular curve }
    return X`curve;
end intrinsic;

intrinsic Genus(X::MDCrvMod) -> CrvPln
{ Return the genus of this modular curve }
    return Genus(X`curve);
end intrinsic;

intrinsic BaseRing(X::MDCrvMod) -> Rng
{ Return the base ring over which this modular curve is defined }
    return (assigned X`curve ) select BaseRing(X`curve)  else "<unassigned>";
end intrinsic;

intrinsic EllipticCurve(X::MDCrvMod, x::PlcCrvElt) -> CrvEll
{ Return the elliptic curve corresponding to the place x on X }
    E := EllipticCurve([Evaluate(f, x) : f in X`_E]);
    return E;
end intrinsic;

intrinsic  jInvariantMap(X::MDCrvMod) -> CrvEll
{ Return the jInvariant map to X(1) }
    E := EllipticCurve(X`_E);
    return jInvariant(E);
end intrinsic;

intrinsic  jInvariant(X::MDCrvMod, x::PlcCrvElt) -> FldElt
{ Return the jInvariant of the elliptic curve corresponding to E }
    E := EllipticCurve(X, x);
    return jInvariant(E);
end intrinsic;

intrinsic  DiscriminantMap(X::MDCrvMod) -> CrvEll
{ Return the discriminant of the universal elliptic curve over X }
    E := EllipticCurve(X`_E);
    return Discriminant(E);
end intrinsic

intrinsic IsCusp(X::MDCrvMod, x::PlcCrvElt) -> BoolElt
{ Returns whether the place x on X  is a cusp}
    try
      E := EllipticCurve(X,x);
    catch e;
      return true;
    end try;
    return false;
end intrinsic;

intrinsic NoncuspidalPlaces(X::MDCrvMod, d::RngIntElt) -> SeqEnum[PlcCrvElt]
{ Return the noncuspidal places of degree d on X}
    return [x : x in Places(Curve(X), d) | not IsCusp(X, x)];
end intrinsic;

intrinsic Cusps(X::MDCrvMod, d::RngIntElt) -> SeqEnum[PlcCrvElt]
{ The cusps of degree d on this modular curve }
    return [C : C in Cusps(X) | Degree(C) eq d];
end intrinsic;

intrinsic HeckeOperator(X::MDCrvMod, p::RngIntElt, x::PlcCrvElt) -> DivCrvElt
{ Return the result of applying the hecke operator T_p on x as a divisor on X }
    char := Characteristic(BaseRing(X));
    if char gt 0 then
      assert GCD(char, p) eq 1;
    end if;
    ZZ := Integers();
    d := Degree(x);
    E := EllipticCurve(X, x);
    L := LevelStructure(X, x);
    isogenies := MDIsogenies(E,p);
    tp := [<Codomain(phi),ApplyIsogeny(X, phi, L)> : phi in isogenies];
    // the below doesn't work since magma has a bug where equal elements are not
    // identified in the multiset below.
    // tp := Multiset([ModuliPoint(X, EL[1], EL[2]): EL in tp]);
    tp := MDMultiset([ModuliPoint(X, EL[1], EL[2]): EL in tp]);
    Tpx := &+[ (ZZ ! (Multiplicity(tp,x)*d/Degree(x)))*x : x in MultisetToSet(tp)];
    return Tpx;
end intrinsic;

intrinsic HeckeOperator(X::MDCrvMod, p::RngIntElt, D::DivCrvElt) -> DivCrvElt
{ Return the result of applying the hecke operator T_p on D as a divisor on X }
    a,b := Support(D);
    TpD := [b[i]*HeckeOperator(X, p, a[i]) : i in [1..#a]];
    return &+TpD;
end intrinsic;

intrinsic DiamondOperator(X::MDCrvMod, d::RngIntElt, x::PlcCrvElt) -> PlcCrvElt
{ Return the result of applying the diamond operator <d> on x as a place on X }
    E := EllipticCurve(X, x);
    L := LevelStructure(X, x);
    return ModuliPoint(X, E, DiamondOperator(X, d, L));
end intrinsic;

intrinsic DiamondOperator(X::MDCrvMod, d::RngIntElt, D::DivCrvElt) -> DivCrvElt
{ Return the result of applying the diamond operator <d> on D as a divisor on X }
    a,b := Support(D);
    dD := [b[i]*DiamondOperator(X, d, a[i]) : i in [1..#a]];
    return &+dD;
end intrinsic;

intrinsic DiamondOperator(X::MDCrvMod, d::RngIntElt, x::PlcCrvElt) -> PlcCrvElt
{ Return the result of applying the diamond operator <d> on x as a place on X }
    E := EllipticCurve(X, x);
    L := LevelStructure(X, x);
    return ModuliPoint(X, E, DiamondOperator(X, d, L));
end intrinsic;

intrinsic DiamondOrbit(X::MDCrvMod, x::PlcCrvElt) -> SeqEnum[PlcCrvElt]
{ Return the orbit of x under the diamond operators }
    E := EllipticCurve(X, x);
    L := LevelStructure(X, x);
    N := Level(X);
    diamonds := [i : i in [1..(N div 2)] | GCD(i,N) eq 1];
    return [ModuliPoint(X, E, DiamondOperator(X, d, L)) : d in diamonds];
end intrinsic;

intrinsic DiamondOperator(X::MDCrvMod, d::RngIntElt, D::DivCrvElt) -> DivCrvElt
{ Return the result of applying the diamond operator <d> on D as a divisor on X }
    a,b := Support(D);
    dD := [b[i]*DiamondOperator(X, d, a[i]) : i in [1..#a]];
    return &+dD;
end intrinsic;

intrinsic PlacesUpToDiamond(X::MDCrvMod, S::SeqEnum[PlcCrvElt]) -> SeqEnum[PlcCrvElt]
{ Return one representative of each orbit in S under the action of the diamond operators }
    representatives := [];
    orbits := AssociativeArray();
    // the orbits are grouped by minimap polynomial of the j-invariant to speed up
    // everything
    N := Level(X);
    F := PrimeField(BaseRing(X));
    diamonds := [i : i in [1..(N div 2)] | GCD(i,N) eq 1];
    for P in S do
        already_added := false;
        jP := MinimalPolynomial(jInvariant(X,P),F);
        if IsDefined(orbits, jP) then
            if P in orbits[jP] then
                already_added := true;
            end if;
        end if;
        if not already_added then
            Append(~representatives, P);
            orbit := DiamondOrbit(X, P);
            if IsDefined(orbits, jP) then
                orbits[jP] :=  orbits[jP] cat orbit;
            else
                orbits[jP] :=  orbit;
            end if;
        end if;
    end for;
    return representatives;
end intrinsic;

intrinsic NoncuspidalPlacesUpToDiamond(X::MDCrvMod, d::RngIntElt) -> SeqEnum[PlcCrvElt]
{ Return the noncuspidal places of degree d on X up to the action of the diamond operators}
     return PlacesUpToDiamond(X, NoncuspidalPlaces(X,d));
end intrinsic;

intrinsic DegeneracyMap(X::MDCrvMod, Y::MDCrvMod, D::DivCrvElt) -> DivCrvElt
{ Return the result of applying the degeneracy map on the level of divisors on X, see
  the documentation where D is a PlcCrvElt for more details.
}
    a,b := Support(D);
    dD := [b[i]*DegeneracyMap(X, Y, a[i]) : i in [1..#a]];
    return &+dD;
end intrinsic;

intrinsic ClassGroup(X::MDCrvMod : as_tuple := false) -> GrpAb, Map, Map
{ Return the class group of the curve underlying X
}
    if not assigned X`_classgroup then
        G, m1, m2 := ClassGroup(Curve(X));
        X`_classgroup := <G, m1, m2>;
    end if;
    if as_tuple then
        return X`_classgroup;
    end if;
    return Explode(X`_classgroup);
end intrinsic;

intrinsic CuspidalClassGroupQ(X::MDCrvMod) -> GrpAb, Map, Map
{ Return the subgroup of the class group of X generated by Gal(Q) orbits of cusps,
  See also CuspOrbitsQ(X).
}
    cusps := CuspOrbitsQ(X);
    return MDClassGroup(Curve(X),  cusps: classgroup:=ClassGroup(X : as_tuple:=true));
end intrinsic;

intrinsic CuspidalClassGroup(X::MDCrvMod, d::RngIntElt) -> GrpAb, Map, Map
{ Return the subgroup of the class group of X generated by the cusps of degree dividing d
}
    cusps := &cat [Cusps(X,d1) : d1 in Divisors(d)];
    return MDClassGroup(Curve(X),  cusps: classgroup:=ClassGroup(X : as_tuple:=true));
end intrinsic;