Skip to main content

fixed_bigint/fixeduint/
power_of_two_impl.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Power-of-two operations for FixedUInt.
16
17use super::{FixedUInt, MachineWord};
18use crate::machineword::ConstMachineWord;
19use const_num_traits::{
20    Bounded, ConstOne, ConstZero, IsPowerOfTwo, NextPowerOfTwo, One, PrimBits, WrappingSub, Zero,
21};
22use const_num_traits::{Personality, PersonalityTag};
23
24c0nst::c0nst! {
25    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> IsPowerOfTwo for FixedUInt<T, N, P> {
26        fn is_power_of_two(self) -> bool {
27            match P::TAG {
28                PersonalityTag::Nct => {
29                    !<Self as Zero>::is_zero(&self) && <Self as Zero>::is_zero(&(self & (self - <Self as One>::one())))
30                }
31                PersonalityTag::Ct => {
32                    // Opacify `a` so LLVM can't rewrite `a & b` back into
33                    // a short-circuit `if a { b } else { false }` — same
34                    // defence as `const_ct_select` at `fixeduint.rs`.
35                    let a = core::hint::black_box(!<Self as Zero>::is_zero(&self));
36                    let b = <Self as Zero>::is_zero(&(self & <Self as WrappingSub>::wrapping_sub(self, <Self as One>::one())));
37                    a & b
38                }
39            }
40        }
41
42    }
43
44    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> NextPowerOfTwo for FixedUInt<T, N, P> {
45        type Output = Self;
46
47        fn wrapping_next_power_of_two(self) -> Self {
48            // Overflow wraps to ZERO under both personalities, matching
49            // std's primitive `wrapping_next_power_of_two`. The Ct arm
50            // is the same branchless body as `next_power_of_two` below,
51            // but selects ZERO on overflow instead of MAX.
52            match P::TAG {
53                PersonalityTag::Nct => match self.checked_next_power_of_two() {
54                    Some(v) => v,
55                    None => <Self as ConstZero>::ZERO,
56                },
57                PersonalityTag::Ct => {
58                    let m_one = <Self as WrappingSub>::wrapping_sub(self, Self::one());
59                    let leading = PrimBits::leading_zeros(m_one);
60                    let bits = Self::BIT_SIZE as u32 - leading;
61                    let shifted = Self::one() << (bits as usize);
62                    let is_zero = <Self as Zero>::is_zero(&self) as u8;
63                    let overflow = ((bits >= Self::BIT_SIZE as u32) as u8) & (1u8 ^ is_zero);
64                    let wrapped = crate::fixeduint::const_ct_select(
65                        shifted,
66                        <Self as ConstZero>::ZERO,
67                        overflow,
68                    );
69                    crate::fixeduint::const_ct_select(wrapped, Self::one(), is_zero)
70                }
71            }
72        }
73
74        fn next_power_of_two(self) -> Self {
75            match P::TAG {
76                PersonalityTag::Nct => {
77                    match self.checked_next_power_of_two() {
78                        Some(v) => v,
79                        None => panic!("FixedUInt::next_power_of_two overflow: result exceeds type capacity"),
80                    }
81                }
82                PersonalityTag::Ct => {
83                    // CT path: saturate to MAX on overflow (same convention
84                    // as SaturatingAdd/Sub/Mul). The Nct path's panic is
85                    // value-dependent and so unavailable here; silently
86                    // returning a wrong power of two would be worse than
87                    // a defined saturation sentinel.
88                    let m_one = <Self as WrappingSub>::wrapping_sub(self, Self::one());
89                    let leading = PrimBits::leading_zeros(m_one);
90                    let bits = Self::BIT_SIZE as u32 - leading;
91                    let shifted = Self::one() << (bits as usize);
92                    let is_zero = <Self as Zero>::is_zero(&self) as u8;
93                    // Overflow when bits >= BIT_SIZE (and self != 0).
94                    let overflow = ((bits >= Self::BIT_SIZE as u32) as u8) & (1u8 ^ is_zero);
95                    let saturated = crate::fixeduint::const_ct_select(
96                        shifted,
97                        <Self as Bounded>::max_value(),
98                        overflow,
99                    );
100                    crate::fixeduint::const_ct_select(saturated, Self::one(), is_zero)
101                }
102            }
103        }
104
105        // NOTE: This impl is NOT constant-time on a `FixedUInt<_, _, Ct>`
106        // carrier — the `Option` shape leaks whether the input was zero
107        // and whether `next_power_of_two` overflowed, both of which are
108        // range predicates on the secret. Ct callers should use the
109        // inherent `FixedUInt::<_, _, Ct>::ct_checked_next_power_of_two`
110        // instead, which returns `CtOption` with a value-masked flag.
111        fn checked_next_power_of_two(self) -> Option<Self> {
112            if self.is_zero() {
113                return Some(Self::one());
114            }
115            // Bit manipulation trick: (n-1).leading_zeros() gives the bit position
116            // needed for the next power of two, handling both power-of-two and
117            // non-power-of-two inputs correctly.
118            let m_one = self - Self::one();
119            let leading = PrimBits::leading_zeros(m_one);
120            let bits = Self::BIT_SIZE as u32 - leading;
121
122            // Check for overflow
123            if bits >= Self::BIT_SIZE as u32 {
124                return None;
125            }
126            Some(Self::one() << (bits as usize))
127        }
128    }
129
130    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> IsPowerOfTwo for &FixedUInt<T, N, P> {
131        fn is_power_of_two(self) -> bool {
132            <FixedUInt<T, N, P> as IsPowerOfTwo>::is_power_of_two(FixedUInt::from_array(self.array))
133        }
134    }
135
136    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> NextPowerOfTwo for &FixedUInt<T, N, P> {
137        type Output = FixedUInt<T, N, P>;
138        fn next_power_of_two(self) -> FixedUInt<T, N, P> {
139            <FixedUInt<T, N, P> as NextPowerOfTwo>::next_power_of_two(FixedUInt::from_array(self.array))
140        }
141        fn checked_next_power_of_two(self) -> Option<FixedUInt<T, N, P>> {
142            <FixedUInt<T, N, P> as NextPowerOfTwo>::checked_next_power_of_two(FixedUInt::from_array(self.array))
143        }
144        fn wrapping_next_power_of_two(self) -> FixedUInt<T, N, P> {
145            <FixedUInt<T, N, P> as NextPowerOfTwo>::wrapping_next_power_of_two(FixedUInt::from_array(self.array))
146        }
147    }
148}
149
150// ── CtIsPowerOfTwo (masked-return is_power_of_two) ───────────────────
151//
152// `nonzero & is_zero(self & (self - 1))` — same predicate as the bool
153// form, just composed of Choice values via `subtle::ConstantTimeEq`
154// (transitively through `CtIsZero`). Uniform across both personalities.
155impl<T, const N: usize, P: Personality> const_num_traits::ops::ct::CtIsPowerOfTwo
156    for FixedUInt<T, N, P>
157where
158    T: MachineWord + subtle::ConstantTimeEq,
159{
160    fn ct_is_power_of_two(&self) -> subtle::Choice {
161        use const_num_traits::ops::ct::CtIsZero;
162        let nonzero = !self.ct_is_zero();
163        let one = <Self as ConstOne>::ONE;
164        // Route through the by-ref `WrappingSub` so `self` isn't
165        // deref-copied onto the stack — matters on Ct paths where
166        // `self` may point into a `Zeroizing<T>` nonce.
167        let masked = self & <&Self as WrappingSub>::wrapping_sub(self, &one);
168        let pow2 = masked.ct_is_zero();
169        nonzero & pow2
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_is_power_of_two() {
179        type U16 = FixedUInt<u8, 2>;
180
181        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(0u8)));
182        assert!(IsPowerOfTwo::is_power_of_two(U16::from(1u8)));
183        assert!(IsPowerOfTwo::is_power_of_two(U16::from(2u8)));
184        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(3u8)));
185        assert!(IsPowerOfTwo::is_power_of_two(U16::from(4u8)));
186        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(5u8)));
187        assert!(IsPowerOfTwo::is_power_of_two(U16::from(8u8)));
188        assert!(IsPowerOfTwo::is_power_of_two(U16::from(16u8)));
189        assert!(IsPowerOfTwo::is_power_of_two(U16::from(128u8)));
190        assert!(IsPowerOfTwo::is_power_of_two(U16::from(256u16)));
191        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(255u8)));
192    }
193
194    #[test]
195    fn test_next_power_of_two() {
196        type U16 = FixedUInt<u8, 2>;
197
198        assert_eq!(
199            NextPowerOfTwo::next_power_of_two(U16::from(0u8)),
200            U16::from(1u8)
201        );
202        assert_eq!(
203            NextPowerOfTwo::next_power_of_two(U16::from(1u8)),
204            U16::from(1u8)
205        );
206        assert_eq!(
207            NextPowerOfTwo::next_power_of_two(U16::from(2u8)),
208            U16::from(2u8)
209        );
210        assert_eq!(
211            NextPowerOfTwo::next_power_of_two(U16::from(3u8)),
212            U16::from(4u8)
213        );
214        assert_eq!(
215            NextPowerOfTwo::next_power_of_two(U16::from(4u8)),
216            U16::from(4u8)
217        );
218        assert_eq!(
219            NextPowerOfTwo::next_power_of_two(U16::from(5u8)),
220            U16::from(8u8)
221        );
222        assert_eq!(
223            NextPowerOfTwo::next_power_of_two(U16::from(7u8)),
224            U16::from(8u8)
225        );
226        assert_eq!(
227            NextPowerOfTwo::next_power_of_two(U16::from(8u8)),
228            U16::from(8u8)
229        );
230        assert_eq!(
231            NextPowerOfTwo::next_power_of_two(U16::from(9u8)),
232            U16::from(16u8)
233        );
234        assert_eq!(
235            NextPowerOfTwo::next_power_of_two(U16::from(100u8)),
236            U16::from(128u8)
237        );
238        assert_eq!(
239            NextPowerOfTwo::next_power_of_two(U16::from(128u8)),
240            U16::from(128u8)
241        );
242        assert_eq!(
243            NextPowerOfTwo::next_power_of_two(U16::from(129u8)),
244            U16::from(256u16)
245        );
246    }
247
248    #[test]
249    fn test_checked_next_power_of_two() {
250        type U16 = FixedUInt<u8, 2>;
251
252        assert_eq!(
253            NextPowerOfTwo::checked_next_power_of_two(U16::from(0u8)),
254            Some(U16::from(1u8))
255        );
256        assert_eq!(
257            NextPowerOfTwo::checked_next_power_of_two(U16::from(1u8)),
258            Some(U16::from(1u8))
259        );
260        assert_eq!(
261            NextPowerOfTwo::checked_next_power_of_two(U16::from(100u8)),
262            Some(U16::from(128u8))
263        );
264
265        // Test overflow case - 32769 needs next power of 2 = 65536 which overflows u16
266        let large = U16::from(32769u16);
267        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(large), None);
268
269        // But 32768 is already a power of two
270        let pow2 = U16::from(32768u16);
271        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(pow2), Some(pow2));
272    }
273
274    c0nst::c0nst! {
275        pub c0nst fn const_is_power_of_two<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
276            v: &FixedUInt<T, N, P>,
277        ) -> bool {
278            IsPowerOfTwo::is_power_of_two(FixedUInt::<T, N, P>::from_array(v.array))
279        }
280
281        pub c0nst fn const_next_power_of_two<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
282            v: FixedUInt<T, N, P>,
283        ) -> FixedUInt<T, N, P> {
284            NextPowerOfTwo::next_power_of_two(v)
285        }
286    }
287
288    #[test]
289    fn test_const_power_of_two() {
290        type U16 = FixedUInt<u8, 2>;
291
292        assert!(const_is_power_of_two(&U16::from(4u8)));
293        assert!(!const_is_power_of_two(&U16::from(5u8)));
294        assert_eq!(const_next_power_of_two(U16::from(5u8)), U16::from(8u8));
295
296        #[cfg(feature = "nightly")]
297        {
298            const FOUR: U16 = FixedUInt::from_array([4, 0]);
299            const FIVE: U16 = FixedUInt::from_array([5, 0]);
300            const IS_POW2_TRUE: bool = const_is_power_of_two(&FOUR);
301            const IS_POW2_FALSE: bool = const_is_power_of_two(&FIVE);
302            const NEXT_POW2: U16 = const_next_power_of_two(FIVE);
303
304            assert!(IS_POW2_TRUE);
305            assert!(!IS_POW2_FALSE);
306            assert_eq!(NEXT_POW2, FixedUInt::from_array([8, 0]));
307        }
308    }
309
310    #[test]
311    fn wrapping_next_power_of_two_wraps_to_zero_under_both_personalities() {
312        use const_num_traits::{Ct, Nct};
313        type U16Nct = FixedUInt<u8, 2, Nct>;
314        type U16Ct = FixedUInt<u8, 2, Ct>;
315        // 32769 > 2^15; next power of two is 2^16 = 0x1_0000, doesn't fit u16.
316        // std's `u16::wrapping_next_power_of_two(32769) == 0`.
317        assert_eq!(
318            U16Nct::from(32769u16).wrapping_next_power_of_two(),
319            U16Nct::from_array([0, 0])
320        );
321        assert_eq!(
322            U16Ct::from(32769u16).wrapping_next_power_of_two(),
323            U16Ct::from_array([0, 0])
324        );
325        // MAX-input overflow behaves the same way.
326        assert_eq!(
327            U16Nct::from(0xFFFFu16).wrapping_next_power_of_two(),
328            U16Nct::from_array([0, 0])
329        );
330        assert_eq!(
331            U16Ct::from(0xFFFFu16).wrapping_next_power_of_two(),
332            U16Ct::from_array([0, 0])
333        );
334        // Non-overflow: both personalities agree on the same real result.
335        assert_eq!(
336            U16Nct::from(100u8).wrapping_next_power_of_two(),
337            U16Nct::from(128u8)
338        );
339        assert_eq!(
340            U16Ct::from(100u8).wrapping_next_power_of_two(),
341            U16Ct::from(128u8)
342        );
343    }
344
345    #[test]
346    fn ct_is_power_of_two_matches_is_power_of_two() {
347        use const_num_traits::ops::ct::CtIsPowerOfTwo;
348        type U16 = FixedUInt<u8, 2>;
349        // Zero is NOT a power of two
350        assert!(!bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(
351            0u8
352        ))));
353        // Powers of two
354        for v in [1u16, 2, 4, 8, 16, 128, 256, 32768] {
355            assert!(
356                bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(v))),
357                "ct_is_power_of_two({v}) should mask Some"
358            );
359        }
360        // Non-powers of two
361        for v in [3u16, 5, 6, 7, 9, 100, 255] {
362            assert!(
363                !bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(v))),
364                "ct_is_power_of_two({v}) should mask None"
365            );
366        }
367    }
368}