/* 距离下界认证(2026-10-17,方案 §8.3 / §4.4) * 关节直线段 a→b 安全 ⇔ 对每个组对 (gA,gB)(gB ≥ gA+2): * gap_ab(a) + gap_ab(b) > Σ_{k=gA}^{gB-1} r_k·|Δθ_k| * gap 取 S.gapMatrix(距离 − need,含 margin),r_k 为 S.leverBounds 的每轴杠杆上界(mm/°)。 * 不满足就二分,子段 < MIN_DEG 仍不过则判不安全。连续轴按 unwrapTo 口径展开后再插值。 */ 'use strict'; module.exports = function makeCert(S, opt) { opt = opt || {}; const CAP = opt.cap || 40, MIN_DEG = opt.minDeg || 0.002; const R = opt.lever || S.leverBounds(4000); const L = S.LIM, O = S.ORDER, w = S.wrap180; const memo = new Map(); const key = q => q.map(v => v.toFixed(5)).join(','); let queries = 0; const gm = q => { const k = key(q); let M = memo.get(k); if (!M) { M = S.gapMatrix(q, CAP); queries++; if (memo.size > 50000) memo.clear(); memo.set(k, M); } return M; }; const minGap = M => { let m = Infinity; for (let a = 0; a < 7; a++) for (let b = a + 2; b < 7; b++) m = Math.min(m, M[a * 7 + b]); return m; }; const inBox = q => O.every((id, i) => L[id].cont || (q[i] >= L[id].lo - 1e-6 && q[i] <= L[id].hi + 1e-6)); function seg(a, b, depth) { const Ma = gm(a), Mb = gm(b); const dq = a.map((v, i) => Math.abs(b[i] - v)); let ok = true; for (let ga = 0; ga < 7 && ok; ga++) for (let gb = ga + 2; gb < 7; gb++) { const lhs = Ma[ga * 7 + gb] + Mb[ga * 7 + gb]; let rhs = 0; for (let k = ga; k < gb; k++) rhs += R[k] * dq[k]; if (!(lhs > rhs)) { ok = false; break; } } if (ok) return true; if (minGap(Ma) <= 0 || minGap(Mb) <= 0) return false; if (Math.max.apply(null, dq) < MIN_DEG || depth > 40) return false; const m = a.map((v, i) => 0.5 * (v + b[i])); return seg(a, m, depth + 1) && seg(m, b, depth + 1); } /* 整条折线:返回 {ok, queries, minGap, failAt} */ function path(pts) { const q0 = queries; let mg = Infinity; const P = [pts[0].slice()]; for (let i = 1; i < pts.length; i++) P.push(P[i - 1].map((v, k) => L[O[k]].cont ? v + w(pts[i][k] - v) : pts[i][k])); for (let i = 0; i < P.length; i++) { if (!inBox(P[i])) return { ok: false, queries: queries - q0, minGap: mg, failAt: i, why: 'box' }; mg = Math.min(mg, minGap(gm(P[i]))); } for (let i = 1; i < P.length; i++) if (!seg(P[i - 1], P[i], 0)) return { ok: false, queries: queries - q0, minGap: mg, failAt: i, why: 'cert' }; return { ok: true, queries: queries - q0, minGap: mg, unwrapped: P }; } return { seg: (a, b) => seg(a, b, 0), path, lever: R, gapMin: q => minGap(gm(q)) }; };