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(*self)
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(*self)
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(*self)
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(*self)
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        let masked = *self & <Self as WrappingSub>::wrapping_sub(*self, one);
165        let pow2 = masked.ct_is_zero();
166        nonzero & pow2
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    #[test]
175    fn test_is_power_of_two() {
176        type U16 = FixedUInt<u8, 2>;
177
178        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(0u8)));
179        assert!(IsPowerOfTwo::is_power_of_two(U16::from(1u8)));
180        assert!(IsPowerOfTwo::is_power_of_two(U16::from(2u8)));
181        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(3u8)));
182        assert!(IsPowerOfTwo::is_power_of_two(U16::from(4u8)));
183        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(5u8)));
184        assert!(IsPowerOfTwo::is_power_of_two(U16::from(8u8)));
185        assert!(IsPowerOfTwo::is_power_of_two(U16::from(16u8)));
186        assert!(IsPowerOfTwo::is_power_of_two(U16::from(128u8)));
187        assert!(IsPowerOfTwo::is_power_of_two(U16::from(256u16)));
188        assert!(!IsPowerOfTwo::is_power_of_two(U16::from(255u8)));
189    }
190
191    #[test]
192    fn test_next_power_of_two() {
193        type U16 = FixedUInt<u8, 2>;
194
195        assert_eq!(
196            NextPowerOfTwo::next_power_of_two(U16::from(0u8)),
197            U16::from(1u8)
198        );
199        assert_eq!(
200            NextPowerOfTwo::next_power_of_two(U16::from(1u8)),
201            U16::from(1u8)
202        );
203        assert_eq!(
204            NextPowerOfTwo::next_power_of_two(U16::from(2u8)),
205            U16::from(2u8)
206        );
207        assert_eq!(
208            NextPowerOfTwo::next_power_of_two(U16::from(3u8)),
209            U16::from(4u8)
210        );
211        assert_eq!(
212            NextPowerOfTwo::next_power_of_two(U16::from(4u8)),
213            U16::from(4u8)
214        );
215        assert_eq!(
216            NextPowerOfTwo::next_power_of_two(U16::from(5u8)),
217            U16::from(8u8)
218        );
219        assert_eq!(
220            NextPowerOfTwo::next_power_of_two(U16::from(7u8)),
221            U16::from(8u8)
222        );
223        assert_eq!(
224            NextPowerOfTwo::next_power_of_two(U16::from(8u8)),
225            U16::from(8u8)
226        );
227        assert_eq!(
228            NextPowerOfTwo::next_power_of_two(U16::from(9u8)),
229            U16::from(16u8)
230        );
231        assert_eq!(
232            NextPowerOfTwo::next_power_of_two(U16::from(100u8)),
233            U16::from(128u8)
234        );
235        assert_eq!(
236            NextPowerOfTwo::next_power_of_two(U16::from(128u8)),
237            U16::from(128u8)
238        );
239        assert_eq!(
240            NextPowerOfTwo::next_power_of_two(U16::from(129u8)),
241            U16::from(256u16)
242        );
243    }
244
245    #[test]
246    fn test_checked_next_power_of_two() {
247        type U16 = FixedUInt<u8, 2>;
248
249        assert_eq!(
250            NextPowerOfTwo::checked_next_power_of_two(U16::from(0u8)),
251            Some(U16::from(1u8))
252        );
253        assert_eq!(
254            NextPowerOfTwo::checked_next_power_of_two(U16::from(1u8)),
255            Some(U16::from(1u8))
256        );
257        assert_eq!(
258            NextPowerOfTwo::checked_next_power_of_two(U16::from(100u8)),
259            Some(U16::from(128u8))
260        );
261
262        // Test overflow case - 32769 needs next power of 2 = 65536 which overflows u16
263        let large = U16::from(32769u16);
264        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(large), None);
265
266        // But 32768 is already a power of two
267        let pow2 = U16::from(32768u16);
268        assert_eq!(NextPowerOfTwo::checked_next_power_of_two(pow2), Some(pow2));
269    }
270
271    c0nst::c0nst! {
272        pub c0nst fn const_is_power_of_two<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
273            v: &FixedUInt<T, N, P>,
274        ) -> bool {
275            IsPowerOfTwo::is_power_of_two(*v)
276        }
277
278        pub c0nst fn const_next_power_of_two<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
279            v: FixedUInt<T, N, P>,
280        ) -> FixedUInt<T, N, P> {
281            NextPowerOfTwo::next_power_of_two(v)
282        }
283    }
284
285    #[test]
286    fn test_const_power_of_two() {
287        type U16 = FixedUInt<u8, 2>;
288
289        assert!(const_is_power_of_two(&U16::from(4u8)));
290        assert!(!const_is_power_of_two(&U16::from(5u8)));
291        assert_eq!(const_next_power_of_two(U16::from(5u8)), U16::from(8u8));
292
293        #[cfg(feature = "nightly")]
294        {
295            const FOUR: U16 = FixedUInt::from_array([4, 0]);
296            const FIVE: U16 = FixedUInt::from_array([5, 0]);
297            const IS_POW2_TRUE: bool = const_is_power_of_two(&FOUR);
298            const IS_POW2_FALSE: bool = const_is_power_of_two(&FIVE);
299            const NEXT_POW2: U16 = const_next_power_of_two(FIVE);
300
301            assert!(IS_POW2_TRUE);
302            assert!(!IS_POW2_FALSE);
303            assert_eq!(NEXT_POW2, FixedUInt::from_array([8, 0]));
304        }
305    }
306
307    #[test]
308    fn wrapping_next_power_of_two_wraps_to_zero_under_both_personalities() {
309        use const_num_traits::{Ct, Nct};
310        type U16Nct = FixedUInt<u8, 2, Nct>;
311        type U16Ct = FixedUInt<u8, 2, Ct>;
312        // 32769 > 2^15; next power of two is 2^16 = 0x1_0000, doesn't fit u16.
313        // std's `u16::wrapping_next_power_of_two(32769) == 0`.
314        assert_eq!(
315            U16Nct::from(32769u16).wrapping_next_power_of_two(),
316            U16Nct::from_array([0, 0])
317        );
318        assert_eq!(
319            U16Ct::from(32769u16).wrapping_next_power_of_two(),
320            U16Ct::from_array([0, 0])
321        );
322        // MAX-input overflow behaves the same way.
323        assert_eq!(
324            U16Nct::from(0xFFFFu16).wrapping_next_power_of_two(),
325            U16Nct::from_array([0, 0])
326        );
327        assert_eq!(
328            U16Ct::from(0xFFFFu16).wrapping_next_power_of_two(),
329            U16Ct::from_array([0, 0])
330        );
331        // Non-overflow: both personalities agree on the same real result.
332        assert_eq!(
333            U16Nct::from(100u8).wrapping_next_power_of_two(),
334            U16Nct::from(128u8)
335        );
336        assert_eq!(
337            U16Ct::from(100u8).wrapping_next_power_of_two(),
338            U16Ct::from(128u8)
339        );
340    }
341
342    #[test]
343    fn ct_is_power_of_two_matches_is_power_of_two() {
344        use const_num_traits::ops::ct::CtIsPowerOfTwo;
345        type U16 = FixedUInt<u8, 2>;
346        // Zero is NOT a power of two
347        assert!(!bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(
348            0u8
349        ))));
350        // Powers of two
351        for v in [1u16, 2, 4, 8, 16, 128, 256, 32768] {
352            assert!(
353                bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(v))),
354                "ct_is_power_of_two({v}) should mask Some"
355            );
356        }
357        // Non-powers of two
358        for v in [3u16, 5, 6, 7, 9, 100, 255] {
359            assert!(
360                !bool::from(CtIsPowerOfTwo::ct_is_power_of_two(&U16::from(v))),
361                "ct_is_power_of_two({v}) should mask None"
362            );
363        }
364    }
365}