operations: OperationSet field to struct (automaton.rs:106)new() constructor defaulting to OperationSet::standard() (automaton.rs:128-133)with_operations() constructor for custom operation sets (automaton.rs:161-166)Goal: Pass OperationSet from automaton down to state transition logic
Changes Required:
GeneralizedState::transition() signature needs operations: &OperationSet parameterGeneralizedAutomaton::accepts() must pass &self.operations to state.transition()Files to Modify:
src/transducer/generalized/state.rs:148-178src/transducer/generalized/automaton.rs (accept method call sites)Goal: Replace hardcoded standard operations with OperationSet iteration (Phase 2a)
Scope: Only single-character operations first:
Current Implementation:
successors_i_type_standard() (state.rs:216-301)successors_m_type_standard() (state.rs:303-356)Refactoring Strategy:
operations.operations():
op.can_apply(dict_chars, query_chars)consume_x, consume_y, weightImplementation Notes:
offset + n gives bit vector indexnew_errors = errors + op.weight() as u8Goal: Support operations with consume_x > 1 or consume_y > 1
Examples:
Challenges:
Key Implementation Points:
consume_x characters starting at current word positionconsume_y characters from current input positionword[i:i+2] == reversed input[j:j+2]Goal: Correctly handle unrestricted vs restricted operations
Unrestricted Operations:
Restricted Operations:
op.can_apply(dict_chars, query_chars) methodIntegration:
for op in operations.operations() {
// Extract characters
let dict_chars = &word_chars[pos..pos+op.consume_x()];
let query_chars = &input_chars[pos..pos+op.consume_y()];
// Check if operation can apply
if !op.can_apply(dict_chars, query_chars) {
continue; // Skip this operation
}
// Compute successor...
}
Test Cases Required:
A. Transposition Tests:
#[test]
fn test_transposition_adjacent_swap() {
let ops = OperationSet::with_transposition();
let automaton = GeneralizedAutomaton::with_operations(2, ops);
// "test" vs "tset" (swap 'e' and 's')
assert!(automaton.accepts("test", "tset"));
// "test" vs "etst" (swap at start)
assert!(automaton.accepts("test", "etst"));
}
#[test]
fn test_transposition_with_other_edits() {
let ops = OperationSet::with_transposition();
let automaton = GeneralizedAutomaton::with_operations(2, ops);
// Transposition + substitution
assert!(automaton.accepts("test", "txst")); // e→x + swap
}
B. Phonetic Tests:
#[test]
fn test_phonetic_ph_to_f() {
let mut phonetic = SubstitutionSet::new();
phonetic.allow_str("ph", "f");
let mut ops = OperationSetBuilder::new()
.with_match()
.with_operation(OperationType::with_restriction(
2, 1, 0.15,
phonetic,
"ph_to_f"
))
.with_standard_ops()
.build();
let automaton = GeneralizedAutomaton::with_operations(1, ops);
// "phone" vs "fone" (ph→f digraph)
assert!(automaton.accepts("phone", "fone"));
// "graph" vs "graf"
assert!(automaton.accepts("graph", "graf"));
}
C. Edge Cases:
Goal: Ensure GeneralizedAutomaton matches UniversalAutomaton for transposition
Test Setup:
#[test]
fn test_cross_validation_transposition() {
use crate::transducer::universal::{UniversalAutomaton, Transposition};
let universal: UniversalAutomaton<Transposition> = UniversalAutomaton::new(2);
let generalized = GeneralizedAutomaton::with_operations(
2,
OperationSet::with_transposition()
);
let test_cases = vec![
("test", "tset"), // adjacent swap
("test", "etst"), // swap at start
("test", "test"), // identical
("test", "tets"), // swap at end
("hello", "hlelo"), // 'e' and 'l' swap
];
for (word, input) in test_cases {
let universal_result = universal.accepts(word, input);
let gen_result = generalized.accepts(word, input);
assert_eq!(universal_result, gen_result,
"Mismatch for word='{}', input='{}'",
word, input);
}
}
Files to Update:
A. Module documentation (src/transducer/generalized/mod.rs):
B. Automaton documentation (src/transducer/generalized/automaton.rs):
with_operations() examplesC. State documentation (src/transducer/generalized/state.rs):
transition() docs to mention OperationSetD. README (docs/generalized/README.md):
Recommended sequence:
Thread OperationSet (30 min)
Refactor single-char operations (2-3 hours)
Add transposition tests (30 min)
Multi-character support (2-3 hours)
Operation matching logic (1 hour)
Phonetic tests (30 min)
Cross-validation (30 min)
Documentation (1 hour)
Total estimated time: 8-10 hours
SKIP-TO-MATCH: Must be preserved for standard operations
Out-of-window handling: Required for correctness
GeneralizedAutomaton::new() must default to standard operationsBit vector semantics:
Position tracking:
At each implementation step:
cargo test generalizedM-type position successors: Less well understood than I-type
Multi-character at word boundaries:
SKIP-TO-MATCH with multi-char:
Phase 2 is complete when:
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 |