package unit; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * celery-0001: ResultSet.update() / ResultSet.add() * * Defect: self.results is a plain list. The expression * 'r not in self.results' (Python) * becomes an O(N) scan on each of M elements in update() → O(M*N) total. * * Fix: maintain a parallel dict keyed by result ID for O(1) membership. * * Op counting: each comparison during the list scan counts as one op. * For hash lookup we count 1 op per lookup. */ public class ResultSetAlgorithm { // --- Slow path: simulate list.contains() with explicit scan counting --- static long slowUpdate(int existingCount, int newCount) { long ops = 0; List results = new ArrayList<>(); for (int i = 0; i < existingCount; i++) { results.add("result-" + i); } // simulate: self.results.extend(r for r in results if r not in self.results) for (int i = existingCount; i < existingCount + newCount; i++) { String r = "result-" + i; // Scan the list: worst case is a full pass (item not found) for (String existing : results) { ops++; // each comparison costs 1 op if (existing.equals(r)) break; } // Item not in list, add it results.add(r); } return ops; } // --- Fast path: LinkedHashMap (id→result) simulation --- static long fastUpdate(int existingCount, int newCount) { long ops = 0; Map resultMap = new LinkedHashMap<>(); for (int i = 0; i < existingCount; i++) { String r = "result-" + i; resultMap.put(r, r); } for (int i = existingCount; i < existingCount + newCount; i++) { String r = "result-" + i; ops++; // O(1) hash lookup counted as 1 op if (!resultMap.containsKey(r)) { resultMap.put(r, r); } } return ops; } // --- Slow path: add() pattern --- // Simulates: if result not in self.results: self.results.append(result) // Called N times for sequential task completions in a chord group. static long slowAdd(int N) { long ops = 0; List results = new ArrayList<>(); for (int i = 0; i < N; i++) { String r = "result-" + i; // Scan entire list for membership check for (String existing : results) { ops++; if (existing.equals(r)) break; } // Not found → append (all N items are unique, so full scan each time) results.add(r); } return ops; } // --- Fast path: add() pattern --- static long fastAdd(int N) { long ops = 0; Map resultMap = new LinkedHashMap<>(); for (int i = 0; i < N; i++) { String r = "result-" + i; ops++; // O(1) lookup resultMap.putIfAbsent(r, r); } return ops; } static void runTest(String label, int N, long slowOps, long fastOps) { double ratio = (double) slowOps / fastOps; boolean pass = ratio >= 10.0; System.out.printf(" %-14s N=%-5d slow=%,8d fast=%,5d ratio=%6.1fx %s%n", label, N, slowOps, fastOps, ratio, pass ? "PASS" : "FAIL"); if (!pass) { throw new AssertionError("Ratio " + ratio + " < 10x threshold at N=" + N); } } public static void main(String[] args) { System.out.println("celery-0001: ResultSetAlgorithm"); System.out.println(" Defect: list.contains() per element in update()/add() → O(N²)"); System.out.println(" Fix: dict/map key lookup → O(N)"); System.out.println(); int[] sizes = {200, 500, 1000}; int passed = 0; int total = sizes.length * 2; for (int N : sizes) { // Test update(): merge N new results into N existing long slowU = slowUpdate(N, N); long fastU = fastUpdate(N, N); try { runTest("update", N, slowU, fastU); passed++; } catch (AssertionError e) { System.out.println(" FAIL update: " + e.getMessage()); } // Test add(): add N items one at a time (each unique) long slowA = slowAdd(N); long fastA = fastAdd(N); try { runTest("add", N, slowA, fastA); passed++; } catch (AssertionError e) { System.out.println(" FAIL add: " + e.getMessage()); } } System.out.println(); System.out.println(passed + "/" + total + " PASS"); if (passed < total) { System.exit(1); } } }