use std::marker::PhantomData; use super::interner::{FixedSizeInterner, Interned}; /// A compact set of [`Interned`] /// /// This set optimizes storage by storing the set of values in a bitmap, and further optimizes /// for bitmaps where the highest possible index (describing the limits of the "universe") /// is smaller than 64 by storing them as a `u64`. pub struct SmallBitmap { // internals are not typed as they only represent the indexes that are set internal: SmallBitmapInternal, // restores typing with a tag _phantom: PhantomData, } // manual implementation for when `T` is not Clone. impl Clone for SmallBitmap { fn clone(&self) -> Self { Self { internal: self.internal.clone(), _phantom: PhantomData } } } impl SmallBitmap { /// Constructs a new, **empty**, `SmallBitmap` with an universe large enough to hold all elements /// from `interner`. /// /// The constructed bitmap does not refer to any element in the interner, use [`from_iter`] if there should be /// some interned values in the bitmap after construction. pub fn for_interned_values_in(interner: &FixedSizeInterner) -> Self { Self::new(interner.len()) } /// Constructs a new, **empty**, `SmallBitmap` with an universe at least as large as specified. /// /// If the passed universe length is not a multiple of 64, it will be rounded up to the next multiple of 64. pub fn new(universe_length: u16) -> Self { if universe_length <= 64 { Self { internal: SmallBitmapInternal::Tiny(0), _phantom: PhantomData } } else { Self { internal: SmallBitmapInternal::Small( vec![0; 1 + (universe_length - 1) as usize / 64].into_boxed_slice(), ), _phantom: PhantomData, } } } /// The highest index that can be set in this bitmap. /// /// The universe length is always a multiple of 64, and may be higher than the value passed to [`Self::new`]. pub fn universe_length(&self) -> u16 { self.internal.universe_length() } /// Constructs a new `SmallBitmap` with an universe large enough to hold all elements /// from `from_interner`, and containing all the `Interned` produced by `xs`. /// /// It is a logic error to pass an iterator producing `Interned`s that don't belong to the passed interner. /// /// # Panics /// /// - If `xs` produces an element that doesn't fit the universe length obtained from `for_interner`. pub fn from_iter( xs: impl Iterator>, for_interner: &FixedSizeInterner, ) -> Self { Self { internal: SmallBitmapInternal::from_iter(xs.map(|x| x.into_raw()), for_interner.len()), _phantom: PhantomData, } } /// Returns `true` if this bitmap does not contain any `Interned`. pub fn is_empty(&self) -> bool { self.internal.is_empty() } /// Removes all `Interned` from this bitmap, such that it [`is_empty`] returns `true` after this call. pub fn clear(&mut self) { self.internal.clear() } /// Whether `x` is part of the bitmap. /// /// It is a logic error to pass an `Interned` from a different interner that the one this bitmap references. /// /// # Panics /// /// - if `x` does not fit in [`universe_length`] pub fn contains(&self, x: Interned) -> bool { self.internal.contains(x.into_raw()) } /// Adds `x` to the bitmap, such that [`contains(x)`] returns `true` after this call. /// /// It is a logic error to pass an `Interned` from a different interner that the one this bitmap references. /// /// # Panics /// /// - if `x` does not fit in [`universe_length`] pub fn insert(&mut self, x: Interned) { self.internal.insert(x.into_raw()) } /// Removes `x` from the bitmap, such that [`contains(x)`] returns `false` after this call. /// /// It is a logic error to pass an `Interned` from a different interner that the one this bitmap references. /// /// # Panics /// /// - if `x` does not fit in [`universe_length`] pub fn remove(&mut self, x: Interned) { self.internal.remove(x.into_raw()) } /// Modifies in place this bitmap to retain only the elements that are also present in `other`. /// /// # Panics /// /// - if the universe lengths of `self` and `other` differ pub fn intersection(&mut self, other: &Self) { self.internal.intersection(&other.internal) } /// Modifies in place this bitmap to add the elements that are present in `other`. /// /// # Panics /// /// - if the universe lengths of `self` and `other` differ pub fn union(&mut self, other: &Self) { self.internal.union(&other.internal) } /// Modifies in place this bitmap to remove the elements that are also present in `other`. /// /// # Panics /// /// - if the universe lengths of `self` and `other` differ pub fn subtract(&mut self, other: &Self) { self.internal.subtract(&other.internal) } /// Whether all the elements of `self` are contained in `other`. /// /// # Panics /// /// - if the universe lengths of `self` and `other` differ pub fn is_subset(&self, other: &Self) -> bool { self.internal.is_subset(&other.internal) } /// Whether any element of `self` is contained in `other`. /// /// # Panics /// /// - if the universe lengths of `self` and `other` differ pub fn intersects(&self, other: &Self) -> bool { self.internal.intersects(&other.internal) } /// Returns an iterator of the `Interned` that are present in this bitmap. pub fn iter(&self) -> impl Iterator> + '_ { self.internal.iter().map(|x| Interned::from_raw(x)) } } #[derive(Clone)] enum SmallBitmapInternal { Tiny(u64), Small(Box<[u64]>), } impl SmallBitmapInternal { fn new(universe_length: u16) -> Self { if universe_length <= 64 { Self::Tiny(0) } else { Self::Small(vec![0; 1 + universe_length as usize / 64].into_boxed_slice()) } } fn from_iter(xs: impl Iterator, universe_length: u16) -> Self { let mut s = Self::new(universe_length); for x in xs { s.insert(x); } s } pub fn is_empty(&self) -> bool { match self { SmallBitmapInternal::Tiny(set) => *set == 0, SmallBitmapInternal::Small(sets) => { for set in sets.iter() { if *set != 0 { return false; } } true } } } pub fn clear(&mut self) { match self { SmallBitmapInternal::Tiny(set) => *set = 0, SmallBitmapInternal::Small(sets) => { for set in sets.iter_mut() { *set = 0; } } } } pub fn universe_length(&self) -> u16 { match &self { SmallBitmapInternal::Tiny(_) => 64, SmallBitmapInternal::Small(xs) => 64 * xs.len() as u16, } } fn get_set_index(&self, x: u16) -> (u64, u16) { match self { SmallBitmapInternal::Tiny(set) => { assert!( x < 64, "index out of bounds: the universe length is 64 but the index is {}", x ); (*set, x) } SmallBitmapInternal::Small(set) => { let idx = (x as usize) / 64; assert!( idx < set.len(), "index out of bounds: the universe length is {} but the index is {}", self.universe_length(), x ); (set[idx], x % 64) } } } fn get_set_index_mut(&mut self, x: u16) -> (&mut u64, u16) { match self { SmallBitmapInternal::Tiny(set) => { assert!( x < 64, "index out of bounds: the universe length is 64 but the index is {}", x ); (set, x) } SmallBitmapInternal::Small(set) => { let idx = (x as usize) / 64; assert!( idx < set.len(), "index out of bounds: the universe length is {} but the index is {}", 64 * set.len() as u16, x ); (&mut set[idx], x % 64) } } } pub fn contains(&self, x: u16) -> bool { let (set, x) = self.get_set_index(x); set & 0b1 << x != 0 } pub fn insert(&mut self, x: u16) { let (set, x) = self.get_set_index_mut(x); *set |= 0b1 << x; } pub fn remove(&mut self, x: u16) { let (set, x) = self.get_set_index_mut(x); *set &= !(0b1 << x); } pub fn intersection(&mut self, other: &SmallBitmapInternal) { self.apply_op(other, |a, b| *a &= b); } pub fn union(&mut self, other: &SmallBitmapInternal) { self.apply_op(other, |a, b| *a |= b); } pub fn subtract(&mut self, other: &SmallBitmapInternal) { self.apply_op(other, |a, b| *a &= !b); } pub fn apply_op(&mut self, other: &SmallBitmapInternal, op: impl Fn(&mut u64, u64)) { match (self, other) { (SmallBitmapInternal::Tiny(a), SmallBitmapInternal::Tiny(b)) => op(a, *b), (SmallBitmapInternal::Small(a), SmallBitmapInternal::Small(b)) => { assert!( a.len() == b.len(), "universe length mismatch: left is {}, but right is {}", a.len() * 64, other.universe_length() ); for (a, b) in a.iter_mut().zip(b.iter()) { op(a, *b); } } (this, other) => { panic!( "universe length mismatch: left is {}, but right is {}", this.universe_length(), other.universe_length() ); } } } fn all_satisfy_op(&self, other: &SmallBitmapInternal, op: impl Fn(u64, u64) -> bool) -> bool { match (self, other) { (SmallBitmapInternal::Tiny(a), SmallBitmapInternal::Tiny(b)) => op(*a, *b), (SmallBitmapInternal::Small(a), SmallBitmapInternal::Small(b)) => { assert!( a.len() == b.len(), "universe length mismatch: left is {}, but right is {}", a.len() * 64, other.universe_length() ); for (a, b) in a.iter().zip(b.iter()) { if !op(*a, *b) { return false; } } true } _ => { panic!( "universe length mismatch: left is {}, but right is {}", self.universe_length(), other.universe_length() ); } } } fn any_satisfy_op(&self, other: &SmallBitmapInternal, op: impl Fn(u64, u64) -> bool) -> bool { match (self, other) { (SmallBitmapInternal::Tiny(a), SmallBitmapInternal::Tiny(b)) => op(*a, *b), (SmallBitmapInternal::Small(a), SmallBitmapInternal::Small(b)) => { assert!( a.len() == b.len(), "universe length mismatch: left is {}, but right is {}", a.len() * 64, other.universe_length() ); for (a, b) in a.iter().zip(b.iter()) { if op(*a, *b) { return true; } } false } _ => { panic!( "universe length mismatch: left is {}, but right is {}", self.universe_length(), other.universe_length() ); } } } pub fn is_subset(&self, other: &SmallBitmapInternal) -> bool { self.all_satisfy_op(other, |a, b| a & !b == 0) } pub fn intersects(&self, other: &SmallBitmapInternal) -> bool { self.any_satisfy_op(other, |a, b| a & b != 0) } pub fn iter(&self) -> SmallBitmapInternalIter<'_> { match self { SmallBitmapInternal::Tiny(x) => SmallBitmapInternalIter::Tiny(*x), SmallBitmapInternal::Small(xs) => { SmallBitmapInternalIter::Small { cur: xs[0], next: &xs[1..], base: 0 } } } } } pub enum SmallBitmapInternalIter<'b> { Tiny(u64), Small { cur: u64, next: &'b [u64], base: u16 }, } impl<'b> Iterator for SmallBitmapInternalIter<'b> { type Item = u16; fn next(&mut self) -> Option { match self { SmallBitmapInternalIter::Tiny(set) => { if *set > 0 { let idx = set.trailing_zeros() as u16; *set &= *set - 1; Some(idx) } else { None } } SmallBitmapInternalIter::Small { cur, next, base } => { if *cur > 0 { let idx = cur.trailing_zeros() as u16; *cur &= *cur - 1; Some(idx + *base) } else if next.is_empty() { return None; } else { *base += 64; *cur = next[0]; *next = &next[1..]; self.next() } } } } }