Skip to main content

fixed_bigint/heapless/
power_of_two.rs

1//! `const_num_traits::IsPowerOfTwo` / `NextPowerOfTwo` for `HeaplessBigInt`.
2//!
3//! `is_power_of_two` is personality-generic (the Ct arm is `black_box`-guarded,
4//! no branch). `NextPowerOfTwo` is implemented for both personalities: the
5//! `Nct` arm branches on overflow (panic / wrap-to-zero); the `Ct` arm is
6//! constant-time — the `1 << bits` shift by a secret `bits` goes through the
7//! [`ct_shl`](super::shift::ct_shl) barrel shifter (a plain `<<` would leak the
8//! magnitude), and the overflow/zero selects use [`ct_select`]. Both arms share
9//! the value-based `checked_next_pow2` helper; `checked_next_power_of_two`
10//! itself stays branchful (its `Option` reveals the overflow bit) — not CT,
11//! same caveat as FixedUInt — so Ct callers use `next`/`wrapping`.
12//!
13//! Result width is the operand width: `one` is widened before the shift so the
14//! power of two lands at `len`, not the minimal identity width.
15
16use super::cmp::ct_select;
17use super::shift::ct_shl;
18use super::{HeaplessBigInt, arith::max_at_len};
19use crate::MachineWord;
20use const_num_traits::{
21    Ct, IsPowerOfTwo, Nct, NextPowerOfTwo, One, Personality, PersonalityTag, PrimBits, WrappingSub,
22    Zero,
23};
24
25impl<T, const CAP: usize, P: Personality> IsPowerOfTwo for HeaplessBigInt<T, CAP, P>
26where
27    T: MachineWord,
28{
29    fn is_power_of_two(self) -> bool {
30        // A power of two has exactly one bit set: `x != 0 && x & (x - 1) == 0`.
31        match P::TAG {
32            PersonalityTag::Nct => {
33                !<Self as Zero>::is_zero(&self)
34                    && <Self as Zero>::is_zero(&(self & (self - <Self as One>::one())))
35            }
36            PersonalityTag::Ct => {
37                // `black_box` stops LLVM rewriting `a & b` back into a
38                // short-circuit; `wrapping_sub` avoids the Nct underflow panic.
39                let a = core::hint::black_box(!<Self as Zero>::is_zero(&self));
40                let b = <Self as Zero>::is_zero(
41                    &(self & <Self as WrappingSub>::wrapping_sub(self, Self::one())),
42                );
43                a & b
44            }
45        }
46    }
47}
48
49// Shared value-based checked next-power-of-two, P-generic. Branchful (the
50// `Option` reveals the overflow bit), so on `Ct` it is NOT constant-time — Ct
51// callers use `next`/`wrapping`, which are constant-time. `wrapping_sub` keeps
52// it usable on `Ct` without the Nct underflow panic; `self` is non-zero there.
53impl<T, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P>
54where
55    T: MachineWord,
56{
57    fn checked_next_pow2(self) -> Option<Self> {
58        let width = self.len();
59        let word_bits = core::mem::size_of::<T>() as u32 * 8;
60        let width_bits = width as u32 * word_bits;
61        if <Self as Zero>::is_zero(&self) {
62            return Some(<Self as One>::one().widened(core::cmp::max(1, width)));
63        }
64        // `(n - 1).leading_zeros()` gives the position of the next power of two.
65        let m_one = <Self as WrappingSub>::wrapping_sub(self, <Self as One>::one());
66        let bits = width_bits - PrimBits::leading_zeros(m_one);
67        if bits >= width_bits {
68            return None; // 2^width_bits doesn't fit the value width
69        }
70        Some(<Self as One>::one().widened(core::cmp::max(1, width)) << (bits as usize))
71    }
72}
73
74impl<T, const CAP: usize> NextPowerOfTwo for HeaplessBigInt<T, CAP, Nct>
75where
76    T: MachineWord,
77{
78    type Output = Self;
79
80    fn wrapping_next_power_of_two(self) -> Self {
81        let width = self.len();
82        self.checked_next_pow2()
83            .unwrap_or_else(|| Self::new_zero_with_len(width))
84    }
85
86    fn next_power_of_two(self) -> Self {
87        self.checked_next_pow2().unwrap_or_else(|| {
88            panic!("HeaplessBigInt::next_power_of_two overflow: exceeds the value width")
89        })
90    }
91
92    fn checked_next_power_of_two(self) -> Option<Self> {
93        self.checked_next_pow2()
94    }
95}
96
97// Constant-time Ct core, shared by `next`/`wrapping` (differ only in the
98// `overflow_sentinel`: width-max for `next`, zero for `wrapping`). Mirrors
99// FixedUInt's Ct `next_power_of_two` but the secret-amount `1 << bits` shift
100// goes through `ct_shl` (a barrel shifter) rather than the leaky `<<`, and the
101// overflow flag is combined with `&` (not short-circuiting `&&`) so nothing
102// branches on the secret.
103#[inline]
104fn ct_next_pow2<T, const CAP: usize>(
105    x: HeaplessBigInt<T, CAP, Ct>,
106    overflow_sentinel: HeaplessBigInt<T, CAP, Ct>,
107) -> HeaplessBigInt<T, CAP, Ct>
108where
109    T: MachineWord + subtle::ConditionallySelectable,
110{
111    type H<T, const CAP: usize> = HeaplessBigInt<T, CAP, Ct>;
112    let width = x.len();
113    let word_bits = core::mem::size_of::<T>() as u32 * 8;
114    let width_bits = width as u32 * word_bits;
115    let one_w = <H<T, CAP> as One>::one().widened(core::cmp::max(1, width));
116    let m_one = <H<T, CAP> as WrappingSub>::wrapping_sub(x, one_w);
117    let bits = width_bits - PrimBits::leading_zeros(m_one);
118    let shifted = ct_shl(one_w, bits); // CT shift by the secret `bits`
119    let is_zero = <H<T, CAP> as Zero>::is_zero(&x);
120    let overflow = (bits >= width_bits) & !is_zero;
121    let saturated = ct_select(&shifted, &overflow_sentinel, overflow);
122    ct_select(&saturated, &one_w, is_zero)
123}
124
125impl<T, const CAP: usize> NextPowerOfTwo for HeaplessBigInt<T, CAP, Ct>
126where
127    T: MachineWord + subtle::ConditionallySelectable,
128{
129    type Output = Self;
130
131    fn next_power_of_two(self) -> Self {
132        // Saturate to the width-max on overflow (the Nct panic is value-
133        // dependent, so unavailable here — a defined sentinel beats a wrong one).
134        ct_next_pow2(self, max_at_len(self.len()))
135    }
136
137    fn wrapping_next_power_of_two(self) -> Self {
138        ct_next_pow2(self, Self::new_zero_with_len(self.len()))
139    }
140
141    fn checked_next_power_of_two(self) -> Option<Self> {
142        // Branchful (see `checked_next_pow2`): NOT constant-time on Ct.
143        self.checked_next_pow2()
144    }
145}
146
147// `&Self` mirrors so `(&h).next_power_of_two()` resolves without an explicit copy.
148impl<T, const CAP: usize, P: Personality> IsPowerOfTwo for &HeaplessBigInt<T, CAP, P>
149where
150    T: MachineWord,
151{
152    fn is_power_of_two(self) -> bool {
153        <HeaplessBigInt<T, CAP, P> as IsPowerOfTwo>::is_power_of_two(*self)
154    }
155}
156
157impl<T, const CAP: usize> NextPowerOfTwo for &HeaplessBigInt<T, CAP, Nct>
158where
159    T: MachineWord,
160{
161    type Output = HeaplessBigInt<T, CAP, Nct>;
162
163    fn wrapping_next_power_of_two(self) -> Self::Output {
164        <HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::wrapping_next_power_of_two(*self)
165    }
166
167    fn next_power_of_two(self) -> Self::Output {
168        <HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::next_power_of_two(*self)
169    }
170
171    fn checked_next_power_of_two(self) -> Option<Self::Output> {
172        <HeaplessBigInt<T, CAP, Nct> as NextPowerOfTwo>::checked_next_power_of_two(*self)
173    }
174}
175
176impl<T, const CAP: usize> NextPowerOfTwo for &HeaplessBigInt<T, CAP, Ct>
177where
178    T: MachineWord + subtle::ConditionallySelectable,
179{
180    type Output = HeaplessBigInt<T, CAP, Ct>;
181
182    fn next_power_of_two(self) -> Self::Output {
183        <HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::next_power_of_two(*self)
184    }
185
186    fn wrapping_next_power_of_two(self) -> Self::Output {
187        <HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::wrapping_next_power_of_two(*self)
188    }
189
190    fn checked_next_power_of_two(self) -> Option<Self::Output> {
191        <HeaplessBigInt<T, CAP, Ct> as NextPowerOfTwo>::checked_next_power_of_two(*self)
192    }
193}
194
195// `CtIsPowerOfTwo` — masked-return `is_power_of_two`, the same subtle-predicate
196// style as `CtIsZero`/`CtParity` (`nonzero & is_zero(x & (x - 1))` composed of
197// `Choice`s). No `const_ct_select` needed, so it's personality-generic.
198impl<T, const CAP: usize, P: Personality> const_num_traits::ops::ct::CtIsPowerOfTwo
199    for HeaplessBigInt<T, CAP, P>
200where
201    T: MachineWord + subtle::ConstantTimeEq,
202{
203    fn ct_is_power_of_two(&self) -> subtle::Choice {
204        use const_num_traits::ops::ct::CtIsZero;
205        let nonzero = !self.ct_is_zero();
206        let one = <Self as const_num_traits::ConstOne>::ONE;
207        let masked = *self & <Self as WrappingSub>::wrapping_sub(*self, one);
208        nonzero & masked.ct_is_zero()
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::HeaplessBigInt;
215    use const_num_traits::NextPowerOfTwo;
216    use const_num_traits::ops::ct::CtIsPowerOfTwo;
217
218    type H = HeaplessBigInt<u8, 8>;
219
220    // The result carries the operand width, not the minimal identity width:
221    // `one` is widened before `<< bits`. A value-only `==` can't see this —
222    // only the `.len` assertions catch a narrowing regression.
223    #[test]
224    fn next_power_of_two_preserves_width() {
225        let five = H::from(5u8).widened(8);
226        let np = NextPowerOfTwo::next_power_of_two(five);
227        assert_eq!(np, H::from(8u8));
228        assert_eq!(np.len(), 8);
229
230        // Zero widens the identity to the operand width too.
231        let zero = H::new_zero_with_len(8);
232        let np0 = NextPowerOfTwo::next_power_of_two(zero);
233        assert_eq!(np0, H::from(1u8));
234        assert_eq!(np0.len(), 8);
235
236        let already = H::from(128u8).widened(8);
237        let np2 = NextPowerOfTwo::next_power_of_two(already);
238        assert_eq!(np2.len(), 8);
239    }
240
241    #[test]
242    fn checked_overflow_at_value_width() {
243        // A one-word carrier: 0x81 rounds up to 0x100, past the 8-bit width.
244        let x = HeaplessBigInt::<u8, 8>::from(0x81u8);
245        assert_eq!(x.len(), 1);
246        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(x), None);
247        // wrapping wraps to zero, still at the operand width.
248        let w = NextPowerOfTwo::wrapping_next_power_of_two(x);
249        assert_eq!(w, HeaplessBigInt::<u8, 8>::from(0u8));
250        assert_eq!(w.len(), 1);
251    }
252
253    #[test]
254    fn ct_is_power_of_two_matches_bool() {
255        for v in [0u32, 1, 2, 3, 4, 100, 255, 256, 0x8000_0000] {
256            let h = H::from(v);
257            let ct = bool::from(h.ct_is_power_of_two());
258            assert_eq!(ct, v != 0 && v & (v - 1) == 0, "ct_is_power_of_two({v})");
259        }
260    }
261
262    #[test]
263    fn ct_next_power_of_two_matches_and_saturates() {
264        use const_num_traits::Ct;
265        type HC = HeaplessBigInt<u8, 4, Ct>;
266
267        // Non-overflow values agree with std (via the CT barrel-shift path).
268        for v in [1u32, 5, 128, 0x4000_0000] {
269            assert_eq!(
270                NextPowerOfTwo::next_power_of_two(HC::from(v)),
271                HC::from(v.next_power_of_two()),
272                "ct next_power_of_two({v})"
273            );
274        }
275        // 0 -> 1 at the operand width.
276        assert_eq!(
277            NextPowerOfTwo::next_power_of_two(HC::from(0u8).widened(4)),
278            HC::from(1u8)
279        );
280        // 2^31+1 rounds to 2^32, past the 32-bit width: `next` saturates to the
281        // width-max, `wrapping` wraps to zero, `checked` is None.
282        let big = HC::from(0x8000_0001u32);
283        assert_eq!(
284            NextPowerOfTwo::next_power_of_two(big),
285            HC::from(0xFFFF_FFFFu32)
286        );
287        assert_eq!(
288            NextPowerOfTwo::wrapping_next_power_of_two(big),
289            HC::from(0u8)
290        );
291        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(big), None);
292    }
293
294    #[test]
295    fn byref_matches_value() {
296        use const_num_traits::{Ct, IsPowerOfTwo};
297        let a = H::from(5u8);
298        let r = &a; // dispatch through the `&Self` mirror
299        assert_eq!(
300            IsPowerOfTwo::is_power_of_two(r),
301            IsPowerOfTwo::is_power_of_two(a)
302        );
303        assert_eq!(
304            NextPowerOfTwo::next_power_of_two(r),
305            NextPowerOfTwo::next_power_of_two(a)
306        );
307        assert_eq!(
308            NextPowerOfTwo::wrapping_next_power_of_two(r),
309            NextPowerOfTwo::wrapping_next_power_of_two(a)
310        );
311        assert_eq!(
312            NextPowerOfTwo::checked_next_power_of_two(r),
313            NextPowerOfTwo::checked_next_power_of_two(a)
314        );
315
316        type HC = HeaplessBigInt<u8, 4, Ct>;
317        let c = HC::from(5u8);
318        assert_eq!(
319            NextPowerOfTwo::next_power_of_two(&c),
320            NextPowerOfTwo::next_power_of_two(c)
321        );
322    }
323}