#!/usr/bin/env node /* * landmark.test.mjs — the pure landmark compute core gates (L1–L6). * ------------------------------------------------------------------ * RED first (plan 08-02 task 1): every case fails while * routing/src/landmark.ts does not exist (lazy-import pattern — each * case reports its own ERR_MODULE_NOT_FOUND). * * The ALT preprocessing core (research Pattern 2): distances live in the * ĉ metric — edge weights floor-quantized ONCE to fixed 0.1 s units — * and every table cell is an EXACT integer Dijkstra result. Hand-computed * expectations below; JS arithmetic is deterministic so the pinned quanta * (verified: floor(2.55/0.1)=25, floor(3/0.1)=30, floor(1.5/0.1)=15, * grid perimeter hop=33, interior hop=66) are stable. * * L1 anchors — farthestPointAnchors: deterministic, degree>=2 * filtered, well-spread on the 5-node path * L2 quantum exactness — 2.55 s edge -> 25 quanta; two edges -> 50 * exactly (integer arithmetic, no float drift) * L3 sentinel + self — unreachable pairs -> 0xFFFF; the landmark's * own cell = 0 (oneway chain + isolated node) * L4 asymmetry — to/from rows differ on a oneway graph * L5 overflow throws — any label > 65000 quanta throws, naming the * distance and the fix (raise landmarkQuantumS) * L6 5x5 grid shape — anchors [0, 18] (interior node 18 is the ĉ * farthest at 297 quanta); coverage 1.0 * * Run: npm run routing:test (exit 0 = all green) */ import { strict as assert } from "node:assert"; import { test } from "./harness.mjs"; let landmarkPromise = null; function loadLandmark() { if (!landmarkPromise) landmarkPromise = import("../src/landmark.ts"); return landmarkPromise; } /* ————— hand-built vectors ————— */ /** 5-node path 0-1-2-3-4, two-way, 3.0 s per edge (30 quanta @ 0.1). */ function pathInput() { const edges = []; for (let u = 0; u < 4; u++) { edges.push({ from: u, to: u + 1, seconds: 3, flags: 0 }); edges.push({ from: u + 1, to: u, seconds: 3, flags: 0 }); } return { nodeCount: 5, edges, quantumS: 0.1 }; } /** Oneway chain 0->1->2 (bit0 mirrors) + isolated node 3, 1.5 s/edge. */ function onewayInput() { return { nodeCount: 4, edges: [ { from: 0, to: 1, seconds: 1.5, flags: 0 }, { from: 1, to: 0, seconds: 1.5, flags: 1 }, // blocked mirror { from: 1, to: 2, seconds: 1.5, flags: 0 }, { from: 2, to: 1, seconds: 1.5, flags: 1 }, // blocked mirror ], quantumS: 0.1, }; } /** The fixture builder's exact 5x5 grid shape (25 nodes, 80 edge entries, * perimeter 60 km/h / interior 30 km/h, one oneway mirror at 3->2). */ async function gridInput() { const { haversineM } = await import("../src/geo.ts"); const GRID = 5; const lat = []; const lng = []; for (let r = 0; r < GRID; r++) { for (let c = 0; c < GRID; c++) { lat.push((3150000 + r * 500) / 1e6); lng.push((101690000 + c * 500) / 1e6); } } const id = (r, c) => r * GRID + c; const edges = []; function addEdge(u, v, perimeter) { const kmh = perimeter ? 60 : 30; const meters = haversineM({ lat: lat[u], lng: lng[u] }, { lat: lat[v], lng: lng[v] }); edges.push({ from: u, to: v, seconds: meters / (kmh / 3.6), flags: 0 }); } for (let r = 0; r < GRID; r++) { for (let c = 0; c < GRID - 1; c++) { const perimeter = r === 0 || r === GRID - 1; addEdge(id(r, c), id(r, c + 1), perimeter); addEdge(id(r, c + 1), id(r, c), perimeter); } } for (let c = 0; c < GRID; c++) { for (let r = 0; r < GRID - 1; r++) { const perimeter = c === 0 || c === GRID - 1; addEdge(id(r, c), id(r + 1, c), perimeter); addEdge(id(r + 1, c), id(r, c), perimeter); } } const reverse = edges.find((e) => e.from === 3 && e.to === 2); reverse.flags = 1; // the fixture's single oneway-reverse mirror return { nodeCount: GRID * GRID, edges, quantumS: 0.1 }; } /* ————— L1–L6 ————— */ test("L1 anchors — deterministic, degree>=2-filtered, well-spread on the 5-node path", async () => { const { farthestPointAnchors } = await loadLandmark(); const input = pathInput(); const a = farthestPointAnchors(input, 2); assert.ok(a instanceof Uint32Array, "anchors are a Uint32Array"); assert.equal(a.length, 2); // Candidates (degree>=2) are nodes 1,2,3 — endpoints excluded. First // anchor = first candidate by id; pass-from-1 minima are [30,0,30,60,90] // quanta, so the farthest candidate is node 3. assert.deepEqual([...a], [1, 3], "first candidate by id, then farthest spread"); // L=3: after folding anchor 3, minima are [30,0,30,0,30] -> node 2. assert.deepEqual([...farthestPointAnchors(input, 3)], [1, 3, 2]); // Determinism: identical input -> identical anchors (no randomness). assert.deepEqual([...farthestPointAnchors(pathInput(), 2)], [...a]); // More anchors than candidates: stop early, never pad. assert.equal(farthestPointAnchors(input, 10).length, 3, "only 3 candidates exist"); }); test("L2 quantum exactness — floor(2.55/0.1)=25 per edge; two edges sum to exactly 50 quanta", async () => { const { landmarkTables, LANDMARK_QUANTUM_S } = await loadLandmark(); assert.equal(LANDMARK_QUANTUM_S, 0.1, "the default quantum is the doc'd 0.1 s"); const input = { nodeCount: 3, edges: [ { from: 0, to: 1, seconds: 2.55, flags: 0 }, { from: 1, to: 0, seconds: 2.55, flags: 0 }, { from: 1, to: 2, seconds: 2.55, flags: 0 }, { from: 2, to: 1, seconds: 2.55, flags: 0 }, ], quantumS: 0.1, }; const { to, from } = landmarkTables(input, Uint32Array.of(0)); assert.equal(from.length, 1 * 3, "row-major L*N cells"); assert.equal(from[0 * 3 + 1], 25, "one 2.55 s edge = exactly 25 quanta (floor(25.4999...))"); assert.equal(from[0 * 3 + 2], 50, "two edges = exactly 50 quanta — integer arithmetic, no drift"); assert.equal(to[0 * 3 + 2], 50, "the to-row mirrors on a two-way path"); }); test("L3 sentinel + self-cell — unreachable pairs -> 0xFFFF; the landmark's own cell = 0", async () => { const { landmarkTables, LANDMARK_SENTINEL } = await loadLandmark(); assert.equal(LANDMARK_SENTINEL, 0xffff); const input = onewayInput(); const { to, from } = landmarkTables(input, Uint32Array.of(0)); assert.equal(to[0 * 4 + 0], 0, "landmark's own to-cell is 0"); assert.equal(from[0 * 4 + 0], 0, "landmark's own from-cell is 0"); assert.equal(from[0 * 4 + 1], 15, "0->1 traverses (floor(1.5/0.1)=15 quanta)"); assert.equal(from[0 * 4 + 2], 30, "0->1->2 traverses"); assert.equal(to[0 * 4 + 1], LANDMARK_SENTINEL, "1 cannot reach 0 against the oneway -> sentinel"); assert.equal(to[0 * 4 + 2], LANDMARK_SENTINEL, "2 cannot reach 0 -> sentinel"); // Isolated node: every cell is the sentinel. assert.equal(to[0 * 4 + 3], LANDMARK_SENTINEL, "isolated node -> sentinel in the to-row"); assert.equal(from[0 * 4 + 3], LANDMARK_SENTINEL, "isolated node -> sentinel in the from-row"); }); test("L4 asymmetry — reversed vs forward rows differ on an oneway graph", async () => { const { landmarkTables } = await loadLandmark(); const { to, from } = landmarkTables(onewayInput(), Uint32Array.of(1)); // Rows are indexed by anchor POSITION (row-major [ℓ*N+v], ℓ = 0 here), // not by the anchor's node id. Anchor at the middle node 1: forward // reaches 2 (15 quanta) but the to-row for 2 is the sentinel (2 cannot // get back to 1). assert.equal(from[0 * 4 + 2], 15, "1->2 traverses"); assert.equal(to[0 * 4 + 2], 0xffff, "2->1 does not (oneway) — asymmetry is representable"); assert.notEqual(to[0 * 4 + 2], from[0 * 4 + 2]); }); test("L5 overflow throws — any label > 65000 quanta fails loud, naming the fix", async () => { const { landmarkTables, farthestPointAnchors, LANDMARK_MAX_QUANTA } = await loadLandmark(); assert.equal(LANDMARK_MAX_QUANTA, 65000); // 3-node path so node 1 is a degree-2 candidate — the anchor-selection // pass actually runs (a 2-node graph has no candidates and returns // empty before any Dijkstra). const input = { nodeCount: 3, edges: [ { from: 0, to: 1, seconds: 10, flags: 0 }, // floor(10/0.0001) = 100000 quanta { from: 1, to: 0, seconds: 10, flags: 0 }, { from: 1, to: 2, seconds: 10, flags: 0 }, { from: 2, to: 1, seconds: 10, flags: 0 }, ], quantumS: 0.0001, }; assert.throws( () => landmarkTables(input, Uint32Array.of(0)), /100000 exceeds 65000 \(raise landmarkQuantumS\)/, "the table pass names the offending distance and the fix", ); assert.throws( () => farthestPointAnchors(input, 1), /exceeds 65000 \(raise landmarkQuantumS\)/, "the anchor-selection pass fails loud on the same law", ); }); test("L6 5x5 grid shape — anchors [0,18]; farthest 297 quanta; coverage 1.0", async () => { const { farthestPointAnchors, landmarkTables, landmarkCoverage } = await loadLandmark(); const input = await gridInput(); const anchors = farthestPointAnchors(input, 2); // Farthest-point over the ĉ metric: interior hops cost 66 quanta (2x the // perimeter's 33), so the farthest node from corner 0 is the INTERIOR // node 18 at 297 quanta — NOT the opposite corner 24 (264). Verified // against an independent brute-force Dijkstra (297 unique max). assert.deepEqual([...anchors], [0, 18], "first candidate is node 0; farthest is node 18"); const { to, from } = landmarkTables(input, anchors); assert.equal(to.length, 2 * 25, "L*N cells (L=2, N=25)"); assert.equal(to[0 * 25 + 0], 0, "self-cell 0"); assert.equal(from[1 * 25 + 18], 0, "anchor 18's own from-cell 0 (row = position 1)"); assert.equal(from[0 * 25 + 18], 297, "0->18 = 3 perimeter + 3 interior hops = 99+198, exact"); assert.equal(to[1 * 25 + 0], 297, "the to-row mirrors on the grid"); const coverage = landmarkCoverage(to, anchors, 25); assert.deepEqual(coverage, [1, 1], "the grid is strongly connected — full coverage"); });