Skip to main content

fixed_bigint/
fixeduint.rs

1// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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// ConstToBytes trait (nightly only, uses generic_const_exprs)
51#[cfg(feature = "nightly")]
52mod const_to_from_bytes;
53// num_traits::ToBytes/FromBytes (stable impl, no generic_const_exprs viral bounds)
54#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
55mod to_from_bytes;
56
57#[cfg(feature = "zeroize")]
58use zeroize::DefaultIsZeroes;
59
60/// Fixed-size unsigned integer, represented by array of N words of builtin unsigned type T
61#[derive(Debug, Copy)]
62pub struct FixedUInt<T, const N: usize>
63where
64    T: MachineWord,
65{
66    /// Little-endian word array
67    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    /// Creates and zero-initializes a FixedUInt.
91    pub fn new() -> FixedUInt<T, N> {
92        FixedUInt {
93            array: [T::zero(); N],
94        }
95    }
96
97    /// Returns the underlying array.
98    pub fn words(&self) -> &[T; N] {
99        &self.array
100    }
101
102    /// Returns number of used bits.
103    pub fn bit_length(&self) -> u32 {
104        Self::BIT_SIZE as u32 - ConstPrimInt::leading_zeros(*self)
105    }
106
107    /// Performs a division, returning both the quotient and remainder in a tuple.
108    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
114// Const-compatible from_bytes helper functions
115c0nst::c0nst! {
116    /// Const-compatible from_le_bytes implementation for slices.
117    /// Derives word_size internally from size_of::<T>().
118    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    /// Const-compatible from_be_bytes implementation for slices.
140    /// Derives word_size internally from size_of::<T>().
141    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        // For consistent truncation semantics with from_le_bytes, always take the
150        // least significant bytes (rightmost bytes in big-endian representation)
151        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            // Take bytes from the end of the input (least significant in BE)
160            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
173// Inherent from_bytes methods (not const - use ConstFromBytes trait for const access)
174impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
175    /// Create a little-endian integer value from its representation as a byte array in little endian.
176    pub fn from_le_bytes(bytes: &[u8]) -> Self {
177        Self {
178            array: impl_from_le_bytes_slice::<T, N>(bytes),
179        }
180    }
181
182    /// Create a big-endian integer value from its representation as a byte array in big endian.
183    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    /// Converts the FixedUInt into a little-endian byte array.
192    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); // Buffer too small
196        }
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    /// Converts the FixedUInt into a big-endian byte array.
207    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); // Buffer too small
211        }
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    /// Converts to hex string, given a buffer. CAVEAT: This method removes any leading zeroes
222    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        // need length minus leading zeros
227        let need_bits = self.bit_length() as usize;
228        // number of needed characters (bits/4 = bytes * 2)
229        let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
230
231        if result.len() < need_chars {
232            // not enough space in result...
233            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    /// Converts to decimal string, given a buffer. CAVEAT: This method removes any leading zeroes
275    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 {}); // Radix out of supported range
284        }
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 {}); // not enough space in result...
305            }
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,          // digits
313                10..=16 => b'a' + (digit - 10), // alphabetic digits for bases > 10
314                _ => 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    /// Const-compatible add implementation operating on raw arrays
372    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    /// Const-compatible sub implementation operating on raw arrays
395    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    /// Const-compatible left shift implementation
418    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 shift >= total bits, result is zero
430        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        // Move words (backwards)
440        let mut i = N;
441        while i > nwords {
442            i -= 1;
443            target.array[i] = target.array[i - nwords];
444        }
445        // Zero out the lower words
446        let mut i = 0;
447        while i < nwords {
448            target.array[i] = T::zero();
449            i += 1;
450        }
451
452        if nbits != 0 {
453            // Shift remaining bits (backwards)
454            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    /// Const-compatible right shift implementation
466    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 shift >= total bits, result is zero
478        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        // Move words (forwards)
491        let mut i = 0;
492        while i < last_word {
493            target.array[i] = target.array[i + nwords];
494            i += 1;
495        }
496
497        // Zero out the upper words
498        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            // Shift remaining bits (forwards)
506            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    /// Standalone const-compatible array multiplication (no FixedUInt dependency)
518    /// Returns (result_array, overflowed)
519    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        // Zero for double word type
528        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    /// Get the bit width of a word type.
568    pub(crate) c0nst fn const_word_bits<T>() -> usize {
569        core::mem::size_of::<T>() * 8
570    }
571
572    /// Compare two words, returning Some(ordering) if not equal, None if equal.
573    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    /// Count leading zeros in a const-compatible way
584    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    /// Count trailing zeros in a const-compatible way
601    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    /// Get bit length of array (total bits - leading zeros)
618    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    /// Check if array is zero
627    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    /// Set a specific bit in the array.
641    ///
642    /// The array uses little-endian representation where index 0 contains
643    /// the least significant word, and bit 0 is the least significant bit
644    /// of the entire integer.
645    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    /// Compare two arrays in a const-compatible way.
659    ///
660    /// Arrays use little-endian representation where index 0 contains
661    /// the least significant word.
662    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    /// Get the value of array's word at position `word_idx` when logically shifted left.
677    ///
678    /// This helper computes what value would be at `word_idx` if the array
679    /// were shifted left by `word_shift` words plus `bit_shift` bits.
680    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        // Guard against invalid bit_shift that would cause UB
689        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            // Get bits from the primary source word
709            if source_idx < N {
710                result |= array[source_idx] << bit_shift;
711            }
712
713            // Get high bits from the next lower word (if it exists)
714            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    /// Compare array vs (other << shift_bits) in a const-compatible way.
724    ///
725    /// This is useful for division algorithms where we need to compare
726    /// the dividend against a shifted divisor without allocating.
727    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            // other << shift_bits would overflow to 0
741            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        // Compare from most significant words down
751        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    /// Subtract (other << shift_bits) from array in-place.
768    ///
769    /// This is used in division algorithms to subtract shifted divisor
770    /// from the remainder without allocating.
771    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    /// In-place division: dividend becomes quotient, returns remainder.
802    ///
803    /// Low-level const-compatible division on arrays.
804    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            // dividend < divisor: quotient = 0, remainder = dividend
812            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            // dividend == divisor: quotient = 1, remainder = 0
822            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        // Calculate initial bit position
839        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        // Adjust bit position to find the first position where divisor can be subtracted
849        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        // Main division loop
858        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    /// Const-compatible div_rem: returns (quotient, remainder).
877    ///
878    /// Panics on divide by zero.
879    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
908// #region Equality and Ordering
909
910c0nst::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
932// #endregion Equality and Ordering
933
934// #region core::convert::From<primitive>
935
936c0nst::c0nst! {
937    /// Const-compatible conversion from little-endian bytes to array of words.
938    /// Delegates to impl_from_le_bytes_slice to avoid code duplication.
939    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
970// #endregion core::convert::From<primitive>
971
972// #region helpers
973
974// This is slightly less than ideal, but PIE isn't directly constructible
975// due to unstable members.
976fn 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// #endregion helpers
1025
1026#[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        // Simple add, no overflow
1118        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        // Add with carry propagation
1125        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        // Add with overflow
1132        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        // Test with u32 words
1139        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        // Simple sub, no overflow
1160        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        // Sub with borrow propagation
1167        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        // Sub with underflow
1174        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        // Test with u32 words
1181        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        // Simple mul: 3 * 4 = 12
1202        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        // Mul with carry: 200 * 2 = 400 = 0x190 = [0x90, 0x01]
1209        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        // Mul with overflow: 256 * 256 = 65536 which overflows 16 bits
1216        let a: [u8; 2] = [0, 1]; // 256
1217        let b: [u8; 2] = [0, 1]; // 256
1218        let (_result, overflow) = test_mul(&a, &b, 8);
1219        assert!(overflow);
1220
1221        // N=3 overflow at high position (round=4, i=2, j=2)
1222        // a = [0, 0, 1] = 65536, b = [0, 0, 1] = 65536
1223        // a * b = 65536^2 = 4294967296 which overflows 24 bits
1224        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        // N=3 overflow with larger high word values
1230        // a = [0, 0, 2] = 131072, b = [0, 0, 2] = 131072
1231        // a * b = 131072^2 = 17179869184 which overflows 24 bits
1232        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        // N=3 non-overflow case: values that fit in 24 bits
1241        // a = [0, 1, 0] = 256, b = [0, 1, 0] = 256
1242        // a * b = 256 * 256 = 65536 = [0, 0, 1] which fits in 24 bits
1243        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        // N=3 non-overflow with carry propagation
1253        // a = [255, 0, 0] = 255, b = [255, 0, 0] = 255
1254        // a * b = 255 * 255 = 65025 = 0xFE01 = [0x01, 0xFE, 0x00]
1255        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        // Test leading_zeros
1271        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1272        assert_eq!(arr_leading_zeros(&[1u8, 0, 0, 0]), 31); // single bit
1273        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 1]), 7); // high byte has 1
1274        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0x80]), 0); // MSB set
1275        assert_eq!(arr_leading_zeros(&[255u8, 255, 255, 255]), 0); // all ones
1276
1277        // Test trailing_zeros
1278        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1279        assert_eq!(arr_trailing_zeros(&[1u8, 0, 0, 0]), 0); // LSB set
1280        assert_eq!(arr_trailing_zeros(&[0u8, 1, 0, 0]), 8); // second byte
1281        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 1]), 24); // fourth byte
1282        assert_eq!(arr_trailing_zeros(&[0x80u8, 0, 0, 0]), 7); // bit 7 of first byte
1283
1284        // Test bit_length
1285        assert_eq!(arr_bit_length(&[0u8, 0, 0, 0]), 0); // zero
1286        assert_eq!(arr_bit_length(&[1u8, 0, 0, 0]), 1); // 1
1287        assert_eq!(arr_bit_length(&[2u8, 0, 0, 0]), 2); // 2
1288        assert_eq!(arr_bit_length(&[3u8, 0, 0, 0]), 2); // 3
1289        assert_eq!(arr_bit_length(&[0u8, 1, 0, 0]), 9); // 256
1290        assert_eq!(arr_bit_length(&[0xF0u8, 0, 0, 0]), 8); // 240 (0xF0)
1291        assert_eq!(arr_bit_length(&[255u8, 255, 255, 255]), 32); // max
1292
1293        // Test is_zero
1294        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        // Test set_bit
1300        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        // Set multiple bits
1313        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        // Out of bounds should be no-op
1320        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        // Test with u32 words
1325        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        // Equal arrays
1365        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        // Greater - high word differs
1369        assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1370
1371        // Less - high word differs
1372        assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1373
1374        // Greater - low word differs (high words equal)
1375        assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1376
1377        // Less - low word differs
1378        assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1379
1380        // Test with u32 words
1381        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        // No shift - same as regular cmp
1401        assert_eq!(
1402            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1403            Ordering::Equal
1404        );
1405
1406        // Compare [0, 1, 0, 0] (256) vs [1, 0, 0, 0] << 8 (256) = Equal
1407        assert_eq!(
1408            arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1409            Ordering::Equal
1410        );
1411
1412        // Compare [0, 2, 0, 0] (512) vs [1, 0, 0, 0] << 8 (256) = Greater
1413        assert_eq!(
1414            arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1415            Ordering::Greater
1416        );
1417
1418        // Compare [0, 0, 0, 0] (0) vs [1, 0, 0, 0] << 8 (256) = Less
1419        assert_eq!(
1420            arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1421            Ordering::Less
1422        );
1423
1424        // Shift overflow: shift >= bit_size, other becomes 0
1425        // Compare [1, 0, 0, 0] vs [1, 0, 0, 0] << 32 (0) = Greater
1426        assert_eq!(
1427            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1428            Ordering::Greater
1429        );
1430
1431        // Compare [0, 0, 0, 0] vs anything << 32 (0) = Equal
1432        assert_eq!(
1433            arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1434            Ordering::Equal
1435        );
1436
1437        // Test get_shifted_word helper with bit_shift == 0
1438        // [1, 2, 3, 4] shifted left by 1 word (8 bits for u8)
1439        // word 0 should be 0, word 1 should be 1, word 2 should be 2, etc.
1440        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        // Test get_shifted_word with bit_shift != 0 (cross-word bit combination)
1445        // [0x0F, 0xF0, 0, 0] with word_shift=0, bit_shift=4
1446        // word 0: 0x0F << 4 = 0xF0 (no lower word to borrow from)
1447        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1448        // word 1: (0xF0 << 4) | (0x0F >> 4) = 0x00 | 0x00 = 0x00
1449        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1450
1451        // [0xFF, 0x00, 0, 0] with bit_shift=4
1452        // word 0: 0xFF << 4 = 0xF0
1453        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1454        // word 1: (0x00 << 4) | (0xFF >> 4) = 0x00 | 0x0F = 0x0F
1455        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1456
1457        // Combined word_shift and bit_shift
1458        // [0xAB, 0xCD, 0, 0] with word_shift=1, bit_shift=4
1459        // word 0: below word_shift, returns 0
1460        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1461        // word 1: source_idx=0, 0xAB << 4 = 0xB0 (no lower word)
1462        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1463        // word 2: source_idx=1, (0xCD << 4) | (0xAB >> 4) = 0xD0 | 0x0A = 0xDA
1464        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        // Overflow
1617        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        // Test bit_length with value 1000
1708        let value = Bn32::from_u16(1000).unwrap();
1709
1710        // 1000 in binary is 1111101000, which has 10 bits
1711        // Let's verify the implementation is working correctly
1712        assert_eq!(value.to_u32().unwrap(), 1000);
1713        assert_eq!(value.bit_length(), 10);
1714
1715        // Test some edge cases around 1000
1716        assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); // 2^9 = 512
1717        assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); // 2^10 - 1 = 1023
1718        assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); // 2^10 = 1024
1719
1720        // Test with different word sizes to see if this makes a difference
1721        assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
1722        assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1723
1724        // Test with different initialization methods
1725        let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1726        assert_eq!(value_from_str.bit_length(), 10);
1727
1728        // This is the problematic case - let's debug it
1729        let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1730        // Let's see what the actual value is
1731        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 suite for division implementation
1832    #[test]
1833    fn test_div_small() {
1834        type TestInt = FixedUInt<u8, 2>;
1835
1836        // Test small values
1837        let test_cases = [
1838            (20u16, 3u16, 6u16),        // 20 / 3 = 6
1839            (100u16, 7u16, 14u16),      // 100 / 7 = 14
1840            (255u16, 5u16, 51u16),      // 255 / 5 = 51
1841            (65535u16, 256u16, 255u16), // max u16 / 256 = 255
1842        ];
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        // Division by 1
1865        let dividend = TestInt::from(1000u16);
1866        let divisor = TestInt::from(1u16);
1867        assert_eq!(dividend / divisor, TestInt::from(1000u16));
1868
1869        // Equal values
1870        let dividend = TestInt::from(42u16);
1871        let divisor = TestInt::from(42u16);
1872        assert_eq!(dividend / divisor, TestInt::from(1u16));
1873
1874        // Dividend < divisor
1875        let dividend = TestInt::from(5u16);
1876        let divisor = TestInt::from(10u16);
1877        assert_eq!(dividend / divisor, TestInt::from(0u16));
1878
1879        // Powers of 2
1880        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        // Test const_set_bit
1890        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)); // bit 0 + bit 8 = 1 + 256 = 257
1896
1897        // Test const_cmp_shifted
1898        let a = TestInt::from(8u8); // 1000 in binary
1899        let b = TestInt::from(1u8); // 0001 in binary
1900
1901        // b << 3 = 8, so a == (b << 3)
1902        assert_eq!(
1903            const_cmp_shifted(&a.array, &b.array, 3),
1904            core::cmp::Ordering::Equal
1905        );
1906
1907        // a > (b << 2) because b << 2 = 4
1908        assert_eq!(
1909            const_cmp_shifted(&a.array, &b.array, 2),
1910            core::cmp::Ordering::Greater
1911        );
1912
1913        // a < (b << 4) because b << 4 = 16
1914        assert_eq!(
1915            const_cmp_shifted(&a.array, &b.array, 4),
1916            core::cmp::Ordering::Less
1917        );
1918
1919        // Test const_sub_shifted
1920        let mut val = TestInt::from(10u8);
1921        let one = TestInt::from(1u8);
1922        const_sub_shifted(&mut val.array, &one.array, 2); // subtract 1 << 2 = 4
1923        assert_eq!(val, TestInt::from(6u8)); // 10 - 4 = 6
1924    }
1925
1926    #[test]
1927    fn test_shifted_operations_comprehensive() {
1928        type TestInt = FixedUInt<u32, 2>;
1929
1930        // Test cmp_shifted with various word boundary cases
1931        let a = TestInt::from(0x12345678u32);
1932        let b = TestInt::from(0x12345678u32);
1933
1934        // Equal comparison
1935        assert_eq!(
1936            const_cmp_shifted(&a.array, &b.array, 0),
1937            core::cmp::Ordering::Equal
1938        );
1939
1940        // Test shifts that cross word boundaries (assuming 32-bit words)
1941        let c = TestInt::from(0x123u32); // Small number
1942        let d = TestInt::from(0x48d159e2u32); // c << 16 + some bits
1943
1944        // c << 16 should be less than d
1945        assert_eq!(
1946            const_cmp_shifted(&d.array, &c.array, 16),
1947            core::cmp::Ordering::Greater
1948        );
1949
1950        // Test large shifts (beyond bit size, so shifted value becomes 0)
1951        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        // When shift is beyond bit size, 1 << 100 becomes 0, so 0 == 0
1958        assert_eq!(
1959            const_cmp_shifted(&zero.array, &e.array, 100),
1960            core::cmp::Ordering::Equal
1961        );
1962
1963        // Test sub_shifted with word boundary crossing
1964        let mut val = TestInt::from(0x10000u32); // 65536
1965        let one = TestInt::from(1u32);
1966        const_sub_shifted(&mut val.array, &one.array, 15); // subtract 1 << 15 = 32768
1967        assert_eq!(val, TestInt::from(0x8000u32)); // 65536 - 32768 = 32768
1968
1969        // Test sub_shifted with multi-word operations
1970        let mut big_val = TestInt::from(0x100000000u64); // 2^32
1971        const_sub_shifted(&mut big_val.array, &one.array, 31); // subtract 1 << 31 = 2^31
1972        assert_eq!(big_val, TestInt::from(0x80000000u64)); // 2^32 - 2^31 = 2^31
1973    }
1974
1975    #[test]
1976    fn test_shifted_operations_edge_cases() {
1977        type TestInt = FixedUInt<u32, 2>;
1978
1979        // Test zero shifts
1980        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        // Test massive shifts (beyond bit size)
1993        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); // Should be no-op
2002        assert_eq!(d, TestInt::from(123u32));
2003
2004        // Test with zero values
2005        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        // Test that optimized operations give same results as naive shift+op
2022        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            // Test cmp_shifted equivalence
2034            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            // Test sub_shifted equivalence (if subtraction won't underflow)
2043            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        // Test that div_assign uses the optimized in-place algorithm
2062        let test_cases = [
2063            (100u32, 10u32, 10u32, 0u32),     // 100 / 10 = 10 remainder 0
2064            (123u32, 7u32, 17u32, 4u32),      // 123 / 7 = 17 remainder 4
2065            (1000u32, 13u32, 76u32, 12u32),   // 1000 / 13 = 76 remainder 12
2066            (65535u32, 255u32, 257u32, 0u32), // 65535 / 255 = 257 remainder 0
2067        ];
2068
2069        for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2070            // Test div_assign
2071            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            // Test div_rem directly
2085            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            // Verify: quotient * divisor + remainder == original dividend
2105            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>; // 16 bytes each
2117
2118        // Create test values
2119        let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2120        let divisor = TestInt::from(0x12345u32);
2121        let original_dividend = dividend;
2122
2123        // Perform in-place division
2124        dividend /= divisor;
2125
2126        // Verify correctness
2127        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),    // 100 % 10 = 0
2137            (123u32, 7u32, 4u32),     // 123 % 7 = 4
2138            (1000u32, 13u32, 12u32),  // 1000 % 13 = 12
2139            (65535u32, 255u32, 0u32), // 65535 % 255 = 0
2140        ];
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        // Test division with remainder property verification
2163        let test_cases = [
2164            (100u32, 10u32, 10u32),     // 100 / 10 = 10
2165            (123u32, 7u32, 17u32),      // 123 / 7 = 17
2166            (1000u32, 13u32, 76u32),    // 1000 / 13 = 76
2167            (65535u32, 255u32, 257u32), // 65535 / 255 = 257
2168        ];
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            // Test that div operator (which uses div_impl) works correctly
2175            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            // Verify the division property still holds
2186            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        // Verify division property holds
2201        let dividend = TestInt::from(12345u32);
2202        let divisor = TestInt::from(67u32);
2203        let quotient = dividend / divisor;
2204        let remainder = dividend % divisor;
2205
2206        // The division property should still hold
2207        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        // Test specific case: 17 % 5 = 2
2215        let mut a = TestInt::from(17u32);
2216        let b = TestInt::from(5u32);
2217
2218        // Historical note: an old bug caused quotient corruption during remainder calculation
2219        // Now const_div_rem properly computes both without corrupting intermediate state
2220        a %= b;
2221        assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2222
2223        // Test that the original RemAssign bug would have failed this
2224        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        // Property: quotient * divisor + remainder == dividend
2238        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            // Property verification: quotient * divisor + remainder == dividend
2252            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            // Remainder should be less than divisor
2268            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}