use std::collections::HashSet;
use tinyvec::TinyVec;
use crate::process::process_type::ProcessType;
use crate::process::step::{TransformStep, get_transform_step};
#[derive(Clone)]
pub(crate) struct ProcessTypeBitNode {
process_type_list: TinyVec<[ProcessType; 4]>,
pub(crate) process_type_bit: ProcessType,
pub(crate) children: TinyVec<[usize; 4]>,
pub(crate) step: Option<&'static TransformStep>,
pub(crate) pt_index_mask: u64,
}
impl ProcessTypeBitNode {
pub(crate) fn recompute_mask_with_index(&mut self, pt_index_table: &[u8; 64]) {
self.pt_index_mask = self.process_type_list.iter().fold(0u64, |acc, pt| {
acc | (1u64 << pt_index_table[pt.bits() as usize])
});
}
}
pub(crate) fn build_process_type_tree(
process_type_set: &HashSet<ProcessType>,
) -> Vec<ProcessTypeBitNode> {
let max_nodes: usize = 1 + process_type_set
.iter()
.map(|pt| pt.bits().count_ones() as usize)
.sum::<usize>();
let mut process_type_tree = Vec::with_capacity(max_nodes);
let mut root = ProcessTypeBitNode {
process_type_list: TinyVec::new(),
process_type_bit: ProcessType::None,
children: TinyVec::new(),
step: None,
pt_index_mask: 0,
};
if process_type_set.contains(&ProcessType::None) {
root.process_type_list.push(ProcessType::None);
root.pt_index_mask |= 1u64 << ProcessType::None.bits();
}
process_type_tree.push(root);
for &process_type in process_type_set.iter() {
let mut current_node_index = 0;
for process_type_bit in process_type.iter() {
let current_node = &process_type_tree[current_node_index];
if current_node.process_type_bit == process_type_bit {
continue;
}
let found_child = current_node
.children
.iter()
.find(|&&idx| process_type_tree[idx].process_type_bit == process_type_bit)
.copied();
if let Some(child_idx) = found_child {
current_node_index = child_idx;
process_type_tree[current_node_index]
.process_type_list
.push(process_type);
process_type_tree[current_node_index].pt_index_mask |= 1u64 << process_type.bits();
} else {
let mut child = ProcessTypeBitNode {
process_type_list: TinyVec::new(),
process_type_bit,
children: TinyVec::new(),
step: Some(get_transform_step(process_type_bit)),
pt_index_mask: 0,
};
child.process_type_list.push(process_type);
child.pt_index_mask |= 1u64 << process_type.bits();
process_type_tree.push(child);
let new_node_index = process_type_tree.len() - 1;
process_type_tree[current_node_index]
.children
.push(new_node_index);
current_node_index = new_node_index;
}
}
}
process_type_tree
}
#[cfg(test)]
mod tests {
use super::*;
use crate::process::ProcessType;
#[test]
fn test_tree_single_none() {
let set: HashSet<ProcessType> = [ProcessType::None].into_iter().collect();
let tree = build_process_type_tree(&set);
assert_eq!(tree.len(), 1); assert!(tree[0].children.is_empty());
assert_ne!(
tree[0].pt_index_mask, 0,
"root should have non-zero mask for None"
);
assert_eq!(tree[0].process_type_bit, ProcessType::None);
}
#[test]
fn test_tree_prefix_sharing() {
let set: HashSet<ProcessType> = [
ProcessType::Fanjian,
ProcessType::Fanjian | ProcessType::Delete,
]
.into_iter()
.collect();
let tree = build_process_type_tree(&set);
assert_eq!(tree.len(), 3);
assert_eq!(tree[0].children.len(), 1);
let fj_idx = tree[0].children[0];
assert_eq!(tree[fj_idx].process_type_bit, ProcessType::Fanjian);
assert_eq!(tree[fj_idx].children.len(), 1);
let del_idx = tree[fj_idx].children[0];
assert_eq!(tree[del_idx].process_type_bit, ProcessType::Delete);
assert!(tree[del_idx].children.is_empty());
}
#[test]
fn test_tree_branching() {
let set: HashSet<ProcessType> = [ProcessType::Fanjian, ProcessType::Delete]
.into_iter()
.collect();
let tree = build_process_type_tree(&set);
assert_eq!(tree.len(), 3);
assert_eq!(tree[0].children.len(), 2);
let bits: Vec<_> = tree[0]
.children
.iter()
.map(|&idx| tree[idx].process_type_bit)
.collect();
assert!(bits.contains(&ProcessType::Fanjian));
assert!(bits.contains(&ProcessType::Delete));
}
#[test]
fn test_recompute_mask_with_index() {
let set: HashSet<ProcessType> = [ProcessType::None, ProcessType::Fanjian]
.into_iter()
.collect();
let mut tree = build_process_type_tree(&set);
let mut pt_index_table = [u8::MAX; 64];
pt_index_table[ProcessType::None.bits() as usize] = 0;
pt_index_table[ProcessType::Fanjian.bits() as usize] = 1;
for node in &mut tree {
node.recompute_mask_with_index(&pt_index_table);
}
assert!(tree[0].pt_index_mask & (1u64 << 0) != 0);
let fj_idx = tree[0].children[0];
assert!(tree[fj_idx].pt_index_mask & (1u64 << 1) != 0);
}
}