Debugging challengeAdvanced

Rolling hash and bitmask that silently corrupt

Scenario

A Rabin–Karp matcher and a bitmask DP both work in C++ and were ported line by line to JavaScript. The JS version reports false matches on long strings, the DP "visits" states it never set, and a factorial helper returns the wrong value for n = 21. Find the three numeric bugs.

1const MOD = 1e18 + 9;
2const BASE = 131;
3
4// polynomial rolling hash of s
5function hash(s) {
6 let h = 0;
7 for (const ch of s) h = (h * BASE + ch.charCodeAt(0)) % MOD;
8 return h;
9}
10
11// mark subset of 40 items as visited
12function markVisited(visited, mask, item) {
13 visited.add(mask | (1 << item));
14}
15
16function factorial(n) {
17 let f = 1;
18 for (let i = 2; i <= n; i++) f *= i;
19 return f;
20}
21
22console.log(hash('a'.repeat(30)) === hash('a'.repeat(29) + 'b')); // sometimes true
23const visited = new Set();
24markVisited(visited, 0, 31);
25markVisited(visited, 0, 32);
26console.log(visited); // Set { -2147483648, 1 } — item 32 == item 0
27console.log(factorial(21)); // 51090942171709440000, expected 51090942171709440000? no: 51090942171709440000 is wrong

Your task

  1. What is the largest integer JavaScript can represent exactly? What happens to h * BASE when h is close to MOD?
  2. How many bits do JavaScript bitwise operators use? What is 1 << 32?
  3. At which n does factorial(n) first lose precision, and why does it not throw?
  4. Fix all three: choose a safe modulus, a safe way to build a 40-bit mask, and exact factorials.
  5. State the complexity of the fixes and any runtime cost of BigInt.
DebuggingEdge CasesSystematic Reasoning

Work it out

Write your analysis before revealing anything. The self-check below compares it against what a strong answer contains.

Reveal

Progressive — each section builds on the previous one.

The bug
Why it happens
The fix
Edge cases
Complexity

Self-check

Tick what your analysis covered. Be honest — this feeds your readiness profile.

0/6

Related concepts