188 lines
7.1 KiB
Java
188 lines
7.1 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* vim-0002: au_find_group() O(G) linear scan called per item in autocmd_add_or_delete loop.
|
||
*
|
||
* Models autocmd_add_or_delete() iterating over L list items and calling
|
||
* au_find_group() (O(G) scan over augroups array) for each.
|
||
* Total work: O(L×G).
|
||
*
|
||
* The fix uses a HashMap<name, index> for O(1) group lookup.
|
||
*/
|
||
public class AuFindGroupTest {
|
||
|
||
// ----- SLOW path: linear scan over augroups array (mirrors Vim's autocmd.c) -----
|
||
|
||
static int auFindGroupSlow(String[] augroups, String name, long[] ops) {
|
||
for (int i = 0; i < augroups.length; i++) {
|
||
ops[0]++;
|
||
if (augroups[i] != null && augroups[i].equals(name)) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1; // AUGROUP_ERROR
|
||
}
|
||
|
||
static long slowAutocmdAddOrDelete(String[] augroups, String[] listGroupNames) {
|
||
long[] ops = {0};
|
||
for (String groupName : listGroupNames) {
|
||
// au_find_group() call per list item
|
||
int group = auFindGroupSlow(augroups, groupName, ops);
|
||
if (group == -1) {
|
||
// au_new_group also calls au_find_group first (already counted above)
|
||
// then does another scan for a free slot
|
||
for (int i = 0; i < augroups.length; i++) {
|
||
ops[0]++;
|
||
if (augroups[i] == null) break;
|
||
}
|
||
}
|
||
}
|
||
return ops[0];
|
||
}
|
||
|
||
// ----- FAST path: HashMap<name, index> lookup -----
|
||
|
||
static long fastAutocmdAddOrDelete(Map<String, Integer> augroupsMap, String[] listGroupNames) {
|
||
long ops = 0;
|
||
for (String groupName : listGroupNames) {
|
||
ops++; // O(1) hash lookup
|
||
augroupsMap.get(groupName); // may return null (group not found)
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
// ----- Helpers -----
|
||
|
||
static String[] buildAugroups(int G) {
|
||
String[] augroups = new String[G];
|
||
for (int i = 0; i < G; i++) augroups[i] = "group_" + i;
|
||
return augroups;
|
||
}
|
||
|
||
static Map<String, Integer> buildAugroupsMap(int G) {
|
||
Map<String, Integer> map = new HashMap<>();
|
||
for (int i = 0; i < G; i++) map.put("group_" + i, i);
|
||
return map;
|
||
}
|
||
|
||
static String[] buildListItems(int L, int G) {
|
||
// Each list item references a random group from the pool
|
||
String[] items = new String[L];
|
||
for (int i = 0; i < L; i++) items[i] = "group_" + (i % G);
|
||
return items;
|
||
}
|
||
|
||
// ----- Test runner -----
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println("vim-0002: AuFindGroupTest");
|
||
System.out.println("========================");
|
||
|
||
int passed = 0;
|
||
int total = 0;
|
||
|
||
// Test 1: correctness — both paths resolve the same group indices
|
||
{
|
||
total++;
|
||
int G = 10;
|
||
String[] augroups = buildAugroups(G);
|
||
Map<String, Integer> augroupsMap = buildAugroupsMap(G);
|
||
String[] queries = {"group_0", "group_5", "group_9", "group_99"};
|
||
boolean ok = true;
|
||
long[] ops = {0};
|
||
for (String q : queries) {
|
||
int slowResult = auFindGroupSlow(augroups, q, ops);
|
||
Integer fastResult = augroupsMap.get(q);
|
||
int fastInt = (fastResult == null) ? -1 : fastResult;
|
||
if (slowResult != fastInt) { ok = false; break; }
|
||
}
|
||
System.out.println("Test 1 (correctness): " + (ok ? "PASS" : "FAIL"));
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// Test 2: op-count ratio >= 5x at L=100, G=20
|
||
{
|
||
total++;
|
||
int L = 100, G = 20;
|
||
String[] augroups = buildAugroups(G);
|
||
Map<String, Integer> augroupsMap = buildAugroupsMap(G);
|
||
String[] listItems = buildListItems(L, G);
|
||
|
||
long slowOps = slowAutocmdAddOrDelete(augroups, listItems);
|
||
long fastOps = fastAutocmdAddOrDelete(augroupsMap, listItems);
|
||
double ratio = (double) slowOps / fastOps;
|
||
|
||
System.out.printf("Test 2 (L=%d, G=%d): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||
L, G, 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 L=500, G=100
|
||
{
|
||
total++;
|
||
int L = 500, G = 100;
|
||
String[] augroups = buildAugroups(G);
|
||
Map<String, Integer> augroupsMap = buildAugroupsMap(G);
|
||
String[] listItems = buildListItems(L, G);
|
||
|
||
long slowOps = slowAutocmdAddOrDelete(augroups, listItems);
|
||
long fastOps = fastAutocmdAddOrDelete(augroupsMap, listItems);
|
||
double ratio = (double) slowOps / fastOps;
|
||
|
||
System.out.printf("Test 3 (L=%d, G=%d): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||
L, G, 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 >= 80x at L=500, G=100, all distinct group names
|
||
{
|
||
total++;
|
||
int L = 500, G = 100;
|
||
String[] augroups = buildAugroups(G);
|
||
Map<String, Integer> augroupsMap = buildAugroupsMap(G);
|
||
// Each item references a distinct group — worst case for linear scan
|
||
// when group is not found (searches entire array each time)
|
||
String[] listItems = new String[L];
|
||
for (int i = 0; i < L; i++) listItems[i] = "missing_group_" + i;
|
||
|
||
long slowOps = slowAutocmdAddOrDelete(augroups, listItems);
|
||
long fastOps = fastAutocmdAddOrDelete(augroupsMap, listItems);
|
||
double ratio = (double) slowOps / fastOps;
|
||
|
||
System.out.printf("Test 4 (L=%d, G=%d, all-miss): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||
L, G, slowOps, fastOps, ratio);
|
||
boolean ok = ratio >= 80.0;
|
||
System.out.println("Test 4 (ratio >= 80x): " + (ok ? "PASS" : "FAIL"));
|
||
if (ok) passed++;
|
||
}
|
||
|
||
// Test 5: op-count ratio >= 5x at small scale L=50, G=10
|
||
{
|
||
total++;
|
||
int L = 50, G = 10;
|
||
String[] augroups = buildAugroups(G);
|
||
Map<String, Integer> augroupsMap = buildAugroupsMap(G);
|
||
String[] listItems = buildListItems(L, G);
|
||
|
||
long slowOps = slowAutocmdAddOrDelete(augroups, listItems);
|
||
long fastOps = fastAutocmdAddOrDelete(augroupsMap, listItems);
|
||
double ratio = (double) slowOps / fastOps;
|
||
|
||
System.out.printf("Test 5 (L=%d, G=%d): slow=%d ops, fast=%d ops, ratio=%.1fx%n",
|
||
L, G, 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";
|
||
}
|
||
}
|