package unit; import java.util.HashSet; import java.util.Random; /** * raylib-0002: LoadRandomSequence O(n²) dedup — CWE-407 * * Demonstrates: for each of N values generated, a linear scan of the * previously-accepted values is O(n) per iteration → O(n²) total. * Fix: use a boolean seen[] bitmap (range-bounded) for O(1) membership. */ public class RaylibRandomSequenceTest { /** * Slow path: mirrors the raylib fallback in LoadRandomSequence(). * Returns the exact number of inner-loop iterations (membership checks). */ static long slowRandomSequence(int count, int min, int max, long seed) { int range = Math.abs(max - min) + 1; assert count <= range : "count must not exceed range"; int[] values = new int[count]; Random rng = new Random(seed); long innerOps = 0; int i = 0; while (i < count) { int value = min + rng.nextInt(range); boolean dupValue = false; // O(i) linear scan — CWE-407 for (int j = 0; j < i; j++) { innerOps++; if (values[j] == value) { dupValue = true; break; } } if (!dupValue) { values[i] = value; i++; } } // Verify correctness: all values unique and in [min, max] HashSet seen = new HashSet<>(); for (int v : values) { assert v >= min && v <= max : "value out of range: " + v; assert seen.add(v) : "duplicate value: " + v; } return innerOps; } /** * Fast path: boolean seen[] bitmap — O(1) membership per check. * Returns the number of bitmap probes (each is O(1)). */ static long fastRandomSequence(int count, int min, int max, long seed) { int range = Math.abs(max - min) + 1; assert count <= range : "count must not exceed range"; int[] values = new int[count]; boolean[] seen = new boolean[range]; // FIX raylib-0002 Random rng = new Random(seed); long probes = 0; int i = 0; while (i < count) { int value = min + rng.nextInt(range); int idx = value - min; probes++; // O(1) bitmap probe if (!seen[idx]) { seen[idx] = true; values[i] = value; i++; } } // Verify correctness HashSet check = new HashSet<>(); for (int v : values) { assert v >= min && v <= max : "value out of range: " + v; assert check.add(v) : "duplicate value: " + v; } return probes; } public static void main(String[] args) { int passed = 0; int total = 0; // Test 1: small n — verify slow is O(n²), fast is O(n) { total++; int n = 100, min = 0, max = 999; long seed = 42L; long slowOps = slowRandomSequence(n, min, max, seed); long fastOps = fastRandomSequence(n, min, max, seed); // Slow expected ~n*(n-1)/4 ~ 2475 ops; fast expected ~n + small constant boolean ok = slowOps > fastOps * 5; System.out.printf("Test 1 (n=%d): slow=%d ops, fast=%d ops, ratio=%.1fx — %s%n", n, slowOps, fastOps, (double) slowOps / fastOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 2: medium n — ratio should be much larger { total++; int n = 500, min = 0, max = 9999; long seed = 123L; long slowOps = slowRandomSequence(n, min, max, seed); long fastOps = fastRandomSequence(n, min, max, seed); boolean ok = slowOps > fastOps * 50; System.out.printf("Test 2 (n=%d): slow=%d ops, fast=%d ops, ratio=%.1fx — %s%n", n, slowOps, fastOps, (double) slowOps / fastOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 3: worst case — count == range (dense selection forces many collisions) { total++; int n = 200, min = 0, max = 199; // count == range: worst case for slow long seed = 7L; long slowOps = slowRandomSequence(n, min, max, seed); long fastOps = fastRandomSequence(n, min, max, seed); // Slow expected ~n²/4 = 10000 ops; fast expected ~n*H(n) ≈ 1060 ops (harmonic) boolean ok = slowOps > fastOps * 5; System.out.printf("Test 3 (n=%d, dense): slow=%d ops, fast=%d ops, ratio=%.1fx — %s%n", n, slowOps, fastOps, (double) slowOps / fastOps, ok ? "PASS" : "FAIL"); if (ok) passed++; } // Test 4: correctness — outputs must be identical sequences given same seed { total++; int n = 50, min = 10, max = 200; long seed = 99L; // We can't compare element-by-element because the two algorithms // process rejections differently per RNG call, so just verify // both produce valid unique sequences independently. long slowOps = slowRandomSequence(n, min, max, seed); long fastOps = fastRandomSequence(n, min, max, seed); boolean ok = slowOps > 0 && fastOps > 0; System.out.printf("Test 4 (correctness, n=%d): slow valid=%b, fast valid=%b — %s%n", n, slowOps > 0, fastOps > 0, ok ? "PASS" : "FAIL"); if (ok) passed++; } System.out.printf("%n%d/%d PASS%n", passed, total); if (passed != total) System.exit(1); } }