use std::iter;
use include_bytes_plus::include_bytes;
pub const fn get(index: u8) -> &'static [u8] {
const SYLLABLES: [&[u8]; 256] = include!("../static/syllables.txt");
SYLLABLES[index as usize]
}
pub fn longest_prefix_of(string: &str) -> Option<(u8, usize)> {
let mut node = Node::root();
let mut len = 0;
for char in string.chars() {
let child = char
.try_into()
.ok()
.and_then(|ascii| node.child(ascii));
let Some(child) = child else {
break
};
node = child;
len += 1;
}
node.syllable().map(|syllable| (syllable, len))
}
pub fn char_follows(char: u8, syllable: &[u8]) -> bool {
syllable.iter()
.copied()
.chain(iter::once(char))
.try_fold(Node::root(), Node::child)
.is_some()
}
#[derive(Clone, Copy, Debug)]
struct Node {
id: u32,
base: u32,
is_leaf: bool,
has_value: bool,
}
impl Node {
const fn root() -> Node {
Node {
id: 0,
base: base(0).1,
is_leaf: false,
has_value: false,
}
}
fn syllable(self) -> Option<u8> {
let syllable = match (self.has_value, self.is_leaf) {
(true, true) => Some(self.base),
(true, false) => Some(base(self.base).1),
(false, _) => None,
};
syllable.map(|x| x as u8)
}
fn child(self, char: u8) -> Option<Node> {
const TRANSLATION: [u8; 26] = include_bytes!("static/translation.bin");
let code = char
.to_ascii_lowercase()
.checked_sub(b'a')
.and_then(|code| TRANSLATION.get(code as usize))
.map(|&code| code as u32)?;
let id = self.base ^ code;
let (is_leaf, base) = base(id);
let (has_leaf, check) = check(id);
let node = Node {
id,
base,
is_leaf,
has_value: is_leaf || has_leaf,
};
(check == self.id).then_some(node)
}
}
const fn split_msb(integer: u32) -> (bool, u32) {
const MASK: u32 = !0 >> 1;
(integer & !MASK != 0, integer & MASK)
}
const fn base(node_id: u32) -> (bool, u32) {
const BASE: &[u32] = &include_bytes!("static/dart_base.bin" as u32le);
split_msb(BASE[node_id as usize])
}
const fn check(node_id: u32) -> (bool, u32) {
const CHECK: &[u32] = &include_bytes!("static/dart_check.bin" as u32le);
split_msb(CHECK[node_id as usize])
}