110 lines
4.1 KiB
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");
|
|
}
|
|
}
|
|
}
|