Skip to main content

fixed_bigint/heapless/
strict.rs

1//! `Strict*` arithmetic for `HeaplessBigInt` (the family minus `StrictPow`,
2//! which lives with the other pow parallels in [`pow`](super::pow)).
3//!
4//! The strict ops panic on overflow in every build — a value-dependent
5//! semantic incompatible with constant time, so they are `Nct`-only, matching
6//! `FixedUInt`. Bodies delegate to the existing `Overflowing*` paths (add/sub/
7//! mul) or the panicking `Div`/`Rem`/shift operators, turning the overflow
8//! flag into a `panic!`. Result width follows the delegate (`max(operand len)`
9//! for arithmetic, `self.len` for shifts).
10
11use super::HeaplessBigInt;
12use crate::MachineWord;
13use const_num_traits::{
14    CarryingMul, Nct, OverflowingAdd, OverflowingMul, OverflowingSub, StrictAdd, StrictDiv,
15    StrictMul, StrictRem, StrictShl, StrictShr, StrictSub,
16};
17
18impl<T, const CAP: usize> StrictAdd for HeaplessBigInt<T, CAP, Nct>
19where
20    T: MachineWord,
21{
22    type Output = Self;
23    fn strict_add(self, v: Self) -> Self {
24        let (res, overflow) = OverflowingAdd::overflowing_add(self, v);
25        assert!(!overflow, "HeaplessBigInt: strict_add overflowed");
26        res
27    }
28}
29
30impl<T, const CAP: usize> StrictSub for HeaplessBigInt<T, CAP, Nct>
31where
32    T: MachineWord,
33{
34    type Output = Self;
35    fn strict_sub(self, v: Self) -> Self {
36        let (res, overflow) = OverflowingSub::overflowing_sub(self, v);
37        assert!(!overflow, "HeaplessBigInt: strict_sub underflowed");
38        res
39    }
40}
41
42impl<T, const CAP: usize> StrictMul for HeaplessBigInt<T, CAP, Nct>
43where
44    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
45{
46    type Output = Self;
47    fn strict_mul(self, v: Self) -> Self {
48        let (res, overflow) = OverflowingMul::overflowing_mul(self, v);
49        assert!(!overflow, "HeaplessBigInt: strict_mul overflowed");
50        res
51    }
52}
53
54impl<T, const CAP: usize> StrictDiv for HeaplessBigInt<T, CAP, Nct>
55where
56    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
57{
58    type Output = Self;
59    fn strict_div(self, v: Self) -> Self {
60        // Unsigned: the only overflow mode is `v == 0`, on which `/` panics.
61        self / v
62    }
63}
64
65impl<T, const CAP: usize> StrictRem for HeaplessBigInt<T, CAP, Nct>
66where
67    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
68{
69    type Output = Self;
70    fn strict_rem(self, v: Self) -> Self {
71        self % v
72    }
73}
74
75impl<T, const CAP: usize> StrictShl for HeaplessBigInt<T, CAP, Nct>
76where
77    T: MachineWord,
78{
79    type Output = Self;
80    fn strict_shl(self, rhs: u32) -> Self {
81        let value_bits = self.len() as u32 * (core::mem::size_of::<T>() as u32 * 8);
82        assert!(
83            rhs < value_bits,
84            "HeaplessBigInt: strict_shl shift exceeds the value width"
85        );
86        self << (rhs as usize)
87    }
88}
89
90impl<T, const CAP: usize> StrictShr for HeaplessBigInt<T, CAP, Nct>
91where
92    T: MachineWord,
93{
94    type Output = Self;
95    fn strict_shr(self, rhs: u32) -> Self {
96        let value_bits = self.len() as u32 * (core::mem::size_of::<T>() as u32 * 8);
97        assert!(
98            rhs < value_bits,
99            "HeaplessBigInt: strict_shr shift exceeds the value width"
100        );
101        self >> (rhs as usize)
102    }
103}
104
105// `&Self` reference-receiver mirrors. `HeaplessBigInt` is `Copy`, so each
106// mirror derefs its receiver/operands and forwards to the value impl above.
107
108impl<T, const CAP: usize> StrictAdd for &HeaplessBigInt<T, CAP, Nct>
109where
110    T: MachineWord,
111{
112    type Output = HeaplessBigInt<T, CAP, Nct>;
113    fn strict_add(self, v: Self) -> Self::Output {
114        <HeaplessBigInt<T, CAP, Nct> as StrictAdd>::strict_add(*self, *v)
115    }
116}
117
118impl<T, const CAP: usize> StrictSub for &HeaplessBigInt<T, CAP, Nct>
119where
120    T: MachineWord,
121{
122    type Output = HeaplessBigInt<T, CAP, Nct>;
123    fn strict_sub(self, v: Self) -> Self::Output {
124        <HeaplessBigInt<T, CAP, Nct> as StrictSub>::strict_sub(*self, *v)
125    }
126}
127
128impl<T, const CAP: usize> StrictMul for &HeaplessBigInt<T, CAP, Nct>
129where
130    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
131{
132    type Output = HeaplessBigInt<T, CAP, Nct>;
133    fn strict_mul(self, v: Self) -> Self::Output {
134        <HeaplessBigInt<T, CAP, Nct> as StrictMul>::strict_mul(*self, *v)
135    }
136}
137
138impl<T, const CAP: usize> StrictDiv for &HeaplessBigInt<T, CAP, Nct>
139where
140    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
141{
142    type Output = HeaplessBigInt<T, CAP, Nct>;
143    fn strict_div(self, v: Self) -> Self::Output {
144        <HeaplessBigInt<T, CAP, Nct> as StrictDiv>::strict_div(*self, *v)
145    }
146}
147
148impl<T, const CAP: usize> StrictRem for &HeaplessBigInt<T, CAP, Nct>
149where
150    T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
151{
152    type Output = HeaplessBigInt<T, CAP, Nct>;
153    fn strict_rem(self, v: Self) -> Self::Output {
154        <HeaplessBigInt<T, CAP, Nct> as StrictRem>::strict_rem(*self, *v)
155    }
156}
157
158impl<T, const CAP: usize> StrictShl for &HeaplessBigInt<T, CAP, Nct>
159where
160    T: MachineWord,
161{
162    type Output = HeaplessBigInt<T, CAP, Nct>;
163    fn strict_shl(self, rhs: u32) -> Self::Output {
164        <HeaplessBigInt<T, CAP, Nct> as StrictShl>::strict_shl(*self, rhs)
165    }
166}
167
168impl<T, const CAP: usize> StrictShr for &HeaplessBigInt<T, CAP, Nct>
169where
170    T: MachineWord,
171{
172    type Output = HeaplessBigInt<T, CAP, Nct>;
173    fn strict_shr(self, rhs: u32) -> Self::Output {
174        <HeaplessBigInt<T, CAP, Nct> as StrictShr>::strict_shr(*self, rhs)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::HeaplessBigInt;
181    use const_num_traits::{StrictAdd, StrictMul, StrictShl, StrictSub};
182
183    type H = HeaplessBigInt<u8, 4>; // 32-bit width at len 4
184
185    #[test]
186    fn strict_ok_paths() {
187        let a = H::from(10u8).widened(4);
188        assert_eq!(StrictAdd::strict_add(a, H::from(20u8)), H::from(30u8));
189        assert_eq!(StrictSub::strict_sub(a, H::from(3u8)), H::from(7u8));
190        assert_eq!(StrictMul::strict_mul(a, H::from(3u8)), H::from(30u8));
191        assert_eq!(
192            StrictShl::strict_shl(H::from(1u8).widened(4), 8),
193            H::from(256u16)
194        );
195    }
196
197    #[test]
198    #[should_panic(expected = "strict_add overflowed")]
199    fn strict_add_overflow_panics() {
200        StrictAdd::strict_add(H::from(u32::MAX), H::from(1u8));
201    }
202
203    #[test]
204    #[should_panic(expected = "strict_shl shift exceeds")]
205    fn strict_shl_over_width_panics() {
206        StrictShl::strict_shl(H::from(1u8).widened(4), 32);
207    }
208
209    #[test]
210    fn byref_matches_value() {
211        let a = H::from(10u8).widened(4);
212        let b = H::from(20u8);
213        assert_eq!(StrictAdd::strict_add(&a, &b), StrictAdd::strict_add(a, b));
214    }
215}