use crate::{Checked, CheckedUnsigned, Numeric, SignedInteger, UnsignedInteger};
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct BitCount<const MAX: u32> {
pub(crate) bits: u32,
}
impl<const MAX: u32> BitCount<MAX> {
pub const fn new<const BITS: u32>() -> Self {
const {
assert!(BITS <= MAX, "BITS must be <= MAX");
}
Self { bits: BITS }
}
#[inline]
pub const fn checked_add<const NEW_MAX: u32>(self, bits: u32) -> Option<BitCount<NEW_MAX>> {
match self.bits.checked_add(bits) {
Some(bits) if bits <= NEW_MAX => Some(BitCount { bits }),
_ => None,
}
}
#[inline]
pub const fn checked_sub<const NEW_MAX: u32>(self, bits: u32) -> Option<BitCount<NEW_MAX>> {
match self.bits.checked_sub(bits) {
Some(bits) if bits <= NEW_MAX => Some(BitCount { bits }),
_ => None,
}
}
#[inline]
pub fn try_map<const NEW_MAX: u32, F>(self, f: F) -> Option<BitCount<NEW_MAX>>
where
F: FnOnce(u32) -> Option<u32>,
{
f(self.bits)
.filter(|bits| *bits <= NEW_MAX)
.map(|bits| BitCount { bits })
}
#[inline(always)]
pub const fn max(&self) -> u32 {
MAX
}
#[inline(always)]
pub const fn signed_count(&self) -> Option<SignedBitCount<MAX>> {
match self.bits.checked_sub(1) {
Some(bits) => Some(SignedBitCount {
bits: *self,
unsigned: BitCount { bits },
}),
None => None,
}
}
pub fn mask_lsb<U: UnsignedInteger>(self) -> impl Fn(U) -> (U, CheckedUnsigned<MAX, U>) {
let (mask, shift, count) = match U::BITS_SIZE.checked_sub(self.bits) {
Some(mask_bits) => (U::ALL.shr_default(mask_bits), self.bits, self),
None => (
U::ALL,
U::BITS_SIZE,
BitCount { bits: U::BITS_SIZE },
),
};
move |v| {
(
v.shr_default(shift),
Checked {
value: v & mask,
count,
},
)
}
}
#[inline]
pub fn range<U: UnsignedInteger>(&self) -> core::ops::RangeInclusive<U> {
match U::ONE.checked_shl(self.bits) {
Some(top) => U::ZERO..=(top - U::ONE),
None => U::ZERO..=U::ALL,
}
}
#[inline(always)]
pub fn min(self, bits: u32) -> Self {
Self {
bits: self.bits.min(bits),
}
}
#[inline(always)]
pub fn none<U: UnsignedInteger>(self) -> CheckedUnsigned<MAX, U> {
CheckedUnsigned {
value: U::ZERO,
count: self,
}
}
#[inline(always)]
pub fn all<U: UnsignedInteger>(self) -> CheckedUnsigned<MAX, U> {
CheckedUnsigned {
value: match U::ONE.checked_shl(self.bits) {
Some(top) => top - U::ONE,
None => U::ALL,
},
count: self,
}
}
}
impl<const MAX: u32> core::convert::TryFrom<u32> for BitCount<MAX> {
type Error = u32;
fn try_from(bits: u32) -> Result<Self, Self::Error> {
(bits <= MAX).then_some(Self { bits }).ok_or(bits)
}
}
impl<const MAX: u32> core::fmt::Display for BitCount<MAX> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(&self.bits, f)
}
}
impl BitCount<{ u32::MAX }> {
pub const fn unknown(bits: u32) -> Self {
Self { bits }
}
}
#[test]
fn test_unknown_bitcount() {
let count = BitCount::unknown(u32::MAX);
assert!(u32::from(count) <= count.max());
}
impl<const MAX: u32> From<BitCount<MAX>> for u32 {
#[inline(always)]
fn from(BitCount { bits }: BitCount<MAX>) -> u32 {
bits
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct SignedBitCount<const MAX: u32> {
pub(crate) bits: BitCount<MAX>,
pub(crate) unsigned: BitCount<MAX>,
}
impl<const MAX: u32> SignedBitCount<MAX> {
pub const fn new<const BITS: u32>() -> Self {
const {
assert!(BITS > 0, "BITS must be > 0");
}
Self {
bits: BitCount::new::<BITS>(),
unsigned: BitCount { bits: BITS - 1 },
}
}
#[inline]
pub const fn checked_add<const NEW_MAX: u32>(
self,
bits: u32,
) -> Option<SignedBitCount<NEW_MAX>> {
match self.bits.checked_add(bits) {
Some(bits_new) => match self.unsigned.checked_add(bits) {
Some(unsigned) => Some(SignedBitCount {
bits: bits_new,
unsigned,
}),
None => None,
},
None => None,
}
}
#[inline]
pub const fn checked_sub<const NEW_MAX: u32>(
self,
bits: u32,
) -> Option<SignedBitCount<NEW_MAX>> {
match self.bits.checked_sub(bits) {
Some(bits_new) => match self.unsigned.checked_sub(bits) {
Some(unsigned) => Some(SignedBitCount {
bits: bits_new,
unsigned,
}),
None => None,
},
None => None,
}
}
#[inline]
pub fn try_map<const NEW_MAX: u32, F>(self, f: F) -> Option<SignedBitCount<NEW_MAX>>
where
F: FnOnce(u32) -> Option<u32>,
{
self.bits.try_map(f).and_then(|b| b.signed_count())
}
#[inline(always)]
pub const fn max(&self) -> u32 {
MAX
}
#[inline(always)]
pub const fn count(&self) -> BitCount<MAX> {
self.bits
}
pub fn range<S: SignedInteger>(&self) -> core::ops::RangeInclusive<S> {
if self.bits.bits < S::BITS_SIZE {
(!S::ZERO << self.unsigned.bits)..=((S::ONE << self.unsigned.bits) - S::ONE)
} else {
S::Unsigned::ZERO.as_negative(S::BITS_SIZE)..=(S::Unsigned::ALL >> 1).as_non_negative()
}
}
}
impl<const MAX: u32> core::fmt::Display for SignedBitCount<MAX> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Display::fmt(&self.bits, f)
}
}
impl<const MAX: u32> core::convert::TryFrom<BitCount<MAX>> for SignedBitCount<MAX> {
type Error = ();
#[inline]
fn try_from(count: BitCount<MAX>) -> Result<Self, Self::Error> {
count.signed_count().ok_or(())
}
}
impl<const MAX: u32> core::convert::TryFrom<u32> for SignedBitCount<MAX> {
type Error = u32;
#[inline]
fn try_from(count: u32) -> Result<Self, Self::Error> {
BitCount::<MAX>::try_from(count).and_then(|b| b.signed_count().ok_or(count))
}
}
impl<const MAX: u32> From<SignedBitCount<MAX>> for u32 {
#[inline(always)]
fn from(
SignedBitCount {
bits: BitCount { bits },
..
}: SignedBitCount<MAX>,
) -> u32 {
bits
}
}