import { test, expect } from "bun:test"; import { dtw } from "../../src/align/dtw.ts"; // Ground-truth path: diagonal with a kink (j runs ahead by 10 after i=50). function truthJ(i: number): number { return i < 50 ? i : i + 10; } const N = 100, M = 110; // Path cells cost 0, others grow with distance from the true path. const cost = (i: number, j: number) => { const t = truthJ(i); return Math.min(1, Math.abs(j - t) * 0.2); }; test("dtw recovers diagonal-with-kink path", () => { const p = dtw(cost, N, M); expect(p[0]).toEqual([0, 0]); expect(p[p.length - 1]).toEqual([N - 1, M - 1]); for (const [i, j] of p) if (i !== 49 && i !== 50) expect(Math.abs(j - truthJ(i))).toBeLessThanOrEqual(1); for (let k = 1; k < p.length; k++) { const di = p[k]![0] - p[k - 1]![0], dj = p[k]![1] - p[k - 1]![1]; expect(di >= 0 && di <= 1 && dj >= 0 && dj <= 1 && di + dj >= 1).toBe(true); } }); test("banded dtw equals unbanded when the band covers the path", () => { const full = dtw(cost, N, M); const banded = dtw(cost, N, M, { center: (i) => truthJ(i), width: 15 }); expect(banded).toEqual(full); }); test("banded dtw only visits cells in the band", () => { let calls = 0; const p = dtw((i, j) => { calls++; return Math.abs(i - j) * 0.1; }, 200, 200, { center: (i) => i, width: 5 }); expect(calls).toBeLessThanOrEqual(200 * 11); expect(p[p.length - 1]).toEqual([199, 199]); }); test("3000x3000 unbanded dtw finishes in < 5 s", () => { const n = 3000; const t0 = performance.now(); const p = dtw((i, j) => Math.abs(i - j) * 0.001 + ((i * 7 + j * 13) % 5) * 0.0001, n, n); const ms = performance.now() - t0; console.log(`dtw 3000x3000: ${ms.toFixed(0)} ms`); expect(p[p.length - 1]).toEqual([n - 1, n - 1]); expect(ms).toBeLessThan(5000); });