java-topology/defects/pulumi/unit/PulumiPackageInfoAlgorithm.java

177 lines
6.9 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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* pulumi-0001: package_info.go Required slice O(P×R) membership test
*
* Slow: slices.Contains(Required, name) inside for _, name := range SortedKeys(Properties) — O(P×R)
* Fast: pre-build requiredSet map[string]bool, then requiredSet[name] — O(P+R)
*
* Verifies: slow_ops >= P*(P-1)/2, fast_ops == P+R, ratio >= 10x at P=500, R=200
*/
public class PulumiPackageInfoAlgorithm {
static long slowOps = 0;
static long fastOps = 0;
/** Simulate slices.Contains — counts ops against slowOps */
static boolean slicesContains(List<String> list, String target) {
for (String s : list) {
slowOps++;
if (s.equals(target)) return true;
}
return false;
}
/**
* Slow version: mirrors original package_info.go
* for _, name := range maputil.SortedKeys(properties) {
* if slices.Contains(required, name) { ... }
* }
*/
static int slowDisplayProperties(Map<String, String> properties, List<String> required) {
slowOps = 0;
int requiredCount = 0;
// SortedKeys produces sorted iteration
for (String name : new TreeMap<>(properties).keySet()) {
if (slicesContains(required, name)) {
requiredCount++;
}
}
return requiredCount;
}
/**
* Fast version: pre-build a set for O(1) lookup
* requiredSet := make(map[string]bool, len(required))
* for _, r := range required { requiredSet[r] = true }
* for _, name := range sortedKeys { if requiredSet[name] { ... } }
*/
static int fastDisplayProperties(Map<String, String> properties, List<String> required) {
fastOps = 0;
Map<String, Boolean> requiredSet = new HashMap<>(required.size() * 2);
for (String r : required) {
fastOps++;
requiredSet.put(r, true);
}
int requiredCount = 0;
for (String name : new TreeMap<>(properties).keySet()) {
fastOps++;
if (requiredSet.containsKey(name)) {
requiredCount++;
}
}
return requiredCount;
}
/** Build a schema with P properties and R required fields */
static Object[] buildSchema(int P, int R) {
Map<String, String> properties = new TreeMap<>();
List<String> required = new ArrayList<>();
for (int i = 0; i < P; i++) {
String name = "prop_" + String.format("%04d", i);
properties.put(name, "string");
if (i < R) {
required.add(name);
}
}
return new Object[]{properties, required};
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
int passed = 0;
int total = 0;
// Test 1: correctness — both return same required count
{
total++;
Object[] schema = buildSchema(20, 8);
Map<String, String> props = (Map<String, String>) schema[0];
List<String> required = (List<String>) schema[1];
int slowResult = slowDisplayProperties(props, required);
int fastResult = fastDisplayProperties(props, required);
boolean ok = (slowResult == 8 && fastResult == 8);
System.out.println((ok ? "PASS" : "FAIL") + " [correctness P=20 R=8]: slow=" + slowResult + " fast=" + fastResult);
if (ok) passed++;
}
// Test 2: no required fields
{
total++;
Object[] schema = buildSchema(10, 0);
Map<String, String> props = (Map<String, String>) schema[0];
List<String> required = (List<String>) schema[1];
int slowResult = slowDisplayProperties(props, required);
int fastResult = fastDisplayProperties(props, required);
boolean ok = (slowResult == 0 && fastResult == 0);
System.out.println((ok ? "PASS" : "FAIL") + " [no required P=10 R=0]: slow=" + slowResult + " fast=" + fastResult);
if (ok) passed++;
}
// Test 3: all required
{
total++;
Object[] schema = buildSchema(15, 15);
Map<String, String> props = (Map<String, String>) schema[0];
List<String> required = (List<String>) schema[1];
int slowResult = slowDisplayProperties(props, required);
int fastResult = fastDisplayProperties(props, required);
boolean ok = (slowResult == 15 && fastResult == 15);
System.out.println((ok ? "PASS" : "FAIL") + " [all required P=15 R=15]: slow=" + slowResult + " fast=" + fastResult);
if (ok) passed++;
}
// Test 4: slow op count is O(P×R)
{
total++;
int P = 500, R = 200;
Object[] schema = buildSchema(P, R);
slowDisplayProperties((Map<String, String>) schema[0], (List<String>) schema[1]);
// For required properties: scan stops at position (roughly avg R/2 per hit + full scan for misses)
// At minimum: R properties each stop at avg R/2 + (P-R) properties each scan full R = R²/2 + (P-R)*R
long minExpected = (long) (P - R) * R; // just the non-required properties scanning full required list
boolean ok = slowOps >= minExpected;
System.out.println((ok ? "PASS" : "FAIL") + " [slow O(P×R) P=" + P + " R=" + R + "]: ops=" + slowOps + " >= " + minExpected);
if (ok) passed++;
}
// Test 5: fast op count is O(P+R)
{
total++;
int P = 500, R = 200;
Object[] schema = buildSchema(P, R);
fastDisplayProperties((Map<String, String>) schema[0], (List<String>) schema[1]);
// fastOps = R (set build) + P (property scan) = P+R
long expected = (long) P + R;
boolean ok = fastOps == expected;
System.out.println((ok ? "PASS" : "FAIL") + " [fast O(P+R) P=" + P + " R=" + R + "]: ops=" + fastOps + " == " + expected);
if (ok) passed++;
}
// Test 6: ratio >= 10x at P=500, R=200
{
total++;
int P = 500, R = 200;
Object[] schema = buildSchema(P, R);
Map<String, String> props = (Map<String, String>) schema[0];
List<String> required = (List<String>) schema[1];
slowDisplayProperties(props, required);
long slowCount = slowOps;
fastDisplayProperties(props, required);
long fastCount = fastOps;
double ratio = (double) slowCount / fastCount;
boolean ok = ratio >= 10.0;
System.out.printf((ok ? "PASS" : "FAIL") + " [ratio P=%d R=%d]: slowOps=%d fastOps=%d ratio=%.1fx%n", P, R, slowCount, fastCount, ratio);
if (ok) passed++;
}
System.out.println(passed + "/" + total + " PASS");
if (passed != total) System.exit(1);
}
}