Introduction

An elliptic curve is one of the friendliest objects in number theory: the set of points satisfying an equation like y2y^{2} = x3x^{3} + ax + b. Draw it and you get a smooth, looping curve. The interesting question is not where the curve goes, but which of its points have rational coordinates — both x and y are ordinary fractions.

Here is the mystery. Some curves carry only a handful of rational points. Others carry infinitely many, all generated from just a few "starting" points by a beautiful addition law. The number of independent starting points you need is called the rank of the curve. Rank 0 means essentially no rational points; rank 1, 2, 3… means richer and richer families.

So: given a curve, how many independent rational points does it have? That single number is shockingly hard to pin down — and a million-dollar conjecture says the answer is hiding somewhere you would never expect.

Count the Points

Below is an elliptic curve plotted over the real numbers. The highlighted dots are its rational points — points whose coordinates are honest fractions. Choose a curve and watch how many appear. Then use the group law: pick two rational points, draw the line through them, and the third place it crosses the curve (reflected over the x-axis) is another rational point.

<p class="hint">An elliptic curve <b>y² = x³ + ax + b</b> over the rationals. The dots are <b>rational points</b>. Pick a curve, then press <b>Add the generator (P + P + …)</b> to use the group law: each step produces a brand-new rational point. A rank-0 curve runs out fast; a rank-1 curve never does.</p>
<div class="curves">
  <button data-c="0" class="csel active" type="button">y² = x³ − x &nbsp;(rank 0)</button>
  <button data-c="1" class="csel" type="button">y² = x³ + 1 &nbsp;(rank 0)</button>
  <button data-c="2" class="csel" type="button">y² = x³ − 2 &nbsp;(rank 1)</button>
</div>
<canvas id="plot" width="360" height="300"></canvas>
<div class="status" id="status">Choose a curve and add its generator.</div>
<div class="btns">
  <button id="step" type="button">Add the generator (P + P + …)</button>
  <button id="reset" type="button" class="ghost">Reset</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.curves { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .5rem; }
.csel { font: 600 13px system-ui, sans-serif; padding: .35rem .6rem; border: 1px solid #cdd9e3;
        background: #e8eef3; color: #1d3557; border-radius: 8px; cursor: pointer; }
.csel.active { background: #1d3557; color: #fff; border-color: #1d3557; }
canvas { background: #fbfcfe; border: 1px solid #cdd9e3; border-radius: 8px; display: block; margin: .3rem 0; }
.status { font-size: .95rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; line-height: 1.4; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Birch & Swinnerton-Dyer demo: an elliptic curve y^2 = x^3 + a x + b over Q.
// We start from a known generator P and repeatedly compute P, 2P, 3P, ... with the
// exact group law in rational arithmetic. A rank-1 curve yields endless new rational points; a rank-0 curve only has finitely many (torsion), so the orbit cycles / hits O.
//
// The conjecture: rank == order of vanishing of L(E,s) at s=1 (open Millennium problem).

// --- exact rational arithmetic with BigInt fractions ---
function gcd(a,b){ a=a<0n?-a:a; b=b<0n?-b:b; while(b){ [a,b]=[b,a%b]; } return a; }
function fr(n,d){ if(d<0n){n=-n;d=-d;} const g=gcd(n,d)||1n; return [n/g, d/g]; }
function add(p,q){ return fr(p[0]*q[1]+q[0]*p[1], p[1]*q[1]); }
function sub(p,q){ return fr(p[0]*q[1]-q[0]*p[1], p[1]*q[1]); }
function mul(p,q){ return fr(p[0]*q[0], p[1]*q[1]); }
function div(p,q){ return fr(p[0]*q[1], p[1]*q[0]); }
function eq(p,q){ return p[0]===q[0] && p[1]===q[1]; }
function isZero(p){ return p[0]===0n; }
function toNum(p){ return Number(p[0]) / Number(p[1]); }
function show(p){ return p[1]===1n ? p[0].toString() : p[0].toString()+'/'+p[1].toString(); }

// Curves: a, b (integers), and a starting generator P (or null = no nontrivial point).
const Z = (n)=>[BigInt(n),1n];
const CURVES = [
  // y^2 = x^3 - x : rank 0. Only torsion: (0,0),(1,0),(-1,0) and O. 2P = O for each.
  { a:-1n, b:0n, gen:[Z(0), Z(0)], rank:0 },
  // y^2 = x^3 + 1 : rank 0. Torsion of order 6, generator (2,3). Orbit is finite.
  { a:0n, b:1n, gen:[Z(2), Z(3)], rank:0 },
  // y^2 = x^3 - 2 : rank 1. Generator (3,5): 3^3-2 = 25 = 5^2. Endless new points.
  { a:0n, b:-2n, gen:[Z(3), Z(5)], rank:1 },
];

let curve = CURVES[0];
let P = null;        // running multiple kP, as {x:fr,y:fr} or null = O
let k = 0;           // how many times we've added the generator
let history = [];    // list of rational points found

function onCurve(x,y){ // y^2 == x^3 + a x + b  (exact)
  const lhs = mul(y,y);
  const rhs = add(add(mul(mul(x,x),x), mul([curve.a,1n], x)), [curve.b,1n]);
  return eq(lhs, rhs);
}

// Group law: returns {x,y} or null (= point at infinity O).
function ecAdd(Pp, Qp){
  if(Pp===null) return Qp;
  if(Qp===null) return Pp;
  // P + (-P) = O
  if(eq(Pp.x,Qp.x) && eq(Pp.y, fr(-Qp.y[0],Qp.y[1]))) return null;
  let m;
  if(eq(Pp.x,Qp.x) && eq(Pp.y,Qp.y)){
    if(isZero(Pp.y)) return null;            // vertical tangent -> O (2-torsion)
    // m = (3x^2 + a) / (2y)
    m = div(add(mul(Z(3), mul(Pp.x,Pp.x)), [curve.a,1n]), mul(Z(2), Pp.y));
  } else {
    m = div(sub(Qp.y,Pp.y), sub(Qp.x,Pp.x)); // chord slope
  }
  const x3 = sub(sub(mul(m,m), Pp.x), Qp.x);
  const y3 = sub(mul(m, sub(Pp.x, x3)), Pp.y);
  return { x:x3, y:y3 };
}

const cv = document.getElementById('plot');
const ctx = cv.getContext('2d');
const W = cv.width, H = cv.height, SCALE = 28, OX = W/2, OY = H/2;
const sx = (x)=> OX + x*SCALE;
const sy = (y)=> OY - y*SCALE;

function drawAxes(){
  ctx.clearRect(0,0,W,H);
  ctx.strokeStyle = '#dce5ed'; ctx.lineWidth = 1;
  for(let gx=-6;gx<=6;gx++){ ctx.beginPath(); ctx.moveTo(sx(gx),0); ctx.lineTo(sx(gx),H); ctx.stroke(); }
  for(let gy=-5;gy<=5;gy++){ ctx.beginPath(); ctx.moveTo(0,sy(gy)); ctx.lineTo(W,sy(gy)); ctx.stroke(); }
  ctx.strokeStyle = '#9fb2c8'; ctx.lineWidth = 1.4;
  ctx.beginPath(); ctx.moveTo(0,sy(0)); ctx.lineTo(W,sy(0)); ctx.stroke();
  ctx.beginPath(); ctx.moveTo(sx(0),0); ctx.lineTo(sx(0),H); ctx.stroke();
}
function drawCurve(){
  const a = Number(curve.a), b = Number(curve.b);
  ctx.strokeStyle = '#1d3557'; ctx.lineWidth = 2;
  for(const sign of [1,-1]){
    ctx.beginPath(); let started=false;
    for(let px=0; px<=W; px++){
      const x = (px-OX)/SCALE; const r = x*x*x + a*x + b;
      if(r<0){ started=false; continue; }
      const y = sign*Math.sqrt(r); const py = sy(y);
      if(!started){ ctx.moveTo(px,py); started=true; } else ctx.lineTo(px,py);
    }
    ctx.stroke();
  }
}
function dot(x,y,color,rad){
  ctx.beginPath(); ctx.arc(sx(x),sy(y),rad,0,7); ctx.fillStyle=color; ctx.fill();
  ctx.strokeStyle='#fff'; ctx.lineWidth=1.2; ctx.stroke();
}
function redraw(){
  drawAxes(); drawCurve();
  history.forEach((pt,i)=>{ if(pt) dot(toNum(pt.x),toNum(pt.y), i===history.length-1?'#e63946':'#0a7d33', i===history.length-1?6:4.5); });
}

function setStatus(t,cls){ const s=document.getElementById('status'); s.innerHTML=t; s.className='status '+(cls||''); }

function selectCurve(idx){
  curve = CURVES[idx]; P=null; k=0; history=[];
  document.querySelectorAll('.csel').forEach((bn,i)=>bn.classList.toggle('active', i===idx));
  redraw();
  setStatus('Curve loaded. Press the button to add its generator.', '');
}

function step(){
  const g = { x:curve.gen[0], y:curve.gen[1] };
  if(!onCurve(g.x,g.y)){ setStatus('Generator off curve (bug).','bad'); return; }
  P = ecAdd(P, g); k++;
  if(P===null){
    history.push(null); redraw();
    setStatus(fmt('%k%·P = O (the point at infinity). This curve is <b>rank 0</b> — its rational points are a finite torsion set. Reset to try a rank-1 curve.', { k: k }),'bad');
    return;
  }
  // duplicate? (torsion cycle)
  const dup = history.some(h=>h && eq(h.x,P.x) && eq(h.y,P.y));
  history.push({x:P.x,y:P.y});
  redraw();
  const label = k+'·P = ( '+show(P.x)+' , '+show(P.y)+' )';
  if(dup){
    setStatus(fmt('%label% — already seen. The orbit <b>cycles</b>: finitely many rational points, <b>rank 0</b>.', { label: label }),'bad');
  } else if(curve.rank===0){
    setStatus(fmt('%label% (still torsion; will close up soon).', { label: label }), '');
  } else {
    setStatus(fmt('%label% — a <b>new</b> rational point. Numerators and denominators keep growing: <b>rank 1</b>, infinitely many points.', { label: label }),'ok');
  }
  console.log(label);
}

document.querySelectorAll('.csel').forEach((bn)=> bn.addEventListener('click',()=>selectCurve(+bn.dataset.c)));
document.getElementById('step').addEventListener('click', step);
document.getElementById('reset').addEventListener('click', ()=>selectCurve(CURVES.indexOf(curve)));
selectCurve(0);
Result
Console

Notice the pattern. A rank-0 curve runs out of points almost immediately — a few torsion points and nothing more. A rank-1 curve never runs out: keep adding a generator to itself and fresh rational points keep appearing forever. The conjecture of Birch and Swinnerton-Dyer claims this infinite-or-not behavior is mirrored exactly by a completely different object — the curve's L-function.

The Real Complexity

How hard is it to know the rank of a curve? Hard enough to be a Millennium Prize Problem, one of the seven $1,000,000 questions named by the Clay Mathematics Institute in 2000.

  • The geometric side is the rank r: the number of independent rational points that generate all the others. It is what our demo gropes toward by adding points.
  • The analytic side is an L-function L(E, s), built from how many solutions the curve has modulo each prime p. It packages all that local data into one analytic function of a complex variable s.
  • The conjecture, stated by Bryan Birch and Peter Swinnerton-Dyer in 1965 from extensive computer experiments, says these two sides match exactly: the rank equals the order of vanishing of L(E, s) at s = 1. If L doesn't vanish there, rank 0; if it vanishes to order one, rank 1; and so on.
  • Status: open. Only partial results are proved. Thanks to work by Coates–Wiles, Gross–Zagier, and Kolyvagin, the conjecture is known when the order of vanishing is 0 or 1 — but the general case, and the full refined formula with the mysterious Tate–Shafarevich group, remain unproven.

That is the punchline: a question as concrete as "how many rational points?" is conjecturally answered by an object from a totally different universe, and proving they truly agree is one of the deepest open problems in mathematics — a cousin in spirit to P vs NP, where a clean statement guards an enormous difficulty.

Where It Matters

Elliptic curves are not an exotic curiosity — they sit at the center of modern mathematics and technology:

  • Cryptography: elliptic-curve cryptography (ECC) secures messaging apps, TLS connections and cryptocurrencies, all built on the arithmetic of points on these curves.
  • Fermat's Last Theorem: Andrew Wiles' 1994 proof ran entirely through elliptic curves and the modularity theorem — the same machinery that gives L-functions their meaning.
  • The congruent number problem: deciding which integers are the area of a right triangle with rational sides reduces directly to the rank of an elliptic curve, so Birch–Swinnerton-Dyer would settle a question older than a thousand years.
  • Computational number theory: ranking and tabulating curves drives huge databases like the LMFDB, where the conjecture is tested against millions of examples.

Understand why counting points is hard and you've met the heart of arithmetic geometry — the same field behind factoring and the security of the keys protecting your data.

Conclusion

The Birch and Swinnerton-Dyer conjecture hides a beautiful secret: the geometry of an elliptic curve — how many rational points it carries — is conjecturally written, letter for letter, in the analysis of its L-function at the single point s = 1. Counting points is concrete; the bridge to L-functions is profound.

So far we can build the bridge only at its low end, for rank 0 and rank 1. The full span has stood open since 1965, a $1,000,000 Millennium Prize still unclaimed. The next time a curve hands you point after rational point with no end in sight, remember — you are watching, in plain fractions, one of the deepest unproven truths in mathematics, much like P vs NP hiding behind an innocent question.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/birch-swinnerton-dyer/Content licensed under CC BY-NC 4.0.