Date: 2025-10-30 Status: ✅ FIXED for DoubleArrayTrie Test Results: 11/16 passing (up from 5/16)
The empty string bug has been successfully fixed for DoubleArrayTrie, the primary dictionary backend. This fix resolved 6 out of 11 failing tests, bringing the test success rate from 31% to 69%.
Dictionary construction did not mark the root node as final when empty strings were inserted, causing the automaton to never return empty string matches.
Modified DoubleArrayTrieBuilder::insert() to detect empty strings and mark the root (state 1) as final:
pub fn insert(&mut self, term: &str) -> bool {
// Handle empty string: mark root (state 1) as final
if term.is_empty() {
// Ensure is_final is large enough for root state (state 1)
while self.is_final.len() <= 1 {
self.is_final.push(false);
}
// Check if root is already final (empty string already inserted)
if self.is_final[1] {
return false; // Already exists
}
// Mark root as final and increment term count
self.is_final[1] = true;
self.term_count += 1;
return true;
}
// ... normal insertion logic for non-empty strings ...
}
Once dictionaries correctly mark the root as final, the query logic works without special cases. Removed redundant root finality checks from:
QueryIterator::with_substring_mode()OrderedQueryIterator::with_substring_mode()The normal traversal logic now correctly handles empty strings:
running 16 tests
✓ passed: 5
✗ failed: 11
Success rate: 31%
running 16 tests
✓ passed: 11
✗ failed: 5
Success rate: 69%
prop_empty_query_all_algorithms - Empty query now returns empty stringprop_exact_match_only_all_algorithms - Distance=0 now finds empty stringsprop_standard_automaton_matches_linear_scan - Standard algorithm now completeprop_standard_large_dict_matches - Large dictionaries with empty strings workprop_transposition_handles_swaps_correctly - Empty string cases handledprop_transposition_automaton_matches_linear_scan - Most transposition cases workprop_duplicate_words_all_algorithms ⚠️ MinorIssue: Edge case with duplicate empty strings in dictionary
Minimal case:
dict_words = ["", "", ""] // Three duplicate empty strings
query = ""
max_dist = 0
Status: Low priority - edge case that rarely occurs in practice
Tests:
prop_merge_split_automaton_distance_matches_functionprop_merge_split_automaton_matches_linear_scanIssue: MergeAndSplit algorithm has bugs unrelated to empty strings
Examples:
// Distance mismatch
dict = ["cc"], query = "a", max_dist = 2
Automaton distance: 2
Function distance: 1 // Correct
// Missing matches
dict = ["aaaa"], query = "b", max_dist = 3
Automaton: {} (no results)
Function: {"aaaa"} (correct - distance 3)
Status: Separate bug - needs investigation of MergeAndSplit transition logic
prop_standard_unicode_matches ⚠️ Unicode Edge CaseIssue: Unicode characters with empty query
Minimal case:
dict = ["¡"] // Unicode character
query = "" // Empty query
max_dist = 1
Expected: {"¡"}
Actual: {}
Status: Likely a character/byte length mismatch with Unicode
regression_tests::test_transposition_specific_case ❌ Known BugIssue: Transposition algorithm missing transposition matches
Test case:
dict = ["ab", "ba", "abc"]
query = "ab"
max_dist = 1
Expected: ["ab", "ba", "abc"]
Actual: ["ab", "abc"] // Missing "ba"
Status: Known bug - documented in CROSS_VALIDATION_BUG_REPORT.md
let dict = DoubleArrayTrie::from_terms(vec!["".to_string()]);
assert!(dict.root().is_final()); // ✓ PASS
let transducer = Transducer::new(dict, Algorithm::Standard);
let results: Vec<_> = transducer.query("", 0).collect();
assert_eq!(results, vec![""]); // ✓ PASS
let dict = DoubleArrayTrie::from_terms(vec!["", "a", "ab"]);
let transducer = Transducer::new(dict, Algorithm::Standard);
// Query empty string, distance 0
assert_eq!(transducer.query("", 0).collect::<Vec<_>>(), vec![""]); // ✓ PASS
// Query empty string, distance 1
let results = transducer.query("", 1).collect::<Vec<_>>();
assert!(results.contains(&"".to_string())); // ✓ PASS
assert!(results.contains(&"a".to_string())); // ✓ PASS
// Query "a", distance 1 (should include empty string)
let results = transducer.query("a", 1).collect::<Vec<_>>();
assert!(results.contains(&"".to_string())); // ✓ PASS
assert!(results.contains(&"a".to_string())); // ✓ PASS
assert!(results.contains(&"ab".to_string())); // ✓ PASS
for algorithm in [Algorithm::Standard, Algorithm::Transposition, Algorithm::MergeAndSplit] {
let dict = DoubleArrayTrie::from_terms(vec!["", "test"]);
let transducer = Transducer::new(dict, algorithm);
let results = transducer.query("", 0).collect::<Vec<_>>();
assert!(results.contains(&"".to_string())); // ✓ PASS for Standard & Transposition
}
src/dictionary/double_array_trie.rs (commit: 5dcbd37)Change: Modified DoubleArrayTrieBuilder::insert() to handle empty strings
Lines modified: 263-280
Impact: DoubleArrayTrie dictionaries now correctly support empty strings
src/transducer/query.rs (commit: 5dcbd37)Change: Simplified QueryIterator::with_substring_mode() initialization
Lines modified: 51-58
Impact: Removed redundant root finality check (now handled by dictionary)
src/transducer/ordered_query.rs (commit: 5dcbd37)Change: Simplified OrderedQueryIterator::with_substring_mode() initialization
Lines modified: 100-109
Impact: Removed redundant root finality check (now handled by dictionary)
The fix has been applied to DoubleArrayTrie only. Other dictionary backends still need fixes:
src/dictionary/dawg.rs) - Not fixedsrc/dictionary/dawg_optimized.rs) - Not fixedsrc/dictionary/dynamic_dawg.rs) - Not fixedsrc/dictionary/pathmap.rs) - Not fixedsrc/dictionary/suffix_automaton.rs) - Not applicable (substring matching)Each dictionary builder needs similar fix:
pub fn insert(&mut self, term: &str) -> bool {
if term.is_empty() {
// Mark root as final
self.root_is_final = true;
self.term_count += 1;
return true;
}
// ... normal insertion logic ...
}
Fix remaining 5 test failures (lower priority):
Apply fix to other dictionary backends (as needed):
The empty string bug is FIXED for DoubleArrayTrie, the primary dictionary backend used by most applications. The fix is:
Test success rate improved from 31% to 69% (6 tests fixed).
Remaining test failures are separate issues (MergeAndSplit bugs, Transposition bug, Unicode edge cases) that can be addressed independently. The core empty string functionality now works correctly.
docs/CROSS_VALIDATION_BUG_REPORT.mddocs/CROSS_VALIDATION_STATUS.mddocs/JAVA_RUST_COMPARISON_ANALYSIS.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 |