
randpool tester
Run the recurrence ( x = x * x ) and ( y = x * x - y * y ) for a BigInt number of iterations. Optionally reduce values modulo a modulus to keep sizes bounded.
??
??
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>randpool tester</title>
<style>
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial; max-width: 900px; margin: 32px auto; padding: 0 16px; color:#111; }
label { display:block; margin-top:12px; font-weight:600; }
input, select, textarea { width:100%; padding:8px; margin-top:6px; box-sizing:border-box; font-family:monospace; }
button { margin-top:12px; padding:8px 12px; }
.row { display:flex; gap:12px; align-items:center; }
.col { flex:1; }
pre { background:#f6f8fa; padding:12px; overflow:auto; }
.error { color:#b00020; font-weight:600; margin-top:8px; }
.meta { color:#555; font-size:0.9rem; margin-top:6px; }
</style>
</head>
<body>
<h1>randpool tester</h1>
<p>Run the recurrence ( <code>x = x * x</code> ) and ( <code>y = x * x - y * y</code> ) for a BigInt number of iterations. Optionally reduce values modulo a modulus to keep sizes bounded.</p>
<label>Count (BigInt)
<input id="count" placeholder="e.g. 0, 1, 10, 100" value="4" />
</label>
<div class="row">
<div class="col">
<label>startX (BigInt)
<input id="startX" placeholder="10" value="10" />
</label>
</div>
<div class="col">
<label>startY (BigInt)
<input id="startY" placeholder="1" value="1" />
</label>
</div>
</div>
<label>modulus (optional BigInt)
<input id="modulus" placeholder="e.g. 1 << 256" />
<div class="meta">empty to allow unbounded growth (may be slow or crash for large counts).</div>
</label>
<div class="row">
<label>reduceInterval(positive integer)
<input id="reduceInterval" value="1" />
</label>
</div>
<div class="row">
<label>show as
<select id="format">
<option value="dec">decimal (BigInt)</option>
<option value="hex">hex</option>
<option value="bytes">hex bytes (fixed width if modulus is power-of-two)</option>
</select>
</label>
</div>
<div class="row">
<button id="run">Run</button>
<button id="copy">Copy result</button>
<button id="example">Load examples</button>
</div>
<div id="error" class="error" role="alert" style="display:none"></div>
<label>Result</label>
<pre id="result">??</pre>
<label>Details</label>
<pre id="details">??</pre>
<script>
// --- Utilities ---
function parseBigIntInput(raw) {
if (raw === null || raw === undefined || raw.trim() === '') return undefined;
const s = raw.trim();
// Accept forms like "10n", "10", "0b101", "0xFF", "1 << 256", "1n << 256n"
// We'll try a few safe transforms; avoid eval on arbitrary input.
// Replace trailing 'n' if present, then try BigInt directly.
try {
// Allow simple shift expressions like "1 << 256" or "1n << 256n"
if (s.includes('<<')) {
const parts = s.split('<<').map(p => p.trim().replace(/n$/,''));
if (parts.length === 2 && /^\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
return 1n << BigInt(parts[1]);
}
}
// Allow exponent form "2n**127n" or "2**127"
if (s.includes('**')) {
const parts = s.split('**').map(p => p.trim().replace(/n$/,''));
if (parts.length === 2 && /^\d+$/.test(parts[0]) && /^\d+$/.test(parts[1])) {
return BigInt(parts[0]) ** BigInt(parts[1]);
}
}
// Strip trailing n and parse common prefixes
const cleaned = s.endsWith('n') ? s.slice(0, -1) : s;
if (/^0x[0-9a-fA-F]+$/.test(cleaned) || /^0b[01]+$/.test(cleaned) || /^\d+$/.test(cleaned)) {
return BigInt(cleaned);
}
// As a last resort, try BigInt on the raw string (handles "123", "0xFF", "0b101")
return BigInt(s);
} catch (e) {
throw new TypeError('Invalid BigInt expression: ' + s);
}
}
function isPowerOfTwoBigInt(n) {
return n > 0n && (n & (n - 1n)) === 0n;
}
// Modular reducer factory
function makeModReducer(modulus) {
if (modulus === undefined) return (v) => v;
if (isPowerOfTwoBigInt(modulus)) {
// compute width k where modulus === 1n << k
let m = modulus;
let k = 0;
while (m > 1n) { m >>= 1n; k++; }
return (v) => BigInt.asUintN(k, v);
}
return (v) => {
const r = v % modulus;
return r >= 0n ? r : r + modulus;
};
}
// randpool implementation with modulus and reduceInterval
function randpoolBig(count, startX, startY, modulus, reduceInterval) {
if (typeof count !== 'bigint') throw new TypeError('count must be a BigInt');
if (count < 0n) throw new RangeError('count must be non-negative');
if (typeof startX !== 'bigint' || typeof startY !== 'bigint') {
throw new TypeError('startX and startY must be BigInt');
}
if (modulus !== undefined && (typeof modulus !== 'bigint' || modulus <= 1n)) {
throw new TypeError('modulus must be a BigInt > 1n when provided');
}
if (!Number.isInteger(reduceInterval) || reduceInterval <= 0) {
throw new TypeError('reduceInterval must be a positive integer');
}
const modReduce = makeModReducer(modulus);
// Fast path when count fits in Number
if (count <= BigInt(Number.MAX_SAFE_INTEGER)) {
return randpoolSmall(Number(count), startX, startY, modReduce, reduceInterval);
}
let x = startX;
let y = startY;
let remaining = count;
let iter = 0;
while (remaining > 0n) {
const xNext = x * x;
const yNext = xNext - y * y;
x = xNext;
y = yNext;
remaining -= 1n;
iter += 1;
if (modulus !== undefined && (iter % reduceInterval === 0)) {
x = modReduce(x);
y = modReduce(y);
}
}
return modulus === undefined ? y : modReduce(y);
}
function randpoolSmall(countNum, startX, startY, modReduce, reduceInterval) {
let x = startX;
let y = startY;
for (let i = 0; i < countNum; i++) {
const xNext = x * x;
const yNext = xNext - y * y;
x = xNext;
y = yNext;
if (modReduce !== undefined && ((i + 1) % reduceInterval === 0)) {
x = modReduce(x);
y = modReduce(y);
}
}
return modReduce !== undefined ? modReduce(y) : y;
}
// Format helpers
function toHex(big) {
if (big === 0n) return '0x0';
const neg = big < 0n;
const v = neg ? -big : big;
return (neg ? '-0x' : '0x') + v.toString(16);
}
function toHexBytes(big, fixedWidthBytes) {
// produce hex bytes string like "00 1f a3 ..."
const v = big < 0n ? -big : big;
let hex = v.toString(16);
if (hex.length % 2) hex = '0' + hex;
if (fixedWidthBytes) {
const needed = fixedWidthBytes * 2;
if (hex.length < needed) hex = hex.padStart(needed, '0');
else if (hex.length > needed) hex = hex.slice(-needed); // keep low-order bytes
}
const pairs = hex.match(/.{1,2}/g) || [];
return pairs.join(' ');
}
// UI wiring
const el = {
count: document.getElementById('count'),
startX: document.getElementById('startX'),
startY: document.getElementById('startY'),
modulus: document.getElementById('modulus'),
reduceInterval: document.getElementById('reduceInterval'),
run: document.getElementById('run'),
result: document.getElementById('result'),
details: document.getElementById('details'),
error: document.getElementById('error'),
copy: document.getElementById('copy'),
format: document.getElementById('format'),
example: document.getElementById('example'),
};
function showError(msg) {
el.error.style.display = 'block';
el.error.textContent = msg;
}
function clearError() {
el.error.style.display = 'none';
el.error.textContent = '';
}
el.run.addEventListener('click', () => {
clearError();
el.result.textContent = 'Running…';
el.details.textContent = '';
try {
const count = parseBigIntInput(el.count.value);
if (count === undefined) throw new TypeError('count is required');
const startX = parseBigIntInput(el.startX.value) ?? 10n;
const startY = parseBigIntInput(el.startY.value) ?? 1n;
const modulus = parseBigIntInput(el.modulus.value); // may be undefined
const reduceInterval = parseInt(el.reduceInterval.value, 10) || 1;
const out = randpoolBig(count, startX, startY, modulus, reduceInterval);
const fmt = el.format.value;
let display;
if (fmt === 'dec') {
display = out.toString();
} else if (fmt === 'hex') {
display = toHex(out);
} else { // bytes
let fixedBytes;
if (modulus !== undefined && isPowerOfTwoBigInt(modulus)) {
// compute width in bytes
let m = modulus;
let k = 0;
while (m > 1n) { m >>= 1n; k++; }
fixedBytes = Math.ceil(k / 8);
}
display = toHexBytes(out, fixedBytes);
}
el.result.textContent = display;
// details
const details = [
`count: ${count} (${typeof count})`,
`startX: ${startX}`,
`startY: ${startY}`,
`modulus: ${modulus === undefined ? 'none' : modulus}`,
`reduceInterval: ${reduceInterval}`,
`bitLength (approx): ${out === 0n ? 0 : out.toString(2).length}`,
].join('\n');
el.details.textContent = details;
} catch (err) {
el.result.textContent = '—';
showError(err && err.message ? err.message : String(err));
}
});
el.copy.addEventListener('click', async () => {
try {
const text = el.result.textContent;
if (!text || text === '—') return;
await navigator.clipboard.writeText(text);
el.copy.textContent = 'Copied';
setTimeout(() => el.copy.textContent = 'Copy result', 1200);
} catch (e) {
showError('Copy failed: ' + (e && e.message ? e.message : String(e)));
}
});
el.example.addEventListener('click', () => {
// load a few example inputs
el.count.value = '10';
el.startX.value = '10';
el.startY.value = '1';
el.modulus.value = '1 << 256';
el.reduceInterval.value = '1';
el.format.value = 'hex';
})
</script>

