Choose a non-square D below. The tool runs the continued-fraction algorithm to find the fundamental (smallest) solution (x1, y1), then climbs the ladder: each new solution comes from the previous one by the rule
xk+1=x1cdotxk+Dcdoty1cdotyk,yk+1=x1cdotyk+y1cdotxk.
<p class="hint">Pick a non-square <b>D</b>. The tool finds the smallest (fundamental)
solution of <b>x² − D·y² = 1</b> using continued fractions, then climbs the ladder:
each new solution is built from the first by multiplication — no more searching.</p>
<div class="controls">
<label>D =
<select id="d">
<option value="2">2</option>
<option value="3">3</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="13">13</option>
<option value="61">61</option>
</select>
</label>
<button id="gen" type="button">Find & generate</button>
<button id="more" type="button" class="ghost">+ next solution</button>
</div>
<div class="status" id="status">Choose a D and press "Find & generate".</div>
<table id="tbl" class="tbl"><thead><tr><th>k</th><th>x</th><th>y</th><th>x² − D·y²</th></tr></thead><tbody></tbody></table>
* { 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; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: .4rem 0; }
label { font-weight: 600; font-size: .95rem; }
select { font: 600 14px system-ui, sans-serif; padding: .35rem .5rem; border-radius: 8px; border: 1px solid #1d3557; }
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; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; color: #0a7d33; }
.status.bad { color: #c92f3c; }
.tbl { border-collapse: collapse; width: 100%; font: 500 14px ui-monospace, monospace; }
.tbl th, .tbl td { border: 1px solid #cdd9e3; padding: .35rem .55rem; text-align: right; }
.tbl th { background: #e8eef3; color: #1d3557; }
.tbl td:first-child, .tbl th:first-child { text-align: center; width: 2.2rem; }
.tbl tr.fund td { background: #eaf6ee; font-weight: 700; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Pell's equation x² - D y² = 1. For non-square D there are infinitely many
integer solutions (Lagrange, 1768). The fundamental (smallest) one is read
off the periodic continued fraction of √D; every other solution is a power
of it via xₖ₊₁ = x₁xₖ + D y₁yₖ, yₖ₊₁ = x₁yₖ + y₁xₖ. We use BigInt so the
huge D=61 case (x₁ = 1766319049) stays exact. */
function fundamental(D){
/* Continued-fraction expansion of √D; convergents h/k. First convergent with
h² - D k² = 1 is the fundamental solution. */
const a0 = Math.floor(Math.sqrt(D));
if(a0*a0 === D) return null; // perfect square: no solution
let m = 0, d = 1, a = a0;
let hPrev = 1n, h = BigInt(a0); // numerators
let kPrev = 0n, k = 1n; // denominators
const Db = BigInt(D);
// check the 0-th convergent first (rare, but safe)
if(h*h - Db*k*k === 1n) return { x: h, y: k };
for(let i=0;i<2000;i++){
m = d*a - m;
d = (D - m*m)/d;
a = Math.floor((a0 + m)/d);
const hNext = BigInt(a)*h + hPrev;
const kNext = BigInt(a)*k + kPrev;
hPrev = h; h = hNext;
kPrev = k; k = kNext;
if(h*h - Db*k*k === 1n) return { x: h, y: k };
}
return null;
}
let D = 2, fund = null, cur = null, idx = 0;
const $ = id => document.getElementById(id);
function residual(x,y){ return x*x - BigInt(D)*y*y; }
function addRow(k,x,y){
const tb = $('tbl').querySelector('tbody');
const tr = document.createElement('tr');
if(k===1) tr.className = 'fund';
tr.innerHTML = '<td>'+k+'</td><td>'+x.toString()+'</td><td>'+y.toString()+'</td><td>'+residual(x,y).toString()+'</td>';
tb.appendChild(tr);
}
function setStatus(t,bad){ const s=$('status'); s.textContent=t; s.className='status'+(bad?' bad':''); }
function generate(){
D = parseInt($('d').value,10);
$('tbl').querySelector('tbody').innerHTML='';
fund = fundamental(D);
if(!fund){ setStatus(fmt(`D = %D% is a perfect square — no solution.`, { D: D }), true); cur=null; return; }
cur = { x: fund.x, y: fund.y }; idx = 1;
addRow(1, cur.x, cur.y);
setStatus(fmt(`Fundamental solution for D = %D%: x₁ = %x%, y₁ = %y%. Press "+ next solution".`, { D: D, x: fund.x, y: fund.y }));
console.log(fmt(`D=%D% fundamental x1=%x% y1=%y% residual=%residual%`, { D: D, x: fund.x, y: fund.y, residual: residual(fund.x,fund.y) }));
}
function next(){
if(!fund){ generate(); return; }
// (xₖ₊₁, yₖ₊₁) = (x₁xₖ + D y₁yₖ, x₁yₖ + y₁xₖ)
const Db = BigInt(D);
const nx = fund.x*cur.x + Db*fund.y*cur.y;
const ny = fund.x*cur.y + fund.y*cur.x;
cur = { x: nx, y: ny }; idx++;
addRow(idx, cur.x, cur.y);
setStatus(fmt(`Solution #%idx% generated by one multiplication — still exact.`, { idx: idx }));
}
$('gen').addEventListener('click', generate);
$('more').addEventListener('click', next);
generate();
<p class="hint">Pick a non-square <b>D</b>. The tool finds the smallest (fundamental)
solution of <b>x² − D·y² = 1</b> using continued fractions, then climbs the ladder:
each new solution is built from the first by multiplication — no more searching.</p>
<div class="controls">
<label>D =
<select id="d">
<option value="2">2</option>
<option value="3">3</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="13">13</option>
<option value="61">61</option>
</select>
</label>
<button id="gen" type="button">Find & generate</button>
<button id="more" type="button" class="ghost">+ next solution</button>
</div>
<div class="status" id="status">Choose a D and press "Find & generate".</div>
<table id="tbl" class="tbl"><thead><tr><th>k</th><th>x</th><th>y</th><th>x² − D·y²</th></tr></thead><tbody></tbody></table>
* { 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; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: .4rem 0; }
label { font-weight: 600; font-size: .95rem; }
select { font: 600 14px system-ui, sans-serif; padding: .35rem .5rem; border-radius: 8px; border: 1px solid #1d3557; }
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; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; color: #0a7d33; }
.status.bad { color: #c92f3c; }
.tbl { border-collapse: collapse; width: 100%; font: 500 14px ui-monospace, monospace; }
.tbl th, .tbl td { border: 1px solid #cdd9e3; padding: .35rem .55rem; text-align: right; }
.tbl th { background: #e8eef3; color: #1d3557; }
.tbl td:first-child, .tbl th:first-child { text-align: center; width: 2.2rem; }
.tbl tr.fund td { background: #eaf6ee; font-weight: 700; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Pell's equation x² - D y² = 1. For non-square D there are infinitely many
integer solutions (Lagrange, 1768). The fundamental (smallest) one is read
off the periodic continued fraction of √D; every other solution is a power
of it via xₖ₊₁ = x₁xₖ + D y₁yₖ, yₖ₊₁ = x₁yₖ + y₁xₖ. We use BigInt so the
huge D=61 case (x₁ = 1766319049) stays exact. */
function fundamental(D){
/* Continued-fraction expansion of √D; convergents h/k. First convergent with
h² - D k² = 1 is the fundamental solution. */
const a0 = Math.floor(Math.sqrt(D));
if(a0*a0 === D) return null; // perfect square: no solution
let m = 0, d = 1, a = a0;
let hPrev = 1n, h = BigInt(a0); // numerators
let kPrev = 0n, k = 1n; // denominators
const Db = BigInt(D);
// check the 0-th convergent first (rare, but safe)
if(h*h - Db*k*k === 1n) return { x: h, y: k };
for(let i=0;i<2000;i++){
m = d*a - m;
d = (D - m*m)/d;
a = Math.floor((a0 + m)/d);
const hNext = BigInt(a)*h + hPrev;
const kNext = BigInt(a)*k + kPrev;
hPrev = h; h = hNext;
kPrev = k; k = kNext;
if(h*h - Db*k*k === 1n) return { x: h, y: k };
}
return null;
}
let D = 2, fund = null, cur = null, idx = 0;
const $ = id => document.getElementById(id);
function residual(x,y){ return x*x - BigInt(D)*y*y; }
function addRow(k,x,y){
const tb = $('tbl').querySelector('tbody');
const tr = document.createElement('tr');
if(k===1) tr.className = 'fund';
tr.innerHTML = '<td>'+k+'</td><td>'+x.toString()+'</td><td>'+y.toString()+'</td><td>'+residual(x,y).toString()+'</td>';
tb.appendChild(tr);
}
function setStatus(t,bad){ const s=$('status'); s.textContent=t; s.className='status'+(bad?' bad':''); }
function generate(){
D = parseInt($('d').value,10);
$('tbl').querySelector('tbody').innerHTML='';
fund = fundamental(D);
if(!fund){ setStatus(fmt(`D = %D% is a perfect square — no solution.`, { D: D }), true); cur=null; return; }
cur = { x: fund.x, y: fund.y }; idx = 1;
addRow(1, cur.x, cur.y);
setStatus(fmt(`Fundamental solution for D = %D%: x₁ = %x%, y₁ = %y%. Press "+ next solution".`, { D: D, x: fund.x, y: fund.y }));
console.log(fmt(`D=%D% fundamental x1=%x% y1=%y% residual=%residual%`, { D: D, x: fund.x, y: fund.y, residual: residual(fund.x,fund.y) }));
}
function next(){
if(!fund){ generate(); return; }
// (xₖ₊₁, yₖ₊₁) = (x₁xₖ + D y₁yₖ, x₁yₖ + y₁xₖ)
const Db = BigInt(D);
const nx = fund.x*cur.x + Db*fund.y*cur.y;
const ny = fund.x*cur.y + fund.y*cur.x;
cur = { x: nx, y: ny }; idx++;
addRow(idx, cur.x, cur.y);
setStatus(fmt(`Solution #%idx% generated by one multiplication — still exact.`, { idx: idx }));
}
$('gen').addEventListener('click', generate);
$('more').addEventListener('click', next);
generate();
Watch what happens. The first solution can be small (D = 2 gives 32 − 2·22 = 1) or alarming (D = 61 gives x1 = 1766319049). Yet once you have it, every other solution falls out by simple multiplication — no further searching. Notice the asymmetry: checking that x2 − Dy2 = 1 is one multiplication, but finding that first pair by brute force could take astronomically many tries.
Comments
Loading comments...