Last active
March 16, 2023 07:22
-
-
Save CoffeeVampir3/362a8420f116b865b8e6f727d84b3a04 to your computer and use it in GitHub Desktop.
N Length Bit Set
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mod testing_suite; | |
use std::ops::{BitAnd, BitOr}; | |
use std::marker::PhantomData; | |
#[derive(Debug, Clone, PartialEq, Eq)] | |
struct EnumSet<E, const N: usize = 2> { | |
flags: [u64; N], | |
_phantom: PhantomData<E>, | |
} | |
impl<E: Copy + Into<u64>, const N: usize> EnumSet<E, N> { | |
pub fn new(tags: &[E]) -> Self { | |
let mut seq = Self { | |
flags: [0; N], | |
_phantom: PhantomData, | |
}; | |
seq.add_many(tags); | |
seq | |
} | |
pub fn default() -> Self { | |
Self { | |
flags: [0; N], | |
_phantom: PhantomData, | |
} | |
} | |
fn add(&mut self, item: E) { | |
let (idx, pos) = self.enum_to_pos(item); | |
self.flags[idx] |= 1 << pos; | |
} | |
pub fn add_many(&mut self, tags: &[E]) { | |
for tag in tags { | |
self.add(*tag); | |
} | |
} | |
fn remove(&mut self, item: E) { | |
let (idx, pos) = self.enum_to_pos(item); | |
self.flags[idx] &= !(1 << pos); | |
} | |
pub fn remove_many(&mut self, tags: &[E]) { | |
for tag in tags { | |
self.remove(*tag); | |
} | |
} | |
pub fn contains_many(&self, tags: &[E]) -> bool { | |
tags.iter().all(|tag| self.contains(*tag)) | |
} | |
fn contains(&self, item: E) -> bool { | |
let (idx, pos) = self.enum_to_pos(item); | |
self.flags[idx] & (1 << pos) != 0 | |
} | |
pub fn is_subset_of(&self, other: &Self) -> bool { | |
self.flags.iter().zip(other.flags.iter()).all(|(a, b)| a & b == *a) | |
} | |
pub fn is_superset_of(&self, other: &Self) -> bool { | |
other.is_subset_of(self) | |
} | |
pub fn has_any_of(&self, other: &EnumSet<E, N>) -> bool { | |
for i in 0..N { | |
if self.flags[i] & other.flags[i] != 0 { | |
return true; | |
} | |
} | |
false | |
} | |
fn enum_to_pos(&self, item: E) -> (usize, u64) { | |
let pos = item.into(); | |
(pos as usize / 64, pos % 64) | |
} | |
} | |
impl<E: Copy + Into<u64>, const N: usize> BitAnd for EnumSet<E, N> { | |
type Output = Self; | |
fn bitand(self, rhs: Self) -> Self::Output { | |
let mut flags = [0; N]; | |
for i in 0..N { | |
flags[i] = self.flags[i] & rhs.flags[i]; | |
} | |
Self { | |
flags, | |
_phantom: PhantomData, | |
} | |
} | |
} | |
impl<E: Copy + Into<u64>, const N: usize> BitOr for EnumSet<E, N> { | |
type Output = Self; | |
fn bitor(self, rhs: Self) -> Self::Output { | |
let mut flags = [0; N]; | |
for i in 0..N { | |
flags[i] = self.flags[i] | rhs.flags[i]; | |
} | |
Self { | |
flags, | |
_phantom: PhantomData, | |
} | |
} | |
} | |
impl<E: Copy + Into<u64>, const N: usize> BitAnd for &EnumSet<E, N> { | |
type Output = EnumSet<E, N>; | |
fn bitand(self, rhs: Self) -> Self::Output { | |
let mut flags = [0; N]; | |
for i in 0..N { | |
flags[i] = self.flags[i] & rhs.flags[i]; | |
} | |
Self::Output { | |
flags, | |
_phantom: PhantomData, | |
} | |
} | |
} | |
impl<E: Copy + Into<u64>, const N: usize> BitOr for &EnumSet<E, N> { | |
type Output = EnumSet<E, N>; | |
fn bitor(self, rhs: Self) -> Self::Output { | |
let mut flags = [0; N]; | |
for i in 0..N { | |
flags[i] = self.flags[i] | rhs.flags[i]; | |
} | |
Self::Output { | |
flags, | |
_phantom: PhantomData, | |
} | |
} | |
} | |
fn main() { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[cfg(test)] | |
mod tests { | |
use crate::*; | |
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | |
pub enum TestTag { | |
Placeholder, | |
Creature, | |
Villian, | |
Last = 127, | |
} | |
impl From<TestTag> for u64 { | |
fn from(value: TestTag) -> Self { | |
value as u64 | |
} | |
} | |
#[test] | |
fn test_subset_superset_operations() { | |
fn test_is_subset_of_n<const N: usize>() { | |
let seq1 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian, TestTag::Last]); | |
let seq2 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian]); | |
let seq3 = EnumSet::<TestTag, N>::new(&[TestTag::Placeholder, TestTag::Last]); | |
assert_eq!(seq2.is_subset_of(&seq1), true); | |
assert_eq!(seq3.is_subset_of(&seq1), false); | |
} | |
fn test_is_superset_of_n<const N: usize>() { | |
let seq1 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian, TestTag::Last]); | |
let seq2 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian]); | |
let seq3 = EnumSet::<TestTag, N>::new(&[TestTag::Placeholder, TestTag::Last]); | |
assert_eq!(seq1.is_superset_of(&seq2), true); | |
assert_eq!(seq1.is_superset_of(&seq3), false); | |
} | |
test_is_subset_of_n::<2>(); | |
test_is_subset_of_n::<4>(); | |
test_is_subset_of_n::<8>(); | |
test_is_superset_of_n::<2>(); | |
test_is_superset_of_n::<4>(); | |
test_is_superset_of_n::<8>(); | |
} | |
#[test] | |
fn test_add() { | |
fn test_add_n<const N: usize>() { | |
let mut seq = EnumSet::<TestTag, N>::default(); | |
seq.add(TestTag::Creature); | |
assert_eq!(seq.contains(TestTag::Creature), true); | |
} | |
test_add_n::<1>(); | |
test_add_n::<2>(); | |
test_add_n::<8>(); | |
test_add_n::<16>(); | |
} | |
fn test_remove_n<const N: usize>() { | |
let mut seq = EnumSet::<TestTag, N>::new(&[TestTag::Creature]); | |
seq.remove(TestTag::Creature); | |
assert_eq!(seq.contains(TestTag::Creature), false); | |
} | |
#[test] | |
fn test_remove() { | |
test_remove_n::<1>(); | |
test_remove_n::<2>(); | |
test_remove_n::<4>(); | |
test_remove_n::<8>(); | |
test_remove_n::<16>(); | |
} | |
#[test] | |
fn test_has_any_of() { | |
fn test_has_any_of_n<const N: usize>() { | |
let seq1 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian, TestTag::Last]); | |
let seq2 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian]); | |
let seq3 = EnumSet::<TestTag, N>::new(&[TestTag::Placeholder, TestTag::Last]); | |
let seq4 = EnumSet::<TestTag, N>::new(&[TestTag::Placeholder]); | |
assert_eq!(seq1.has_any_of(&seq2), true); | |
assert_eq!(seq1.has_any_of(&seq3), true); | |
assert_eq!(seq1.has_any_of(&seq4), false); | |
} | |
test_has_any_of_n::<2>(); | |
test_has_any_of_n::<4>(); | |
test_has_any_of_n::<8>(); | |
test_has_any_of_n::<16>(); | |
} | |
#[test] | |
fn test_contains() { | |
fn test_contains_n<const N: usize>() { | |
let seq = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian]); | |
assert_eq!(seq.contains(TestTag::Creature), true); | |
assert_eq!(seq.contains(TestTag::Villian), true); | |
assert_eq!(seq.contains(TestTag::Placeholder), false); | |
} | |
test_contains_n::<1>(); | |
test_contains_n::<2>(); | |
test_contains_n::<4>(); | |
test_contains_n::<8>(); | |
test_contains_n::<16>(); | |
} | |
#[test] | |
fn test_bitwise_operations() { | |
fn test_bitwise_operations_n<const N: usize>() { | |
let seq1 = EnumSet::<TestTag, N>::new(&[TestTag::Creature, TestTag::Villian]); | |
let mut seq2 = EnumSet::<TestTag, N>::default(); | |
seq2.add(TestTag::Creature); | |
seq2.add(TestTag::Placeholder); | |
let intersection = &seq1 & &seq2; | |
let union = &seq1 | &seq2; | |
assert_eq!(intersection.contains(TestTag::Creature), true); | |
assert_eq!(intersection.contains(TestTag::Villian), false); | |
assert_eq!(intersection.contains(TestTag::Placeholder), false); | |
assert_eq!(union.contains(TestTag::Creature), true); | |
assert_eq!(union.contains(TestTag::Villian), true); | |
assert_eq!(union.contains(TestTag::Placeholder), true); | |
} | |
test_bitwise_operations_n::<2>(); | |
test_bitwise_operations_n::<4>(); | |
test_bitwise_operations_n::<8>(); | |
test_bitwise_operations_n::<16>(); | |
} | |
#[test] | |
fn test_add_many() { | |
fn test_add_many_n<const N: usize>() { | |
let tags = [TestTag::Placeholder, TestTag::Creature, TestTag::Villian, TestTag::Last]; | |
let mut seq = EnumSet::<TestTag, N>::default(); | |
seq.add_many(&tags); | |
for tag in tags.iter() { | |
assert_eq!(seq.contains(*tag), true); | |
} | |
} | |
test_add_many_n::<2>(); | |
test_add_many_n::<4>(); | |
test_add_many_n::<8>(); | |
test_add_many_n::<16>(); | |
} | |
#[test] | |
fn test_remove_many() { | |
fn test_remove_many_n<const N: usize>() { | |
let tags = [TestTag::Placeholder, TestTag::Creature, TestTag::Villian, TestTag::Last]; | |
let mut seq = EnumSet::<TestTag, N>::default(); | |
seq.add_many(&tags); | |
seq.remove_many(&tags); | |
for tag in tags.iter() { | |
assert_eq!(seq.contains(*tag), false); | |
} | |
} | |
test_remove_many_n::<2>(); | |
test_remove_many_n::<4>(); | |
test_remove_many_n::<8>(); | |
test_remove_many_n::<16>(); | |
} | |
#[test] | |
fn test_contains_many() { | |
fn test_contains_many_n<const N: usize>() { | |
let tags = [TestTag::Placeholder, TestTag::Creature, TestTag::Villian, TestTag::Last]; | |
let seq = EnumSet::<TestTag, N>::new(&tags); | |
assert_eq!(seq.contains_many(&tags), true); | |
} | |
test_contains_many_n::<2>(); | |
test_contains_many_n::<4>(); | |
test_contains_many_n::<8>(); | |
test_contains_many_n::<16>(); | |
} | |
#[test] | |
fn test_bit_operations() { | |
fn test_bit_operations_n<const N: usize>() { | |
let tags1 = [TestTag::Placeholder, TestTag::Creature]; | |
let tags2 = [TestTag::Creature, TestTag::Villian, TestTag::Last]; | |
let seq1 = EnumSet::<TestTag, N>::new(&tags1); | |
let seq2 = EnumSet::<TestTag, N>::new(&tags2); | |
let intersection = &seq1 & &seq2; | |
let union = &seq1 | &seq2; | |
assert_eq!(intersection.contains(TestTag::Creature), true); | |
assert_eq!(intersection.contains(TestTag::Placeholder), false); | |
assert_eq!(intersection.contains(TestTag::Villian), false); | |
assert_eq!(intersection.contains(TestTag::Last), false); | |
assert_eq!(union.contains(TestTag::Creature), true); | |
assert_eq!(union.contains(TestTag::Placeholder), true); | |
assert_eq!(union.contains(TestTag::Villian), true); | |
assert_eq!(union.contains(TestTag::Last), true); | |
} | |
test_bit_operations_n::<2>(); | |
test_bit_operations_n::<4>(); | |
test_bit_operations_n::<8>(); | |
test_bit_operations_n::<16>(); | |
} | |
#[test] | |
fn test_bit_and() { | |
let seq1 = EnumSet::<TestTag, 2>::new(&[TestTag::Placeholder, TestTag::Creature]); | |
let seq2 = EnumSet::<TestTag, 2>::new(&[TestTag::Creature, TestTag::Villian]); | |
let intersection = seq1 & seq2; | |
assert_eq!(intersection.contains(TestTag::Creature), true); | |
assert_eq!(intersection.contains(TestTag::Placeholder), false); | |
assert_eq!(intersection.contains(TestTag::Villian), false); | |
} | |
#[test] | |
fn test_bit_or() { | |
let seq1 = EnumSet::<TestTag, 2>::new(&[TestTag::Placeholder, TestTag::Creature]); | |
let seq2 = EnumSet::<TestTag, 2>::new(&[TestTag::Creature, TestTag::Villian]); | |
let union = seq1 | seq2; | |
assert_eq!(union.contains(TestTag::Creature), true); | |
assert_eq!(union.contains(TestTag::Placeholder), true); | |
assert_eq!(union.contains(TestTag::Villian), true); | |
} | |
#[test] | |
fn test_empty() { | |
let seq = EnumSet::<TestTag, 2>::default(); | |
assert_eq!(seq.contains(TestTag::Creature), false); | |
assert_eq!(seq.contains(TestTag::Placeholder), false); | |
assert_eq!(seq.contains(TestTag::Villian), false); | |
} | |
#[test] | |
fn test_add_all() { | |
let mut seq = EnumSet::<TestTag, 2>::default(); | |
seq.add(TestTag::Placeholder); | |
seq.add(TestTag::Creature); | |
seq.add(TestTag::Villian); | |
assert_eq!(seq.contains(TestTag::Creature), true); | |
assert_eq!(seq.contains(TestTag::Placeholder), true); | |
assert_eq!(seq.contains(TestTag::Villian), true); | |
} | |
#[test] | |
fn test_boundary_conditions() { | |
fn test_boundary_conditions_n<const N: usize>() { | |
let seq = EnumSet::<TestTag, N>::new(&[TestTag::Placeholder, TestTag::Creature]); | |
let boundary_element = TestTag::Last; | |
let mut seq_boundary = EnumSet::<TestTag, 2>::default(); | |
seq_boundary.add(boundary_element); | |
assert_eq!(seq.contains(TestTag::Placeholder), true); | |
assert_eq!(seq.contains(TestTag::Creature), true); | |
assert_eq!(seq.contains(boundary_element), false); | |
assert_eq!(seq_boundary.contains(boundary_element), true); | |
assert_eq!(seq_boundary.contains(TestTag::Creature), false); | |
assert_eq!(seq_boundary.contains(TestTag::Placeholder), false); | |
} | |
test_boundary_conditions_n::<2>(); | |
test_boundary_conditions_n::<4>(); | |
test_boundary_conditions_n::<8>(); | |
test_boundary_conditions_n::<16>(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment