wave17 complete: systemd/emacs/vim/qemu/tcl/kafka-0007/spark-0004 + 559/240
This commit is contained in:
parent
4221966e66
commit
cce7ec653a
32 changed files with 3007 additions and 5 deletions
186
defects/vim/unit/InsComplDedupTest.java
Normal file
186
defects/vim/unit/InsComplDedupTest.java
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* vim-0001: ins_compl_add linked-list linear scan O(N²) in insert-mode completion.
|
||||
*
|
||||
* Models ins_compl_add_matches() calling ins_compl_add() for N candidates.
|
||||
* Each ins_compl_add() does a full O(M) walk of the existing completions linked
|
||||
* list to check for duplicates. Total work is O(N²).
|
||||
*
|
||||
* The fix replaces the linked-list scan with a HashSet lookup: O(N) total.
|
||||
*/
|
||||
public class InsComplDedupTest {
|
||||
|
||||
// ----- SLOW path: linked-list dedup (mirrors Vim's insexpand.c) -----
|
||||
|
||||
static class ComplNode {
|
||||
String str;
|
||||
ComplNode next;
|
||||
ComplNode(String s) { this.str = s; }
|
||||
}
|
||||
|
||||
/** Returns op count for adding one candidate to the linked list. */
|
||||
static long slowAddCandidate(ComplNode head, String candidate, long[] ops) {
|
||||
// Walk linked list to check for duplicates — O(M)
|
||||
ComplNode cur = head;
|
||||
while (cur != null) {
|
||||
ops[0]++;
|
||||
if (cur.str.equals(candidate)) {
|
||||
return -1; // already present
|
||||
}
|
||||
cur = cur.next;
|
||||
}
|
||||
return 0; // not found — would be added
|
||||
}
|
||||
|
||||
static long slowBuildCompletions(String[] candidates) {
|
||||
long[] ops = {0};
|
||||
ComplNode head = null;
|
||||
ComplNode tail = null;
|
||||
List<String> accepted = new ArrayList<>();
|
||||
|
||||
for (String c : candidates) {
|
||||
// Simulate the head-pointer (compl_first_match is not null)
|
||||
if (head == null) {
|
||||
head = new ComplNode(c);
|
||||
tail = head;
|
||||
accepted.add(c);
|
||||
continue;
|
||||
}
|
||||
long result = slowAddCandidate(head, c, ops);
|
||||
if (result == 0) {
|
||||
// Add to list
|
||||
ComplNode node = new ComplNode(c);
|
||||
tail.next = node;
|
||||
tail = node;
|
||||
accepted.add(c);
|
||||
}
|
||||
}
|
||||
return ops[0];
|
||||
}
|
||||
|
||||
// ----- FAST path: HashSet dedup -----
|
||||
|
||||
static long fastBuildCompletions(String[] candidates) {
|
||||
long ops = 0;
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (String c : candidates) {
|
||||
ops++; // O(1) hash lookup
|
||||
seen.add(c);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ----- Test runner -----
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("vim-0001: InsComplDedupTest");
|
||||
System.out.println("==========================");
|
||||
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// Test 1: correctness — both paths accept same unique elements
|
||||
{
|
||||
total++;
|
||||
String[] cands = {"alpha", "beta", "gamma", "alpha", "delta", "beta"};
|
||||
// Slow path: collect accepted list
|
||||
long[] ops = {0};
|
||||
ComplNode head = null;
|
||||
ComplNode tail = null;
|
||||
List<String> slowAccepted = new ArrayList<>();
|
||||
for (String c : cands) {
|
||||
if (head == null) {
|
||||
head = new ComplNode(c); tail = head;
|
||||
slowAccepted.add(c); continue;
|
||||
}
|
||||
if (slowAddCandidate(head, c, ops) == 0) {
|
||||
ComplNode node = new ComplNode(c); tail.next = node; tail = node;
|
||||
slowAccepted.add(c);
|
||||
}
|
||||
}
|
||||
Set<String> fastAccepted = new HashSet<>(Arrays.asList(cands));
|
||||
boolean ok = new HashSet<>(slowAccepted).equals(fastAccepted);
|
||||
System.out.println("Test 1 (correctness): " + (ok ? "PASS" : "FAIL"));
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 2: op-count ratio >= 5x at N=200 unique candidates
|
||||
{
|
||||
total++;
|
||||
int N = 200;
|
||||
String[] candidates = new String[N];
|
||||
for (int i = 0; i < N; i++) candidates[i] = "word_" + i;
|
||||
|
||||
long slowOps = slowBuildCompletions(candidates);
|
||||
long fastOps = fastBuildCompletions(candidates);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
|
||||
System.out.printf("Test 2 (N=%d unique): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||||
N, slowOps, fastOps, ratio);
|
||||
boolean ok = ratio >= 5.0;
|
||||
System.out.println("Test 2 (ratio >= 5x): " + (ok ? "PASS" : "FAIL"));
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 3: op-count ratio >= 50x at N=500 unique candidates
|
||||
{
|
||||
total++;
|
||||
int N = 500;
|
||||
String[] candidates = new String[N];
|
||||
for (int i = 0; i < N; i++) candidates[i] = "word_" + i;
|
||||
|
||||
long slowOps = slowBuildCompletions(candidates);
|
||||
long fastOps = fastBuildCompletions(candidates);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
|
||||
System.out.printf("Test 3 (N=%d unique): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||||
N, slowOps, fastOps, ratio);
|
||||
boolean ok = ratio >= 50.0;
|
||||
System.out.println("Test 3 (ratio >= 50x): " + (ok ? "PASS" : "FAIL"));
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 4: op-count ratio >= 100x at N=1000 unique candidates
|
||||
{
|
||||
total++;
|
||||
int N = 1000;
|
||||
String[] candidates = new String[N];
|
||||
for (int i = 0; i < N; i++) candidates[i] = "word_" + i;
|
||||
|
||||
long slowOps = slowBuildCompletions(candidates);
|
||||
long fastOps = fastBuildCompletions(candidates);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
|
||||
System.out.printf("Test 4 (N=%d unique): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||||
N, slowOps, fastOps, ratio);
|
||||
boolean ok = ratio >= 100.0;
|
||||
System.out.println("Test 4 (ratio >= 100x): " + (ok ? "PASS" : "FAIL"));
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
// Test 5: with duplicates — correctness and ratio hold
|
||||
{
|
||||
total++;
|
||||
int N = 400;
|
||||
String[] candidates = new String[N];
|
||||
for (int i = 0; i < N; i++) candidates[i] = "dup_" + (i % 50); // 50 unique, 8 dups each
|
||||
|
||||
long slowOps = slowBuildCompletions(candidates);
|
||||
long fastOps = fastBuildCompletions(candidates);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
|
||||
System.out.printf("Test 5 (N=%d w/dups, 50 unique): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||||
N, slowOps, fastOps, ratio);
|
||||
boolean ok = ratio >= 5.0;
|
||||
System.out.println("Test 5 (ratio >= 5x): " + (ok ? "PASS" : "FAIL"));
|
||||
if (ok) passed++;
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
System.out.println(passed + "/" + total + " PASS");
|
||||
assert passed == total : passed + "/" + total + " tests passed";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue