java-topology/defects/vim/unit/AuFindGroupTest.java

188 lines
7.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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";
}
}