All files / random / _pcg32.ts

100.00% Branches 2/2
100.00% Lines 45/45
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
 
 
 
 
x11
 
x11
 
 
x11
x11
x11
 
 
 
 
 
 
 
 
 
 
 
 
 
x11
x28
x28
x28
 
 
 
 
x11
x41325
x41325
 
 
 
 
x11
x112
 
x28
x28
x28
x28
 
 
 
 
 
 
 
 
 
 
 
 
x11
x41254
x41254
 
x41254
x41254
x41254
x41254
 
 
x41254
x41254
x41254
x41254
x41254
 
 
 
 
 
x11
x65
 
x260
 
 
x65
 
x65
x245
x245
 
x65
x65
x78
x78
x78
x78
 
x65
x65


































































































// Copyright 2018-2025 the Deno authors. MIT license.
// Based on Rust `rand` crate (https://github.com/rust-random/rand). Apache-2.0 + MIT license.

/** Multiplier for the PCG32 algorithm. */
const MUL: bigint = 6364136223846793005n;
/** Initial increment for the PCG32 algorithm. Only used during seeding. */
const INC: bigint = 11634580027462260723n;

// Constants are for 64-bit state, 32-bit output
const ROTATE = 59n; // 64 - 5
const XSHIFT = 18n; // (5 + 32) / 2
const SPARE = 27n; // 64 - 32 - 5

/**
 * Internal state for the PCG32 algorithm.
 * `state` prop is mutated by each step, whereas `inc` prop remains constant.
 */
type PcgMutableState = {
  state: bigint;
  inc: bigint;
};

/**
 * Modified from https://github.com/rust-random/rand/blob/f7bbccaedf6c63b02855b90b003c9b1a4d1fd1cb/rand_pcg/src/pcg64.rs#L129-L135
 */
export function fromSeed(seed: Uint8Array) {
  const d = new DataView(seed.buffer);
  return fromStateIncr(d.getBigUint64(0, true), d.getBigUint64(8, true) | 1n);
}

/**
 * Mutates `pcg` by advancing `pcg.state`.
 */
function step(pgc: PcgMutableState) {
  pgc.state = BigInt.asUintN(64, pgc.state * MUL + (pgc.inc | 1n));
}

/**
 * Modified from https://github.com/rust-random/rand/blob/f7bbccaedf6c63b02855b90b003c9b1a4d1fd1cb/rand_pcg/src/pcg64.rs#L99-L105
 */
function fromStateIncr(state: bigint, inc: bigint): PcgMutableState {
  const pcg: PcgMutableState = { state, inc };
  // Move away from initial value
  pcg.state = BigInt.asUintN(64, state + inc);
  step(pcg);
  return pcg;
}

/**
 * Internal PCG32 implementation, used by both the public seeded random
 * function and the seed generation algorithm.
 *
 * Modified from https://github.com/rust-random/rand/blob/f7bbccaedf6c63b02855b90b003c9b1a4d1fd1cb/rand_pcg/src/pcg64.rs#L140-L153
 *
 * `pcg.state` is internally advanced by the function.
 *
 * @param pcg The state and increment values to use for the PCG32 algorithm.
 * @returns The next pseudo-random 32-bit integer.
 */
export function nextU32(pcg: PcgMutableState): number {
  const state = pcg.state;
  step(pcg);
  // Output function XSH RR: xorshift high (bits), followed by a random rotate
  const rot = state >> ROTATE;
  const xsh = BigInt.asUintN(32, (state >> XSHIFT ^ state) >> SPARE);
  return Number(rotateRightU32(xsh, rot));
}

// `n`, `rot`, and return val are all u32
function rotateRightU32(n: bigint, rot: bigint): bigint {
  const left = BigInt.asUintN(32, n << (-rot & 31n));
  const right = n >> rot;
  return left | right;
}

/**
 * Convert a scalar bigint seed to a Uint8Array of the specified length.
 * Modified from https://github.com/rust-random/rand/blob/f7bbccaedf6c63b02855b90b003c9b1a4d1fd1cb/rand_core/src/lib.rs#L359-L388
 */
export function seedFromU64(state: bigint, numBytes: number): Uint8Array {
  const seed = new Uint8Array(numBytes);

  const pgc: PcgMutableState = { state: BigInt.asUintN(64, state), inc: INC };
  // We advance the state first (to get away from the input value,
  // in case it has low Hamming Weight).
  step(pgc);

  for (let i = 0; i < Math.floor(numBytes / 4); ++i) {
    new DataView(seed.buffer).setUint32(i * 4, nextU32(pgc), true);
  }

  const rem = numBytes % 4;
  if (rem) {
    const bytes = new Uint8Array(4);
    new DataView(bytes.buffer).setUint32(0, nextU32(pgc), true);
    seed.set(bytes.subarray(0, rem), numBytes - rem);
  }

  return seed;
}