const BIGRAM_ALPHABET: usize = 128;
const BIGRAM_CODE_SHIFT: u32 = 10;
static BIGRAM_CODE: &[u8; BIGRAM_ALPHABET * BIGRAM_ALPHABET / 2] =
include_bytes!("bigram_code.bin");
static BIGRAM_H: [u16; BIGRAM_ALPHABET] = [
1712, 811, 1084, 886, 596, 91, 132, 450, 724, 8461, 16403, 286, 1348, 6151, 140, 343, 333, 64,
162, 45, 103, 178, 27, 5, 35, 0, 161, 2323, 70, 125, 44, 101, 13403, 8259, 11824, 8316, 8468,
8305, 8173, 10620, 10671, 9834, 9082, 8674, 9982, 10753, 11148, 10895, 10788, 10958, 10844,
10474, 10331, 10220, 10066, 9935, 10004, 9859, 10243, 9293, 9469, 9631, 9908, 8272, 8073, 7272,
7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272,
7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 7272, 9286, 8956, 8593, 6514, 9968, 8679,
11923, 11057, 11570, 11787, 12294, 11116, 10949, 10849, 11401, 9455, 10261, 11463, 11238,
11595, 11281, 11422, 9364, 11668, 12007, 11945, 10678, 10380, 10412, 10197, 10534, 9280, 8890,
7898, 8767, 6372, 1475,
];
#[inline]
pub(crate) fn bigram_priority_rolling(a: u8, b: u8, h_a: u32) -> (u32, u32) {
let h_b = BIGRAM_H[(b & (BIGRAM_ALPHABET as u8 - 1)) as usize] as u32;
if (a | b) >= BIGRAM_ALPHABET as u8 {
return (0, h_b);
}
let idx = a as usize * BIGRAM_ALPHABET + b as usize;
let code = (BIGRAM_CODE[idx >> 1] >> ((idx & 1) * 4)) & 0xF;
let base = h_a + h_b + ((code as u32) << BIGRAM_CODE_SHIFT) + 1;
((base << 16) | idx as u32, h_b)
}
#[inline]
pub(crate) fn bigram_h(byte: u8) -> u32 {
BIGRAM_H[(byte & (BIGRAM_ALPHABET as u8 - 1)) as usize] as u32
}
pub fn bigram_priority(a: u8, b: u8) -> u32 {
bigram_priority_rolling(a, b, bigram_h(a)).0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_ascii_is_zero() {
assert_eq!(bigram_priority(0x80, b'a'), 0);
assert_eq!(bigram_priority(b'a', 0x80), 0);
assert_eq!(bigram_priority(0xff, 0xff), 0);
}
#[test]
fn ascii_bigrams_are_positive_and_unique() {
assert!(bigram_priority(b'a', b'b') > 0);
assert_ne!(bigram_priority(b'a', b'b'), bigram_priority(b'b', b'a'));
assert_ne!(bigram_priority(b'a', b'b'), bigram_priority(b'a', b'c'));
}
#[test]
fn rolling_matches_direct() {
for a in 0u8..128 {
for b in 0u8..128 {
let (p, h_b) = bigram_priority_rolling(a, b, bigram_h(a));
assert_eq!(p, bigram_priority(a, b), "mismatch at ({a}, {b})");
assert_eq!(h_b, bigram_h(b), "h_b mismatch at ({a}, {b})");
}
}
}
}