use smallvec::SmallVec;
use std::cmp::Ordering;
use std::ops::Deref;
use rkyv::Archive;
use crate::license_detection::index::dictionary::{TokenDictionary, TokenId, TokenKind};
#[derive(Clone, Debug, PartialEq, Eq, Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct TokenSet(SmallVec<[u16; 64]>);
impl TokenSet {
pub fn from_u16_iter<I: IntoIterator<Item = u16>>(iter: I) -> Self {
let mut inner: SmallVec<[u16; 64]> = iter.into_iter().collect();
inner.sort_unstable();
inner.dedup();
Self(inner)
}
pub fn from_token_ids<I: IntoIterator<Item = TokenId>>(iter: I) -> Self {
Self::from_u16_iter(iter.into_iter().map(|tid| tid.raw()))
}
pub fn new() -> Self {
Self(SmallVec::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn contains_token_id(&self, token_id: TokenId) -> bool {
self.0.contains(&token_id.raw())
}
pub fn high_subset(&self, dictionary: &TokenDictionary) -> Self {
Self::from_u16_iter(
self.iter()
.filter(|&tid| dictionary.token_kind(TokenId::new(tid)) == TokenKind::Legalese),
)
}
pub fn intersection_count(&self, other: &TokenSet) -> usize {
let (mut i, mut j, mut count) = (0, 0, 0);
while i < self.0.len() && j < other.0.len() {
match self.0[i].cmp(&other.0[j]) {
Ordering::Less => i += 1,
Ordering::Greater => j += 1,
Ordering::Equal => {
count += 1;
i += 1;
j += 1;
}
}
}
count
}
pub fn intersection(&self, other: &TokenSet) -> TokenSet {
let mut result = SmallVec::new();
let (mut i, mut j) = (0, 0);
while i < self.0.len() && j < other.0.len() {
match self.0[i].cmp(&other.0[j]) {
Ordering::Less => i += 1,
Ordering::Greater => j += 1,
Ordering::Equal => {
result.push(self.0[i]);
i += 1;
j += 1;
}
}
}
Self(result)
}
pub fn iter(&self) -> impl Iterator<Item = u16> + '_ {
self.0.iter().copied()
}
}
impl Deref for TokenSet {
type Target = [u16];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, Archive, rkyv::Serialize, rkyv::Deserialize)]
pub struct HighBitset(Box<[u64]>);
impl HighBitset {
pub fn from_token_set(set: &TokenSet, len_legalese: usize) -> Self {
let mut words = vec![0u64; len_legalese.div_ceil(64)].into_boxed_slice();
for tid in set.iter() {
let bit = tid as usize;
if let Some(word) = words.get_mut(bit / 64) {
*word |= 1u64 << (bit % 64);
}
}
Self(words)
}
#[inline]
pub fn intersection_count(&self, other: &HighBitset) -> usize {
debug_assert_eq!(
self.0.len(),
other.0.len(),
"HighBitset width mismatch: {} vs {} words",
self.0.len(),
other.0.len()
);
self.0
.iter()
.zip(other.0.iter())
.map(|(a, b)| (a & b).count_ones() as usize)
.sum()
}
}
impl Default for TokenSet {
fn default() -> Self {
Self::new()
}
}
impl std::iter::FromIterator<u16> for TokenSet {
fn from_iter<T: IntoIterator<Item = u16>>(iter: T) -> Self {
Self::from_u16_iter(iter)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::license_detection::index::dictionary::tid;
#[test]
fn test_from_token_ids() {
let set = TokenSet::from_token_ids([tid(4), tid(2), tid(4), tid(1)]);
assert_eq!(set.iter().collect::<Vec<_>>(), vec![1, 2, 4]);
assert!(set.contains_token_id(tid(1)));
assert!(set.contains_token_id(tid(2)));
assert!(set.contains_token_id(tid(4)));
}
#[test]
fn test_high_subset() {
let set = TokenSet::from_u16_iter([1, 2, 5, 10]);
let dict = TokenDictionary::new_with_legalese_pairs(&[("one", 1), ("two", 2)]);
let high_set = set.high_subset(&dict);
assert_eq!(high_set.iter().collect::<Vec<_>>(), vec![1, 2]);
}
#[test]
fn high_bitset_intersection_count_overlap_cases() {
let a = TokenSet::from_u16_iter([1, 5, 9, 63, 64, 200]);
let b = TokenSet::from_u16_iter([5, 9, 64, 201]);
let width = 256;
let ba = HighBitset::from_token_set(&a, width);
let bb = HighBitset::from_token_set(&b, width);
assert_eq!(ba.intersection_count(&bb), 3);
assert_eq!(ba.intersection_count(&ba), a.len());
let c = HighBitset::from_token_set(&TokenSet::from_u16_iter([2, 3, 4]), width);
let d = HighBitset::from_token_set(&TokenSet::from_u16_iter([10, 11]), width);
assert_eq!(c.intersection_count(&d), 0);
assert_eq!(ba.intersection_count(&bb), a.intersection_count(&b));
}
#[test]
fn high_bitset_handles_non_multiple_of_64_width_and_boundary_bits() {
let width = 65;
let a = HighBitset::from_token_set(&TokenSet::from_u16_iter([0, 63, 64]), width);
let b = HighBitset::from_token_set(&TokenSet::from_u16_iter([63, 64]), width);
assert_eq!(a.intersection_count(&b), 2);
}
#[test]
fn high_bitset_skips_ids_beyond_allocated_words() {
let width = 8;
let a = HighBitset::from_token_set(&TokenSet::from_u16_iter([1, 7, 100]), width);
let b = HighBitset::from_token_set(&TokenSet::from_u16_iter([7, 100]), width);
assert_eq!(a.intersection_count(&b), 1);
}
}