ZETT

unlicense

randpool tester

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.

empty to allow unbounded growth (may be slow or crash for large counts).

??

??
<!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>

zelah

zombie

DrunkEliza