Status: implemented and evidence-gated · Owner: Phase 6 under root epic
extending-liblevenshtein-automaton-families-4bb97598
Implement budget-bounded unrestricted Damerau–Levenshtein as a static
AutomatonVariant whose DamerauPending positions carry one endpoint delta.
Keep State and public query iterator types concrete. Select the variant once
per dictionary edge through with_variant!.
This design preserves every Standard transition, adds exact Lowrance–Wagner
macro chains, keeps Position at 24 bytes on 64-bit targets, and avoids
alphabet-sized history in a state.
Algorithm is a runtime, serde, FFI, WASM, and REPL value. Parameterizing
the public iterator by a variant would multiply public types and force
boxing or a breaking signature change.OperationSet represents alignments. True Damerau is history-dependent and
cannot be recovered from a local transposition tuple.Algorithm::DamerauLevenshtein maps to VariantSpec::Damerau, which selects
DamerauV. transition_state_inner computes a characteristic vector at each
position's origin. DamerauV::successors delegates to
transition_damerau_into. The shared state insertion kernel then applies the
variant's kind-aware subsumption rule.
The state-flow diagram shows the complete lifecycle. The detailed recurrence and literate pseudocode live in the algorithm chapter.
For every reachable position:
Normal implies aux == 0.DamerauPending implies $1\le\mathrm{aux}\le255$.aux + 1 query
units from the stored origin.The public transition boundary rejects a true-Damerau budget above 255 before constructing state. This converts a former silent loss of macros into an explicit contract failure without changing the Phase 5 layout/code-generation decision.
Normal/normal uses the classic diagonal rule. Pending/pending dominance requires equal origin and delta plus non-greater cost. All cross-kind pairs are incomparable. This is deliberately conservative: a normal path and a pending path consume different future languages even if their indices and costs match.
It is direct but makes state size depend on the alphabet and would copy or share a map on each dictionary edge. The bounded macro needs only the one endpoint currently owed.
Position<V> and State<V>This pushes variant type parameters through every iterator and backend. The closed, one-dispatch-per-edge seam already gives static hot loops without that API multiplication.
OperationSetThe projection computes OSA because every tuple consumes its symbols once. The public conversion remains for local-repertoire compatibility and is documented as lossy.
PositionF64 and the phonetic products have no delta/history carrier. They now
fail explicitly rather than silently label OSA or Standard behavior as true
Damerau.
Rocq proves arithmetic and transition invariants without axioms or admissions.
Verus checks a Rust-shaped executable model. Z3 and cvc5 independently show
the negations of five bounded laws are unsatisfiable. TLA+ explores every
state of the $k=3$ entry/extension/resolution protocol and checks budget,
validity, and no-pending-epsilon invariants. Each law is mirrored in focused or
generated Rust tests so the proof is connected to implementation behavior.
The repeated-unit release profile measured maximum state sizes 2, 4, and 7 at
budgets 1, 2, and 3. The one-position successor buffer did not spill. The
Birkbeck release gate observed 136 true/OSA separators among 42,395 explicit
pairs and no direction violation. Its restored DoubleArrayTrie arm verifies
the exact inventory before checking 37,472 budget-eligible corrections. Full
results and protocol amendments are in
the scientific ledger.
The design refines the last-occurrence recurrence of Lowrance and Wagner, DOI 10.1145/321879.321880.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |