java-topology/defects/nim/unit/NimSeqUtilsDeduplicateAlgorithm.java

110 lines
4.1 KiB
Java

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<Integer> deduplicate(List<Integer> s) {
List<Integer> 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<Integer> deduplicate(List<Integer> s) {
List<Integer> result = new ArrayList<>();
Set<Integer> 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<Integer> makeSeq(int n, long seed) {
Random rng = new Random(seed);
List<Integer> 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<Integer> s = makeSeq(n, 12345L + n);
SlowDeduplicate slow = new SlowDeduplicate();
FastDeduplicate fast = new FastDeduplicate();
List<Integer> slowResult = slow.deduplicate(new ArrayList<>(s));
List<Integer> 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");
}
}
}