import java.util.*; /** * musescore-0001: CWE-407 pastedHarmony dedup O(A*H) -> O(A) fix. * * Simulates the pattern in Read400/Read410/Read460::pasteStaff where * pastedHarmony was a std::vector, causing O(A * H) dedup during paste * operations with many chord symbols (Harmony elements). * * Fix: replace std::vector + std::find with std::unordered_set + .find(). */ public class MuseScore0001Test { // Simulate: vector-based dedup (defect - O(A*H)) static int simulatePasteVectorDedup(int numAnnotations, int numHarmonies) { List pastedHarmony = new ArrayList<>(); int ops = 0; for (int h = 0; h < numHarmonies; h++) { int harmonyId = h; // For each harmony pasted: scan existing annotations for (int a = 0; a < numAnnotations; a++) { // std::find scan: O(pastedHarmony.size()) ops += pastedHarmony.size() + 1; // linear scan cost // annotation not in pastedHarmony -> would be removed } pastedHarmony.add(harmonyId); } return ops; } // Simulate: unordered_set-based dedup (fix - O(A)) static int simulatePasteSetDedup(int numAnnotations, int numHarmonies) { Set pastedHarmony = new HashSet<>(); int ops = 0; for (int h = 0; h < numHarmonies; h++) { int harmonyId = h; // For each harmony pasted: O(1) hash lookup per annotation for (int a = 0; a < numAnnotations; a++) { ops += 1; // O(1) hash set lookup } pastedHarmony.add(harmonyId); } return ops; } public static void main(String[] args) { System.out.println("musescore-0001: pastedHarmony dedup O(A*H) -> O(A)"); // Small case: 10 annotations, 10 harmonies int vectorOpsSmall = simulatePasteVectorDedup(10, 10); int setOpsSmall = simulatePasteSetDedup(10, 10); System.out.printf(" N=10x10: vector=%d ops, set=%d ops%n", vectorOpsSmall, setOpsSmall); assert vectorOpsSmall > setOpsSmall : "vector should be more expensive"; // Medium: 50 annotations, 50 harmonies (large score paste) int vectorOpsMed = simulatePasteVectorDedup(50, 50); int setOpsMed = simulatePasteSetDedup(50, 50); System.out.printf(" N=50x50: vector=%d ops, set=%d ops%n", vectorOpsMed, setOpsMed); assert vectorOpsMed > setOpsMed : "vector should be more expensive"; // Large: 200 annotations, 200 harmonies (big jazz/leadsheet paste) int vectorOpsLarge = simulatePasteVectorDedup(200, 200); int setOpsLarge = simulatePasteSetDedup(200, 200); double ratio = (double) vectorOpsLarge / setOpsLarge; System.out.printf(" N=200x200: vector=%d ops, set=%d ops, ratio=%.1fx%n", vectorOpsLarge, setOpsLarge, ratio); assert ratio > 50.0 : "expected >50x speedup at N=200, got " + ratio; // Verify correctness: set dedup produces same membership result List vectorResult = new ArrayList<>(); Set setResult = new HashSet<>(); Random rand = new Random(42); List annotations = new ArrayList<>(); for (int i = 0; i < 20; i++) annotations.add(rand.nextInt(15)); // Vector approach: add 10 harmonies, skip annotations already in list List vectorRemoved = new ArrayList<>(); for (int h = 0; h < 10; h++) { for (int ann : annotations) { if (!vectorResult.contains(ann)) { vectorRemoved.add(ann); } } vectorResult.add(h); } // Set approach: same logic with hash set List setRemoved = new ArrayList<>(); for (int h = 0; h < 10; h++) { for (int ann : annotations) { if (!setResult.contains(ann)) { setRemoved.add(ann); } } setResult.add(h); } assert vectorRemoved.equals(setRemoved) : "vector and set approaches must produce identical removal lists"; System.out.println(" PASS: correctness verified, vector and set produce identical results"); System.out.printf(" PASS: %.1fx speedup confirmed at N=200%n", ratio); System.out.println("PASS"); } }