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
// The functions here make it easy to combine two cover descent on hyper elliptic curves
// together with elliptic curve Chabauty.

QQ := Rationals();

// Innocent helper function
function Zip(a, b)
  //similar to pythons zip function
  assert #a eq #b;
  n := #a;
  return [<a[i],b[i]> : i in [1..n]];
end function;

// Magma's TwoCoverDescent function returns a function AtoHk that maps elements from
// some algebra A to the fake 2-Selmer set Hk. This function returns the inverse of this
// map. Elements in the algebra A can be used to construct the two-cover of the
// corresponding element in the fake 2-Selmer set.
function createHktoA(AtoHk, expvecs, factorbase)
  A := Domain(AtoHk);
  A2 := Parent(factorbase[1]);
  phi := hom< A2 -> A | A.1 >;
  Hk := Domain(expvecs);
  HktoA := map< Hk -> A | h :-> phi(&*[ef[2]^ef[1] : ef in Zip(Eltseq(expvecs(h)),factorbase)])>;
  return HktoA;
end function;

// This function carries out the elliptic curve chabauty algorithm explained in Section
// 4.2 of the paper. It is inspired by the example shown in:
//   https://magma.maths.usyd.edu.au/magma/handbook/text/1566#18074
// If C is a hyper elliptic curve given by y^2=f(x) then g should be a factor of degree
// 3 or 4 of f. The factor g is allowed to be defined over a field extension.
// The element hk should be an element of the fake 2 selmer group. And corresponds to
// a two cover D -> C . D will map to the genus 1 curve gamma*y^2=g(x) for some
// twisting factor gamma. This function applies elliptic curve chabauty to find all
// points on C(Q) that come from a point on D(Q). The return value is a 4 tuple:
//
//    (success, gamma, points, message)
//
//  - success: a boolean that when true guarantees that all points on C(Q) coming from
//    D(Q) are contained in the set of points output by this function
//  - gamma: is the twisting factor corresponding to g and hk ensuring that D has a map to
//    gamma*y^2=g(x)
//  - points: a set of points on C
//  - message: an message that indicates why we failed to do elliptic curve chabauty in
//    case of failure
function EllipticChabauty(C, g, hk, HktoA : Bound:= 100)
  // Basic setup is as follows
  assert IsMonic(g);
  assert BaseRing(C) eq QQ;
  assert Degree(g) in [3,4];
  success := true;
  points := {@ @};
  P1:=ProjectiveSpace(QQ,1);
  CtoP1 := map< C -> P1 | [C.1,C.3] >;
  K := BaseRing(g);
  A := Codomain(HktoA);
  f := ChangeRing(Modulus(A), QQ);
  L := quo< Parent(g) | g>;
  assert Evaluate(f, L.1) eq 0;
  AtoL := hom< A -> L | L.1>;

  // For some gamma, we have that a 2-cover hk covers the genus 1 curve E:y^2=gamma * g.
  // The following obtains this gamma.

  gamma := Norm(AtoL(HktoA(hk)));
  for p in PrimeDivisors(Numerator(Norm(gamma))) do
    while IsIntegral(gamma/p^2) do
      gamma := gamma/p^2;
    end while;
  end for;
  print "gamma", gamma;
  gamma_g := <gamma,g>;

  // the promised elliptic curve above
  E := HyperellipticCurve(gamma*g);

  // However, we need it as an EllipticCurve object, so we do the following
  EtoP1:=map<E->P1|[E.1,E.3]>;
  iselliptic, E1raw, EtoE1raw, E1rawtoE := IsEllipticCurve(E);

  // If magma didn't find an elliptic curve model, we manually search for a rational
  // point to function as the point at infinity
  if not iselliptic then
    print "finding points";
    time Epoints := Points(E : Bound:=Bound);

    // If we can't find a point, we report failure. Theoretically we could still try
    // to prove that E is a pointless genus 1 curve. However this will be hard since
    // the two descent we did earlier implies that E is coverd by an everywhere
    // locally solvable curve. And hence there are no local obstructions.
    if #Epoints eq 0 then
      success := false;
      return success, gamma_g, points, "point search failed";
    end if;

    iselliptic := true;
    P0 := Setseq(Epoints)[1];
    E1raw,EtoE1raw:=EllipticCurve(E,P0);
    E1rawtoE := Inverse(EtoE1raw);
  end if;

  // Just thing of the following E1 as E but with a nicer model
  E1 := MinimalModel(E1raw);

  print "Elliptic curve", E1;

  // We need to compute the MW group of the elliptic curve. Return if that fails.
  time MW1, MW1toE1set, flag1, flag2 := MordellWeilGroup(E1);


  if not (flag1 and flag2) then;
    r := Degree(K);
    return success, gamma, points, <"mw failed",Invariants(MW1), r>;
  end if;

  // Some functions to make certain objects more explicit
  MW1toE1 := map<MW1 -> E1 | x :-> MW1toE1set(x)>;
  E1toE1raw := Isomorphism(E1,E1raw);
  E1toE := E1toE1raw*E1rawtoE;
  E1toP1 := Expand(E1toE1raw*E1rawtoE*EtoP1);

  // The Chabauty rank condition for elliptic curve Chabauty to work
  if TorsionFreeRank(MW1) ge Degree(K) then
    success := false;
    return success, gamma_g, points, <"rank to large",TorsionFreeRank(MW1),Degree(K)>;
  end if;

  // If we got here, then we can do elliptic curve Chabauty
  print "starting chabauty";
  pointset, R := Chabauty(MW1toE1, E1toP1);
  print R, #pointset;


  // At this point the strategy is successful, so we can determine all points on the twist
  for t in pointset do
    P := E1toE(MW1toE1(t));
    xP := EtoP1(P);
    xP := P1(QQ) ! xP;
    new_points := RationalPoints( xP@@CtoP1 );
    points := points join new_points;
  end for;

  return success, gamma_g, points, "found all points";
end function;