namespace AdventOfCode2019_22_2 { const DAY = 22; const PROBLEM = 2; export async function run() { const CARDS = 119315717514047n; const SHUFFLES = 101741582076661n; /* NOTES lcg skipping https://www.nayuki.io/page/fast-skipping-in-a-linear-congruential-generator cracking lcg https://tailcall.net/blog/cracking-randomness-lcgs/ */ let input = await AdventOfCode.getInput(DAY); if (input == null) return; const cmds = input .split(new RegExp('\r?\n')) .filter(p => p.trim().length > 0) .map(p => parseLine(p)); let states = []; let card_index = 0n; for (let i=0; i<10; i++) { states.push(card_index) card_index = getSourceIndex(card_index, cmds, CARDS) } AdventOfCode.outputConsole("[states]: " + states.join(" ; ")); const [modulus, multiplier, increment] = lcg_crack(states, CARDS); AdventOfCode.outputConsole("[modulus]: " + modulus); AdventOfCode.outputConsole("[multiplier]: " + multiplier); AdventOfCode.outputConsole("[increment]: " + increment); let r = lcg_skip(multiplier, increment, modulus, 2020n, SHUFFLES); AdventOfCode.output(DAY, PROBLEM, r.toString()); } function lcg_skip(a: bigint, b: bigint, m: bigint, x:bigint, skip: bigint): bigint { //let ainv = reciprocal_mod(a, m); let a1 = a - 1n; let ma = a1 * m; let y = (modpow(a, skip, ma) - 1n) / a1 * b let z = modpow(a, skip, m) * x x = (y + z) % m return x; } function modpow(base: bigint, exp: bigint, mod: bigint): bigint { base = base % mod; let r = 1n; while (exp > 0n) { if (base === 0n) return 0n; if (exp % 2n === 1n) r = (r * base) % mod; exp /= 2n; base = (base * base) % mod; } return r; } function reciprocal_mod(x: bigint, mod: bigint): bigint { let y = x; x = mod; let [a, b] = [0n, 1n]; while (y !== 0n) { let _a = b; let _b = (a-x) / (y * b); a=_a; b=_b; let _x = y; let _y = x%y; x=_x;y=_y; } if (x === 1n) return a % mod else throw ("Reciprocal does not exist") } function getSourceIndex(idxDest: bigint, cmds: Cmd[], decklength: bigint) { let idx = idxDest; for (let cmd of cmds.slice().reverse()) { //AdventOfCode.outputConsole("[idx]: " + idx); if (cmd.type === CmdType.CUT) { idx = (idx + cmd.param + decklength) % decklength; } else if (cmd.type === CmdType.DEAL && cmd.param > 1n) { let f = false; for(let i=0n; i