Skip to main content

fixed_bigint/heapless/
identities.rs

1//! `Zero`, `One`, `Default` for `HeaplessBigInt`.
2//!
3//! - `Zero`: mathematical zero, `len = 0`.
4//! - `One`: `len = 1`, `limbs[0] = T::ONE`.
5//! - `Default = Zero`. (The CIOS full-CAP zero is a separate constructor,
6//!   `cios_accumulator`, not `Default`.)
7
8use super::{AssertCapFits, HeaplessBigInt, zero};
9use crate::MachineWord;
10use const_num_traits::{Bounded, ConstOne, ConstZero, One, Personality, PersonalityTag, Zero};
11use core::marker::PhantomData;
12
13// ── const_num_traits::Zero / One ──
14
15impl<T: MachineWord, const CAP: usize, P: Personality> Zero for HeaplessBigInt<T, CAP, P> {
16    #[inline]
17    fn zero() -> Self {
18        Self::const_zero()
19    }
20
21    #[inline]
22    fn is_zero(&self) -> bool {
23        // Any limb non-zero → non-zero. `Nct` short-circuits; `Ct`
24        // OR-folds every limb so timing is value-independent (the returned
25        // `bool` is still branchable — see `CtIsZero::ct_is_zero` for the
26        // `Choice`-returning form). Limbs beyond `len` are zero by the
27        // zero-tail invariant, so scanning `0..len` suffices.
28        let n = self.len as usize;
29        match P::TAG {
30            PersonalityTag::Nct => {
31                let mut i = 0;
32                while i < n {
33                    if !super::is_zero(&self.limbs[i]) {
34                        return false;
35                    }
36                    i += 1;
37                }
38                true
39            }
40            PersonalityTag::Ct => {
41                let mut acc = zero::<T>();
42                let mut i = 0;
43                while i < n {
44                    acc |= self.limbs[i];
45                    i += 1;
46                }
47                super::is_zero(&acc)
48            }
49        }
50    }
51
52    #[inline]
53    fn set_zero(&mut self) {
54        *self = <Self as Zero>::zero();
55    }
56}
57
58impl<T: MachineWord, const CAP: usize, P: Personality> One for HeaplessBigInt<T, CAP, P> {
59    #[inline]
60    fn one() -> Self {
61        let () = <Self as AssertCapFits>::CHECK;
62        Self::const_one()
63    }
64
65    #[inline]
66    fn set_one(&mut self) {
67        *self = <Self as One>::one();
68    }
69
70    #[inline]
71    fn is_one(&self) -> bool {
72        // `len` is a public shape parameter, so branching on it is fine in
73        // both personalities. `Nct` short-circuits the limb scan; `Ct`
74        // folds `(limbs[0] ^ 1) | limbs[1] | …` with no early return.
75        let n = self.len as usize;
76        if n == 0 {
77            return false;
78        }
79        match P::TAG {
80            PersonalityTag::Nct => {
81                if !<T as const_num_traits::One>::is_one(&self.limbs[0]) {
82                    return false;
83                }
84                let mut i = 1;
85                while i < n {
86                    if !super::is_zero(&self.limbs[i]) {
87                        return false;
88                    }
89                    i += 1;
90                }
91                true
92            }
93            PersonalityTag::Ct => const_is_one_ct(&self.limbs, n),
94        }
95    }
96}
97
98/// CT fold for [`One::is_one`]: `(limbs[0] ^ 1) | limbs[1] | … | limbs[n-1]`,
99/// zero iff the value is the canonical one. Timing depends only on the public
100/// `len` (`n`), never on where the value first diverges from one. Caller
101/// guarantees `n >= 1`.
102///
103/// `#[inline(never)]` so the fold's `len`-bounded loop lands in one attestable
104/// helper symbol; inlined into its lone fixture caller, the runtime-`len` loop
105/// would read as an un-attestable branch to the CT gate.
106#[inline(never)]
107pub(crate) fn const_is_one_ct<T: MachineWord, const CAP: usize>(
108    limbs: &[T; CAP],
109    n: usize,
110) -> bool {
111    let mut acc = limbs[0] ^ <T as ConstOne>::ONE;
112    let mut i = 1;
113    while i < n {
114        acc |= limbs[i];
115        i += 1;
116    }
117    super::is_zero(&acc)
118}
119
120impl<T: MachineWord, const CAP: usize, P: Personality> Default for HeaplessBigInt<T, CAP, P> {
121    #[inline]
122    fn default() -> Self {
123        <Self as Zero>::zero()
124    }
125}
126
127// ── const_num_traits::ConstZero / ConstOne ──
128//
129// Declared as `const` items so downstream can use them in const
130// expressions. `ConstOne::ONE` needs a mutable-array initialisation
131// step, which requires a helper `const fn` on stable.
132
133impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
134    #[inline]
135    const fn const_zero() -> Self {
136        Self {
137            limbs: [<T as ConstZero>::ZERO; CAP],
138            len: 0,
139            _p: PhantomData,
140        }
141    }
142
143    #[inline]
144    const fn const_one() -> Self {
145        assert!(CAP >= 1, "HeaplessBigInt::ONE requires CAP >= 1");
146        let mut limbs = [<T as ConstZero>::ZERO; CAP];
147        limbs[0] = <T as ConstOne>::ONE;
148        Self {
149            limbs,
150            len: 1,
151            _p: PhantomData,
152        }
153    }
154}
155
156impl<T: MachineWord, const CAP: usize, P: Personality> ConstZero for HeaplessBigInt<T, CAP, P> {
157    const ZERO: Self = Self::const_zero();
158}
159
160impl<T: MachineWord, const CAP: usize, P: Personality> ConstOne for HeaplessBigInt<T, CAP, P> {
161    const ONE: Self = Self::const_one();
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use const_num_traits::Ct;
168
169    type Hc = HeaplessBigInt<u8, 4, Ct>;
170
171    // `One::is_one` on the `Ct` carrier folds through `const_is_one_ct` with no
172    // early return; it must still match the predicate exactly, including the
173    // `len == 0` (zero) guard and a `1` that sits in a higher limb.
174    #[test]
175    fn ct_is_one() {
176        assert!(<Hc as One>::is_one(&<Hc as One>::one()));
177        assert!(!<Hc as One>::is_one(&<Hc as Zero>::zero()));
178        assert!(!<Hc as One>::is_one(&Hc::from_limbs([2, 0, 0, 0], 1)));
179        assert!(!<Hc as One>::is_one(&Hc::from_limbs([0, 1, 0, 0], 2)));
180    }
181}
182
183// ── const_num_traits::Bounded ──
184//
185// `min = 0` (len 0). `max` is the capacity bound: every one of `CAP` limbs
186// saturated, `len = CAP`. This is the one answer `CAP` legitimately sets —
187// it is the widest value the storage can represent, not a value width.
188impl<T: MachineWord, const CAP: usize, P: Personality> Bounded for HeaplessBigInt<T, CAP, P> {
189    #[inline]
190    fn min_value() -> Self {
191        <Self as ConstZero>::ZERO
192    }
193
194    #[inline]
195    fn max_value() -> Self {
196        // `len = CAP` here, so CAP must fit u16 — assert it the same way the
197        // shape-setting constructors do, rather than silently truncating.
198        let () = <Self as AssertCapFits>::CHECK;
199        Self {
200            limbs: [<T as Bounded>::max_value(); CAP],
201            len: CAP as u16,
202            _p: PhantomData,
203        }
204    }
205}