1use std::iter::Chain;
5use std::iter::Once;
6use std::iter::once;
7use std::sync::Arc;
8
9use vortex_buffer::BitBuffer;
10use vortex_buffer::BitChunkIterator;
11use vortex_buffer::BufferMut;
12use vortex_buffer::CpuKernel;
13use vortex_error::VortexExpect;
14
15use crate::Mask;
16use crate::MaskValues;
17
18trait DepositBits {
19 const PREFER_BRANCHES: bool;
24
25 fn deposit_bits(source: u64, mask: u64, mask_count: usize) -> u64;
26}
27
28trait SelectBit {
29 fn select_bit_position(word: u64, rank: usize) -> usize;
32}
33
34struct Portable;
35
36impl DepositBits for Portable {
37 const PREFER_BRANCHES: bool = true;
38
39 #[inline]
40 fn deposit_bits(source: u64, mask: u64, mask_count: usize) -> u64 {
41 if mask_count >= 16 && source.count_ones() as usize * 8 < mask_count {
42 return deposit_sparse_source(source, mask);
43 }
44
45 deposit_by_mask(source, mask)
46 }
47}
48
49impl SelectBit for Portable {
50 #[inline]
51 fn select_bit_position(word: u64, rank: usize) -> usize {
52 select_bit_position_portable(word, rank)
53 }
54}
55
56#[inline]
57fn deposit_by_mask(mut source: u64, mut mask: u64) -> u64 {
58 let mut result = 0u64;
59 while mask != 0 {
60 let bit = mask & mask.wrapping_neg();
61 if source & 1 != 0 {
62 result |= bit;
63 }
64 source >>= 1;
65 mask &= mask - 1;
66 }
67 result
68}
69
70#[inline]
71fn deposit_sparse_source(mut source: u64, mask: u64) -> u64 {
72 let mut result = 0u64;
73 while source != 0 {
74 result |= select_set_bit(mask, source.trailing_zeros() as usize);
75 source &= source - 1;
76 }
77 result
78}
79
80#[inline]
81fn select_set_bit(word: u64, rank: usize) -> u64 {
82 1u64 << select_bit_position_portable(word, rank)
83}
84
85#[inline]
86fn select_bit_position_portable(word: u64, mut rank: usize) -> usize {
87 debug_assert!(rank < word.count_ones() as usize);
88 let mut bit_offset = 0usize;
89 for byte in word.to_le_bytes() {
90 let count = byte.count_ones() as usize;
91 if rank < count {
92 let mut bits = byte;
93 for _ in 0..rank {
94 bits &= bits - 1;
95 }
96
97 return bit_offset + bits.trailing_zeros() as usize;
98 }
99
100 rank -= count;
101 bit_offset += 8;
102 }
103
104 debug_assert!(false, "rank out of bounds");
105 0
106}
107
108#[cfg(target_arch = "x86_64")]
109struct Bmi2;
110
111#[cfg(target_arch = "x86_64")]
112impl DepositBits for Bmi2 {
113 const PREFER_BRANCHES: bool = false;
114
115 #[inline]
116 fn deposit_bits(source: u64, mask: u64, _mask_count: usize) -> u64 {
117 unsafe { pdep_bmi2(source, mask) }
119 }
120}
121
122#[cfg(target_arch = "x86_64")]
123impl SelectBit for Bmi2 {
124 #[inline]
125 fn select_bit_position(word: u64, rank: usize) -> usize {
126 unsafe { select_bit_position_bmi2(word, rank) }
128 }
129}
130
131#[cfg(target_arch = "x86_64")]
132#[target_feature(enable = "bmi2")]
133unsafe fn pdep_bmi2(source: u64, mask: u64) -> u64 {
134 use std::arch::x86_64;
135 x86_64::_pdep_u64(source, mask)
136}
137
138#[cfg(target_arch = "x86_64")]
139#[target_feature(enable = "bmi2")]
140unsafe fn select_bit_position_bmi2(word: u64, rank: usize) -> usize {
141 use std::arch::x86_64;
142 debug_assert!(rank < word.count_ones() as usize);
143 let bit = x86_64::_pdep_u64(1u64 << rank, word);
146 bit.trailing_zeros() as usize
147}
148
149struct RankBitReader<'a> {
155 chunk_iter: Chain<BitChunkIterator<'a>, Once<u64>>,
156 current: u64,
157 next: u64,
158 bit_offset: usize,
159}
160
161impl<'a> RankBitReader<'a> {
162 fn new(buffer: &'a BitBuffer) -> Self {
163 let chunks = buffer.chunks();
164 let mut chunk_iter = chunks.iter().chain(once(chunks.remainder_bits()));
165
166 let current = chunk_iter.next().unwrap_or(0);
167 let next = chunk_iter.next().unwrap_or(0);
168
169 Self {
170 chunk_iter,
171 current,
172 next,
173 bit_offset: 0,
174 }
175 }
176
177 #[inline]
178 fn fetch_next(&mut self) -> u64 {
179 self.chunk_iter.next().unwrap_or(0)
180 }
181
182 #[inline]
183 fn read(&mut self, bit_count: usize) -> u64 {
184 debug_assert!(bit_count <= 64);
185
186 let combined = ((self.next as u128) << 64) | (self.current as u128);
190 #[expect(clippy::cast_possible_truncation)]
193 let bits = (combined >> self.bit_offset) as u64 & low_bits(bit_count);
194
195 let new_offset = self.bit_offset + bit_count;
196 if new_offset >= 64 {
197 self.current = self.next;
198 self.next = self.fetch_next();
199 self.bit_offset = new_offset - 64;
200 } else {
201 self.bit_offset = new_offset;
202 }
203
204 bits
205 }
206}
207
208#[inline]
209fn low_bits(bit_count: usize) -> u64 {
210 debug_assert!(bit_count <= 64);
211 if bit_count == 64 {
212 u64::MAX
213 } else {
214 (1u64 << bit_count) - 1
215 }
216}
217
218#[inline]
219fn mask_from_buffer(buffer: BitBuffer, true_count: usize) -> Mask {
220 let len = buffer.len();
221 if true_count == 0 {
222 return Mask::new_false(len);
223 }
224 if true_count == len {
225 return Mask::new_true(len);
226 }
227
228 Mask::Values(Arc::new(MaskValues {
229 buffer,
230 indices: Default::default(),
231 slices: Default::default(),
232 true_count,
233 density: true_count as f64 / len as f64,
234 }))
235}
236
237#[inline]
238fn push_result_chunk<D: DepositBits>(
239 result: &mut BufferMut<u64>,
240 self_chunk: u64,
241 self_count: usize,
242 rank_bits: u64,
243) {
244 let chunk = if D::PREFER_BRANCHES {
245 if rank_bits == 0 {
246 0
247 } else if self_chunk == u64::MAX {
248 rank_bits
249 } else {
250 D::deposit_bits(rank_bits, self_chunk, self_count)
251 }
252 } else {
253 D::deposit_bits(rank_bits, self_chunk, self_count)
254 };
255
256 unsafe { result.push_unchecked(chunk) };
258}
259
260fn intersect_bit_buffers<D: DepositBits>(
261 self_buffer: &BitBuffer,
262 mask_buffer: &BitBuffer,
263 true_count: usize,
264) -> Mask {
265 let len = self_buffer.len();
266 let mut result = BufferMut::with_capacity(len.div_ceil(64));
267 let mut reader = RankBitReader::new(mask_buffer);
268 let self_chunks = self_buffer.chunks();
269
270 for self_chunk in self_chunks.iter() {
271 let self_count = self_chunk.count_ones() as usize;
272 let rank_bits = reader.read(self_count);
273 push_result_chunk::<D>(&mut result, self_chunk, self_count, rank_bits);
274 }
275
276 if self_chunks.remainder_len() != 0 {
277 let self_chunk = self_chunks.remainder_bits();
278 let self_count = self_chunk.count_ones() as usize;
279 let rank_bits = reader.read(self_count);
280 push_result_chunk::<D>(&mut result, self_chunk, self_count, rank_bits);
281 }
282
283 mask_from_buffer(
284 BitBuffer::new(result.freeze().into_byte_buffer(), len),
285 true_count,
286 )
287}
288
289fn intersect_bit_buffer_by_rank_indices<D: DepositBits>(
290 self_buffer: &BitBuffer,
291 mask_indices: &[usize],
292) -> Mask {
293 let len = self_buffer.len();
294 let mut result = BufferMut::with_capacity(len.div_ceil(64));
295 let self_chunks = self_buffer.chunks();
296 let mut rank_base = 0usize;
297 let mut rank_idx = 0usize;
298
299 for self_chunk in self_chunks.iter() {
300 let self_count = self_chunk.count_ones() as usize;
301 let next_rank_base = rank_base + self_count;
302 let rank_bits = rank_bits_for_chunk(mask_indices, &mut rank_idx, rank_base, next_rank_base);
303 push_result_chunk::<D>(&mut result, self_chunk, self_count, rank_bits);
304 rank_base = next_rank_base;
305 }
306
307 if self_chunks.remainder_len() != 0 {
308 let self_chunk = self_chunks.remainder_bits();
309 let self_count = self_chunk.count_ones() as usize;
310 let next_rank_base = rank_base + self_count;
311 let rank_bits = rank_bits_for_chunk(mask_indices, &mut rank_idx, rank_base, next_rank_base);
312 push_result_chunk::<D>(&mut result, self_chunk, self_count, rank_bits);
313 }
314
315 debug_assert_eq!(rank_idx, mask_indices.len());
316
317 mask_from_buffer(
318 BitBuffer::new(result.freeze().into_byte_buffer(), len),
319 mask_indices.len(),
320 )
321}
322
323fn intersect_mask_driven<S, I>(self_buffer: &BitBuffer, mask_indices: I, true_count: usize) -> Mask
330where
331 S: SelectBit,
332 I: Iterator<Item = usize>,
333{
334 let len = self_buffer.len();
335 if true_count == 0 {
336 return Mask::new_false(len);
337 }
338
339 let mut chunk_iter = self_buffer.chunks().iter_padded();
340
341 let mut current_chunk = chunk_iter.next().unwrap_or(0);
342 let mut current_count = current_chunk.count_ones() as usize;
343 let mut current_chunk_idx = 0usize;
344 let mut rank_before = 0usize;
345
346 let mut output = Vec::with_capacity(true_count);
347
348 for global_rank in mask_indices {
349 while rank_before + current_count <= global_rank {
350 rank_before += current_count;
351 current_chunk_idx += 1;
352 current_chunk = chunk_iter.next().vortex_expect("mask index out of bounds");
353 current_count = current_chunk.count_ones() as usize;
354 }
355
356 let local_rank = global_rank - rank_before;
357 let bit_pos = S::select_bit_position(current_chunk, local_rank);
358 output.push(current_chunk_idx * 64 + bit_pos);
359 }
360
361 debug_assert_eq!(output.len(), true_count);
362 Mask::from_indices(len, output)
363}
364
365#[inline]
366fn rank_bits_for_chunk(
367 mask_indices: &[usize],
368 rank_idx: &mut usize,
369 rank_base: usize,
370 next_rank_base: usize,
371) -> u64 {
372 let mut rank_bits = 0u64;
373 while let Some(&rank) = mask_indices.get(*rank_idx) {
374 if rank >= next_rank_base {
375 break;
376 }
377 rank_bits |= 1u64 << (rank - rank_base);
378 *rank_idx += 1;
379 }
380 rank_bits
381}
382
383fn intersect_by_rank_indices(len: usize, self_indices: &[usize], mask_indices: &[usize]) -> Mask {
384 Mask::from_indices(
385 len,
386 mask_indices.iter().map(|idx| {
387 unsafe { *self_indices.get_unchecked(*idx) }
390 }),
391 )
392}
393
394#[inline]
395fn intersect_bit_buffers_dispatch(
396 self_buffer: &BitBuffer,
397 mask_buffer: &BitBuffer,
398 true_count: usize,
399) -> Mask {
400 type IntersectBuffers = fn(&BitBuffer, &BitBuffer, usize) -> Mask;
401 static KERNEL: CpuKernel<IntersectBuffers> = CpuKernel::new(|| {
402 #[cfg(target_arch = "x86_64")]
403 {
404 if std::arch::is_x86_feature_detected!("bmi2") {
405 return intersect_bit_buffers::<Bmi2>;
406 }
407 }
408 intersect_bit_buffers::<Portable>
409 });
410 KERNEL.get()(self_buffer, mask_buffer, true_count)
411}
412
413#[inline]
414fn intersect_rank_indices_dispatch(self_buffer: &BitBuffer, mask_indices: &[usize]) -> Mask {
415 type IntersectRankIndices = fn(&BitBuffer, &[usize]) -> Mask;
416 static KERNEL: CpuKernel<IntersectRankIndices> = CpuKernel::new(|| {
417 #[cfg(target_arch = "x86_64")]
418 {
419 if std::arch::is_x86_feature_detected!("bmi2") {
420 return intersect_bit_buffer_by_rank_indices::<Bmi2>;
421 }
422 }
423 intersect_bit_buffer_by_rank_indices::<Portable>
424 });
425 KERNEL.get()(self_buffer, mask_indices)
426}
427
428#[inline]
429fn intersect_mask_driven_dispatch<I>(
430 self_buffer: &BitBuffer,
431 mask_indices: I,
432 true_count: usize,
433) -> Mask
434where
435 I: Iterator<Item = usize>,
436{
437 #[cfg(target_arch = "x86_64")]
438 if std::arch::is_x86_feature_detected!("bmi2") {
439 return intersect_mask_driven::<Bmi2, _>(self_buffer, mask_indices, true_count);
440 }
441
442 intersect_mask_driven::<Portable, _>(self_buffer, mask_indices, true_count)
443}
444
445fn mask_is_sparse(values: &Arc<MaskValues>) -> bool {
449 values.true_count().saturating_mul(64) < values.len()
450}
451
452fn rank_mask_is_sparse(values: &Arc<MaskValues>) -> bool {
457 values.true_count().saturating_mul(32) < values.len()
458}
459
460impl Mask {
461 pub fn intersect_by_rank(&self, mask: &Mask) -> Mask {
480 assert_eq!(self.true_count(), mask.len());
481
482 match (self, mask) {
483 (Self::AllTrue(_), _) => mask.clone(),
484 (_, Self::AllTrue(_)) => self.clone(),
485 (Self::AllFalse(_), _) | (_, Self::AllFalse(_)) => Self::new_false(self.len()),
486 (Self::Values(self_values), Self::Values(mask_values)) => {
487 if let Some(mask_indices) = mask_values.indices.get() {
494 if let Some(self_indices) = self_values.indices.get()
495 && mask_indices.len() < self.len().div_ceil(64)
496 {
497 return intersect_by_rank_indices(self.len(), self_indices, mask_indices);
498 }
499
500 let self_is_very_sparse = mask_is_sparse(self_values);
501 let mask_is_very_sparse = rank_mask_is_sparse(mask_values);
502
503 if self_is_very_sparse {
504 return intersect_by_rank_indices(
505 self.len(),
506 self_values.indices(),
507 mask_indices,
508 );
509 }
510
511 if mask_is_very_sparse {
512 return intersect_mask_driven_dispatch(
513 self_values.bit_buffer(),
514 mask_indices.iter().copied(),
515 mask_values.true_count(),
516 );
517 }
518
519 if mask_indices.len().saturating_mul(4) > mask.len() {
520 return intersect_bit_buffers_dispatch(
521 self_values.bit_buffer(),
522 mask_values.bit_buffer(),
523 mask_values.true_count(),
524 );
525 }
526
527 return intersect_rank_indices_dispatch(self_values.bit_buffer(), mask_indices);
528 }
529
530 let self_is_very_sparse = mask_is_sparse(self_values);
531 let mask_is_very_sparse = rank_mask_is_sparse(mask_values);
532
533 if self_is_very_sparse {
534 return intersect_by_rank_indices(
535 self.len(),
536 self_values.indices(),
537 mask_values.indices(),
538 );
539 }
540
541 if mask_is_very_sparse {
542 return intersect_mask_driven_dispatch(
543 self_values.bit_buffer(),
544 mask_values.bit_buffer().set_indices(),
545 mask_values.true_count(),
546 );
547 }
548
549 intersect_bit_buffers_dispatch(
550 self_values.bit_buffer(),
551 mask_values.bit_buffer(),
552 mask_values.true_count(),
553 )
554 }
555 }
556 }
557}
558
559#[cfg(test)]
560mod test {
561 use rstest::rstest;
562 use vortex_buffer::BitBuffer;
563
564 use crate::Mask;
565
566 #[test]
567 fn mask_bitand_all_as_bit_and() {
568 let this = Mask::from_buffer(BitBuffer::from_iter(vec![true, true, true, true, true]));
569 let mask = Mask::from_buffer(BitBuffer::from_iter(vec![false, true, false, true, true]));
570 assert_eq!(
571 this.intersect_by_rank(&mask),
572 Mask::from_indices(5, vec![1, 3, 4])
573 );
574 }
575
576 #[test]
577 fn mask_bitand_all_true() {
578 let this = Mask::from_buffer(BitBuffer::from_iter(vec![false, false, true, true, true]));
579 let mask = Mask::from_buffer(BitBuffer::from_iter(vec![true, true, true]));
580 assert_eq!(
581 this.intersect_by_rank(&mask),
582 Mask::from_indices(5, vec![2, 3, 4])
583 );
584 }
585
586 #[test]
587 fn mask_bitand_true() {
588 let this = Mask::from_buffer(BitBuffer::from_iter(vec![true, false, false, true, true]));
589 let mask = Mask::from_buffer(BitBuffer::from_iter(vec![true, false, true]));
590 assert_eq!(
591 this.intersect_by_rank(&mask),
592 Mask::from_indices(5, vec![0, 4])
593 );
594 }
595
596 #[test]
597 fn mask_bitand_false() {
598 let this = Mask::from_buffer(BitBuffer::from_iter(vec![true, false, false, true, true]));
599 let mask = Mask::from_buffer(BitBuffer::from_iter(vec![false, false, false]));
600 assert_eq!(this.intersect_by_rank(&mask), Mask::from_indices(5, vec![]));
601 }
602
603 #[test]
604 fn mask_intersect_by_rank_all_false() {
605 let this = Mask::AllFalse(10);
606 let mask = Mask::AllFalse(0);
607 assert_eq!(this.intersect_by_rank(&mask), Mask::AllFalse(10));
608 }
609
610 #[rstest]
611 #[case::all_true_with_all_true(
612 Mask::new_true(5),
613 Mask::new_true(5),
614 vec![0, 1, 2, 3, 4]
615 )]
616 #[case::all_true_with_all_false(
617 Mask::new_true(5),
618 Mask::new_false(5),
619 vec![]
620 )]
621 #[case::all_false_with_any(
622 Mask::new_false(10),
623 Mask::new_true(0),
624 vec![]
625 )]
626 #[case::indices_with_all_true(
627 Mask::from_indices(10, vec![2, 5, 7, 9]),
628 Mask::new_true(4),
629 vec![2, 5, 7, 9]
630 )]
631 #[case::indices_with_all_false(
632 Mask::from_indices(10, vec![2, 5, 7, 9]),
633 Mask::new_false(4),
634 vec![]
635 )]
636 fn test_intersect_by_rank_special_cases(
637 #[case] base_mask: Mask,
638 #[case] rank_mask: Mask,
639 #[case] expected_indices: Vec<usize>,
640 ) {
641 let result = base_mask.intersect_by_rank(&rank_mask);
642
643 match result.indices() {
644 crate::AllOr::All => assert_eq!(expected_indices.len(), result.len()),
645 crate::AllOr::None => assert!(expected_indices.is_empty()),
646 crate::AllOr::Some(indices) => assert_eq!(indices, &expected_indices[..]),
647 }
648 }
649
650 #[test]
651 fn test_intersect_by_rank_example() {
652 let m1 = Mask::from_iter([true, false, false, true, true, true, false, true]);
654 let m2 = Mask::from_iter([false, false, true, false, true]);
655 let result = m1.intersect_by_rank(&m2);
656 let expected = Mask::from_iter([false, false, false, false, true, false, false, true]);
657 assert_eq!(result, expected);
658 }
659
660 #[test]
661 #[should_panic]
662 fn test_intersect_by_rank_wrong_length() {
663 let m1 = Mask::from_indices(10, vec![2, 5, 7]); let m2 = Mask::new_true(5); m1.intersect_by_rank(&m2);
666 }
667
668 #[rstest]
669 #[case::single_element(
670 vec![3],
671 vec![true],
672 vec![3]
673 )]
674 #[case::single_element_masked(
675 vec![3],
676 vec![false],
677 vec![]
678 )]
679 #[case::alternating(
680 vec![0, 2, 4, 6, 8],
681 vec![true, false, true, false, true],
682 vec![0, 4, 8]
683 )]
684 #[case::consecutive(
685 vec![5, 6, 7, 8, 9],
686 vec![false, true, true, true, false],
687 vec![6, 7, 8]
688 )]
689 fn test_intersect_by_rank_patterns(
690 #[case] base_indices: Vec<usize>,
691 #[case] rank_pattern: Vec<bool>,
692 #[case] expected_indices: Vec<usize>,
693 ) {
694 let base = Mask::from_indices(10, base_indices);
695 let rank = Mask::from_iter(rank_pattern);
696 let result = base.intersect_by_rank(&rank);
697
698 match result.indices() {
699 crate::AllOr::Some(indices) => assert_eq!(indices, &expected_indices[..]),
700 crate::AllOr::None => assert!(expected_indices.is_empty()),
701 _ => panic!("Unexpected result"),
702 }
703 }
704
705 #[rstest]
706 #[case::dense_len_1024(1024, 31, 0.5, 0.5)]
708 #[case::sparse_mask_1pct(1024, 17, 0.5, 0.01)]
711 #[case::sparse_mask_2pct(2048, 0, 0.5, 0.02)]
712 #[case::very_sparse_mask_with_offsets(513, 5, 0.5, 0.005)]
713 fn test_intersect_by_rank_density_matrix(
714 #[case] base_len: usize,
715 #[case] base_offset: usize,
716 #[case] base_density: f64,
717 #[case] rank_density: f64,
718 ) {
719 #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
720 let base_threshold = (base_density * 1024.0) as usize;
721 #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
722 let rank_threshold = (rank_density * 1024.0) as usize;
723
724 let base_source: Vec<bool> = (0..base_len + base_offset + 16)
725 .map(|i| (i * 7 + 13) % 1024 < base_threshold)
726 .collect();
727 let base_bits = base_source[base_offset..base_offset + base_len].to_vec();
728 let base = Mask::from_buffer(
729 BitBuffer::from(base_source).slice(base_offset..base_offset + base_len),
730 );
731
732 let rank_len = base.true_count();
733 let rank_bits: Vec<bool> = (0..rank_len)
734 .map(|i| (i * 11 + 7) % 1024 < rank_threshold)
735 .collect();
736 let rank_from_buffer = Mask::from_buffer(BitBuffer::from(rank_bits.clone()));
737 let rank_indices_vec = rank_bits
738 .iter()
739 .enumerate()
740 .filter_map(|(idx, &v)| v.then_some(idx))
741 .collect::<Vec<_>>();
742 let rank_from_indices = Mask::from_indices(rank_len, rank_indices_vec);
743
744 let expected = expected_intersect_by_rank(&base_bits, &rank_bits);
745
746 assert_eq!(
747 base.intersect_by_rank(&rank_from_buffer),
748 expected,
749 "uncached rank"
750 );
751 assert_eq!(
752 base.intersect_by_rank(&rank_from_indices),
753 expected,
754 "cached rank"
755 );
756 }
757
758 #[rstest]
759 #[case::short(37, 0, 0)]
760 #[case::base_offset(257, 5, 0)]
761 #[case::rank_offset(257, 0, 3)]
762 #[case::both_offsets(513, 6, 5)]
763 fn test_intersect_by_rank_bitbuffer_paths_with_offsets(
764 #[case] base_len: usize,
765 #[case] base_offset: usize,
766 #[case] rank_offset: usize,
767 ) {
768 let base_source: Vec<bool> = (0..base_len + base_offset + 16)
769 .map(|i| (i % 3 == 0) ^ (i % 11 == 0) ^ (i % 17 == 0))
770 .collect();
771 let base_bits = base_source[base_offset..base_offset + base_len].to_vec();
772 let base = Mask::from_buffer(
773 BitBuffer::from(base_source).slice(base_offset..base_offset + base_len),
774 );
775
776 let rank_len = base.true_count();
777 let rank_bits: Vec<bool> = (0..rank_len)
778 .map(|i| (i % 5 == 0) || (i % 13 == 3))
779 .collect();
780 let mut rank_source = vec![false; rank_offset];
781 rank_source.extend(rank_bits.iter().copied());
782 rank_source.extend([true, false, true, false, true, false, true, false]);
783
784 let rank_from_buffer = Mask::from_buffer(
785 BitBuffer::from(rank_source).slice(rank_offset..rank_offset + rank_len),
786 );
787 let rank_indices = rank_bits
788 .iter()
789 .enumerate()
790 .filter_map(|(idx, &value)| value.then_some(idx))
791 .collect::<Vec<_>>();
792 let rank_from_indices = Mask::from_indices(rank_len, rank_indices);
793
794 let expected = expected_intersect_by_rank(&base_bits, &rank_bits);
795
796 assert_eq!(base.intersect_by_rank(&rank_from_buffer), expected);
797 assert_eq!(base.intersect_by_rank(&rank_from_indices), expected);
798 }
799
800 fn expected_intersect_by_rank(base_bits: &[bool], rank_bits: &[bool]) -> Mask {
801 let mut rank = 0usize;
802 Mask::from_iter(base_bits.iter().map(|&is_set| {
803 if is_set {
804 let keep = rank_bits[rank];
805 rank += 1;
806 keep
807 } else {
808 false
809 }
810 }))
811 }
812}