1use num_traits::{ToPrimitive, Zero};
16
17use core::convert::TryFrom;
18use core::fmt::Write;
19
20pub use crate::const_numtraits::{
21 ConstAbsDiff, ConstBitPrimInt, ConstBorrowingSub, ConstBounded, ConstCarryingAdd,
22 ConstCarryingMul, ConstCheckedPow, ConstDivCeil, ConstIlog, ConstIsqrt, ConstMultiple,
23 ConstOne, ConstPowerOfTwo, ConstPrimInt, ConstWideningMul, ConstZero,
24};
25use crate::machineword::{ConstMachineWord, MachineWord};
26
27#[allow(unused_imports)]
28use num_traits::{FromPrimitive, Num};
29
30mod abs_diff_impl;
31mod add_sub_impl;
32mod bit_ops_impl;
33mod checked_pow_impl;
34mod div_ceil_impl;
35mod euclid;
36mod extended_precision_impl;
37mod ilog_impl;
38mod isqrt_impl;
39mod iter_impl;
40mod midpoint_impl;
41mod mul_acc_ops_impl;
42mod mul_div_impl;
43mod multiple_impl;
44mod num_integer_impl;
45mod num_traits_casts;
46mod num_traits_identity;
47mod power_of_two_impl;
48mod prim_int_impl;
49mod roots_impl;
50mod string_conversion;
51#[cfg(feature = "nightly")]
53mod const_to_from_bytes;
54#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
56mod to_from_bytes;
57
58use crate::personality::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
59#[cfg(feature = "zeroize")]
60use zeroize::DefaultIsZeroes;
61
62#[derive(Copy)]
72pub struct FixedUInt<T, const N: usize, P: Personality = Nct>
73where
74 T: MachineWord,
75{
76 pub(super) array: [T; N],
78 pub(super) _p: PersonalityMarker<P>,
80}
81
82impl<T: MachineWord + core::fmt::Debug, const N: usize> core::fmt::Debug for FixedUInt<T, N, Nct> {
87 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 f.debug_struct("FixedUInt")
89 .field("array", &self.array)
90 .finish()
91 }
92}
93
94impl<T: MachineWord, const N: usize> core::fmt::Debug for FixedUInt<T, N, Ct> {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 f.write_str("FixedUInt<…>")
97 }
98}
99
100#[cfg(feature = "zeroize")]
101impl<T: MachineWord, const N: usize, P: Personality> DefaultIsZeroes for FixedUInt<T, N, P> {}
102
103impl<T, const N: usize, P: Personality> From<[T; N]> for FixedUInt<T, N, P>
104where
105 T: MachineWord,
106{
107 fn from(array: [T; N]) -> Self {
108 Self {
109 array,
110 _p: core::marker::PhantomData,
111 }
112 }
113}
114
115impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
118 pub(crate) const fn from_array(array: [T; N]) -> Self {
119 Self {
120 array,
121 _p: core::marker::PhantomData,
122 }
123 }
124}
125
126impl<T: MachineWord, const N: usize> From<FixedUInt<T, N, Nct>> for FixedUInt<T, N, Ct> {
134 fn from(v: FixedUInt<T, N, Nct>) -> Self {
135 FixedUInt::from_array(v.array)
136 }
137}
138
139impl<T: MachineWord, const N: usize> FixedUInt<T, N, Ct> {
140 pub const fn forget_ct(self) -> FixedUInt<T, N, Nct> {
147 FixedUInt::from_array(self.array)
148 }
149}
150
151impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize> FixedUInt<T, N, Ct> {
152 pub fn ct_checked_add(&self, other: &Self) -> subtle::CtOption<Self> {
158 let (res, overflow) =
159 <Self as crate::const_numtraits::ConstOverflowingAdd>::overflowing_add(self, other);
160 let valid = subtle::Choice::from((!overflow) as u8);
161 subtle::CtOption::new(res, valid)
162 }
163
164 pub fn ct_checked_sub(&self, other: &Self) -> subtle::CtOption<Self> {
166 let (res, overflow) =
167 <Self as crate::const_numtraits::ConstOverflowingSub>::overflowing_sub(self, other);
168 let valid = subtle::Choice::from((!overflow) as u8);
169 subtle::CtOption::new(res, valid)
170 }
171
172 pub fn ct_checked_mul(&self, other: &Self) -> subtle::CtOption<Self> {
174 let (res, overflow) =
175 <Self as crate::const_numtraits::ConstOverflowingMul>::overflowing_mul(self, other);
176 let valid = subtle::Choice::from((!overflow) as u8);
177 subtle::CtOption::new(res, valid)
178 }
179
180 pub fn ct_checked_shl(&self, bits: u32) -> subtle::CtOption<Self> {
182 let (res, overflow) =
183 <Self as crate::const_numtraits::ConstOverflowingShl>::overflowing_shl(self, bits);
184 let valid = subtle::Choice::from((!overflow) as u8);
185 subtle::CtOption::new(res, valid)
186 }
187
188 pub fn ct_checked_shr(&self, bits: u32) -> subtle::CtOption<Self> {
190 let (res, overflow) =
191 <Self as crate::const_numtraits::ConstOverflowingShr>::overflowing_shr(self, bits);
192 let valid = subtle::Choice::from((!overflow) as u8);
193 subtle::CtOption::new(res, valid)
194 }
195
196 pub fn ct_checked_next_power_of_two(self) -> subtle::CtOption<Self>
198 where
199 T: subtle::ConstantTimeEq,
200 {
201 let one = <Self as num_traits::One>::one();
202 let m_one = <Self as crate::const_numtraits::ConstWrappingSub>::wrapping_sub(&self, &one);
203 let leading = <Self as crate::const_numtraits::ConstBitPrimInt>::leading_zeros(m_one);
204 let bits = Self::BIT_SIZE as u32 - leading;
205 let shifted = one << (bits as usize);
206 let is_zero_choice =
207 <Self as subtle::ConstantTimeEq>::ct_eq(&self, &<Self as num_traits::Zero>::zero());
208 let result = <Self as subtle::ConditionallySelectable>::conditional_select(
210 &shifted,
211 &one,
212 is_zero_choice,
213 );
214 let overflow = (bits >= Self::BIT_SIZE as u32) as u8;
217 let valid_otherwise = subtle::Choice::from(1u8 ^ overflow);
218 let valid = <subtle::Choice as subtle::ConditionallySelectable>::conditional_select(
219 &valid_otherwise,
220 &subtle::Choice::from(1u8),
221 is_zero_choice,
222 );
223 subtle::CtOption::new(result, valid)
224 }
225
226 pub fn ct_checked_pow(self, exp: u32) -> subtle::CtOption<Self>
227 where
228 T: subtle::ConstantTimeEq + subtle::ConstantTimeGreater,
229 for<'a> &'a Self: core::ops::Mul<&'a Self, Output = Self>,
230 {
231 use num_traits::ops::overflowing::OverflowingMul;
232 let mut result = <Self as num_traits::One>::one();
233 let mut base = self;
234 let mut e = exp;
235 let mut any_overflow: u8 = 0;
236 for _ in 0..u32::BITS {
237 let bit = core::hint::black_box((e & 1) as u8);
241 let (candidate, mul_ov) = OverflowingMul::overflowing_mul(&result, &base);
242 any_overflow |= (mul_ov as u8) & bit;
244 let bit_t = <T as core::convert::From<u8>>::from(bit);
246 let mask = core::hint::black_box(
247 bit_t * <T as crate::const_numtraits::ConstBounded>::max_value(),
248 );
249 for i in 0..N {
250 let diff = result.array[i] ^ candidate.array[i];
251 result.array[i] ^= mask & diff;
252 }
253 e >>= 1;
254 let (new_base, base_ov) = OverflowingMul::overflowing_mul(&base, &base);
255 let any_remaining: u8 = core::hint::black_box((e != 0) as u8);
257 any_overflow |= (base_ov as u8) & any_remaining;
258 base = new_base;
259 }
260 let valid = subtle::Choice::from(1u8 ^ any_overflow);
261 subtle::CtOption::new(result, valid)
262 }
263}
264
265impl<T: MachineWord + subtle::ConstantTimeEq, const N: usize> subtle::ConstantTimeEq
270 for FixedUInt<T, N, Ct>
271{
272 fn ct_eq(&self, other: &Self) -> subtle::Choice {
273 <[T] as subtle::ConstantTimeEq>::ct_eq(self.array.as_slice(), other.array.as_slice())
274 }
275}
276
277impl<T: MachineWord + subtle::ConditionallySelectable, const N: usize>
278 subtle::ConditionallySelectable for FixedUInt<T, N, Ct>
279{
280 fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
281 let mut array = a.array;
282 let mut i = 0;
283 while i < N {
284 array[i] = T::conditional_select(&a.array[i], &b.array[i], choice);
285 i += 1;
286 }
287 FixedUInt::from_array(array)
288 }
289}
290
291impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
298 subtle::ConstantTimeGreater for FixedUInt<T, N, Ct>
299{
300 fn ct_gt(&self, other: &Self) -> subtle::Choice {
301 let mut gt = subtle::Choice::from(0u8);
302 let mut undecided = subtle::Choice::from(1u8);
303 let mut i = N;
304 while i > 0 {
305 i -= 1;
306 let gt_here = self.array[i].ct_gt(&other.array[i]);
307 let eq_here = self.array[i].ct_eq(&other.array[i]);
308 gt |= undecided & gt_here;
309 undecided &= eq_here;
310 }
311 gt
312 }
313}
314
315impl<T: MachineWord + subtle::ConstantTimeEq + subtle::ConstantTimeGreater, const N: usize>
316 subtle::ConstantTimeLess for FixedUInt<T, N, Ct>
317{
318}
319
320const LONGEST_WORD_IN_BITS: usize = 128;
321
322impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
323 const WORD_SIZE: usize = core::mem::size_of::<T>();
324 const WORD_BITS: usize = Self::WORD_SIZE * 8;
325 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
326 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
327
328 pub fn new() -> FixedUInt<T, N, P> {
330 FixedUInt::from_array([T::zero(); N])
331 }
332
333 pub fn words(&self) -> &[T; N] {
335 &self.array
336 }
337
338 pub fn bit_length(&self) -> u32 {
340 Self::BIT_SIZE as u32 - ConstBitPrimInt::leading_zeros(*self)
341 }
342}
343
344impl<T: MachineWord, const N: usize> FixedUInt<T, N, Nct> {
345 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
347 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
348 (Self::from_array(quotient), Self::from_array(remainder))
349 }
350
351 pub fn to_radix_str<'a>(
353 &self,
354 result: &'a mut [u8],
355 radix: u8,
356 ) -> Result<&'a str, core::fmt::Error> {
357 type Error = core::fmt::Error;
358
359 if !(2..=16).contains(&radix) {
360 return Err(Error {}); }
362 for byte in result.iter_mut() {
363 *byte = b'0';
364 }
365 if Zero::is_zero(self) {
366 if !result.is_empty() {
367 result[0] = b'0';
368 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
369 } else {
370 return Err(Error {});
371 }
372 }
373
374 let mut number = *self;
375 let mut idx = result.len();
376
377 let radix_t = Self::from(radix);
378
379 while !Zero::is_zero(&number) {
380 if idx == 0 {
381 return Err(Error {}); }
383
384 idx -= 1;
385 let (quotient, remainder) = number.div_rem(&radix_t);
386
387 let digit = remainder.to_u8().unwrap();
388 result[idx] = match digit {
389 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
392 };
393
394 number = quotient;
395 }
396
397 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
398 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
399 Ok(radix_str)
400 }
401}
402
403c0nst::c0nst! {
405 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
408 bytes: &[u8],
409 ) -> [T; N] {
410 let word_size = core::mem::size_of::<T>();
411 let mut ret: [T; N] = [T::zero(); N];
412 let capacity = N * word_size;
413 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
414
415 let mut byte_index = 0;
416 while byte_index < total_bytes {
417 let word_index = byte_index / word_size;
418 let byte_in_word = byte_index % word_size;
419
420 let byte_value: T = T::from(bytes[byte_index]);
421 let shifted_value = byte_value.shl(byte_in_word * 8);
422 ret[word_index] = ret[word_index].bitor(shifted_value);
423 byte_index += 1;
424 }
425 ret
426 }
427
428 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
431 bytes: &[u8],
432 ) -> [T; N] {
433 let word_size = core::mem::size_of::<T>();
434 let mut ret: [T; N] = [T::zero(); N];
435 let capacity_bytes = N * word_size;
436 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
437
438 let start_offset = if bytes.len() > capacity_bytes {
441 bytes.len() - capacity_bytes
442 } else {
443 0
444 };
445
446 let mut byte_index = 0;
447 while byte_index < total_bytes {
448 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
450 let word_index = byte_index / word_size;
451 let byte_in_word = byte_index % word_size;
452
453 let byte_value: T = T::from(bytes[be_byte_index]);
454 let shifted_value = byte_value.shl(byte_in_word * 8);
455 ret[word_index] = ret[word_index].bitor(shifted_value);
456 byte_index += 1;
457 }
458 ret
459 }
460}
461
462impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
464 pub fn from_le_bytes(bytes: &[u8]) -> Self {
466 Self::from_array(impl_from_le_bytes_slice::<T, N>(bytes))
467 }
468
469 pub fn from_be_bytes(bytes: &[u8]) -> Self {
471 Self::from_array(impl_from_be_bytes_slice::<T, N>(bytes))
472 }
473}
474
475impl<T: MachineWord, const N: usize, P: Personality> FixedUInt<T, N, P> {
476 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
478 let total_bytes = N * Self::WORD_SIZE;
479 if output_buffer.len() < total_bytes {
480 return Err(false); }
482 for (i, word) in self.array.iter().enumerate() {
483 let start = i * Self::WORD_SIZE;
484 let end = start + Self::WORD_SIZE;
485 let word_bytes = word.to_le_bytes();
486 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
487 }
488 Ok(&output_buffer[..total_bytes])
489 }
490
491 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
493 let total_bytes = N * Self::WORD_SIZE;
494 if output_buffer.len() < total_bytes {
495 return Err(false); }
497 for (i, word) in self.array.iter().rev().enumerate() {
498 let start = i * Self::WORD_SIZE;
499 let end = start + Self::WORD_SIZE;
500 let word_bytes = word.to_be_bytes();
501 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
502 }
503 Ok(&output_buffer[..total_bytes])
504 }
505
506 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
508 type Error = core::fmt::Error;
509
510 let word_size = Self::WORD_SIZE;
511 let need_bits = self.bit_length() as usize;
513 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
515
516 if result.len() < need_chars {
517 return Err(Error {});
519 }
520 let offset = result.len() - need_chars;
521 for i in result.iter_mut() {
522 *i = b'0';
523 }
524
525 for iter_words in 0..self.array.len() {
526 let word = self.array[iter_words];
527 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
528 let encode_slice = &mut encoded[0..word_size * 2];
529 let mut wordbytes = word.to_le_bytes();
530 wordbytes.as_mut().reverse();
531 let wordslice = wordbytes.as_ref();
532 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
533 for iter_chars in 0..encode_slice.len() {
534 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
535 if copy_char_to <= need_chars {
536 let reverse_index = offset + (need_chars - copy_char_to);
537 if reverse_index <= result.len() && reverse_index > 0 {
538 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
539 result[reverse_index - 1] = current_char;
540 }
541 }
542 }
543 }
544
545 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
546 let pos = convert.find(|c: char| c != '0');
547 match pos {
548 Some(x) => Ok(&convert[x..convert.len()]),
549 None => {
550 if convert.starts_with('0') {
551 Ok("0")
552 } else {
553 Ok(convert)
554 }
555 }
556 }
557 }
558
559 #[must_use]
564 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2, P> {
565 let mut array = [T::zero(); N2];
566 let min_size = N.min(N2);
567 array[..min_size].copy_from_slice(&self.array[..min_size]);
568 FixedUInt::<T, N2, P>::from_array(array)
569 }
570
571 fn hex_fmt(
572 &self,
573 formatter: &mut core::fmt::Formatter<'_>,
574 uppercase: bool,
575 ) -> Result<(), core::fmt::Error>
576 where
577 u8: core::convert::TryFrom<T>,
578 {
579 type Err = core::fmt::Error;
580
581 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
582 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
583 if uppercase {
584 digit.to_uppercase().next().ok_or(Err {})
585 } else {
586 digit.to_lowercase().next().ok_or(Err {})
587 }
588 }
589
590 let mut leading_zero: bool = true;
591
592 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
593 leading_zero &= nibble == '0';
594 if !leading_zero {
595 formatter.write_char(nibble)?;
596 }
597 Ok(())
598 };
599
600 for index in (0..N).rev() {
601 let val = self.array[index];
602 let mask: T = 0xff.into();
603 for j in (0..Self::WORD_SIZE as u32).rev() {
604 let masked = val & mask.shl((j * 8) as usize);
605
606 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
607
608 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
609 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
610 }
611 }
612 Ok(())
613 }
614}
615
616c0nst::c0nst! {
617 pub(crate) c0nst fn add_with_carry<T: [c0nst] ConstMachineWord, const N: usize>(
622 a: &[T; N],
623 b: &[T; N],
624 carry_in: bool,
625 ) -> ([T; N], bool) {
626 let mut result = [T::zero(); N];
627 let mut carry = carry_in;
628 let mut i = 0usize;
629 while i < N {
630 let (sum, c) = ConstCarryingAdd::carrying_add(a[i], b[i], carry);
631 result[i] = sum;
632 carry = c;
633 i += 1;
634 }
635 (result, carry)
636 }
637
638 pub(crate) c0nst fn sub_with_borrow<T: [c0nst] ConstMachineWord, const N: usize>(
640 a: &[T; N],
641 b: &[T; N],
642 borrow_in: bool,
643 ) -> ([T; N], bool) {
644 let mut result = [T::zero(); N];
645 let mut borrow = borrow_in;
646 let mut i = 0usize;
647 while i < N {
648 let (diff, br) = ConstBorrowingSub::borrowing_sub(a[i], b[i], borrow);
649 result[i] = diff;
650 borrow = br;
651 i += 1;
652 }
653 (result, borrow)
654 }
655
656 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
661 target: &mut [T; N],
662 other: &[T; N]
663 ) -> bool {
664 let mut carry = false;
665 let mut i = 0usize;
666 while i < N {
667 let (sum, c) = ConstCarryingAdd::carrying_add(target[i], other[i], carry);
668 target[i] = sum;
669 carry = c;
670 i += 1;
671 }
672 carry
673 }
674
675 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
677 target: &mut [T; N],
678 other: &[T; N]
679 ) -> bool {
680 let mut borrow = false;
681 let mut i = 0usize;
682 while i < N {
683 let (diff, br) = ConstBorrowingSub::borrowing_sub(target[i], other[i], borrow);
684 target[i] = diff;
685 borrow = br;
686 i += 1;
687 }
688 borrow
689 }
690}
691
692c0nst::c0nst! {
693 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
695 target: &mut FixedUInt<T, N, P>,
696 bits: usize,
697 ) {
698 if N == 0 {
699 return;
700 }
701 let word_bits = FixedUInt::<T, N>::WORD_BITS;
702 let nwords = bits / word_bits;
703 let nbits = bits - nwords * word_bits;
704
705 if nwords >= N {
707 let mut i = 0;
708 while i < N {
709 target.array[i] = T::zero();
710 i += 1;
711 }
712 return;
713 }
714
715 let mut i = N;
717 while i > nwords {
718 i -= 1;
719 target.array[i] = target.array[i - nwords];
720 }
721 let mut i = 0;
723 while i < nwords {
724 target.array[i] = T::zero();
725 i += 1;
726 }
727
728 if nbits != 0 {
729 let mut i = N;
731 while i > 1 {
732 i -= 1;
733 let right = target.array[i] << nbits;
734 let left = target.array[i - 1] >> (word_bits - nbits);
735 target.array[i] = right | left;
736 }
737 target.array[0] <<= nbits;
738 }
739 }
740
741 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(
743 target: &mut FixedUInt<T, N, P>,
744 bits: usize,
745 ) {
746 if N == 0 {
747 return;
748 }
749 let word_bits = FixedUInt::<T, N>::WORD_BITS;
750 let nwords = bits / word_bits;
751 let nbits = bits - nwords * word_bits;
752
753 if nwords >= N {
755 let mut i = 0;
756 while i < N {
757 target.array[i] = T::zero();
758 i += 1;
759 }
760 return;
761 }
762
763 let last_index = N - 1;
764 let last_word = N - nwords;
765
766 let mut i = 0;
768 while i < last_word {
769 target.array[i] = target.array[i + nwords];
770 i += 1;
771 }
772
773 let mut i = last_word;
775 while i < N {
776 target.array[i] = T::zero();
777 i += 1;
778 }
779
780 if nbits != 0 {
781 let mut i = 0;
783 while i < last_index {
784 let left = target.array[i] >> nbits;
785 let right = target.array[i + 1] << (word_bits - nbits);
786 target.array[i] = left | right;
787 i += 1;
788 }
789 target.array[last_index] >>= nbits;
790 }
791 }
792
793 pub(crate) c0nst fn const_shl_ct<
802 T: [c0nst] ConstMachineWord + MachineWord,
803 const N: usize,
804 P: Personality,
805 >(
806 target: &mut FixedUInt<T, N, P>,
807 bits: usize,
808 ) {
809 if N == 0 {
810 return;
811 }
812 let layers = core::mem::size_of::<usize>() * 8;
815 let mut k = 0;
816 while k < layers {
817 let amount = 1usize << k;
818 let mut shifted = *target;
820 const_shl_impl(&mut shifted, amount);
821 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
825 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
826 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as ConstBounded>::max_value());
827 let mut i = 0;
829 while i < N {
830 let diff =
831 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
832 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
833 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
834 i += 1;
835 }
836 k += 1;
837 }
838 }
839
840 pub(crate) c0nst fn const_shr_ct<
843 T: [c0nst] ConstMachineWord + MachineWord,
844 const N: usize,
845 P: Personality,
846 >(
847 target: &mut FixedUInt<T, N, P>,
848 bits: usize,
849 ) {
850 if N == 0 {
851 return;
852 }
853 let layers = core::mem::size_of::<usize>() * 8;
856 let mut k = 0;
857 while k < layers {
858 let amount = 1usize << k;
859 let mut shifted = *target;
860 const_shr_impl(&mut shifted, amount);
861 let bit_k = core::hint::black_box(((bits >> k) & 1) as u8);
863 let bit_k_t = <T as core::convert::From<u8>>::from(bit_k);
864 let mask = <T as core::ops::Mul>::mul(bit_k_t, <T as ConstBounded>::max_value());
865 let mut i = 0;
866 while i < N {
867 let diff =
868 <T as core::ops::BitXor>::bitxor(target.array[i], shifted.array[i]);
869 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
870 target.array[i] = <T as core::ops::BitXor>::bitxor(target.array[i], masked);
871 i += 1;
872 }
873 k += 1;
874 }
875 }
876
877 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool, P: Personality>(
887 op1: &[T; N],
888 op2: &[T; N],
889 word_bits: usize,
890 ) -> ([T; N], bool) {
891 let mut result: [T; N] = [<T as ConstZero>::zero(); N];
892 let mut overflowed = false;
893 let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
894 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
895
896 let mut i = 0;
897 while i < N {
898 let mut carry = dw_zero;
899 let mut j = 0;
900 while j < N {
901 let round = i + j;
902 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
903 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
904 let mul_res = op1_dw * op2_dw;
905 let mut accumulator = if round < N {
906 <T as ConstMachineWord>::to_double(result[round])
907 } else {
908 dw_zero
909 };
910 accumulator += mul_res + carry;
911
912 match P::TAG {
913 PersonalityTag::Nct => {
914 if accumulator > t_max {
915 carry = accumulator >> word_bits;
916 accumulator &= t_max;
917 } else {
918 carry = dw_zero;
919 }
920 }
921 PersonalityTag::Ct => {
922 carry = accumulator >> word_bits;
923 accumulator &= t_max;
924 }
925 }
926 if round < N {
927 result[round] = <T as ConstMachineWord>::from_double(accumulator);
928 } else if CHECK_OVERFLOW {
929 overflowed |= accumulator != dw_zero;
930 }
931 j += 1;
932 }
933 if CHECK_OVERFLOW {
934 overflowed |= carry != dw_zero;
935 }
936 i += 1;
937 }
938 (result, overflowed)
939 }
940
941 pub(crate) c0nst fn const_word_bits<T>() -> usize {
943 core::mem::size_of::<T>() * 8
944 }
945
946 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
948 if a > b {
949 Some(core::cmp::Ordering::Greater)
950 } else if a < b {
951 Some(core::cmp::Ordering::Less)
952 } else {
953 None
954 }
955 }
956
957 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
959 array: &[T; N],
960 ) -> u32 {
961 let mut ret = 0u32;
962 let mut index = N;
963 while index > 0 {
964 index -= 1;
965 let v = array[index];
966 ret += <T as ConstBitPrimInt>::leading_zeros(v);
967 if !<T as ConstZero>::is_zero(&v) {
968 break;
969 }
970 }
971 ret
972 }
973
974 pub(crate) c0nst fn const_leading_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
981 array: &[T; N],
982 ) -> u32 {
983 let mut total: u32 = 0;
984 let mut decided: u32 = 0;
986 let mut index = N;
987 while index > 0 {
988 index -= 1;
989 let v = array[index];
990 let v_lz = <T as ConstBitPrimInt>::leading_zeros(v);
991 let undecided = core::hint::black_box(!decided);
995 total += undecided & v_lz;
996 let v_nz_bit = (!<T as ConstZero>::is_zero(&v)) as u32;
998 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
999 decided |= v_nz_mask;
1000 }
1001 total
1002 }
1003
1004 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1006 array: &[T; N],
1007 ) -> u32 {
1008 let mut ret = 0u32;
1009 let mut index = 0;
1010 while index < N {
1011 let v = array[index];
1012 ret += <T as ConstBitPrimInt>::trailing_zeros(v);
1013 if !<T as ConstZero>::is_zero(&v) {
1014 break;
1015 }
1016 index += 1;
1017 }
1018 ret
1019 }
1020
1021 pub(crate) c0nst fn const_trailing_zeros_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1026 array: &[T; N],
1027 ) -> u32 {
1028 let mut total: u32 = 0;
1029 let mut decided: u32 = 0;
1031 let mut index = 0;
1032 while index < N {
1033 let v = array[index];
1034 let v_tz = <T as ConstBitPrimInt>::trailing_zeros(v);
1035 let undecided = core::hint::black_box(!decided);
1038 total += undecided & v_tz;
1039 let v_nz_bit = (!<T as ConstZero>::is_zero(&v)) as u32;
1040 let v_nz_mask = core::hint::black_box(v_nz_bit.wrapping_neg());
1041 decided |= v_nz_mask;
1042 index += 1;
1043 }
1044 total
1045 }
1046
1047 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1049 array: &[T; N],
1050 ) -> usize {
1051 let word_bits = const_word_bits::<T>();
1052 let bit_size = N * word_bits;
1053 bit_size - const_leading_zeros::<T, N>(array) as usize
1054 }
1055
1056 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1058 array: &[T; N],
1059 ) -> bool {
1060 let mut index = 0;
1061 while index < N {
1062 if !<T as ConstZero>::is_zero(&array[index]) {
1063 return false;
1064 }
1065 index += 1;
1066 }
1067 true
1068 }
1069
1070 pub(crate) c0nst fn const_is_zero_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1075 array: &[T; N],
1076 ) -> bool {
1077 let mut acc = <T as ConstZero>::zero();
1078 let mut index = 0;
1079 while index < N {
1080 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1081 index += 1;
1082 }
1083 <T as ConstZero>::is_zero(&acc)
1084 }
1085
1086 pub(crate) c0nst fn const_is_one<T: [c0nst] ConstMachineWord, const N: usize>(
1091 array: &[T; N],
1092 ) -> bool {
1093 if N == 0 || !array[0].is_one() {
1094 return false;
1095 }
1096 let mut i = 1;
1097 while i < N {
1098 if !<T as ConstZero>::is_zero(&array[i]) {
1099 return false;
1100 }
1101 i += 1;
1102 }
1103 true
1104 }
1105
1106 pub(crate) c0nst fn const_is_one_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1111 array: &[T; N],
1112 ) -> bool {
1113 if N == 0 {
1114 return false;
1115 }
1116 let mut acc = <T as core::ops::BitXor>::bitxor(array[0], <T as ConstOne>::one());
1117 let mut index = 1;
1118 while index < N {
1119 acc = <T as core::ops::BitOr>::bitor(acc, array[index]);
1120 index += 1;
1121 }
1122 <T as ConstZero>::is_zero(&acc)
1123 }
1124
1125 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1131 array: &mut [T; N],
1132 pos: usize,
1133 ) {
1134 let word_bits = const_word_bits::<T>();
1135 let word_idx = pos / word_bits;
1136 if word_idx >= N {
1137 return;
1138 }
1139 let bit_idx = pos % word_bits;
1140 array[word_idx] |= <T as ConstOne>::one() << bit_idx;
1141 }
1142
1143 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1148 a: &[T; N],
1149 b: &[T; N],
1150 ) -> core::cmp::Ordering {
1151 let mut index = N;
1152 while index > 0 {
1153 index -= 1;
1154 if let Some(ord) = const_cmp_words(a[index], b[index]) {
1155 return ord;
1156 }
1157 }
1158 core::cmp::Ordering::Equal
1159 }
1160
1161 pub(crate) c0nst fn const_cmp_ct<T: [c0nst] ConstMachineWord, const N: usize>(
1166 a: &[T; N],
1167 b: &[T; N],
1168 ) -> core::cmp::Ordering {
1169 let mut result: u8 = 0;
1171 let mut decided: u8 = 0;
1173 let mut index = N;
1174 while index > 0 {
1175 index -= 1;
1176 let gt = (a[index] > b[index]) as u8;
1177 let lt = (a[index] < b[index]) as u8;
1178 let here = (gt << 1) | lt;
1180 let undecided_mask = core::hint::black_box(!decided);
1182 result |= undecided_mask & here;
1183 let here_nz_mask = core::hint::black_box(((here != 0) as u8).wrapping_neg());
1185 decided |= here_nz_mask;
1186 }
1187 match result {
1188 2 => core::cmp::Ordering::Greater,
1189 1 => core::cmp::Ordering::Less,
1190 _ => core::cmp::Ordering::Equal,
1191 }
1192 }
1193
1194 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1199 array: &[T; N],
1200 word_idx: usize,
1201 word_shift: usize,
1202 bit_shift: usize,
1203 ) -> T {
1204 let word_bits = const_word_bits::<T>();
1205
1206 if bit_shift >= word_bits {
1208 return <T as ConstZero>::zero();
1209 }
1210
1211 if word_idx < word_shift {
1212 return <T as ConstZero>::zero();
1213 }
1214
1215 let source_idx = word_idx - word_shift;
1216
1217 if bit_shift == 0 {
1218 if source_idx < N {
1219 array[source_idx]
1220 } else {
1221 <T as ConstZero>::zero()
1222 }
1223 } else {
1224 let mut result = <T as ConstZero>::zero();
1225
1226 if source_idx < N {
1228 result |= array[source_idx] << bit_shift;
1229 }
1230
1231 if source_idx > 0 && source_idx - 1 < N {
1233 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
1234 result |= high_bits;
1235 }
1236
1237 result
1238 }
1239 }
1240
1241 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1246 array: &[T; N],
1247 other: &[T; N],
1248 shift_bits: usize,
1249 ) -> core::cmp::Ordering {
1250 let word_bits = const_word_bits::<T>();
1251
1252 if shift_bits == 0 {
1253 return const_cmp::<T, N>(array, other);
1254 }
1255
1256 let word_shift = shift_bits / word_bits;
1257 if word_shift >= N {
1258 if const_is_zero::<T, N>(array) {
1260 return core::cmp::Ordering::Equal;
1261 } else {
1262 return core::cmp::Ordering::Greater;
1263 }
1264 }
1265
1266 let bit_shift = shift_bits % word_bits;
1267
1268 let mut index = N;
1270 while index > 0 {
1271 index -= 1;
1272 let self_word = array[index];
1273 let other_shifted_word = const_get_shifted_word::<T, N>(
1274 other, index, word_shift, bit_shift
1275 );
1276
1277 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
1278 return ord;
1279 }
1280 }
1281
1282 core::cmp::Ordering::Equal
1283 }
1284
1285 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1290 array: &mut [T; N],
1291 other: &[T; N],
1292 shift_bits: usize,
1293 ) {
1294 let word_bits = const_word_bits::<T>();
1295
1296 if shift_bits == 0 {
1297 sub_impl::<T, N>(array, other);
1298 return;
1299 }
1300
1301 let word_shift = shift_bits / word_bits;
1302 if word_shift >= N {
1303 return;
1304 }
1305
1306 let bit_shift = shift_bits % word_bits;
1307 let mut borrow = T::zero();
1308 let mut index = 0;
1309 while index < N {
1310 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
1311 let (res, borrow1) = array[index].overflowing_sub(&other_word);
1312 let (res, borrow2) = res.overflowing_sub(&borrow);
1313 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
1314 array[index] = res;
1315 index += 1;
1316 }
1317 }
1318
1319 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
1323 dividend: &mut [T; N],
1324 divisor: &[T; N],
1325 ) -> [T; N] {
1326 use core::cmp::Ordering;
1327
1328 match const_cmp::<T, N>(dividend, divisor) {
1329 Ordering::Less => {
1331 let remainder = *dividend;
1332 let mut i = 0;
1333 while i < N {
1334 dividend[i] = <T as ConstZero>::zero();
1335 i += 1;
1336 }
1337 return remainder;
1338 }
1339 Ordering::Equal => {
1341 let mut i = 0;
1342 while i < N {
1343 dividend[i] = <T as ConstZero>::zero();
1344 i += 1;
1345 }
1346 if N > 0 {
1347 dividend[0] = <T as ConstOne>::one();
1348 }
1349 return [<T as ConstZero>::zero(); N];
1350 }
1351 Ordering::Greater => {}
1352 }
1353
1354 let mut quotient = [<T as ConstZero>::zero(); N];
1355
1356 let dividend_bits = const_bit_length::<T, N>(dividend);
1358 let divisor_bits = const_bit_length::<T, N>(divisor);
1359
1360 let mut bit_pos = if dividend_bits >= divisor_bits {
1361 dividend_bits - divisor_bits
1362 } else {
1363 0
1364 };
1365
1366 while bit_pos > 0 {
1368 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1369 if !matches!(cmp, Ordering::Less) {
1370 break;
1371 }
1372 bit_pos -= 1;
1373 }
1374
1375 loop {
1377 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
1378 if !matches!(cmp, Ordering::Less) {
1379 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
1380 const_set_bit::<T, N>(&mut quotient, bit_pos);
1381 }
1382
1383 if bit_pos == 0 {
1384 break;
1385 }
1386 bit_pos -= 1;
1387 }
1388
1389 let remainder = *dividend;
1390 *dividend = quotient;
1391 remainder
1392 }
1393
1394 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
1398 dividend: &[T; N],
1399 divisor: &[T; N],
1400 ) -> ([T; N], [T; N]) {
1401 if const_is_zero(divisor) {
1402 maybe_panic(PanicReason::DivByZero)
1403 }
1404 let mut quotient = *dividend;
1405 let remainder = const_div(&mut quotient, divisor);
1406 (quotient, remainder)
1407 }
1408}
1409
1410c0nst::c0nst! {
1411 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Default for FixedUInt<T, N, P> {
1412 fn default() -> Self {
1413 FixedUInt::from_array([<T as ConstZero>::zero(); N])
1414 }
1415 }
1416
1417 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Clone for FixedUInt<T, N, P> {
1418 fn clone(&self) -> Self {
1419 *self
1420 }
1421 }
1422}
1423
1424impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N, Nct> {}
1427
1428c0nst::c0nst! {
1431 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialEq for FixedUInt<T, N, P> {
1432 fn eq(&self, other: &Self) -> bool {
1433 match P::TAG {
1434 PersonalityTag::Nct => self.array == other.array,
1435 PersonalityTag::Ct => {
1436 let mut diff = <T as crate::const_numtraits::ConstZero>::zero();
1437 let mut i = 0;
1438 while i < N {
1439 let x = <T as core::ops::BitXor>::bitxor(self.array[i], other.array[i]);
1440 diff = <T as core::ops::BitOr>::bitor(diff, x);
1441 i += 1;
1442 }
1443 <T as crate::const_numtraits::ConstZero>::is_zero(&diff)
1444 }
1445 }
1446 }
1447 }
1448
1449 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Eq for FixedUInt<T, N, P> {}
1450
1451 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::Ord for FixedUInt<T, N, P> {
1452 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1453 match P::TAG {
1454 PersonalityTag::Nct => const_cmp(&self.array, &other.array),
1455 PersonalityTag::Ct => const_cmp_ct(&self.array, &other.array),
1456 }
1457 }
1458 }
1459
1460 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::cmp::PartialOrd for FixedUInt<T, N, P> {
1461 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
1462 Some(self.cmp(other))
1463 }
1464 }
1465}
1466
1467c0nst::c0nst! {
1472 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
1475 bytes: [u8; B],
1476 ) -> [T; N] {
1477 impl_from_le_bytes_slice::<T, N>(&bytes)
1478 }
1479
1480 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u8> for FixedUInt<T, N, P> {
1481 fn from(x: u8) -> Self {
1482 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1483 }
1484 }
1485
1486 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u16> for FixedUInt<T, N, P> {
1487 fn from(x: u16) -> Self {
1488 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1489 }
1490 }
1491
1492 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u32> for FixedUInt<T, N, P> {
1493 fn from(x: u32) -> Self {
1494 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1495 }
1496 }
1497
1498 c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> core::convert::From<u64> for FixedUInt<T, N, P> {
1499 fn from(x: u64) -> Self {
1500 Self::from_array(const_from_le_bytes(x.to_le_bytes()))
1501 }
1502 }
1503}
1504
1505fn make_parse_int_err() -> core::num::ParseIntError {
1512 <u8>::from_str_radix("-", 2).err().unwrap()
1513}
1514fn make_overflow_err() -> core::num::ParseIntError {
1515 <u8>::from_str_radix("101", 16).err().unwrap()
1516}
1517fn make_empty_error() -> core::num::ParseIntError {
1518 <u8>::from_str_radix("", 8).err().unwrap()
1519}
1520
1521fn to_slice_hex<T: AsRef<[u8]>>(
1522 input: T,
1523 output: &mut [u8],
1524) -> Result<(), core::num::ParseIntError> {
1525 fn from_digit(byte: u8) -> Option<char> {
1526 core::char::from_digit(byte as u32, 16)
1527 }
1528 let r = input.as_ref();
1529 if r.len() * 2 != output.len() {
1530 return Err(make_parse_int_err());
1531 }
1532 for i in 0..r.len() {
1533 let byte = r[i];
1534 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1535 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1536 }
1537
1538 Ok(())
1539}
1540
1541pub(super) enum PanicReason {
1542 Add,
1543 Sub,
1544 Mul,
1545 DivByZero,
1546}
1547
1548c0nst::c0nst! {
1549 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1550 match r {
1551 PanicReason::Add => panic!("attempt to add with overflow"),
1552 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1553 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1554 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1555 }
1556 }
1557
1558 pub(crate) c0nst fn const_ct_select<
1571 T: [c0nst] ConstMachineWord + MachineWord,
1572 const N: usize,
1573 P: Personality,
1574 >(
1575 if_zero: FixedUInt<T, N, P>,
1576 if_one: FixedUInt<T, N, P>,
1577 choice: u8,
1578 ) -> FixedUInt<T, N, P> {
1579 let choice = core::hint::black_box(choice);
1580 let bit_t = <T as core::convert::From<u8>>::from(choice);
1581 let mask = <T as core::ops::Mul>::mul(bit_t, <T as ConstBounded>::max_value());
1582 let mut result = if_zero;
1583 let mut i = 0;
1584 while i < N {
1585 let diff = <T as core::ops::BitXor>::bitxor(if_zero.array[i], if_one.array[i]);
1586 let masked = <T as core::ops::BitAnd>::bitand(mask, diff);
1587 result.array[i] = <T as core::ops::BitXor>::bitxor(if_zero.array[i], masked);
1588 i += 1;
1589 }
1590 result
1591 }
1592
1593 pub(super) c0nst fn maybe_panic_if<P: Personality>(
1594 overflow: bool,
1595 reason: PanicReason,
1596 ) {
1597 match P::TAG {
1598 PersonalityTag::Nct => {
1599 if overflow {
1600 maybe_panic(reason);
1601 }
1602 }
1603 PersonalityTag::Ct => {
1604 let _ = overflow;
1605 let _ = reason;
1606 }
1607 }
1608 }
1609}
1610
1611#[cfg(test)]
1614mod tests {
1615 use super::FixedUInt as Bn;
1616 use super::*;
1617 use num_traits::One;
1618
1619 type Bn8 = Bn<u8, 8>;
1620 type Bn16 = Bn<u16, 4>;
1621 type Bn32 = Bn<u32, 2>;
1622
1623 c0nst::c0nst! {
1624 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1625 a: &mut [T; N],
1626 b: &[T; N]
1627 ) -> bool {
1628 add_impl(a, b)
1629 }
1630
1631 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1632 a: &mut [T; N],
1633 b: &[T; N]
1634 ) -> bool {
1635 sub_impl(a, b)
1636 }
1637
1638 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1639 a: &[T; N],
1640 b: &[T; N],
1641 word_bits: usize,
1642 ) -> ([T; N], bool) {
1643 const_mul::<T, N, true, crate::personality::Nct>(a, b, word_bits)
1644 }
1645
1646 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1647 a: &[T; N],
1648 ) -> u32 {
1649 const_leading_zeros::<T, N>(a)
1650 }
1651
1652 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1653 a: &[T; N],
1654 ) -> u32 {
1655 const_trailing_zeros::<T, N>(a)
1656 }
1657
1658 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1659 a: &[T; N],
1660 ) -> usize {
1661 const_bit_length::<T, N>(a)
1662 }
1663
1664 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1665 a: &[T; N],
1666 ) -> bool {
1667 const_is_zero::<T, N>(a)
1668 }
1669
1670 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1671 a: &mut [T; N],
1672 pos: usize,
1673 ) {
1674 const_set_bit::<T, N>(a, pos)
1675 }
1676
1677 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1678 a: &[T; N],
1679 b: &[T; N],
1680 ) -> core::cmp::Ordering {
1681 const_cmp::<T, N>(a, b)
1682 }
1683
1684 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1685 a: &[T; N],
1686 b: &[T; N],
1687 shift_bits: usize,
1688 ) -> core::cmp::Ordering {
1689 const_cmp_shifted::<T, N>(a, b, shift_bits)
1690 }
1691
1692 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1693 a: &[T; N],
1694 word_idx: usize,
1695 word_shift: usize,
1696 bit_shift: usize,
1697 ) -> T {
1698 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1699 }
1700 }
1701
1702 #[test]
1703 fn test_const_add_impl() {
1704 let mut a: [u8; 4] = [1, 0, 0, 0];
1706 let b: [u8; 4] = [2, 0, 0, 0];
1707 let overflow = test_add(&mut a, &b);
1708 assert_eq!(a, [3, 0, 0, 0]);
1709 assert!(!overflow);
1710
1711 let mut a: [u8; 4] = [255, 0, 0, 0];
1713 let b: [u8; 4] = [1, 0, 0, 0];
1714 let overflow = test_add(&mut a, &b);
1715 assert_eq!(a, [0, 1, 0, 0]);
1716 assert!(!overflow);
1717
1718 let mut a: [u8; 4] = [255, 255, 255, 255];
1720 let b: [u8; 4] = [1, 0, 0, 0];
1721 let overflow = test_add(&mut a, &b);
1722 assert_eq!(a, [0, 0, 0, 0]);
1723 assert!(overflow);
1724
1725 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1727 let b: [u32; 2] = [1, 0];
1728 let overflow = test_add(&mut a, &b);
1729 assert_eq!(a, [0, 1]);
1730 assert!(!overflow);
1731
1732 #[cfg(feature = "nightly")]
1733 {
1734 const ADD_RESULT: ([u8; 4], bool) = {
1735 let mut a = [1u8, 0, 0, 0];
1736 let b = [2u8, 0, 0, 0];
1737 let overflow = test_add(&mut a, &b);
1738 (a, overflow)
1739 };
1740 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1741 }
1742 }
1743
1744 #[test]
1745 fn test_const_sub_impl() {
1746 let mut a: [u8; 4] = [3, 0, 0, 0];
1748 let b: [u8; 4] = [1, 0, 0, 0];
1749 let overflow = test_sub(&mut a, &b);
1750 assert_eq!(a, [2, 0, 0, 0]);
1751 assert!(!overflow);
1752
1753 let mut a: [u8; 4] = [0, 1, 0, 0];
1755 let b: [u8; 4] = [1, 0, 0, 0];
1756 let overflow = test_sub(&mut a, &b);
1757 assert_eq!(a, [255, 0, 0, 0]);
1758 assert!(!overflow);
1759
1760 let mut a: [u8; 4] = [0, 0, 0, 0];
1762 let b: [u8; 4] = [1, 0, 0, 0];
1763 let overflow = test_sub(&mut a, &b);
1764 assert_eq!(a, [255, 255, 255, 255]);
1765 assert!(overflow);
1766
1767 let mut a: [u32; 2] = [0, 1];
1769 let b: [u32; 2] = [1, 0];
1770 let overflow = test_sub(&mut a, &b);
1771 assert_eq!(a, [0xFFFFFFFF, 0]);
1772 assert!(!overflow);
1773
1774 #[cfg(feature = "nightly")]
1775 {
1776 const SUB_RESULT: ([u8; 4], bool) = {
1777 let mut a = [3u8, 0, 0, 0];
1778 let b = [1u8, 0, 0, 0];
1779 let overflow = test_sub(&mut a, &b);
1780 (a, overflow)
1781 };
1782 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1783 }
1784 }
1785
1786 #[test]
1787 fn test_const_mul_impl() {
1788 let a: [u8; 2] = [3, 0];
1790 let b: [u8; 2] = [4, 0];
1791 let (result, overflow) = test_mul(&a, &b, 8);
1792 assert_eq!(result, [12, 0]);
1793 assert!(!overflow);
1794
1795 let a: [u8; 2] = [200, 0];
1797 let b: [u8; 2] = [2, 0];
1798 let (result, overflow) = test_mul(&a, &b, 8);
1799 assert_eq!(result, [0x90, 0x01]);
1800 assert!(!overflow);
1801
1802 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1806 assert!(overflow);
1807
1808 let a: [u8; 3] = [0, 0, 1];
1812 let b: [u8; 3] = [0, 0, 1];
1813 let (_result, overflow) = test_mul(&a, &b, 8);
1814 assert!(overflow, "N=3 high-position overflow not detected");
1815
1816 let a: [u8; 3] = [0, 0, 2];
1820 let b: [u8; 3] = [0, 0, 2];
1821 let (_result, overflow) = test_mul(&a, &b, 8);
1822 assert!(
1823 overflow,
1824 "N=3 high-position overflow with larger values not detected"
1825 );
1826
1827 let a: [u8; 3] = [0, 1, 0];
1831 let b: [u8; 3] = [0, 1, 0];
1832 let (result, overflow) = test_mul(&a, &b, 8);
1833 assert_eq!(result, [0, 0, 1]);
1834 assert!(
1835 !overflow,
1836 "N=3 non-overflow incorrectly detected as overflow"
1837 );
1838
1839 let a: [u8; 3] = [255, 0, 0];
1843 let b: [u8; 3] = [255, 0, 0];
1844 let (result, overflow) = test_mul(&a, &b, 8);
1845 assert_eq!(result, [0x01, 0xFE, 0x00]);
1846 assert!(!overflow);
1847
1848 #[cfg(feature = "nightly")]
1849 {
1850 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1851 assert_eq!(MUL_RESULT, ([12, 0], false));
1852 }
1853 }
1854
1855 #[test]
1856 fn test_const_helpers() {
1857 assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0]), 32); assert_eq!(arr_leading_zeros(&[1u8, 0, 0, 0]), 31); assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 1]), 7); assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0x80]), 0); assert_eq!(arr_leading_zeros(&[255u8, 255, 255, 255]), 0); assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 0]), 32); assert_eq!(arr_trailing_zeros(&[1u8, 0, 0, 0]), 0); assert_eq!(arr_trailing_zeros(&[0u8, 1, 0, 0]), 8); assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 1]), 24); assert_eq!(arr_trailing_zeros(&[0x80u8, 0, 0, 0]), 7); assert_eq!(arr_bit_length(&[0u8, 0, 0, 0]), 0); assert_eq!(arr_bit_length(&[1u8, 0, 0, 0]), 1); assert_eq!(arr_bit_length(&[2u8, 0, 0, 0]), 2); assert_eq!(arr_bit_length(&[3u8, 0, 0, 0]), 2); assert_eq!(arr_bit_length(&[0u8, 1, 0, 0]), 9); assert_eq!(arr_bit_length(&[0xF0u8, 0, 0, 0]), 8); assert_eq!(arr_bit_length(&[255u8, 255, 255, 255]), 32); assert!(arr_is_zero(&[0u8, 0, 0, 0]));
1882 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1883 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1884 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1885
1886 let mut arr: [u8; 4] = [0, 0, 0, 0];
1888 arr_set_bit(&mut arr, 0);
1889 assert_eq!(arr, [1, 0, 0, 0]);
1890
1891 let mut arr: [u8; 4] = [0, 0, 0, 0];
1892 arr_set_bit(&mut arr, 8);
1893 assert_eq!(arr, [0, 1, 0, 0]);
1894
1895 let mut arr: [u8; 4] = [0, 0, 0, 0];
1896 arr_set_bit(&mut arr, 31);
1897 assert_eq!(arr, [0, 0, 0, 0x80]);
1898
1899 let mut arr: [u8; 4] = [0, 0, 0, 0];
1901 arr_set_bit(&mut arr, 0);
1902 arr_set_bit(&mut arr, 3);
1903 arr_set_bit(&mut arr, 8);
1904 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1905
1906 let mut arr: [u8; 4] = [0, 0, 0, 0];
1908 arr_set_bit(&mut arr, 32);
1909 assert_eq!(arr, [0, 0, 0, 0]);
1910
1911 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1913 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1914 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1915 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1916 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1917 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1918 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1919 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1920
1921 #[cfg(feature = "nightly")]
1922 {
1923 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1924 assert_eq!(LEADING, 15);
1925
1926 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1927 assert_eq!(TRAILING, 16);
1928
1929 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1930 assert_eq!(BIT_LEN, 17);
1931
1932 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1933 assert!(IS_ZERO);
1934
1935 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1936 assert!(!NOT_ZERO);
1937
1938 const SET_BIT_RESULT: [u8; 4] = {
1939 let mut arr = [0u8, 0, 0, 0];
1940 arr_set_bit(&mut arr, 10);
1941 arr
1942 };
1943 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1944 }
1945 }
1946
1947 #[test]
1948 fn test_const_cmp() {
1949 use core::cmp::Ordering;
1950
1951 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1953 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1954
1955 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1957
1958 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1960
1961 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1963
1964 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1966
1967 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1969 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1970 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1971
1972 #[cfg(feature = "nightly")]
1973 {
1974 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1975 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1976 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1977 assert_eq!(CMP_EQ, Ordering::Equal);
1978 assert_eq!(CMP_GT, Ordering::Greater);
1979 assert_eq!(CMP_LT, Ordering::Less);
1980 }
1981 }
1982
1983 #[test]
1984 fn test_const_cmp_shifted() {
1985 use core::cmp::Ordering;
1986
1987 assert_eq!(
1989 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1990 Ordering::Equal
1991 );
1992
1993 assert_eq!(
1995 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1996 Ordering::Equal
1997 );
1998
1999 assert_eq!(
2001 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
2002 Ordering::Greater
2003 );
2004
2005 assert_eq!(
2007 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
2008 Ordering::Less
2009 );
2010
2011 assert_eq!(
2014 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
2015 Ordering::Greater
2016 );
2017
2018 assert_eq!(
2020 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
2021 Ordering::Equal
2022 );
2023
2024 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
2028 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
2029 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
2030
2031 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
2035 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
2037
2038 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
2041 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
2043
2044 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
2048 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
2050 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
2052
2053 #[cfg(feature = "nightly")]
2054 {
2055 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
2056 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
2057 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
2058 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
2059 }
2060 }
2061
2062 #[test]
2063 fn test_core_convert_u8() {
2064 let f = Bn::<u8, 1>::from(1u8);
2065 assert_eq!(f.array, [1]);
2066 let f = Bn::<u8, 2>::from(1u8);
2067 assert_eq!(f.array, [1, 0]);
2068
2069 let f = Bn::<u16, 1>::from(1u8);
2070 assert_eq!(f.array, [1]);
2071 let f = Bn::<u16, 2>::from(1u8);
2072 assert_eq!(f.array, [1, 0]);
2073
2074 #[cfg(feature = "nightly")]
2075 {
2076 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
2077 assert_eq!(F1.array, [42, 0]);
2078 }
2079 }
2080
2081 #[test]
2082 fn test_core_convert_u16() {
2083 let f = Bn::<u8, 1>::from(1u16);
2084 assert_eq!(f.array, [1]);
2085 let f = Bn::<u8, 2>::from(1u16);
2086 assert_eq!(f.array, [1, 0]);
2087
2088 let f = Bn::<u8, 1>::from(256u16);
2089 assert_eq!(f.array, [0]);
2090 let f = Bn::<u8, 2>::from(257u16);
2091 assert_eq!(f.array, [1, 1]);
2092 let f = Bn::<u8, 2>::from(65535u16);
2093 assert_eq!(f.array, [255, 255]);
2094
2095 let f = Bn::<u16, 1>::from(1u16);
2096 assert_eq!(f.array, [1]);
2097 let f = Bn::<u16, 2>::from(1u16);
2098 assert_eq!(f.array, [1, 0]);
2099
2100 let f = Bn::<u16, 1>::from(65535u16);
2101 assert_eq!(f.array, [65535]);
2102
2103 #[cfg(feature = "nightly")]
2104 {
2105 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
2106 assert_eq!(F1.array, [0x02, 0x01]);
2107 }
2108 }
2109
2110 #[test]
2111 fn test_core_convert_u32() {
2112 let f = Bn::<u8, 1>::from(1u32);
2113 assert_eq!(f.array, [1]);
2114 let f = Bn::<u8, 1>::from(256u32);
2115 assert_eq!(f.array, [0]);
2116
2117 let f = Bn::<u8, 2>::from(1u32);
2118 assert_eq!(f.array, [1, 0]);
2119 let f = Bn::<u8, 2>::from(257u32);
2120 assert_eq!(f.array, [1, 1]);
2121 let f = Bn::<u8, 2>::from(65535u32);
2122 assert_eq!(f.array, [255, 255]);
2123
2124 let f = Bn::<u8, 4>::from(1u32);
2125 assert_eq!(f.array, [1, 0, 0, 0]);
2126 let f = Bn::<u8, 4>::from(257u32);
2127 assert_eq!(f.array, [1, 1, 0, 0]);
2128 let f = Bn::<u8, 4>::from(u32::max_value());
2129 assert_eq!(f.array, [255, 255, 255, 255]);
2130
2131 let f = Bn::<u8, 1>::from(1u32);
2132 assert_eq!(f.array, [1]);
2133 let f = Bn::<u8, 1>::from(256u32);
2134 assert_eq!(f.array, [0]);
2135
2136 let f = Bn::<u16, 2>::from(65537u32);
2137 assert_eq!(f.array, [1, 1]);
2138
2139 let f = Bn::<u32, 1>::from(1u32);
2140 assert_eq!(f.array, [1]);
2141 let f = Bn::<u32, 2>::from(1u32);
2142 assert_eq!(f.array, [1, 0]);
2143
2144 let f = Bn::<u32, 1>::from(65537u32);
2145 assert_eq!(f.array, [65537]);
2146
2147 let f = Bn::<u32, 1>::from(u32::max_value());
2148 assert_eq!(f.array, [4294967295]);
2149
2150 #[cfg(feature = "nightly")]
2151 {
2152 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
2153 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
2154 }
2155 }
2156
2157 #[test]
2158 fn test_core_convert_u64() {
2159 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
2160 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2161
2162 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
2163 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
2164
2165 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
2166 assert_eq!(f.array, [0x05060708, 0x01020304]);
2167
2168 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
2169 assert_eq!(f.array, [0x0102030405060708]);
2170
2171 #[cfg(feature = "nightly")]
2172 {
2173 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
2174 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
2175 }
2176 }
2177
2178 #[test]
2179 fn testsimple() {
2180 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
2181
2182 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
2183 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
2184 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
2185 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
2186 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
2187 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
2188 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
2189 }
2190 #[test]
2191 fn testfrom() {
2192 let mut n1 = Bn::<u8, 8>::new();
2193 n1.array[0] = 1;
2194 assert_eq!(Some(1), n1.to_u32());
2195 n1.array[1] = 1;
2196 assert_eq!(Some(257), n1.to_u32());
2197
2198 let mut n2 = Bn::<u16, 8>::new();
2199 n2.array[0] = 0xffff;
2200 assert_eq!(Some(65535), n2.to_u32());
2201 n2.array[0] = 0x0;
2202 n2.array[2] = 0x1;
2203 assert_eq!(None, n2.to_u32());
2205 assert_eq!(Some(0x100000000), n2.to_u64());
2206 }
2207
2208 #[test]
2209 fn test_from_str_bitlengths() {
2210 let test_s64 = "81906f5e4d3c2c01";
2211 let test_u64: u64 = 0x81906f5e4d3c2c01;
2212 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
2213 let cc = Bn8::from_u64(test_u64).unwrap();
2214 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2215 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
2216 let dd = Bn16::from_u64(test_u64).unwrap();
2217 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
2218 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2219 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
2220 let ee = Bn32::from_u64(test_u64).unwrap();
2221 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
2222 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
2223 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
2224 }
2225
2226 #[test]
2227 fn test_from_str_stringlengths() {
2228 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
2229 assert_eq!(
2230 ab.array,
2231 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
2232 );
2233 assert_eq!(
2234 [0x2c01, 0x4d3c, 0x6f5e, 0],
2235 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
2236 .unwrap()
2237 .array
2238 );
2239 assert_eq!(
2240 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
2241 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
2242 .unwrap()
2243 .array
2244 );
2245 assert_eq!(
2246 Err(make_overflow_err()),
2247 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
2248 );
2249 assert_eq!(
2250 Err(make_overflow_err()),
2251 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
2252 );
2253 assert_eq!(
2254 Err(make_overflow_err()),
2255 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
2256 );
2257 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
2258 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
2259 }
2260
2261 #[test]
2262 fn test_resize() {
2263 type TestInt1 = FixedUInt<u32, 1>;
2264 type TestInt2 = FixedUInt<u32, 2>;
2265
2266 let a = TestInt1::from(u32::MAX);
2267 let b: TestInt2 = a.resize();
2268 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
2269
2270 let a = TestInt2::from([u32::MAX, u32::MAX]);
2271 let b: TestInt1 = a.resize();
2272 assert_eq!(b, TestInt1::from(u32::MAX));
2273 }
2274
2275 #[test]
2276 fn test_bit_length() {
2277 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
2278 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
2279 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
2280 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
2281 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
2282 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
2283 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
2284
2285 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
2286 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
2287 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
2288 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
2289 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
2290
2291 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
2292 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
2293 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
2294 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
2295 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
2296 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
2297 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
2298
2299 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
2300 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
2301 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
2302 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
2303 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
2304 }
2305
2306 #[test]
2307 fn test_bit_length_1000() {
2308 let value = Bn32::from_u16(1000).unwrap();
2310
2311 assert_eq!(value.to_u32().unwrap(), 1000);
2314 assert_eq!(value.bit_length(), 10);
2315
2316 assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
2323 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
2324
2325 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
2327 assert_eq!(value_from_str.bit_length(), 10);
2328
2329 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
2331 assert_eq!(
2333 value_from_bytes.to_u32().unwrap_or(0),
2334 1000,
2335 "from_le_bytes didn't create the correct value"
2336 );
2337 assert_eq!(value_from_bytes.bit_length(), 10);
2338 }
2339 #[test]
2340 fn test_cmp() {
2341 let f0 = <Bn8 as Zero>::zero();
2342 let f1 = <Bn8 as Zero>::zero();
2343 let f2 = <Bn8 as One>::one();
2344 assert_eq!(f0, f1);
2345 assert!(f2 > f0);
2346 assert!(f0 < f2);
2347 let f3 = Bn32::from_u64(990223).unwrap();
2348 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
2349 let f4 = Bn32::from_u64(990224).unwrap();
2350 assert!(f4 > Bn32::from_u64(990223).unwrap());
2351
2352 let f3 = Bn8::from_u64(990223).unwrap();
2353 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
2354 let f4 = Bn8::from_u64(990224).unwrap();
2355 assert!(f4 > Bn8::from_u64(990223).unwrap());
2356
2357 #[cfg(feature = "nightly")]
2358 {
2359 use core::cmp::Ordering;
2360
2361 const A: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2362 const B: FixedUInt<u8, 2> = FixedUInt::from_array([20, 0]);
2363 const C: FixedUInt<u8, 2> = FixedUInt::from_array([10, 0]);
2364
2365 const CMP_LT: Ordering = A.cmp(&B);
2366 const CMP_GT: Ordering = B.cmp(&A);
2367 const CMP_EQ: Ordering = A.cmp(&C);
2368 const EQ_TRUE: bool = A.eq(&C);
2369 const EQ_FALSE: bool = A.eq(&B);
2370
2371 assert_eq!(CMP_LT, Ordering::Less);
2372 assert_eq!(CMP_GT, Ordering::Greater);
2373 assert_eq!(CMP_EQ, Ordering::Equal);
2374 assert!(EQ_TRUE);
2375 assert!(!EQ_FALSE);
2376 }
2377 }
2378
2379 #[test]
2380 fn test_default() {
2381 let d: Bn8 = Default::default();
2382 assert!(Zero::is_zero(&d));
2383
2384 #[cfg(feature = "nightly")]
2385 {
2386 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
2387 assert!(Zero::is_zero(&D));
2388 }
2389 }
2390
2391 #[test]
2392 fn test_clone() {
2393 let a: Bn8 = 42u8.into();
2394 let b = a.clone();
2395 assert_eq!(a, b);
2396
2397 #[cfg(feature = "nightly")]
2398 {
2399 const A: FixedUInt<u8, 2> = FixedUInt::from_array([42, 0]);
2400 const B: FixedUInt<u8, 2> = A.clone();
2401 assert_eq!(A.array, B.array);
2402 }
2403 }
2404
2405 #[test]
2406 fn test_le_be_bytes() {
2407 let le_bytes = [1, 2, 3, 4];
2408 let be_bytes = [4, 3, 2, 1];
2409 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
2410 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
2411 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
2412 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
2413 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
2414 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
2415
2416 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
2417 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
2418 assert_eq!(u32_ver.array, [0x04030201]);
2419 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
2420 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
2421 assert_eq!(u32_ver_be.array, [0x04030201]);
2422
2423 let mut output_buffer = [0u8; 16];
2424 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2425 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2426 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2427 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2428 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
2429 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
2430 }
2431
2432 #[test]
2434 fn test_div_small() {
2435 type TestInt = FixedUInt<u8, 2>;
2436
2437 let test_cases = [
2439 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
2444
2445 for (dividend_val, divisor_val, expected) in test_cases {
2446 let dividend = TestInt::from(dividend_val);
2447 let divisor = TestInt::from(divisor_val);
2448 let expected_result = TestInt::from(expected);
2449
2450 assert_eq!(
2451 dividend / divisor,
2452 expected_result,
2453 "Division failed for {} / {} = {}",
2454 dividend_val,
2455 divisor_val,
2456 expected
2457 );
2458 }
2459 }
2460
2461 #[test]
2462 fn test_div_edge_cases() {
2463 type TestInt = FixedUInt<u16, 2>;
2464
2465 let dividend = TestInt::from(1000u16);
2467 let divisor = TestInt::from(1u16);
2468 assert_eq!(dividend / divisor, TestInt::from(1000u16));
2469
2470 let dividend = TestInt::from(42u16);
2472 let divisor = TestInt::from(42u16);
2473 assert_eq!(dividend / divisor, TestInt::from(1u16));
2474
2475 let dividend = TestInt::from(5u16);
2477 let divisor = TestInt::from(10u16);
2478 assert_eq!(dividend / divisor, TestInt::from(0u16));
2479
2480 let dividend = TestInt::from(1024u16);
2482 let divisor = TestInt::from(4u16);
2483 assert_eq!(dividend / divisor, TestInt::from(256u16));
2484 }
2485
2486 #[test]
2487 fn test_helper_methods() {
2488 type TestInt = FixedUInt<u8, 2>;
2489
2490 let mut val = <TestInt as Zero>::zero();
2492 const_set_bit(&mut val.array, 0);
2493 assert_eq!(val, TestInt::from(1u8));
2494
2495 const_set_bit(&mut val.array, 8);
2496 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
2504 const_cmp_shifted(&a.array, &b.array, 3),
2505 core::cmp::Ordering::Equal
2506 );
2507
2508 assert_eq!(
2510 const_cmp_shifted(&a.array, &b.array, 2),
2511 core::cmp::Ordering::Greater
2512 );
2513
2514 assert_eq!(
2516 const_cmp_shifted(&a.array, &b.array, 4),
2517 core::cmp::Ordering::Less
2518 );
2519
2520 let mut val = TestInt::from(10u8);
2522 let one = TestInt::from(1u8);
2523 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
2526
2527 #[test]
2528 fn test_shifted_operations_comprehensive() {
2529 type TestInt = FixedUInt<u32, 2>;
2530
2531 let a = TestInt::from(0x12345678u32);
2533 let b = TestInt::from(0x12345678u32);
2534
2535 assert_eq!(
2537 const_cmp_shifted(&a.array, &b.array, 0),
2538 core::cmp::Ordering::Equal
2539 );
2540
2541 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
2547 const_cmp_shifted(&d.array, &c.array, 16),
2548 core::cmp::Ordering::Greater
2549 );
2550
2551 let e = TestInt::from(1u32);
2553 let zero = TestInt::from(0u32);
2554 assert_eq!(
2555 const_cmp_shifted(&e.array, &zero.array, 100),
2556 core::cmp::Ordering::Greater
2557 );
2558 assert_eq!(
2560 const_cmp_shifted(&zero.array, &e.array, 100),
2561 core::cmp::Ordering::Equal
2562 );
2563
2564 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
2567 const_sub_shifted(&mut val.array, &one.array, 15); assert_eq!(val, TestInt::from(0x8000u32)); let mut big_val = TestInt::from(0x100000000u64); const_sub_shifted(&mut big_val.array, &one.array, 31); assert_eq!(big_val, TestInt::from(0x80000000u64)); }
2575
2576 #[test]
2577 fn test_shifted_operations_edge_cases() {
2578 type TestInt = FixedUInt<u32, 2>;
2579
2580 let a = TestInt::from(42u32);
2582 let a2 = TestInt::from(42u32);
2583 assert_eq!(
2584 const_cmp_shifted(&a.array, &a2.array, 0),
2585 core::cmp::Ordering::Equal
2586 );
2587
2588 let mut b = TestInt::from(42u32);
2589 let ten = TestInt::from(10u32);
2590 const_sub_shifted(&mut b.array, &ten.array, 0);
2591 assert_eq!(b, TestInt::from(32u32));
2592
2593 let c = TestInt::from(123u32);
2595 let large = TestInt::from(456u32);
2596 assert_eq!(
2597 const_cmp_shifted(&c.array, &large.array, 200),
2598 core::cmp::Ordering::Greater
2599 );
2600
2601 let mut d = TestInt::from(123u32);
2602 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2604
2605 let zero = TestInt::from(0u32);
2607 let one = TestInt::from(1u32);
2608 assert_eq!(
2609 const_cmp_shifted(&zero.array, &zero.array, 10),
2610 core::cmp::Ordering::Equal
2611 );
2612 assert_eq!(
2613 const_cmp_shifted(&one.array, &zero.array, 10),
2614 core::cmp::Ordering::Greater
2615 );
2616 }
2617
2618 #[test]
2619 fn test_shifted_operations_equivalence() {
2620 type TestInt = FixedUInt<u32, 2>;
2621
2622 let test_cases = [
2624 (0x12345u32, 0x678u32, 4),
2625 (0x1000u32, 0x10u32, 8),
2626 (0xABCDu32, 0x1u32, 16),
2627 (0x80000000u32, 0x1u32, 1),
2628 ];
2629
2630 for (a_val, b_val, shift) in test_cases {
2631 let a = TestInt::from(a_val);
2632 let b = TestInt::from(b_val);
2633
2634 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2636 let naive_cmp = a.cmp(&(b << shift));
2637 assert_eq!(
2638 optimized_cmp, naive_cmp,
2639 "cmp_shifted mismatch: {} vs ({} << {})",
2640 a_val, b_val, shift
2641 );
2642
2643 if a >= (b << shift) {
2645 let mut optimized_result = a;
2646 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2647
2648 let naive_result = a - (b << shift);
2649 assert_eq!(
2650 optimized_result, naive_result,
2651 "sub_shifted mismatch: {} - ({} << {})",
2652 a_val, b_val, shift
2653 );
2654 }
2655 }
2656 }
2657
2658 #[test]
2659 fn test_div_assign_in_place_optimization() {
2660 type TestInt = FixedUInt<u32, 2>;
2661
2662 let test_cases = [
2664 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2669
2670 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2671 let mut dividend = TestInt::from(dividend_val);
2673 let divisor = TestInt::from(divisor_val);
2674
2675 dividend /= divisor;
2676 assert_eq!(
2677 dividend,
2678 TestInt::from(expected_quotient),
2679 "div_assign: {} / {} should be {}",
2680 dividend_val,
2681 divisor_val,
2682 expected_quotient
2683 );
2684
2685 let dividend2 = TestInt::from(dividend_val);
2687 let (quotient, remainder) = dividend2.div_rem(&divisor);
2688 assert_eq!(
2689 quotient,
2690 TestInt::from(expected_quotient),
2691 "div_rem quotient: {} / {} should be {}",
2692 dividend_val,
2693 divisor_val,
2694 expected_quotient
2695 );
2696 assert_eq!(
2697 remainder,
2698 TestInt::from(expected_remainder),
2699 "div_rem remainder: {} % {} should be {}",
2700 dividend_val,
2701 divisor_val,
2702 expected_remainder
2703 );
2704
2705 assert_eq!(
2707 quotient * divisor + remainder,
2708 TestInt::from(dividend_val),
2709 "Property check failed for {}",
2710 dividend_val
2711 );
2712 }
2713 }
2714
2715 #[test]
2716 fn test_div_assign_stack_efficiency() {
2717 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2721 let divisor = TestInt::from(0x12345u32);
2722 let original_dividend = dividend;
2723
2724 dividend /= divisor;
2726
2727 let remainder = original_dividend % divisor;
2729 assert_eq!(dividend * divisor + remainder, original_dividend);
2730 }
2731
2732 #[test]
2733 fn test_rem_assign_optimization() {
2734 type TestInt = FixedUInt<u32, 2>;
2735
2736 let test_cases = [
2737 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2742
2743 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2744 let mut dividend = TestInt::from(dividend_val);
2745 let divisor = TestInt::from(divisor_val);
2746
2747 dividend %= divisor;
2748 assert_eq!(
2749 dividend,
2750 TestInt::from(expected_remainder),
2751 "rem_assign: {} % {} should be {}",
2752 dividend_val,
2753 divisor_val,
2754 expected_remainder
2755 );
2756 }
2757 }
2758
2759 #[test]
2760 fn test_div_with_remainder_property() {
2761 type TestInt = FixedUInt<u32, 2>;
2762
2763 let test_cases = [
2765 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2770
2771 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2772 let dividend = TestInt::from(dividend_val);
2773 let divisor = TestInt::from(divisor_val);
2774
2775 let quotient = dividend / divisor;
2777 assert_eq!(
2778 quotient,
2779 TestInt::from(expected_quotient),
2780 "Division: {} / {} should be {}",
2781 dividend_val,
2782 divisor_val,
2783 expected_quotient
2784 );
2785
2786 let remainder = dividend % divisor;
2788 assert_eq!(
2789 quotient * divisor + remainder,
2790 dividend,
2791 "Division property check failed for {}",
2792 dividend_val
2793 );
2794 }
2795 }
2796
2797 #[test]
2798 fn test_code_simplification_benefits() {
2799 type TestInt = FixedUInt<u32, 2>;
2800
2801 let dividend = TestInt::from(12345u32);
2803 let divisor = TestInt::from(67u32);
2804 let quotient = dividend / divisor;
2805 let remainder = dividend % divisor;
2806
2807 assert_eq!(quotient * divisor + remainder, dividend);
2809 }
2810
2811 #[test]
2812 fn test_rem_assign_correctness_after_fix() {
2813 type TestInt = FixedUInt<u32, 2>;
2814
2815 let mut a = TestInt::from(17u32);
2817 let b = TestInt::from(5u32);
2818
2819 a %= b;
2822 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2823
2824 let mut test_val = TestInt::from(100u32);
2826 test_val %= TestInt::from(7u32);
2827 assert_eq!(
2828 test_val,
2829 TestInt::from(2u32),
2830 "100 % 7 should be 2 (not 14, the quotient)"
2831 );
2832 }
2833
2834 #[test]
2835 fn test_div_property_based() {
2836 type TestInt = FixedUInt<u16, 2>;
2837
2838 let test_pairs = [
2840 (12345u16, 67u16),
2841 (1000u16, 13u16),
2842 (65535u16, 255u16),
2843 (5000u16, 7u16),
2844 ];
2845
2846 for (dividend_val, divisor_val) in test_pairs {
2847 let dividend = TestInt::from(dividend_val);
2848 let divisor = TestInt::from(divisor_val);
2849
2850 let quotient = dividend / divisor;
2851
2852 let remainder = dividend - (quotient * divisor);
2854 let reconstructed = quotient * divisor + remainder;
2855
2856 assert_eq!(
2857 reconstructed,
2858 dividend,
2859 "Property failed for {} / {}: {} * {} + {} != {}",
2860 dividend_val,
2861 divisor_val,
2862 quotient.to_u32().unwrap_or(0),
2863 divisor_val,
2864 remainder.to_u32().unwrap_or(0),
2865 dividend_val
2866 );
2867
2868 assert!(
2870 remainder < divisor,
2871 "Remainder {} >= divisor {} for {} / {}",
2872 remainder.to_u32().unwrap_or(0),
2873 divisor_val,
2874 dividend_val,
2875 divisor_val
2876 );
2877 }
2878 }
2879}