feat(08-02): ARG2 codec — one version, one decode path (G-suite GREEN)
graph.ts versioned IN PLACE (no second codec): GRAPH_MAGIC 0x41524732, GRAPH_VERSION 2, HEADER_SIZE 72 (72%8==0 keeps f64 views aligned); header gains landmarkCount u32 @60 (the old pad), landmarkQuantumS f32 @64, reserved u32 @68; the landmark suffix (u16 toTable, u16 fromTable, u32 landmarkNodes) extends layoutFor's total arithmetic — exact-size law covers the variable suffix BEFORE any view allocation (T-08-01). D-01 fail-honest: ARG1 bytes / version!=2 / landmarkCount=0 / hostile quantum / size mismatch all decode null (committed mini-graph-arg1.bin is the standing rejection vector). GraphInput.landmarks REQUIRED and loud-validated (T-08-04). RoadGraph gains the five landmark fields (Plan 08-03's ALT potentials consume them). Fixture regenerated as ARG2 through the SAME encode authority: the builder gains the tiny real landmark stage (L=2, anchors [0,18], quantum 0.1s, coverage 1.0 — identical bytes every re-run). Rule-3 call-site updates forced by the required-landmarks law (missed by the plan's files list): astar.test A3 + snap.test S3 synthetics carry inert hand landmark blocks. Fixture regen pulled forward from task 3 so this commit's own battery is green (74 passed, 0 failed).
This commit is contained in:
+43
-2
@@ -16,6 +16,12 @@
|
||||
* Usage: node routing/test/fixtures/build-mini-graph.mjs
|
||||
* Exit: 0 on write; throws on any invariant violation (never writes).
|
||||
*
|
||||
* ARG2 (plan 08-02): the fixture carries a tiny real landmark stage —
|
||||
* L=2 anchors via farthestPointAnchors + quantized c-metric tables via
|
||||
* landmarkTables (the SAME pure module the real build script uses), passed
|
||||
* through the ONE encodeGraph authority. Deterministic: identical output
|
||||
* bytes on every re-run.
|
||||
*
|
||||
* Design (pinned by 06-01): a hand-built 5x5 grid in a ~0.002 deg x 0.002 deg
|
||||
* bbox near KL center (lat 3.1500–3.1520, lng 101.6900–101.6920):
|
||||
* - 25 nodes, 80 directed edge entries (20 H + 20 V segments, both ways)
|
||||
@@ -28,6 +34,7 @@
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { encodeGraph } from "../../src/graph.ts";
|
||||
import { haversineM } from "../../src/geo.ts";
|
||||
import { LANDMARK_QUANTUM_S, farthestPointAnchors, landmarkTables, landmarkCoverage } from "../../src/landmark.ts";
|
||||
|
||||
const GRID = 5;
|
||||
const LAT0 = 3.15;
|
||||
@@ -85,6 +92,40 @@ reverse.flags = 1;
|
||||
|
||||
if (edges.length !== 80) throw new Error(`expected 80 directed edges, built ${edges.length}`);
|
||||
|
||||
const bin = encodeGraph({ lat, lng, edges, names, maxSpeedKmh: SPEED_PERIMETER_KMH });
|
||||
// LANDMARK STAGE (plan 08-02): the fixture regenerates as ARG2 THROUGH the
|
||||
// one encode authority — the same farthestPointAnchors + landmarkTables the
|
||||
// real build script (08-04) will run, at a tiny L. Fail-loud invariants
|
||||
// before any write (the `edges.length !== 80` style).
|
||||
const LANDMARK_COUNT = 2; // tiny L: the 5x5 grid needs exactly 2 well-spread anchors
|
||||
const landmarkInput = { nodeCount: GRID * GRID, edges, quantumS: LANDMARK_QUANTUM_S };
|
||||
const anchors = farthestPointAnchors(landmarkInput, LANDMARK_COUNT);
|
||||
const { to, from } = landmarkTables(landmarkInput, anchors);
|
||||
if (anchors.length !== LANDMARK_COUNT) throw new Error(`expected ${LANDMARK_COUNT} anchors, got ${anchors.length}`);
|
||||
if (to.length !== LANDMARK_COUNT * GRID * GRID || from.length !== LANDMARK_COUNT * GRID * GRID) {
|
||||
throw new Error("landmark tables are not L*N row-major");
|
||||
}
|
||||
for (let l = 0; l < anchors.length; l++) {
|
||||
const a = anchors[l];
|
||||
if (to[l * GRID * GRID + a] !== 0 || from[l * GRID * GRID + a] !== 0) {
|
||||
throw new Error(`anchor ${l} (node ${a}) own cells are not 0 — table law broken`);
|
||||
}
|
||||
}
|
||||
const coverage = landmarkCoverage(to, anchors, GRID * GRID);
|
||||
if (coverage.some((c) => c !== 1)) {
|
||||
throw new Error(`the grid is strongly connected — coverage must be 1.0, got ${coverage.join(", ")}`);
|
||||
}
|
||||
|
||||
const bin = encodeGraph({
|
||||
lat,
|
||||
lng,
|
||||
edges,
|
||||
names,
|
||||
maxSpeedKmh: SPEED_PERIMETER_KMH,
|
||||
landmarks: { quantumS: LANDMARK_QUANTUM_S, nodes: anchors, to, from },
|
||||
});
|
||||
writeFileSync(new URL("./mini-graph.bin", import.meta.url), Buffer.from(bin));
|
||||
console.log(`mini-graph.bin: ${GRID * GRID} nodes, ${edges.length} edges, 3 names, ${bin.byteLength} B`);
|
||||
console.log(
|
||||
`mini-graph.bin: ${GRID * GRID} nodes, ${edges.length} edges, 3 names, ` +
|
||||
`L=${LANDMARK_COUNT} landmarks (nodes ${[...anchors].join(",")}), quantum ${LANDMARK_QUANTUM_S}s, ` +
|
||||
`coverage ${coverage.map((c) => c.toFixed(2)).join("/")}, ${bin.byteLength} B`,
|
||||
);
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user