provenant/license_detection/
token_set.rs1use smallvec::SmallVec;
5use std::cmp::Ordering;
6use std::ops::Deref;
7
8use rkyv::Archive;
9
10use crate::license_detection::index::dictionary::{TokenDictionary, TokenId, TokenKind};
11
12#[derive(Clone, Debug, PartialEq, Eq, Archive, rkyv::Serialize, rkyv::Deserialize)]
18pub struct TokenSet(SmallVec<[u16; 64]>);
19
20impl TokenSet {
21 pub fn from_u16_iter<I: IntoIterator<Item = u16>>(iter: I) -> Self {
24 let mut inner: SmallVec<[u16; 64]> = iter.into_iter().collect();
25 inner.sort_unstable();
26 inner.dedup();
27 Self(inner)
28 }
29
30 pub fn from_token_ids<I: IntoIterator<Item = TokenId>>(iter: I) -> Self {
32 Self::from_u16_iter(iter.into_iter().map(|tid| tid.raw()))
33 }
34
35 pub fn new() -> Self {
37 Self(SmallVec::new())
38 }
39
40 pub fn len(&self) -> usize {
42 self.0.len()
43 }
44
45 pub fn is_empty(&self) -> bool {
47 self.0.is_empty()
48 }
49
50 pub fn contains_token_id(&self, token_id: TokenId) -> bool {
52 self.0.contains(&token_id.raw())
53 }
54
55 pub fn high_subset(&self, dictionary: &TokenDictionary) -> Self {
57 Self::from_u16_iter(
58 self.iter()
59 .filter(|&tid| dictionary.token_kind(TokenId::new(tid)) == TokenKind::Legalese),
60 )
61 }
62
63 pub fn intersection_count(&self, other: &TokenSet) -> usize {
65 let (mut i, mut j, mut count) = (0, 0, 0);
66 while i < self.0.len() && j < other.0.len() {
67 match self.0[i].cmp(&other.0[j]) {
68 Ordering::Less => i += 1,
69 Ordering::Greater => j += 1,
70 Ordering::Equal => {
71 count += 1;
72 i += 1;
73 j += 1;
74 }
75 }
76 }
77 count
78 }
79
80 pub fn intersection(&self, other: &TokenSet) -> TokenSet {
82 let mut result = SmallVec::new();
83 let (mut i, mut j) = (0, 0);
84 while i < self.0.len() && j < other.0.len() {
85 match self.0[i].cmp(&other.0[j]) {
86 Ordering::Less => i += 1,
87 Ordering::Greater => j += 1,
88 Ordering::Equal => {
89 result.push(self.0[i]);
90 i += 1;
91 j += 1;
92 }
93 }
94 }
95 Self(result)
96 }
97
98 pub fn iter(&self) -> impl Iterator<Item = u16> + '_ {
100 self.0.iter().copied()
101 }
102}
103
104impl Deref for TokenSet {
105 type Target = [u16];
106 fn deref(&self) -> &Self::Target {
107 &self.0
108 }
109}
110
111#[derive(Clone, Debug, PartialEq, Eq, Archive, rkyv::Serialize, rkyv::Deserialize)]
121pub struct HighBitset(Box<[u64]>);
122
123impl HighBitset {
124 pub fn from_token_set(set: &TokenSet, len_legalese: usize) -> Self {
130 let mut words = vec![0u64; len_legalese.div_ceil(64)].into_boxed_slice();
131 for tid in set.iter() {
132 let bit = tid as usize;
133 if let Some(word) = words.get_mut(bit / 64) {
134 *word |= 1u64 << (bit % 64);
135 }
136 }
137 Self(words)
138 }
139
140 #[inline]
144 pub fn intersection_count(&self, other: &HighBitset) -> usize {
145 debug_assert_eq!(
146 self.0.len(),
147 other.0.len(),
148 "HighBitset width mismatch: {} vs {} words",
149 self.0.len(),
150 other.0.len()
151 );
152 self.0
153 .iter()
154 .zip(other.0.iter())
155 .map(|(a, b)| (a & b).count_ones() as usize)
156 .sum()
157 }
158}
159
160impl Default for TokenSet {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166impl std::iter::FromIterator<u16> for TokenSet {
167 fn from_iter<T: IntoIterator<Item = u16>>(iter: T) -> Self {
168 Self::from_u16_iter(iter)
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use crate::license_detection::index::dictionary::tid;
176
177 #[test]
178 fn test_from_token_ids() {
179 let set = TokenSet::from_token_ids([tid(4), tid(2), tid(4), tid(1)]);
180
181 assert_eq!(set.iter().collect::<Vec<_>>(), vec![1, 2, 4]);
182 assert!(set.contains_token_id(tid(1)));
183 assert!(set.contains_token_id(tid(2)));
184 assert!(set.contains_token_id(tid(4)));
185 }
186
187 #[test]
188 fn test_high_subset() {
189 let set = TokenSet::from_u16_iter([1, 2, 5, 10]);
190 let dict = TokenDictionary::new_with_legalese_pairs(&[("one", 1), ("two", 2)]);
191
192 let high_set = set.high_subset(&dict);
193
194 assert_eq!(high_set.iter().collect::<Vec<_>>(), vec![1, 2]);
195 }
196
197 #[test]
198 fn high_bitset_intersection_count_overlap_cases() {
199 let a = TokenSet::from_u16_iter([1, 5, 9, 63, 64, 200]);
200 let b = TokenSet::from_u16_iter([5, 9, 64, 201]);
201 let width = 256;
204 let ba = HighBitset::from_token_set(&a, width);
205 let bb = HighBitset::from_token_set(&b, width);
206 assert_eq!(ba.intersection_count(&bb), 3);
208 assert_eq!(ba.intersection_count(&ba), a.len());
210 let c = HighBitset::from_token_set(&TokenSet::from_u16_iter([2, 3, 4]), width);
212 let d = HighBitset::from_token_set(&TokenSet::from_u16_iter([10, 11]), width);
213 assert_eq!(c.intersection_count(&d), 0);
214 assert_eq!(ba.intersection_count(&bb), a.intersection_count(&b));
216 }
217
218 #[test]
219 fn high_bitset_handles_non_multiple_of_64_width_and_boundary_bits() {
220 let width = 65;
222 let a = HighBitset::from_token_set(&TokenSet::from_u16_iter([0, 63, 64]), width);
223 let b = HighBitset::from_token_set(&TokenSet::from_u16_iter([63, 64]), width);
224 assert_eq!(a.intersection_count(&b), 2);
225 }
226
227 #[test]
228 fn high_bitset_skips_ids_beyond_allocated_words() {
229 let width = 8;
234 let a = HighBitset::from_token_set(&TokenSet::from_u16_iter([1, 7, 100]), width);
235 let b = HighBitset::from_token_set(&TokenSet::from_u16_iter([7, 100]), width);
236 assert_eq!(a.intersection_count(&b), 1);
238 }
239}