use super::cmp::ct_select;
use super::shift::ct_shl;
use super::{HeaplessBigInt, arith::max_at_len};
use crate::MachineWord;
use const_num_traits::{
Ct, IsPowerOfTwo, Nct, NextPowerOfTwo, One, Personality, PersonalityTag, PrimBits, WrappingSub,
Zero,
};
impl<T, const CAP: usize, P: Personality> IsPowerOfTwo for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn is_power_of_two(self) -> bool {
match P::TAG {
PersonalityTag::Nct => {
!<Self as Zero>::is_zero(&self)
&& <Self as Zero>::is_zero(&(self & (self - <Self as One>::one())))
}
PersonalityTag::Ct => {
let a = core::hint::black_box(!<Self as Zero>::is_zero(&self));
let b = <Self as Zero>::is_zero(
&(self & <Self as WrappingSub>::wrapping_sub(self, Self::one())),
);
a & b
}
}
}
}
impl<T, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn checked_next_pow2(self) -> Option<Self> {
let width = self.len();
let word_bits = core::mem::size_of::<T>() as u32 * 8;
let width_bits = width as u32 * word_bits;
if <Self as Zero>::is_zero(&self) {
return Some(<Self as One>::one().widened(core::cmp::max(1, width)));
}
let m_one = <Self as WrappingSub>::wrapping_sub(self, <Self as One>::one());
let bits = width_bits - PrimBits::leading_zeros(m_one);
if bits >= width_bits {
return None; }
Some(<Self as One>::one().widened(core::cmp::max(1, width)) << (bits as usize))
}
}
impl<T, const CAP: usize> NextPowerOfTwo for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = Self;
fn wrapping_next_power_of_two(self) -> Self {
let width = self.len();
self.checked_next_pow2()
.unwrap_or_else(|| Self::new_zero_with_len(width))
}
fn next_power_of_two(self) -> Self {
self.checked_next_pow2().unwrap_or_else(|| {
panic!("HeaplessBigInt::next_power_of_two overflow: exceeds the value width")
})
}
fn checked_next_power_of_two(self) -> Option<Self> {
self.checked_next_pow2()
}
}
#[inline]
fn ct_next_pow2<T, const CAP: usize>(
x: HeaplessBigInt<T, CAP, Ct>,
overflow_sentinel: HeaplessBigInt<T, CAP, Ct>,
) -> HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type H<T, const CAP: usize> = HeaplessBigInt<T, CAP, Ct>;
let width = x.len();
let word_bits = core::mem::size_of::<T>() as u32 * 8;
let width_bits = width as u32 * word_bits;
let one_w = <H<T, CAP> as One>::one().widened(core::cmp::max(1, width));
let m_one = <H<T, CAP> as WrappingSub>::wrapping_sub(x, one_w);
let bits = width_bits - PrimBits::leading_zeros(m_one);
let shifted = ct_shl(one_w, bits); let is_zero = <H<T, CAP> as Zero>::is_zero(&x);
let overflow = (bits >= width_bits) & !is_zero;
let saturated = ct_select(&shifted, &overflow_sentinel, overflow);
ct_select(&saturated, &one_w, is_zero)
}
impl<T, const CAP: usize> NextPowerOfTwo for HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = Self;
fn next_power_of_two(self) -> Self {
ct_next_pow2(self, max_at_len(self.len()))
}
fn wrapping_next_power_of_two(self) -> Self {
ct_next_pow2(self, Self::new_zero_with_len(self.len()))
}
fn checked_next_power_of_two(self) -> Option<Self> {
self.checked_next_pow2()
}
}
impl<T, const CAP: usize, P: Personality> IsPowerOfTwo for &HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn is_power_of_two(self) -> bool {
<HeaplessBigInt<T, CAP, P> as IsPowerOfTwo>::is_power_of_two(*self)
}
}
impl<T, const CAP: usize> NextPowerOfTwo for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn wrapping_next_power_of_two(self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::wrapping_next_power_of_two(*self)
}
fn next_power_of_two(self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::next_power_of_two(*self)
}
fn checked_next_power_of_two(self) -> Option<Self::Output> {
<HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::checked_next_power_of_two(*self)
}
}
impl<T, const CAP: usize> NextPowerOfTwo for &HeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
type Output = HeaplessBigInt<T, CAP, Ct>;
fn next_power_of_two(self) -> Self::Output {
<HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::next_power_of_two(*self)
}
fn wrapping_next_power_of_two(self) -> Self::Output {
<HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::wrapping_next_power_of_two(*self)
}
fn checked_next_power_of_two(self) -> Option<Self::Output> {
<HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::checked_next_power_of_two(*self)
}
}
impl<T, const CAP: usize, P: Personality> const_num_traits::ops::ct::CtIsPowerOfTwo
for HeaplessBigInt<T, CAP, P>
where
T: MachineWord + subtle::ConstantTimeEq,
{
fn ct_is_power_of_two(&self) -> subtle::Choice {
use const_num_traits::ops::ct::CtIsZero;
let nonzero = !self.ct_is_zero();
let one = <Self as const_num_traits::ConstOne>::ONE;
let masked = *self & <Self as WrappingSub>::wrapping_sub(*self, one);
nonzero & masked.ct_is_zero()
}
}
#[cfg(test)]
mod tests {
use super::HeaplessBigInt;
use const_num_traits::NextPowerOfTwo;
use const_num_traits::ops::ct::CtIsPowerOfTwo;
type H = HeaplessBigInt<u8, 8>;
#[test]
fn next_power_of_two_preserves_width() {
let five = H::from(5u8).widened(8);
let np = NextPowerOfTwo::next_power_of_two(five);
assert_eq!(np, H::from(8u8));
assert_eq!(np.len(), 8);
let zero = H::new_zero_with_len(8);
let np0 = NextPowerOfTwo::next_power_of_two(zero);
assert_eq!(np0, H::from(1u8));
assert_eq!(np0.len(), 8);
let already = H::from(128u8).widened(8);
let np2 = NextPowerOfTwo::next_power_of_two(already);
assert_eq!(np2.len(), 8);
}
#[test]
fn checked_overflow_at_value_width() {
let x = HeaplessBigInt::<u8, 8>::from(0x81u8);
assert_eq!(x.len(), 1);
assert_eq!(NextPowerOfTwo::checked_next_power_of_two(x), None);
let w = NextPowerOfTwo::wrapping_next_power_of_two(x);
assert_eq!(w, HeaplessBigInt::<u8, 8>::from(0u8));
assert_eq!(w.len(), 1);
}
#[test]
fn ct_is_power_of_two_matches_bool() {
for v in [0u32, 1, 2, 3, 4, 100, 255, 256, 0x8000_0000] {
let h = H::from(v);
let ct = bool::from(h.ct_is_power_of_two());
assert_eq!(ct, v != 0 && v & (v - 1) == 0, "ct_is_power_of_two({v})");
}
}
#[test]
fn ct_next_power_of_two_matches_and_saturates() {
use const_num_traits::Ct;
type HC = HeaplessBigInt<u8, 4, Ct>;
for v in [1u32, 5, 128, 0x4000_0000] {
assert_eq!(
NextPowerOfTwo::next_power_of_two(HC::from(v)),
HC::from(v.next_power_of_two()),
"ct next_power_of_two({v})"
);
}
assert_eq!(
NextPowerOfTwo::next_power_of_two(HC::from(0u8).widened(4)),
HC::from(1u8)
);
let big = HC::from(0x8000_0001u32);
assert_eq!(
NextPowerOfTwo::next_power_of_two(big),
HC::from(0xFFFF_FFFFu32)
);
assert_eq!(
NextPowerOfTwo::wrapping_next_power_of_two(big),
HC::from(0u8)
);
assert_eq!(NextPowerOfTwo::checked_next_power_of_two(big), None);
}
#[test]
fn byref_matches_value() {
use const_num_traits::{Ct, IsPowerOfTwo};
let a = H::from(5u8);
let r = &a; assert_eq!(
IsPowerOfTwo::is_power_of_two(r),
IsPowerOfTwo::is_power_of_two(a)
);
assert_eq!(
NextPowerOfTwo::next_power_of_two(r),
NextPowerOfTwo::next_power_of_two(a)
);
assert_eq!(
NextPowerOfTwo::wrapping_next_power_of_two(r),
NextPowerOfTwo::wrapping_next_power_of_two(a)
);
assert_eq!(
NextPowerOfTwo::checked_next_power_of_two(r),
NextPowerOfTwo::checked_next_power_of_two(a)
);
type HC = HeaplessBigInt<u8, 4, Ct>;
let c = HC::from(5u8);
assert_eq!(
NextPowerOfTwo::next_power_of_two(&c),
NextPowerOfTwo::next_power_of_two(c)
);
}
}