1use num_traits::{ToPrimitive, Zero};
16
17use core::convert::TryFrom;
18use core::fmt::Write;
19
20pub use crate::const_numtraits::{
21 ConstAbsDiff, ConstBorrowingSub, ConstBounded, ConstCarryingAdd, ConstCarryingMul,
22 ConstCheckedPow, ConstDivCeil, ConstIlog, ConstIsqrt, ConstMultiple, ConstOne, ConstPowerOfTwo,
23 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_div_impl;
42mod multiple_impl;
43mod num_integer_impl;
44mod num_traits_casts;
45mod num_traits_identity;
46mod power_of_two_impl;
47mod prim_int_impl;
48mod roots_impl;
49mod string_conversion;
50#[cfg(feature = "nightly")]
52mod const_to_from_bytes;
53#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
55mod to_from_bytes;
56
57#[cfg(feature = "zeroize")]
58use zeroize::DefaultIsZeroes;
59
60#[derive(Debug, Copy)]
62pub struct FixedUInt<T, const N: usize>
63where
64 T: MachineWord,
65{
66 pub(super) array: [T; N],
68}
69
70#[cfg(feature = "zeroize")]
71impl<T: MachineWord, const N: usize> DefaultIsZeroes for FixedUInt<T, N> {}
72
73impl<T, const N: usize> From<[T; N]> for FixedUInt<T, N>
74where
75 T: MachineWord,
76{
77 fn from(array: [T; N]) -> Self {
78 Self { array }
79 }
80}
81
82const LONGEST_WORD_IN_BITS: usize = 128;
83
84impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
85 const WORD_SIZE: usize = core::mem::size_of::<T>();
86 const WORD_BITS: usize = Self::WORD_SIZE * 8;
87 const BYTE_SIZE: usize = Self::WORD_SIZE * N;
88 const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
89
90 pub fn new() -> FixedUInt<T, N> {
92 FixedUInt {
93 array: [T::zero(); N],
94 }
95 }
96
97 pub fn words(&self) -> &[T; N] {
99 &self.array
100 }
101
102 pub fn bit_length(&self) -> u32 {
104 Self::BIT_SIZE as u32 - ConstPrimInt::leading_zeros(*self)
105 }
106
107 pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
109 let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
110 (Self { array: quotient }, Self { array: remainder })
111 }
112}
113
114c0nst::c0nst! {
116 pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
119 bytes: &[u8],
120 ) -> [T; N] {
121 let word_size = core::mem::size_of::<T>();
122 let mut ret: [T; N] = [T::zero(); N];
123 let capacity = N * word_size;
124 let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
125
126 let mut byte_index = 0;
127 while byte_index < total_bytes {
128 let word_index = byte_index / word_size;
129 let byte_in_word = byte_index % word_size;
130
131 let byte_value: T = T::from(bytes[byte_index]);
132 let shifted_value = byte_value.shl(byte_in_word * 8);
133 ret[word_index] = ret[word_index].bitor(shifted_value);
134 byte_index += 1;
135 }
136 ret
137 }
138
139 pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
142 bytes: &[u8],
143 ) -> [T; N] {
144 let word_size = core::mem::size_of::<T>();
145 let mut ret: [T; N] = [T::zero(); N];
146 let capacity_bytes = N * word_size;
147 let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
148
149 let start_offset = if bytes.len() > capacity_bytes {
152 bytes.len() - capacity_bytes
153 } else {
154 0
155 };
156
157 let mut byte_index = 0;
158 while byte_index < total_bytes {
159 let be_byte_index = start_offset + total_bytes - 1 - byte_index;
161 let word_index = byte_index / word_size;
162 let byte_in_word = byte_index % word_size;
163
164 let byte_value: T = T::from(bytes[be_byte_index]);
165 let shifted_value = byte_value.shl(byte_in_word * 8);
166 ret[word_index] = ret[word_index].bitor(shifted_value);
167 byte_index += 1;
168 }
169 ret
170 }
171}
172
173impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
175 pub fn from_le_bytes(bytes: &[u8]) -> Self {
177 Self {
178 array: impl_from_le_bytes_slice::<T, N>(bytes),
179 }
180 }
181
182 pub fn from_be_bytes(bytes: &[u8]) -> Self {
184 Self {
185 array: impl_from_be_bytes_slice::<T, N>(bytes),
186 }
187 }
188}
189
190impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
191 pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
193 let total_bytes = N * Self::WORD_SIZE;
194 if output_buffer.len() < total_bytes {
195 return Err(false); }
197 for (i, word) in self.array.iter().enumerate() {
198 let start = i * Self::WORD_SIZE;
199 let end = start + Self::WORD_SIZE;
200 let word_bytes = word.to_le_bytes();
201 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
202 }
203 Ok(&output_buffer[..total_bytes])
204 }
205
206 pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
208 let total_bytes = N * Self::WORD_SIZE;
209 if output_buffer.len() < total_bytes {
210 return Err(false); }
212 for (i, word) in self.array.iter().rev().enumerate() {
213 let start = i * Self::WORD_SIZE;
214 let end = start + Self::WORD_SIZE;
215 let word_bytes = word.to_be_bytes();
216 output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
217 }
218 Ok(&output_buffer[..total_bytes])
219 }
220
221 pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
223 type Error = core::fmt::Error;
224
225 let word_size = Self::WORD_SIZE;
226 let need_bits = self.bit_length() as usize;
228 let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
230
231 if result.len() < need_chars {
232 return Err(Error {});
234 }
235 let offset = result.len() - need_chars;
236 for i in result.iter_mut() {
237 *i = b'0';
238 }
239
240 for iter_words in 0..self.array.len() {
241 let word = self.array[iter_words];
242 let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
243 let encode_slice = &mut encoded[0..word_size * 2];
244 let mut wordbytes = word.to_le_bytes();
245 wordbytes.as_mut().reverse();
246 let wordslice = wordbytes.as_ref();
247 to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
248 for iter_chars in 0..encode_slice.len() {
249 let copy_char_to = (iter_words * word_size * 2) + iter_chars;
250 if copy_char_to <= need_chars {
251 let reverse_index = offset + (need_chars - copy_char_to);
252 if reverse_index <= result.len() && reverse_index > 0 {
253 let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
254 result[reverse_index - 1] = current_char;
255 }
256 }
257 }
258 }
259
260 let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
261 let pos = convert.find(|c: char| c != '0');
262 match pos {
263 Some(x) => Ok(&convert[x..convert.len()]),
264 None => {
265 if convert.starts_with('0') {
266 Ok("0")
267 } else {
268 Ok(convert)
269 }
270 }
271 }
272 }
273
274 pub fn to_radix_str<'a>(
276 &self,
277 result: &'a mut [u8],
278 radix: u8,
279 ) -> Result<&'a str, core::fmt::Error> {
280 type Error = core::fmt::Error;
281
282 if !(2..=16).contains(&radix) {
283 return Err(Error {}); }
285 for byte in result.iter_mut() {
286 *byte = b'0';
287 }
288 if Zero::is_zero(self) {
289 if !result.is_empty() {
290 result[0] = b'0';
291 return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
292 } else {
293 return Err(Error {});
294 }
295 }
296
297 let mut number = *self;
298 let mut idx = result.len();
299
300 let radix_t = Self::from(radix);
301
302 while !Zero::is_zero(&number) {
303 if idx == 0 {
304 return Err(Error {}); }
306
307 idx -= 1;
308 let (quotient, remainder) = number.div_rem(&radix_t);
309
310 let digit = remainder.to_u8().unwrap();
311 result[idx] = match digit {
312 0..=9 => b'0' + digit, 10..=16 => b'a' + (digit - 10), _ => return Err(Error {}),
315 };
316
317 number = quotient;
318 }
319
320 let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
321 let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
322 Ok(radix_str)
323 }
324
325 fn hex_fmt(
326 &self,
327 formatter: &mut core::fmt::Formatter<'_>,
328 uppercase: bool,
329 ) -> Result<(), core::fmt::Error>
330 where
331 u8: core::convert::TryFrom<T>,
332 {
333 type Err = core::fmt::Error;
334
335 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
336 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
337 if uppercase {
338 digit.to_uppercase().next().ok_or(Err {})
339 } else {
340 digit.to_lowercase().next().ok_or(Err {})
341 }
342 }
343
344 let mut leading_zero: bool = true;
345
346 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
347 leading_zero &= nibble == '0';
348 if !leading_zero {
349 formatter.write_char(nibble)?;
350 }
351 Ok(())
352 };
353
354 for index in (0..N).rev() {
355 let val = self.array[index];
356 let mask: T = 0xff.into();
357 for j in (0..Self::WORD_SIZE as u32).rev() {
358 let masked = val & mask.shl((j * 8) as usize);
359
360 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
361
362 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
363 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
364 }
365 }
366 Ok(())
367 }
368}
369
370c0nst::c0nst! {
371 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
373 target: &mut [T; N],
374 other: &[T; N]
375 ) -> bool {
376 let mut carry = T::zero();
377 let mut i = 0usize;
378 while i < N {
379 let (res, carry1) = target[i].overflowing_add(&other[i]);
380 let (res, carry2) = res.overflowing_add(&carry);
381 carry = if carry1 || carry2 {
382 T::one()
383 } else {
384 T::zero()
385 };
386 target[i] = res;
387 i += 1;
388 }
389 !carry.is_zero()
390 }
391}
392
393c0nst::c0nst! {
394 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
396 target: &mut [T; N],
397 other: &[T; N]
398 ) -> bool {
399 let mut borrow = T::zero();
400 let mut i = 0usize;
401 while i < N {
402 let (res, borrow1) = target[i].overflowing_sub(&other[i]);
403 let (res, borrow2) = res.overflowing_sub(&borrow);
404 borrow = if borrow1 || borrow2 {
405 T::one()
406 } else {
407 T::zero()
408 };
409 target[i] = res;
410 i += 1;
411 }
412 !borrow.is_zero()
413 }
414}
415
416c0nst::c0nst! {
417 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
419 target: &mut FixedUInt<T, N>,
420 bits: usize,
421 ) {
422 if N == 0 {
423 return;
424 }
425 let word_bits = FixedUInt::<T, N>::WORD_BITS;
426 let nwords = bits / word_bits;
427 let nbits = bits - nwords * word_bits;
428
429 if nwords >= N {
431 let mut i = 0;
432 while i < N {
433 target.array[i] = T::zero();
434 i += 1;
435 }
436 return;
437 }
438
439 let mut i = N;
441 while i > nwords {
442 i -= 1;
443 target.array[i] = target.array[i - nwords];
444 }
445 let mut i = 0;
447 while i < nwords {
448 target.array[i] = T::zero();
449 i += 1;
450 }
451
452 if nbits != 0 {
453 let mut i = N;
455 while i > 1 {
456 i -= 1;
457 let right = target.array[i] << nbits;
458 let left = target.array[i - 1] >> (word_bits - nbits);
459 target.array[i] = right | left;
460 }
461 target.array[0] <<= nbits;
462 }
463 }
464
465 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
467 target: &mut FixedUInt<T, N>,
468 bits: usize,
469 ) {
470 if N == 0 {
471 return;
472 }
473 let word_bits = FixedUInt::<T, N>::WORD_BITS;
474 let nwords = bits / word_bits;
475 let nbits = bits - nwords * word_bits;
476
477 if nwords >= N {
479 let mut i = 0;
480 while i < N {
481 target.array[i] = T::zero();
482 i += 1;
483 }
484 return;
485 }
486
487 let last_index = N - 1;
488 let last_word = N - nwords;
489
490 let mut i = 0;
492 while i < last_word {
493 target.array[i] = target.array[i + nwords];
494 i += 1;
495 }
496
497 let mut i = last_word;
499 while i < N {
500 target.array[i] = T::zero();
501 i += 1;
502 }
503
504 if nbits != 0 {
505 let mut i = 0;
507 while i < last_index {
508 let left = target.array[i] >> nbits;
509 let right = target.array[i + 1] << (word_bits - nbits);
510 target.array[i] = left | right;
511 i += 1;
512 }
513 target.array[last_index] >>= nbits;
514 }
515 }
516
517 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool>(
520 op1: &[T; N],
521 op2: &[T; N],
522 word_bits: usize,
523 ) -> ([T; N], bool) {
524 let mut result: [T; N] = [<T as ConstZero>::zero(); N];
525 let mut overflowed = false;
526 let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
527 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
529
530 let mut i = 0;
531 while i < N {
532 let mut carry = dw_zero;
533 let mut j = 0;
534 while j < N {
535 let round = i + j;
536 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
537 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
538 let mul_res = op1_dw * op2_dw;
539 let mut accumulator = if round < N {
540 <T as ConstMachineWord>::to_double(result[round])
541 } else {
542 dw_zero
543 };
544 accumulator = accumulator + mul_res + carry;
545
546 if accumulator > t_max {
547 carry = accumulator >> word_bits;
548 accumulator &= t_max;
549 } else {
550 carry = dw_zero;
551 }
552 if round < N {
553 result[round] = <T as ConstMachineWord>::from_double(accumulator);
554 } else if CHECK_OVERFLOW {
555 overflowed = overflowed || accumulator != dw_zero;
556 }
557 j += 1;
558 }
559 if carry != dw_zero && CHECK_OVERFLOW {
560 overflowed = true;
561 }
562 i += 1;
563 }
564 (result, overflowed)
565 }
566
567 pub(crate) c0nst fn const_word_bits<T>() -> usize {
569 core::mem::size_of::<T>() * 8
570 }
571
572 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
574 if a > b {
575 Some(core::cmp::Ordering::Greater)
576 } else if a < b {
577 Some(core::cmp::Ordering::Less)
578 } else {
579 None
580 }
581 }
582
583 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
585 array: &[T; N],
586 ) -> u32 {
587 let mut ret = 0u32;
588 let mut index = N;
589 while index > 0 {
590 index -= 1;
591 let v = array[index];
592 ret += <T as ConstPrimInt>::leading_zeros(v);
593 if !<T as ConstZero>::is_zero(&v) {
594 break;
595 }
596 }
597 ret
598 }
599
600 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
602 array: &[T; N],
603 ) -> u32 {
604 let mut ret = 0u32;
605 let mut index = 0;
606 while index < N {
607 let v = array[index];
608 ret += <T as ConstPrimInt>::trailing_zeros(v);
609 if !<T as ConstZero>::is_zero(&v) {
610 break;
611 }
612 index += 1;
613 }
614 ret
615 }
616
617 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
619 array: &[T; N],
620 ) -> usize {
621 let word_bits = const_word_bits::<T>();
622 let bit_size = N * word_bits;
623 bit_size - const_leading_zeros::<T, N>(array) as usize
624 }
625
626 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
628 array: &[T; N],
629 ) -> bool {
630 let mut index = 0;
631 while index < N {
632 if !<T as ConstZero>::is_zero(&array[index]) {
633 return false;
634 }
635 index += 1;
636 }
637 true
638 }
639
640 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
646 array: &mut [T; N],
647 pos: usize,
648 ) {
649 let word_bits = const_word_bits::<T>();
650 let word_idx = pos / word_bits;
651 if word_idx >= N {
652 return;
653 }
654 let bit_idx = pos % word_bits;
655 array[word_idx] |= <T as ConstOne>::one() << bit_idx;
656 }
657
658 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
663 a: &[T; N],
664 b: &[T; N],
665 ) -> core::cmp::Ordering {
666 let mut index = N;
667 while index > 0 {
668 index -= 1;
669 if let Some(ord) = const_cmp_words(a[index], b[index]) {
670 return ord;
671 }
672 }
673 core::cmp::Ordering::Equal
674 }
675
676 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
681 array: &[T; N],
682 word_idx: usize,
683 word_shift: usize,
684 bit_shift: usize,
685 ) -> T {
686 let word_bits = const_word_bits::<T>();
687
688 if bit_shift >= word_bits {
690 return <T as ConstZero>::zero();
691 }
692
693 if word_idx < word_shift {
694 return <T as ConstZero>::zero();
695 }
696
697 let source_idx = word_idx - word_shift;
698
699 if bit_shift == 0 {
700 if source_idx < N {
701 array[source_idx]
702 } else {
703 <T as ConstZero>::zero()
704 }
705 } else {
706 let mut result = <T as ConstZero>::zero();
707
708 if source_idx < N {
710 result |= array[source_idx] << bit_shift;
711 }
712
713 if source_idx > 0 && source_idx - 1 < N {
715 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
716 result |= high_bits;
717 }
718
719 result
720 }
721 }
722
723 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
728 array: &[T; N],
729 other: &[T; N],
730 shift_bits: usize,
731 ) -> core::cmp::Ordering {
732 let word_bits = const_word_bits::<T>();
733
734 if shift_bits == 0 {
735 return const_cmp::<T, N>(array, other);
736 }
737
738 let word_shift = shift_bits / word_bits;
739 if word_shift >= N {
740 if const_is_zero::<T, N>(array) {
742 return core::cmp::Ordering::Equal;
743 } else {
744 return core::cmp::Ordering::Greater;
745 }
746 }
747
748 let bit_shift = shift_bits % word_bits;
749
750 let mut index = N;
752 while index > 0 {
753 index -= 1;
754 let self_word = array[index];
755 let other_shifted_word = const_get_shifted_word::<T, N>(
756 other, index, word_shift, bit_shift
757 );
758
759 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
760 return ord;
761 }
762 }
763
764 core::cmp::Ordering::Equal
765 }
766
767 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
772 array: &mut [T; N],
773 other: &[T; N],
774 shift_bits: usize,
775 ) {
776 let word_bits = const_word_bits::<T>();
777
778 if shift_bits == 0 {
779 sub_impl::<T, N>(array, other);
780 return;
781 }
782
783 let word_shift = shift_bits / word_bits;
784 if word_shift >= N {
785 return;
786 }
787
788 let bit_shift = shift_bits % word_bits;
789 let mut borrow = T::zero();
790 let mut index = 0;
791 while index < N {
792 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
793 let (res, borrow1) = array[index].overflowing_sub(&other_word);
794 let (res, borrow2) = res.overflowing_sub(&borrow);
795 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
796 array[index] = res;
797 index += 1;
798 }
799 }
800
801 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
805 dividend: &mut [T; N],
806 divisor: &[T; N],
807 ) -> [T; N] {
808 use core::cmp::Ordering;
809
810 match const_cmp::<T, N>(dividend, divisor) {
811 Ordering::Less => {
813 let remainder = *dividend;
814 let mut i = 0;
815 while i < N {
816 dividend[i] = <T as ConstZero>::zero();
817 i += 1;
818 }
819 return remainder;
820 }
821 Ordering::Equal => {
823 let mut i = 0;
824 while i < N {
825 dividend[i] = <T as ConstZero>::zero();
826 i += 1;
827 }
828 if N > 0 {
829 dividend[0] = <T as ConstOne>::one();
830 }
831 return [<T as ConstZero>::zero(); N];
832 }
833 Ordering::Greater => {}
834 }
835
836 let mut quotient = [<T as ConstZero>::zero(); N];
837
838 let dividend_bits = const_bit_length::<T, N>(dividend);
840 let divisor_bits = const_bit_length::<T, N>(divisor);
841
842 let mut bit_pos = if dividend_bits >= divisor_bits {
843 dividend_bits - divisor_bits
844 } else {
845 0
846 };
847
848 while bit_pos > 0 {
850 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
851 if !matches!(cmp, Ordering::Less) {
852 break;
853 }
854 bit_pos -= 1;
855 }
856
857 loop {
859 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
860 if !matches!(cmp, Ordering::Less) {
861 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
862 const_set_bit::<T, N>(&mut quotient, bit_pos);
863 }
864
865 if bit_pos == 0 {
866 break;
867 }
868 bit_pos -= 1;
869 }
870
871 let remainder = *dividend;
872 *dividend = quotient;
873 remainder
874 }
875
876 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
880 dividend: &[T; N],
881 divisor: &[T; N],
882 ) -> ([T; N], [T; N]) {
883 if const_is_zero(divisor) {
884 maybe_panic(PanicReason::DivByZero)
885 }
886 let mut quotient = *dividend;
887 let remainder = const_div(&mut quotient, divisor);
888 (quotient, remainder)
889 }
890}
891
892c0nst::c0nst! {
893 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Default for FixedUInt<T, N> {
894 fn default() -> Self {
895 <Self as ConstZero>::zero()
896 }
897 }
898
899 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Clone for FixedUInt<T, N> {
900 fn clone(&self) -> Self {
901 *self
902 }
903 }
904}
905
906impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
907
908c0nst::c0nst! {
911 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialEq for FixedUInt<T, N> {
912 fn eq(&self, other: &Self) -> bool {
913 self.array == other.array
914 }
915 }
916
917 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Eq for FixedUInt<T, N> {}
918
919 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Ord for FixedUInt<T, N> {
920 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
921 const_cmp(&self.array, &other.array)
922 }
923 }
924
925 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialOrd for FixedUInt<T, N> {
926 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
927 Some(self.cmp(other))
928 }
929 }
930}
931
932c0nst::c0nst! {
937 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
940 bytes: [u8; B],
941 ) -> [T; N] {
942 impl_from_le_bytes_slice::<T, N>(&bytes)
943 }
944
945 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u8> for FixedUInt<T, N> {
946 fn from(x: u8) -> Self {
947 Self { array: const_from_le_bytes(x.to_le_bytes()) }
948 }
949 }
950
951 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u16> for FixedUInt<T, N> {
952 fn from(x: u16) -> Self {
953 Self { array: const_from_le_bytes(x.to_le_bytes()) }
954 }
955 }
956
957 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u32> for FixedUInt<T, N> {
958 fn from(x: u32) -> Self {
959 Self { array: const_from_le_bytes(x.to_le_bytes()) }
960 }
961 }
962
963 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u64> for FixedUInt<T, N> {
964 fn from(x: u64) -> Self {
965 Self { array: const_from_le_bytes(x.to_le_bytes()) }
966 }
967 }
968}
969
970fn make_parse_int_err() -> core::num::ParseIntError {
977 <u8>::from_str_radix("-", 2).err().unwrap()
978}
979fn make_overflow_err() -> core::num::ParseIntError {
980 <u8>::from_str_radix("101", 16).err().unwrap()
981}
982fn make_empty_error() -> core::num::ParseIntError {
983 <u8>::from_str_radix("", 8).err().unwrap()
984}
985
986fn to_slice_hex<T: AsRef<[u8]>>(
987 input: T,
988 output: &mut [u8],
989) -> Result<(), core::num::ParseIntError> {
990 fn from_digit(byte: u8) -> Option<char> {
991 core::char::from_digit(byte as u32, 16)
992 }
993 let r = input.as_ref();
994 if r.len() * 2 != output.len() {
995 return Err(make_parse_int_err());
996 }
997 for i in 0..r.len() {
998 let byte = r[i];
999 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1000 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1001 }
1002
1003 Ok(())
1004}
1005
1006pub(super) enum PanicReason {
1007 Add,
1008 Sub,
1009 Mul,
1010 DivByZero,
1011}
1012
1013c0nst::c0nst! {
1014 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1015 match r {
1016 PanicReason::Add => panic!("attempt to add with overflow"),
1017 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1018 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1019 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1020 }
1021 }
1022}
1023
1024#[cfg(test)]
1027mod tests {
1028 use super::FixedUInt as Bn;
1029 use super::*;
1030 use num_traits::One;
1031
1032 type Bn8 = Bn<u8, 8>;
1033 type Bn16 = Bn<u16, 4>;
1034 type Bn32 = Bn<u32, 2>;
1035
1036 c0nst::c0nst! {
1037 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1038 a: &mut [T; N],
1039 b: &[T; N]
1040 ) -> bool {
1041 add_impl(a, b)
1042 }
1043
1044 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1045 a: &mut [T; N],
1046 b: &[T; N]
1047 ) -> bool {
1048 sub_impl(a, b)
1049 }
1050
1051 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1052 a: &[T; N],
1053 b: &[T; N],
1054 word_bits: usize,
1055 ) -> ([T; N], bool) {
1056 const_mul::<T, N, true>(a, b, word_bits)
1057 }
1058
1059 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1060 a: &[T; N],
1061 ) -> u32 {
1062 const_leading_zeros::<T, N>(a)
1063 }
1064
1065 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1066 a: &[T; N],
1067 ) -> u32 {
1068 const_trailing_zeros::<T, N>(a)
1069 }
1070
1071 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1072 a: &[T; N],
1073 ) -> usize {
1074 const_bit_length::<T, N>(a)
1075 }
1076
1077 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1078 a: &[T; N],
1079 ) -> bool {
1080 const_is_zero::<T, N>(a)
1081 }
1082
1083 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1084 a: &mut [T; N],
1085 pos: usize,
1086 ) {
1087 const_set_bit::<T, N>(a, pos)
1088 }
1089
1090 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1091 a: &[T; N],
1092 b: &[T; N],
1093 ) -> core::cmp::Ordering {
1094 const_cmp::<T, N>(a, b)
1095 }
1096
1097 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1098 a: &[T; N],
1099 b: &[T; N],
1100 shift_bits: usize,
1101 ) -> core::cmp::Ordering {
1102 const_cmp_shifted::<T, N>(a, b, shift_bits)
1103 }
1104
1105 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1106 a: &[T; N],
1107 word_idx: usize,
1108 word_shift: usize,
1109 bit_shift: usize,
1110 ) -> T {
1111 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1112 }
1113 }
1114
1115 #[test]
1116 fn test_const_add_impl() {
1117 let mut a: [u8; 4] = [1, 0, 0, 0];
1119 let b: [u8; 4] = [2, 0, 0, 0];
1120 let overflow = test_add(&mut a, &b);
1121 assert_eq!(a, [3, 0, 0, 0]);
1122 assert!(!overflow);
1123
1124 let mut a: [u8; 4] = [255, 0, 0, 0];
1126 let b: [u8; 4] = [1, 0, 0, 0];
1127 let overflow = test_add(&mut a, &b);
1128 assert_eq!(a, [0, 1, 0, 0]);
1129 assert!(!overflow);
1130
1131 let mut a: [u8; 4] = [255, 255, 255, 255];
1133 let b: [u8; 4] = [1, 0, 0, 0];
1134 let overflow = test_add(&mut a, &b);
1135 assert_eq!(a, [0, 0, 0, 0]);
1136 assert!(overflow);
1137
1138 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1140 let b: [u32; 2] = [1, 0];
1141 let overflow = test_add(&mut a, &b);
1142 assert_eq!(a, [0, 1]);
1143 assert!(!overflow);
1144
1145 #[cfg(feature = "nightly")]
1146 {
1147 const ADD_RESULT: ([u8; 4], bool) = {
1148 let mut a = [1u8, 0, 0, 0];
1149 let b = [2u8, 0, 0, 0];
1150 let overflow = test_add(&mut a, &b);
1151 (a, overflow)
1152 };
1153 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1154 }
1155 }
1156
1157 #[test]
1158 fn test_const_sub_impl() {
1159 let mut a: [u8; 4] = [3, 0, 0, 0];
1161 let b: [u8; 4] = [1, 0, 0, 0];
1162 let overflow = test_sub(&mut a, &b);
1163 assert_eq!(a, [2, 0, 0, 0]);
1164 assert!(!overflow);
1165
1166 let mut a: [u8; 4] = [0, 1, 0, 0];
1168 let b: [u8; 4] = [1, 0, 0, 0];
1169 let overflow = test_sub(&mut a, &b);
1170 assert_eq!(a, [255, 0, 0, 0]);
1171 assert!(!overflow);
1172
1173 let mut a: [u8; 4] = [0, 0, 0, 0];
1175 let b: [u8; 4] = [1, 0, 0, 0];
1176 let overflow = test_sub(&mut a, &b);
1177 assert_eq!(a, [255, 255, 255, 255]);
1178 assert!(overflow);
1179
1180 let mut a: [u32; 2] = [0, 1];
1182 let b: [u32; 2] = [1, 0];
1183 let overflow = test_sub(&mut a, &b);
1184 assert_eq!(a, [0xFFFFFFFF, 0]);
1185 assert!(!overflow);
1186
1187 #[cfg(feature = "nightly")]
1188 {
1189 const SUB_RESULT: ([u8; 4], bool) = {
1190 let mut a = [3u8, 0, 0, 0];
1191 let b = [1u8, 0, 0, 0];
1192 let overflow = test_sub(&mut a, &b);
1193 (a, overflow)
1194 };
1195 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1196 }
1197 }
1198
1199 #[test]
1200 fn test_const_mul_impl() {
1201 let a: [u8; 2] = [3, 0];
1203 let b: [u8; 2] = [4, 0];
1204 let (result, overflow) = test_mul(&a, &b, 8);
1205 assert_eq!(result, [12, 0]);
1206 assert!(!overflow);
1207
1208 let a: [u8; 2] = [200, 0];
1210 let b: [u8; 2] = [2, 0];
1211 let (result, overflow) = test_mul(&a, &b, 8);
1212 assert_eq!(result, [0x90, 0x01]);
1213 assert!(!overflow);
1214
1215 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1219 assert!(overflow);
1220
1221 let a: [u8; 3] = [0, 0, 1];
1225 let b: [u8; 3] = [0, 0, 1];
1226 let (_result, overflow) = test_mul(&a, &b, 8);
1227 assert!(overflow, "N=3 high-position overflow not detected");
1228
1229 let a: [u8; 3] = [0, 0, 2];
1233 let b: [u8; 3] = [0, 0, 2];
1234 let (_result, overflow) = test_mul(&a, &b, 8);
1235 assert!(
1236 overflow,
1237 "N=3 high-position overflow with larger values not detected"
1238 );
1239
1240 let a: [u8; 3] = [0, 1, 0];
1244 let b: [u8; 3] = [0, 1, 0];
1245 let (result, overflow) = test_mul(&a, &b, 8);
1246 assert_eq!(result, [0, 0, 1]);
1247 assert!(
1248 !overflow,
1249 "N=3 non-overflow incorrectly detected as overflow"
1250 );
1251
1252 let a: [u8; 3] = [255, 0, 0];
1256 let b: [u8; 3] = [255, 0, 0];
1257 let (result, overflow) = test_mul(&a, &b, 8);
1258 assert_eq!(result, [0x01, 0xFE, 0x00]);
1259 assert!(!overflow);
1260
1261 #[cfg(feature = "nightly")]
1262 {
1263 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1264 assert_eq!(MUL_RESULT, ([12, 0], false));
1265 }
1266 }
1267
1268 #[test]
1269 fn test_const_helpers() {
1270 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]));
1295 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1296 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1297 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1298
1299 let mut arr: [u8; 4] = [0, 0, 0, 0];
1301 arr_set_bit(&mut arr, 0);
1302 assert_eq!(arr, [1, 0, 0, 0]);
1303
1304 let mut arr: [u8; 4] = [0, 0, 0, 0];
1305 arr_set_bit(&mut arr, 8);
1306 assert_eq!(arr, [0, 1, 0, 0]);
1307
1308 let mut arr: [u8; 4] = [0, 0, 0, 0];
1309 arr_set_bit(&mut arr, 31);
1310 assert_eq!(arr, [0, 0, 0, 0x80]);
1311
1312 let mut arr: [u8; 4] = [0, 0, 0, 0];
1314 arr_set_bit(&mut arr, 0);
1315 arr_set_bit(&mut arr, 3);
1316 arr_set_bit(&mut arr, 8);
1317 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1318
1319 let mut arr: [u8; 4] = [0, 0, 0, 0];
1321 arr_set_bit(&mut arr, 32);
1322 assert_eq!(arr, [0, 0, 0, 0]);
1323
1324 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1326 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1327 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1328 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1329 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1330 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1331 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1332 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1333
1334 #[cfg(feature = "nightly")]
1335 {
1336 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1337 assert_eq!(LEADING, 15);
1338
1339 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1340 assert_eq!(TRAILING, 16);
1341
1342 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1343 assert_eq!(BIT_LEN, 17);
1344
1345 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1346 assert!(IS_ZERO);
1347
1348 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1349 assert!(!NOT_ZERO);
1350
1351 const SET_BIT_RESULT: [u8; 4] = {
1352 let mut arr = [0u8, 0, 0, 0];
1353 arr_set_bit(&mut arr, 10);
1354 arr
1355 };
1356 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1357 }
1358 }
1359
1360 #[test]
1361 fn test_const_cmp() {
1362 use core::cmp::Ordering;
1363
1364 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1366 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1367
1368 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1370
1371 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1373
1374 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1376
1377 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1379
1380 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1382 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1383 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1384
1385 #[cfg(feature = "nightly")]
1386 {
1387 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1388 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1389 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1390 assert_eq!(CMP_EQ, Ordering::Equal);
1391 assert_eq!(CMP_GT, Ordering::Greater);
1392 assert_eq!(CMP_LT, Ordering::Less);
1393 }
1394 }
1395
1396 #[test]
1397 fn test_const_cmp_shifted() {
1398 use core::cmp::Ordering;
1399
1400 assert_eq!(
1402 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1403 Ordering::Equal
1404 );
1405
1406 assert_eq!(
1408 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1409 Ordering::Equal
1410 );
1411
1412 assert_eq!(
1414 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1415 Ordering::Greater
1416 );
1417
1418 assert_eq!(
1420 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1421 Ordering::Less
1422 );
1423
1424 assert_eq!(
1427 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1428 Ordering::Greater
1429 );
1430
1431 assert_eq!(
1433 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1434 Ordering::Equal
1435 );
1436
1437 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
1441 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
1442 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
1443
1444 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1448 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1450
1451 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1454 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1456
1457 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1461 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1463 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
1465
1466 #[cfg(feature = "nightly")]
1467 {
1468 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
1469 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
1470 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
1471 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
1472 }
1473 }
1474
1475 #[test]
1476 fn test_core_convert_u8() {
1477 let f = Bn::<u8, 1>::from(1u8);
1478 assert_eq!(f.array, [1]);
1479 let f = Bn::<u8, 2>::from(1u8);
1480 assert_eq!(f.array, [1, 0]);
1481
1482 let f = Bn::<u16, 1>::from(1u8);
1483 assert_eq!(f.array, [1]);
1484 let f = Bn::<u16, 2>::from(1u8);
1485 assert_eq!(f.array, [1, 0]);
1486
1487 #[cfg(feature = "nightly")]
1488 {
1489 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
1490 assert_eq!(F1.array, [42, 0]);
1491 }
1492 }
1493
1494 #[test]
1495 fn test_core_convert_u16() {
1496 let f = Bn::<u8, 1>::from(1u16);
1497 assert_eq!(f.array, [1]);
1498 let f = Bn::<u8, 2>::from(1u16);
1499 assert_eq!(f.array, [1, 0]);
1500
1501 let f = Bn::<u8, 1>::from(256u16);
1502 assert_eq!(f.array, [0]);
1503 let f = Bn::<u8, 2>::from(257u16);
1504 assert_eq!(f.array, [1, 1]);
1505 let f = Bn::<u8, 2>::from(65535u16);
1506 assert_eq!(f.array, [255, 255]);
1507
1508 let f = Bn::<u16, 1>::from(1u16);
1509 assert_eq!(f.array, [1]);
1510 let f = Bn::<u16, 2>::from(1u16);
1511 assert_eq!(f.array, [1, 0]);
1512
1513 let f = Bn::<u16, 1>::from(65535u16);
1514 assert_eq!(f.array, [65535]);
1515
1516 #[cfg(feature = "nightly")]
1517 {
1518 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
1519 assert_eq!(F1.array, [0x02, 0x01]);
1520 }
1521 }
1522
1523 #[test]
1524 fn test_core_convert_u32() {
1525 let f = Bn::<u8, 1>::from(1u32);
1526 assert_eq!(f.array, [1]);
1527 let f = Bn::<u8, 1>::from(256u32);
1528 assert_eq!(f.array, [0]);
1529
1530 let f = Bn::<u8, 2>::from(1u32);
1531 assert_eq!(f.array, [1, 0]);
1532 let f = Bn::<u8, 2>::from(257u32);
1533 assert_eq!(f.array, [1, 1]);
1534 let f = Bn::<u8, 2>::from(65535u32);
1535 assert_eq!(f.array, [255, 255]);
1536
1537 let f = Bn::<u8, 4>::from(1u32);
1538 assert_eq!(f.array, [1, 0, 0, 0]);
1539 let f = Bn::<u8, 4>::from(257u32);
1540 assert_eq!(f.array, [1, 1, 0, 0]);
1541 let f = Bn::<u8, 4>::from(u32::max_value());
1542 assert_eq!(f.array, [255, 255, 255, 255]);
1543
1544 let f = Bn::<u8, 1>::from(1u32);
1545 assert_eq!(f.array, [1]);
1546 let f = Bn::<u8, 1>::from(256u32);
1547 assert_eq!(f.array, [0]);
1548
1549 let f = Bn::<u16, 2>::from(65537u32);
1550 assert_eq!(f.array, [1, 1]);
1551
1552 let f = Bn::<u32, 1>::from(1u32);
1553 assert_eq!(f.array, [1]);
1554 let f = Bn::<u32, 2>::from(1u32);
1555 assert_eq!(f.array, [1, 0]);
1556
1557 let f = Bn::<u32, 1>::from(65537u32);
1558 assert_eq!(f.array, [65537]);
1559
1560 let f = Bn::<u32, 1>::from(u32::max_value());
1561 assert_eq!(f.array, [4294967295]);
1562
1563 #[cfg(feature = "nightly")]
1564 {
1565 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
1566 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
1567 }
1568 }
1569
1570 #[test]
1571 fn test_core_convert_u64() {
1572 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
1573 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1574
1575 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
1576 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
1577
1578 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
1579 assert_eq!(f.array, [0x05060708, 0x01020304]);
1580
1581 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
1582 assert_eq!(f.array, [0x0102030405060708]);
1583
1584 #[cfg(feature = "nightly")]
1585 {
1586 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
1587 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1588 }
1589 }
1590
1591 #[test]
1592 fn testsimple() {
1593 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
1594
1595 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
1596 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
1597 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
1598 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
1599 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
1600 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
1601 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
1602 }
1603 #[test]
1604 fn testfrom() {
1605 let mut n1 = Bn::<u8, 8>::new();
1606 n1.array[0] = 1;
1607 assert_eq!(Some(1), n1.to_u32());
1608 n1.array[1] = 1;
1609 assert_eq!(Some(257), n1.to_u32());
1610
1611 let mut n2 = Bn::<u16, 8>::new();
1612 n2.array[0] = 0xffff;
1613 assert_eq!(Some(65535), n2.to_u32());
1614 n2.array[0] = 0x0;
1615 n2.array[2] = 0x1;
1616 assert_eq!(None, n2.to_u32());
1618 assert_eq!(Some(0x100000000), n2.to_u64());
1619 }
1620
1621 #[test]
1622 fn test_from_str_bitlengths() {
1623 let test_s64 = "81906f5e4d3c2c01";
1624 let test_u64: u64 = 0x81906f5e4d3c2c01;
1625 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
1626 let cc = Bn8::from_u64(test_u64).unwrap();
1627 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1628 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1629 let dd = Bn16::from_u64(test_u64).unwrap();
1630 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
1631 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1632 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1633 let ee = Bn32::from_u64(test_u64).unwrap();
1634 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
1635 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
1636 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
1637 }
1638
1639 #[test]
1640 fn test_from_str_stringlengths() {
1641 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
1642 assert_eq!(
1643 ab.array,
1644 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
1645 );
1646 assert_eq!(
1647 [0x2c01, 0x4d3c, 0x6f5e, 0],
1648 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
1649 .unwrap()
1650 .array
1651 );
1652 assert_eq!(
1653 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
1654 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
1655 .unwrap()
1656 .array
1657 );
1658 assert_eq!(
1659 Err(make_overflow_err()),
1660 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
1661 );
1662 assert_eq!(
1663 Err(make_overflow_err()),
1664 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
1665 );
1666 assert_eq!(
1667 Err(make_overflow_err()),
1668 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
1669 );
1670 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
1671 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
1672 }
1673
1674 #[test]
1675 fn test_bit_length() {
1676 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
1677 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
1678 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
1679 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
1680 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
1681 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
1682 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
1683
1684 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
1685 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
1686 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
1687 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
1688 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
1689
1690 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
1691 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
1692 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
1693 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
1694 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
1695 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
1696 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
1697
1698 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
1699 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
1700 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
1701 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
1702 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
1703 }
1704
1705 #[test]
1706 fn test_bit_length_1000() {
1707 let value = Bn32::from_u16(1000).unwrap();
1709
1710 assert_eq!(value.to_u32().unwrap(), 1000);
1713 assert_eq!(value.bit_length(), 10);
1714
1715 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);
1722 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1723
1724 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1726 assert_eq!(value_from_str.bit_length(), 10);
1727
1728 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1730 assert_eq!(
1732 value_from_bytes.to_u32().unwrap_or(0),
1733 1000,
1734 "from_le_bytes didn't create the correct value"
1735 );
1736 assert_eq!(value_from_bytes.bit_length(), 10);
1737 }
1738 #[test]
1739 fn test_cmp() {
1740 let f0 = <Bn8 as Zero>::zero();
1741 let f1 = <Bn8 as Zero>::zero();
1742 let f2 = <Bn8 as One>::one();
1743 assert_eq!(f0, f1);
1744 assert!(f2 > f0);
1745 assert!(f0 < f2);
1746 let f3 = Bn32::from_u64(990223).unwrap();
1747 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
1748 let f4 = Bn32::from_u64(990224).unwrap();
1749 assert!(f4 > Bn32::from_u64(990223).unwrap());
1750
1751 let f3 = Bn8::from_u64(990223).unwrap();
1752 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
1753 let f4 = Bn8::from_u64(990224).unwrap();
1754 assert!(f4 > Bn8::from_u64(990223).unwrap());
1755
1756 #[cfg(feature = "nightly")]
1757 {
1758 use core::cmp::Ordering;
1759
1760 const A: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1761 const B: FixedUInt<u8, 2> = FixedUInt { array: [20, 0] };
1762 const C: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1763
1764 const CMP_LT: Ordering = A.cmp(&B);
1765 const CMP_GT: Ordering = B.cmp(&A);
1766 const CMP_EQ: Ordering = A.cmp(&C);
1767 const EQ_TRUE: bool = A.eq(&C);
1768 const EQ_FALSE: bool = A.eq(&B);
1769
1770 assert_eq!(CMP_LT, Ordering::Less);
1771 assert_eq!(CMP_GT, Ordering::Greater);
1772 assert_eq!(CMP_EQ, Ordering::Equal);
1773 assert!(EQ_TRUE);
1774 assert!(!EQ_FALSE);
1775 }
1776 }
1777
1778 #[test]
1779 fn test_default() {
1780 let d: Bn8 = Default::default();
1781 assert!(Zero::is_zero(&d));
1782
1783 #[cfg(feature = "nightly")]
1784 {
1785 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
1786 assert!(Zero::is_zero(&D));
1787 }
1788 }
1789
1790 #[test]
1791 fn test_clone() {
1792 let a: Bn8 = 42u8.into();
1793 let b = a.clone();
1794 assert_eq!(a, b);
1795
1796 #[cfg(feature = "nightly")]
1797 {
1798 const A: FixedUInt<u8, 2> = FixedUInt { array: [42, 0] };
1799 const B: FixedUInt<u8, 2> = A.clone();
1800 assert_eq!(A.array, B.array);
1801 }
1802 }
1803
1804 #[test]
1805 fn test_le_be_bytes() {
1806 let le_bytes = [1, 2, 3, 4];
1807 let be_bytes = [4, 3, 2, 1];
1808 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1809 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1810 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1811 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1812 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1813 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1814
1815 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1816 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1817 assert_eq!(u32_ver.array, [0x04030201]);
1818 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1819 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1820 assert_eq!(u32_ver_be.array, [0x04030201]);
1821
1822 let mut output_buffer = [0u8; 16];
1823 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1824 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1825 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1826 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1827 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1828 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1829 }
1830
1831 #[test]
1833 fn test_div_small() {
1834 type TestInt = FixedUInt<u8, 2>;
1835
1836 let test_cases = [
1838 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
1843
1844 for (dividend_val, divisor_val, expected) in test_cases {
1845 let dividend = TestInt::from(dividend_val);
1846 let divisor = TestInt::from(divisor_val);
1847 let expected_result = TestInt::from(expected);
1848
1849 assert_eq!(
1850 dividend / divisor,
1851 expected_result,
1852 "Division failed for {} / {} = {}",
1853 dividend_val,
1854 divisor_val,
1855 expected
1856 );
1857 }
1858 }
1859
1860 #[test]
1861 fn test_div_edge_cases() {
1862 type TestInt = FixedUInt<u16, 2>;
1863
1864 let dividend = TestInt::from(1000u16);
1866 let divisor = TestInt::from(1u16);
1867 assert_eq!(dividend / divisor, TestInt::from(1000u16));
1868
1869 let dividend = TestInt::from(42u16);
1871 let divisor = TestInt::from(42u16);
1872 assert_eq!(dividend / divisor, TestInt::from(1u16));
1873
1874 let dividend = TestInt::from(5u16);
1876 let divisor = TestInt::from(10u16);
1877 assert_eq!(dividend / divisor, TestInt::from(0u16));
1878
1879 let dividend = TestInt::from(1024u16);
1881 let divisor = TestInt::from(4u16);
1882 assert_eq!(dividend / divisor, TestInt::from(256u16));
1883 }
1884
1885 #[test]
1886 fn test_helper_methods() {
1887 type TestInt = FixedUInt<u8, 2>;
1888
1889 let mut val = <TestInt as Zero>::zero();
1891 const_set_bit(&mut val.array, 0);
1892 assert_eq!(val, TestInt::from(1u8));
1893
1894 const_set_bit(&mut val.array, 8);
1895 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
1903 const_cmp_shifted(&a.array, &b.array, 3),
1904 core::cmp::Ordering::Equal
1905 );
1906
1907 assert_eq!(
1909 const_cmp_shifted(&a.array, &b.array, 2),
1910 core::cmp::Ordering::Greater
1911 );
1912
1913 assert_eq!(
1915 const_cmp_shifted(&a.array, &b.array, 4),
1916 core::cmp::Ordering::Less
1917 );
1918
1919 let mut val = TestInt::from(10u8);
1921 let one = TestInt::from(1u8);
1922 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
1925
1926 #[test]
1927 fn test_shifted_operations_comprehensive() {
1928 type TestInt = FixedUInt<u32, 2>;
1929
1930 let a = TestInt::from(0x12345678u32);
1932 let b = TestInt::from(0x12345678u32);
1933
1934 assert_eq!(
1936 const_cmp_shifted(&a.array, &b.array, 0),
1937 core::cmp::Ordering::Equal
1938 );
1939
1940 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
1946 const_cmp_shifted(&d.array, &c.array, 16),
1947 core::cmp::Ordering::Greater
1948 );
1949
1950 let e = TestInt::from(1u32);
1952 let zero = TestInt::from(0u32);
1953 assert_eq!(
1954 const_cmp_shifted(&e.array, &zero.array, 100),
1955 core::cmp::Ordering::Greater
1956 );
1957 assert_eq!(
1959 const_cmp_shifted(&zero.array, &e.array, 100),
1960 core::cmp::Ordering::Equal
1961 );
1962
1963 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
1966 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)); }
1974
1975 #[test]
1976 fn test_shifted_operations_edge_cases() {
1977 type TestInt = FixedUInt<u32, 2>;
1978
1979 let a = TestInt::from(42u32);
1981 let a2 = TestInt::from(42u32);
1982 assert_eq!(
1983 const_cmp_shifted(&a.array, &a2.array, 0),
1984 core::cmp::Ordering::Equal
1985 );
1986
1987 let mut b = TestInt::from(42u32);
1988 let ten = TestInt::from(10u32);
1989 const_sub_shifted(&mut b.array, &ten.array, 0);
1990 assert_eq!(b, TestInt::from(32u32));
1991
1992 let c = TestInt::from(123u32);
1994 let large = TestInt::from(456u32);
1995 assert_eq!(
1996 const_cmp_shifted(&c.array, &large.array, 200),
1997 core::cmp::Ordering::Greater
1998 );
1999
2000 let mut d = TestInt::from(123u32);
2001 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2003
2004 let zero = TestInt::from(0u32);
2006 let one = TestInt::from(1u32);
2007 assert_eq!(
2008 const_cmp_shifted(&zero.array, &zero.array, 10),
2009 core::cmp::Ordering::Equal
2010 );
2011 assert_eq!(
2012 const_cmp_shifted(&one.array, &zero.array, 10),
2013 core::cmp::Ordering::Greater
2014 );
2015 }
2016
2017 #[test]
2018 fn test_shifted_operations_equivalence() {
2019 type TestInt = FixedUInt<u32, 2>;
2020
2021 let test_cases = [
2023 (0x12345u32, 0x678u32, 4),
2024 (0x1000u32, 0x10u32, 8),
2025 (0xABCDu32, 0x1u32, 16),
2026 (0x80000000u32, 0x1u32, 1),
2027 ];
2028
2029 for (a_val, b_val, shift) in test_cases {
2030 let a = TestInt::from(a_val);
2031 let b = TestInt::from(b_val);
2032
2033 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2035 let naive_cmp = a.cmp(&(b << shift));
2036 assert_eq!(
2037 optimized_cmp, naive_cmp,
2038 "cmp_shifted mismatch: {} vs ({} << {})",
2039 a_val, b_val, shift
2040 );
2041
2042 if a >= (b << shift) {
2044 let mut optimized_result = a;
2045 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2046
2047 let naive_result = a - (b << shift);
2048 assert_eq!(
2049 optimized_result, naive_result,
2050 "sub_shifted mismatch: {} - ({} << {})",
2051 a_val, b_val, shift
2052 );
2053 }
2054 }
2055 }
2056
2057 #[test]
2058 fn test_div_assign_in_place_optimization() {
2059 type TestInt = FixedUInt<u32, 2>;
2060
2061 let test_cases = [
2063 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2068
2069 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2070 let mut dividend = TestInt::from(dividend_val);
2072 let divisor = TestInt::from(divisor_val);
2073
2074 dividend /= divisor;
2075 assert_eq!(
2076 dividend,
2077 TestInt::from(expected_quotient),
2078 "div_assign: {} / {} should be {}",
2079 dividend_val,
2080 divisor_val,
2081 expected_quotient
2082 );
2083
2084 let dividend2 = TestInt::from(dividend_val);
2086 let (quotient, remainder) = dividend2.div_rem(&divisor);
2087 assert_eq!(
2088 quotient,
2089 TestInt::from(expected_quotient),
2090 "div_rem quotient: {} / {} should be {}",
2091 dividend_val,
2092 divisor_val,
2093 expected_quotient
2094 );
2095 assert_eq!(
2096 remainder,
2097 TestInt::from(expected_remainder),
2098 "div_rem remainder: {} % {} should be {}",
2099 dividend_val,
2100 divisor_val,
2101 expected_remainder
2102 );
2103
2104 assert_eq!(
2106 quotient * divisor + remainder,
2107 TestInt::from(dividend_val),
2108 "Property check failed for {}",
2109 dividend_val
2110 );
2111 }
2112 }
2113
2114 #[test]
2115 fn test_div_assign_stack_efficiency() {
2116 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2120 let divisor = TestInt::from(0x12345u32);
2121 let original_dividend = dividend;
2122
2123 dividend /= divisor;
2125
2126 let remainder = original_dividend % divisor;
2128 assert_eq!(dividend * divisor + remainder, original_dividend);
2129 }
2130
2131 #[test]
2132 fn test_rem_assign_optimization() {
2133 type TestInt = FixedUInt<u32, 2>;
2134
2135 let test_cases = [
2136 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2141
2142 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2143 let mut dividend = TestInt::from(dividend_val);
2144 let divisor = TestInt::from(divisor_val);
2145
2146 dividend %= divisor;
2147 assert_eq!(
2148 dividend,
2149 TestInt::from(expected_remainder),
2150 "rem_assign: {} % {} should be {}",
2151 dividend_val,
2152 divisor_val,
2153 expected_remainder
2154 );
2155 }
2156 }
2157
2158 #[test]
2159 fn test_div_with_remainder_property() {
2160 type TestInt = FixedUInt<u32, 2>;
2161
2162 let test_cases = [
2164 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2169
2170 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2171 let dividend = TestInt::from(dividend_val);
2172 let divisor = TestInt::from(divisor_val);
2173
2174 let quotient = dividend / divisor;
2176 assert_eq!(
2177 quotient,
2178 TestInt::from(expected_quotient),
2179 "Division: {} / {} should be {}",
2180 dividend_val,
2181 divisor_val,
2182 expected_quotient
2183 );
2184
2185 let remainder = dividend % divisor;
2187 assert_eq!(
2188 quotient * divisor + remainder,
2189 dividend,
2190 "Division property check failed for {}",
2191 dividend_val
2192 );
2193 }
2194 }
2195
2196 #[test]
2197 fn test_code_simplification_benefits() {
2198 type TestInt = FixedUInt<u32, 2>;
2199
2200 let dividend = TestInt::from(12345u32);
2202 let divisor = TestInt::from(67u32);
2203 let quotient = dividend / divisor;
2204 let remainder = dividend % divisor;
2205
2206 assert_eq!(quotient * divisor + remainder, dividend);
2208 }
2209
2210 #[test]
2211 fn test_rem_assign_correctness_after_fix() {
2212 type TestInt = FixedUInt<u32, 2>;
2213
2214 let mut a = TestInt::from(17u32);
2216 let b = TestInt::from(5u32);
2217
2218 a %= b;
2221 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2222
2223 let mut test_val = TestInt::from(100u32);
2225 test_val %= TestInt::from(7u32);
2226 assert_eq!(
2227 test_val,
2228 TestInt::from(2u32),
2229 "100 % 7 should be 2 (not 14, the quotient)"
2230 );
2231 }
2232
2233 #[test]
2234 fn test_div_property_based() {
2235 type TestInt = FixedUInt<u16, 2>;
2236
2237 let test_pairs = [
2239 (12345u16, 67u16),
2240 (1000u16, 13u16),
2241 (65535u16, 255u16),
2242 (5000u16, 7u16),
2243 ];
2244
2245 for (dividend_val, divisor_val) in test_pairs {
2246 let dividend = TestInt::from(dividend_val);
2247 let divisor = TestInt::from(divisor_val);
2248
2249 let quotient = dividend / divisor;
2250
2251 let remainder = dividend - (quotient * divisor);
2253 let reconstructed = quotient * divisor + remainder;
2254
2255 assert_eq!(
2256 reconstructed,
2257 dividend,
2258 "Property failed for {} / {}: {} * {} + {} != {}",
2259 dividend_val,
2260 divisor_val,
2261 quotient.to_u32().unwrap_or(0),
2262 divisor_val,
2263 remainder.to_u32().unwrap_or(0),
2264 dividend_val
2265 );
2266
2267 assert!(
2269 remainder < divisor,
2270 "Remainder {} >= divisor {} for {} / {}",
2271 remainder.to_u32().unwrap_or(0),
2272 divisor_val,
2273 dividend_val,
2274 divisor_val
2275 );
2276 }
2277 }
2278}