1#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct BitVec2048 {
9 words: [u64; 32],
10}
11
12impl Default for BitVec2048 {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl BitVec2048 {
19 pub fn new() -> Self {
21 Self { words: [0u64; 32] }
22 }
23
24 pub fn set(&mut self, bit: usize) {
29 assert!(
30 bit < 2048,
31 "bit index {bit} out of range for BitVec2048 (max 2047)"
32 );
33 self.words[bit / 64] |= 1u64 << (bit % 64);
34 }
35
36 pub fn get(&self, bit: usize) -> bool {
41 assert!(
42 bit < 2048,
43 "bit index {bit} out of range for BitVec2048 (max 2047)"
44 );
45 (self.words[bit / 64] >> (bit % 64)) & 1 == 1
46 }
47
48 #[inline]
55 pub fn popcount(&self) -> u32 {
56 self.words.iter().map(|w| w.count_ones()).sum()
57 }
58
59 #[inline]
61 pub fn and(&self, other: &Self) -> Self {
62 let mut result = Self::new();
63 for (out, (a, b)) in result
64 .words
65 .iter_mut()
66 .zip(self.words.iter().zip(other.words.iter()))
67 {
68 *out = a & b;
69 }
70 result
71 }
72
73 #[inline]
75 pub fn or(&self, other: &Self) -> Self {
76 let mut result = Self::new();
77 for (out, (a, b)) in result
78 .words
79 .iter_mut()
80 .zip(self.words.iter().zip(other.words.iter()))
81 {
82 *out = a | b;
83 }
84 result
85 }
86
87 #[inline]
93 pub fn intersection_popcount(&self, other: &Self) -> u32 {
94 self.words
95 .iter()
96 .zip(other.words.iter())
97 .map(|(a, b)| (a & b).count_ones())
98 .sum()
99 }
100
101 #[inline]
111 pub fn tanimoto_with_counts(
112 &self,
113 other: &Self,
114 self_popcount: u32,
115 other_popcount: u32,
116 ) -> f32 {
117 let inter = self.intersection_popcount(other) as f32;
118 let union = self_popcount as f32 + other_popcount as f32 - inter;
119 if union == 0.0 { 1.0 } else { inter / union }
120 }
121
122 pub fn tanimoto(&self, other: &Self) -> f64 {
126 let intersection = self.and(other).popcount() as f64;
127 let a = self.popcount() as f64;
128 let b = other.popcount() as f64;
129 let union = a + b - intersection;
130 if union == 0.0 {
131 1.0
132 } else {
133 intersection / union
134 }
135 }
136
137 pub fn dice(&self, other: &Self) -> f64 {
141 let intersection = self.and(other).popcount() as f64;
142 let a = self.popcount() as f64;
143 let b = other.popcount() as f64;
144 let denom = a + b;
145 if denom == 0.0 {
146 1.0
147 } else {
148 2.0 * intersection / denom
149 }
150 }
151
152 pub fn fold(&self, bits: usize) -> Self {
162 assert!(
163 matches!(bits, 256 | 512 | 1024),
164 "fold target must be 1024, 512, or 256; got {bits}"
165 );
166
167 let mut current = self.clone();
168 let mut current_bits = 2048usize;
169
170 while current_bits > bits {
171 let half_words = current_bits / 64 / 2; let mut folded = Self::new();
173 for i in 0..half_words {
174 folded.words[i] = current.words[i] ^ current.words[i + half_words];
175 }
176 current = folded;
177 current_bits /= 2;
178 }
179
180 current
181 }
182
183 pub fn to_bitvecn(&self) -> BitVecN {
185 BitVecN {
186 words: self.words.to_vec(),
187 bits: 2048,
188 }
189 }
190}
191
192#[derive(Debug, Clone, PartialEq, Eq)]
197pub struct BitVecN {
198 words: Vec<u64>,
199 bits: usize,
200}
201
202impl BitVecN {
203 pub fn new(bits: usize) -> Self {
208 assert!(bits > 0, "BitVecN must have at least 1 bit");
209 let num_words = bits.div_ceil(64);
210 Self {
211 words: vec![0u64; num_words],
212 bits,
213 }
214 }
215
216 pub fn bit_width(&self) -> usize {
218 self.bits
219 }
220
221 pub fn set(&mut self, bit: usize) {
226 assert!(
227 bit < self.bits,
228 "bit index {bit} out of range for BitVecN (max {})",
229 self.bits - 1
230 );
231 self.words[bit / 64] |= 1u64 << (bit % 64);
232 }
233
234 pub fn get(&self, bit: usize) -> bool {
239 assert!(
240 bit < self.bits,
241 "bit index {bit} out of range for BitVecN (max {})",
242 self.bits - 1
243 );
244 (self.words[bit / 64] >> (bit % 64)) & 1 == 1
245 }
246
247 pub fn popcount(&self) -> u32 {
249 self.words.iter().map(|w| w.count_ones()).sum()
250 }
251
252 pub fn tanimoto(&self, other: &Self) -> f64 {
259 assert_eq!(
260 self.bits, other.bits,
261 "BitVecN tanimoto requires same bit width"
262 );
263 let mut intersection = 0u32;
264 for (a, b) in self.words.iter().zip(other.words.iter()) {
265 intersection += (a & b).count_ones();
266 }
267 let intersection = intersection as f64;
268 let a = self.popcount() as f64;
269 let b = other.popcount() as f64;
270 let union = a + b - intersection;
271 if union == 0.0 {
272 1.0
273 } else {
274 intersection / union
275 }
276 }
277
278 pub fn from_bitvec2048(bv: &BitVec2048) -> Self {
280 BitVecN {
281 words: bv.words.to_vec(),
282 bits: 2048,
283 }
284 }
285
286 pub fn to_bitvec2048(&self) -> Option<BitVec2048> {
290 if self.bits != 2048 {
291 return None;
292 }
293 let mut arr = [0u64; 32];
294 for (i, &w) in self.words.iter().enumerate() {
295 arr[i] = w;
296 }
297 Some(BitVec2048 { words: arr })
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
308 fn new_bitvec_is_all_zero() {
309 let bv = BitVec2048::new();
310 assert_eq!(bv.popcount(), 0, "new bitvec must have popcount 0");
311 }
312
313 #[test]
314 fn set_and_get_boundary_bits() {
315 let mut bv = BitVec2048::new();
316 bv.set(0);
317 bv.set(2047);
318 assert_eq!(bv.popcount(), 2);
319 assert!(bv.get(0));
320 assert!(!bv.get(1));
321 assert!(bv.get(2047));
322 }
323
324 #[test]
325 fn tanimoto_identical_nonzero() {
326 let mut bv = BitVec2048::new();
327 bv.set(42);
328 bv.set(100);
329 assert_eq!(bv.tanimoto(&bv.clone()), 1.0);
330 }
331
332 #[test]
333 fn tanimoto_disjoint_is_zero() {
334 let mut a = BitVec2048::new();
335 a.set(10);
336 let mut b = BitVec2048::new();
337 b.set(20);
338 assert_eq!(a.tanimoto(&b), 0.0);
339 }
340
341 #[test]
342 fn dice_identical_nonzero() {
343 let mut bv = BitVec2048::new();
344 bv.set(7);
345 assert_eq!(bv.dice(&bv.clone()), 1.0);
346 }
347
348 #[test]
349 fn fold_1024_popcount_leq_original() {
350 let mut bv = BitVec2048::new();
352 for i in (0..2048).step_by(3) {
353 bv.set(i);
354 }
355 let folded = bv.fold(1024);
356 assert!(
357 folded.popcount() <= bv.popcount(),
358 "folded popcount ({}) must be <= original ({})",
359 folded.popcount(),
360 bv.popcount()
361 );
362 }
363
364 #[test]
365 fn and_or_basic_correctness() {
366 let mut a = BitVec2048::new();
367 a.set(5);
368 a.set(10);
369
370 let mut b = BitVec2048::new();
371 b.set(10);
372 b.set(15);
373
374 let and = a.and(&b);
375 assert!(and.get(10), "AND: shared bit 10 should be set");
376 assert!(!and.get(5), "AND: bit 5 only in A should be clear");
377 assert!(!and.get(15), "AND: bit 15 only in B should be clear");
378
379 let or = a.or(&b);
380 assert!(or.get(5), "OR: bit 5 should be set");
381 assert!(or.get(10), "OR: bit 10 should be set");
382 assert!(or.get(15), "OR: bit 15 should be set");
383 assert!(!or.get(0), "OR: bit 0 should be clear");
384 }
385
386 #[test]
389 fn bitvecn_new_creates_zero_vector() {
390 let bv = BitVecN::new(512);
391 assert_eq!(bv.bit_width(), 512);
392 assert_eq!(bv.popcount(), 0);
393 }
394
395 #[test]
396 fn bitvecn_set_get_basic() {
397 let mut bv = BitVecN::new(1024);
398 bv.set(0);
399 bv.set(512);
400 bv.set(1023);
401 assert!(bv.get(0));
402 assert!(bv.get(512));
403 assert!(bv.get(1023));
404 assert!(!bv.get(1));
405 assert_eq!(bv.popcount(), 3);
406 }
407
408 #[test]
409 fn bitvecn_tanimoto_identical() {
410 let mut bv = BitVecN::new(256);
411 bv.set(10);
412 bv.set(50);
413 assert_eq!(bv.tanimoto(&bv.clone()), 1.0);
414 }
415
416 #[test]
417 fn bitvecn_tanimoto_disjoint() {
418 let mut a = BitVecN::new(256);
419 a.set(5);
420 let mut b = BitVecN::new(256);
421 b.set(10);
422 assert_eq!(a.tanimoto(&b), 0.0);
423 }
424
425 #[test]
426 fn bitvecn_conversion_to_from_2048() {
427 let mut bv2048 = BitVec2048::new();
428 bv2048.set(42);
429 bv2048.set(100);
430
431 let bvn = BitVecN::from_bitvec2048(&bv2048);
432 assert_eq!(bvn.bit_width(), 2048);
433 assert!(bvn.get(42));
434 assert!(bvn.get(100));
435 assert_eq!(bvn.popcount(), 2);
436
437 let bv2048_back = bvn.to_bitvec2048();
438 assert_eq!(bv2048_back, Some(bv2048));
439 }
440
441 #[test]
442 fn bitvecn_arbitrary_sizes() {
443 for size in [128, 256, 512, 1024, 2048, 4096] {
444 let mut bv = BitVecN::new(size);
445 bv.set(0);
446 bv.set(size - 1);
447 assert_eq!(bv.popcount(), 2);
448 assert_eq!(bv.bit_width(), size);
449 }
450 }
451}