use core::cmp::PartialEq;
use core::ops::{BitAnd, BitOr, Shl, Shr};
pub trait BitBucket: Default {
fn push(&mut self, bit: bool);
fn pop(&mut self) -> bool;
fn top(&self) -> bool;
}
impl<T> BitBucket for T
where
T: Shl<u8, Output = T>
+ Shr<u8, Output = T>
+ BitAnd<T, Output = T>
+ BitOr<Output = T>
+ PartialEq
+ Clone
+ Default,
T: From<u8>, {
fn push(&mut self, bit: bool) {
*self = (self.clone() << 1u8) | T::from(bit as u8);
}
fn pop(&mut self) -> bool {
let bit = (self.clone() & T::from(1)) != T::from(0);
*self = self.clone() >> 1u8;
bit
}
fn top(&self) -> bool {
(self.clone() & T::from(1)) != T::from(0)
}
}
pub trait DepthCounter: core::fmt::Debug + Copy {
fn zero() -> Self;
fn increment(self) -> (Self, bool);
fn decrement(self) -> (Self, bool);
fn is_zero(self) -> bool;
}
macro_rules! impl_depth_counter {
($($t:ty),*) => {
$(
impl DepthCounter for $t {
#[inline]
fn zero() -> Self { 0 }
#[inline]
fn increment(self) -> (Self, bool) { self.overflowing_add(1) }
#[inline]
fn decrement(self) -> (Self, bool) { self.overflowing_sub(1) }
#[inline]
fn is_zero(self) -> bool { self == 0 }
}
)*
};
}
impl_depth_counter!(u8, u16, u32, u64, u128, usize);
pub trait BitStackConfig {
type Bucket: BitBucket + Default;
type Counter: DepthCounter + Default;
}
pub struct DefaultConfig;
impl BitStackConfig for DefaultConfig {
type Bucket = u32;
type Counter = u8;
}
pub struct BitStackStruct<B, C> {
_phantom: core::marker::PhantomData<(B, C)>,
}
impl<B, C> BitStackConfig for BitStackStruct<B, C>
where
B: BitBucket + Default,
C: DepthCounter + Default,
{
type Bucket = B;
type Counter = C;
}
pub type ArrayBitStack<const N: usize, T, D> = BitStackStruct<ArrayBitBucket<N, T>, D>;
#[derive(Debug)]
pub struct ArrayBitBucket<const N: usize, T>(pub [T; N]);
impl<const N: usize, T: Default + Copy> Default for ArrayBitBucket<N, T> {
fn default() -> Self {
ArrayBitBucket([T::default(); N])
}
}
impl<const N: usize, T> BitBucket for ArrayBitBucket<N, T>
where
T: Shl<u8, Output = T>
+ Shr<u8, Output = T>
+ BitAnd<T, Output = T>
+ core::ops::BitOr<Output = T>
+ PartialEq
+ Clone
+ From<u8>
+ Copy
+ Default,
{
fn push(&mut self, bit: bool) {
let bit_val = T::from(bit as u8);
let mut carry = bit_val;
let element_bits = (core::mem::size_of::<T>() * 8) as u8;
let msb_shift = element_bits.saturating_sub(1);
for i in (0..N).rev() {
let old_msb = if let Some(element) = self.0.get(i) {
(*element >> msb_shift) & T::from(1) } else {
continue;
};
if let Some(element_mut) = self.0.get_mut(i) {
*element_mut = (*element_mut << 1u8) | carry;
}
carry = old_msb;
}
}
fn pop(&mut self) -> bool {
let bit = if let Some(last_element) = self.0.get(N.saturating_sub(1)) {
(*last_element & T::from(1)) != T::from(0)
} else {
return false;
};
let mut carry = T::from(0);
let element_bits = (core::mem::size_of::<T>() * 8) as u8;
let msb_shift = element_bits.saturating_sub(1);
for i in 0..N {
let old_lsb = if let Some(element) = self.0.get(i) {
*element & T::from(1) } else {
continue;
};
if let Some(element_mut) = self.0.get_mut(i) {
*element_mut = (*element_mut >> 1u8) | (carry << msb_shift);
}
carry = old_lsb;
}
bit
}
fn top(&self) -> bool {
if let Some(last_element) = self.0.get(N.saturating_sub(1)) {
(*last_element & T::from(1)) != T::from(0)
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitstack() {
let mut bitstack = 0;
bitstack.push(true);
bitstack.push(false);
assert!(!bitstack.pop());
assert!(bitstack.pop());
}
#[test]
fn test_array_bitstack() {
let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
bitstack.push(true);
bitstack.push(false);
bitstack.push(true);
assert!(bitstack.top());
assert!(bitstack.top());
assert!(bitstack.pop());
assert!(!bitstack.pop());
assert!(bitstack.pop());
}
#[test]
fn test_array_bitstack_large_capacity() {
let mut bitstack: ArrayBitBucket<10, u32> = ArrayBitBucket::default();
let pattern = [true, false, true, true, false, false, true, false];
for &bit in &pattern {
bitstack.push(bit);
}
for &expected in pattern.iter().rev() {
assert_eq!(bitstack.pop(), expected);
}
}
#[test]
fn test_element_size_handling() {
let mut bitstack_u8: ArrayBitBucket<1, u8> = ArrayBitBucket::default();
for i in 0..8 {
bitstack_u8.push(i % 2 == 0); }
for i in (0..8).rev() {
assert_eq!(bitstack_u8.pop(), i % 2 == 0);
}
let mut bitstack_u32: ArrayBitBucket<1, u32> = ArrayBitBucket::default();
for i in 0..32 {
bitstack_u32.push(i % 3 == 0); }
for i in (0..32).rev() {
assert_eq!(bitstack_u32.pop(), i % 3 == 0);
}
}
#[test]
fn test_array_bitstack_basic_moved() {
let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
bitstack.push(true);
bitstack.push(false);
bitstack.push(true);
assert!(bitstack.top());
assert!(bitstack.top());
assert!(bitstack.pop());
assert!(!bitstack.pop());
assert!(bitstack.pop());
}
#[test]
fn test_array_bitstack_large_capacity_moved() {
let mut bitstack: ArrayBitBucket<10, u32> = ArrayBitBucket::default();
let pattern = [true, false, true, true, false, false, true, false];
for &bit in &pattern {
bitstack.push(bit);
}
for &expected in pattern.iter().rev() {
assert_eq!(bitstack.pop(), expected);
}
}
#[test]
fn test_array_bitstack_element_overflow_moved() {
let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
let bits = [
true, false, true, false, true, false, true, false, true, true,
];
for &bit in &bits {
bitstack.push(bit);
}
for &expected in bits.iter().rev() {
assert_eq!(bitstack.pop(), expected);
}
}
#[test]
fn test_array_bitstack_empty_behavior_moved() {
let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
assert!(!bitstack.pop(), "Empty stack returns false");
assert!(!bitstack.top(), "Empty stack top() returns false");
assert!(!bitstack.pop(), "Multiple underflow calls don't panic");
assert!(!bitstack.pop(), "Multiple underflow calls don't panic");
}
#[test]
fn test_array_bitstack_underflow_does_not_panic_moved() {
let mut bitstack: ArrayBitBucket<1, u8> = ArrayBitBucket::default();
for i in 0..5 {
let result = bitstack.pop();
assert!(
!result,
"Empty ArrayBitStack pop() attempt {} should return false",
i + 1
);
let top_result = bitstack.top();
assert!(
!top_result,
"Empty ArrayBitStack top() attempt {} should return false",
i + 1
);
}
}
}