Date: 2025-11-13 Status: Research Phase
Analyzing merge and split operations from the lazy automaton (src/transducer/transition.rs:380-495) to design the Universal automaton implementation.
Merge Operation: Two input characters combine into one word character
Split Operation: One input character expands into two word characters
Unlike transposition (which has intermediate state), split is also stateful:
Enter Split (transition.rs:415, 430, 449, 469):
!s (not in special state) and i < query_lengthPosition::new_special(i, e + 1)i, increments erroris_special = true (split state)Complete Split (transition.rs:459, 475, 490):
s (in special state)Position::new(i + 1, e)is_special = falseKey Insight: Split is similar to transposition's two-step process!
i#e → i#(e+1)_s (same i, increment error)i#(e+1)_s → (i+1)#e (advance i by 1, decrement error)Wait - this doesn't match the ⟨1,2,1⟩ notation! Let me re-analyze...
Looking more carefully at line 459:
// In special state (completing split)
next.push(Position::new(i + 1, e));
The split completes by advancing i by 1, but what about the word?
The key is that split is entered when checking cv[h] (current word position), and completed when checking cv[h] again at the NEXT input character. The bit vector advancement handles the "consume 2 word characters" part!
Corrected Understanding:
k, word position i, reading word char 1 of 2
i#e → i#(e+1)_sk+1, still at word position i, reading word char 2 of 2
i#(e+1)_s → (i+1)#eSo split allows consuming 2 consecutive word characters for 1 input character!
From lines 420, 454:
// Merge operation: skip 2 query chars (only if we have 2 chars available)
if i + 2 <= query_length {
next.push(Position::new(i + 2, e + 1));
}
Merge is simpler - it's a direct operation:
i#e → (i+2)#(e+1) in one stepJust like transposition, merge/split includes ALL standard operations (transition.rs:412-421, 446-455):
i#e → i#(e+1)i#e → (i+1)#(e+1)i#e → (i+1)#(e+1)Merge: i#e → (i+2)#(e+1)
At input k, position I+offset#e:
i = offset + kk+1: i+2 at input position k+1offset' + (k+1) = i+2 = (offset+k)+2offset' = offset + 1Enter Split: i#e → i#(e+1)_s
At input k, position I+offset#e:
i = offset + kk+1: still at i (same word position)offset' + (k+1) = i = offset+koffset' = offset - 1Complete Split: i#(e+1)_s → (i+1)#e
At input k, position I+offset#(e+1)_s:
i = offset + kk+1: i+1offset' + (k+1) = i+1 = (offset+k)+1offset' = offset| Operation | Enter | Complete |
|---|---|---|
| Transposition | offset - 1 | offset + 1 |
| Split | offset - 1 | offset + 0 |
| Merge | N/A (direct) | offset + 1 |
Interesting patterns:
offset - 1 (stay at same word position)#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MergeSplitState {
Usual, // Normal state
Splitting, // In the middle of split operation
}
For I-type positions:
Standard Operations (same as before)
Merge Operation (when not splitting)
// Merge: consume 2 inputs, 1 word char
if !is_splitting && next_match_index < bit_vector.len()
&& bit_vector.is_match(next_match_index) && errors < max_distance {
// i#e → (i+2)#(e+1)
// offset' = offset + 1
successors.push(UniversalPosition::new_i_with_state(
offset + 1,
errors + 1,
max_distance,
MergeSplitState::Usual,
));
}
Split Entry (when not splitting)
// Split entry: one input becomes two word chars
if !is_splitting && match_index < bit_vector.len()
&& bit_vector.is_match(match_index) && errors < max_distance {
// i#e → i#(e+1)_s
// offset' = offset - 1
successors.push(UniversalPosition::new_i_with_state(
offset - 1,
errors + 1,
max_distance,
MergeSplitState::Splitting,
));
}
Split Completion (when splitting)
if is_splitting && match_index < bit_vector.len() && bit_vector.is_match(match_index) {
// i#(e+1)_s → (i+1)#e
// offset' = offset + 0
successors.push(UniversalPosition::new_i_with_state(
offset,
errors - 1,
max_distance,
MergeSplitState::Usual,
));
}
Does the lazy automaton allow split at ANY position or only at specific boundaries?
i < query_length, so yes, allowed anywhereCan merge and split be combined in the same transition?
!s (not splitting), lines 419, 453What happens at word boundaries during split?
h + 1 == w boundary case, split still worksCan split complete even at max_distance?
e == max_distanceMergeSplitState enumMergeAndSplit trait with complete logicsrc/transducer/transition.rs:380-495docs/universal/transposition_phase2_summary.mdCan 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 |