1#![allow(clippy::let_unit_value)]
14
15use super::HeaplessBigInt;
16use crate::MachineWord;
17use const_num_traits::{CarryingMul, Ct, DivNonZero, HasNonZero, Nct, One, Personality, Zero};
18
19#[repr(transparent)]
25#[derive(Clone, Copy, PartialEq, Eq)]
26pub struct NonZeroHeaplessBigInt<T, const CAP: usize, P: Personality>(HeaplessBigInt<T, CAP, P>)
27where
28 T: MachineWord;
29
30impl<T: MachineWord + core::fmt::Debug, const CAP: usize> core::fmt::Debug
33 for NonZeroHeaplessBigInt<T, CAP, Nct>
34{
35 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36 write!(f, "NonZero({:?})", self.0)
37 }
38}
39
40impl<T: MachineWord, const CAP: usize> core::fmt::Debug for NonZeroHeaplessBigInt<T, CAP, Ct> {
41 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42 write!(f, "NonZero({:?})", self.0)
43 }
44}
45
46impl<T, const CAP: usize, P: Personality> NonZeroHeaplessBigInt<T, CAP, P>
47where
48 T: MachineWord,
49{
50 #[inline]
52 pub fn get(self) -> HeaplessBigInt<T, CAP, P> {
53 self.0
54 }
55}
56
57trait AssertNonzeroCarrier {
60 const CHECK: ();
61}
62impl<T: MachineWord, const CAP: usize, P: Personality> AssertNonzeroCarrier
63 for NonZeroHeaplessBigInt<T, CAP, P>
64{
65 const CHECK: () = assert!(
66 CAP > 0,
67 "NonZeroHeaplessBigInt::default() requires CAP > 0 (a CAP=0 carrier can only hold zero)"
68 );
69}
70
71impl<T, const CAP: usize, P: Personality> Default for NonZeroHeaplessBigInt<T, CAP, P>
80where
81 T: MachineWord,
82{
83 fn default() -> Self {
84 let _ = <Self as AssertNonzeroCarrier>::CHECK;
85 NonZeroHeaplessBigInt(<HeaplessBigInt<T, CAP, P> as One>::one().widened(CAP as u16))
86 }
87}
88
89impl<T, const CAP: usize> subtle::ConditionallySelectable for NonZeroHeaplessBigInt<T, CAP, Ct>
93where
94 T: MachineWord + subtle::ConditionallySelectable,
95{
96 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
97 Self(
98 <HeaplessBigInt<T, CAP, Ct> as subtle::ConditionallySelectable>::conditional_select(
99 &a.0, &b.0, choice,
100 ),
101 )
102 }
103}
104
105impl<T, const CAP: usize, P: Personality> HasNonZero for HeaplessBigInt<T, CAP, P>
106where
107 T: MachineWord,
108{
109 type NonZero = NonZeroHeaplessBigInt<T, CAP, P>;
110
111 #[inline]
115 fn into_nonzero(self) -> Option<Self::NonZero> {
116 if <Self as Zero>::is_zero(&self) {
117 None
118 } else {
119 Some(NonZeroHeaplessBigInt(self))
120 }
121 }
122
123 #[inline]
124 fn nonzero_get(nz: Self::NonZero) -> Self {
125 nz.0
126 }
127}
128
129impl<T, const CAP: usize> DivNonZero for HeaplessBigInt<T, CAP, Nct>
142where
143 T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
144{
145 type Output = Self;
146
147 #[inline]
148 fn div_nonzero(self, d: Self::NonZero) -> Self::Output {
149 self / d.0
150 }
151
152 #[inline]
153 fn rem_nonzero(self, d: Self::NonZero) -> Self::Output {
154 self % d.0
155 }
156}
157
158impl<T, const CAP: usize, P: Personality> const_num_traits::CtNonZero for HeaplessBigInt<T, CAP, P>
161where
162 T: MachineWord + subtle::ConstantTimeEq,
163{
164 fn into_nonzero_ct(self) -> subtle::CtOption<Self::NonZero> {
165 use const_num_traits::ops::ct::CtIsZero;
166 let zero = self.ct_is_zero();
167 subtle::CtOption::new(NonZeroHeaplessBigInt(self), !zero)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use const_num_traits::CtNonZero;
175
176 type H = HeaplessBigInt<u8, 4, Nct>;
177 type HCt = HeaplessBigInt<u8, 4, Ct>;
178
179 #[test]
180 fn into_nonzero_some_none() {
181 assert!(H::from(5u32).into_nonzero().is_some());
182 assert!(H::from(0u32).into_nonzero().is_none());
183 }
184
185 #[test]
186 fn default_is_one_at_full_capacity() {
187 let d = <NonZeroHeaplessBigInt<u8, 4, Nct> as Default>::default();
189 assert_eq!(d.get(), H::from(1u8));
190 assert_eq!(d.get().len(), 4);
191 }
192
193 #[test]
194 fn nonzero_round_trip() {
195 let v = H::from(42u32);
196 let nz = v.into_nonzero().unwrap();
197 assert_eq!(<H as HasNonZero>::nonzero_get(nz), v);
198 assert_eq!(nz.get(), v);
199 }
200
201 #[test]
202 fn div_rem_nonzero_match_operators() {
203 let a = H::from(100u32);
204 let m = H::from(7u32);
205 let nz = m.into_nonzero().unwrap();
206 assert_eq!(<H as DivNonZero>::div_nonzero(a, nz), a / m);
207 assert_eq!(<H as DivNonZero>::rem_nonzero(a, nz), a % m);
208 }
209
210 static_assertions::assert_not_impl_any!(HeaplessBigInt<u8, 4, Ct>: DivNonZero);
213
214 #[test]
215 fn into_nonzero_ct_masks_zero() {
216 let nz = H::from(5u32).into_nonzero_ct();
217 assert!(bool::from(nz.is_some()));
218 assert_eq!(nz.unwrap().get(), H::from(5u32));
219 assert!(!bool::from(H::from(0u32).into_nonzero_ct().is_some()));
220
221 let nz_ct = HCt::from(42u32);
223 assert!(bool::from(nz_ct.into_nonzero_ct().is_some()));
224 let z_ct = HCt::from(0u32);
225 assert!(!bool::from(z_ct.into_nonzero_ct().is_some()));
226 }
227}