NOTES ON STATISTICS, PROBABILITY and MATHEMATICS


Elliptic Curves:


In Sage, an elliptic curve is always specified by (the coefficient of) a long Weierstrass equation

\[y^2 + a_1 xy + a_3 y = x^3 + a_2 x^2 + a_4 x + a_6.\]

So

EllipticCurve([a,b,c,d,e])

will yield

\[y^2 + a xy + c y = x^3 + b x^2 + d x + e.\]

E = EllipticCurve([1,2,3,4,5])
E
E = EllipticCurve([-2,2])
E
F = EllipticCurve([-2,1.1])
F
plot(E, xmax=5, ymin=-5, ymax=5, figsize=6, thickness=4) + plot(F, color='aqua', thickness=4)

For this curve, the number of rational solutions over \(\mathbb Z/ {p \mathbb Z}\) where the prime is \(5\) is \(9.\)

# The number of integer solutions modulus 5 is:
E.Np(5)
Emod5 = EllipticCurve(Zmod(5), [1,2,3,4,5])
Emod5.points()

From the Cremona index:

plot(EllipticCurve('37b2'))
E = EllipticCurve([2,5])
print(E)
Emod5 = EllipticCurve(Zmod(5), [2,5])
Emod5.points()

So we have the point \((0,0)\) as a solution because \(0^2\equiv_{(\text{mod} 5)} 0^3 + 2\times 0 +5.\) There are no other integer solutions.

The second point \((0:1:0)\) is just infinite.

Algebraically, this can be understood as homogeneous coordinates, in which initially there is a change of variables as to translate the \(x-y\) Cartesian plane where the curve is plotted to a plane translated \(1\) unit up along a newly introduced \(z\) axis so that the range of points on the plane are connected to the origin as a projection of a cone of lines emanating from the origin or “pencil of lines.” This is projective geometry.

This is explained in this lecture by Bill Shillito and in this video by NJ Wildberger.

After this transformation, if we imagine the points on any of these lines to be \([X, Y ,Z],\) the points of the curve on the translated plane would be \([\frac X Z, \frac Y Z, 1]\), which corresponds to scaling any of the lines connecting the origin to the points (\([X,Y,Z]\)) by \(Z\). This is called the projective plane \(\left(\mathbb P^2\right)\) and forms and equivalence relation where \((1,1,1)\equiv (2,2,2)\).

Notice the upper case of the new variables \(X, Y.\)

Plugging these back into the initial equation of an elliptic curve \(y^2 =x^3 - 2x +2\) would result in \(\left(\frac Y Z \right)^2 = \left(\frac X Z \right)^3 - 2 \left(\frac X Z \right) + 2.\)

To convert into the homogeneous expression, all the terms should be of the same degree, which is achieved by getting rid of the denominators as in \(( Y^2 Z) = (X)^3 - 2(X Z^2) + 2X^3\).

What is the advantage of homogeneous coordinates? The allow interpreting the points at infinity more readily:The points at infinity are not in the new plane at \(Z=1\), rather, \(Z=0.\)

Therefore we end up with \(Y^2 \, 0 = X^3.\) This means that \(X\) has to be zero, and \(Y\) can take any value. Hence \((0:1:0),\) as above. This is explained in here.


There are explanations about mapping the infinity plane to a sphere to visualize the point at infinity as in here, or here or here.

Imaging the curve on a unit sphere allows imagining the point at infinity as the North Pole, and the original plot as in here:

The equations for a unit sphere centered at \((0,0)\) are:

\[ \begin{align} \left(\frac{2x}{x^2 + y^2 + 1}, \frac{2y}{x^2 + y^2 + 1}, \frac{-1 + x^2 + y^2}{x^2 + y^2 + 1} \right) \end{align}\]

as in here.

It turns out that elliptic curves have a group structure with the addition operation defined as:

If P and Q are two points on the curve, then we can uniquely describe a third point P + Q in the following way. First, draw the line that intersects P and Q. This will generally intersect the cubic at a third point, R. We then take P + Q to be −R, the point opposite R.

Why reflect across the \(x\)-axis? So that the point at infinity \(\mathcal O\) is the identity element. Without reflection, \(\mathcal O + P = –P\) instead of \(\mathcal O + P = P.\)

The following visualization makes operations on points across the \(x\) axis understandable:



It is clear therefore, why \(P + Q + P = \mathcal O.\)



For modulo \(7\) it may be necessary to use an ad hoc function as in here because the curve is singular.

def points_mod(modulus):
    i = 1
    for x in range(modulus):
        for y in range(modulus):
            if (y^2 - (x^3 + 2*x + 5)) % modulus == 0:
                print(f"{i}: (x, y) = ({x}, {y})")
                i += 1
print(E.Np(7)) # 7 points
points_mod(7) # 6 points plus infinity

The difference between the number of solutions of an elliptic curve over a finite field \(\mathbb F_p\) and \(p-1\) is \(\epsilon_p,\) which is calculated as E.ap().

What is a finite field?

A field \(\mathbb F_p\) is finite if it has \(p\) elements, such that \(p = q^r\), where \(q\) is prime. From Wikipedia:

If \(p = q^r\), all fields of order \(p\) are isomorphic. Moreover, a field cannot contain two different finite subfields with the same order. One may therefore identify all finite fields with the same order, and they are unambiguously denoted \(\mathbb{F}_{p}\) or \(\text{GF}(p)\), where the letters GF stand for “Galois field”.

The number of solutions to an elliptical curve on a finite field is related to the Legendre symbol by:

\[\vert E(\mathbb F_p) \vert= \sum_{x \in \mathbb F_p} 1 + \left (\frac{f(x)}{p} \right) + 1 = p + 1 + \left (\frac{f(x)}{p} \right)= p + 1 - a_p\]

The first \(1\) is because when the Legendre symbol is \(1,\) we have a square, and the negative of it, will also be a solution. If it is not a square it will cancel the \(-1\) from the Legendre symbol. The last \(1\) is the \(\mathcal O.\) The symbol \(a_p = - \left (\frac{f(x)}{p} \right).\)

The upper-bound of \(a_p\) (the cone the plots below) is given by \(2 \sqrt p\) and the lower bound by \(2 \sqrt p\) (Hasse-Weil bound).

ap_list = []
for i in primes_first_n(5000):
    ap_list += [E.ap(i)]
import numpy
list_plot(numpy.array(ap_list), size=1, figsize=8)

What follows is a simple example illustrating Elliptic Curve Cryptography from a YT presentation by Robert Pierce.

From here:

In cryptography, an elliptic curve is a group which has a given size \(n\). We normally work in a subgroup of prime order \(r\), where \(r\) divides \(n\). The “cofactor” is \(h = n/r\).

The curve used below is \[y^2 = x^3 + 2x +2\]

over the field \(\mathbb Z/{p\mathbb Z}= \text{mod }(17).\)

And the cyclic subgroup used has the generating point \(G=(5,1).\)

F = Zmod(17)
E = EllipticCurve(F,[2,2])
print(E)
plot(E, figsize=8)

This group is of order

\[\vert E(\mathbb Z/ {p \mathbb Z}\vert=18\]

The cyclic group generated by the point \(G = (5,1)\) hits all the points in the curve and therefore it has order:

\[\text{ord}(G)= 18\]

with cofactor

\[h=\frac{\vert E(\mathbb Z/ {p \mathbb Z}\vert}{\text{ord {(G)}}}=1\]

plot(E, color='red', figsize=3) + points(((5,1), (6,3), (10,6), (3,1), (9,16), (16,13), 
                  (0,6), (13,7), (7,6), (7,11), (13,10), (0,11), 
                  (16,4), (9,1), (3,16), (10,11), (6,14), (5,16)), color='red', figsize=8)
Em = EllipticCurve(Zmod(2503), [3,7])
Em
plot(Em, figsize=8, size=1)
E = EllipticCurve(GF(11),[2,7])
print(E)
E.plot()

From this presentation, here is an exampe of the curve \[E: y^2 = x^3 + x + 1\] over \(\mathbb Q\) with two points we are going to add: \(P=(1,1)\) and \(Q=(2,3).\)

The line between \(P\) and \(Q\) is \(y = \frac{3 - 1}{2 - 1}x - 1 = 2x - 1.\)

Substituting this into the \(E\)

\[(2x-1)^2 = x^3 + x + 1\]

which can be foiled into

\[0 = (x-1)^2 (x-2)^2\]

which implies that the point \(P=(1,1)\) has multiplicity \(2\) because of \((x-1)^{\color{red}2}\). Therefore the third point of intersection between \(E\) and this line is thought of hitting \((1,1)\) twice.

Reflecting it over the \(x\)-axis, we get \((1,-1).\)

E = EllipticCurve([1,-1])
print(E)
Eplot = E.plot()
points = [(1,1),(2,3)] 
Eplot += plot(2*x-1, (x,0,2.5), color='green')
Eplot += list_plot(points, color='red', pointsize=45)
P=E([1,1]); Q=E([2,3])
P+Q
Eplot + list_plot([(1,-1)],color='gold', pointsize=45)
E = EllipticCurve([-5,4])
print(E)
P = E([1,0]); Q = E(0,2) # Two points on the curve.
P + Q
P + P # This is the point to infinity

Mordell–Weil’s theorem shows \(E(\mathbb Q )\) is a finitely generated abelian group, thus \(\displaystyle E(\mathbb {Q} )\cong E(\mathbb {Q} )_{tors}\times \mathbb {Z} ^{r}\) where \(E(\mathbb {Q} )_{tors}\) is the finite torsion subgroup and \(r\) is the rank of the elliptic curve.

\[y^2 = x^3 -x\]

E = EllipticCurve([-1,0])
E

It’s group is isomorphic to \(\mathbb Z/2\mathbb Z \oplus \mathbb Z/2\mathbb Z\)

E.torsion_subgroup()
E.rank() # Rank 0: There are no points of infinite order, only finite order points
E.torsion_points()
2*E((-1 , 0 , 1))
4*E((0 , 0 , 1))
E((1 , 0 , 1)) + E((1 , 0 , 1)) 
\[y^2 + y = x^3 - x\]
sage E = EllipticCurve([0,0,1,-1,0]) E
sage E.torsion_subgroup()
sage E.rank()

\(y^2 = x^3 − 5x + 4\)

E = EllipticCurve([-5,4])
E
E.torsion_subgroup()
E.torsion_points()
E.rank()
E.gens()

You can construct infinitely many points on E, by adding a torsion point and some multiple of the generator

P = E(1,0) + 9 * E(0,2)
P # P is on the curve. To check just run E(P)
E(P)
E.modular_form()

L-function of an elliptic curve:

In the study of arithmetic geometry, the L-function of an elliptic curve \(E\) over \(\mathbb{Q}\) is a powerful complex-analytic tool that packages deep arithmetic information about the curve — specifically, the number of points it has over finite fields (or, interchangeably, Galois fields). The L-function acts as a bridge between the algebraic world of the curve and the analytic world of modular forms, a connection famously solidified by the modularity theorem.

On the algebraic side we look at the curve over \(\mathbb{F}_p\), find the Frobenius trace, and get the integer \(a_p\) (explained below). On the analytic side we take the modular form \(f(\tau)\), look at the \(p\)-th coefficient of its \(q\)-expansion, and find \(a_p\). The bridge to the analytic side is \(q = \exp(2 \pi i \tau)\). It allows us to turn a function of a complex variable \(\tau\) into a power series, known as a Fourier expansion or q-expansion:

\[f(\tau) = \sum_{n=1}^{\infty} a_n q^n\]

It is called analytic because once we express the curve’s data as a \(q\)-expansion, we can use the heavy machinery of complex analysis: \(1.\) Analytic continuation: we can define the L-function even where the sum doesn’t normally converge. \(2.\) Functional equations: we can prove the function has a perfect symmetry when we reflect it across a specific line (usually \(s=1\)). \(3.\) The BSD conjecture: we can use the steepness of this complex function at \(s=1\) to predict the rank of the elliptic curve (how many infinite points it has).


Let \(E\) be an elliptic curve defined over a finite field \(\mathbb{F}_p\). The Frobenius map \(\phi_p\) is a specific geometric transformation that takes a point \((x, y)\) on the curve and raises its coordinates to the \(p\)-th power:

\[\phi_p(x, y) = (x^p, y^p)\]

Because of the way finite fields work, and specifically Fermat’s Little Theorem, where

\[x^p \equiv x \pmod p\]

a point \(P\) known to be on the curve \(E\) is fixed by this map if and only if its coordinates are in the base field \(\mathbb{F}_p\), i.e. \(x, y \in \mathbb{F}_p\) In other words:

\[P \in E(\mathbb{F}_p) \iff \phi_p(P) = P\]

This means that finding the number of points on \(E(\mathbb{F}_p)\) is equivalent to finding the number of fixed points of the Frobenius map.

Since \(\mathcal{O}\) doesn’t have standard \((x, y)\) coordinates (it’s a direction in projective space), we usually define \(\phi_p(\mathcal{O}) = \mathcal{O}\) by default. Thus, the equivalence \(P \in E(\mathbb{F}_p) \iff \phi_p(P) = P\) remains true even for the point at infinity.

Let’s analyze this:

\(\implies\): If \(P\) is a point on the curve whose coordinates are simple integers (in \(\mathbb{F}_p\)) - big parenthesis to explain…

“simple integers”: These are just the numbers \(\{0, 1, 2, \dots, p-1\}\). They are finite because they stop at \(p\). The \(p\)-adic integer (the infinite to the left string) is denoted \(\mathbb{Z}_p\) is an infinite expansions:

\[d_0 + d_1 p + d_2 p^2 + d_3 p^3 + \dots\]

The “simple integer” in \(\mathbb{F}_p\) is just the first digit (\(d_0\)) of a \(p\)-adic integer: it would have the form \(\dots 0000d_0\).

A point on the curve with \(x\) and \(y\) coordinates simple integers, i.e. \(E(\mathbb{F}_p),\) is a solution to the curve equation modulo \(p\). A point in \(E(\mathbb{Z}_p)\) is a lift of that solution. It is the infinite string that starts with \(d_0\) and continues forever in a way that always satisfies the equation at every higher power of \(p\).

… End of parenthesis… Continuing…

If a point on the curve has simple integer coordinates, the Frobenius map won’t move it, because the Frobenius map doesn’t move any of the base field numbers.

\(\impliedby\): If you find a point on the curve (even a “ghost point” in the extension field) (remember that \(P\) by definition is on the curve), and the Frobenius map doesn’t move it, i.e. \(\phi_p(P)=P,\) that proves its coordinates must have actually been simple integers all along, i.e. \(P \in E(\mathbb F_p)\) - If they are “ghosts” in \(\mathbb{F}_{p^k}\) (and not in the base field), the Frobenius map will move them.

Note that the fixed-point property (the logic of who stays still and who moves) belongs entirely to the “horizontal” world of field extensions (\(\mathbb{F}_p\) or \(\mathbb{F}_{p^k}\)), not the “vertical” world of \(p\)-adic lifts (\(\mathbb{Z}_p\)). This happens regardless of whether we ever lift the point to \(\mathbb{Z}_p\), which is separate step intended to calculate the \(a_p\) with enough precision to distinguish it from other numbers. If we stayed only at the \(\mathbb{F}_{p^k}\) level, we might know \(a_p \pmod 5\), but we wouldn’t be able to find the true integer value of \(a_p\) as easily - a separate topic from the Frobenius map.

This is critical to understand. For instance, the \(p\)-adic integer (“integer” \(\implies\) no digits to the right of the radix point) \(\underset{\text{base 10}}{4357} \in \mathbb{Z}_5\) (the base \(10\) integer \(4357\) interpreted as \(5\)-adic) can be written as a sum of powers of \(5:\)

\[4357 = 2(5^0) + 1(5^1) + 4(5^2) + 4(5^3) + 1(5^4) + 1(5^5)\]

or \(\dots000114412._5.\)

This is calculated here.

When we write \(4357\) as a sum of powers of \(5\), we are looking at its vertical resolution. This is just a standard base-\(5\) representation, as shown in the link. In the \(p\)-adic world, we say this number has precision \(6\) because we know \(6\) digits. Even though it uses \(5^5\), the alphabet we are using is just the set of simple integers \(\{0, 1, 2, 3, 4\}\).

Notice that ‘precision’ is not ‘valuation’: The valuation is a measure of how many factors of \(p\) are inside the number. It tells us how many trailing zeros (before a power of \(5\) has a non-zero coefficient) the number has in base \(p\). In our example \(4357\), the first digit is a \(2\) at the \(5^0\) level (\(2 \cdot 1\)). Since it doesn’t have a factor of \(5\) hiding in it, its valuation is \(0.\) If the number were \(50\), which is \(2 \cdot 5^2\) (or \(200\) in base \(5\)), the valuation would be \(2.\)

It is critical to see that the \(5\)-adic expression of \(4357\) is not in \(\mathbb{F}_{5^5}.\) The \(5\)-adic integer \(4357 \in \mathbb{Z}_5.\) If \(4357\) were an element of \(\mathbb{F}_{5^5}\), it wouldn’t be growing “upward” into powers of \(5\); it would be growing “sideways” into powers of a symbolic root \(\alpha\).

An element of \(\mathbb{F}_{5^5}\) looks like this:

\[c_0 + c_1\alpha + c_2\alpha^2 + c_3\alpha^3 + c_4\alpha^4\]

To make \(\alpha\) concrete, we have to look for a “missing number” in the base field. For \(p = 5\), in the base field \(\mathbb{F}_5\), we only have the numbers \(\{0, 1, 2, 3, 4\}\). If we look at their squares:\(0^2 = 0\), \(1^2 = 1\), \(2^2 = 4\), \(3^2 = 9 \equiv 4\), \(4^2 = 16 \equiv 1\). No number in the base field squares to \(2\) or \(3.\) They are missing their square roots. But, much as with \(i =\sqrt{-1}\) we can create a “ghost” (\(\alpha\)). To build the extension field \(\mathbb{F}_{5^2}\), we simply “invent” a number \(\alpha\) and define it by the rule: \(\alpha^2 = 2\). Now, \(\alpha\) is a legitimate resident of our new, wider field. Every “number” in this field is now a combination of a “real” part and an “alpha” part:

\[\text{Number} = c_0 + c_1\alpha\]

It’s called \(\mathbb{F}_{5^2}\) because that exponent (\(2\)) tells us exactly how many “dimensions” the number has, which in turn determines the total population of the field. When we create numbers like \(c_0 + c_1\alpha\), we have two independent slots to fill: The \(c_0\) slot with \(5\) choices \(\{0, 1, 2, 3, 4\},\) and the \(c_1\) slot with again \(5\) choices \(\{0, 1, 2, 3, 4\}\). To find the total number of unique elements in this new world, we multiply the choices: \(5 \times 5 = 5^2 = 25\).

To create an extension like \(\mathbb{F}_{p^3}\), we don’t just add a double (like another square root); we add a number that solves a cubic equation. In field theory, the exponent in \(\mathbb{F}_{p^k}\) tells us the degree of the ghost number we need to invent. For \(\mathbb{F}_{5^2}\), we used \(\alpha^2 = 2\). For \(\mathbb{F}_{5^3}\), we need an \(\alpha\) that satisfies a cubic equation that has no solutions in \(\mathbb{F}_5\).

In the world of finite fields, we don’t have to keep adding new letters (\(\alpha, \beta, \gamma\)) for every new root we want. Because of how these fields are structured, one single ghost is powerful enough to generate everything. When we write \(GF(5^4)\), the exponent \(4\) tells us the degree of the “super-ghost” (\(\alpha\)) we are inventing. Even though we may want to find two different square roots (\(\sqrt{2}\) and \(\sqrt{3}\)) in \(5\)-adics, we don’t define them separately. Instead, we find a single irreducible polynomial of degree \(4,\) like: \(x^4 + 4x^2 + 2 = 0.\) The root of this polynomial, let’s call it \(\alpha\), is our only ghost.

It turns out that inside the world created by \(\alpha\), both \(\sqrt{2}\) and \(\sqrt{3}\) already exist as polynomials of \(\alpha,\) like if we only need \(i\), instead of separate ghost for \(\sqrt{-2}\), because \(\sqrt{-2}\) is just \(i\sqrt{2}\). See the explanation here 🐓

Instead of just real and alpha, you now have alpha-squared:

\[\text{Number} = c_0 + c_1\alpha + c_2\alpha^2\]

There are \(5\) choices in \(c_0,\) \(5\) choices in \(c_1,\) and \(5\) choices in \(c_2.\)


The big idea:

We start with our “blurry” torsion point coordinate \(x_q\) from the extension field \(\mathbb{F}_{p^k}\).

The input looks like \(x_q = c_0 + c_1\alpha + \dots + c_{k-1}\alpha^{k-1}\).

This \(x_q\) must be a root of the division polynomial \(\psi_\ell(x)\).

In a finite extension like \(GF(5^2)\) - Galois field of order five squared - we define a new number \(\alpha\) which is a root of an equation like \(x^2 + 2 = 0\). This \(\alpha\) is our “ghost” because it doesn’t exist in the base field \(\mathbb{F}_5\). A ghost point \(P\) on a curve might look like this:

\[P = (2\alpha + 1, \quad 3\alpha + 4)\]

The coefficients \((2, 1)\) and \((3, 4)\) are just integers from \(0\) to \(4\). If you tried to plot this on a standard grid of \(\mathbb{F}_5\), it wouldn’t be there. It only exists in the “horizontal” extension.

The vertical lift (The \(p\)-adic lift): Now, we lift this point into the \(p\)-adic skyscraper. We want to find a point \((x, y)\) that satisfies the curve equation not just modulo \(5\), but modulo \(5^2, 5^3, \dots, 5^n\). As we lift, the identity of the ghost (\(\alpha\)) stays the same, but its coefficients must change to stay on the curve. This is the “skyscraper” growing taller. Instead of two simple coordinates, the point becomes two power series in \(5\). The ghost \(\alpha\) (which we’ll call \(\beta\) in the \(p\)-adic world) appears at every level:

The \(x\)-coordinate lift:

\[x = \underbrace{(2\beta + 1)}_{\text{Ground Floor}} + \underbrace{(1\beta + 3)}_{\text{1st Floor}} \cdot 5 + \underbrace{(4\beta + 0)}_{\text{2nd Floor}} \cdot 25 + \dots + O(5^n)\]

The \(y\)-coordinate lift:

\[y = \underbrace{(3\beta + 4)}_{\text{Ground Floor}} + \underbrace{(0\beta + 2)}_{\text{1st Floor}} \cdot 5 + \underbrace{(2\beta + 1)}_{\text{2nd Floor}} \cdot 25 + \dots + O(5^n)\]

At the start, \(\psi_\ell(x_q) \equiv 0 \pmod p\). There is a small error that is a multiple of \(p\). To lift, we don’t just guess the next digit. We use the Taylor Expansion of the polynomial. The Taylor expansion for a function \(f(x)\) at a point \(a\) usually looks like:

\[f(a + \epsilon) = f(a) + \epsilon f'(a) + \frac{\epsilon^2}{2!} f''(a) + \dots + \frac{\epsilon^n}{n!} f^{(n)}(a)\]

In the \(p\)-adic lift, since we are only trying to solve the equation modulo \(p^2\) at this specific step, every term containing \(\epsilon^2, \epsilon^3, \dots\) becomes congruent to zero because \(\epsilon\) is a multiple of \(p\) (specifically \(\epsilon = d_1 p^1\)).

\[\psi_\ell(x_q + \epsilon) \equiv \psi_\ell(x_q) + \epsilon \psi'_\ell(x_q) \pmod{p^2}\]

Since \(\epsilon\) is a multiple of \(p\) (i.e., \(\epsilon \equiv 0 \pmod p\)), then:\(\epsilon^2\) is a multiple of \(p^2\), so \(\epsilon^2 \equiv 0 \pmod{p^2}\).\(\epsilon^3\) is a multiple of \(p^3\), which is also \(0 \pmod{p^2}\).

Hensel’s rule: To solve for that missing digit \(\epsilon\), we rearrange the equation.

At the start of the step, we have \(x_q\), which is a root at floor \(1.\) This means:

\[\psi_\ell(x_q) \equiv 0 \pmod{p}\]

But it is not zero modulo \(p^2\) yet. There is a leftover “remainder” (a nudge). To move up, we want to find an \(\epsilon\) such that:

\[\psi_\ell(x_q + \epsilon) \equiv 0 \pmod{p^2}\]

which implies that

\[\epsilon = -\frac{\psi_\ell(x_q)}{\psi'_\ell(x_q)}\]

The numerator tells how far off the equation is from zero. The denominator is the derivative of the division polynomial. It acts as the slope that tells you exactly how much to adjust your coordinate to get closer to the true root.

We are using that \(\epsilon\) to zero out the error at the next level of precision. When we solve \(\epsilon = -\frac{\psi_\ell(x_q)}{\psi'_\ell(x_q)}\), we are finding the exact “nudge” needed so that the polynomial evaluated at the new, slightly taller point \((x_q + \epsilon)\) becomes zero modulo \(p^2\) (and eventually \(p^3, p^4, \dots\)).

When we solve the equation, we find that:

\[\epsilon = d_1 p^1 + d_2 p^2 + d_3 p^3 + \dots\]

But because we are working \(\pmod{p^2}\), all the higher floors (\(p^2, p^3, \dots\)) are strictly zero. If we are working modulo \(p^k\), we have essentially declared that \(p^k\) is the new zero. If we have a term \(p^m\) where \(m \ge k\), you can always factor it: \(p^m = p^k \cdot p^{m-k}.\) Since \(p^k \equiv 0 \pmod{p^k}\), the entire term becomes: \(0 \cdot p^{m-k} = 0.\)

This leaves us with:

\[\epsilon \equiv d_1 p^1 \pmod{p^2}\]

How do we extract the digit \(d_1\)? When Sage code runs that formula, it gets a value for \(\epsilon\). To find the actual digit (\(d_1\)) to put in your expansion, we effectively divide by \(p\):

\[d_1 = \frac{\epsilon}{p} \pmod{p}\]

At this point we already have \(x_1 = d_0 + d_1 p\), which is our root modulo \(p^2\). To find \(d_2\), we define our new point as:

\[x_2 = x_1 + \epsilon \quad \text{where } \epsilon = d_2 p^2\]

The Taylor expansion for \(d_2:\) We expand \(\psi_\ell(x_1 + \epsilon)\) around the current point \(x_1\):

\[\psi_\ell(x_1 + \epsilon) = \psi_\ell(x_1) + \epsilon \psi'_\ell(x_1) + \frac{\epsilon^2}{2!} \psi''_\ell(x_1) + \dots\]

Now, we look at this \(\pmod{p^3}\):

The first term \(\psi_\ell(x_1)\): Since \(x_1\) is already a root modulo \(p^2\), this value is strictly a multiple of \(p^2\).

The second term \(\epsilon \psi'_\ell(x_1)\): Since \(\epsilon\) contains \(p^2\), this term is also a multiple of \(p^2\). This is our active correction term.

The third term and beyond: Since \(\epsilon = d_2 p^2\), then \(\epsilon^2\) is a multiple of \(p^4\). Because \(p^4 \equiv 0 \pmod{p^3}\), the \(\epsilon^2\) term (and every term after it) becomes strictly zero. The resulting equation just like before, show that the higher-order curvatures of the polynomial vanish because the \(p\)-adic resolution at \(p^3\) is too coarse to see them. The equation simplifies to:

\[0 \equiv \psi_\ell(x_1) + \epsilon \psi'_\ell(x_1) \pmod{p^3}\]

To isolate \(d_2\), we substitute \(\epsilon = d_2 p^2\):

\[d_2 p^2 \equiv -\frac{\psi_\ell(x_1)}{\psi'_\ell(x_1)} \pmod{p^3}\]


Below (🪁) there is a sketch of what the mechanics of this calculation would entail, but here is the print out of an example in SageMath:

from sage.all import *

# --- CONFIGURATION ---
p = 5       # The Prime (Base Field)
ell = 3     # The Torsion Prime (The type of Ghost we are hunting)
prec = 4    # The Vertical Precision (How many floors to build)

# 1. THE FOUNDATION (Horizontal Extension)
# We use degree 8 because we know the 3-torsion for this curve lives there.
K8.<a> = GF(p^8)
E = EllipticCurve(GF(p), [1, 2])
f = E.division_polynomial(ell)

# This prints the modulus of the field K8
print(f"Defining Polynomial (Modulus): {K8.modulus()}")
# To create $GF(5^8)$, SageMath needs to find an irreducible polynomial of degree 8 over $\mathbb{F}_5$.
# The Analogy: For $i$ to exist, you must first declare the rule $x^2 + 1 = 0$. 
# That is the "Defining Polynomial" for the complex numbers.

# This prints the minimal polynomial of 'a'
print(f"Minimal Polynomial of a: {a.minpoly()}")
# What is the smallest-degree equation that this specific element $a$ solves?
# If your modulus is $x^5 + 4x + 3$, then every time the math results in $a^5$, Sage uses this polynomial to replace it with $-4a - 3$. 
# This keeps the extension from growing into $a^6, a^7,$ etc., and keeps it at a fixed degree.

# This prints the characteristic polynomial of 'a'
print(f"Characteristic Polynomial of a: {a.charpoly()}")

# Find all unique x-coordinates for the ell-torsion
all_x_ghosts = [r[0] for r in f.roots(K8)]

print(f"--- HORIZONTAL PHASE ---")
print(f"Found {len(all_x_ghosts)} 'Ghost' x-coordinates in GF({p}^8)\n")

# 2. THE BRIDGE & 3. THE REVEAL (Vertical Expansion)
# Define the p-adic extension ring (The Skyscraper Parent)
Zq.<b> = Qq(p^8, prec=prec, modulus=K8.modulus(), print_mode='series')

print(f"--- VERTICAL PHASE (Lifting to p-adic precision {prec}) ---")

for i, x_ghost in enumerate(all_x_ghosts):
    # THE BRIDGE: 
    # Convert 'a' coefficients to 'b' and force the precision 'depth'
    x_lifted = Zq(x_ghost.list(), absprec=prec)
    
    # THE REVEAL: 
    # Use the curve equation y^2 = x^3 + x + 2 to find the p-adic y
    y_sq = x_lifted^3 + x_lifted + 2
    y_skyscraper = y_sq.sqrt()
    
    # We choose the positive root for display; sign depends on the specific point
    print(f"SKYSCRAPER {i+1}:")
    print(f"p-adic x: {x_lifted}")
    print(f"p-adic y: {y_skyscraper}")
    print("-" * 50)
    
---

OUTPUT:

Defining Polynomial (Modulus): x^8 + x^4 + 3*x^2 + 4*x + 2
Minimal Polynomial of a: x^8 + x^4 + 3*x^2 + 4*x + 2
Characteristic Polynomial of a: x^8 + x^4 + 3*x^2 + 4*x + 2
--- HORIZONTAL PHASE ---
Found 4 'Ghost' x-coordinates in GF(5^8)

--- VERTICAL PHASE (Lifting to p-adic precision 4) ---
SKYSCRAPER 1:
p-adic x: (3*b^6 + 4*b^5 + 3*b^4 + 2*b^3 + 3*b^2 + 4*b + 2) + O(5)
p-adic y: (4*b^7 + 3*b^5 + 4*b^4 + 2*b^2 + 1) + O(5)
--------------------------------------------------
SKYSCRAPER 2:
p-adic x: (4*b^7 + 4*b^6 + 4*b^5 + 3*b^2 + 4*b + 3) + O(5)
p-adic y: (3*b^7 + 4*b^6 + 2*b^5 + b^3 + 3*b^2 + 2) + O(5)
--------------------------------------------------
SKYSCRAPER 3:
p-adic x: (2*b^7 + 4*b^6 + 3*b^5 + 4*b^4 + 4*b^3 + 3*b^2 + 3*b + 3) + O(5)
p-adic y: (3*b^7 + 4*b^6 + 3*b^5 + b^3 + 2*b^2 + 4*b + 2) + O(5)
--------------------------------------------------
SKYSCRAPER 4:
p-adic x: (4*b^7 + 4*b^6 + 4*b^5 + 3*b^4 + 4*b^3 + b^2 + 4*b + 2) + O(5)
p-adic y: (3*b^7 + 2*b^6 + 2*b^5 + 4*b^4 + 3*b^3 + 2) + O(5)
--------------------------------------------------

Although not critical to the rest of the explanation, to count these fixed points mathematically, we define a new map: \((\phi_p - id)\).

\(id\) is the identity map (it does nothing: \(id(P) = P\)).

\((\phi_p - id)\) takes a point \(P\) and calculates the difference between its Frobenius transform and itself.

If \(P\) is a fixed point, then \(\phi_p(P) - id(P) = \mathcal{O}\) (the zero point).

In the world of algebraic geometry, the number of points that a map sends to zero is called the degree of that map. Therefore:

\[N_p = \deg(\phi_p - id)\]


Degrees are hard to calculate directly. We want to turn this geometry into simple matrix algebra. In arithmetic geometry, we often use \(p\) to refer to the prime we are “counting modulo” (the field \(\mathbb{F}_p\)). To avoid confusion, we pick a different prime to help us probe the curve’s internal structure. We call this second prime \(\ell\). \(\ell\) can be any prime number as long as it isn’t \(p\). If \(p=5\), we might pick \(\ell=2\) or \(\ell=3\). \(\ell\) is like a measurement tool we use to see how the curve is shaped.

An elliptic curve is a group. If you add a point \(P\) to itself \(\ell\) times, we write that as \(\ell \cdot P\). A point \(P\) is called an \(\ell\)-torsion point if adding it to itself \(\ell\) times brings you back to the zero of the curve (the point at infinity, denoted \(\mathcal{O}\)):

\[\underbrace{P + P + \dots + P}_{\ell \text{ times}} = \mathcal{O}\]

We look at the \(\ell\)-torsion points of the curve. These are points \(P\) where \(\ell \cdot P = \mathcal{O}\).

The size of \(\ell\) relative to \(p\) does not matter at all. \(\ell\) can be much smaller (like \(2\)) or much larger (like \(1,000,009\)). The math works exactly the same in both cases. When we say a point is “\(\ell\)-torsional,” we are saying it is a solution to a specific algebraic equation.

On an elliptic curve, adding a point to itself \(\ell\) times is a process that can be written as a massive polynomial. Finding \(\ell\)-torsion points is essentially just finding the roots of a polynomial, called division polynomial, denoted as \(\psi_n(x, y)\). These are the “massive polynomials” that define the \(n\)-torsion points of an elliptic curve.

For an elliptic curve \(E: y^2 = x^3 + Ax + B\), a point \(P = (x, y)\) satisfies \(n \cdot P = \mathcal{O}\) if and only if \(\psi_n(x, y) = 0\).

Every time we draw a line and find an intersection, we are essentially solving a system of equations formed by the equation of the line \(y = mx + c,\) and the equation of the curve \(y^2 = x^3 + Ax + B\). When we substitute the line into the curve, we get a cubic equation in \(x\):

\[(mx + c)^2 = x^3 + Ax + B\]

The roots of that cubic are the \(x\)-coordinates of the three intersection points.

When we add a point to itself, the slope \(m\) is a function of the coordinates: \(m = \frac{3x^2 + A}{2y}\). The equation \(m = \frac{3x^2 + A}{2y}\) is the formula for the tangent slope at a point \(P = (x, y)\) on the curve \(y^2 = x^3 + Ax + B\).

Using implicit differentiation (taking the derivative of both sides with respect to \(x\)): Since \(y\) is a function of \(x\), we use the chain rule to get \(2y \frac{dy}{dx}\). On the right side (\(x^3 + Ax + B\)): \(3x^2 + A\). Equate them: \(2y \frac{dy}{dx} = 3x^2 + A\) and solve for the slope (\(m = \frac{dy}{dx}\)): \(m = \frac{3x^2 + A}{2y}.\)

Because the group law for an elliptic curve is defined by rational functions, the result of \([n]P\) is always going to be some massive expression:

\[[n]P = \left( \frac{\text{Poly}_1(x,y)}{\text{Poly}_2(x,y)}, \frac{\text{Poly}_3(x,y)}{\text{Poly}_4(x,y)} \right)\]

\([n]P\) is shorthand for scalar multiplication. It means adding the point \(P\) to itself \(n\) times:

\[[n]P = \underbrace{P + P + \dots + P}_{n \text{ times}}\]

The division polynomial \(\psi_n\) is essentially the denominator of these expressions.

To explicitly determine the \(x\)-coordinates of these \(n\)-torsion points, we introduce the division polynomials. These polynomials provide a recursive algebraic framework for point multiplication, where the zeros of \(\psi_n\) correspond precisely to the \(x\)-coordinates of the points in \(E[n] \setminus \{ \mathcal{O} \}\). Their roots tell us exactly where the \(n\)-torsion points live on the \(x\)-axis.

The division polynomial is fixed for a given curve because it represents the intrinsic geometry of that curve. The lines change based on the point. The polynomial changes based on the curve (the coefficients \(A\) and \(B\)). For the elliptic curve \(y^2=x^3 +A + B\), when you add a point to itself, the slope \(m\) is a function of the coordinates: \(m = \frac{3x^2 + A}{2y}\), and the intersection with the equation for the elliptic curve, for instance, \(y^2 = x^3 + x\), determines the division polynomial, in this case \(\psi_3 = 3x^4 + 6x^2 - 1\). \(\psi_3(x)\) is the master equation for all points that satisfy \([3]P = \mathcal{O}\). On a donut (the complex torus), the 3rd torsion points form a \(3 \times 3\) grid, giving \(9\) points total. There is a general formula for \(\psi_3\) for any curve \(y^2 = x^3 + Ax + B\):

\[\psi_3(x) = 3x^4 + 6Ax^2 + 12Bx - A^2\]

If we plug in our curve’s coefficients (\(A=1\) and \(B=0\)): \(3x^4\) stays \(3x^4\). \(6(1)x^2\) becomes \(6x^2\). \(12(0)x\) disappears. \(-(1)^2\) becomes \(-1\). This gives us exactly the \(3x^4 + 6x^2 - 1.\)

A polynomial of degree \(n\) always has exactly \(n\) roots, provided we are willing to use complex enough numbers. For an elliptic curve, the torsion polynomial for \(\ell\) always has roots. When we solve it, we always find exactly \(\ell^2\) points. Even if our starting field \(\mathbb{F}_p\) is tiny (say \(p=2\)) and the curve has only one point there, the mathematical “ghost” of the \(2 \times 2\) grid is still there. If we expand our field to \(\mathbb{F}_{2^2}, \mathbb{F}_{2^3}, \dots\) eventually all \(\ell^2\) points will show up.

In the field \(\mathbb{F}_p\), we only have the integers \(\{0, 1, \dots, p-1\}\). If we have a polynomial — like the one that defines the \(\ell\)-torsion points — and it has no roots in \(\mathbb{F}_p\), we do exactly what we do in high school algebra: we invent them: \(\mathbb{F}_p \to \mathbb{F}_{p^k}\) (where \(k\) is the degree of the extension). We call \(\mathbb{F}_{p^k}\) an extension field. It contains \(\mathbb{F}_p\), but it also contains all the “complex” coordinates needed to describe the \(\ell\)-torsion points that were missing from the original field. If you keep adding roots for every possible polynomial, you eventually reach the algebraic closure, denoted \(\overline{\mathbb{F}}_p\). This is the ultimate “super field.” It is infinite in size. Every polynomial has all its roots here. This is the place where the full \(2 \times 2\) grid of \(\ell^2\) torsion points is guaranteed to exist.

Recapping, we want to spot the \(\ell\)-torsion points (the \(x, y\) coordinates that satisfy \([ \ell ]P = \mathcal{O}\)). We start on the “ground floor” at \(\mathbb{F}_p\). Usually, we will only find a few \(\ell\)-torsion points here — sometimes only the point at infinity. The rest are “ghosts” that are mathematically required to exist but don’t have coordinates in our current number system. To find them, we expand outward to \(\mathbb{F}_{p^k}\). As we increase \(k\) new points appear. We keep increasing \(k\) until we find the full \(\ell^2\) grid (for \(\ell=3\), that’s the \(9\)-point grid). This is a field expansion. It’s about “horizontal” discovery. Once we have spotted these points in \(\mathbb{F}_{p^k}\), we only have their “blurry” versions (just the first digit, \(d_0\)). To calculate a precise L-function, we need to know exactly how the Frobenius map shuffles them. We expand upward into the \(p\)-adic tower \(\mathbb{Z}_q\) (where \(q=p^k\)). As we increase the power of \(p\) (e.g., \(p^{20}\)) points stay the same; we aren’t finding new points anymore; we’ve already found the grid. We are calculating \(d_1, d_2, d_3 \dots d_{19}\). We are getting more and more “decimal places” (to the left) for the points we already caught. This is a ring expansion. It’s about “vertical” precision. Now that we have a “high-definition” grid, we apply the Frobenius map: \((x, y) \to (x^p, y^p)\). Because our points are high-precision, we can see exactly which point in the grid moved to which other point. We turn this shuffle into a \(2 \times 2\) matrix of integers. The trace (\(a_p\)) of this matrix is the final “integer signature” of the curve at that prime.

Once we find the points on the curve in the horizontal extension of the field, the Frobenius will permute them, but none of them will be sent to a point not on the curve. See why here 🙊.

The coefficients for powers of \(p\) (like \(a_{p^2}, a_{p^3}\)) are not new independent data. They are forced by a recurrence relation based entirely on \(a_p\) and \(p\):\(a_{p^2} = a_p^2 - p\)\(a_{p^3} = a_p \cdot a_{p^2} - p \cdot a_p\).

Notice that \(\mathbb{F}_{p^k}\) is a finite field. The first term of a \(p\)-adic series \(d_0\) (the coefficient of \(p^0\)) is actually a polynomial in the “ghost” \(\alpha\):

\[d_0 = c_0 + c_1\alpha + c_2\alpha^2 + \dots + c_{k-1}\alpha^{k-1}\]

Each of those \(c_i\) is a digit from \(\{0, 1, \dots, p-1\}\). The field contains \(p^k\) unique elements, each of which serves as a potential “ground floor” (\(d_0\)) for a \(p\)-adic expansion.

You can add, subtract, multiply, and (crucially) divide. Every non-zero element has an inverse. This is where we “spot” the \(\ell^2\) points. However, every coordinate is effectively just a single complex digit modulo \(p\). The purpose is to find the “ghost” points that don’t exist in the base field. Yet, it is low resolution.

That’s why we “lift” our coordinates from the field into the \(p\)-adic ring \(\mathbb{Z}_q\): it is the ring of integers in a \(p\)-adic field. We keep adding digits to the left (\(\dots d_2 d_1 d_0\)) until the coordinate is precise enough that our matrix math doesn’t have any rounding errors. It is not a field because for \(p=5,\) we can divide by \(3\), but we cannot divide by \(5\). If we try to calculate \(1/5\), the string would have to shift to the right of the decimal point (the radix), which makes it no longer a p-adic integer. In the \(p\)-adic ring \(\mathbb{Z}_q\), we can divide by many things, but never by \(p\). We can divide by any number that doesn’t have \(p\) as a factor. For example, \(1/2\) exists in the \(5\)-adics because \(2\) is “coprime” to \(5\). It just looks like an infinite string of digits. But we cannot calculate \(1/5\) inside \(\mathbb{Z}_5\). If we try to write a string starting with \(d_0 + d_1 \cdot 5 \dots\) that equals \(1/5\), it’s impossible. However, in the field \(\mathbb{Q}_q\) we include the \(p\)-adics with negative powers of \(p\).

It’s perhaps superficially obvious that in \(\mathbb F_5\) we have only \(\{0,1,2,3,4\},\) and it may seem as though this doesn’t change in \(\mathbb Z_5\) since the coefficients in the expansion \(d_0+ d_15+ d_25^2+\cdots,\) are also in \(\{0,1,2,3,4\}.\) However, when this vector space view has to undergo the necessary transformation to allow multiplication, the \(5^k\) basis vectors become elements of the algebraic structure: a ring.

The points in \(E(\mathbb{F}_p)\) are the “frozen” points under Frobenius. Their \(p\)-adic expansion is just a single digit at the \(p^0\) position followed by infinite zeros to the left (\(\dots00000d.\)). Frobenius cannot move them. But there are torsion points that can only be seen in the extension \(\mathbb F_{p^k}\): These are the “movers.” Their \(p\)-adic expansions are infinite, non-zero strings to the left. Frobenius shuffles them around the grid. We use the moving points to calculate the “shuffle” (the Frobenius matrix). The Trace (\(a_p\)) of that matrix then tells us the exact count of the frozen points (\(N_p = p + 1 - a_p\)).

Therefore, to determine \(a_p\) (and \(N_p\)), the trick is to use the \(\ell\)-torsion grid to measure the Frobenius map (find below a more detailed explanation (⛷️)). The total set of residents with coordinates in the base field \(\mathbb{F}_p\) is called \(E(\mathbb{F}_p)\), and it forms a finite abelian group. Since every element of a finite group has finite order, adding any resident to itself a certain number of times will hit \(\mathcal{O}\), which means that every resident is, by definition, a torsion point of some order.

When we ask for \(N_p\) (the number of points on the curve over \(\mathbb{F}_p\)), we are effectively asking: “Which of the torsion points have coordinates that are just simple integers \(\dots00000d.\) - in other words, \(\dots\underset{\text{all 0's but one...}}{00000}\)🪼\(\bf{.}\) NOTHING R 👉 of the dot” Since we can’t always see the points directly, we use the Frobenius map as a filter. The Frobenius map shuffles any point that has “ghostly” coordinates (non-zero digits to the left). The Frobenius map leaves alone any point whose coordinates are in \(\mathbb{F}_p\).

This is ultimately why we use \(p\)-adics. If we only work modulo \(p\) (in \(\mathbb{F}_p\)), we only see the world in “low resolution.” We might find that \(a_p \equiv 2 \pmod 5\), but we wouldn’t know if \(a_p\) is \(2, 7, -3,\) or \(12\). By lifting the coordinates to \(\mathbb{Z}_p\), we are essentially adding “decimal places” (to the left). This allows us to calculate the Frobenius matrix with zero error, and ensure that when we take the trace, we get the exact integer \(a_p\), not just a remainder.

\(\mathbb{F}_p\) is a coarse filter; it only sees points with simple coordinates. \(\mathbb{F}_{p^k}\) is a finer filter; it lets us see “complex” points that were previously invisible. We can think of the complex extension as looking at the fundamental domain (the square that makes the donut - the lattice) and realizing it is actually tiled by smaller sub-tiles. By going to a larger field, we aren’t changing the donut; we are just increasing the “pixel density” so we can finally see where the \(\ell\)-torsion points are sitting.

If the \(x\)-coordinates of our torsion points don’t fit in \(\mathbb{F}_p\), we must expand the field. We go to larger powers of \(p\) (the extension field \(\mathbb{F}_{p^k}\)) to find the ghost points, and higher \(p\)-adic precision (\(\mathbb{Z}_p \pmod{p^{20}}\)) to calculate \(a_p\).

There are two main algorithms to find \(a_p.\) The one sketched above (horizontal \(\mathbb{F}_{p^k}\) extended field with vertical lift \(\mathbb Z_p\) to a certain p-adic precision is called SEA Algorithm (Schoof-Elkies-Atkin). Now, the Schoof algorithm looks at many different small primes \(\ell\). It stays “flat” on the ground floor of \(\mathbb{F}_p\), but it visits many different \(\ell\)-torsion “neighborhoods.” It relies on the Chinese remainder theorem, and \(\mathbb Z_\ell.\) Schoof only looks at the very bottom of the Tate module (the \(E[\ell]\) level). It sees a \(2 \times 2\) matrix with entries in \(\mathbb{F}_\ell\). By finding the Trace of this tiny matrix, it finds \(a_p \pmod \ell\). He then “glues” these together using the Chinese Remainder Theorem.t is explained below 🆘. This is important to understand what follows.

The SEA algorithm uses the Tate module to find “special” directions (eigenvectors). It uses the \(p\)-adic skyscraper to lift these directions to high precision. Because it finds the “slope” of the Frobenius map so accurately, it can calculate the trace (\(a_p\)) much faster than Schoof could by visiting dozens of primes. Crucially, while the standard Schoof algorithm requires tracking the entire \(2 \times 2\) grid (a basis of two points), the SEA algorithm identifies an “Elkies prime” where the Frobenius map has a specific invariant line. Because this line is one-dimensional, we only need to find and lift one single point to determine the eigenvalue \(\lambda\). Once we have the “speed” of the Frobenius map along this one rail, the symmetry of the curve (determinant = \(p\)) allows us to mathematically deduce the trace without ever constructing a full \(2 \times 2\) matrix.

To understand why we can ‘zoom in’ on these points in the SEA algorithm using Hensel’s lifting, we must look at their geometric nature. While we calculate them using \(p\)-adic digits (\(x, y\)), their internal structure is defined by their position on a lattice. This lattice can be subdivided into smaller and smaller \(\ell\)-power pieces, and the Tate Module is the mathematical object that tracks a point’s ‘address’ through all those infinite subdivisions.

When we find a root of the division polynomial \(\psi_3(x)\) in \(\mathbb{Q}_{11}\), we are forcing these two worlds to meet. The lattice tells the polynomial. “Give me the \(x\)-coordinates for the points at address \(1/3\).” The \(p\)-adic Field provides the answer: “\(x = 5 + 4 \cdot 11 + \dots\).” If the address and the \((x, y)\) coordinate didn’t coincide, the SEA algorithm would be impossible.

The “grid coordinates” are conceptual addresses. The division polynomial \(\psi_3(x)\) acts as a filter. Its roots are, by definition, the \(x\)-coordinates of the points that have the address on the lattice. We don’t need to know if a point is \((1/3, 0)\) or \((1/3, 1/3)\). We just know it is a \(3\)-torsion point because it’s a root of \(\psi_3\). The only “numbers” that ever appear in our computer’s memory are the coefficients of the polynomials and the \(p\)-adic digits (like \(5, 4, 2, \dots\)).

We can think of the lattice \(\Lambda\) in the definition of an elliptic curve as \(\mathbb C/\Lambda\) as a grid of stakes driven into an infinite flat field. Every stake represents the zero point (\(\mathcal{O}\)) because when we wrap the field into a donut, all those stakes land on the exact same spot. The original lattice \(\Lambda\) contains points like \((1, 0)\) or \((0, 1)\) (in terms of the basis vectors \(\omega_1, \omega_2\)). The \(3 \times 3\) grid contains points like \((\frac{1}{3}, 0)\), \((\frac{2}{3}, 0)\), \((\frac{1}{3}, \frac{1}{3})\), and so on. This is just a way of explaining it: Instead of a fraction, a point in the Tate module is an \(\ell\)-adic vector \(v = \begin{pmatrix} x \\ y \end{pmatrix}\), where \(x\) and \(y\) are \(\ell\)-adic integers (\(\mathbb{Z}_\ell\)). These \(x\) and \(y\) are essentially the infinite strings of digits that tell us exactly where the point sits in the “infinite subdivision.”

The Tate module \(T_\ell(E)\) is the inverse limit of the torsion groups:

\[T_\ell(E) = \varprojlim E[\ell^n]\]

In the torsion group \(E[\ell^n]\), the elements are the points \((x, y)\) that satisfy the algebraic condition \([\ell^n]P = \mathcal{O}\). Geometrically, if we imagine the torus (donut) as a square field with the sides glued together, the elements are the points on a perfectly spaced grid. Algebraically, each element can be identified by its “grid coordinate.” Point addition is the group operation. In the torsion group \(E[\ell^n]\), the “multiplication” by \(\ell^n\) is simply adding a point to itself \(\ell^n\) times.

\(E[\ell^1]\): The \(3 \times 3\) grid (for \(\ell=3\)). It contains \(9\) elements:\[(0,0), (1,0), (2,0), (0,1), (1,1), (2,1), (0,2), (1,2), (2,2)\](Note: These are indices, not the \(p\)-adic coordinates of the points.)

\(E[\ell^2]\): The \(9 \times 9\) grid.

Each point in the \(3 \times 3\) grid “splits” into \(9\) smaller points.

\(E[\ell^\infty]\): The Tate module.

It captures the coordinates of a point as it moves through this infinite subdivision. It is the limit of all the \(\ell\)-power grids: a high-precision coordinate system for the curves’s internal symmetry. The Tate module is an infinitely dense grid (fractal) which by definition includes all the coordinates where the torsion points can live: it assigns GPS coordinates to any point on the curve, turning geometry into linear algebra: The Frobenius map acts on the entire structure at once.

A vector space is a special kind of module with scalars belonging to a field, whereas in a module the scalars belong to a ring, which in this case is \(\mathbb Z_l\), the ring of \(\ell\)-adic integers (infinite expansions to the left).

We use \(p\)-adic - not \(\ell\)-adic - numbers to calculate the \(\ell\)-adic shuffle (torsion). There are no strict laws that say \(p\) must be the one we count (field prime) and \(\ell\) must be the grid - pattern prime. If we chose to look at the curve over \(\mathbb{F}_3\) and study its \(5\)-torsion, then \(p=3\) and \(\ell=5\). But we can as well look at the curve over \(\mathbb{F}_5\) and study its \(3\)-torsion, so \(p=5\) and \(\ell=3\). The only rule is \(\ell \neq p\) because if they were the same, the “grid” would collapse (this is called the “supersingular” or “anomalous” case, where the geometry gets very messy).

The Tate module gives every torsion point a permanent “seat number” in the grid. For a \(3\)-torsion point, the seat number is an \(\ell\)-adic vector like \(v = \begin{pmatrix} 1 \\ 0 \end{pmatrix}\). This “\(1\)” is an \(\ell\)-adic integer (\(\dots00001.\) in base \(3\)). It tells the math: “This is the first basis point of the grid. The actual values of the \(x\) and \(y\) coordinates for that specific seat are \(p\)-adic integers. The coordinates are strings like \(x = \dots42314.\) (in base \(5\)). The coincidence between these two systems happens through the elliptic curve equation. When you take the \(p\)-adic values from the”occupant” and plug them into the equation \(y^2 = x^3 + Ax + B\), the math confirms that this specific occupant belongs in the torsion grid labeled by \(\mathbb{Z}_\ell\). The Tate module is the structure that ensures that as you apply the Frobenius map, the “occupant” moves to a new “seat” in a way that is perfectly consistent with linear algebra. We use the \(p\)-adic values to track the movement, but you use the \(\ell\)-adic labels to write down the matrix.

The grid isn’t just a visual overlay; it is defined by a specific algebraic requirement. A point \(P = (x, y)\) is in the \(\ell\)-torsion grid if and only if: \([\ell]P = \mathcal{O}\). This means if you add the point to itself \(\ell\) times using the group law (the slope formulas discussed earlier), we must land on the point at infinity. It is the division polynomial that links the EC to the Tate module or grid structure.

The use of \(p\)-adic numbers works because we are considering a small set of numbers (roots of polynomials, i.e. algebraic numbers) that live in both worlds. The \(3\)-torsion point \(x\)-coordinate may be a root of \(3x^4 + 6x - 1 = 0\). This root can be “embedded” into \(\mathbb{C}\) as a decimal like \(0.165\dots\). The exact same root can be “embedded” into \(\mathbb{Z}_3\) as a 3-adic expansion \(\dots 2101\). The \(\ell\)-adic numbers are the “completion” of these roots in one direction, and \(\mathbb{C}\) is the “completion” in the other.

Going back to the analogy these fractional points are not in the original lattice \(\Lambda\). However, they have a very special relationship to it: if you multiply them by \(3,\) they land exactly on a stake in \(\Lambda\). We talk about “tiling the grid with larger copies,” but it is often more helpful to think of it as subdividing the original tile. Start with our Fundamental Domain (the parallelogram formed by \(\Lambda\)). To find the \(3\)-torsion, you divide that parallelogram into \(9\) smaller “mini-parallelograms.” The corners of these mini-parallelograms are our \(3\)-torsion points. These corners are “new” points in the complex plane, but in the group theory of the donut, they are the only points that know how to get back to the lattice stakes in exactly \(3\) steps. Now, when you move to \(9 \times 9\), we are subdividing those mini-parallelograms even further. The \(3\)-torsion points are at coordinates like \(\frac{1}{3}, \frac{2}{3}\). The \(9\)-torsion points are at coordinates like \(\frac{1}{9}, \frac{2}{9}, \frac{3}{9}, \dots\) Notice that \(\frac{3}{9}\) is the same as \(\frac{1}{3}\). This is why the \(3 \times 3\) grid is contained within the \(9 \times 9\) grid. The \(9 \times 9\) system has a higher density of points, and it “swallows” the \(3 \times 3\) grid. The Tate Module is the limit of this subdivision process. Imagine we subdivide the parallelogram by \(\ell, \ell^2, \ell^3 \dots\) forever. The points are getting closer and closer together in the complex plane. In the \(\ell\)-adic world, these points are forming a continuous space (a “free module”). The Frobenius map \(\phi_p\) acts on this infinite subdivision. Because it is a linear map, it doesn’t just move one point; it maps the entire “subdivided tiling” to another “subdivided tiling.”

For a prime \(\ell\) (where \(\ell \neq p\)), there are exactly \(\ell^2\) torsion points. They form a \(2 \times 2\) grid, much like a vector space.

In SageMath we can find the torsion points with complex numbers:

E = EllipticCurve([1, 0]) # y^2 = x^3 + x
ell = 3

# 1. Move to the Algebraic Closure to see the "ghost" points
K = QQbar
E_K = E.base_extend(K)

# 2. Get the Origin point (Point at Infinity)
Origin = E_K(0)

# 3. Find points P such that 3*P = Origin
# We call division_points on the Point object itself
points = Origin.division_points(ell)

# Print the 3rd division polynomial
print(f"The elliptic curve is: {E}")
print("-" * 30)
print(f"The {ell}-rd division polynomial (l=3) psi_{ell} is: {E.division_polynomial(ell)}")
print(f"Total {ell}-torsion points found: {len(points)}")
print("-" * 30)
print(f"TORSION POINTS (in algebraic coordinates):")
for i, P in enumerate(points):
    # Formatting to show the algebraic coordinates clearly
    print(f"Point {i}: ({P[0]} : {P[1]} : {P[2]})")
    
---
# OUTPUT:
---

The elliptic curve is: Elliptic Curve defined by y^2 = x^3 + x over Rational Field
------------------------------
The 3-rd division polynomial (l=3) psi_3 is: 3*x^4 + 6*x^2 - 1
------------------------------
TORSION POINTS (in algebraic coordinates):
Total 3-torsion points found: 9
Point 0: (0 : 1 : 0)
Point 1: (-0.3933198931903287? : -0.6739189064139262?*I : 1)
Point 2: (-0.3933198931903287? : 0.?e-15 + 0.673918906413926?*I : 1)
Point 3: (-1.467889825013871?*I : -0.9205903462520508? - 0.9205903462520508?*I : 1)
Point 4: (-1.467889825013871?*I : 0.9205903462520508? + 0.9205903462520508?*I : 1)
Point 5: (1.467889825013871?*I : -0.9205903462520508? + 0.9205903462520508?*I : 1)
Point 6: (1.467889825013871?*I : 0.9205903462520508? - 0.9205903462520508?*I : 1)
Point 7: (0.3933198931903287? : -0.6739189064139262? : 1)
Point 8: (0.3933198931903287? : 0.6739189064139262? : 1)

QQbar (\(\overline{\mathbb{Q}}\)) is the Field of algebraic aumbers. It contains every number that is a root of a polynomial with rational coefficients. This includes all integers, fractions, square roots (\(\sqrt{2}\)), and imaginary numbers (\(i\)).

But we can get p-adics:

E = EllipticCurve([1, 0]) 
ell = 3  # The order of torsion
p = 11   # The p-adic prime

# 1. The Division Polynomial for the ell-torsion
psi = E.division_polynomial(ell) 

# 2. The Algebraic View (The "True" Root)
# We find the root in the algebraic closure
root_alg = psi.roots(QQbar, multiplicities=False)[0] 

print(f"--- Algebraic Perspective ---")
print(f"Investigating {ell}-torsion for {E}")
print(f"Division polynomial psi_{ell}: {psi}")
print(f"A root x is: {root_alg}")

# 3. The p-adic View (The "Local" Address)
K_p = Qp(p, prec=5)
padic_roots = psi.roots(K_p, multiplicities=False)

print("-" * 30)
print(f"--- {p}-adic Perspective ---")
if padic_roots:
    root_padic = padic_roots[0]
    print(f"In Q_{p}, that same root looks like: {root_padic}")
    print(f"Expansion: {root_padic.expansion()}")
else:
    print(f"The {ell}-torsion x-coordinates are not in Q_{p} directly.")
---

OUTPUT:

--- Algebraic Perspective ---
Investigating 3-torsion for Elliptic Curve defined by y^2 = x^3 + x over Rational Field
Division polynomial psi_3: 3*x^4 + 6*x^2 - 1
A root x is: -0.3933198931903287?
------------------------------
--- 11-adic Perspective ---
In Q_11, that same root looks like: 5 + 4*11 + 2*11^2 + 7*11^3 + 7*11^4 + O(11^5)
Expansion: 11-adic expansion of 5 + 4*11 + 2*11^2 + 7*11^3 + 7*11^4 + O(11^5)

Interpretation: It shows exactly how a single geometric point on our donut (the elliptic curve) is translated into two different mathematical languages: the “analog” (complex) and the “digital” (\(p\)-adic).

The algebraic blueprint (QQbar): This is the “analog” view of the point. The Value \((-0.3933...)\): This is a decimal approximation of where the point sits on the complex plane. It’s hard to do exact arithmetic with it.

The \(11\)-adic digital address (Qp): We are looking at the exact same point, but expressed as a “digital string” in base \(11.\) The “\(5\)” (The first digit): This is the “coarse address.” If you looked at your donut through a very blurry lens (modulo \(11\)), the point would be sitting at \(x=5\).

The expansion (\(5 + 4 \cdot 11 + 2 \cdot 11^2 \dots\)): This is the “high-resolution Address.”The \(4 * 11\) tells us which “sub-tile” the point is in. The \(2 * 11^2\) zooms in further.

The \(O(11^5)\): This is our “zoom Level.” We have calculated the position of this torsion point down to \(5\) digits of \(11\)-adic precision.

The L-function doesn’t care about the decimal \(-0.3933.\) It cares about the symmetry. The address (\(11\)-adic string) changes depending on the prime. The Tate Module is the “registry” that tracks how these addresses (like \(5 + 4 \cdot 11 \dots\)) are shuffled by the Frobenius map. When we run the Frobenius shuffle, we are literally watching that string of digits (\(5, 4, 2, 7, 7\)) be transformed into the expansion of a different root of the same division polynomial (\(3x^4 + 6x^2 - 1 = 0\)). The trace (\(a_p\)) is the summary of that transformation.

The Frobenius map \(\pi_p\) acts on the coordinates of the point. If a point \(P\) has coordinates \((x, y)\), the Frobenius map sends it to \((x^p, y^p)\). If \(x\) and \(y\) are simple integers (or elements of \(\mathbb{F}_p\)), then \(x^p \equiv x \pmod{p}\). These points stay put. For a torsion point, the \(x\)-coordinate is usually a root of a polynomial that doesn’t factor easily. These roots are spread out across the field extension. Frobenius acts like a permutation: it picks up one root of the division polynomial and moves it to a different root of the same polynomial. For example, imagine the roots of \(\psi_3\) are \(\{r_1, r_2, r_3, r_4\}\). The Frobenius map looks at the \(11\)-adic string for \(r_1\); it does the “Frobenius math” (\(x^{11}, y^{11}\)), and the result is a new \(11\)-adic string that matches the expansion of \(r_2\). When we talk about the Frobenius map \(\pi_p\) acting on the \(n\)-torsion points of an elliptic curve, we are describing a specific Galois automorphism. In the \(p\)-adic setting, we call it the Frobenius element in the local Galois group \(Gal(\overline{\mathbb{Q}}_p/\mathbb{Q}_p)\).

The Frobenius map is an automorphism, which means it must preserve the entire algebraic structure of the field. If it moves the “coarse address” (\(11^0\)), it must move the “fine details” (\(11^1, 11^2, \dots\)) in a way that is perfectly consistent with the curve’s equation.


If we look at all the points that satisfy \(\ell \cdot P = \mathcal{O}\), there aren’t just \(\ell\) of them. There are \(\ell^2\) of them. Geometrically, an elliptic curve (over complex numbers) is shaped like a torus (a donut). To “cycle” around a donut and get back to where we started, we can go two ways: around the tube or through the hole. Because there are two independent directions to move, the torsion points form a \(2\)-dimensional structure. If \(\ell = 3\), we have \(3^2 = 9\) points. We can think of these points as being organized like a grid: \(3\) points along the tube direction, and \(3\) points along the hole direction.

If we take two points from the grid and add them together, we stay in the grid. Since it has two directions and behaves like a grid, it acts exactly like a \(2\)-dimensional vector space over the field \(\mathbb{F}_\ell\) (the integers modulo \(\ell\)). In a normal 2D vector space, any point can be written as \((x, y)\). Here, every \(\ell\)-torsion point can be identified by its “coordinates” on that grid.

The Frobenius map \(\phi_p\) is defined by:

\[\phi_p(x, y) = (x^p, y^p)\]

If a point \(P = (x, y)\) is an \(\ell\)-torsion point, it means that adding \(P\) to itself \(\ell\) times gives the zero point: \(\ell \cdot P = \mathcal{O}\). Now, because the Frobenius map is an endomorphism (a map from the curve to itself that respects addition), it has a very important property:

\[\phi_p(P + Q) = \phi_p(P) + \phi_p(Q)\]

This implies that

\[\phi_p(\ell \cdot P) = \ell \cdot \phi_p(P)\]

If \(P\) is an \(\ell\)-torsion point, then \(\ell \cdot P = \mathcal{O}\), and we know \(\phi_p(\mathcal{O}) = \mathcal{O}\). Therefore:

\[\ell \cdot \phi_p(P) = \mathcal{O}\]

Notice that if \(P\) is an \(\ell\)-torsion point, it is automatically an \(\ell^k\)-torsion point for any \(k \ge 1\).

The Frobenius map takes an \(\ell\)-torsion point and lands it precisely on another \(\ell\)-torsion point. It shuffles the points within that \(2 \times 2\) grid. If a point is in \(\mathbb{F}_p\): \(\phi_p(x,y) = (x^p, y^p) = (x, y)\), it lands on itself. If a point is “complex” (extension field): \(\phi_p(x,y) = (x^p, y^p) \neq (x, y),\) it lands on a “sibling” point in the grid.

However, counting points only tells us about the curve at one specific resolution (\(\mathbb{F}_p\)). But an elliptic curve is a “global” object. If you know the Frobenius Matrix \(M_p\), you don’t just know the count for \(\mathbb{F}_p\). You automatically know the count for every possible extension field (\(\mathbb{F}_{p^2}, \mathbb{F}_{p^3}, \mathbb{F}_{p^4} \dots\)). The number of points in \(\mathbb{F}_{p^k}\) is determined by the formula:

\[N_{p^k} = p^k + 1 - (\alpha^k + \beta^k)\]

where \(\alpha\) and \(\beta\) are the eigenvalues of your Frobenius matrix. Without the matrix, we’d have to start our counting from scratch every time you moved to a larger field. The goal of the L-function isn’t just to store a list of point counts. Its goal is to bridge the gap between arithmetic (counting) and analysis (calculus and modular forms).

Since there are \(\ell^2\) points in this grid, and they form a structure like a 2D vector space \((\mathbb{Z}/\ell\mathbb{Z})^2\), we can choose a basis. Pick two “primary” \(\ell\)-torsion points, \(E_1\) and \(E_2\), that are not multiples of each other. Every other torsion point \(P\) in the grid can be written as:

\[P = c_1 E_1 + c_2 E_2\]

where \(c_1\) and \(c_2\) are coefficients from \(0\) to \(\ell-1\). When we apply \(\phi_p\) to our basis points, they must land somewhere in the grid, so they can also be written in terms of that basis: \(\phi_p(E_1) = a E_1 + c E_2\) and \(\phi_p(E_2) = b E_1 + d E_2\)

This gives us our \(2 \times 2\) matrix over the field \(\mathbb{F}_\ell\):

\[M_{\phi_p, \ell} = \begin{pmatrix} a & b \\ c & d \end{pmatrix}\]

The trace of this matrix is simply \(a + d\). If you were to pick \(\ell = 2\) (the \(2 \times 2\) grid), the matrix would tell you how \(\phi_p\) swaps the three non-zero torsion points. When we say the \(\ell\)-torsion points form an \(\ell \times \ell\) grid, the total number of points is exactly \(\ell^2\). For \(\ell = 2\), the grid has \(2^2 = \mathbf{4}\) points. However, one of those points is always the identity element (the point at infinity, \(\mathcal{O}\)), which acts like the “zero” in a vector space. In any vector space, the zero vector is always fixed by any linear transformation. Because the Frobenius map \(\phi_p\) is a linear map (it respects the addition of points): \(\phi_p(\mathcal{O})\) is always \(\mathcal{O}\). The map cannot “move” the origin; it can only shuffle the remaining points. If you pick \(\ell = 3\) (the \(3 \times 3\) grid), the matrix would be larger in values, but the “sum of the diagonal” (\(a+d\)) would still be equivalent to the same \(a_p\) (modulo \(\ell\)).

The Tate module \(T_\ell(E)\) uses \(\ell\)-adic integers \(\mathbb{Z}_\ell\). If we only look at the \(\ell\)-torsion, we only know \(a_p \pmod \ell\). That isn’t enough information to know the exact value of \(a_p\). If \(\ell=2\) and \(a_p \equiv 1 \pmod 2\), \(a_p\) could be \(1, 3, 5, \dots\). To find the exact integer \(a_p\), we look at \(\ell^2\)-torsion, \(\ell^3\)-torsion, and so on. By looking at the action on the whole Tate module (the “tower” of all \(\ell^n\)-torsion), we are essentially looking at a matrix with \(\ell\)-adic entries. This matrix has a trace that is an \(\ell\)-adic integer. Since \(a_p\) is a fixed integer and the Tate module construction works for any \(\ell^n\), this \(\ell\)-adic trace converges to the actual integer \(a_p\) that we use in the L-function.

In the following SageMath code, we attempt to get the polynomial sitting in the denominator of the L-function

\[L(E, s) = \prod_{p} \frac{1}{1 - a_p p^{-s} + p^{1-2s}}\]

for a single prime \(p=3\). By constructing L_poly = 1 - ap*T + p*T^2, we are manually building the polynomial that sits in the denominator for the prime \(p\). To this end we need to extract the two parameters for that local polynomial: The prime (\(p=3\)), which defines the “degree” or weight of the quadratic term, and the trace (\(a_p\)), which defines the linear term and carries the specific data about the curve’s point-count at that prime.

The local L-factor is the “slice” of the L-function at a specific prime \(p\). The global L-function \(L(E, s)\) is built by multiplying an infinite number of these local pieces together (an Euler Product). For a prime of good reduction, the L-factor is the denominator of the Euler factor:

\[L_p(T) = 1 - a_p T + p T^2\]

We cannot see the trace \(a_p\) directly by looking at the curve’s equation. To measure \(a_p\), we have to see how the curve’s points behave. The \(\ell\)-torsion points act as a representative sample of the whole curve. The Tate module is built entirely out of these torsion points. For a prime \(\ell\), the \(\ell\)-adic Tate module \(T_\ell(E)\) is the “limit” of all the \(\ell^n\)-torsion points as you zoom in deeper and deeper. Mathematically, it is defined as the inverse limit:\[T_\ell(E) = \varprojlim_{n} E[\ell^n]\] Essentially, you are stacking the torsion groups:

At level 1, we have \(E[\ell] \cong (\mathbb{Z}/\ell\mathbb{Z})^2\).

At level 2, we have \(E[\ell^2] \cong (\mathbb{Z}/\ell^2\mathbb{Z})^2\)

…and so on.

The result is a 2-dimensional vector space over the \(\ell\)-adic integers \(\mathbb{Z}_\ell\). It’s 2-dimensional because of the lattice structure of these different levels of zooming in: At level \(1,\) the grid is \(\ell \times \ell\). We need \(2\) generators (\(P_1, P_2\)). At level \(2,\) the grid is \(\ell^2 \times \ell^2\). We still only need 2 generators (\(Q_1, Q_2\)), they just have higher “resolution.”

When the code prints the torsion points, it is showing you the raw data that the Frobenius map is about to shuffle. The Trace is just a summary of that shuffle.

E = EllipticCurve([1, 0]) # y^2 = x^3 + x
p = 3
ell = 3

# 1. Get the pieces for the L-factor
ap = E.ap(p) # Trace of Frobenius at p
print(f"Trace of Frobenius (a_p) at p={p}: {ap}")

# 2. Manually construct the Local L-factor polynomial
# Using T as the variable to match standard L-function notation
R.<T> = PolynomialRing(ZZ)
L_poly = 1 - ap*T + p*T^2

print(f"Local L-factor (Denominator): {L_poly}")

print(f"\n--- Torsion & p-adic Resolution ---")
psi = E.division_polynomial(ell)
print(f"{ell}-Division Polynomial: {psi}")

# Using p=11 for the p-adic view
p_view = 11
K_p = Qp(p_view, prec=5)
padic_roots = psi.roots(K_p, multiplicities=False)

if padic_roots:
    root = padic_roots[0]
    print(f"x-coord in {p_view}-adics: {root}")
    print(f"Expansion: {root.expansion()}")
---

OUTPUT: 
Trace of Frobenius (a_p) at p=3: 0
Local L-factor (Denominator): 3*T^2 + 1

--- Torsion & p-adic Resolution ---
3-Division Polynomial: 3*x^4 + 6*x^2 - 1
x-coord in 11-adics: 5 + 4*11 + 2*11^2 + 7*11^3 + 7*11^4 + O(11^5)
Expansion: 11-adic expansion of 5 + 4*11 + 2*11^2 + 7*11^3 + 7*11^4 + O(11^5)

Interpretation: When the Frobenius map \(\phi_3\) (the map \((x, y) \to (x^3, y^3)\)) acted on the \(3 \times 3\) torsion grid, it was a perfect rotation. Because the trace is \(0\), it means that none of the \(8\) non-trivial points on the “donut” landed on themselves. They were all shuffled to new positions. In the language of point counting (\(N_p = p + 1 - a_p\)), this tells us there are exactly \(3 + 1 - 0 = 4\) points on the curve over \(\mathbb{F}_3\).

The L-factor: \(3T^2 + 1\) This is the “DNA capsule” for the prime \(p=3\). The formula is \(1 - a_p x + p x^2\). Since \(a_p = 0\), the middle term vanishes. Why it matters: When we build the L-function, we turn this into a fraction in which \(T\) is replaced by \(3^{-s}\) : \(\frac{1}{1 + 3(3^{-2s})} = \frac{1}{1 + 3^{1-2s}}\). This specific polynomial “weights” the importance of the prime \(3\) in the global L-series.

The \(3\)-Division Polynomial: \(3x^4 + 6x^2 - 1\) This is the blueprint for the \(3 \times 3\) grid. Every \(x\)-coordinate of the \(9\) points in the torsion grid must be a root of this polynomial (or the point at infinity). Notice it is degree \(4.\) Since each \(x\) usually gives two \(y\) values (\(\pm y\)), these \(4\) roots correspond to the \(8\) points sitting on the “tube” and “hole” of the donut.

The \(p\)-adic address: \(5 + 4 \cdot 11 + 2 \cdot 11^2 \dots\) It’s the high-resolution coordinate of one of those \(9\) points. The base \((11)\): We looked at the point through the “lens” of the prime \(11.\)

The digits (\(5, 4, 2, 7, 7\)): This is the “GPS coordinate” of the point in the Tate Module. The \(5\) is the location on a coarse \(11 \times 11\) grid. The \(4 * 11\) is the correction needed to find the point on a finer \(121 \times 121\) grid. The \(O(11^5)\): This is the “zoom level.” It means we know exactly where this “fractional stake” is located down to a resolution of \(11^5\) (\(161,051\) sub-tiles).


To get a complete picture, we don’t just look at \(\ell\)-torsion, but \(\ell^2, \ell^3, \dots\) and so on. We “stack” these layers together to create the Tate Module \(T_\ell(E)\). Because we are stacking powers of \(\ell\), the coordinates of these points naturally live in the \(\ell\)-adic integers \(\mathbb{Z}_\ell\). The Tate Module is a 2-dimensional space over \(\mathbb{Z}_\ell\).

While SageMath doesn’t typically output a “matrix of digits” for the Tate module in a single command, you can compute the representation of the Galois action (which is what the Frobenius matrix \(M\) is) on the \(\ell\)-torsion points.

E = EllipticCurve([1, 0]) # y^2 = x^3 + x
p = 5
ell = 3

K_p = Qp(p, prec=20)
psi = E.division_polynomial(ell)

psi_mod_p = psi.change_ring(GF(p))
factors_mod_p = psi_mod_p.factor()
E = EllipticCurve([1, 0])
p = 5
ell = 3

# 1. Get the division polynomial
psi = E.division_polynomial(ell)

# 2. Find the splitting field modulo p
# This automatically determines the correct degree 'd'
K_finite = GF(p)['x'](psi).splitting_field('a_bar')
d = K_finite.degree()

# 3. Use that degree to build your p-adic extension
K_ext = Qq(p^d, prec=20, names='a')

print(f"Detected required degree: {d}")
print(f"Defining polynomial for the p-adic digits: {K_ext.defining_polynomial()}")
print(f"The 'digit' a satisfies: {def_poly}")

psi_over_ext = psi.change_ring(K_ext)
roots_in_ext = psi_over_ext.roots(multiplicities=False)

print(f"--- {p}-adic Grid Shuffle (The Frobenius Lift) ---")
for i, root in enumerate(roots_in_ext):
    frob_root = root.frobenius()
    val = psi_over_ext(frob_root).valuation()
    is_still_root = val >= 20
    print(f"Root {i})
    print(f"Root {i} maps to: {frob_root}")
    print(f"  -> Still in the grid? {is_still_root} (Valuation: {val})")
    print("-" * 35)

print(f"Trace a_p at p={p}: {E.ap(p)}")

---

OUTPUT:

Detected required degree: 4
Defining polynomial for the p-adic extension: (1 + O(5^20))*x^4 + O(5^20)*x^3 + (4 + O(5^20))*x^2 + (4 + O(5^20))*x + 2 + O(5^20)
The generator 'a' satisfies: (1 + O(5^20))*x^4 + O(5^20)*x^3 + (4 + O(5^20))*x^2 + (4 + O(5^20))*x + 2 + O(5^20)
--- 5-adic Grid Shuffle (The Frobenius Lift) ---
Root 0: (a^3 + 3*a^2 + 3*a + 4) + (a^2 + 4*a + 3)*5 + (2*a^2 + 4*a + 4)*5^2 + (4*a^3 + 2*a^2 + 4*a + 1)*5^3 + (a + 3)*5^4 + (4*a^3 + a^2 + 4)*5^5 + (a^3 + 4*a)*5^6 + (3*a^2 + a + 2)*5^7 + (3*a^3 + a^2 + 2*a + 2)*5^8 + (3*a^3 + 2*a^2 + 2*a)*5^9 + a^2*5^10 + (2*a^3 + 3*a + 2)*5^11 + (a^3 + 3*a + 4)*5^12 + (4*a^3 + 4*a^2 + a)*5^13 + (3*a^3 + 4*a + 3)*5^14 + (4*a^2 + a)*5^15 + (3*a^3 + 2*a^2)*5^16 + (4*a^3 + 4*a^2 + a + 3)*5^17 + 3*a^2*5^18 + (2*a^2 + 3*a + 1)*5^19 + O(5^20)
Root 0 maps to: (4*a^3 + 3*a + 2) + (a^3 + 4*a)*5 + (2*a^3 + 3*a + 2)*5^2 + (a^3 + 2*a + 4)*5^3 + (a^3 + a^2)*5^4 + (4*a^2 + 3*a + 4)*5^5 + (a^3 + 3*a^2 + a)*5^6 + (a^3 + 4*a)*5^7 + (2*a^3 + 4*a + 2)*5^8 + (a^3 + a^2 + 2*a + 1)*5^9 + (4*a^3 + 3*a^2 + a + 4)*5^10 + (2*a^3 + 4*a^2 + a + 2)*5^11 + a^2*5^12 + (3*a^3 + 4*a^2 + a + 3)*5^13 + (2*a^3 + a^2 + 1)*5^14 + (a^3 + 2*a^2 + 2*a + 4)*5^15 + (2*a^3 + 2*a^2 + 4*a + 1)*5^16 + (3*a^3 + 4*a^2 + 2*a + 4)*5^17 + (3*a^2 + 4)*5^18 + (a^3 + 3*a^2)*5^19 + O(5^20)
  -> Still in the grid? True (Valuation: 20)
-----------------------------------
Root 1: (a^3 + 2*a + 3) + (3*a^3 + 4)*5 + (2*a^3 + a + 2)*5^2 + (3*a^3 + 2*a)*5^3 + (3*a^3 + 4*a^2 + 4*a + 4)*5^4 + (4*a^3 + a)*5^5 + (3*a^3 + a^2 + 3*a + 4)*5^6 + (3*a^3 + 4*a^2 + 4)*5^7 + (2*a^3 + 4*a^2 + 2)*5^8 + (3*a^3 + 3*a^2 + 2*a + 3)*5^9 + (a^2 + 3*a)*5^10 + (2*a^3 + 3*a + 2)*5^11 + (4*a^3 + 3*a^2 + 4*a + 4)*5^12 + (a^3 + 3*a + 1)*5^13 + (2*a^3 + 3*a^2 + 4*a + 3)*5^14 + (3*a^3 + 2*a^2 + 2*a)*5^15 + (2*a^3 + 2*a^2 + 3)*5^16 + (a^3 + 2*a)*5^17 + (4*a^3 + a^2 + 4*a)*5^18 + (3*a^3 + a^2 + 4*a + 4)*5^19 + O(5^20)
Root 1 maps to: (a^3 + 3*a^2 + 3*a + 4) + (a^2 + 4*a + 3)*5 + (2*a^2 + 4*a + 4)*5^2 + (4*a^3 + 2*a^2 + 4*a + 1)*5^3 + (a + 3)*5^4 + (4*a^3 + a^2 + 4)*5^5 + (a^3 + 4*a)*5^6 + (3*a^2 + a + 2)*5^7 + (3*a^3 + a^2 + 2*a + 2)*5^8 + (3*a^3 + 2*a^2 + 2*a)*5^9 + a^2*5^10 + (2*a^3 + 3*a + 2)*5^11 + (a^3 + 3*a + 4)*5^12 + (4*a^3 + 4*a^2 + a)*5^13 + (3*a^3 + 4*a + 3)*5^14 + (4*a^2 + a)*5^15 + (3*a^3 + 2*a^2)*5^16 + (4*a^3 + 4*a^2 + a + 3)*5^17 + 3*a^2*5^18 + (2*a^2 + 3*a + 1)*5^19 + O(5^20)
  -> Still in the grid? True (Valuation: 20)
-----------------------------------
Root 2: (4*a^3 + 2*a^2 + 2*a + 1) + (4*a^3 + 3*a^2 + 1)*5 + (4*a^3 + 2*a^2)*5^2 + (2*a^2 + 3)*5^3 + (4*a^3 + 4*a^2 + 3*a + 1)*5^4 + (3*a^2 + 4*a)*5^5 + (3*a^3 + 4*a^2 + 4)*5^6 + (4*a^3 + a^2 + 3*a + 2)*5^7 + (a^3 + 3*a^2 + 2*a + 2)*5^8 + (a^3 + 2*a^2 + 2*a + 4)*5^9 + (4*a^3 + 3*a^2 + 4*a + 4)*5^10 + (2*a^3 + 4*a^2 + a + 2)*5^11 + (3*a^3 + 4*a^2 + a)*5^12 + (3*a + 4)*5^13 + (a^3 + 4*a^2 + 1)*5^14 + (4*a^3 + 3*a + 4)*5^15 + (a^3 + 2*a^2 + 4*a + 4)*5^16 + (3*a + 1)*5^17 + (4*a^3 + a^2 + 4*a + 4)*5^18 + (4*a^3 + 2*a^2 + a + 3)*5^19 + O(5^20)
Root 2 maps to: (a^3 + 2*a + 3) + (3*a^3 + 4)*5 + (2*a^3 + a + 2)*5^2 + (3*a^3 + 2*a)*5^3 + (3*a^3 + 4*a^2 + 4*a + 4)*5^4 + (4*a^3 + a)*5^5 + (3*a^3 + a^2 + 3*a + 4)*5^6 + (3*a^3 + 4*a^2 + 4)*5^7 + (2*a^3 + 4*a^2 + 2)*5^8 + (3*a^3 + 3*a^2 + 2*a + 3)*5^9 + (a^2 + 3*a)*5^10 + (2*a^3 + 3*a + 2)*5^11 + (4*a^3 + 3*a^2 + 4*a + 4)*5^12 + (a^3 + 3*a + 1)*5^13 + (2*a^3 + 3*a^2 + 4*a + 3)*5^14 + (3*a^3 + 2*a^2 + 2*a)*5^15 + (2*a^3 + 2*a^2 + 3)*5^16 + (a^3 + 2*a)*5^17 + (4*a^3 + a^2 + 4*a)*5^18 + (3*a^3 + a^2 + 4*a + 4)*5^19 + O(5^20)
  -> Still in the grid? True (Valuation: 20)
-----------------------------------
Root 3: (4*a^3 + 3*a + 2) + (a^3 + 4*a)*5 + (2*a^3 + 3*a + 2)*5^2 + (a^3 + 2*a + 4)*5^3 + (a^3 + a^2)*5^4 + (4*a^2 + 3*a + 4)*5^5 + (a^3 + 3*a^2 + a)*5^6 + (a^3 + 4*a)*5^7 + (2*a^3 + 4*a + 2)*5^8 + (a^3 + a^2 + 2*a + 1)*5^9 + (4*a^3 + 3*a^2 + a + 4)*5^10 + (2*a^3 + 4*a^2 + a + 2)*5^11 + a^2*5^12 + (3*a^3 + 4*a^2 + a + 3)*5^13 + (2*a^3 + a^2 + 1)*5^14 + (a^3 + 2*a^2 + 2*a + 4)*5^15 + (2*a^3 + 2*a^2 + 4*a + 1)*5^16 + (3*a^3 + 4*a^2 + 2*a + 4)*5^17 + (3*a^2 + 4)*5^18 + (a^3 + 3*a^2)*5^19 + O(5^20)
Root 3 maps to: (4*a^3 + 2*a^2 + 2*a + 1) + (4*a^3 + 3*a^2 + 1)*5 + (4*a^3 + 2*a^2)*5^2 + (2*a^2 + 3)*5^3 + (4*a^3 + 4*a^2 + 3*a + 1)*5^4 + (3*a^2 + 4*a)*5^5 + (3*a^3 + 4*a^2 + 4)*5^6 + (4*a^3 + a^2 + 3*a + 2)*5^7 + (a^3 + 3*a^2 + 2*a + 2)*5^8 + (a^3 + 2*a^2 + 2*a + 4)*5^9 + (4*a^3 + 3*a^2 + 4*a + 4)*5^10 + (2*a^3 + 4*a^2 + a + 2)*5^11 + (3*a^3 + 4*a^2 + a)*5^12 + (3*a + 4)*5^13 + (a^3 + 4*a^2 + 1)*5^14 + (4*a^3 + 3*a + 4)*5^15 + (a^3 + 2*a^2 + 4*a + 4)*5^16 + (3*a + 1)*5^17 + (4*a^3 + a^2 + 4*a + 4)*5^18 + (4*a^3 + 2*a^2 + a + 3)*5^19 + O(5^20)
  -> Still in the grid? True (Valuation: 20)
-----------------------------------
Trace a_p at p=5: 2

Interpretation: This output is the definitive proof that the L-function of an elliptic curve is built on a foundation of perfect \(p\)-adic symmetry. The Still in the grid? True: seeing True (Valuation: 20): By using the .frobenius() lifting in the Qq extension, we successfully shuffled the torsion points without causing them to “drift.”

The Precision: A valuation of \(20\) means that even at a resolution of \(5^{20}\) (about \(95\) trillion sub-tiles on our donut), the point landed exactly in another seat in the grid. This confirms that the Frobenius map is a true automorphism of the curve’s arithmetic structure.

Look at the expanded strings like \((4*a^3 + 3*a + 2) + (a^3 + 4*a)*5\dots\) The first term (e.g., \(4a^3 + 3a + 2\)): This is the location of the point on the “coarse” grid of the finite field \(\mathbb{F}_{5^d}\). The powers of \(5:\) These are the “digital signatures” of the Tate Module. Each subsequent term is a higher-resolution refinement of the point’s position.

The Trace \(a_p = 2\): This single integer is the ultimate “summary” of that massive digital shuffle. In our experiment, the Frobenius map took each of the \(4\) roots and moved them to different addresses. The Trace (\(a_5 = 2\)) is the mathematical result of summing the “diagonal” of the \(2 \times 2\) matrix that represents this shuffle. This number tells us that over the finite field \(\mathbb{F}_5\), the curve has \(N_p = p + 1 - a_p = 5 + 1 - 2 = \mathbf{4}\) points.

We have just manually verified one “Euler Factor” of the L-function. Because the grid stayed intact (True), we know the representation is stable. The L-function is just a massive product: \(L(E, s) = \prod_{p} \frac{1}{1 - a_p p^{-s} + p^{1-2s}}\). For \(p=3\), we found \(a_3 = 0\). For \(p=5\), we found \(a_5 = 2\). Each prime \(p\) provides one of these \(a_p\) “heartbeats.” By proving that the \(p\)-adic addresses shuffle perfectly, we’ve shown that the curve’s internal geometry is consistent enough to be studied as a global analytic function.

We’ve moved from the Geometric \((y^2 = x^3 + x)\) to the Arithmetic (p-adic addresses) to the Analytic (\(a_p\) values). This is the exact path taken by researchers proving the Modularity Theorem. We are essentially looking at the “source code” of the connection between elliptic curves and modular forms.

But how are these roots constructed?

The structure of the “digits” (\(a\)) comes from the prime \(\ell\), but the “resolution” (\(5^0, 5^1, 5^2 \dots\)) comes from the prime \(p\). The digits are calculated to satisfy the the division polynomial across higher powers of \(p\)

In a \(p\)-adic expansion like:

\[\text{root of }\psi_{\ell} = d_0 + d_1 p^1 + d_2 p^2 + \dots\]

Each coefficient (\(d_k\)) is found by looking at the division polynomial \(\psi_{\ell}\) modulo higher powers of \(p\).

\(d_0\) is a root of \(\psi_{\ell} \pmod p\).

\(d_1\) is calculated so that \((d_0 + d_1 p)\) is a root of \(\psi_{\ell} \pmod{p^2}\).

\(d_2\) is calculated so that \((d_0 + d_1 p + d_2 p^2)\) is a root of \(\psi_{\ell} \pmod{p^3}\).

This process is called Hensel’s Lemma. It is like using Newton’s method to find a root, but instead of getting more decimal places, you are getting more \(p\)-adic places (powers of \(p\)).

From the basics, what is a:

Each digit \(d_i\) in our \(p\)-adic string \(d_0 + d_1 p^1 + d_2 p^2 + \dots\) is a polynomial in \(a\):

\[d_i = c_0 + c_1 a + c_2 a^2 + c_3 a^3\]

The coefficients \(c_j\) are just integers from \(\{0, 1, 2, 3, 4\}\).

The \(a\) is essentially a placeholder for the internal symmetry of the donut’s loops. a is the root of the polynomial that defines the symmetry of the \(\ell=3\) torsion.

Since you are looking at \(3\)-torsion, there are \(8\) non-trivial points (\(3^2 - 1\)).

The division polynomial \(\psi_3(x)\) can be found in the lines:

Defining polynomial for the p-adic digits: (1 + O(5^20))*x^4 + O(5^20)*x^3 + (4 + O(5^20))*x^2 + (4 + O(5^20))*x + 2 + O(5^20) The 'digit' a satisfies: (1 + O(5^20))*x^4 + O(5^20)*x^3 + (4 + O(5^20))*x^2 + (4 + O(5^20))*x + 2 + O(5^20)

i.e. the polynomial \[\psi_3(x) = 3x^4 + 6x^2 - 1\]

If \(i\) in complex numbers is defined by the rule \(i^2 + 1 = 0\), what is the specific “rule” for \(a\)?

For the curve \(y^2 = x^3 + x\) and the prime \(p=5\), we can actually find the rule. The \(3\)-division polynomial is:

\[\psi_3(x) = 3x^4 + 6x^2 - 1\]

which does not change with higher powers of \(p\): we are simply solving the same \(\ell\)-related equation

\[\psi_{\ell}(x) \equiv0 \mod p^n\]

to higher and higher precision. If \(x_i\) is a root modulo \(p^i\)

\[\psi_l(x) \equiv 0 \mod p^i\]

what’s the next digit such that \(x_{i+1}=x_i + d_i p^i,\) where \(x_{i+1}\) is the root modulo \(p^{i+1}\):

\[\psi'(x_0) \cdot d_i \equiv -\frac{\psi(x_i)}{p^i}\mod p\]

or

\[d_i\equiv - \frac{\psi(x_i)}{p^i}\left[ \psi'(x_0)\right]^{-1} \mod p\] The result is a polynomial in \(a\).

When we look at this modulo \(5\), \(\psi_{\ell}(x)\) polynomial becomes:

\[3x^4 + x^2 + 4 \equiv 0 \pmod 5\]

If you factor this over \(\mathbb{F}_5\), you find it is irreducible (it has no roots in the normal numbers \(0, 1, 2, 3, 4\)). So, just as \(i\) is the root of \(x^2+1\), Sage “invents” \(a\) to be the root of this polynomial (or one of its factors).

The rule for your \(a\) is:

\[3a^4 + a^2 + 4 = 0 \mod 5\]

a is the generator of the limited field extension \(\mathbb F_{p^d}.\)

a is the exact symbolic representation of the torsion point. We don’t need to solve for \(a\) because the interest is in how the points move. The shuffling provides the trace \(a_p\), which is a number.

Each digit \(d_k\) in \(d_0 + d_1 p + d_2 p^2 + \cdots\) is a polynomial in \(a.\) For instance \(2a^2 + 4a +4.\) Frobenius applies a field automorphism:

\[\phi_5(2a^2 + 4a +4) = 2(a^5)^2+4(a^5) +4\]

and because \(a\) is defined by \(3a^4+a^2+4=0,\) \(a^5\) can be simplified to a lower power so it stays withing \(4\) dimensional bounds.

To get to the trace \(a_p\) one \(\ell\) is all that is needed. These are the next steps:

We take the roots of your division polynomial \(\psi_\ell\) and label them. For \(\ell=3\), there are \(4\) distinct \(x\)-coordinates. Let’s call them: \(X = \{r_0, r_1, r_2, r_3\}.\) These are the “seats.” In the code output, these were those long \(p\)-adic strings involving \(a\), as for example:

Root 0: (a^3 + 3*a^2 + 3*a + 4) + (a^2 + 4*a + 3)*5 + (2*a^2 + 4*a + 4)*5^2 + (4*a^3 + 2*a^2 + 4*a + 1)*5^3 + (a + 3)*5^4 + (4*a^3 + a^2 + 4)*5^5 + (a^3 + 4*a)*5^6 + (3*a^2 + a + 2)*5^7 + (3*a^3 + a^2 + 2*a + 2)*5^8 + (3*a^3 + 2*a^2 + 2*a)*5^9 + a^2*5^10 + (2*a^3 + 3*a + 2)*5^11 + (a^3 + 3*a + 4)*5^12 + (4*a^3 + 4*a^2 + a)*5^13 + (3*a^3 + 4*a + 3)*5^14 + (4*a^2 + a)*5^15 + (3*a^3 + 2*a^2)*5^16 + (4*a^3 + 4*a^2 + a + 3)*5^17 + 3*a^2*5^18 + (2*a^2 + 3*a + 1)*5^19 + O(5^20)

Next we need to apply the Frobenius map \(\phi_p\) to each root. The Frobenius doesn’t create new roots; it just moves the existing ones around. For example, your output might show:\(\phi_p(r_0) = r_2\), \(\phi_p(r_2) = r_0\), \(\phi_p(r_1) = r_3\), and \(\phi_p(r_3) = r_1\). This is a permutation. In this example, the Frobenius is swapping two pairs of roots.

Move from \(x\)-coordinates to points (The 2D grid). This is the most critical step. An \(x\)-coordinate is just a shadow. Each \(x\)-coordinate (except where \(y=0\)) corresponds to two points on the curve: \((x, y)\) and \((x, -y)\). The \(3\)-torsion points form a \(2\)-dimensional vector space over \(\mathbb{F}_3\) (the “donut grid”). We pick a basis for this grid—two points \(P\) and \(Q\) that can generate all others.

\(P = (x_1, y_1)\)

\(Q = (x_2, y_2)\)

To build that \(2 \times 2\) matrix, we need to pick a basis for the torsion group. Since the \(3\)-torsion group is 2-dimensional (like a \(3 \times 3\) grid on a donut), we pick two points, \(P\) and \(Q\), that aren’t just multiples of each other. \(P\) has an \(x\)-coordinate, let’s call it \(x_P\). \(Q\) has an \(x\)-coordinate, let’s call it \(x_Q\). These \(x_P\) and \(x_Q\) are indeed two of the roots (the \(r_i\) values) from your division polynomial \(\psi_3.\)

Next we build the matrix. We watch where Frobenius sends our basis points \(P\) and \(Q\). Because the Frobenius is a linear transformation, the result must be some combination of \(P\) and \(Q\):

\(\text{Frob}_p(P) = A \cdot P + C \cdot Q\)

\(\text{Frob}_p(Q) = B \cdot P + D \cdot Q\)

The coefficients \(A, B, C, D\) (which are numbers in \(\mathbb{F}_\ell\)) form our Frobenius Matrix:

\[\text{Frob}_p = \begin{pmatrix} A & B \\ C & D \end{pmatrix}\]

Let’s say we’ve applied the Frobenius to our basis points and gotten two new points, which we will call \(P'\) and \(Q'\): \(\text{Frob}_p(P) = P'\)\(\text{Frob}_p(Q) = Q'\) At this stage, \(P'\) and \(Q'\) are just \(p\)-adic coordinates. You know they are “in the grid,” but you don’t yet know their “grid coordinates” (how many \(P\)s and \(Q\)s they are made of).

Finally, extract the trace (\(a_p\)). Once we have the matrix, the \(a_p \pmod \ell\) is simply the trace:

\[a_p \equiv A + D \pmod \ell\]

If the matrix is

\[\begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix}\] the trace is \(1+1=2\).

This matches the \(a_p = 2\) in our output!


Now, the Frobenius map \(\phi_p\) acts on this 2D space \(T_\ell(E)\) just like a \(2 \times 2\) matrix. From linear algebra, every \(2 \times 2\) matrix \(M\) has a characteristic polynomial:

\[P(T) = \det(T \cdot I - M) = T^2 - \text{Tr}(M)T + \det(M)\]

When we apply this to the Frobenius matrix:

The Determinant is always \(p\).

The Trace (the sum of the diagonal) is exactly our \(a_p\).

So the polynomial that describes the curve’s “DNA” at prime \(p\) is:

\[P_p(T) = T^2 - a_p T + p\]

Finally, to get the L-function, we take these individual polynomials \(P_p(T)\) and “package” them. We use the reciprocal of the polynomial and evaluate it at \(T = p^{-s}\) in what are known as Euler factors or local factors:

\[L_p(s) = \frac{1}{1 - a_p p^{-s} + p \cdot p^{-2s}}\]

The shift from \(T^2 - a_p T + p\) to \(1 - a_p T + p T^2\) happens because we aren’t just plugging in a value; we are moving to the reciprocal polynomial. In linear algebra, if a matrix \(M\) has eigenvalues \(\lambda_1, \lambda_2\), its characteristic polynomial is:

\[P(T) = \text{det}(T \cdot I - M) = T^2 - \text{Trace}(M)T + \text{det}(M)\]

However, when we build a generating function (like an L-function), we want to describe the “flow” of the data using a power series. To do this, we use the Reciprocal Polynomial, which is defined as:

\[P^*(T) = T^2 P(1/T) = 1 - \text{Trace}(M)T + \text{det}(M)T^2\]

The “1 -” structure is required for the Euler Product to work. In number theory, we want to represent the L-function as a sum that looks like the Riemann Zeta function:

\[\sum \frac{a_n}{n^s} = \prod_p \frac{1}{\text{Polynomial}(p^{-s})}\]

For a fraction like \(\frac{1}{1 - X}\) to be expanded into a series (\(1 + X + X^2 + \dots\)), the denominator must start with 1. If we used \(T^2 - a_p T + p\), the expansion would be messy and wouldn’t match the \(a_n\) coefficients of our elliptic curve. By using \(1 - a_p T + p T^2\), the expansion begins:

\[1 + (a_p)T + (a_p^2 - p)T^2 + \dots\]

When you plug in \(T = p^{-s}\), the first term is \(1\), and the second term is \(a_p p^{-s}\), which is exactly the \(p\)-th term of the Dirichlet series we are trying to build.

The packaging of all these polynomials usually is written as

\[L(E, s) = \prod_{p \nmid \Delta} \frac{1}{1 - a_p p^{-s} + p \cdot p^{-2s}} \times \prod_{p \mid \Delta} L_p(s)\]

When you multiply this infinite “wall” of fractions together, you get the Dirichlet series (see below (🚀)):

\[L(E, s) = \sum_{n=1}^{\infty} \frac{a_n}{n^s}\]

The polynomial \(P_p(T) = T^2 - a_p T + p\) is the standard characteristic polynomial of the Frobenius matrix \(M\): the Frobenius matrix \(M\) is the linear representation of the Frobenius endomorphism \(\phi_p\) as it acts on the Tate module \(T_\ell(E)\). Because the Tate module is a 2-dimensional vector space over the \(\ell\)-adic integers \(\mathbb{Z}_\ell\), any linear transformation on it (like Frobenius) can be written as a \(2 \times 2\) matrix:

\[M = \begin{pmatrix} \alpha & \beta \\ \gamma & \delta \end{pmatrix}\]

The entries \(\alpha, \beta, \gamma, \delta\) are \(\ell\)-adic integers. Their specific values depend on the basis you choose for your grid of torsion points. While the individual entries change if you pick a different basis, the invariants of the matrix do not change. For any elliptic curve \(E\) at a prime \(p\) of good reduction:

The Trace: \(\text{Tr}(M) = \alpha + \delta = a_p\)

The Determinant: \(\det(M) = \alpha\delta - \beta\gamma = p\)

The characteristic polynomial \(P_p(T)\) is defined as the determinant of

\[(T \cdot I - M)\]

Using the invariants above:

\[P_p(T) = \det(T \cdot I - M)\]

\[P_p(T) = \det \begin{pmatrix} T - \alpha & -\beta \\ -\gamma & T - \delta \end{pmatrix}\]

\[P_p(T) = (T - \alpha)(T - \delta) - (-\beta)(-\gamma)\]

\[P_p(T) = T^2 - (\alpha + \delta)T + (\alpha\delta - \beta\gamma)\]

\[P_p(T) = T^2 - a_p T + p\]

However, when we build an L-function, we aren’t interested in the eigenvalues themselves as much as we are interested in how they “scale” our series. In complex analysis and number theory, we use a slightly different determinant construction for Euler products:

\[\text{Local Factor} = \frac{1}{\det(I - M \cdot T)}\]

where \(T = p^{-s}\).

Watch what happens when we expand \(\det(I - M \cdot T)\) for a \(2 \times 2\) matrix \(M\): The identity \(I\) is \(\begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix}\). The term \(M \cdot T\) is

\[\begin{pmatrix} m_{11}T & m_{12}T \\ m_{21}T & m_{22}T \end{pmatrix}\]

The subtraction \(I - MT\) is

\[\begin{pmatrix} 1 - m_{11}T & -m_{12}T \\ -m_{21}T & 1 - m_{22}T \end{pmatrix}\]

When you calculate the determinant of that result:

\[\det(I - MT) = (1 - m_{11}T)(1 - m_{22}T) - (-m_{12}T)(-m_{21}T)\]

\[= 1 - (m_{11} + m_{22})T + (m_{11}m_{22} - m_{12}m_{21})T^2\]

Notice the results: The constant is now \(1\).The coefficient of \(T\) is the Trace (\(a_p\)). The coefficient of \(T^2\) is the Determinant (\(p\)).

The reason we want the “1” at the front is to allow the local factor to be expanded into a geometric series. If the denominator started with \(p\) (like \(p - a_p T + T^2\)), we couldn’t easily turn it into an infinite sum starting with \(1\). By having the denominator start with \(1\):

\[\frac{1}{1 - (a_p T - p T^2)} = 1 + (a_p T - p T^2) + (a_p T - p T^2)^2 + \dots\]

This expansion ensures that the very first term of the L-function is \(1\). When you multiply all these local factors together (\(1 \cdot 1 \cdot 1 \dots\)), we get the starting term of the global Dirichlet series:

\[L(E, s) = \mathbf{1} + \frac{a_2}{2^s} + \frac{a_3}{3^s} + \dots\]

In summary\(T^2 - a_p T + p\) is the polynomial whose roots are the eigenvalues of Frobenius.

\(1 - a_p T + p T^2\) is the reciprocal polynomial. Its roots are the inverses of the eigenvalues. We use the reciprocal version because it lets us expand the L-function into a series that starts with \(1\), perfectly matching the structure of the integers in our sum.

The global L-function is simply the product of all these local pieces for every prime \(p\):

\[L(E, s) = \prod_{p} L_p(s)\]

This product, when multiplied out, creates the infinite sum (Dirichlet series) \(\sum a_n n^{-s}\) that links the curve’s point-counting to the world of complex analysis and modular forms.

Example:

Let’s look at a concrete example using a very small prime so we can see every “ingredient” at work. We’ll take the elliptic curve \(E\):

\[y^2 = x^3 + x\]

And we will look at it through the lens of the prime \(p = 3\).

  • Simple view:

First, we look for points in our field \(\mathbb{F}_3 = \{0, 1, 2\}\). We test every \(x\):

If \(x=0\): \(y^2 = 0 \implies y=0\). Point: \((0,0)\)

If \(x=1\): \(y^2 = 1+1 = 2\). In \(\mathbb{F}_3\), there is no number that squared equals \(2\). No points.

If \(x=2\): \(y^2 = 8 + 2 = 10 \equiv 1 \pmod 3 \implies y=1\) or \(y=2\). Points: \((2,1)\) and \((2,2)\)

Don’t forget the Point at infinity: \(\mathcal{O}\)

Total points \(N_3 = 4\). Our trace of Frobenius is \(a_3 = (3 + 1) - 4 = \mathbf{0}\).

  • “Complex” view (field extensions):

Now, let’s pick our “measuring stick” \(\ell = 2\). We want to find the \(2^2 = 4\) points that satisfy \(2P = \mathcal{O}\).

These are the points where \(y=0\), because adding a point twice resulting in the point at infinity means that the tangent is vertical, which means \(x^3 + x = 0\).

Solving \(x(x^2 + 1) = 0\) in \(\mathbb{F}_3\):

\(x = 0\) (We already found this: \((0,0)\))

\(x^2 + 1 = 0 \implies x^2 = -1 \equiv 2 \pmod 3\)

In \(\mathbb{F}_3\), there is no square root of \(2\). To find these points, we must go to the “complex” extension field \(\mathbb{F}_{3^2}\) (which we can think of as \(\mathbb{F}_3[i]\) where \(i^2 = 2\)). In this larger field, our missing \(x\)-coordinates are \(i\) and \(-i\). In \(\mathbb{F}_3\), notice that: \(2 \equiv -1 \pmod 3\). So \(i^2 = 2\) is literally saying \(i^2 = -1\). This makes \(i\) the perfect name for this element! It behaves almost exactly like the imaginary unit we know from calculus or physics, but it lives in a world with only 9 numbers total (\(\mathbb{F}_{3^2}\)).

Our 2-torsion grid is:

\[\{\mathcal{O}, (0,0), (i, 0), (-i, 0)\}\]

  • The Frobenius Action

Now we apply the “shuffling engine” \(\phi_3(x, y) = (x^3, y^3)\) to our grid:

\[\phi_3(\mathcal{O}) = \mathcal{O}\quad\text{fixed}\]

\[\phi_3(0, 0) = (0^3, 0^3) = (0,0)\quad\text{fixed}\]

\[\phi_3(i, 0) = (i^3, 0^3)\]

What is \(i^3\)? Since \(i^2 = 2\), then \(i^3 = 2i \equiv -i \pmod 3\). So, \(\phi_3\) swaps the two “complex” points:

\[(i, 0) \to (-i, 0) \quad \text{and} \quad (-i, 0) \to (i, 0)\]

  • The Matrix and the Trace

To make the matrix, we choose a basis. Let \(E_1 = (0,0)\) and \(E_2 = (i,0)\).

\(\phi_3(E_1) = 1 \cdot E_1 + 0 \cdot E_2\) (It stayed put)

\(\phi_3(E_2) = 0 \cdot E_1 + (-1) \cdot E_2\) (It became its negative sibling)

The matrix is:

\[M = \begin{pmatrix} 1 & 0 \\ 0 & -1 \end{pmatrix}\]

Now, calculate the “DNA” of the matrix:

Determinant: \((1)(-1) - (0)(0) = -1\). Wait! We expected \(p=3\). But remember, this matrix is working in the world of \(\ell=2\). In the world of “integers modulo \(2\),” \(-1 \equiv 1 \pmod 2\).

Trace: \(1 + (-1) = \mathbf{0}\).

  • The Result:

Our matrix approach gave us a Trace of \(0.\) Our “counting points” approach in Step 1 gave us \(a_3 = 0\). They match perfectly. The “complex” shuffling of the hidden points in the extension field tells us exactly how many “real” points were in our starting field.

For the L-function, we now know the piece for \(p=3\) is:

\[L_p(E, s) = \frac{1}{1 - a_p p^{-s} + p \cdot p^{-2s}}\]

and therefore,

\[L_3(s) = \frac{1}{1 - 0(3^{-s}) + 3(3^{-2s})} = \frac{1}{1 + 3^{1-2s}}\]


(⛷️) On one side we have a purely arbitrary decision: we choose any prime number different to \(p\). Say, \(111181821213.\) We are now imagining a lattice of \(111181821213^2\) points. And we say, we want \(a_5\). So we have to operate \(p =5.\) No problem. We have to find the torsion points with coordinates \(x\) and \(y\) in \(\mathbb F_{p^k}\) among all the points on the curve under the field \(\mathbb F_{p^k}\) that are \(111181821213\)-torsion points, i.e. points such that \(111181821213 P =\mathcal O.\) To do so we would need a much larger number system than \(\mathbb F_5\) to describe them, and hence the horizontal extension to \(\mathbb F_{p^k}\). Now, we don’t actually do this literally. We just get two points instead by using the division polynomial, build a matrix (Frobenius) and use linearity to get the trace. Since the \(\ell\)-torsion grid is essentially a 2D plane (the \(2 \times 2\) matrix), you only need two independent points to see how the whole plane rotates. If you find one point in \(\mathbb{F}_{p^{234}}\), you’ve found a “spoke” of the wheel.

But the idea is that if there were points, say \(w \in W,\) with coordinates \(x\) and \(y\) in the base field \(\mathbb F_5,\) which multiplied \(111181821213 w =\mathcal O\) the Frobenius map would not move them. The chances of finding any \(w\) besides \(\mathcal O\) may be zero; however, we don’t care because we have a division polynomial that expects \(111181821213\) roots in \(F_{p^k},\) so all we have to do is extend the \(k.\)

Because we are interested in the trace of a linear transformation, we only need two independent ‘spokes’ to define the rotation of the entire lattice. Suppose one of these points, \(q\), is found in the extension field \(\mathbb{F}_{p^{234}}\). At this stage, the coordinates of \(q\) are only known at precision \(1,\) meaning we only see the first digit, \(v = d_0 \pmod p\). We need two of these points to construct the Frobenius matrix, but we cannot afford any ‘blurriness’ in our calculation of the trace (\(a_p\)). If we stayed at this low resolution, we couldn’t tell the difference between the true \(p\)-adic coordinate and one shifted by a multiple of \(p\) (e.g., \(v\) vs. \(v + 6p\)). Since \(a_p\) is a specific integer like \(-3\), and matrix math compounds any small errors, a ‘blurry’ coordinate would leave us unable to distinguish the correct \(a_p\) from a sea of massive, incorrect integers. To fix this, we make \(q\) precise by lifting with Hensel, increasing the resolution from \(\pmod p\) to a high-definition string known to \(\pmod{p^{20}}\).

To translate from the field \(\mathbb{F}_{p^{234}}\) to the ring \(\mathbb{Z}_{p^{20}}\), we take the “flat” element from the finite field and treat it as the seed for an infinite \(p\)-adic string (integer). The intersection between these two systems exists because the first floor of the \(p\)-adic ring \(\mathbb{Z}_p\) is identical to the base field \(\mathbb{F}_p\). Mathematically, we say there is a map:

\[\mathbb{Z}_p \to \mathbb{Z}/p\mathbb{Z} \cong \mathbb{F}_p\]

When we create \(\mathbb{F}_{p^{234}}\), we aren’t just making the base field larger. We are adding a new, symbolic element — let’s call it \(\alpha\) — that acts as a root to an irreducible polynomial of degree \(234.\) Every number in this field is forced to be written as a combination of powers of that \(\alpha\). If you found a point \(q\) in this field, its \(x\)-coordinate isn’t a single digit. It looks like this:

\[x_q = c_0 + c_1 \alpha + c_2 \alpha^2 + \dots + c_{233} \alpha^{233}\]

Each \(c_i\) is a number from the base field \(\mathbb{F}_p\) (like \(0, 1, 2, 3,\) or \(4\) if \(p=5\)). This is the polynomial.

Example:

The field extension (\(\mathbb{F}_{5^2}\)): Imagine our torsion point \(q\) exists in a field where we’ve added a “ghost” number \(\alpha\) such that \(\alpha^2 = 2\), akin to \(i^2=-1\). In this field, every number is a polynomial: \(c_0 + c_1\alpha\). Let’s say we find a point \(q\) in the field \(\mathbb{F}_{5^2}\) with \(x\)-coordinate:

\[x_q = 1 + 2\alpha\]

This is our \(d_0\). It’s a “polynomial digit.” Now we want to lift this to Precision 2 (\(\pmod{25}\)). Now we are looking for a “first floor” \(d_1\), but this \(d_1\) must also be a polynomial in \(\alpha\).


(🚀) Take one piece of the product for a prime \(p\). Using the geometric series formula \(\frac{1}{1-X} = 1 + X + X^2 + \dots\), we expand the denominator:

\[L_p(s) = \frac{1}{1 - a_p p^{-s} + p \cdot p^{-2s}}\]

If we let \(T = p^{-s}\), the factor looks like

\[\frac{1}{1 - (a_p T - p T^2)}\]

When you expand this out,

\[X = (a_p T - p T^2)\]

Using the formula \(\frac{1}{1-X} = 1 + X + X^2 + X^3 + \dots\), and plugging in your \(X\), we get:

\[1 + (a_p T - p T^2) + (a_p T - p T^2)^2 + (a_p T - p T^2)^3 + \dots\]

Expanding out:

The 0th power: \(1\)

The 1st power \((X^1)\): \(a_p T - p T^2\)

The 2nd power \((X^2)\): \((a_p T - p T^2)^2 = (a_p T)^2 - 2(a_p T)(p T^2) + (p T^2)^2= a_p^2 T^2 - 2a_p p T^3 + p^2 T^4\)

The 3rd power \((X^3)\): \((a_p T - p T^2)^3 = (a_p T)^3 - 3(a_p T)^2(p T^2) + \dots= a_p^3 T^3 - 3a_p^2 p T^4 + \dots\)

Now we “harvest” all the pieces for each specific power of \(T\):

For \(T^1\): The only place a \(T^1\) appears is in the first power \((X^1)\). Coefficient: \(a_p\)

For \(T^2\): We get a \(-p\) from the \(X^1\) term and an \(a_p^2\) from the \(X^2\) term. Coefficient: \((a_p^2 - p)\)

For \(T^3\):We get a \(-2a_p p\) from the \(X^2\) term and an \(a_p^3\) from the \(X^3\) term. Coefficient: \((a_p^3 - 2a_p p)\)

Finally we get:

\[1 + (a_p)T + (a_p^2 - p)T^2 + (a_p^3 - 2pa_p)T^3 + \dots\]

Now, we define \(a_{p^k}\) to be the coefficient of the \(T^k\) term (which is \(p^{-ks}\)).

For \(k=1\): \(a_p = a_p\)

For \(k=2\): \(a_{p^2} = a_p^2 - p\)

For \(k=3\): \(a_{p^3} = a_p a_{p^2} - p a_p\)

This recurrence relation is actually the recursive “DNA” of the elliptic curve. It tells you exactly how to build the value for a power of a prime using only the \(a_p\) and the prime \(p\) itself.

Multiplying the Primes Together. Now, imagine doing this for \(p=2\), \(p=3\), \(p=5\), and so on:

\[L(E, s) = \left( 1 + \frac{a_2}{2^s} + \frac{a_4}{4^s} + \dots \right) \times \left( 1 + \frac{a_3}{3^s} + \frac{a_9}{9^s} + \dots \right) \times \left( 1 + \frac{a_5}{5^s} + \dots \right)\]

When you multiply these together, you pick one term from every set of parentheses. For example, picking \(\frac{a_2}{2^s}\) and \(\frac{a_3}{3^s}\) gives you \(\frac{a_2 a_3}{(2 \cdot 3)^s}\), which we define as \(\frac{a_6}{6^s}\).

Because every integer \(n\) has a unique prime factorization, every possible fraction \(\frac{a_n}{n^s}\) appears exactly once in the final result. The “wall of fractions” has successfully been converted into a single, infinite sum:

\[L(E, s) = \frac{a_1}{1^s} + \frac{a_2}{2^s} + \frac{a_3}{3^s} + \frac{a_4}{4^s} + \dots = \sum_{n=1}^{\infty} \frac{a_n}{n^s}\]


(🪁)

First example:

We need to get the roots of a division polynomial \(\psi(x) = x^2 - 2\) (in this case we want to find \(\sqrt{2}\) in \(p\)-adics). Say we are working \(p = 5\).

The seed (\(x_0\)): We need a starting value such that \(x_0^2 - 2 \equiv 0 \pmod 5\). We will need to extend the \(\mathbb F_5\) field. Let’s actually use \(x_0 = \alpha\) where \(\alpha^2 = 2\) exactly. We have just created the finite field \(\mathbb{F}_{5^2}\) (also written as \(\mathbb{F}_{25}\)).

We want to find the next version of \(x\), let’s call it \(x_1\), such that:

\[x_1 = x_0 + d_1 \cdot 5\]

And we require that this new \(x_1\) satisfies the polynomial at a higher power:

\[\psi(x_1) \equiv 0 \pmod{5^2}\]

We Taylor expand the polynomial \(\psi(x_0 + d_1 \cdot 5)\) around \(x_0\):

\[\psi(x_0 + d_1 \cdot 5) \approx \psi(x_0) + (d_1 \cdot 5) \cdot \psi'(x_0)\]

Because we are working \(\mod{5^2}\), all higher-order terms (those involving \(5^2, 5^3\), etc.) disappear.

Why are we working modulo \(5^2\)? To find our seed (\(x_0\)), we work \(\pmod 5\). But the moment we decide to “lift” the number to find the next digit (\(d_1\)), we must change our perspective to \(\pmod{5^2}\). To find \(x_0 = \alpha\), we solve: \(\psi(x) \equiv 0 \pmod 5.\) In this world, anything divisible by \(5\) is zero. You find that \(\alpha^2 \equiv 2 \pmod 5\). Now we want to find the next digit, \(d_1\). If we stayed working \(\pmod 5\), our equation would be useless: \((x_0 + 5d_1)^2 - 2 \equiv 0 \pmod 5\). Since \(5d_1\) is already \(0 \pmod 5\), this just collapses back to \(x_0^2 - 2 \equiv 0 \pmod 5\). It doesn’t help us find \(d_1\) at all. To “see” \(d_1\), we have to increase the resolution of our world to \(\pmod{25}\).

We set this equal to zero to solve for the unknown digit \(d_1\):

\[\psi(x_0) + (d_1 \cdot 5) \cdot \psi'(x_0) \equiv 0 \pmod{25}\]

Evaluate \(\psi(x_0)\): Since \(x_0 = \alpha\) and \(\alpha^2 = 2\), \(\psi(\alpha) = \alpha^2 - 2 = 0\). In a real scenario, \(x_0\) is a \(p\)-adic approximation, so \(\psi(x_0)\) would be some multiple of \(5,\) such as \(15\). Our current \(\psi(x_0) =0\) would produce a trail of zeros.

Evaluate the derivative \(\psi'(x_0)\):

\[\psi'(x) = 2x \implies \psi'(x_0) = 2\alpha\]

Substitute these into the linear equation:

\[15 + (d_1 \cdot 5) \cdot (2\alpha) \equiv 0 \pmod{25}\]

Divide the entire equation by \(5\) to isolate the digit \(d_1\):

\[3 + d_1 \cdot (2\alpha) \equiv 0 \pmod 5\]

Now, solve for \(d_1\) in the finite field \(\mathbb{F}_5\):

\[d_1 \cdot (2\alpha) \equiv -3 \equiv 2 \pmod 5\]

Once we find \(d_1\), we update our coordinate:

\[x_{\text{new}} = \alpha + d_1 \cdot 5\]

This new value is now our “seed” for the next round. We repeat this until our \(x\) has \(20\) digits.

The torsion points usually live in a ghost field (the extension field). This \(\alpha\) is the key that unlocks those points. This \(\alpha\) gets promoted to the \(p\)-adic skyscraper. We cannot calculate a matrix with a finite field. We must “lift” \(\alpha\) from \(\mathbb{F}_{p^k}\) into a \(p\)-adic extension ring, often written as \(\mathbb{Z}_p[\alpha]\). We don’t turn \(\alpha\) it into a normal number. In Sage we define a \(p\)-adic extension ring using the same irreducible polynomial that created \(\alpha\).

Instead of our \(x\)-coordinate being a single number, it becomes a combination of \(\alpha\):

\[x = d_0 + d_1 \alpha + d_2 \alpha^2 + \dots\]

Where each \(d_n\) is a \(p\)-adic number. You treat \(\alpha\) just like the imaginary unit \(i\) in complex numbers. Eventually, when we solve the equation: \(\phi_p(P) = aP + bQ,\) the coefficients \(a\) and \(b\) are integers (specifically, integers modulo \(\ell\)).


More complex example:

The prime: \(p = 7\).

The curve: \(y^2 = x^3 + 2\).

The division polynomial: \(f(x) = x^2 - 2\).

The seed: In \(\mathbb{F}_7\), \(3^2 = 9 \equiv 2 \pmod 7\). So our starting “horizontal” root is \(x_0 = 3,\) rendering \(f(x) = x^2 - 2\equiv0 \mod 7.\)

At the \(\mathbb{F}_7\) level, we have:

\[f(3) = 3^2 - 2 = 7\]

which in the finite field \(\mathbb{F}_7\) is exactly \(0\).

But to “lift” it, we look at it as an integer and see that it is \(7^1\). This is the “residual” that drives the next step.

Why?

We can think of the equation \(f(x) = x^2 - 2\) as a physical system. At \(x=3\), the system has an “energy” of \(7.\) If we reduce \(\pmod 7\), we are effectively saying, “we don’t care about any energy level higher than zero.” By not reducing, we are acknowledging that while \(3\) is a root at precision \(1,\) it is not a root at precision \(2.\) The \(7\) is the evidence that our \(x_0\) is slightly off-center from the “true” \(p\)-adic root. We use that \(7\) to calculate exactly how much to nudge \(x_0\) to make the error disappear at the next level (\(49\)).

We are now working modulo \(7^2\) (which is \(49\)). Our universe expands to \(\{0, 1, 2, \dots, 48\}\). Our number now looks like \(d_0 + d_1(7)\).

We want to find \(x_1 = 3 + d_1 \cdot 7\) such that \(f(x_1) \equiv 0 \pmod{49}\).

The derivative:

\[f'(x) = 2x \implies f'(3) = 2(3) = 6\]

We use the Taylor expansion formula:

\[f(3) + (d_1 \cdot 7) \cdot f'(3) \equiv 0 \pmod{49}\]

Substitute our values:

\[7 + (d_1 \cdot 7) \cdot 6 \equiv 0 \pmod{49}\]

Solve for the digit \(d_1\):

Divide the whole equation by \(7\) to isolate \(d_1\) in the “horizontal” world (\(\mathbb{F}_7\)):

\[1 + d_1 \cdot 6 \equiv 0 \pmod 7\]

\[d_1 \cdot 6 \equiv -1 \equiv 6 \pmod 7\]

\[d_1 = 1\]

The new coordinate:

\[x_1 = 3 + (1 \cdot 7) = 10\]

Check: \(10^2 - 2 = 98\). Since \(98 = 2 \times 49\), \(x_1 = 10\) is an exact root modulo \(49\).

The second lift (moving to \(7^3\)):

Now we want \(x_2 = 10 + d_2 \cdot 49\) such that \(f(x_2) \equiv 0 \pmod{343}\).

The derivative stays the same: \(f'(10) = 2(10) = 20\).

The linear equation:

\[f(10) + (d_2 \cdot 49) \cdot f'(10) \equiv 0 \pmod{343}\]

\[98 + (d_2 \cdot 49) \cdot 20 \equiv 0 \pmod{343}\]

Solving for \(d_2\):

Dividing by \(49\):

\[2 + d_2 \cdot 20 \equiv 0 \pmod 7\]

Since \(20 \equiv 6 \pmod 7\):

\[2 + d_2 \cdot 6 \equiv 0 \pmod 7\]

\[d_2 \cdot 6 \equiv -2 \equiv 5 \pmod 7\]

Looking for \(d_2\): \(6 \times 2 = 12 \equiv 5\). So \(d_2 = 2\).

The new coordinate:

\[x_2 = 10 + (2 \cdot 49) = 10 + 98 = 108\]

When we say \(108,\) we are writing the value in standard base-\(10\) so it’s easier for us to read. But under the hood, its \(p\)-adic structure in base-\(7,\) it is perfectly valid:

\[108 = (3 \times 7^0) + (1 \times 7^1) + (2 \times 7^2)\]

In base-\(7\) digits, \(108\) is written as \(213_7\).

Check: \(108^2 - 2 = 11664 - 2 = 11662\). \(11662 / 343 = 34\).

If we did this \(20\) times as in the SageMath code. We would have an \(x\)-coordinate like:

\[x_{final} = 3 + 1(7) + 2(7^2) + \dots\]

This is a high-precision \(p\)-adic integer, which will be the \(x\) coordinate of the point \(P=(108,y)\)

In SageMath:

from sage.all import Zp

# 1. Define the ring with 'series' print mode
# This ensures we see the + 7 + 7^2 format
Z7 = Zp(7, prec=3, type='capped-rel', print_mode='series')

# 2. Reconstruct the parts of our 108
# x_final = 3 + 1*(7^1) + 2*(7^2)
x0 = Z7(3)
d1 = Z7(1)
d2 = Z7(2)

# 3. Sum them into the full series
x_final = x0 + d1*7 + d2*49

# 4. Print the series and the integer value
print("The Full p-adic Series:")
print(x_final)

print("\nThe Base-10 Integer Value:")
print(x_final.lift())

---

OUTPUT:

The Full p-adic Series:
3 + 7 + 2*7^2 + O(7^3)

The Base-10 Integer Value:
108

The matrix step: we take this \(x = 108\) (and its corresponding \(y\)) and apply the Frobenius map:

\[\phi_7(108, y) = (108^7, y^7)=P'\]

The reason \(\phi_p(P)\) is guaranteed to be on the curve is because of the “freshman’s dream” property in finite fields. If \((x, y)\) satisfies:

\[y^2 = x^3 + Ax + B\]

Then, if we raise everything to the \(p\)-th power:

\[(y^2)^p = (x^3 + Ax + B)^p\]

In a field of characteristic \(p\), the \(p\)-th power distributes over addition:

\[(y^p)^2 = (x^p)^3 + A^p x^p + B^p\]

Since \(A\) and \(B\) are in the base field \(\mathbb{F}_p\), Fermat’s Little Theorem tells us \(A^p = A\) and \(B^p = B.\) So:

\[(y^p)^2 = (x^p)^3 + A(x^p) + B\]

This shows that the new coordinates \((x^p, y^p)\) satisfy the exact same curve equation. The Frobenius map is a “symmetry” of the curve; it moves points around, but it never knocks them off the track.

We apply Frobenius to \(P\). Since \(P\) and \(Q\) are our basis, the result \(\phi_p(P)\) must be some combination of them:

\[\phi_p(P) = aP + bQ\]

We apply Frobenius to \(Q\). The result \(\phi_p(Q)\) must also be a combination:

\[\phi_p(Q) = cP + dQ\]

How do we find \(a\) and \(b\) if we only have one equation for them? In the \(p\)-adic skyscraper, \(P\) and \(Q\) are like vectors in \(2\)-D space. Because we have their high-precision coordinates, we can use point addition math (the same secant-and-tangent rule, but done \(p\)-adically) to test different values of \(a\) and \(b\) until the equation balances. In practice, we often use an algorithm like baby-step giant-step or even simple linear algebra over the torsion group to find these integers. Because our precision is so high (\(20\) digits), there is only one set of integers \((a, b, c, d)\) that will make these equations work.

Once you’ve found those four \(p\)-adic strings, we put them into the grid:

\[\text{Frobenius Matrix} = \begin{pmatrix} a & b \\ c & d \end{pmatrix}\]

The trace (\(a_p\)) is the sum of the diagonal: \(a + d\).


🙊 Let the elliptic curve \(E\) be defined by the Weierstrass equation:

\[y^2 = x^3 + Ax + B\]

where the coefficients \(A\) and \(B\) are in the base field \(\mathbb{F}_p\). This means that \(A^p = A\) and \(B^p = B\) (by Fermat’s Little Theorem).

Suppose we have a point \(P = (x_0, y_0)\) that lives in some extension field \(\mathbb{F}_{p^k}\). If \(P\) is on the curve, it satisfies:

\[y_0^2 = x_0^3 + Ax_0 + B\]

Now, we apply the Frobenius map \(\phi(x, y) = (x^p, y^p)\) to this point. We want to see if the new point \((x_0^p, y_0^p)\) still satisfies the curve equation.

We take the entire equation of the curve and raise both sides to the power of \(p\):

\[(y_0^2)^p = (x_0^3 + Ax_0 + B)^p\]

Using the “freshman’s dream”, in characteristic \(p\), the map \((a+b)^p = a^p + b^p\) is a ring homomorphism. We can distribute the power of \(p\) across every term:

\[(y_0^p)^2 = (x_0^p)^3 + A^p x_0^p + B^p\]

Using the fixed-field property, since \(A\) and \(B\) are in \(\mathbb{F}_p\), we know \(A^p = A\) and \(B^p = B\). Substituting those back in, we get:

\[(y_0^p)^2 = (x_0^p)^3 + A(x_0^p) + B\]

This is exactly the equation for the point \((x_0^p, y_0^p)\) being on the curve. Therefore, if \(P \in E(\mathbb{F}_{p^k})\), then \(\phi(P) \in E(\mathbb{F}_{p^k})\).


🐓 Primitive element theorem: In the world of finite fields, every finite extension is simple, meaning that no matter how many different “ghosts” (\(\sqrt{2}, \sqrt{3}, \sqrt[3]{7}\), etc.) we want to include, we can always find one single “super-ghost” (\(\alpha\)) such that every other number in that field is just a polynomial expression of that \(\alpha\).

When we move to an extension of degree \(k\), we are essentially creating a \(k\)-dimensional coordinate system. In a degree-\(2\) extension (\(c_0 + c_1\alpha\)), we have a \(2\)D plane. In a degree-\(4\) extension (\(c_0 + c_1\alpha + c_2\alpha^2 + c_3\alpha^3\)), we have a \(4\)D space. The reason a higher degree “encompasses all needs” is that once the dimension is large enough, the “shadows” of other roots inevitably fall into that space. For example, \(\sqrt{2}\) and \(\sqrt{3}\) each call for \(2\) dimensions. If you create a \(4\)-dimensional world, there is enough “room” for both of them to exist as specific vectors in that \(4\)D space.

The real “magic” behind why one \(\alpha\) is enough is that the non-zero elements of a finite field \(\mathbb{F}_{p^k}\) form a cyclic group. This means there exists some element \(\alpha\) such that every non-zero element in the field is just a power of \(\alpha\):

\[\text{Field Elements} = \{0, \alpha^1, \alpha^2, \alpha^3, \dots, \alpha^{p^k-1}=1\}\]

If \(\sqrt{2}\) and \(\sqrt{3}\) are in the field, they are just \(\alpha^m\) and \(\alpha^n\) for some integers \(m\) and \(n\). Since every power \(\alpha^m\) can be reduced (using the defining polynomial) to a polynomial of degree less than \(k\), they both end up as:

\[\text{Root} = c_0 + c_1\alpha + \dots + c_{k-1}\alpha^{k-1}\]

If we wanted a cube root (\(\sqrt[3]{2}\)) and a square root (\(\sqrt{3}\)), the same logic applies, but the “room” we need changes. A square root needs degree \(2.\) A cube root needs degree \(3.\) To fit both, we need a field that is a multiple of both dimensions. We would create a degree-\(6\) extension (\(2 \times 3 = 6\)). In that \(6\)D space, one single degree-\(6\) “super-ghost” \(\alpha\) would allow you to express both the cube root and the square root.


🆘 In SEA we get the same blurry points in \(\mathbb F_{p^k}\) and then we lift them through Hensel. For Schoof, we get \(\ell\) blurry points in an extension of \(\mathbb F_{p^k}\) and get \(a_p\mod \ell.\) In addition, we get the counts for other \(\ell\)’s, such as \(\ell=5, 7, 11, \dots\). We start with small primes, like \(\ell=3,\) and the stopping point is guided by Hasse’s theorem. The Chinese remainder theorem is used to solve a system of modular equations and get \(a_p.\)

For any elliptic curve \(E\) over \(\mathbb{F}_p\), the Frobenius endomorphism \(\phi\) satisfies a characteristic polynomial: a quadratic equation in the endomorphism ring of the curve. The endomorphism ring of the curve (\(\text{End}(E)\)). It contains all possible geometric shuffles that can be performed on the entire curve. These maps work on every point \((x, y)\), regardless of whether that point is part of a torsion grid or not. It includes the Frobenius map \(\phi\) and all scalar multiplication maps \([n]\). The characteristic equation lives here and applies to every single point on the donut. The ring (\(\text{End}(E)\)) is a mathematical structure where we can add, subtract, and multiply elements. In the endomorphism ring, the “numbers” we are manipulating are actually the mappings themselves.

Addition: \((\alpha + \beta)(P) = \alpha(P) + \beta(P)\). You apply both maps and add the resulting points using the curve’s addition law.

Multiplication: \((\alpha \cdot \beta)(P) = \alpha(\beta(P))\). Multiplication is defined as composition—applying one map after the other.

The ring allows us to write equations like \(\phi^2 - a_p\phi + p = 0\) as if they were simple algebra. In this notation:\(\phi^2\) means \(\phi(\phi(P))\). \(p\) represents the scalar multiplication map \([p]\). \(0\) represents the map that sends every point to the point at infinity (\(\mathcal{O}\)).

\[\phi^2 - a_p\phi + p = 0\]

In this equation, \(a_p\) (the trace) is the mystery value we want to find. This equation is “characteristic” because it describes the behavior of every point \(P\) on the curve.

We are looking for the trace \(a_p=t\) of the Frobenius endomorphism \(\phi\) by observing its action on the \(\ell\)-torsion subgroups \(E[\ell]\).

While the equation is true for every \(\ell\), a single \(\ell\) only gives us a blurry piece of the answer (the remainder).

\(a_p \pmod 2 = k_1\)

\(a_p \pmod 3 = k_2\)

\(a_p \pmod 5 = k_3\)

…and so on.

The Role of Hasse’s Theorem \(|a_p| \le 2\sqrt{p}:\) We keep picking new \(\ell\)’s and solving the characteristic equation for each one until the product of all those primes

\[L = \ell_1 \cdot \ell_2 \cdot \dots\]

is larger than \(4\sqrt{p}\).

Once \(L > 4\sqrt{p}\), the Chinese Remainder Theorem glues those individual \(\ell\)-clues together to reveal the one and only integer \(a_p\) that fits in that Hasse box.

This ensures that once we know \(t \pmod L\) (where \(L = \prod \ell_i\)), and \(L > 4\sqrt{p}\), there is only one possible value for \(t\) in that range.

We use symbolic \(x\) and \(y\) (not actual values). Mathematically, we are working in a coordinate ring restricted by the curve and the torsion. We perform calculations in the ring:

\[R_\ell = \mathbb{F}_p[x, y] / (y^2 - (x^3 + Ax + B), \psi_\ell(x))\]

\(\mathbb{F}_p[x, y]:\) This represents the set of all possible polynomials using two variables, \(x\) and \(y\), with coefficients from your base field \(\mathbb{F}_p\). At this stage, \(x\) and \(y\) are completely free; they could represent any point in the entire infinite plane.

The first constraint: \((y^2 - (x^3 + Ax + B)).\) By “modding out” by the curve’s equation, we are forcing the variables to live on the curve.

The second constraint: \((\psi_\ell(x)).\) This is the division polynomial. Modding out by \(\psi_\ell(x)\) restricts our workspace further so that the “symbolic” point \((x, y)\) behaves exactly like an \(\ell\)-torsion point. Any identity that holds in \(R_\ell\) is guaranteed to hold for every point \(P \in E[\ell]\).

The characteristic polynomial: We are looking for the eigenvalue/trace \(k \in \mathbb{Z}/\ell\mathbb{Z}\) such that:

\[\phi^2 - k\phi + [p] = 0 \quad \text{in } \text{End}(E[\ell])\]

The ring \(\text{End}(E[\ell])\) is what we get when you take those global rules of \(\text{End}(E)\) and apply them only to the points in a specific \(\ell \times \ell\) grid. We are moving from the infinite curve to a finite \(2 \times 2\) matrix representation. Everything in this ring is reduced modulo \(\ell\). For example, the scalar \([p]\) in the global ring becomes the scalar \([p \pmod \ell]\) in this local ring.

Writing this out in terms of coordinates \((x, y) \in R_\ell\):

\[(x^{p^2}, y^{p^2}) + [p \pmod \ell](x, y) = [k](x^p, y^p)\]

\((x^{p^2}, y^{p^2})\): The action of \(\phi^2\).

\([p \pmod \ell]\): The scalar multiplication map by the remainder of \(p\) modulo \(\ell\).

\([k]\): The scalar multiplication map by our guess for the trace.

For each prime \(\ell\) (e.g., \(\ell = 2, 3, 5, 7, \dots\)), we construct a unique coordinate ring \(R_\ell\) using that specific \(\ell\)-th division polynomial \(\psi_\ell(x)\). Within that ring, we test the equation:

\[(x^{p^2}, y^{p^2}) + [p \pmod \ell](x, y) = [k](x^p, y^p)\]

We are searching for the specific value of \(k\) that makes this identity hold true for that specific \(\ell\). We check every candidate for \(k\) in the set \(\{0, 1, \dots, \ell-1\}\). Once the equation balances, you have successfully “captured” one modular clue: \(a_p \equiv k \pmod \ell\).

While each step gives us \(t \pmod \ell\), the Schoof algorithm is essentially computing the first digit of the \(\ell\)-adic expansion of the trace for several different primes. If we stayed with one prime \(\ell\) and looked at higher division polynomials \(\psi_{\ell^n}\), we would be working in the Tate Module:

\[T_\ell(E) = \lim_{\longleftarrow} E[\ell^n] \cong \mathbb{Z}_\ell \times \mathbb{Z}_\ell\]

In this view, \(\phi\) is a \(2 \times 2\) matrix with entries in \(\mathbb{Z}_\ell\).

The \(k\) we find is:

\[k \equiv \text{Tr}(\phi) \pmod \ell\]

Chinese Remainder Theorem: Finally, we have a system of congruences:

\[t \equiv k_1 \pmod{\ell_1}\]

\[t \equiv k_2 \pmod{\ell_2}\]

\[\dots\]

This is an isomorphism of rings:

\[\mathbb{Z}/L\mathbb{Z} \cong \prod \mathbb{Z}/\ell_i\mathbb{Z}\] By mapping our collection of \(k_i\) back through this isomorphism, we recover the unique \(t \in \mathbb{Z}\) that satisfies the Hasse bound.


Home Page

NOTE: These are tentative notes on different topics for personal use - expect mistakes and misunderstandings.