1use core::fmt;
4
5use num_traits::ConstZero;
6#[cfg(feature = "serde")]
7use serdect::serde::{Deserialize, Deserializer, Serialize, Serializer};
8use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
9
10#[cfg(feature = "serde")]
11use crate::Encoding;
12use crate::{Bounded, ConstChoice, ConstCtOption, Constants, Limb, NonZero, Odd, Uint, Word};
13
14mod add;
15mod bit_and;
16mod bit_not;
17mod bit_or;
18mod bit_xor;
19mod cmp;
20mod div;
21mod div_uint;
22mod encoding;
23mod from;
24mod gcd;
25mod inv_mod;
26mod mul;
27mod mul_uint;
28mod neg;
29mod resize;
30mod shl;
31mod shr;
32mod sign;
33mod sub;
34pub(crate) mod types;
35
36#[cfg(feature = "rand_core")]
37mod rand;
38
39#[allow(clippy::derived_hash_with_manual_eq)]
44#[derive(Copy, Clone, Hash)]
45pub struct Int<const LIMBS: usize>(Uint<LIMBS>);
46
47impl<const LIMBS: usize> Int<LIMBS> {
48 pub const ZERO: Self = Self(Uint::ZERO); pub const ONE: Self = Self(Uint::ONE); pub const MINUS_ONE: Self = Self::FULL_MASK; pub const MIN: Self = Self(Uint::MAX.bitxor(&Uint::MAX.shr(1u32))); pub const MAX: Self = Self(Uint::MAX.shr(1u32)); pub const SIGN_MASK: Self = Self::MIN; pub const FULL_MASK: Self = Self(Uint::MAX); pub const BITS: u32 = Uint::<LIMBS>::BITS;
71
72 pub const BYTES: usize = Uint::<LIMBS>::BYTES;
74
75 pub const LIMBS: usize = LIMBS;
77
78 pub const fn new(limbs: [Limb; LIMBS]) -> Self {
80 Self(Uint::new(limbs))
81 }
82
83 pub(crate) const fn from_bits(value: Uint<LIMBS>) -> Self {
89 Self(value)
90 }
91
92 #[inline]
95 pub const fn from_words(arr: [Word; LIMBS]) -> Self {
96 Self(Uint::from_words(arr))
97 }
98
99 #[inline]
102 pub const fn to_words(self) -> [Word; LIMBS] {
103 self.0.to_words()
104 }
105
106 pub const fn as_words(&self) -> &[Word; LIMBS] {
108 self.0.as_words()
109 }
110
111 pub fn as_words_mut(&mut self) -> &mut [Word; LIMBS] {
113 self.0.as_words_mut()
114 }
115
116 pub const fn as_limbs(&self) -> &[Limb; LIMBS] {
118 self.0.as_limbs()
119 }
120
121 pub const fn as_limbs_mut(&mut self) -> &mut [Limb; LIMBS] {
123 self.0.as_limbs_mut()
124 }
125
126 pub const fn to_limbs(self) -> [Limb; LIMBS] {
128 self.0.to_limbs()
129 }
130
131 pub const fn to_nz(self) -> ConstCtOption<NonZero<Self>> {
135 ConstCtOption::new(NonZero(self), self.0.is_nonzero())
136 }
137
138 pub const fn to_odd(self) -> ConstCtOption<Odd<Self>> {
142 ConstCtOption::new(Odd(self), self.0.is_odd())
143 }
144
145 pub const fn as_uint(&self) -> &Uint<LIMBS> {
147 &self.0
148 }
149
150 pub const fn is_min(&self) -> ConstChoice {
152 Self::eq(self, &Self::MIN)
153 }
154
155 pub fn is_max(&self) -> ConstChoice {
157 Self::eq(self, &Self::MAX)
158 }
159
160 const fn invert_msb(&self) -> Self {
162 Self(self.0.bitxor(&Self::SIGN_MASK.0))
163 }
164}
165
166impl<const LIMBS: usize> AsRef<[Word; LIMBS]> for Int<LIMBS> {
167 fn as_ref(&self) -> &[Word; LIMBS] {
168 self.as_words()
169 }
170}
171
172impl<const LIMBS: usize> AsMut<[Word; LIMBS]> for Int<LIMBS> {
173 fn as_mut(&mut self) -> &mut [Word; LIMBS] {
174 self.as_words_mut()
175 }
176}
177
178impl<const LIMBS: usize> AsRef<[Limb]> for Int<LIMBS> {
179 fn as_ref(&self) -> &[Limb] {
180 self.as_limbs()
181 }
182}
183
184impl<const LIMBS: usize> AsMut<[Limb]> for Int<LIMBS> {
185 fn as_mut(&mut self) -> &mut [Limb] {
186 self.as_limbs_mut()
187 }
188}
189
190impl<const LIMBS: usize> ConditionallySelectable for Int<LIMBS> {
191 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
192 Self(Uint::conditional_select(&a.0, &b.0, choice))
193 }
194}
195
196impl<const LIMBS: usize> Bounded for Int<LIMBS> {
197 const BITS: u32 = Self::BITS;
198 const BYTES: usize = Self::BYTES;
199}
200
201impl<const LIMBS: usize> Constants for Int<LIMBS> {
202 const ONE: Self = Self::ONE;
203 const MAX: Self = Self::MAX;
204}
205
206impl<const LIMBS: usize> Default for Int<LIMBS> {
207 fn default() -> Self {
208 Self::ZERO
209 }
210}
211
212impl<const LIMBS: usize> ConstZero for Int<LIMBS> {
217 const ZERO: Self = Self::ZERO;
218}
219
220impl<const LIMBS: usize> num_traits::Zero for Int<LIMBS> {
221 fn zero() -> Self {
222 Self::ZERO
223 }
224
225 fn is_zero(&self) -> bool {
226 self.0.ct_eq(&Self::ZERO.0).into()
227 }
228}
229
230impl<const LIMBS: usize> num_traits::One for Int<LIMBS> {
231 fn one() -> Self {
232 Self::ONE
233 }
234
235 fn is_one(&self) -> bool {
236 self.0.ct_eq(&Self::ONE.0).into()
237 }
238}
239
240impl<const LIMBS: usize> fmt::Debug for Int<LIMBS> {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "Int(0x{self:X})")
243 }
244}
245
246impl<const LIMBS: usize> fmt::Binary for Int<LIMBS> {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 fmt::Binary::fmt(&self.0, f)
249 }
250}
251
252impl<const LIMBS: usize> fmt::Display for Int<LIMBS> {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 fmt::UpperHex::fmt(self, f)
255 }
256}
257
258impl<const LIMBS: usize> fmt::LowerHex for Int<LIMBS> {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt::LowerHex::fmt(&self.0, f)
261 }
262}
263
264impl<const LIMBS: usize> fmt::UpperHex for Int<LIMBS> {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 fmt::UpperHex::fmt(&self.0, f)
267 }
268}
269
270#[cfg(feature = "serde")]
271impl<'de, const LIMBS: usize> Deserialize<'de> for Int<LIMBS>
272where
273 Int<LIMBS>: Encoding,
274{
275 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
276 where
277 D: Deserializer<'de>,
278 {
279 let mut buffer = Self::ZERO.to_le_bytes();
280 serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?;
281 Ok(Self::from_le_bytes(buffer))
282 }
283}
284
285#[cfg(feature = "serde")]
286impl<const LIMBS: usize> Serialize for Int<LIMBS>
287where
288 Int<LIMBS>: Encoding,
289{
290 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
291 where
292 S: Serializer,
293 {
294 serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
295 }
296}
297
298#[cfg(test)]
299#[allow(clippy::unwrap_used)]
300mod tests {
301 use subtle::ConditionallySelectable;
302
303 use crate::{ConstChoice, I128, U128};
304
305 #[cfg(target_pointer_width = "64")]
306 #[test]
307 fn as_words() {
308 let n = I128::from_be_hex("AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD");
309 assert_eq!(n.as_words(), &[0xCCCCCCCCDDDDDDDD, 0xAAAAAAAABBBBBBBB]);
310 }
311
312 #[cfg(target_pointer_width = "64")]
313 #[test]
314 fn as_words_mut() {
315 let mut n = I128::from_be_hex("AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD");
316 assert_eq!(n.as_words_mut(), &[0xCCCCCCCCDDDDDDDD, 0xAAAAAAAABBBBBBBB]);
317 }
318
319 #[cfg(feature = "alloc")]
320 #[test]
321 fn debug() {
322 let n = I128::from_be_hex("AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD");
323
324 assert_eq!(
325 format!("{:?}", n),
326 "Int(0xAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD)"
327 );
328 }
329
330 #[cfg(feature = "alloc")]
331 #[test]
332 fn display() {
333 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
334 let n = I128::from_be_hex(hex);
335
336 use alloc::string::ToString;
337 assert_eq!(hex, n.to_string());
338
339 let hex = "AAAAAAAABBBBBBBB0000000000000000";
340 let n = I128::from_be_hex(hex);
341 assert_eq!(hex, n.to_string());
342
343 let hex = "AAAAAAAABBBBBBBB00000000DDDDDDDD";
344 let n = I128::from_be_hex(hex);
345 assert_eq!(hex, n.to_string());
346
347 let hex = "AAAAAAAABBBBBBBB0CCCCCCCDDDDDDDD";
348 let n = I128::from_be_hex(hex);
349 assert_eq!(hex, n.to_string());
350 }
351
352 #[test]
353 fn conditional_select() {
354 let a = I128::from_be_hex("00002222444466668888AAAACCCCEEEE");
355 let b = I128::from_be_hex("11113333555577779999BBBBDDDDFFFF");
356
357 let select_0 = I128::conditional_select(&a, &b, 0.into());
358 assert_eq!(a, select_0);
359
360 let select_1 = I128::conditional_select(&a, &b, 1.into());
361 assert_eq!(b, select_1);
362 }
363
364 #[test]
365 fn is_minimal() {
366 let min = I128::from_be_hex("80000000000000000000000000000000");
367 assert_eq!(min.is_min(), ConstChoice::TRUE);
368
369 let random = I128::from_be_hex("11113333555577779999BBBBDDDDFFFF");
370 assert_eq!(random.is_min(), ConstChoice::FALSE);
371 }
372
373 #[test]
374 fn is_maximal() {
375 let max = I128::from_be_hex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
376 assert_eq!(max.is_max(), ConstChoice::TRUE);
377
378 let random = I128::from_be_hex("11113333555577779999BBBBDDDDFFFF");
379 assert_eq!(random.is_max(), ConstChoice::FALSE);
380 }
381
382 #[test]
383 fn as_uint() {
384 assert_eq!(*I128::MIN.as_uint(), U128::ONE << 127);
385 assert_eq!(*I128::MINUS_ONE.as_uint(), U128::MAX);
386 assert_eq!(*I128::ZERO.as_uint(), U128::ZERO);
387 assert_eq!(*I128::ONE.as_uint(), U128::ONE);
388 assert_eq!(*I128::MAX.as_uint(), U128::MAX >> 1);
389 }
390}