177 lines
6.9 KiB
Java
177 lines
6.9 KiB
Java
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);
|
||
}
|
||
}
|