package unit; import java.util.ArrayList; import java.util.Collections; /** * Unit tests modelling CWE-407 defect in GNU Octave: * * octave-0001 — data.cc and numeric/max.cc: vecdim is already sorted by * std::sort, but the subsequent loop uses std::find (O(|vecdim|)) * instead of std::binary_search (O(log |vecdim|)). * * Modelled as: outer loop over ndims=64 dimensions, inner * ArrayList.contains() (unsorted scan, O(|vecdim|)) vs * Collections.binarySearch() on a sorted list (O(log |vecdim|)). * * Pure Java stdlib, instrumented operation counts. */ public class OctaveVecdimTest { /** * Simulate defective path: ndims iterations, std::find scan per iteration. * vecdim has n entries drawn from [0, ndims). */ static long vecdimDefective(int ndims, int vecdimSize) { ArrayList vecdim = new ArrayList<>(); // Populate sorted vecdim with every other dim (representative subset) for (int i = 0; i < vecdimSize; i++) { vecdim.add(i * (ndims / vecdimSize)); } // Mimics std::sort already done — list is sorted Collections.sort(vecdim); long ops = 0; for (int i = 0; i < ndims; i++) { // O(|vecdim|) — mirrors std::find(vecdim.begin(), vecdim.end(), i) boolean found = false; for (int j = 0; j < vecdim.size(); j++) { ops++; if (vecdim.get(j).equals(i)) { found = true; break; } // std::find does not exploit sort order; keep scanning past hits // would continue, but we break on first match (same as std::find) } // remaining iterations for elements not in vecdim scan to the end if (!found) { // already counted full scan above — no extra ops needed } } return ops; } /** * Simulate defective path with full scan (no early exit) to model worst * case where dims not in vecdim cause a full O(|vecdim|) scan. */ static long vecdimDefectiveWorstCase(int ndims, int vecdimSize) { ArrayList vecdim = new ArrayList<>(); for (int i = 0; i < vecdimSize; i++) { vecdim.add(i * (ndims / vecdimSize)); } Collections.sort(vecdim); long ops = 0; for (int i = 0; i < ndims; i++) { // Full O(|vecdim|) scan regardless (std::find scans all when not found) for (int j = 0; j < vecdim.size(); j++) { ops++; if (vecdim.get(j).equals(i)) break; } } return ops; } /** * Simulate fixed path: ndims iterations, Collections.binarySearch per iteration. * Each binarySearch is O(log |vecdim|). */ static long vecdimFixed(int ndims, int vecdimSize) { ArrayList vecdim = new ArrayList<>(); for (int i = 0; i < vecdimSize; i++) { vecdim.add(i * (ndims / vecdimSize)); } Collections.sort(vecdim); long ops = 0; for (int i = 0; i < ndims; i++) { // O(log |vecdim|) — mirrors std::binary_search(vecdim.begin(), vecdim.end(), i) int lo = 0, hi = vecdim.size() - 1; while (lo <= hi) { ops++; int mid = (lo + hi) >>> 1; int cmp = vecdim.get(mid).compareTo(i); if (cmp < 0) lo = mid + 1; else if (cmp > 0) hi = mid - 1; else break; } } return ops; } // ----------------------------------------------------------------------- // Test methods // ----------------------------------------------------------------------- /** * Defective op count must grow as ndims × |vecdim|. * Confirm: opsLarge > opsSmall × 3 when scaling ndims from 16 to 64. */ static void testDefectiveCountGrows() { long opsSmall = vecdimDefectiveWorstCase(16, 8); long opsFull = vecdimDefectiveWorstCase(64, 32); assert opsFull > opsSmall * 3 : "octave-0001: expected super-linear growth, opsSmall=" + opsSmall + " opsFull=" + opsFull; System.out.printf("PASS testDefectiveCountGrows: opsSmall=%d opsFull=%d ratio=%.1fx%n", opsSmall, opsFull, (double) opsFull / opsSmall); } /** * Fixed op count must be O(ndims × log|vecdim|). * At ndims=64, vecdimSize=32: ops <= ndims * ceil(log2(32)+1) = 64*6 = 384. */ static void testFixedCountLogBound() { int ndims = 64, vecdimSize = 32; long ops = vecdimFixed(ndims, vecdimSize); long bound = (long) ndims * (long) Math.ceil(Math.log(vecdimSize) / Math.log(2) + 1); assert ops <= bound : "octave-0001: fixed ops=" + ops + " exceeded log bound=" + bound; System.out.printf("PASS testFixedCountLogBound: ops=%d bound=%d%n", ops, bound); } /** * Speedup ratio defective/fixed must exceed 5× at ndims=64, vecdimSize=32. */ static void testSpeedupRatio() { int ndims = 64, vecdimSize = 32; long defOps = vecdimDefectiveWorstCase(ndims, vecdimSize); long fixOps = vecdimFixed(ndims, vecdimSize); double ratio = (double) defOps / fixOps; assert ratio > 5.0 : "octave-0001: speedup ratio " + ratio + " not > 5x"; System.out.printf("PASS testSpeedupRatio: defective=%d fixed=%d ratio=%.1fx%n", defOps, fixOps, ratio); } /** * Correctness: defective and fixed paths agree on which dims are excluded. * Both should exclude the same set of dims not present in vecdim. */ static void testCorrectnessAgreement() { int ndims = 64, vecdimSize = 32; ArrayList vecdim = new ArrayList<>(); for (int i = 0; i < vecdimSize; i++) { vecdim.add(i * (ndims / vecdimSize)); } Collections.sort(vecdim); ArrayList excludedByFind = new ArrayList<>(); ArrayList excludedByBsearch = new ArrayList<>(); for (int i = 0; i < ndims; i++) { // std::find equivalent if (!vecdim.contains(i)) excludedByFind.add(i); // std::binary_search equivalent if (Collections.binarySearch(vecdim, i) < 0) excludedByBsearch.add(i); } assert excludedByFind.equals(excludedByBsearch) : "octave-0001: correctness mismatch: find=" + excludedByFind + " bsearch=" + excludedByBsearch; System.out.printf("PASS testCorrectnessAgreement: %d dims excluded (identical)%n", excludedByFind.size()); } // ----------------------------------------------------------------------- // Main // ----------------------------------------------------------------------- public static void main(String[] args) { System.out.println("=== OctaveVecdimTest ==="); testDefectiveCountGrows(); testFixedCountLogBound(); testSpeedupRatio(); testCorrectnessAgreement(); System.out.println("All tests passed."); } }