package unit; import java.util.*; /** * NimSeqUtilsDeduplicateAlgorithm — CWE-407 test for nim-0001 * * Models sequtils.deduplicate[T](s, isSorted=false): * slow: result.contains(itm) inside for-loop — O(N²) * fast: HashSet seen + result.add — O(N) * * Test: slow does N*(N/2) avg contains checks; fast does N hash lookups. */ public class NimSeqUtilsDeduplicateAlgorithm { // --- SLOW: O(N²) --- // for itm in items(s): // if not result.contains(itm): result.add(itm) static class SlowDeduplicate { long containsChecks = 0; List deduplicate(List s) { List result = new ArrayList<>(); for (int itm : s) { // result.contains — linear scan boolean found = false; for (int r : result) { containsChecks++; if (r == itm) { found = true; break; } } if (!found) result.add(itm); } return result; } } // --- FAST: O(N) --- // var seen = initHashSet[T]() // for itm in items(s): // if itm notin seen: seen.incl(itm); result.add(itm) static class FastDeduplicate { long hashOps = 0; List deduplicate(List s) { List result = new ArrayList<>(); Set seen = new HashSet<>(); for (int itm : s) { hashOps++; if (seen.add(itm)) result.add(itm); } return result; } } // Build a sequence with ~50% duplicates, unsorted static List makeSeq(int n, long seed) { Random rng = new Random(seed); List s = new ArrayList<>(n); int range = n / 2; // 50% duplicates on average for (int i = 0; i < n; i++) s.add(rng.nextInt(range)); return s; } public static void main(String[] args) { int[] sizes = {100, 500, 1000, 2000}; System.out.println("NimSeqUtilsDeduplicateAlgorithm — nim-0001"); System.out.println(" Pattern: result.contains(itm) in for-loop — O(N²) vs HashSet — O(N)"); System.out.println(); int passed = 0; int total = 0; for (int n : sizes) { List s = makeSeq(n, 12345L + n); SlowDeduplicate slow = new SlowDeduplicate(); FastDeduplicate fast = new FastDeduplicate(); List slowResult = slow.deduplicate(new ArrayList<>(s)); List fastResult = fast.deduplicate(new ArrayList<>(s)); // Both should produce the same unique elements in the same first-seen order boolean same = slowResult.equals(fastResult); // Slow: with 50% duplicates, result grows to ~N/2. Average scan of result = N/4 per element. // Total ≈ N * N/4 = N²/4 checks. long slowChecks = slow.containsChecks; long fastOps = fast.hashOps; boolean slowIsQuadratic = slowChecks >= (long) n * n / 8; // conservative threshold boolean fastIsLinear = fastOps == n; total += 3; if (same) { System.out.println("PASS N=" + n + ": results match, unique=" + slowResult.size()); passed++; } else { System.out.println("FAIL N=" + n + ": result mismatch, slow.size=" + slowResult.size() + " fast.size=" + fastResult.size()); } if (slowIsQuadratic) { System.out.println("PASS N=" + n + ": slow O(N²) checks=" + slowChecks + " >= N²/8=" + (n * n / 8)); passed++; } else { System.out.println("FAIL N=" + n + ": slow not quadratic, checks=" + slowChecks); } if (fastIsLinear) { System.out.println("PASS N=" + n + ": fast O(N) ops=" + fastOps + " == N=" + n); passed++; } else { System.out.println("FAIL N=" + n + ": fast not linear, ops=" + fastOps); } } System.out.println(); System.out.println(passed + "/" + total + " PASS"); if (passed != total) { throw new AssertionError(passed + "/" + total + " tests passed"); } } }