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 #[must_use]
330 pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2> {
331 let mut array = [T::zero(); N2];
332 let min_size = N.min(N2);
333 array[..min_size].copy_from_slice(&self.array[..min_size]);
334 FixedUInt { array }
335 }
336
337 fn hex_fmt(
338 &self,
339 formatter: &mut core::fmt::Formatter<'_>,
340 uppercase: bool,
341 ) -> Result<(), core::fmt::Error>
342 where
343 u8: core::convert::TryFrom<T>,
344 {
345 type Err = core::fmt::Error;
346
347 fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
348 let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
349 if uppercase {
350 digit.to_uppercase().next().ok_or(Err {})
351 } else {
352 digit.to_lowercase().next().ok_or(Err {})
353 }
354 }
355
356 let mut leading_zero: bool = true;
357
358 let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
359 leading_zero &= nibble == '0';
360 if !leading_zero {
361 formatter.write_char(nibble)?;
362 }
363 Ok(())
364 };
365
366 for index in (0..N).rev() {
367 let val = self.array[index];
368 let mask: T = 0xff.into();
369 for j in (0..Self::WORD_SIZE as u32).rev() {
370 let masked = val & mask.shl((j * 8) as usize);
371
372 let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
373
374 maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
375 maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
376 }
377 }
378 Ok(())
379 }
380}
381
382c0nst::c0nst! {
383 pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
385 target: &mut [T; N],
386 other: &[T; N]
387 ) -> bool {
388 let mut carry = T::zero();
389 let mut i = 0usize;
390 while i < N {
391 let (res, carry1) = target[i].overflowing_add(&other[i]);
392 let (res, carry2) = res.overflowing_add(&carry);
393 carry = if carry1 || carry2 {
394 T::one()
395 } else {
396 T::zero()
397 };
398 target[i] = res;
399 i += 1;
400 }
401 !carry.is_zero()
402 }
403}
404
405c0nst::c0nst! {
406 pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
408 target: &mut [T; N],
409 other: &[T; N]
410 ) -> bool {
411 let mut borrow = T::zero();
412 let mut i = 0usize;
413 while i < N {
414 let (res, borrow1) = target[i].overflowing_sub(&other[i]);
415 let (res, borrow2) = res.overflowing_sub(&borrow);
416 borrow = if borrow1 || borrow2 {
417 T::one()
418 } else {
419 T::zero()
420 };
421 target[i] = res;
422 i += 1;
423 }
424 !borrow.is_zero()
425 }
426}
427
428c0nst::c0nst! {
429 pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
431 target: &mut FixedUInt<T, N>,
432 bits: usize,
433 ) {
434 if N == 0 {
435 return;
436 }
437 let word_bits = FixedUInt::<T, N>::WORD_BITS;
438 let nwords = bits / word_bits;
439 let nbits = bits - nwords * word_bits;
440
441 if nwords >= N {
443 let mut i = 0;
444 while i < N {
445 target.array[i] = T::zero();
446 i += 1;
447 }
448 return;
449 }
450
451 let mut i = N;
453 while i > nwords {
454 i -= 1;
455 target.array[i] = target.array[i - nwords];
456 }
457 let mut i = 0;
459 while i < nwords {
460 target.array[i] = T::zero();
461 i += 1;
462 }
463
464 if nbits != 0 {
465 let mut i = N;
467 while i > 1 {
468 i -= 1;
469 let right = target.array[i] << nbits;
470 let left = target.array[i - 1] >> (word_bits - nbits);
471 target.array[i] = right | left;
472 }
473 target.array[0] <<= nbits;
474 }
475 }
476
477 pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
479 target: &mut FixedUInt<T, N>,
480 bits: usize,
481 ) {
482 if N == 0 {
483 return;
484 }
485 let word_bits = FixedUInt::<T, N>::WORD_BITS;
486 let nwords = bits / word_bits;
487 let nbits = bits - nwords * word_bits;
488
489 if nwords >= N {
491 let mut i = 0;
492 while i < N {
493 target.array[i] = T::zero();
494 i += 1;
495 }
496 return;
497 }
498
499 let last_index = N - 1;
500 let last_word = N - nwords;
501
502 let mut i = 0;
504 while i < last_word {
505 target.array[i] = target.array[i + nwords];
506 i += 1;
507 }
508
509 let mut i = last_word;
511 while i < N {
512 target.array[i] = T::zero();
513 i += 1;
514 }
515
516 if nbits != 0 {
517 let mut i = 0;
519 while i < last_index {
520 let left = target.array[i] >> nbits;
521 let right = target.array[i + 1] << (word_bits - nbits);
522 target.array[i] = left | right;
523 i += 1;
524 }
525 target.array[last_index] >>= nbits;
526 }
527 }
528
529 pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool>(
532 op1: &[T; N],
533 op2: &[T; N],
534 word_bits: usize,
535 ) -> ([T; N], bool) {
536 let mut result: [T; N] = [<T as ConstZero>::zero(); N];
537 let mut overflowed = false;
538 let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
539 let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
541
542 let mut i = 0;
543 while i < N {
544 let mut carry = dw_zero;
545 let mut j = 0;
546 while j < N {
547 let round = i + j;
548 let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
549 let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
550 let mul_res = op1_dw * op2_dw;
551 let mut accumulator = if round < N {
552 <T as ConstMachineWord>::to_double(result[round])
553 } else {
554 dw_zero
555 };
556 accumulator = accumulator + mul_res + carry;
557
558 if accumulator > t_max {
559 carry = accumulator >> word_bits;
560 accumulator &= t_max;
561 } else {
562 carry = dw_zero;
563 }
564 if round < N {
565 result[round] = <T as ConstMachineWord>::from_double(accumulator);
566 } else if CHECK_OVERFLOW {
567 overflowed = overflowed || accumulator != dw_zero;
568 }
569 j += 1;
570 }
571 if carry != dw_zero && CHECK_OVERFLOW {
572 overflowed = true;
573 }
574 i += 1;
575 }
576 (result, overflowed)
577 }
578
579 pub(crate) c0nst fn const_word_bits<T>() -> usize {
581 core::mem::size_of::<T>() * 8
582 }
583
584 pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
586 if a > b {
587 Some(core::cmp::Ordering::Greater)
588 } else if a < b {
589 Some(core::cmp::Ordering::Less)
590 } else {
591 None
592 }
593 }
594
595 pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
597 array: &[T; N],
598 ) -> u32 {
599 let mut ret = 0u32;
600 let mut index = N;
601 while index > 0 {
602 index -= 1;
603 let v = array[index];
604 ret += <T as ConstPrimInt>::leading_zeros(v);
605 if !<T as ConstZero>::is_zero(&v) {
606 break;
607 }
608 }
609 ret
610 }
611
612 pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
614 array: &[T; N],
615 ) -> u32 {
616 let mut ret = 0u32;
617 let mut index = 0;
618 while index < N {
619 let v = array[index];
620 ret += <T as ConstPrimInt>::trailing_zeros(v);
621 if !<T as ConstZero>::is_zero(&v) {
622 break;
623 }
624 index += 1;
625 }
626 ret
627 }
628
629 pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
631 array: &[T; N],
632 ) -> usize {
633 let word_bits = const_word_bits::<T>();
634 let bit_size = N * word_bits;
635 bit_size - const_leading_zeros::<T, N>(array) as usize
636 }
637
638 pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
640 array: &[T; N],
641 ) -> bool {
642 let mut index = 0;
643 while index < N {
644 if !<T as ConstZero>::is_zero(&array[index]) {
645 return false;
646 }
647 index += 1;
648 }
649 true
650 }
651
652 pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
658 array: &mut [T; N],
659 pos: usize,
660 ) {
661 let word_bits = const_word_bits::<T>();
662 let word_idx = pos / word_bits;
663 if word_idx >= N {
664 return;
665 }
666 let bit_idx = pos % word_bits;
667 array[word_idx] |= <T as ConstOne>::one() << bit_idx;
668 }
669
670 pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
675 a: &[T; N],
676 b: &[T; N],
677 ) -> core::cmp::Ordering {
678 let mut index = N;
679 while index > 0 {
680 index -= 1;
681 if let Some(ord) = const_cmp_words(a[index], b[index]) {
682 return ord;
683 }
684 }
685 core::cmp::Ordering::Equal
686 }
687
688 pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
693 array: &[T; N],
694 word_idx: usize,
695 word_shift: usize,
696 bit_shift: usize,
697 ) -> T {
698 let word_bits = const_word_bits::<T>();
699
700 if bit_shift >= word_bits {
702 return <T as ConstZero>::zero();
703 }
704
705 if word_idx < word_shift {
706 return <T as ConstZero>::zero();
707 }
708
709 let source_idx = word_idx - word_shift;
710
711 if bit_shift == 0 {
712 if source_idx < N {
713 array[source_idx]
714 } else {
715 <T as ConstZero>::zero()
716 }
717 } else {
718 let mut result = <T as ConstZero>::zero();
719
720 if source_idx < N {
722 result |= array[source_idx] << bit_shift;
723 }
724
725 if source_idx > 0 && source_idx - 1 < N {
727 let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
728 result |= high_bits;
729 }
730
731 result
732 }
733 }
734
735 pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
740 array: &[T; N],
741 other: &[T; N],
742 shift_bits: usize,
743 ) -> core::cmp::Ordering {
744 let word_bits = const_word_bits::<T>();
745
746 if shift_bits == 0 {
747 return const_cmp::<T, N>(array, other);
748 }
749
750 let word_shift = shift_bits / word_bits;
751 if word_shift >= N {
752 if const_is_zero::<T, N>(array) {
754 return core::cmp::Ordering::Equal;
755 } else {
756 return core::cmp::Ordering::Greater;
757 }
758 }
759
760 let bit_shift = shift_bits % word_bits;
761
762 let mut index = N;
764 while index > 0 {
765 index -= 1;
766 let self_word = array[index];
767 let other_shifted_word = const_get_shifted_word::<T, N>(
768 other, index, word_shift, bit_shift
769 );
770
771 if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
772 return ord;
773 }
774 }
775
776 core::cmp::Ordering::Equal
777 }
778
779 pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
784 array: &mut [T; N],
785 other: &[T; N],
786 shift_bits: usize,
787 ) {
788 let word_bits = const_word_bits::<T>();
789
790 if shift_bits == 0 {
791 sub_impl::<T, N>(array, other);
792 return;
793 }
794
795 let word_shift = shift_bits / word_bits;
796 if word_shift >= N {
797 return;
798 }
799
800 let bit_shift = shift_bits % word_bits;
801 let mut borrow = T::zero();
802 let mut index = 0;
803 while index < N {
804 let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
805 let (res, borrow1) = array[index].overflowing_sub(&other_word);
806 let (res, borrow2) = res.overflowing_sub(&borrow);
807 borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
808 array[index] = res;
809 index += 1;
810 }
811 }
812
813 pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
817 dividend: &mut [T; N],
818 divisor: &[T; N],
819 ) -> [T; N] {
820 use core::cmp::Ordering;
821
822 match const_cmp::<T, N>(dividend, divisor) {
823 Ordering::Less => {
825 let remainder = *dividend;
826 let mut i = 0;
827 while i < N {
828 dividend[i] = <T as ConstZero>::zero();
829 i += 1;
830 }
831 return remainder;
832 }
833 Ordering::Equal => {
835 let mut i = 0;
836 while i < N {
837 dividend[i] = <T as ConstZero>::zero();
838 i += 1;
839 }
840 if N > 0 {
841 dividend[0] = <T as ConstOne>::one();
842 }
843 return [<T as ConstZero>::zero(); N];
844 }
845 Ordering::Greater => {}
846 }
847
848 let mut quotient = [<T as ConstZero>::zero(); N];
849
850 let dividend_bits = const_bit_length::<T, N>(dividend);
852 let divisor_bits = const_bit_length::<T, N>(divisor);
853
854 let mut bit_pos = if dividend_bits >= divisor_bits {
855 dividend_bits - divisor_bits
856 } else {
857 0
858 };
859
860 while bit_pos > 0 {
862 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
863 if !matches!(cmp, Ordering::Less) {
864 break;
865 }
866 bit_pos -= 1;
867 }
868
869 loop {
871 let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
872 if !matches!(cmp, Ordering::Less) {
873 const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
874 const_set_bit::<T, N>(&mut quotient, bit_pos);
875 }
876
877 if bit_pos == 0 {
878 break;
879 }
880 bit_pos -= 1;
881 }
882
883 let remainder = *dividend;
884 *dividend = quotient;
885 remainder
886 }
887
888 pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
892 dividend: &[T; N],
893 divisor: &[T; N],
894 ) -> ([T; N], [T; N]) {
895 if const_is_zero(divisor) {
896 maybe_panic(PanicReason::DivByZero)
897 }
898 let mut quotient = *dividend;
899 let remainder = const_div(&mut quotient, divisor);
900 (quotient, remainder)
901 }
902}
903
904c0nst::c0nst! {
905 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Default for FixedUInt<T, N> {
906 fn default() -> Self {
907 <Self as ConstZero>::zero()
908 }
909 }
910
911 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Clone for FixedUInt<T, N> {
912 fn clone(&self) -> Self {
913 *self
914 }
915 }
916}
917
918impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
919
920c0nst::c0nst! {
923 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialEq for FixedUInt<T, N> {
924 fn eq(&self, other: &Self) -> bool {
925 self.array == other.array
926 }
927 }
928
929 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Eq for FixedUInt<T, N> {}
930
931 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Ord for FixedUInt<T, N> {
932 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
933 const_cmp(&self.array, &other.array)
934 }
935 }
936
937 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialOrd for FixedUInt<T, N> {
938 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
939 Some(self.cmp(other))
940 }
941 }
942}
943
944c0nst::c0nst! {
949 c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
952 bytes: [u8; B],
953 ) -> [T; N] {
954 impl_from_le_bytes_slice::<T, N>(&bytes)
955 }
956
957 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u8> for FixedUInt<T, N> {
958 fn from(x: u8) -> 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<u16> for FixedUInt<T, N> {
964 fn from(x: u16) -> Self {
965 Self { array: const_from_le_bytes(x.to_le_bytes()) }
966 }
967 }
968
969 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u32> for FixedUInt<T, N> {
970 fn from(x: u32) -> Self {
971 Self { array: const_from_le_bytes(x.to_le_bytes()) }
972 }
973 }
974
975 impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u64> for FixedUInt<T, N> {
976 fn from(x: u64) -> Self {
977 Self { array: const_from_le_bytes(x.to_le_bytes()) }
978 }
979 }
980}
981
982fn make_parse_int_err() -> core::num::ParseIntError {
989 <u8>::from_str_radix("-", 2).err().unwrap()
990}
991fn make_overflow_err() -> core::num::ParseIntError {
992 <u8>::from_str_radix("101", 16).err().unwrap()
993}
994fn make_empty_error() -> core::num::ParseIntError {
995 <u8>::from_str_radix("", 8).err().unwrap()
996}
997
998fn to_slice_hex<T: AsRef<[u8]>>(
999 input: T,
1000 output: &mut [u8],
1001) -> Result<(), core::num::ParseIntError> {
1002 fn from_digit(byte: u8) -> Option<char> {
1003 core::char::from_digit(byte as u32, 16)
1004 }
1005 let r = input.as_ref();
1006 if r.len() * 2 != output.len() {
1007 return Err(make_parse_int_err());
1008 }
1009 for i in 0..r.len() {
1010 let byte = r[i];
1011 output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1012 output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1013 }
1014
1015 Ok(())
1016}
1017
1018pub(super) enum PanicReason {
1019 Add,
1020 Sub,
1021 Mul,
1022 DivByZero,
1023}
1024
1025c0nst::c0nst! {
1026 pub(super) c0nst fn maybe_panic(r: PanicReason) {
1027 match r {
1028 PanicReason::Add => panic!("attempt to add with overflow"),
1029 PanicReason::Sub => panic!("attempt to subtract with overflow"),
1030 PanicReason::Mul => panic!("attempt to multiply with overflow"),
1031 PanicReason::DivByZero => panic!("attempt to divide by zero"),
1032 }
1033 }
1034}
1035
1036#[cfg(test)]
1039mod tests {
1040 use super::FixedUInt as Bn;
1041 use super::*;
1042 use num_traits::One;
1043
1044 type Bn8 = Bn<u8, 8>;
1045 type Bn16 = Bn<u16, 4>;
1046 type Bn32 = Bn<u32, 2>;
1047
1048 c0nst::c0nst! {
1049 pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1050 a: &mut [T; N],
1051 b: &[T; N]
1052 ) -> bool {
1053 add_impl(a, b)
1054 }
1055
1056 pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1057 a: &mut [T; N],
1058 b: &[T; N]
1059 ) -> bool {
1060 sub_impl(a, b)
1061 }
1062
1063 pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1064 a: &[T; N],
1065 b: &[T; N],
1066 word_bits: usize,
1067 ) -> ([T; N], bool) {
1068 const_mul::<T, N, true>(a, b, word_bits)
1069 }
1070
1071 pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1072 a: &[T; N],
1073 ) -> u32 {
1074 const_leading_zeros::<T, N>(a)
1075 }
1076
1077 pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1078 a: &[T; N],
1079 ) -> u32 {
1080 const_trailing_zeros::<T, N>(a)
1081 }
1082
1083 pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1084 a: &[T; N],
1085 ) -> usize {
1086 const_bit_length::<T, N>(a)
1087 }
1088
1089 pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1090 a: &[T; N],
1091 ) -> bool {
1092 const_is_zero::<T, N>(a)
1093 }
1094
1095 pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1096 a: &mut [T; N],
1097 pos: usize,
1098 ) {
1099 const_set_bit::<T, N>(a, pos)
1100 }
1101
1102 pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1103 a: &[T; N],
1104 b: &[T; N],
1105 ) -> core::cmp::Ordering {
1106 const_cmp::<T, N>(a, b)
1107 }
1108
1109 pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1110 a: &[T; N],
1111 b: &[T; N],
1112 shift_bits: usize,
1113 ) -> core::cmp::Ordering {
1114 const_cmp_shifted::<T, N>(a, b, shift_bits)
1115 }
1116
1117 pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1118 a: &[T; N],
1119 word_idx: usize,
1120 word_shift: usize,
1121 bit_shift: usize,
1122 ) -> T {
1123 const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1124 }
1125 }
1126
1127 #[test]
1128 fn test_const_add_impl() {
1129 let mut a: [u8; 4] = [1, 0, 0, 0];
1131 let b: [u8; 4] = [2, 0, 0, 0];
1132 let overflow = test_add(&mut a, &b);
1133 assert_eq!(a, [3, 0, 0, 0]);
1134 assert!(!overflow);
1135
1136 let mut a: [u8; 4] = [255, 0, 0, 0];
1138 let b: [u8; 4] = [1, 0, 0, 0];
1139 let overflow = test_add(&mut a, &b);
1140 assert_eq!(a, [0, 1, 0, 0]);
1141 assert!(!overflow);
1142
1143 let mut a: [u8; 4] = [255, 255, 255, 255];
1145 let b: [u8; 4] = [1, 0, 0, 0];
1146 let overflow = test_add(&mut a, &b);
1147 assert_eq!(a, [0, 0, 0, 0]);
1148 assert!(overflow);
1149
1150 let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1152 let b: [u32; 2] = [1, 0];
1153 let overflow = test_add(&mut a, &b);
1154 assert_eq!(a, [0, 1]);
1155 assert!(!overflow);
1156
1157 #[cfg(feature = "nightly")]
1158 {
1159 const ADD_RESULT: ([u8; 4], bool) = {
1160 let mut a = [1u8, 0, 0, 0];
1161 let b = [2u8, 0, 0, 0];
1162 let overflow = test_add(&mut a, &b);
1163 (a, overflow)
1164 };
1165 assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1166 }
1167 }
1168
1169 #[test]
1170 fn test_const_sub_impl() {
1171 let mut a: [u8; 4] = [3, 0, 0, 0];
1173 let b: [u8; 4] = [1, 0, 0, 0];
1174 let overflow = test_sub(&mut a, &b);
1175 assert_eq!(a, [2, 0, 0, 0]);
1176 assert!(!overflow);
1177
1178 let mut a: [u8; 4] = [0, 1, 0, 0];
1180 let b: [u8; 4] = [1, 0, 0, 0];
1181 let overflow = test_sub(&mut a, &b);
1182 assert_eq!(a, [255, 0, 0, 0]);
1183 assert!(!overflow);
1184
1185 let mut a: [u8; 4] = [0, 0, 0, 0];
1187 let b: [u8; 4] = [1, 0, 0, 0];
1188 let overflow = test_sub(&mut a, &b);
1189 assert_eq!(a, [255, 255, 255, 255]);
1190 assert!(overflow);
1191
1192 let mut a: [u32; 2] = [0, 1];
1194 let b: [u32; 2] = [1, 0];
1195 let overflow = test_sub(&mut a, &b);
1196 assert_eq!(a, [0xFFFFFFFF, 0]);
1197 assert!(!overflow);
1198
1199 #[cfg(feature = "nightly")]
1200 {
1201 const SUB_RESULT: ([u8; 4], bool) = {
1202 let mut a = [3u8, 0, 0, 0];
1203 let b = [1u8, 0, 0, 0];
1204 let overflow = test_sub(&mut a, &b);
1205 (a, overflow)
1206 };
1207 assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1208 }
1209 }
1210
1211 #[test]
1212 fn test_const_mul_impl() {
1213 let a: [u8; 2] = [3, 0];
1215 let b: [u8; 2] = [4, 0];
1216 let (result, overflow) = test_mul(&a, &b, 8);
1217 assert_eq!(result, [12, 0]);
1218 assert!(!overflow);
1219
1220 let a: [u8; 2] = [200, 0];
1222 let b: [u8; 2] = [2, 0];
1223 let (result, overflow) = test_mul(&a, &b, 8);
1224 assert_eq!(result, [0x90, 0x01]);
1225 assert!(!overflow);
1226
1227 let a: [u8; 2] = [0, 1]; let b: [u8; 2] = [0, 1]; let (_result, overflow) = test_mul(&a, &b, 8);
1231 assert!(overflow);
1232
1233 let a: [u8; 3] = [0, 0, 1];
1237 let b: [u8; 3] = [0, 0, 1];
1238 let (_result, overflow) = test_mul(&a, &b, 8);
1239 assert!(overflow, "N=3 high-position overflow not detected");
1240
1241 let a: [u8; 3] = [0, 0, 2];
1245 let b: [u8; 3] = [0, 0, 2];
1246 let (_result, overflow) = test_mul(&a, &b, 8);
1247 assert!(
1248 overflow,
1249 "N=3 high-position overflow with larger values not detected"
1250 );
1251
1252 let a: [u8; 3] = [0, 1, 0];
1256 let b: [u8; 3] = [0, 1, 0];
1257 let (result, overflow) = test_mul(&a, &b, 8);
1258 assert_eq!(result, [0, 0, 1]);
1259 assert!(
1260 !overflow,
1261 "N=3 non-overflow incorrectly detected as overflow"
1262 );
1263
1264 let a: [u8; 3] = [255, 0, 0];
1268 let b: [u8; 3] = [255, 0, 0];
1269 let (result, overflow) = test_mul(&a, &b, 8);
1270 assert_eq!(result, [0x01, 0xFE, 0x00]);
1271 assert!(!overflow);
1272
1273 #[cfg(feature = "nightly")]
1274 {
1275 const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1276 assert_eq!(MUL_RESULT, ([12, 0], false));
1277 }
1278 }
1279
1280 #[test]
1281 fn test_const_helpers() {
1282 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]));
1307 assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1308 assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1309 assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1310
1311 let mut arr: [u8; 4] = [0, 0, 0, 0];
1313 arr_set_bit(&mut arr, 0);
1314 assert_eq!(arr, [1, 0, 0, 0]);
1315
1316 let mut arr: [u8; 4] = [0, 0, 0, 0];
1317 arr_set_bit(&mut arr, 8);
1318 assert_eq!(arr, [0, 1, 0, 0]);
1319
1320 let mut arr: [u8; 4] = [0, 0, 0, 0];
1321 arr_set_bit(&mut arr, 31);
1322 assert_eq!(arr, [0, 0, 0, 0x80]);
1323
1324 let mut arr: [u8; 4] = [0, 0, 0, 0];
1326 arr_set_bit(&mut arr, 0);
1327 arr_set_bit(&mut arr, 3);
1328 arr_set_bit(&mut arr, 8);
1329 assert_eq!(arr, [0b00001001, 1, 0, 0]);
1330
1331 let mut arr: [u8; 4] = [0, 0, 0, 0];
1333 arr_set_bit(&mut arr, 32);
1334 assert_eq!(arr, [0, 0, 0, 0]);
1335
1336 assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1338 assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1339 assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1340 assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1341 assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1342 assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1343 assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1344 assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1345
1346 #[cfg(feature = "nightly")]
1347 {
1348 const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1349 assert_eq!(LEADING, 15);
1350
1351 const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1352 assert_eq!(TRAILING, 16);
1353
1354 const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1355 assert_eq!(BIT_LEN, 17);
1356
1357 const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1358 assert!(IS_ZERO);
1359
1360 const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1361 assert!(!NOT_ZERO);
1362
1363 const SET_BIT_RESULT: [u8; 4] = {
1364 let mut arr = [0u8, 0, 0, 0];
1365 arr_set_bit(&mut arr, 10);
1366 arr
1367 };
1368 assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1369 }
1370 }
1371
1372 #[test]
1373 fn test_const_cmp() {
1374 use core::cmp::Ordering;
1375
1376 assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1378 assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1379
1380 assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1382
1383 assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1385
1386 assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1388
1389 assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1391
1392 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1394 assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1395 assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1396
1397 #[cfg(feature = "nightly")]
1398 {
1399 const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1400 const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1401 const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1402 assert_eq!(CMP_EQ, Ordering::Equal);
1403 assert_eq!(CMP_GT, Ordering::Greater);
1404 assert_eq!(CMP_LT, Ordering::Less);
1405 }
1406 }
1407
1408 #[test]
1409 fn test_const_cmp_shifted() {
1410 use core::cmp::Ordering;
1411
1412 assert_eq!(
1414 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1415 Ordering::Equal
1416 );
1417
1418 assert_eq!(
1420 arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1421 Ordering::Equal
1422 );
1423
1424 assert_eq!(
1426 arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1427 Ordering::Greater
1428 );
1429
1430 assert_eq!(
1432 arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1433 Ordering::Less
1434 );
1435
1436 assert_eq!(
1439 arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1440 Ordering::Greater
1441 );
1442
1443 assert_eq!(
1445 arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1446 Ordering::Equal
1447 );
1448
1449 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
1453 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
1454 assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
1455
1456 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1460 assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1462
1463 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1466 assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1468
1469 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1473 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1475 assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
1477
1478 #[cfg(feature = "nightly")]
1479 {
1480 const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
1481 const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
1482 assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
1483 assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
1484 }
1485 }
1486
1487 #[test]
1488 fn test_core_convert_u8() {
1489 let f = Bn::<u8, 1>::from(1u8);
1490 assert_eq!(f.array, [1]);
1491 let f = Bn::<u8, 2>::from(1u8);
1492 assert_eq!(f.array, [1, 0]);
1493
1494 let f = Bn::<u16, 1>::from(1u8);
1495 assert_eq!(f.array, [1]);
1496 let f = Bn::<u16, 2>::from(1u8);
1497 assert_eq!(f.array, [1, 0]);
1498
1499 #[cfg(feature = "nightly")]
1500 {
1501 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
1502 assert_eq!(F1.array, [42, 0]);
1503 }
1504 }
1505
1506 #[test]
1507 fn test_core_convert_u16() {
1508 let f = Bn::<u8, 1>::from(1u16);
1509 assert_eq!(f.array, [1]);
1510 let f = Bn::<u8, 2>::from(1u16);
1511 assert_eq!(f.array, [1, 0]);
1512
1513 let f = Bn::<u8, 1>::from(256u16);
1514 assert_eq!(f.array, [0]);
1515 let f = Bn::<u8, 2>::from(257u16);
1516 assert_eq!(f.array, [1, 1]);
1517 let f = Bn::<u8, 2>::from(65535u16);
1518 assert_eq!(f.array, [255, 255]);
1519
1520 let f = Bn::<u16, 1>::from(1u16);
1521 assert_eq!(f.array, [1]);
1522 let f = Bn::<u16, 2>::from(1u16);
1523 assert_eq!(f.array, [1, 0]);
1524
1525 let f = Bn::<u16, 1>::from(65535u16);
1526 assert_eq!(f.array, [65535]);
1527
1528 #[cfg(feature = "nightly")]
1529 {
1530 const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
1531 assert_eq!(F1.array, [0x02, 0x01]);
1532 }
1533 }
1534
1535 #[test]
1536 fn test_core_convert_u32() {
1537 let f = Bn::<u8, 1>::from(1u32);
1538 assert_eq!(f.array, [1]);
1539 let f = Bn::<u8, 1>::from(256u32);
1540 assert_eq!(f.array, [0]);
1541
1542 let f = Bn::<u8, 2>::from(1u32);
1543 assert_eq!(f.array, [1, 0]);
1544 let f = Bn::<u8, 2>::from(257u32);
1545 assert_eq!(f.array, [1, 1]);
1546 let f = Bn::<u8, 2>::from(65535u32);
1547 assert_eq!(f.array, [255, 255]);
1548
1549 let f = Bn::<u8, 4>::from(1u32);
1550 assert_eq!(f.array, [1, 0, 0, 0]);
1551 let f = Bn::<u8, 4>::from(257u32);
1552 assert_eq!(f.array, [1, 1, 0, 0]);
1553 let f = Bn::<u8, 4>::from(u32::max_value());
1554 assert_eq!(f.array, [255, 255, 255, 255]);
1555
1556 let f = Bn::<u8, 1>::from(1u32);
1557 assert_eq!(f.array, [1]);
1558 let f = Bn::<u8, 1>::from(256u32);
1559 assert_eq!(f.array, [0]);
1560
1561 let f = Bn::<u16, 2>::from(65537u32);
1562 assert_eq!(f.array, [1, 1]);
1563
1564 let f = Bn::<u32, 1>::from(1u32);
1565 assert_eq!(f.array, [1]);
1566 let f = Bn::<u32, 2>::from(1u32);
1567 assert_eq!(f.array, [1, 0]);
1568
1569 let f = Bn::<u32, 1>::from(65537u32);
1570 assert_eq!(f.array, [65537]);
1571
1572 let f = Bn::<u32, 1>::from(u32::max_value());
1573 assert_eq!(f.array, [4294967295]);
1574
1575 #[cfg(feature = "nightly")]
1576 {
1577 const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
1578 assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
1579 }
1580 }
1581
1582 #[test]
1583 fn test_core_convert_u64() {
1584 let f = Bn::<u8, 8>::from(0x0102030405060708u64);
1585 assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1586
1587 let f = Bn::<u16, 4>::from(0x0102030405060708u64);
1588 assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
1589
1590 let f = Bn::<u32, 2>::from(0x0102030405060708u64);
1591 assert_eq!(f.array, [0x05060708, 0x01020304]);
1592
1593 let f = Bn::<u64, 1>::from(0x0102030405060708u64);
1594 assert_eq!(f.array, [0x0102030405060708]);
1595
1596 #[cfg(feature = "nightly")]
1597 {
1598 const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
1599 assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1600 }
1601 }
1602
1603 #[test]
1604 fn testsimple() {
1605 assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
1606
1607 assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
1608 assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
1609 assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
1610 assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
1611 assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
1612 assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
1613 assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
1614 }
1615 #[test]
1616 fn testfrom() {
1617 let mut n1 = Bn::<u8, 8>::new();
1618 n1.array[0] = 1;
1619 assert_eq!(Some(1), n1.to_u32());
1620 n1.array[1] = 1;
1621 assert_eq!(Some(257), n1.to_u32());
1622
1623 let mut n2 = Bn::<u16, 8>::new();
1624 n2.array[0] = 0xffff;
1625 assert_eq!(Some(65535), n2.to_u32());
1626 n2.array[0] = 0x0;
1627 n2.array[2] = 0x1;
1628 assert_eq!(None, n2.to_u32());
1630 assert_eq!(Some(0x100000000), n2.to_u64());
1631 }
1632
1633 #[test]
1634 fn test_from_str_bitlengths() {
1635 let test_s64 = "81906f5e4d3c2c01";
1636 let test_u64: u64 = 0x81906f5e4d3c2c01;
1637 let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
1638 let cc = Bn8::from_u64(test_u64).unwrap();
1639 assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1640 assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1641 let dd = Bn16::from_u64(test_u64).unwrap();
1642 let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
1643 assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1644 assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1645 let ee = Bn32::from_u64(test_u64).unwrap();
1646 let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
1647 assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
1648 assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
1649 }
1650
1651 #[test]
1652 fn test_from_str_stringlengths() {
1653 let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
1654 assert_eq!(
1655 ab.array,
1656 [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
1657 );
1658 assert_eq!(
1659 [0x2c01, 0x4d3c, 0x6f5e, 0],
1660 Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
1661 .unwrap()
1662 .array
1663 );
1664 assert_eq!(
1665 [0x2c01, 0x4d3c, 0x6f5e, 0x190],
1666 Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
1667 .unwrap()
1668 .array
1669 );
1670 assert_eq!(
1671 Err(make_overflow_err()),
1672 Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
1673 );
1674 assert_eq!(
1675 Err(make_overflow_err()),
1676 Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
1677 );
1678 assert_eq!(
1679 Err(make_overflow_err()),
1680 Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
1681 );
1682 let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
1683 assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
1684 }
1685
1686 #[test]
1687 fn test_resize() {
1688 type TestInt1 = FixedUInt<u32, 1>;
1689 type TestInt2 = FixedUInt<u32, 2>;
1690
1691 let a = TestInt1::from(u32::MAX);
1692 let b: TestInt2 = a.resize();
1693 assert_eq!(b, TestInt2::from([u32::MAX, 0]));
1694
1695 let a = TestInt2::from([u32::MAX, u32::MAX]);
1696 let b: TestInt1 = a.resize();
1697 assert_eq!(b, TestInt1::from(u32::MAX));
1698 }
1699
1700 #[test]
1701 fn test_bit_length() {
1702 assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
1703 assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
1704 assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
1705 assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
1706 assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
1707 assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
1708 assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
1709
1710 assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
1711 assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
1712 assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
1713 assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
1714 assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
1715
1716 assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
1717 assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
1718 assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
1719 assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
1720 assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
1721 assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
1722 assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
1723
1724 assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
1725 assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
1726 assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
1727 assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
1728 assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
1729 }
1730
1731 #[test]
1732 fn test_bit_length_1000() {
1733 let value = Bn32::from_u16(1000).unwrap();
1735
1736 assert_eq!(value.to_u32().unwrap(), 1000);
1739 assert_eq!(value.bit_length(), 10);
1740
1741 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);
1748 assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1749
1750 let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1752 assert_eq!(value_from_str.bit_length(), 10);
1753
1754 let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1756 assert_eq!(
1758 value_from_bytes.to_u32().unwrap_or(0),
1759 1000,
1760 "from_le_bytes didn't create the correct value"
1761 );
1762 assert_eq!(value_from_bytes.bit_length(), 10);
1763 }
1764 #[test]
1765 fn test_cmp() {
1766 let f0 = <Bn8 as Zero>::zero();
1767 let f1 = <Bn8 as Zero>::zero();
1768 let f2 = <Bn8 as One>::one();
1769 assert_eq!(f0, f1);
1770 assert!(f2 > f0);
1771 assert!(f0 < f2);
1772 let f3 = Bn32::from_u64(990223).unwrap();
1773 assert_eq!(f3, Bn32::from_u64(990223).unwrap());
1774 let f4 = Bn32::from_u64(990224).unwrap();
1775 assert!(f4 > Bn32::from_u64(990223).unwrap());
1776
1777 let f3 = Bn8::from_u64(990223).unwrap();
1778 assert_eq!(f3, Bn8::from_u64(990223).unwrap());
1779 let f4 = Bn8::from_u64(990224).unwrap();
1780 assert!(f4 > Bn8::from_u64(990223).unwrap());
1781
1782 #[cfg(feature = "nightly")]
1783 {
1784 use core::cmp::Ordering;
1785
1786 const A: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1787 const B: FixedUInt<u8, 2> = FixedUInt { array: [20, 0] };
1788 const C: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1789
1790 const CMP_LT: Ordering = A.cmp(&B);
1791 const CMP_GT: Ordering = B.cmp(&A);
1792 const CMP_EQ: Ordering = A.cmp(&C);
1793 const EQ_TRUE: bool = A.eq(&C);
1794 const EQ_FALSE: bool = A.eq(&B);
1795
1796 assert_eq!(CMP_LT, Ordering::Less);
1797 assert_eq!(CMP_GT, Ordering::Greater);
1798 assert_eq!(CMP_EQ, Ordering::Equal);
1799 assert!(EQ_TRUE);
1800 assert!(!EQ_FALSE);
1801 }
1802 }
1803
1804 #[test]
1805 fn test_default() {
1806 let d: Bn8 = Default::default();
1807 assert!(Zero::is_zero(&d));
1808
1809 #[cfg(feature = "nightly")]
1810 {
1811 const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
1812 assert!(Zero::is_zero(&D));
1813 }
1814 }
1815
1816 #[test]
1817 fn test_clone() {
1818 let a: Bn8 = 42u8.into();
1819 let b = a.clone();
1820 assert_eq!(a, b);
1821
1822 #[cfg(feature = "nightly")]
1823 {
1824 const A: FixedUInt<u8, 2> = FixedUInt { array: [42, 0] };
1825 const B: FixedUInt<u8, 2> = A.clone();
1826 assert_eq!(A.array, B.array);
1827 }
1828 }
1829
1830 #[test]
1831 fn test_le_be_bytes() {
1832 let le_bytes = [1, 2, 3, 4];
1833 let be_bytes = [4, 3, 2, 1];
1834 let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1835 let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1836 let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1837 let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1838 let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1839 let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1840
1841 assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1842 assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1843 assert_eq!(u32_ver.array, [0x04030201]);
1844 assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1845 assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1846 assert_eq!(u32_ver_be.array, [0x04030201]);
1847
1848 let mut output_buffer = [0u8; 16];
1849 assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1850 assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1851 assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1852 assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1853 assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1854 assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1855 }
1856
1857 #[test]
1859 fn test_div_small() {
1860 type TestInt = FixedUInt<u8, 2>;
1861
1862 let test_cases = [
1864 (20u16, 3u16, 6u16), (100u16, 7u16, 14u16), (255u16, 5u16, 51u16), (65535u16, 256u16, 255u16), ];
1869
1870 for (dividend_val, divisor_val, expected) in test_cases {
1871 let dividend = TestInt::from(dividend_val);
1872 let divisor = TestInt::from(divisor_val);
1873 let expected_result = TestInt::from(expected);
1874
1875 assert_eq!(
1876 dividend / divisor,
1877 expected_result,
1878 "Division failed for {} / {} = {}",
1879 dividend_val,
1880 divisor_val,
1881 expected
1882 );
1883 }
1884 }
1885
1886 #[test]
1887 fn test_div_edge_cases() {
1888 type TestInt = FixedUInt<u16, 2>;
1889
1890 let dividend = TestInt::from(1000u16);
1892 let divisor = TestInt::from(1u16);
1893 assert_eq!(dividend / divisor, TestInt::from(1000u16));
1894
1895 let dividend = TestInt::from(42u16);
1897 let divisor = TestInt::from(42u16);
1898 assert_eq!(dividend / divisor, TestInt::from(1u16));
1899
1900 let dividend = TestInt::from(5u16);
1902 let divisor = TestInt::from(10u16);
1903 assert_eq!(dividend / divisor, TestInt::from(0u16));
1904
1905 let dividend = TestInt::from(1024u16);
1907 let divisor = TestInt::from(4u16);
1908 assert_eq!(dividend / divisor, TestInt::from(256u16));
1909 }
1910
1911 #[test]
1912 fn test_helper_methods() {
1913 type TestInt = FixedUInt<u8, 2>;
1914
1915 let mut val = <TestInt as Zero>::zero();
1917 const_set_bit(&mut val.array, 0);
1918 assert_eq!(val, TestInt::from(1u8));
1919
1920 const_set_bit(&mut val.array, 8);
1921 assert_eq!(val, TestInt::from(257u16)); let a = TestInt::from(8u8); let b = TestInt::from(1u8); assert_eq!(
1929 const_cmp_shifted(&a.array, &b.array, 3),
1930 core::cmp::Ordering::Equal
1931 );
1932
1933 assert_eq!(
1935 const_cmp_shifted(&a.array, &b.array, 2),
1936 core::cmp::Ordering::Greater
1937 );
1938
1939 assert_eq!(
1941 const_cmp_shifted(&a.array, &b.array, 4),
1942 core::cmp::Ordering::Less
1943 );
1944
1945 let mut val = TestInt::from(10u8);
1947 let one = TestInt::from(1u8);
1948 const_sub_shifted(&mut val.array, &one.array, 2); assert_eq!(val, TestInt::from(6u8)); }
1951
1952 #[test]
1953 fn test_shifted_operations_comprehensive() {
1954 type TestInt = FixedUInt<u32, 2>;
1955
1956 let a = TestInt::from(0x12345678u32);
1958 let b = TestInt::from(0x12345678u32);
1959
1960 assert_eq!(
1962 const_cmp_shifted(&a.array, &b.array, 0),
1963 core::cmp::Ordering::Equal
1964 );
1965
1966 let c = TestInt::from(0x123u32); let d = TestInt::from(0x48d159e2u32); assert_eq!(
1972 const_cmp_shifted(&d.array, &c.array, 16),
1973 core::cmp::Ordering::Greater
1974 );
1975
1976 let e = TestInt::from(1u32);
1978 let zero = TestInt::from(0u32);
1979 assert_eq!(
1980 const_cmp_shifted(&e.array, &zero.array, 100),
1981 core::cmp::Ordering::Greater
1982 );
1983 assert_eq!(
1985 const_cmp_shifted(&zero.array, &e.array, 100),
1986 core::cmp::Ordering::Equal
1987 );
1988
1989 let mut val = TestInt::from(0x10000u32); let one = TestInt::from(1u32);
1992 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)); }
2000
2001 #[test]
2002 fn test_shifted_operations_edge_cases() {
2003 type TestInt = FixedUInt<u32, 2>;
2004
2005 let a = TestInt::from(42u32);
2007 let a2 = TestInt::from(42u32);
2008 assert_eq!(
2009 const_cmp_shifted(&a.array, &a2.array, 0),
2010 core::cmp::Ordering::Equal
2011 );
2012
2013 let mut b = TestInt::from(42u32);
2014 let ten = TestInt::from(10u32);
2015 const_sub_shifted(&mut b.array, &ten.array, 0);
2016 assert_eq!(b, TestInt::from(32u32));
2017
2018 let c = TestInt::from(123u32);
2020 let large = TestInt::from(456u32);
2021 assert_eq!(
2022 const_cmp_shifted(&c.array, &large.array, 200),
2023 core::cmp::Ordering::Greater
2024 );
2025
2026 let mut d = TestInt::from(123u32);
2027 const_sub_shifted(&mut d.array, &large.array, 200); assert_eq!(d, TestInt::from(123u32));
2029
2030 let zero = TestInt::from(0u32);
2032 let one = TestInt::from(1u32);
2033 assert_eq!(
2034 const_cmp_shifted(&zero.array, &zero.array, 10),
2035 core::cmp::Ordering::Equal
2036 );
2037 assert_eq!(
2038 const_cmp_shifted(&one.array, &zero.array, 10),
2039 core::cmp::Ordering::Greater
2040 );
2041 }
2042
2043 #[test]
2044 fn test_shifted_operations_equivalence() {
2045 type TestInt = FixedUInt<u32, 2>;
2046
2047 let test_cases = [
2049 (0x12345u32, 0x678u32, 4),
2050 (0x1000u32, 0x10u32, 8),
2051 (0xABCDu32, 0x1u32, 16),
2052 (0x80000000u32, 0x1u32, 1),
2053 ];
2054
2055 for (a_val, b_val, shift) in test_cases {
2056 let a = TestInt::from(a_val);
2057 let b = TestInt::from(b_val);
2058
2059 let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2061 let naive_cmp = a.cmp(&(b << shift));
2062 assert_eq!(
2063 optimized_cmp, naive_cmp,
2064 "cmp_shifted mismatch: {} vs ({} << {})",
2065 a_val, b_val, shift
2066 );
2067
2068 if a >= (b << shift) {
2070 let mut optimized_result = a;
2071 const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2072
2073 let naive_result = a - (b << shift);
2074 assert_eq!(
2075 optimized_result, naive_result,
2076 "sub_shifted mismatch: {} - ({} << {})",
2077 a_val, b_val, shift
2078 );
2079 }
2080 }
2081 }
2082
2083 #[test]
2084 fn test_div_assign_in_place_optimization() {
2085 type TestInt = FixedUInt<u32, 2>;
2086
2087 let test_cases = [
2089 (100u32, 10u32, 10u32, 0u32), (123u32, 7u32, 17u32, 4u32), (1000u32, 13u32, 76u32, 12u32), (65535u32, 255u32, 257u32, 0u32), ];
2094
2095 for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2096 let mut dividend = TestInt::from(dividend_val);
2098 let divisor = TestInt::from(divisor_val);
2099
2100 dividend /= divisor;
2101 assert_eq!(
2102 dividend,
2103 TestInt::from(expected_quotient),
2104 "div_assign: {} / {} should be {}",
2105 dividend_val,
2106 divisor_val,
2107 expected_quotient
2108 );
2109
2110 let dividend2 = TestInt::from(dividend_val);
2112 let (quotient, remainder) = dividend2.div_rem(&divisor);
2113 assert_eq!(
2114 quotient,
2115 TestInt::from(expected_quotient),
2116 "div_rem quotient: {} / {} should be {}",
2117 dividend_val,
2118 divisor_val,
2119 expected_quotient
2120 );
2121 assert_eq!(
2122 remainder,
2123 TestInt::from(expected_remainder),
2124 "div_rem remainder: {} % {} should be {}",
2125 dividend_val,
2126 divisor_val,
2127 expected_remainder
2128 );
2129
2130 assert_eq!(
2132 quotient * divisor + remainder,
2133 TestInt::from(dividend_val),
2134 "Property check failed for {}",
2135 dividend_val
2136 );
2137 }
2138 }
2139
2140 #[test]
2141 fn test_div_assign_stack_efficiency() {
2142 type TestInt = FixedUInt<u32, 4>; let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2146 let divisor = TestInt::from(0x12345u32);
2147 let original_dividend = dividend;
2148
2149 dividend /= divisor;
2151
2152 let remainder = original_dividend % divisor;
2154 assert_eq!(dividend * divisor + remainder, original_dividend);
2155 }
2156
2157 #[test]
2158 fn test_rem_assign_optimization() {
2159 type TestInt = FixedUInt<u32, 2>;
2160
2161 let test_cases = [
2162 (100u32, 10u32, 0u32), (123u32, 7u32, 4u32), (1000u32, 13u32, 12u32), (65535u32, 255u32, 0u32), ];
2167
2168 for (dividend_val, divisor_val, expected_remainder) in test_cases {
2169 let mut dividend = TestInt::from(dividend_val);
2170 let divisor = TestInt::from(divisor_val);
2171
2172 dividend %= divisor;
2173 assert_eq!(
2174 dividend,
2175 TestInt::from(expected_remainder),
2176 "rem_assign: {} % {} should be {}",
2177 dividend_val,
2178 divisor_val,
2179 expected_remainder
2180 );
2181 }
2182 }
2183
2184 #[test]
2185 fn test_div_with_remainder_property() {
2186 type TestInt = FixedUInt<u32, 2>;
2187
2188 let test_cases = [
2190 (100u32, 10u32, 10u32), (123u32, 7u32, 17u32), (1000u32, 13u32, 76u32), (65535u32, 255u32, 257u32), ];
2195
2196 for (dividend_val, divisor_val, expected_quotient) in test_cases {
2197 let dividend = TestInt::from(dividend_val);
2198 let divisor = TestInt::from(divisor_val);
2199
2200 let quotient = dividend / divisor;
2202 assert_eq!(
2203 quotient,
2204 TestInt::from(expected_quotient),
2205 "Division: {} / {} should be {}",
2206 dividend_val,
2207 divisor_val,
2208 expected_quotient
2209 );
2210
2211 let remainder = dividend % divisor;
2213 assert_eq!(
2214 quotient * divisor + remainder,
2215 dividend,
2216 "Division property check failed for {}",
2217 dividend_val
2218 );
2219 }
2220 }
2221
2222 #[test]
2223 fn test_code_simplification_benefits() {
2224 type TestInt = FixedUInt<u32, 2>;
2225
2226 let dividend = TestInt::from(12345u32);
2228 let divisor = TestInt::from(67u32);
2229 let quotient = dividend / divisor;
2230 let remainder = dividend % divisor;
2231
2232 assert_eq!(quotient * divisor + remainder, dividend);
2234 }
2235
2236 #[test]
2237 fn test_rem_assign_correctness_after_fix() {
2238 type TestInt = FixedUInt<u32, 2>;
2239
2240 let mut a = TestInt::from(17u32);
2242 let b = TestInt::from(5u32);
2243
2244 a %= b;
2247 assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2248
2249 let mut test_val = TestInt::from(100u32);
2251 test_val %= TestInt::from(7u32);
2252 assert_eq!(
2253 test_val,
2254 TestInt::from(2u32),
2255 "100 % 7 should be 2 (not 14, the quotient)"
2256 );
2257 }
2258
2259 #[test]
2260 fn test_div_property_based() {
2261 type TestInt = FixedUInt<u16, 2>;
2262
2263 let test_pairs = [
2265 (12345u16, 67u16),
2266 (1000u16, 13u16),
2267 (65535u16, 255u16),
2268 (5000u16, 7u16),
2269 ];
2270
2271 for (dividend_val, divisor_val) in test_pairs {
2272 let dividend = TestInt::from(dividend_val);
2273 let divisor = TestInt::from(divisor_val);
2274
2275 let quotient = dividend / divisor;
2276
2277 let remainder = dividend - (quotient * divisor);
2279 let reconstructed = quotient * divisor + remainder;
2280
2281 assert_eq!(
2282 reconstructed,
2283 dividend,
2284 "Property failed for {} / {}: {} * {} + {} != {}",
2285 dividend_val,
2286 divisor_val,
2287 quotient.to_u32().unwrap_or(0),
2288 divisor_val,
2289 remainder.to_u32().unwrap_or(0),
2290 dividend_val
2291 );
2292
2293 assert!(
2295 remainder < divisor,
2296 "Remainder {} >= divisor {} for {} / {}",
2297 remainder.to_u32().unwrap_or(0),
2298 divisor_val,
2299 dividend_val,
2300 divisor_val
2301 );
2302 }
2303 }
2304}