use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Clone, Debug)]
pub struct SequenceLexicon<T: Eq + Hash + Clone> {
values: Vec<T>,
begins: Vec<usize>,
index: HashMap<SequenceKey, u32>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct SequenceKey(Vec<u8>);
impl Hash for SequenceKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T: Eq + Hash + Clone> SequenceLexicon<T> {
pub fn new() -> Self {
SequenceLexicon {
values: Vec::new(),
begins: vec![0],
index: HashMap::new(),
}
}
fn make_key(values: &[T]) -> SequenceKey {
use std::collections::hash_map::DefaultHasher;
let mut bytes = Vec::with_capacity(values.len() * 8 + 4);
bytes.extend_from_slice(&(values.len() as u64).to_le_bytes());
for v in values {
let mut hasher = DefaultHasher::new();
v.hash(&mut hasher);
bytes.extend_from_slice(&hasher.finish().to_le_bytes());
}
SequenceKey(bytes)
}
#[expect(clippy::cast_possible_truncation, reason = "IDs always fit in u32")]
pub fn add(&mut self, sequence: &[T]) -> u32 {
let key = Self::make_key(sequence);
if let Some(&existing_id) = self.index.get(&key) {
let existing = self.sequence_slice(existing_id);
if existing.len() == sequence.len()
&& existing.iter().zip(sequence.iter()).all(|(a, b)| a == b)
{
return existing_id;
}
}
self.values.extend_from_slice(sequence);
self.begins.push(self.values.len());
let id = (self.begins.len() - 2) as u32;
self.index.insert(key, id);
id
}
#[expect(clippy::cast_possible_truncation, reason = "IDs always fit in u32")]
pub fn size(&self) -> u32 {
(self.begins.len() - 1) as u32
}
pub fn sequence(&self, id: u32) -> impl Iterator<Item = &T> {
let slice = self.sequence_slice(id);
slice.iter()
}
fn sequence_slice(&self, id: u32) -> &[T] {
let begin = self.begins[id as usize];
let end = self.begins[id as usize + 1];
&self.values[begin..end]
}
pub fn clear(&mut self) {
self.values.clear();
self.begins.clear();
self.begins.push(0);
self.index.clear();
}
}
impl<T: Eq + Hash + Clone> Default for SequenceLexicon<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn expect_sequence<T: Eq + Hash + Clone + std::fmt::Debug>(
expected: &[T],
lex: &SequenceLexicon<T>,
id: u32,
) {
let actual: Vec<&T> = lex.sequence(id).collect();
assert_eq!(
expected.len(),
actual.len(),
"sequence {id} length mismatch"
);
for (i, (e, a)) in expected.iter().zip(actual.iter()).enumerate() {
assert_eq!(e, *a, "sequence {id} element {i} mismatch");
}
}
#[test]
fn test_int64() {
let mut lex = SequenceLexicon::new();
assert_eq!(0, lex.add(&[]));
assert_eq!(1, lex.add(&[5_i64]));
assert_eq!(0, lex.add(&[]));
assert_eq!(2, lex.add(&[5_i64, 5]));
assert_eq!(3, lex.add(&[5_i64, 0, -3]));
assert_eq!(1, lex.add(&[5_i64]));
assert_eq!(4, lex.add(&[0x7fffffffffffffff_i64]));
assert_eq!(3, lex.add(&[5_i64, 0, -3]));
assert_eq!(0, lex.add(&[]));
assert_eq!(5, lex.size());
expect_sequence::<i64>(&[], &lex, 0);
expect_sequence(&[5_i64], &lex, 1);
expect_sequence(&[5_i64, 5], &lex, 2);
expect_sequence(&[5_i64, 0, -3], &lex, 3);
expect_sequence(&[0x7fffffffffffffff_i64], &lex, 4);
}
#[test]
fn test_clear() {
let mut lex = SequenceLexicon::new();
assert_eq!(0, lex.add(&[1_i64]));
assert_eq!(1, lex.add(&[2_i64]));
lex.clear();
assert_eq!(0, lex.add(&[2_i64]));
assert_eq!(1, lex.add(&[1_i64]));
}
#[test]
fn test_copy_constructor() {
let mut original = SequenceLexicon::new();
assert_eq!(0, original.add(&[1_i64, 2]));
let mut lex = original.clone();
drop(original);
assert_eq!(1, lex.add(&[3_i64, 4]));
expect_sequence(&[1_i64, 2], &lex, 0);
expect_sequence(&[3_i64, 4], &lex, 1);
}
#[test]
fn test_move_constructor() {
let mut original = SequenceLexicon::new();
assert_eq!(0, original.add(&[1_i64, 2]));
let mut lex = original; assert_eq!(1, lex.add(&[3_i64, 4]));
expect_sequence(&[1_i64, 2], &lex, 0);
expect_sequence(&[3_i64, 4], &lex, 1);
}
#[test]
fn test_copy_assignment_operator() {
let mut original = SequenceLexicon::new();
assert_eq!(0, original.add(&[1_i64, 2]));
let mut lex = SequenceLexicon::new();
assert_eq!(0, lex.add(&[3_i64, 4]));
assert_eq!(1, lex.add(&[5_i64, 6]));
lex = original.clone();
drop(original);
assert_eq!(1, lex.add(&[7_i64, 8]));
expect_sequence(&[1_i64, 2], &lex, 0);
expect_sequence(&[7_i64, 8], &lex, 1);
}
#[test]
fn test_move_assignment_operator() {
let mut original = SequenceLexicon::new();
assert_eq!(0, original.add(&[1_i64, 2]));
let mut lex = SequenceLexicon::new();
assert_eq!(0, lex.add(&[3_i64, 4]));
assert_eq!(1, lex.add(&[5_i64, 6]));
lex = original; assert_eq!(1, lex.add(&[7_i64, 8]));
expect_sequence(&[1_i64, 2], &lex, 0);
expect_sequence(&[7_i64, 8], &lex, 1);
}
#[test]
fn test_string_sequences() {
let mut lex = SequenceLexicon::new();
let id0 = lex.add(&["cat".to_string(), "dog".to_string()]);
let id1 = lex.add(&["parrot".to_string()]);
assert_eq!(id0, lex.add(&["cat".to_string(), "dog".to_string()]));
assert_ne!(id0, id1);
assert_eq!(2, lex.size());
let seq0: Vec<_> = lex.sequence(id0).collect();
assert_eq!(seq0, vec!["cat", "dog"]);
let seq1: Vec<_> = lex.sequence(id1).collect();
assert_eq!(seq1, vec!["parrot"]);
}
#[test]
fn test_empty_sequence() {
let mut lex = SequenceLexicon::<i32>::new();
let id = lex.add(&[]);
assert_eq!(0, id);
assert_eq!(0, lex.sequence(id).count());
assert_eq!(1, lex.size());
}
#[test]
fn test_order_matters() {
let mut lex = SequenceLexicon::new();
let id_ab = lex.add(&[1_i32, 2]);
let id_ba = lex.add(&[2_i32, 1]);
assert_ne!(id_ab, id_ba);
}
#[test]
fn test_single_element_sequences() {
let mut lex = SequenceLexicon::new();
let id0 = lex.add(&[42_i32]);
let id1 = lex.add(&[43_i32]);
assert_eq!(id0, lex.add(&[42_i32]));
assert_ne!(id0, id1);
}
#[test]
#[expect(clippy::cast_sign_loss, reason = "test values are non-negative")]
fn test_many_sequences() {
let mut lex = SequenceLexicon::new();
for i in 0..1000_i32 {
let id = lex.add(&[i, i + 1]);
assert_eq!(i as u32, id);
}
assert_eq!(1000, lex.size());
for i in 0..1000_i32 {
assert_eq!(i as u32, lex.add(&[i, i + 1]));
}
assert_eq!(1000, lex.size());
}
#[test]
fn test_default() {
let lex = SequenceLexicon::<i32>::default();
assert_eq!(0, lex.size());
}
#[test]
fn test_prefix_not_equal() {
let mut lex = SequenceLexicon::new();
let id_short = lex.add(&[1_i32, 2]);
let id_long = lex.add(&[1_i32, 2, 3]);
assert_ne!(id_short, id_long);
}
}