Skip to main content

fixed_bigint/fixeduint/
num_traits_identity.rs

1use super::{
2    FixedUInt, MachineWord, const_is_one, const_is_one_ct, const_is_zero, const_is_zero_ct,
3};
4use crate::machineword::ConstMachineWord;
5use const_num_traits::{Bounded, ConstOne, ConstZero, One, Zero};
6use const_num_traits::{Personality, PersonalityTag};
7
8c0nst::c0nst! {
9    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Zero for FixedUInt<T, N, P> {
10        fn zero() -> Self {
11            FixedUInt::from_array([<T as Zero>::zero(); N])
12        }
13        fn is_zero(&self) -> bool {
14            match P::TAG {
15                PersonalityTag::Nct => const_is_zero(&self.array),
16                PersonalityTag::Ct => const_is_zero_ct(&self.array),
17            }
18        }
19        fn set_zero(&mut self) {
20            let mut i = 0;
21            while i < N {
22                <T as Zero>::set_zero(&mut self.array[i]);
23                i += 1;
24            }
25        }
26    }
27
28
29    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> One for FixedUInt<T, N, P> {
30        fn one() -> Self {
31            let mut ret = <Self as ConstZero>::ZERO;
32            if N > 0 {
33                ret.array[0] = <T as One>::one();
34            }
35            ret
36        }
37        fn is_one(&self) -> bool {
38            match P::TAG {
39                PersonalityTag::Nct => const_is_one(&self.array),
40                PersonalityTag::Ct => const_is_one_ct(&self.array),
41            }
42        }
43        fn set_one(&mut self) {
44            <Self as Zero>::set_zero(self);
45            if N > 0 {
46                <T as One>::set_one(&mut self.array[0]);
47            }
48        }
49    }
50
51
52    c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Bounded for FixedUInt<T, N, P> {
53        fn min_value() -> Self {
54            <Self as ConstZero>::ZERO
55        }
56        fn max_value() -> Self {
57            FixedUInt::from_array([<T as Bounded>::max_value(); N])
58        }
59    }
60}
61
62// `ConstZero` and `ConstOne` are NOT `c0nst` traits in the external
63// crate (they only declare an associated constant). The c0nst macro
64// rejects mixing const-trait impls with non-const-trait impls in the
65// same block, so these two impls live outside the c0nst! block above.
66
67impl<T: ConstMachineWord + MachineWord, const N: usize, P: Personality> ConstZero
68    for FixedUInt<T, N, P>
69{
70    const ZERO: Self = FixedUInt::from_array([<T as ConstZero>::ZERO; N]);
71}
72
73impl<T: ConstMachineWord + MachineWord, const N: usize, P: Personality> ConstOne
74    for FixedUInt<T, N, P>
75{
76    const ONE: Self = {
77        let mut a = [<T as ConstZero>::ZERO; N];
78        if N > 0 {
79            a[0] = <T as ConstOne>::ONE;
80        }
81        FixedUInt::from_array(a)
82    };
83}
84
85#[cfg(feature = "num-traits")]
86impl<T: MachineWord, const N: usize, P: Personality> num_traits::Zero for FixedUInt<T, N, P> {
87    fn zero() -> Self {
88        <Self as ConstZero>::ZERO
89    }
90    fn is_zero(&self) -> bool {
91        <Self as Zero>::is_zero(self)
92    }
93}
94
95#[cfg(feature = "num-traits")]
96impl<T: MachineWord, const N: usize, P: Personality> num_traits::One for FixedUInt<T, N, P> {
97    fn one() -> Self {
98        <Self as ConstOne>::ONE
99    }
100}
101
102#[cfg(feature = "num-traits")]
103impl<T: MachineWord, const N: usize, P: Personality> num_traits::Bounded for FixedUInt<T, N, P> {
104    fn min_value() -> Self {
105        <Self as Bounded>::min_value()
106    }
107    fn max_value() -> Self {
108        <Self as Bounded>::max_value()
109    }
110}
111
112// ── CtIsZero (masked-return is_zero) ─────────────────────────────────
113//
114// AND-fold `ct_eq(&ZERO)` across all limbs. Uniform across both
115// personalities — the masking is purely about the return shape, the
116// `Zero::is_zero` body's `match P::TAG` already covers the
117// short-circuit-vs-OR-fold choice for the `bool`-returning version,
118// but for the `Choice` return we always do the full AND-fold to avoid
119// branching on the limb-by-limb result.
120impl<T, const N: usize, P: Personality> const_num_traits::ops::ct::CtIsZero for FixedUInt<T, N, P>
121where
122    T: MachineWord + subtle::ConstantTimeEq,
123{
124    fn ct_is_zero(&self) -> subtle::Choice {
125        let mut choice = subtle::Choice::from(1u8);
126        let mut i = 0;
127        while i < N {
128            choice &= self.array[i].ct_eq(&<T as ConstZero>::ZERO);
129            i += 1;
130        }
131        choice
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use const_num_traits::Nct;
139
140    c0nst::c0nst! {
141        c0nst fn const_zero<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>() -> FixedUInt<T, N, P> {
142            <FixedUInt<T, N, P> as ConstZero>::ZERO
143        }
144
145        c0nst fn const_one<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>() -> FixedUInt<T, N, P> {
146            <FixedUInt<T, N, P> as ConstOne>::ONE
147        }
148
149        c0nst fn const_is_zero<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: &FixedUInt<T, N, P>) -> bool {
150            <FixedUInt<T, N, P> as Zero>::is_zero(v)
151        }
152
153        c0nst fn const_is_one<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: &FixedUInt<T, N, P>) -> bool {
154            <FixedUInt<T, N, P> as One>::is_one(v)
155        }
156
157        c0nst fn const_min_value<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>() -> FixedUInt<T, N, P> {
158            <FixedUInt<T, N, P> as Bounded>::min_value()
159        }
160
161        c0nst fn const_max_value<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>() -> FixedUInt<T, N, P> {
162            <FixedUInt<T, N, P> as Bounded>::max_value()
163        }
164
165        c0nst fn const_set_zero<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: &mut FixedUInt<T, N, P>) {
166            <FixedUInt<T, N, P> as Zero>::set_zero(v)
167        }
168
169        c0nst fn const_set_one<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: &mut FixedUInt<T, N, P>) {
170            <FixedUInt<T, N, P> as One>::set_one(v)
171        }
172    }
173
174    #[test]
175    fn test_const_identity_traits() {
176        type TestInt = FixedUInt<u8, 2>;
177
178        // Test zero
179        let zero = const_zero::<u8, 2, Nct>();
180        assert!(const_is_zero(&zero));
181        assert!(!const_is_one(&zero));
182
183        // Test one
184        let one = const_one::<u8, 2, Nct>();
185        assert!(!const_is_zero(&one));
186        assert!(const_is_one(&one));
187
188        // Test min/max
189        let min = const_min_value::<u8, 2, Nct>();
190        let max = const_max_value::<u8, 2, Nct>();
191        assert!(const_is_zero(&min));
192        assert_eq!(max.array, [255, 255]);
193
194        // Test set_zero/set_one
195        let mut val = TestInt::from(42u8);
196        const_set_zero(&mut val);
197        assert!(const_is_zero(&val));
198
199        const_set_one(&mut val);
200        assert!(const_is_one(&val));
201
202        #[cfg(feature = "nightly")]
203        {
204            const ZERO: TestInt = const_zero::<u8, 2, Nct>();
205            const ONE: TestInt = const_one::<u8, 2, Nct>();
206            const IS_ZERO_TRUE: bool = const_is_zero(&ZERO);
207            const IS_ZERO_FALSE: bool = const_is_zero(&ONE);
208            const IS_ONE_TRUE: bool = const_is_one(&ONE);
209            const IS_ONE_FALSE: bool = const_is_one(&ZERO);
210            const MIN: TestInt = const_min_value::<u8, 2, Nct>();
211            const MAX: TestInt = const_max_value::<u8, 2, Nct>();
212
213            assert_eq!(ZERO.array, [0, 0]);
214            assert_eq!(ONE.array, [1, 0]);
215            assert!(IS_ZERO_TRUE);
216            assert!(!IS_ZERO_FALSE);
217            assert!(IS_ONE_TRUE);
218            assert!(!IS_ONE_FALSE);
219            assert_eq!(MIN.array, [0, 0]);
220            assert_eq!(MAX.array, [255, 255]);
221
222            const SET_ZERO_RES: TestInt = {
223                let mut v = FixedUInt::from_array([42, 0]);
224                const_set_zero(&mut v);
225                v
226            };
227            const SET_ONE_RES: TestInt = {
228                let mut v = FixedUInt::from_array([0, 0]);
229                const_set_one(&mut v);
230                v
231            };
232            assert_eq!(SET_ZERO_RES.array, [0, 0]);
233            assert_eq!(SET_ONE_RES.array, [1, 0]);
234        }
235    }
236
237    #[test]
238    fn ct_is_zero_matches_is_zero() {
239        use const_num_traits::Ct;
240        use const_num_traits::ops::ct::CtIsZero;
241        type U16Nct = FixedUInt<u8, 2>;
242        type U16Ct = FixedUInt<u8, 2, Ct>;
243        // Nct
244        assert!(bool::from(CtIsZero::ct_is_zero(&U16Nct::from(0u8))));
245        assert!(!bool::from(CtIsZero::ct_is_zero(&U16Nct::from(1u8))));
246        assert!(!bool::from(CtIsZero::ct_is_zero(&U16Nct::from(0xFFFFu16))));
247        // Non-zero in the high limb only (catches a per-limb-AND bug)
248        assert!(!bool::from(CtIsZero::ct_is_zero(
249            &FixedUInt::<u8, 2>::from_array([0, 1])
250        )));
251        // Ct
252        let z_ct: U16Ct = U16Nct::from(0u8).into();
253        let nz_ct: U16Ct = U16Nct::from(42u8).into();
254        assert!(bool::from(CtIsZero::ct_is_zero(&z_ct)));
255        assert!(!bool::from(CtIsZero::ct_is_zero(&nz_ct)));
256    }
257}