All files / js isaac64.js

92.89% Statements 183/197
88% Branches 22/25
88.88% Functions 8/9
92.89% Lines 183/197

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 19873x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 1105408x 1105408x 73x 73x 73x 1105408x 1105408x 73x 73x 73x 29376x 29376x 117504x 117504x 117504x 117504x 117504x 117504x 117504x 117504x 29376x 73x 73x 73x 4318x 4318x 4318x 4318x 4318x 4318x 4318x 4318x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 4318x 4318x 4318x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 138176x 4318x 4318x 4318x 4318x 4318x 73x 73x 73x 432x 432x 432x 432x 432x 432x 432x 432x 432x 432x 432x 73x 73x 73x 432x 432x 432x 432x 432x 432x 432x 432x 432x 432x 3456x 3456x 432x 432x 432x 432x 432x 432x             432x 432x 432x 432x 432x 432x 432x 432x 432x 13824x 13824x 13824x 13824x 432x 432x 432x 13824x 13824x 13824x 13824x 432x 432x 432x 432x 73x 73x 73x 73x 1003894x 1003894x 1003894x 73x 73x 73x 73x 73x 2237x 2237x 2237x 73x 73x 73x 73x                  
// isaac64.js -- ISAAC64 PRNG (public domain)
// Faithful port of nethack-c/src/isaac64.c by Timothy B. Terriberry.
// Uses BigInt for 64-bit unsigned integer arithmetic.
// cf. isaac64.c — ISAAC64 cryptographic PRNG implementation
 
const MASK = 0xFFFFFFFFFFFFFFFFn;
const SZ_LOG = 8;
const SZ = 256;               // 1 << SZ_LOG
const SEED_SZ_MAX = SZ * 8;   // 2048
const GOLDEN = 0x9E3779B97F4A7C13n;
 
// cf. isaac64.c:39 — extract SZ_LOG bits starting at bit 3
export function lower_bits(x) {
    return Number((x >> 3n) & 0xFFn);
}
 
// cf. isaac64.c:45 — extract SZ_LOG bits starting at bit SZ_LOG+3 = 11
export function upper_bits(y) {
    return Number((y >> 11n) & 0xFFn);
}
 
// cf. isaac64.c:103 — mix 8 values using alternating shift pattern
export function isaac64_mix(x) {
    const SHIFT = [9, 9, 23, 15, 14, 20, 17, 14];
    for (let i = 0; i < 8; i++) {
        x[i] = (x[i] - x[(i + 4) & 7]) & MASK;
        x[(i + 5) & 7] ^= x[(i + 7) & 7] >> BigInt(SHIFT[i]);
        x[(i + 7) & 7] = (x[(i + 7) & 7] + x[i]) & MASK;
        i++;
        x[i] = (x[i] - x[(i + 4) & 7]) & MASK;
        x[(i + 5) & 7] ^= (x[(i + 7) & 7] << BigInt(SHIFT[i])) & MASK;
        x[(i + 7) & 7] = (x[(i + 7) & 7] + x[i]) & MASK;
    }
}
 
// cf. isaac64.c:50 — generate next batch of 256 random values
export function isaac64_update(ctx) {
    const m = ctx.m;
    const r = ctx.r;
    let a = ctx.a;
    ctx.c = (ctx.c + 1n) & MASK;
    let b = (ctx.b + ctx.c) & MASK;
 
    // First half: uses m[i + SZ/2]
    for (let i = 0; i < SZ / 2;) {
        let x, y;
 
        x = m[i];
        a = (((a ^ ((a << 21n) & MASK)) ^ MASK) + m[i + SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ (a >> 5n)) + m[i + SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ ((a << 12n) & MASK)) + m[i + SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ (a >> 33n)) + m[i + SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
    }
 
    // Second half: uses m[i - SZ/2]
    for (let i = SZ / 2; i < SZ;) {
        let x, y;
 
        x = m[i];
        a = (((a ^ ((a << 21n) & MASK)) ^ MASK) + m[i - SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ (a >> 5n)) + m[i - SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ ((a << 12n) & MASK)) + m[i - SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
 
        x = m[i];
        a = ((a ^ (a >> 33n)) + m[i - SZ / 2]) & MASK;
        m[i] = y = (m[lower_bits(x)] + a + b) & MASK;
        r[i] = b = (m[upper_bits(y)] + x) & MASK;
        i++;
    }
 
    ctx.b = b;
    ctx.a = a;
    ctx.n = SZ;
}
 
// cf. isaac64.c:118 — create a new ISAAC64 context and seed it
export function isaac64_init(seed_bytes) {
    const ctx = {
        a: 0n,
        b: 0n,
        c: 0n,
        n: 0,
        r: new Array(SZ).fill(0n),
        m: new Array(SZ).fill(0n),
    };
    isaac64_reseed(ctx, seed_bytes);
    return ctx;
}
 
// cf. isaac64.c:124 — mix seed into state and reinitialize
export function isaac64_reseed(ctx, seed_bytes) {
    const m = ctx.m;
    const r = ctx.r;
    let nseed = seed_bytes ? seed_bytes.length : 0;
    if (nseed > SEED_SZ_MAX) nseed = SEED_SZ_MAX;
 
    // XOR seed bytes into r[] as little-endian uint64s
    let i = 0;
    for (; i < (nseed >> 3); i++) {
        let val = 0n;
        for (let j = 7; j >= 0; j--) {
            val = (val << 8n) | BigInt(seed_bytes[(i << 3) | j]);
        }
        r[i] ^= val;
    }
 
    // Handle remaining bytes
    const remaining = nseed - (i << 3);
    if (remaining > 0) {
        let ri = BigInt(seed_bytes[i << 3]);
        for (let j = 1; j < remaining; j++) {
            ri |= BigInt(seed_bytes[(i << 3) | j]) << BigInt(j << 3);
        }
        r[i++] ^= ri;
    }
 
    // Initialize x[] with golden ratio constant
    const x = new Array(8).fill(GOLDEN);
 
    // 4 rounds of mixing
    for (let k = 0; k < 4; k++) isaac64_mix(x);
 
    // Mix r[] values into m[]
    for (let k = 0; k < SZ; k += 8) {
        for (let j = 0; j < 8; j++) x[j] = (x[j] + r[k + j]) & MASK;
        isaac64_mix(x);
        for (let j = 0; j < 8; j++) m[k + j] = x[j];
    }
 
    // Double-mix with m[]
    for (let k = 0; k < SZ; k += 8) {
        for (let j = 0; j < 8; j++) x[j] = (x[j] + m[k + j]) & MASK;
        isaac64_mix(x);
        for (let j = 0; j < 8; j++) m[k + j] = x[j];
    }
 
    // Generate first batch
    isaac64_update(ctx);
}
 
// cf. isaac64.c:161 — return next random uint64
// Autotranslated from isaac64.c:161
export function isaac64_next_uint64(_ctx) {
  if (!_ctx.n) isaac64_update(_ctx);
  return _ctx.r[--_ctx.n];
}
 
// Peek at the next PRNG value without consuming it.
// Used by mail daemon to check delivery probability without
// perturbing the RNG stream.
export function isaac64_peek_uint64(_ctx) {
  if (!_ctx.n) isaac64_update(_ctx);
  return _ctx.r[_ctx.n - 1];
}
 
// cf. isaac64.c:166 — return unbiased random value in [0, n)
// Autotranslated from isaac64.c:166
export function isaac64_next_uint(_ctx, _n) {
  let r, v, d;
  do {
    r=isaac64_next_uint64(_ctx);
    v=r%_n;
    d=r-v;
  } while (((d+_n-1)&MASK)<d);
  return v;
}