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_acc_ops_impl;
42mod mul_div_impl;
43mod multiple_impl;
44mod num_integer_impl;
45mod num_traits_casts;
46mod num_traits_identity;
47mod power_of_two_impl;
48mod prim_int_impl;
49mod roots_impl;
50mod string_conversion;
51// ConstToBytes trait (nightly only, uses generic_const_exprs)
52#[cfg(feature = "nightly")]
53mod const_to_from_bytes;
54// num_traits::ToBytes/FromBytes (stable impl, no generic_const_exprs viral bounds)
55#[cfg(any(feature = "nightly", feature = "use-unsafe"))]
56mod to_from_bytes;
57
58#[cfg(feature = "zeroize")]
59use zeroize::DefaultIsZeroes;
60
61/// Fixed-size unsigned integer, represented by array of N words of builtin unsigned type T
62#[derive(Debug, Copy)]
63pub struct FixedUInt<T, const N: usize>
64where
65    T: MachineWord,
66{
67    /// Little-endian word array
68    pub(super) array: [T; N],
69}
70
71#[cfg(feature = "zeroize")]
72impl<T: MachineWord, const N: usize> DefaultIsZeroes for FixedUInt<T, N> {}
73
74impl<T, const N: usize> From<[T; N]> for FixedUInt<T, N>
75where
76    T: MachineWord,
77{
78    fn from(array: [T; N]) -> Self {
79        Self { array }
80    }
81}
82
83const LONGEST_WORD_IN_BITS: usize = 128;
84
85impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
86    const WORD_SIZE: usize = core::mem::size_of::<T>();
87    const WORD_BITS: usize = Self::WORD_SIZE * 8;
88    const BYTE_SIZE: usize = Self::WORD_SIZE * N;
89    const BIT_SIZE: usize = Self::BYTE_SIZE * 8;
90
91    /// Creates and zero-initializes a FixedUInt.
92    pub fn new() -> FixedUInt<T, N> {
93        FixedUInt {
94            array: [T::zero(); N],
95        }
96    }
97
98    /// Returns the underlying array.
99    pub fn words(&self) -> &[T; N] {
100        &self.array
101    }
102
103    /// Returns number of used bits.
104    pub fn bit_length(&self) -> u32 {
105        Self::BIT_SIZE as u32 - ConstPrimInt::leading_zeros(*self)
106    }
107
108    /// Performs a division, returning both the quotient and remainder in a tuple.
109    pub fn div_rem(&self, divisor: &Self) -> (Self, Self) {
110        let (quotient, remainder) = const_div_rem(&self.array, &divisor.array);
111        (Self { array: quotient }, Self { array: remainder })
112    }
113}
114
115// Const-compatible from_bytes helper functions
116c0nst::c0nst! {
117    /// Const-compatible from_le_bytes implementation for slices.
118    /// Derives word_size internally from size_of::<T>().
119    pub(crate) c0nst fn impl_from_le_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
120        bytes: &[u8],
121    ) -> [T; N] {
122        let word_size = core::mem::size_of::<T>();
123        let mut ret: [T; N] = [T::zero(); N];
124        let capacity = N * word_size;
125        let total_bytes = if bytes.len() < capacity { bytes.len() } else { capacity };
126
127        let mut byte_index = 0;
128        while byte_index < total_bytes {
129            let word_index = byte_index / word_size;
130            let byte_in_word = byte_index % word_size;
131
132            let byte_value: T = T::from(bytes[byte_index]);
133            let shifted_value = byte_value.shl(byte_in_word * 8);
134            ret[word_index] = ret[word_index].bitor(shifted_value);
135            byte_index += 1;
136        }
137        ret
138    }
139
140    /// Const-compatible from_be_bytes implementation for slices.
141    /// Derives word_size internally from size_of::<T>().
142    pub(crate) c0nst fn impl_from_be_bytes_slice<T: [c0nst] ConstMachineWord, const N: usize>(
143        bytes: &[u8],
144    ) -> [T; N] {
145        let word_size = core::mem::size_of::<T>();
146        let mut ret: [T; N] = [T::zero(); N];
147        let capacity_bytes = N * word_size;
148        let total_bytes = if bytes.len() < capacity_bytes { bytes.len() } else { capacity_bytes };
149
150        // For consistent truncation semantics with from_le_bytes, always take the
151        // least significant bytes (rightmost bytes in big-endian representation)
152        let start_offset = if bytes.len() > capacity_bytes {
153            bytes.len() - capacity_bytes
154        } else {
155            0
156        };
157
158        let mut byte_index = 0;
159        while byte_index < total_bytes {
160            // Take bytes from the end of the input (least significant in BE)
161            let be_byte_index = start_offset + total_bytes - 1 - byte_index;
162            let word_index = byte_index / word_size;
163            let byte_in_word = byte_index % word_size;
164
165            let byte_value: T = T::from(bytes[be_byte_index]);
166            let shifted_value = byte_value.shl(byte_in_word * 8);
167            ret[word_index] = ret[word_index].bitor(shifted_value);
168            byte_index += 1;
169        }
170        ret
171    }
172}
173
174// Inherent from_bytes methods (not const - use ConstFromBytes trait for const access)
175impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
176    /// Create a little-endian integer value from its representation as a byte array in little endian.
177    pub fn from_le_bytes(bytes: &[u8]) -> Self {
178        Self {
179            array: impl_from_le_bytes_slice::<T, N>(bytes),
180        }
181    }
182
183    /// Create a big-endian integer value from its representation as a byte array in big endian.
184    pub fn from_be_bytes(bytes: &[u8]) -> Self {
185        Self {
186            array: impl_from_be_bytes_slice::<T, N>(bytes),
187        }
188    }
189}
190
191impl<T: MachineWord, const N: usize> FixedUInt<T, N> {
192    /// Converts the FixedUInt into a little-endian byte array.
193    pub fn to_le_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
194        let total_bytes = N * Self::WORD_SIZE;
195        if output_buffer.len() < total_bytes {
196            return Err(false); // Buffer too small
197        }
198        for (i, word) in self.array.iter().enumerate() {
199            let start = i * Self::WORD_SIZE;
200            let end = start + Self::WORD_SIZE;
201            let word_bytes = word.to_le_bytes();
202            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
203        }
204        Ok(&output_buffer[..total_bytes])
205    }
206
207    /// Converts the FixedUInt into a big-endian byte array.
208    pub fn to_be_bytes<'a>(&self, output_buffer: &'a mut [u8]) -> Result<&'a [u8], bool> {
209        let total_bytes = N * Self::WORD_SIZE;
210        if output_buffer.len() < total_bytes {
211            return Err(false); // Buffer too small
212        }
213        for (i, word) in self.array.iter().rev().enumerate() {
214            let start = i * Self::WORD_SIZE;
215            let end = start + Self::WORD_SIZE;
216            let word_bytes = word.to_be_bytes();
217            output_buffer[start..end].copy_from_slice(word_bytes.as_ref());
218        }
219        Ok(&output_buffer[..total_bytes])
220    }
221
222    /// Converts to hex string, given a buffer. CAVEAT: This method removes any leading zeroes
223    pub fn to_hex_str<'a>(&self, result: &'a mut [u8]) -> Result<&'a str, core::fmt::Error> {
224        type Error = core::fmt::Error;
225
226        let word_size = Self::WORD_SIZE;
227        // need length minus leading zeros
228        let need_bits = self.bit_length() as usize;
229        // number of needed characters (bits/4 = bytes * 2)
230        let need_chars = if need_bits > 0 { need_bits / 4 } else { 0 };
231
232        if result.len() < need_chars {
233            // not enough space in result...
234            return Err(Error {});
235        }
236        let offset = result.len() - need_chars;
237        for i in result.iter_mut() {
238            *i = b'0';
239        }
240
241        for iter_words in 0..self.array.len() {
242            let word = self.array[iter_words];
243            let mut encoded = [0u8; LONGEST_WORD_IN_BITS / 4];
244            let encode_slice = &mut encoded[0..word_size * 2];
245            let mut wordbytes = word.to_le_bytes();
246            wordbytes.as_mut().reverse();
247            let wordslice = wordbytes.as_ref();
248            to_slice_hex(wordslice, encode_slice).map_err(|_| Error {})?;
249            for iter_chars in 0..encode_slice.len() {
250                let copy_char_to = (iter_words * word_size * 2) + iter_chars;
251                if copy_char_to <= need_chars {
252                    let reverse_index = offset + (need_chars - copy_char_to);
253                    if reverse_index <= result.len() && reverse_index > 0 {
254                        let current_char = encode_slice[(encode_slice.len() - 1) - iter_chars];
255                        result[reverse_index - 1] = current_char;
256                    }
257                }
258            }
259        }
260
261        let convert = core::str::from_utf8(result).map_err(|_| Error {})?;
262        let pos = convert.find(|c: char| c != '0');
263        match pos {
264            Some(x) => Ok(&convert[x..convert.len()]),
265            None => {
266                if convert.starts_with('0') {
267                    Ok("0")
268                } else {
269                    Ok(convert)
270                }
271            }
272        }
273    }
274
275    /// Converts to decimal string, given a buffer. CAVEAT: This method removes any leading zeroes
276    pub fn to_radix_str<'a>(
277        &self,
278        result: &'a mut [u8],
279        radix: u8,
280    ) -> Result<&'a str, core::fmt::Error> {
281        type Error = core::fmt::Error;
282
283        if !(2..=16).contains(&radix) {
284            return Err(Error {}); // Radix out of supported range
285        }
286        for byte in result.iter_mut() {
287            *byte = b'0';
288        }
289        if Zero::is_zero(self) {
290            if !result.is_empty() {
291                result[0] = b'0';
292                return core::str::from_utf8(&result[0..1]).map_err(|_| Error {});
293            } else {
294                return Err(Error {});
295            }
296        }
297
298        let mut number = *self;
299        let mut idx = result.len();
300
301        let radix_t = Self::from(radix);
302
303        while !Zero::is_zero(&number) {
304            if idx == 0 {
305                return Err(Error {}); // not enough space in result...
306            }
307
308            idx -= 1;
309            let (quotient, remainder) = number.div_rem(&radix_t);
310
311            let digit = remainder.to_u8().unwrap();
312            result[idx] = match digit {
313                0..=9 => b'0' + digit,          // digits
314                10..=16 => b'a' + (digit - 10), // alphabetic digits for bases > 10
315                _ => return Err(Error {}),
316            };
317
318            number = quotient;
319        }
320
321        let start = result[idx..].iter().position(|&c| c != b'0').unwrap_or(0);
322        let radix_str = core::str::from_utf8(&result[idx + start..]).map_err(|_| Error {})?;
323        Ok(radix_str)
324    }
325
326    /// Construct a new value with a different size.
327    ///
328    /// - If `N2 < N`, the most-significant (upper) words are truncated.
329    /// - If `N2 > N`, the additional most-significant words are filled with zeros.
330    #[must_use]
331    pub fn resize<const N2: usize>(&self) -> FixedUInt<T, N2> {
332        let mut array = [T::zero(); N2];
333        let min_size = N.min(N2);
334        array[..min_size].copy_from_slice(&self.array[..min_size]);
335        FixedUInt { array }
336    }
337
338    fn hex_fmt(
339        &self,
340        formatter: &mut core::fmt::Formatter<'_>,
341        uppercase: bool,
342    ) -> Result<(), core::fmt::Error>
343    where
344        u8: core::convert::TryFrom<T>,
345    {
346        type Err = core::fmt::Error;
347
348        fn to_casedigit(byte: u8, uppercase: bool) -> Result<char, core::fmt::Error> {
349            let digit = core::char::from_digit(byte as u32, 16).ok_or(Err {})?;
350            if uppercase {
351                digit.to_uppercase().next().ok_or(Err {})
352            } else {
353                digit.to_lowercase().next().ok_or(Err {})
354            }
355        }
356
357        let mut leading_zero: bool = true;
358
359        let mut maybe_write = |nibble: char| -> Result<(), core::fmt::Error> {
360            leading_zero &= nibble == '0';
361            if !leading_zero {
362                formatter.write_char(nibble)?;
363            }
364            Ok(())
365        };
366
367        for index in (0..N).rev() {
368            let val = self.array[index];
369            let mask: T = 0xff.into();
370            for j in (0..Self::WORD_SIZE as u32).rev() {
371                let masked = val & mask.shl((j * 8) as usize);
372
373                let byte = u8::try_from(masked.shr((j * 8) as usize)).map_err(|_| Err {})?;
374
375                maybe_write(to_casedigit((byte & 0xf0) >> 4, uppercase)?)?;
376                maybe_write(to_casedigit(byte & 0x0f, uppercase)?)?;
377            }
378        }
379        Ok(())
380    }
381}
382
383c0nst::c0nst! {
384    /// Const-compatible add implementation operating on raw arrays
385    pub(crate) c0nst fn add_impl<T: [c0nst] ConstMachineWord, const N: usize>(
386        target: &mut [T; N],
387        other: &[T; N]
388    ) -> bool {
389        let mut carry = T::zero();
390        let mut i = 0usize;
391        while i < N {
392            let (res, carry1) = target[i].overflowing_add(&other[i]);
393            let (res, carry2) = res.overflowing_add(&carry);
394            carry = if carry1 || carry2 {
395                T::one()
396            } else {
397                T::zero()
398            };
399            target[i] = res;
400            i += 1;
401        }
402        !carry.is_zero()
403    }
404}
405
406c0nst::c0nst! {
407    /// Const-compatible sub implementation operating on raw arrays
408    pub(crate) c0nst fn sub_impl<T: [c0nst] ConstMachineWord, const N: usize>(
409        target: &mut [T; N],
410        other: &[T; N]
411    ) -> bool {
412        let mut borrow = T::zero();
413        let mut i = 0usize;
414        while i < N {
415            let (res, borrow1) = target[i].overflowing_sub(&other[i]);
416            let (res, borrow2) = res.overflowing_sub(&borrow);
417            borrow = if borrow1 || borrow2 {
418                T::one()
419            } else {
420                T::zero()
421            };
422            target[i] = res;
423            i += 1;
424        }
425        !borrow.is_zero()
426    }
427}
428
429c0nst::c0nst! {
430    /// Const-compatible left shift implementation
431    pub(crate) c0nst fn const_shl_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
432        target: &mut FixedUInt<T, N>,
433        bits: usize,
434    ) {
435        if N == 0 {
436            return;
437        }
438        let word_bits = FixedUInt::<T, N>::WORD_BITS;
439        let nwords = bits / word_bits;
440        let nbits = bits - nwords * word_bits;
441
442        // If shift >= total bits, result is zero
443        if nwords >= N {
444            let mut i = 0;
445            while i < N {
446                target.array[i] = T::zero();
447                i += 1;
448            }
449            return;
450        }
451
452        // Move words (backwards)
453        let mut i = N;
454        while i > nwords {
455            i -= 1;
456            target.array[i] = target.array[i - nwords];
457        }
458        // Zero out the lower words
459        let mut i = 0;
460        while i < nwords {
461            target.array[i] = T::zero();
462            i += 1;
463        }
464
465        if nbits != 0 {
466            // Shift remaining bits (backwards)
467            let mut i = N;
468            while i > 1 {
469                i -= 1;
470                let right = target.array[i] << nbits;
471                let left = target.array[i - 1] >> (word_bits - nbits);
472                target.array[i] = right | left;
473            }
474            target.array[0] <<= nbits;
475        }
476    }
477
478    /// Const-compatible right shift implementation
479    pub(crate) c0nst fn const_shr_impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize>(
480        target: &mut FixedUInt<T, N>,
481        bits: usize,
482    ) {
483        if N == 0 {
484            return;
485        }
486        let word_bits = FixedUInt::<T, N>::WORD_BITS;
487        let nwords = bits / word_bits;
488        let nbits = bits - nwords * word_bits;
489
490        // If shift >= total bits, result is zero
491        if nwords >= N {
492            let mut i = 0;
493            while i < N {
494                target.array[i] = T::zero();
495                i += 1;
496            }
497            return;
498        }
499
500        let last_index = N - 1;
501        let last_word = N - nwords;
502
503        // Move words (forwards)
504        let mut i = 0;
505        while i < last_word {
506            target.array[i] = target.array[i + nwords];
507            i += 1;
508        }
509
510        // Zero out the upper words
511        let mut i = last_word;
512        while i < N {
513            target.array[i] = T::zero();
514            i += 1;
515        }
516
517        if nbits != 0 {
518            // Shift remaining bits (forwards)
519            let mut i = 0;
520            while i < last_index {
521                let left = target.array[i] >> nbits;
522                let right = target.array[i + 1] << (word_bits - nbits);
523                target.array[i] = left | right;
524                i += 1;
525            }
526            target.array[last_index] >>= nbits;
527        }
528    }
529
530    /// Standalone const-compatible array multiplication (no FixedUInt dependency)
531    /// Returns (result_array, overflowed)
532    pub(crate) c0nst fn const_mul<T: [c0nst] ConstMachineWord, const N: usize, const CHECK_OVERFLOW: bool>(
533        op1: &[T; N],
534        op2: &[T; N],
535        word_bits: usize,
536    ) -> ([T; N], bool) {
537        let mut result: [T; N] = [<T as ConstZero>::zero(); N];
538        let mut overflowed = false;
539        let t_max = <T as ConstMachineWord>::to_double(<T as ConstBounded>::max_value());
540        // Zero for double word type
541        let dw_zero = <<T as ConstMachineWord>::ConstDoubleWord as ConstZero>::zero();
542
543        let mut i = 0;
544        while i < N {
545            let mut carry = dw_zero;
546            let mut j = 0;
547            while j < N {
548                let round = i + j;
549                let op1_dw = <T as ConstMachineWord>::to_double(op1[i]);
550                let op2_dw = <T as ConstMachineWord>::to_double(op2[j]);
551                let mul_res = op1_dw * op2_dw;
552                let mut accumulator = if round < N {
553                    <T as ConstMachineWord>::to_double(result[round])
554                } else {
555                    dw_zero
556                };
557                accumulator = accumulator + mul_res + carry;
558
559                if accumulator > t_max {
560                    carry = accumulator >> word_bits;
561                    accumulator &= t_max;
562                } else {
563                    carry = dw_zero;
564                }
565                if round < N {
566                    result[round] = <T as ConstMachineWord>::from_double(accumulator);
567                } else if CHECK_OVERFLOW {
568                    overflowed = overflowed || accumulator != dw_zero;
569                }
570                j += 1;
571            }
572            if carry != dw_zero && CHECK_OVERFLOW {
573                overflowed = true;
574            }
575            i += 1;
576        }
577        (result, overflowed)
578    }
579
580    /// Get the bit width of a word type.
581    pub(crate) c0nst fn const_word_bits<T>() -> usize {
582        core::mem::size_of::<T>() * 8
583    }
584
585    /// Compare two words, returning Some(ordering) if not equal, None if equal.
586    pub(crate) c0nst fn const_cmp_words<T: [c0nst] ConstMachineWord>(a: T, b: T) -> Option<core::cmp::Ordering> {
587        if a > b {
588            Some(core::cmp::Ordering::Greater)
589        } else if a < b {
590            Some(core::cmp::Ordering::Less)
591        } else {
592            None
593        }
594    }
595
596    /// Count leading zeros in a const-compatible way
597    pub(crate) c0nst fn const_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
598        array: &[T; N],
599    ) -> u32 {
600        let mut ret = 0u32;
601        let mut index = N;
602        while index > 0 {
603            index -= 1;
604            let v = array[index];
605            ret += <T as ConstPrimInt>::leading_zeros(v);
606            if !<T as ConstZero>::is_zero(&v) {
607                break;
608            }
609        }
610        ret
611    }
612
613    /// Count trailing zeros in a const-compatible way
614    pub(crate) c0nst fn const_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
615        array: &[T; N],
616    ) -> u32 {
617        let mut ret = 0u32;
618        let mut index = 0;
619        while index < N {
620            let v = array[index];
621            ret += <T as ConstPrimInt>::trailing_zeros(v);
622            if !<T as ConstZero>::is_zero(&v) {
623                break;
624            }
625            index += 1;
626        }
627        ret
628    }
629
630    /// Get bit length of array (total bits - leading zeros)
631    pub(crate) c0nst fn const_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
632        array: &[T; N],
633    ) -> usize {
634        let word_bits = const_word_bits::<T>();
635        let bit_size = N * word_bits;
636        bit_size - const_leading_zeros::<T, N>(array) as usize
637    }
638
639    /// Check if array is zero
640    pub(crate) c0nst fn const_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
641        array: &[T; N],
642    ) -> bool {
643        let mut index = 0;
644        while index < N {
645            if !<T as ConstZero>::is_zero(&array[index]) {
646                return false;
647            }
648            index += 1;
649        }
650        true
651    }
652
653    /// Set a specific bit in the array.
654    ///
655    /// The array uses little-endian representation where index 0 contains
656    /// the least significant word, and bit 0 is the least significant bit
657    /// of the entire integer.
658    pub(crate) c0nst fn const_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
659        array: &mut [T; N],
660        pos: usize,
661    ) {
662        let word_bits = const_word_bits::<T>();
663        let word_idx = pos / word_bits;
664        if word_idx >= N {
665            return;
666        }
667        let bit_idx = pos % word_bits;
668        array[word_idx] |= <T as ConstOne>::one() << bit_idx;
669    }
670
671    /// Compare two arrays in a const-compatible way.
672    ///
673    /// Arrays use little-endian representation where index 0 contains
674    /// the least significant word.
675    pub(crate) c0nst fn const_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
676        a: &[T; N],
677        b: &[T; N],
678    ) -> core::cmp::Ordering {
679        let mut index = N;
680        while index > 0 {
681            index -= 1;
682            if let Some(ord) = const_cmp_words(a[index], b[index]) {
683                return ord;
684            }
685        }
686        core::cmp::Ordering::Equal
687    }
688
689    /// Get the value of array's word at position `word_idx` when logically shifted left.
690    ///
691    /// This helper computes what value would be at `word_idx` if the array
692    /// were shifted left by `word_shift` words plus `bit_shift` bits.
693    pub(crate) c0nst fn const_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
694        array: &[T; N],
695        word_idx: usize,
696        word_shift: usize,
697        bit_shift: usize,
698    ) -> T {
699        let word_bits = const_word_bits::<T>();
700
701        // Guard against invalid bit_shift that would cause UB
702        if bit_shift >= word_bits {
703            return <T as ConstZero>::zero();
704        }
705
706        if word_idx < word_shift {
707            return <T as ConstZero>::zero();
708        }
709
710        let source_idx = word_idx - word_shift;
711
712        if bit_shift == 0 {
713            if source_idx < N {
714                array[source_idx]
715            } else {
716                <T as ConstZero>::zero()
717            }
718        } else {
719            let mut result = <T as ConstZero>::zero();
720
721            // Get bits from the primary source word
722            if source_idx < N {
723                result |= array[source_idx] << bit_shift;
724            }
725
726            // Get high bits from the next lower word (if it exists)
727            if source_idx > 0 && source_idx - 1 < N {
728                let high_bits = array[source_idx - 1] >> (word_bits - bit_shift);
729                result |= high_bits;
730            }
731
732            result
733        }
734    }
735
736    /// Compare array vs (other << shift_bits) in a const-compatible way.
737    ///
738    /// This is useful for division algorithms where we need to compare
739    /// the dividend against a shifted divisor without allocating.
740    pub(crate) c0nst fn const_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
741        array: &[T; N],
742        other: &[T; N],
743        shift_bits: usize,
744    ) -> core::cmp::Ordering {
745        let word_bits = const_word_bits::<T>();
746
747        if shift_bits == 0 {
748            return const_cmp::<T, N>(array, other);
749        }
750
751        let word_shift = shift_bits / word_bits;
752        if word_shift >= N {
753            // other << shift_bits would overflow to 0
754            if const_is_zero::<T, N>(array) {
755                return core::cmp::Ordering::Equal;
756            } else {
757                return core::cmp::Ordering::Greater;
758            }
759        }
760
761        let bit_shift = shift_bits % word_bits;
762
763        // Compare from most significant words down
764        let mut index = N;
765        while index > 0 {
766            index -= 1;
767            let self_word = array[index];
768            let other_shifted_word = const_get_shifted_word::<T, N>(
769                other, index, word_shift, bit_shift
770            );
771
772            if let Some(ord) = const_cmp_words(self_word, other_shifted_word) {
773                return ord;
774            }
775        }
776
777        core::cmp::Ordering::Equal
778    }
779
780    /// Subtract (other << shift_bits) from array in-place.
781    ///
782    /// This is used in division algorithms to subtract shifted divisor
783    /// from the remainder without allocating.
784    pub(crate) c0nst fn const_sub_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
785        array: &mut [T; N],
786        other: &[T; N],
787        shift_bits: usize,
788    ) {
789        let word_bits = const_word_bits::<T>();
790
791        if shift_bits == 0 {
792            sub_impl::<T, N>(array, other);
793            return;
794        }
795
796        let word_shift = shift_bits / word_bits;
797        if word_shift >= N {
798            return;
799        }
800
801        let bit_shift = shift_bits % word_bits;
802        let mut borrow = T::zero();
803        let mut index = 0;
804        while index < N {
805            let other_word = const_get_shifted_word::<T, N>(other, index, word_shift, bit_shift);
806            let (res, borrow1) = array[index].overflowing_sub(&other_word);
807            let (res, borrow2) = res.overflowing_sub(&borrow);
808            borrow = if borrow1 || borrow2 { T::one() } else { T::zero() };
809            array[index] = res;
810            index += 1;
811        }
812    }
813
814    /// In-place division: dividend becomes quotient, returns remainder.
815    ///
816    /// Low-level const-compatible division on arrays.
817    pub(crate) c0nst fn const_div<T: [c0nst] ConstMachineWord, const N: usize>(
818        dividend: &mut [T; N],
819        divisor: &[T; N],
820    ) -> [T; N] {
821        use core::cmp::Ordering;
822
823        match const_cmp::<T, N>(dividend, divisor) {
824            // dividend < divisor: quotient = 0, remainder = dividend
825            Ordering::Less => {
826                let remainder = *dividend;
827                let mut i = 0;
828                while i < N {
829                    dividend[i] = <T as ConstZero>::zero();
830                    i += 1;
831                }
832                return remainder;
833            }
834            // dividend == divisor: quotient = 1, remainder = 0
835            Ordering::Equal => {
836                let mut i = 0;
837                while i < N {
838                    dividend[i] = <T as ConstZero>::zero();
839                    i += 1;
840                }
841                if N > 0 {
842                    dividend[0] = <T as ConstOne>::one();
843                }
844                return [<T as ConstZero>::zero(); N];
845            }
846            Ordering::Greater => {}
847        }
848
849        let mut quotient = [<T as ConstZero>::zero(); N];
850
851        // Calculate initial bit position
852        let dividend_bits = const_bit_length::<T, N>(dividend);
853        let divisor_bits = const_bit_length::<T, N>(divisor);
854
855        let mut bit_pos = if dividend_bits >= divisor_bits {
856            dividend_bits - divisor_bits
857        } else {
858            0
859        };
860
861        // Adjust bit position to find the first position where divisor can be subtracted
862        while bit_pos > 0 {
863            let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
864            if !matches!(cmp, Ordering::Less) {
865                break;
866            }
867            bit_pos -= 1;
868        }
869
870        // Main division loop
871        loop {
872            let cmp = const_cmp_shifted::<T, N>(dividend, divisor, bit_pos);
873            if !matches!(cmp, Ordering::Less) {
874                const_sub_shifted::<T, N>(dividend, divisor, bit_pos);
875                const_set_bit::<T, N>(&mut quotient, bit_pos);
876            }
877
878            if bit_pos == 0 {
879                break;
880            }
881            bit_pos -= 1;
882        }
883
884        let remainder = *dividend;
885        *dividend = quotient;
886        remainder
887    }
888
889    /// Const-compatible div_rem: returns (quotient, remainder).
890    ///
891    /// Panics on divide by zero.
892    pub(crate) c0nst fn const_div_rem<T: [c0nst] ConstMachineWord, const N: usize>(
893        dividend: &[T; N],
894        divisor: &[T; N],
895    ) -> ([T; N], [T; N]) {
896        if const_is_zero(divisor) {
897            maybe_panic(PanicReason::DivByZero)
898        }
899        let mut quotient = *dividend;
900        let remainder = const_div(&mut quotient, divisor);
901        (quotient, remainder)
902    }
903}
904
905c0nst::c0nst! {
906    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Default for FixedUInt<T, N> {
907        fn default() -> Self {
908            <Self as ConstZero>::zero()
909        }
910    }
911
912    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst Clone for FixedUInt<T, N> {
913        fn clone(&self) -> Self {
914            *self
915        }
916    }
917}
918
919impl<T: MachineWord, const N: usize> num_traits::Unsigned for FixedUInt<T, N> {}
920
921// #region Equality and Ordering
922
923c0nst::c0nst! {
924    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialEq for FixedUInt<T, N> {
925        fn eq(&self, other: &Self) -> bool {
926            self.array == other.array
927        }
928    }
929
930    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Eq for FixedUInt<T, N> {}
931
932    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::Ord for FixedUInt<T, N> {
933        fn cmp(&self, other: &Self) -> core::cmp::Ordering {
934            const_cmp(&self.array, &other.array)
935        }
936    }
937
938    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::cmp::PartialOrd for FixedUInt<T, N> {
939        fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
940            Some(self.cmp(other))
941        }
942    }
943}
944
945// #endregion Equality and Ordering
946
947// #region core::convert::From<primitive>
948
949c0nst::c0nst! {
950    /// Const-compatible conversion from little-endian bytes to array of words.
951    /// Delegates to impl_from_le_bytes_slice to avoid code duplication.
952    c0nst fn const_from_le_bytes<T: [c0nst] ConstMachineWord, const N: usize, const B: usize>(
953        bytes: [u8; B],
954    ) -> [T; N] {
955        impl_from_le_bytes_slice::<T, N>(&bytes)
956    }
957
958    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u8> for FixedUInt<T, N> {
959        fn from(x: u8) -> Self {
960            Self { array: const_from_le_bytes(x.to_le_bytes()) }
961        }
962    }
963
964    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u16> for FixedUInt<T, N> {
965        fn from(x: u16) -> Self {
966            Self { array: const_from_le_bytes(x.to_le_bytes()) }
967        }
968    }
969
970    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u32> for FixedUInt<T, N> {
971        fn from(x: u32) -> Self {
972            Self { array: const_from_le_bytes(x.to_le_bytes()) }
973        }
974    }
975
976    impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> c0nst core::convert::From<u64> for FixedUInt<T, N> {
977        fn from(x: u64) -> Self {
978            Self { array: const_from_le_bytes(x.to_le_bytes()) }
979        }
980    }
981}
982
983// #endregion core::convert::From<primitive>
984
985// #region helpers
986
987// This is slightly less than ideal, but PIE isn't directly constructible
988// due to unstable members.
989fn make_parse_int_err() -> core::num::ParseIntError {
990    <u8>::from_str_radix("-", 2).err().unwrap()
991}
992fn make_overflow_err() -> core::num::ParseIntError {
993    <u8>::from_str_radix("101", 16).err().unwrap()
994}
995fn make_empty_error() -> core::num::ParseIntError {
996    <u8>::from_str_radix("", 8).err().unwrap()
997}
998
999fn to_slice_hex<T: AsRef<[u8]>>(
1000    input: T,
1001    output: &mut [u8],
1002) -> Result<(), core::num::ParseIntError> {
1003    fn from_digit(byte: u8) -> Option<char> {
1004        core::char::from_digit(byte as u32, 16)
1005    }
1006    let r = input.as_ref();
1007    if r.len() * 2 != output.len() {
1008        return Err(make_parse_int_err());
1009    }
1010    for i in 0..r.len() {
1011        let byte = r[i];
1012        output[i * 2] = from_digit((byte & 0xf0) >> 4).ok_or_else(make_parse_int_err)? as u8;
1013        output[i * 2 + 1] = from_digit(byte & 0x0f).ok_or_else(make_parse_int_err)? as u8;
1014    }
1015
1016    Ok(())
1017}
1018
1019pub(super) enum PanicReason {
1020    Add,
1021    Sub,
1022    Mul,
1023    DivByZero,
1024}
1025
1026c0nst::c0nst! {
1027    pub(super) c0nst fn maybe_panic(r: PanicReason) {
1028        match r {
1029            PanicReason::Add => panic!("attempt to add with overflow"),
1030            PanicReason::Sub => panic!("attempt to subtract with overflow"),
1031            PanicReason::Mul => panic!("attempt to multiply with overflow"),
1032            PanicReason::DivByZero => panic!("attempt to divide by zero"),
1033        }
1034    }
1035}
1036
1037// #endregion helpers
1038
1039#[cfg(test)]
1040mod tests {
1041    use super::FixedUInt as Bn;
1042    use super::*;
1043    use num_traits::One;
1044
1045    type Bn8 = Bn<u8, 8>;
1046    type Bn16 = Bn<u16, 4>;
1047    type Bn32 = Bn<u32, 2>;
1048
1049    c0nst::c0nst! {
1050        pub c0nst fn test_add<T: [c0nst] ConstMachineWord, const N: usize>(
1051            a: &mut [T; N],
1052            b: &[T; N]
1053        ) -> bool {
1054            add_impl(a, b)
1055        }
1056
1057        pub c0nst fn test_sub<T: [c0nst] ConstMachineWord, const N: usize>(
1058            a: &mut [T; N],
1059            b: &[T; N]
1060        ) -> bool {
1061            sub_impl(a, b)
1062        }
1063
1064        pub c0nst fn test_mul<T: [c0nst] ConstMachineWord, const N: usize>(
1065            a: &[T; N],
1066            b: &[T; N],
1067            word_bits: usize,
1068        ) -> ([T; N], bool) {
1069            const_mul::<T, N, true>(a, b, word_bits)
1070        }
1071
1072        pub c0nst fn arr_leading_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1073            a: &[T; N],
1074        ) -> u32 {
1075            const_leading_zeros::<T, N>(a)
1076        }
1077
1078        pub c0nst fn arr_trailing_zeros<T: [c0nst] ConstMachineWord, const N: usize>(
1079            a: &[T; N],
1080        ) -> u32 {
1081            const_trailing_zeros::<T, N>(a)
1082        }
1083
1084        pub c0nst fn arr_bit_length<T: [c0nst] ConstMachineWord, const N: usize>(
1085            a: &[T; N],
1086        ) -> usize {
1087            const_bit_length::<T, N>(a)
1088        }
1089
1090        pub c0nst fn arr_is_zero<T: [c0nst] ConstMachineWord, const N: usize>(
1091            a: &[T; N],
1092        ) -> bool {
1093            const_is_zero::<T, N>(a)
1094        }
1095
1096        pub c0nst fn arr_set_bit<T: [c0nst] ConstMachineWord, const N: usize>(
1097            a: &mut [T; N],
1098            pos: usize,
1099        ) {
1100            const_set_bit::<T, N>(a, pos)
1101        }
1102
1103        pub c0nst fn arr_cmp<T: [c0nst] ConstMachineWord, const N: usize>(
1104            a: &[T; N],
1105            b: &[T; N],
1106        ) -> core::cmp::Ordering {
1107            const_cmp::<T, N>(a, b)
1108        }
1109
1110        pub c0nst fn arr_cmp_shifted<T: [c0nst] ConstMachineWord, const N: usize>(
1111            a: &[T; N],
1112            b: &[T; N],
1113            shift_bits: usize,
1114        ) -> core::cmp::Ordering {
1115            const_cmp_shifted::<T, N>(a, b, shift_bits)
1116        }
1117
1118        pub c0nst fn arr_get_shifted_word<T: [c0nst] ConstMachineWord, const N: usize>(
1119            a: &[T; N],
1120            word_idx: usize,
1121            word_shift: usize,
1122            bit_shift: usize,
1123        ) -> T {
1124            const_get_shifted_word::<T, N>(a, word_idx, word_shift, bit_shift)
1125        }
1126    }
1127
1128    #[test]
1129    fn test_const_add_impl() {
1130        // Simple add, no overflow
1131        let mut a: [u8; 4] = [1, 0, 0, 0];
1132        let b: [u8; 4] = [2, 0, 0, 0];
1133        let overflow = test_add(&mut a, &b);
1134        assert_eq!(a, [3, 0, 0, 0]);
1135        assert!(!overflow);
1136
1137        // Add with carry propagation
1138        let mut a: [u8; 4] = [255, 0, 0, 0];
1139        let b: [u8; 4] = [1, 0, 0, 0];
1140        let overflow = test_add(&mut a, &b);
1141        assert_eq!(a, [0, 1, 0, 0]);
1142        assert!(!overflow);
1143
1144        // Add with overflow
1145        let mut a: [u8; 4] = [255, 255, 255, 255];
1146        let b: [u8; 4] = [1, 0, 0, 0];
1147        let overflow = test_add(&mut a, &b);
1148        assert_eq!(a, [0, 0, 0, 0]);
1149        assert!(overflow);
1150
1151        // Test with u32 words
1152        let mut a: [u32; 2] = [0xFFFFFFFF, 0];
1153        let b: [u32; 2] = [1, 0];
1154        let overflow = test_add(&mut a, &b);
1155        assert_eq!(a, [0, 1]);
1156        assert!(!overflow);
1157
1158        #[cfg(feature = "nightly")]
1159        {
1160            const ADD_RESULT: ([u8; 4], bool) = {
1161                let mut a = [1u8, 0, 0, 0];
1162                let b = [2u8, 0, 0, 0];
1163                let overflow = test_add(&mut a, &b);
1164                (a, overflow)
1165            };
1166            assert_eq!(ADD_RESULT, ([3, 0, 0, 0], false));
1167        }
1168    }
1169
1170    #[test]
1171    fn test_const_sub_impl() {
1172        // Simple sub, no overflow
1173        let mut a: [u8; 4] = [3, 0, 0, 0];
1174        let b: [u8; 4] = [1, 0, 0, 0];
1175        let overflow = test_sub(&mut a, &b);
1176        assert_eq!(a, [2, 0, 0, 0]);
1177        assert!(!overflow);
1178
1179        // Sub with borrow propagation
1180        let mut a: [u8; 4] = [0, 1, 0, 0];
1181        let b: [u8; 4] = [1, 0, 0, 0];
1182        let overflow = test_sub(&mut a, &b);
1183        assert_eq!(a, [255, 0, 0, 0]);
1184        assert!(!overflow);
1185
1186        // Sub with underflow
1187        let mut a: [u8; 4] = [0, 0, 0, 0];
1188        let b: [u8; 4] = [1, 0, 0, 0];
1189        let overflow = test_sub(&mut a, &b);
1190        assert_eq!(a, [255, 255, 255, 255]);
1191        assert!(overflow);
1192
1193        // Test with u32 words
1194        let mut a: [u32; 2] = [0, 1];
1195        let b: [u32; 2] = [1, 0];
1196        let overflow = test_sub(&mut a, &b);
1197        assert_eq!(a, [0xFFFFFFFF, 0]);
1198        assert!(!overflow);
1199
1200        #[cfg(feature = "nightly")]
1201        {
1202            const SUB_RESULT: ([u8; 4], bool) = {
1203                let mut a = [3u8, 0, 0, 0];
1204                let b = [1u8, 0, 0, 0];
1205                let overflow = test_sub(&mut a, &b);
1206                (a, overflow)
1207            };
1208            assert_eq!(SUB_RESULT, ([2, 0, 0, 0], false));
1209        }
1210    }
1211
1212    #[test]
1213    fn test_const_mul_impl() {
1214        // Simple mul: 3 * 4 = 12
1215        let a: [u8; 2] = [3, 0];
1216        let b: [u8; 2] = [4, 0];
1217        let (result, overflow) = test_mul(&a, &b, 8);
1218        assert_eq!(result, [12, 0]);
1219        assert!(!overflow);
1220
1221        // Mul with carry: 200 * 2 = 400 = 0x190 = [0x90, 0x01]
1222        let a: [u8; 2] = [200, 0];
1223        let b: [u8; 2] = [2, 0];
1224        let (result, overflow) = test_mul(&a, &b, 8);
1225        assert_eq!(result, [0x90, 0x01]);
1226        assert!(!overflow);
1227
1228        // Mul with overflow: 256 * 256 = 65536 which overflows 16 bits
1229        let a: [u8; 2] = [0, 1]; // 256
1230        let b: [u8; 2] = [0, 1]; // 256
1231        let (_result, overflow) = test_mul(&a, &b, 8);
1232        assert!(overflow);
1233
1234        // N=3 overflow at high position (round=4, i=2, j=2)
1235        // a = [0, 0, 1] = 65536, b = [0, 0, 1] = 65536
1236        // a * b = 65536^2 = 4294967296 which overflows 24 bits
1237        let a: [u8; 3] = [0, 0, 1];
1238        let b: [u8; 3] = [0, 0, 1];
1239        let (_result, overflow) = test_mul(&a, &b, 8);
1240        assert!(overflow, "N=3 high-position overflow not detected");
1241
1242        // N=3 overflow with larger high word values
1243        // a = [0, 0, 2] = 131072, b = [0, 0, 2] = 131072
1244        // a * b = 131072^2 = 17179869184 which overflows 24 bits
1245        let a: [u8; 3] = [0, 0, 2];
1246        let b: [u8; 3] = [0, 0, 2];
1247        let (_result, overflow) = test_mul(&a, &b, 8);
1248        assert!(
1249            overflow,
1250            "N=3 high-position overflow with larger values not detected"
1251        );
1252
1253        // N=3 non-overflow case: values that fit in 24 bits
1254        // a = [0, 1, 0] = 256, b = [0, 1, 0] = 256
1255        // a * b = 256 * 256 = 65536 = [0, 0, 1] which fits in 24 bits
1256        let a: [u8; 3] = [0, 1, 0];
1257        let b: [u8; 3] = [0, 1, 0];
1258        let (result, overflow) = test_mul(&a, &b, 8);
1259        assert_eq!(result, [0, 0, 1]);
1260        assert!(
1261            !overflow,
1262            "N=3 non-overflow incorrectly detected as overflow"
1263        );
1264
1265        // N=3 non-overflow with carry propagation
1266        // a = [255, 0, 0] = 255, b = [255, 0, 0] = 255
1267        // a * b = 255 * 255 = 65025 = 0xFE01 = [0x01, 0xFE, 0x00]
1268        let a: [u8; 3] = [255, 0, 0];
1269        let b: [u8; 3] = [255, 0, 0];
1270        let (result, overflow) = test_mul(&a, &b, 8);
1271        assert_eq!(result, [0x01, 0xFE, 0x00]);
1272        assert!(!overflow);
1273
1274        #[cfg(feature = "nightly")]
1275        {
1276            const MUL_RESULT: ([u8; 2], bool) = test_mul(&[3u8, 0], &[4u8, 0], 8);
1277            assert_eq!(MUL_RESULT, ([12, 0], false));
1278        }
1279    }
1280
1281    #[test]
1282    fn test_const_helpers() {
1283        // Test leading_zeros
1284        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1285        assert_eq!(arr_leading_zeros(&[1u8, 0, 0, 0]), 31); // single bit
1286        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 1]), 7); // high byte has 1
1287        assert_eq!(arr_leading_zeros(&[0u8, 0, 0, 0x80]), 0); // MSB set
1288        assert_eq!(arr_leading_zeros(&[255u8, 255, 255, 255]), 0); // all ones
1289
1290        // Test trailing_zeros
1291        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 0]), 32); // all zeros
1292        assert_eq!(arr_trailing_zeros(&[1u8, 0, 0, 0]), 0); // LSB set
1293        assert_eq!(arr_trailing_zeros(&[0u8, 1, 0, 0]), 8); // second byte
1294        assert_eq!(arr_trailing_zeros(&[0u8, 0, 0, 1]), 24); // fourth byte
1295        assert_eq!(arr_trailing_zeros(&[0x80u8, 0, 0, 0]), 7); // bit 7 of first byte
1296
1297        // Test bit_length
1298        assert_eq!(arr_bit_length(&[0u8, 0, 0, 0]), 0); // zero
1299        assert_eq!(arr_bit_length(&[1u8, 0, 0, 0]), 1); // 1
1300        assert_eq!(arr_bit_length(&[2u8, 0, 0, 0]), 2); // 2
1301        assert_eq!(arr_bit_length(&[3u8, 0, 0, 0]), 2); // 3
1302        assert_eq!(arr_bit_length(&[0u8, 1, 0, 0]), 9); // 256
1303        assert_eq!(arr_bit_length(&[0xF0u8, 0, 0, 0]), 8); // 240 (0xF0)
1304        assert_eq!(arr_bit_length(&[255u8, 255, 255, 255]), 32); // max
1305
1306        // Test is_zero
1307        assert!(arr_is_zero(&[0u8, 0, 0, 0]));
1308        assert!(!arr_is_zero(&[1u8, 0, 0, 0]));
1309        assert!(!arr_is_zero(&[0u8, 0, 0, 1]));
1310        assert!(!arr_is_zero(&[0u8, 1, 0, 0]));
1311
1312        // Test set_bit
1313        let mut arr: [u8; 4] = [0, 0, 0, 0];
1314        arr_set_bit(&mut arr, 0);
1315        assert_eq!(arr, [1, 0, 0, 0]);
1316
1317        let mut arr: [u8; 4] = [0, 0, 0, 0];
1318        arr_set_bit(&mut arr, 8);
1319        assert_eq!(arr, [0, 1, 0, 0]);
1320
1321        let mut arr: [u8; 4] = [0, 0, 0, 0];
1322        arr_set_bit(&mut arr, 31);
1323        assert_eq!(arr, [0, 0, 0, 0x80]);
1324
1325        // Set multiple bits
1326        let mut arr: [u8; 4] = [0, 0, 0, 0];
1327        arr_set_bit(&mut arr, 0);
1328        arr_set_bit(&mut arr, 3);
1329        arr_set_bit(&mut arr, 8);
1330        assert_eq!(arr, [0b00001001, 1, 0, 0]);
1331
1332        // Out of bounds should be no-op
1333        let mut arr: [u8; 4] = [0, 0, 0, 0];
1334        arr_set_bit(&mut arr, 32);
1335        assert_eq!(arr, [0, 0, 0, 0]);
1336
1337        // Test with u32 words
1338        assert_eq!(arr_leading_zeros(&[0u32, 0]), 64);
1339        assert_eq!(arr_leading_zeros(&[1u32, 0]), 63);
1340        assert_eq!(arr_leading_zeros(&[0u32, 1]), 31);
1341        assert_eq!(arr_trailing_zeros(&[0u32, 0]), 64);
1342        assert_eq!(arr_trailing_zeros(&[0u32, 1]), 32);
1343        assert_eq!(arr_bit_length(&[0u32, 0]), 0);
1344        assert_eq!(arr_bit_length(&[1u32, 0]), 1);
1345        assert_eq!(arr_bit_length(&[0u32, 1]), 33);
1346
1347        #[cfg(feature = "nightly")]
1348        {
1349            const LEADING: u32 = arr_leading_zeros(&[0u8, 0, 1, 0]);
1350            assert_eq!(LEADING, 15);
1351
1352            const TRAILING: u32 = arr_trailing_zeros(&[0u8, 0, 1, 0]);
1353            assert_eq!(TRAILING, 16);
1354
1355            const BIT_LEN: usize = arr_bit_length(&[0u8, 0, 1, 0]);
1356            assert_eq!(BIT_LEN, 17);
1357
1358            const IS_ZERO: bool = arr_is_zero(&[0u8, 0, 0, 0]);
1359            assert!(IS_ZERO);
1360
1361            const NOT_ZERO: bool = arr_is_zero(&[0u8, 1, 0, 0]);
1362            assert!(!NOT_ZERO);
1363
1364            const SET_BIT_RESULT: [u8; 4] = {
1365                let mut arr = [0u8, 0, 0, 0];
1366                arr_set_bit(&mut arr, 10);
1367                arr
1368            };
1369            assert_eq!(SET_BIT_RESULT, [0, 0b00000100, 0, 0]);
1370        }
1371    }
1372
1373    #[test]
1374    fn test_const_cmp() {
1375        use core::cmp::Ordering;
1376
1377        // Equal arrays
1378        assert_eq!(arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]), Ordering::Equal);
1379        assert_eq!(arr_cmp(&[0u8, 0, 0, 0], &[0u8, 0, 0, 0]), Ordering::Equal);
1380
1381        // Greater - high word differs
1382        assert_eq!(arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]), Ordering::Greater);
1383
1384        // Less - high word differs
1385        assert_eq!(arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]), Ordering::Less);
1386
1387        // Greater - low word differs (high words equal)
1388        assert_eq!(arr_cmp(&[2u8, 0, 0, 0], &[1u8, 0, 0, 0]), Ordering::Greater);
1389
1390        // Less - low word differs
1391        assert_eq!(arr_cmp(&[1u8, 0, 0, 0], &[2u8, 0, 0, 0]), Ordering::Less);
1392
1393        // Test with u32 words
1394        assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 1]), Ordering::Equal);
1395        assert_eq!(arr_cmp(&[0u32, 2], &[0u32, 1]), Ordering::Greater);
1396        assert_eq!(arr_cmp(&[0u32, 1], &[0u32, 2]), Ordering::Less);
1397
1398        #[cfg(feature = "nightly")]
1399        {
1400            const CMP_EQ: Ordering = arr_cmp(&[1u8, 2, 3, 4], &[1u8, 2, 3, 4]);
1401            const CMP_GT: Ordering = arr_cmp(&[0u8, 0, 0, 2], &[0u8, 0, 0, 1]);
1402            const CMP_LT: Ordering = arr_cmp(&[0u8, 0, 0, 1], &[0u8, 0, 0, 2]);
1403            assert_eq!(CMP_EQ, Ordering::Equal);
1404            assert_eq!(CMP_GT, Ordering::Greater);
1405            assert_eq!(CMP_LT, Ordering::Less);
1406        }
1407    }
1408
1409    #[test]
1410    fn test_const_cmp_shifted() {
1411        use core::cmp::Ordering;
1412
1413        // No shift - same as regular cmp
1414        assert_eq!(
1415            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 0),
1416            Ordering::Equal
1417        );
1418
1419        // Compare [0, 1, 0, 0] (256) vs [1, 0, 0, 0] << 8 (256) = Equal
1420        assert_eq!(
1421            arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8),
1422            Ordering::Equal
1423        );
1424
1425        // Compare [0, 2, 0, 0] (512) vs [1, 0, 0, 0] << 8 (256) = Greater
1426        assert_eq!(
1427            arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8),
1428            Ordering::Greater
1429        );
1430
1431        // Compare [0, 0, 0, 0] (0) vs [1, 0, 0, 0] << 8 (256) = Less
1432        assert_eq!(
1433            arr_cmp_shifted(&[0u8, 0, 0, 0], &[1u8, 0, 0, 0], 8),
1434            Ordering::Less
1435        );
1436
1437        // Shift overflow: shift >= bit_size, other becomes 0
1438        // Compare [1, 0, 0, 0] vs [1, 0, 0, 0] << 32 (0) = Greater
1439        assert_eq!(
1440            arr_cmp_shifted(&[1u8, 0, 0, 0], &[1u8, 0, 0, 0], 32),
1441            Ordering::Greater
1442        );
1443
1444        // Compare [0, 0, 0, 0] vs anything << 32 (0) = Equal
1445        assert_eq!(
1446            arr_cmp_shifted(&[0u8, 0, 0, 0], &[255u8, 255, 255, 255], 32),
1447            Ordering::Equal
1448        );
1449
1450        // Test get_shifted_word helper with bit_shift == 0
1451        // [1, 2, 3, 4] shifted left by 1 word (8 bits for u8)
1452        // word 0 should be 0, word 1 should be 1, word 2 should be 2, etc.
1453        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 0, 1, 0), 0);
1454        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 1, 1, 0), 1);
1455        assert_eq!(arr_get_shifted_word(&[1u8, 2, 3, 4], 2, 1, 0), 2);
1456
1457        // Test get_shifted_word with bit_shift != 0 (cross-word bit combination)
1458        // [0x0F, 0xF0, 0, 0] with word_shift=0, bit_shift=4
1459        // word 0: 0x0F << 4 = 0xF0 (no lower word to borrow from)
1460        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 0, 0, 4), 0xF0);
1461        // word 1: (0xF0 << 4) | (0x0F >> 4) = 0x00 | 0x00 = 0x00
1462        assert_eq!(arr_get_shifted_word(&[0x0Fu8, 0xF0, 0, 0], 1, 0, 4), 0x00);
1463
1464        // [0xFF, 0x00, 0, 0] with bit_shift=4
1465        // word 0: 0xFF << 4 = 0xF0
1466        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 0, 0, 4), 0xF0);
1467        // word 1: (0x00 << 4) | (0xFF >> 4) = 0x00 | 0x0F = 0x0F
1468        assert_eq!(arr_get_shifted_word(&[0xFFu8, 0x00, 0, 0], 1, 0, 4), 0x0F);
1469
1470        // Combined word_shift and bit_shift
1471        // [0xAB, 0xCD, 0, 0] with word_shift=1, bit_shift=4
1472        // word 0: below word_shift, returns 0
1473        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 0, 1, 4), 0);
1474        // word 1: source_idx=0, 0xAB << 4 = 0xB0 (no lower word)
1475        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 1, 1, 4), 0xB0);
1476        // word 2: source_idx=1, (0xCD << 4) | (0xAB >> 4) = 0xD0 | 0x0A = 0xDA
1477        assert_eq!(arr_get_shifted_word(&[0xABu8, 0xCD, 0, 0], 2, 1, 4), 0xDA);
1478
1479        #[cfg(feature = "nightly")]
1480        {
1481            const CMP_SHIFTED_EQ: Ordering = arr_cmp_shifted(&[0u8, 1, 0, 0], &[1u8, 0, 0, 0], 8);
1482            const CMP_SHIFTED_GT: Ordering = arr_cmp_shifted(&[0u8, 2, 0, 0], &[1u8, 0, 0, 0], 8);
1483            assert_eq!(CMP_SHIFTED_EQ, Ordering::Equal);
1484            assert_eq!(CMP_SHIFTED_GT, Ordering::Greater);
1485        }
1486    }
1487
1488    #[test]
1489    fn test_core_convert_u8() {
1490        let f = Bn::<u8, 1>::from(1u8);
1491        assert_eq!(f.array, [1]);
1492        let f = Bn::<u8, 2>::from(1u8);
1493        assert_eq!(f.array, [1, 0]);
1494
1495        let f = Bn::<u16, 1>::from(1u8);
1496        assert_eq!(f.array, [1]);
1497        let f = Bn::<u16, 2>::from(1u8);
1498        assert_eq!(f.array, [1, 0]);
1499
1500        #[cfg(feature = "nightly")]
1501        {
1502            const F1: Bn<u8, 2> = Bn::<u8, 2>::from(42u8);
1503            assert_eq!(F1.array, [42, 0]);
1504        }
1505    }
1506
1507    #[test]
1508    fn test_core_convert_u16() {
1509        let f = Bn::<u8, 1>::from(1u16);
1510        assert_eq!(f.array, [1]);
1511        let f = Bn::<u8, 2>::from(1u16);
1512        assert_eq!(f.array, [1, 0]);
1513
1514        let f = Bn::<u8, 1>::from(256u16);
1515        assert_eq!(f.array, [0]);
1516        let f = Bn::<u8, 2>::from(257u16);
1517        assert_eq!(f.array, [1, 1]);
1518        let f = Bn::<u8, 2>::from(65535u16);
1519        assert_eq!(f.array, [255, 255]);
1520
1521        let f = Bn::<u16, 1>::from(1u16);
1522        assert_eq!(f.array, [1]);
1523        let f = Bn::<u16, 2>::from(1u16);
1524        assert_eq!(f.array, [1, 0]);
1525
1526        let f = Bn::<u16, 1>::from(65535u16);
1527        assert_eq!(f.array, [65535]);
1528
1529        #[cfg(feature = "nightly")]
1530        {
1531            const F1: Bn<u8, 2> = Bn::<u8, 2>::from(0x0102u16);
1532            assert_eq!(F1.array, [0x02, 0x01]);
1533        }
1534    }
1535
1536    #[test]
1537    fn test_core_convert_u32() {
1538        let f = Bn::<u8, 1>::from(1u32);
1539        assert_eq!(f.array, [1]);
1540        let f = Bn::<u8, 1>::from(256u32);
1541        assert_eq!(f.array, [0]);
1542
1543        let f = Bn::<u8, 2>::from(1u32);
1544        assert_eq!(f.array, [1, 0]);
1545        let f = Bn::<u8, 2>::from(257u32);
1546        assert_eq!(f.array, [1, 1]);
1547        let f = Bn::<u8, 2>::from(65535u32);
1548        assert_eq!(f.array, [255, 255]);
1549
1550        let f = Bn::<u8, 4>::from(1u32);
1551        assert_eq!(f.array, [1, 0, 0, 0]);
1552        let f = Bn::<u8, 4>::from(257u32);
1553        assert_eq!(f.array, [1, 1, 0, 0]);
1554        let f = Bn::<u8, 4>::from(u32::max_value());
1555        assert_eq!(f.array, [255, 255, 255, 255]);
1556
1557        let f = Bn::<u8, 1>::from(1u32);
1558        assert_eq!(f.array, [1]);
1559        let f = Bn::<u8, 1>::from(256u32);
1560        assert_eq!(f.array, [0]);
1561
1562        let f = Bn::<u16, 2>::from(65537u32);
1563        assert_eq!(f.array, [1, 1]);
1564
1565        let f = Bn::<u32, 1>::from(1u32);
1566        assert_eq!(f.array, [1]);
1567        let f = Bn::<u32, 2>::from(1u32);
1568        assert_eq!(f.array, [1, 0]);
1569
1570        let f = Bn::<u32, 1>::from(65537u32);
1571        assert_eq!(f.array, [65537]);
1572
1573        let f = Bn::<u32, 1>::from(u32::max_value());
1574        assert_eq!(f.array, [4294967295]);
1575
1576        #[cfg(feature = "nightly")]
1577        {
1578            const F1: Bn<u8, 4> = Bn::<u8, 4>::from(0x01020304u32);
1579            assert_eq!(F1.array, [0x04, 0x03, 0x02, 0x01]);
1580        }
1581    }
1582
1583    #[test]
1584    fn test_core_convert_u64() {
1585        let f = Bn::<u8, 8>::from(0x0102030405060708u64);
1586        assert_eq!(f.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1587
1588        let f = Bn::<u16, 4>::from(0x0102030405060708u64);
1589        assert_eq!(f.array, [0x0708, 0x0506, 0x0304, 0x0102]);
1590
1591        let f = Bn::<u32, 2>::from(0x0102030405060708u64);
1592        assert_eq!(f.array, [0x05060708, 0x01020304]);
1593
1594        let f = Bn::<u64, 1>::from(0x0102030405060708u64);
1595        assert_eq!(f.array, [0x0102030405060708]);
1596
1597        #[cfg(feature = "nightly")]
1598        {
1599            const F1: Bn<u8, 8> = Bn::<u8, 8>::from(0x0102030405060708u64);
1600            assert_eq!(F1.array, [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
1601        }
1602    }
1603
1604    #[test]
1605    fn testsimple() {
1606        assert_eq!(Bn::<u8, 8>::new(), Bn::<u8, 8>::new());
1607
1608        assert_eq!(Bn::<u8, 8>::from_u8(3).unwrap().to_u32(), Some(3));
1609        assert_eq!(Bn::<u16, 4>::from_u8(3).unwrap().to_u32(), Some(3));
1610        assert_eq!(Bn::<u32, 2>::from_u8(3).unwrap().to_u32(), Some(3));
1611        assert_eq!(Bn::<u32, 2>::from_u64(3).unwrap().to_u32(), Some(3));
1612        assert_eq!(Bn::<u8, 8>::from_u64(255).unwrap().to_u32(), Some(255));
1613        assert_eq!(Bn::<u8, 8>::from_u64(256).unwrap().to_u32(), Some(256));
1614        assert_eq!(Bn::<u8, 8>::from_u64(65536).unwrap().to_u32(), Some(65536));
1615    }
1616    #[test]
1617    fn testfrom() {
1618        let mut n1 = Bn::<u8, 8>::new();
1619        n1.array[0] = 1;
1620        assert_eq!(Some(1), n1.to_u32());
1621        n1.array[1] = 1;
1622        assert_eq!(Some(257), n1.to_u32());
1623
1624        let mut n2 = Bn::<u16, 8>::new();
1625        n2.array[0] = 0xffff;
1626        assert_eq!(Some(65535), n2.to_u32());
1627        n2.array[0] = 0x0;
1628        n2.array[2] = 0x1;
1629        // Overflow
1630        assert_eq!(None, n2.to_u32());
1631        assert_eq!(Some(0x100000000), n2.to_u64());
1632    }
1633
1634    #[test]
1635    fn test_from_str_bitlengths() {
1636        let test_s64 = "81906f5e4d3c2c01";
1637        let test_u64: u64 = 0x81906f5e4d3c2c01;
1638        let bb = Bn8::from_str_radix(test_s64, 16).unwrap();
1639        let cc = Bn8::from_u64(test_u64).unwrap();
1640        assert_eq!(cc.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1641        assert_eq!(bb.array, [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81]);
1642        let dd = Bn16::from_u64(test_u64).unwrap();
1643        let ff = Bn16::from_str_radix(test_s64, 16).unwrap();
1644        assert_eq!(dd.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1645        assert_eq!(ff.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190]);
1646        let ee = Bn32::from_u64(test_u64).unwrap();
1647        let gg = Bn32::from_str_radix(test_s64, 16).unwrap();
1648        assert_eq!(ee.array, [0x4d3c2c01, 0x81906f5e]);
1649        assert_eq!(gg.array, [0x4d3c2c01, 0x81906f5e]);
1650    }
1651
1652    #[test]
1653    fn test_from_str_stringlengths() {
1654        let ab = Bn::<u8, 9>::from_str_radix("2281906f5e4d3c2c01", 16).unwrap();
1655        assert_eq!(
1656            ab.array,
1657            [0x01, 0x2c, 0x3c, 0x4d, 0x5e, 0x6f, 0x90, 0x81, 0x22]
1658        );
1659        assert_eq!(
1660            [0x2c01, 0x4d3c, 0x6f5e, 0],
1661            Bn::<u16, 4>::from_str_radix("6f5e4d3c2c01", 16)
1662                .unwrap()
1663                .array
1664        );
1665        assert_eq!(
1666            [0x2c01, 0x4d3c, 0x6f5e, 0x190],
1667            Bn::<u16, 4>::from_str_radix("1906f5e4d3c2c01", 16)
1668                .unwrap()
1669                .array
1670        );
1671        assert_eq!(
1672            Err(make_overflow_err()),
1673            Bn::<u16, 4>::from_str_radix("f81906f5e4d3c2c01", 16)
1674        );
1675        assert_eq!(
1676            Err(make_overflow_err()),
1677            Bn::<u16, 4>::from_str_radix("af81906f5e4d3c2c01", 16)
1678        );
1679        assert_eq!(
1680            Err(make_overflow_err()),
1681            Bn::<u16, 4>::from_str_radix("baaf81906f5e4d3c2c01", 16)
1682        );
1683        let ac = Bn::<u16, 5>::from_str_radix("baaf81906f5e4d3c2c01", 16).unwrap();
1684        assert_eq!(ac.array, [0x2c01, 0x4d3c, 0x6f5e, 0x8190, 0xbaaf]);
1685    }
1686
1687    #[test]
1688    fn test_resize() {
1689        type TestInt1 = FixedUInt<u32, 1>;
1690        type TestInt2 = FixedUInt<u32, 2>;
1691
1692        let a = TestInt1::from(u32::MAX);
1693        let b: TestInt2 = a.resize();
1694        assert_eq!(b, TestInt2::from([u32::MAX, 0]));
1695
1696        let a = TestInt2::from([u32::MAX, u32::MAX]);
1697        let b: TestInt1 = a.resize();
1698        assert_eq!(b, TestInt1::from(u32::MAX));
1699    }
1700
1701    #[test]
1702    fn test_bit_length() {
1703        assert_eq!(0, Bn8::from_u8(0).unwrap().bit_length());
1704        assert_eq!(1, Bn8::from_u8(1).unwrap().bit_length());
1705        assert_eq!(2, Bn8::from_u8(2).unwrap().bit_length());
1706        assert_eq!(2, Bn8::from_u8(3).unwrap().bit_length());
1707        assert_eq!(7, Bn8::from_u8(0x70).unwrap().bit_length());
1708        assert_eq!(8, Bn8::from_u8(0xF0).unwrap().bit_length());
1709        assert_eq!(9, Bn8::from_u16(0x1F0).unwrap().bit_length());
1710
1711        assert_eq!(20, Bn8::from_u64(990223).unwrap().bit_length());
1712        assert_eq!(32, Bn8::from_u64(0xefffffff).unwrap().bit_length());
1713        assert_eq!(32, Bn8::from_u64(0x8fffffff).unwrap().bit_length());
1714        assert_eq!(31, Bn8::from_u64(0x7fffffff).unwrap().bit_length());
1715        assert_eq!(34, Bn8::from_u64(0x3ffffffff).unwrap().bit_length());
1716
1717        assert_eq!(0, Bn32::from_u8(0).unwrap().bit_length());
1718        assert_eq!(1, Bn32::from_u8(1).unwrap().bit_length());
1719        assert_eq!(2, Bn32::from_u8(2).unwrap().bit_length());
1720        assert_eq!(2, Bn32::from_u8(3).unwrap().bit_length());
1721        assert_eq!(7, Bn32::from_u8(0x70).unwrap().bit_length());
1722        assert_eq!(8, Bn32::from_u8(0xF0).unwrap().bit_length());
1723        assert_eq!(9, Bn32::from_u16(0x1F0).unwrap().bit_length());
1724
1725        assert_eq!(20, Bn32::from_u64(990223).unwrap().bit_length());
1726        assert_eq!(32, Bn32::from_u64(0xefffffff).unwrap().bit_length());
1727        assert_eq!(32, Bn32::from_u64(0x8fffffff).unwrap().bit_length());
1728        assert_eq!(31, Bn32::from_u64(0x7fffffff).unwrap().bit_length());
1729        assert_eq!(34, Bn32::from_u64(0x3ffffffff).unwrap().bit_length());
1730    }
1731
1732    #[test]
1733    fn test_bit_length_1000() {
1734        // Test bit_length with value 1000
1735        let value = Bn32::from_u16(1000).unwrap();
1736
1737        // 1000 in binary is 1111101000, which has 10 bits
1738        // Let's verify the implementation is working correctly
1739        assert_eq!(value.to_u32().unwrap(), 1000);
1740        assert_eq!(value.bit_length(), 10);
1741
1742        // Test some edge cases around 1000
1743        assert_eq!(Bn32::from_u16(512).unwrap().bit_length(), 10); // 2^9 = 512
1744        assert_eq!(Bn32::from_u16(1023).unwrap().bit_length(), 10); // 2^10 - 1 = 1023
1745        assert_eq!(Bn32::from_u16(1024).unwrap().bit_length(), 11); // 2^10 = 1024
1746
1747        // Test with different word sizes to see if this makes a difference
1748        assert_eq!(Bn8::from_u16(1000).unwrap().bit_length(), 10);
1749        assert_eq!(Bn16::from_u16(1000).unwrap().bit_length(), 10);
1750
1751        // Test with different initialization methods
1752        let value_from_str = Bn32::from_str_radix("1000", 10).unwrap();
1753        assert_eq!(value_from_str.bit_length(), 10);
1754
1755        // This is the problematic case - let's debug it
1756        let value_from_bytes = Bn32::from_le_bytes(&1000u16.to_le_bytes());
1757        // Let's see what the actual value is
1758        assert_eq!(
1759            value_from_bytes.to_u32().unwrap_or(0),
1760            1000,
1761            "from_le_bytes didn't create the correct value"
1762        );
1763        assert_eq!(value_from_bytes.bit_length(), 10);
1764    }
1765    #[test]
1766    fn test_cmp() {
1767        let f0 = <Bn8 as Zero>::zero();
1768        let f1 = <Bn8 as Zero>::zero();
1769        let f2 = <Bn8 as One>::one();
1770        assert_eq!(f0, f1);
1771        assert!(f2 > f0);
1772        assert!(f0 < f2);
1773        let f3 = Bn32::from_u64(990223).unwrap();
1774        assert_eq!(f3, Bn32::from_u64(990223).unwrap());
1775        let f4 = Bn32::from_u64(990224).unwrap();
1776        assert!(f4 > Bn32::from_u64(990223).unwrap());
1777
1778        let f3 = Bn8::from_u64(990223).unwrap();
1779        assert_eq!(f3, Bn8::from_u64(990223).unwrap());
1780        let f4 = Bn8::from_u64(990224).unwrap();
1781        assert!(f4 > Bn8::from_u64(990223).unwrap());
1782
1783        #[cfg(feature = "nightly")]
1784        {
1785            use core::cmp::Ordering;
1786
1787            const A: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1788            const B: FixedUInt<u8, 2> = FixedUInt { array: [20, 0] };
1789            const C: FixedUInt<u8, 2> = FixedUInt { array: [10, 0] };
1790
1791            const CMP_LT: Ordering = A.cmp(&B);
1792            const CMP_GT: Ordering = B.cmp(&A);
1793            const CMP_EQ: Ordering = A.cmp(&C);
1794            const EQ_TRUE: bool = A.eq(&C);
1795            const EQ_FALSE: bool = A.eq(&B);
1796
1797            assert_eq!(CMP_LT, Ordering::Less);
1798            assert_eq!(CMP_GT, Ordering::Greater);
1799            assert_eq!(CMP_EQ, Ordering::Equal);
1800            assert!(EQ_TRUE);
1801            assert!(!EQ_FALSE);
1802        }
1803    }
1804
1805    #[test]
1806    fn test_default() {
1807        let d: Bn8 = Default::default();
1808        assert!(Zero::is_zero(&d));
1809
1810        #[cfg(feature = "nightly")]
1811        {
1812            const D: FixedUInt<u8, 2> = <FixedUInt<u8, 2> as Default>::default();
1813            assert!(Zero::is_zero(&D));
1814        }
1815    }
1816
1817    #[test]
1818    fn test_clone() {
1819        let a: Bn8 = 42u8.into();
1820        let b = a.clone();
1821        assert_eq!(a, b);
1822
1823        #[cfg(feature = "nightly")]
1824        {
1825            const A: FixedUInt<u8, 2> = FixedUInt { array: [42, 0] };
1826            const B: FixedUInt<u8, 2> = A.clone();
1827            assert_eq!(A.array, B.array);
1828        }
1829    }
1830
1831    #[test]
1832    fn test_le_be_bytes() {
1833        let le_bytes = [1, 2, 3, 4];
1834        let be_bytes = [4, 3, 2, 1];
1835        let u8_ver = FixedUInt::<u8, 4>::from_le_bytes(&le_bytes);
1836        let u16_ver = FixedUInt::<u16, 2>::from_le_bytes(&le_bytes);
1837        let u32_ver = FixedUInt::<u32, 1>::from_le_bytes(&le_bytes);
1838        let u8_ver_be = FixedUInt::<u8, 4>::from_be_bytes(&be_bytes);
1839        let u16_ver_be = FixedUInt::<u16, 2>::from_be_bytes(&be_bytes);
1840        let u32_ver_be = FixedUInt::<u32, 1>::from_be_bytes(&be_bytes);
1841
1842        assert_eq!(u8_ver.array, [1, 2, 3, 4]);
1843        assert_eq!(u16_ver.array, [0x0201, 0x0403]);
1844        assert_eq!(u32_ver.array, [0x04030201]);
1845        assert_eq!(u8_ver_be.array, [1, 2, 3, 4]);
1846        assert_eq!(u16_ver_be.array, [0x0201, 0x0403]);
1847        assert_eq!(u32_ver_be.array, [0x04030201]);
1848
1849        let mut output_buffer = [0u8; 16];
1850        assert_eq!(u8_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1851        assert_eq!(u8_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1852        assert_eq!(u16_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1853        assert_eq!(u16_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1854        assert_eq!(u32_ver.to_le_bytes(&mut output_buffer).unwrap(), &le_bytes);
1855        assert_eq!(u32_ver.to_be_bytes(&mut output_buffer).unwrap(), &be_bytes);
1856    }
1857
1858    // Test suite for division implementation
1859    #[test]
1860    fn test_div_small() {
1861        type TestInt = FixedUInt<u8, 2>;
1862
1863        // Test small values
1864        let test_cases = [
1865            (20u16, 3u16, 6u16),        // 20 / 3 = 6
1866            (100u16, 7u16, 14u16),      // 100 / 7 = 14
1867            (255u16, 5u16, 51u16),      // 255 / 5 = 51
1868            (65535u16, 256u16, 255u16), // max u16 / 256 = 255
1869        ];
1870
1871        for (dividend_val, divisor_val, expected) in test_cases {
1872            let dividend = TestInt::from(dividend_val);
1873            let divisor = TestInt::from(divisor_val);
1874            let expected_result = TestInt::from(expected);
1875
1876            assert_eq!(
1877                dividend / divisor,
1878                expected_result,
1879                "Division failed for {} / {} = {}",
1880                dividend_val,
1881                divisor_val,
1882                expected
1883            );
1884        }
1885    }
1886
1887    #[test]
1888    fn test_div_edge_cases() {
1889        type TestInt = FixedUInt<u16, 2>;
1890
1891        // Division by 1
1892        let dividend = TestInt::from(1000u16);
1893        let divisor = TestInt::from(1u16);
1894        assert_eq!(dividend / divisor, TestInt::from(1000u16));
1895
1896        // Equal values
1897        let dividend = TestInt::from(42u16);
1898        let divisor = TestInt::from(42u16);
1899        assert_eq!(dividend / divisor, TestInt::from(1u16));
1900
1901        // Dividend < divisor
1902        let dividend = TestInt::from(5u16);
1903        let divisor = TestInt::from(10u16);
1904        assert_eq!(dividend / divisor, TestInt::from(0u16));
1905
1906        // Powers of 2
1907        let dividend = TestInt::from(1024u16);
1908        let divisor = TestInt::from(4u16);
1909        assert_eq!(dividend / divisor, TestInt::from(256u16));
1910    }
1911
1912    #[test]
1913    fn test_helper_methods() {
1914        type TestInt = FixedUInt<u8, 2>;
1915
1916        // Test const_set_bit
1917        let mut val = <TestInt as Zero>::zero();
1918        const_set_bit(&mut val.array, 0);
1919        assert_eq!(val, TestInt::from(1u8));
1920
1921        const_set_bit(&mut val.array, 8);
1922        assert_eq!(val, TestInt::from(257u16)); // bit 0 + bit 8 = 1 + 256 = 257
1923
1924        // Test const_cmp_shifted
1925        let a = TestInt::from(8u8); // 1000 in binary
1926        let b = TestInt::from(1u8); // 0001 in binary
1927
1928        // b << 3 = 8, so a == (b << 3)
1929        assert_eq!(
1930            const_cmp_shifted(&a.array, &b.array, 3),
1931            core::cmp::Ordering::Equal
1932        );
1933
1934        // a > (b << 2) because b << 2 = 4
1935        assert_eq!(
1936            const_cmp_shifted(&a.array, &b.array, 2),
1937            core::cmp::Ordering::Greater
1938        );
1939
1940        // a < (b << 4) because b << 4 = 16
1941        assert_eq!(
1942            const_cmp_shifted(&a.array, &b.array, 4),
1943            core::cmp::Ordering::Less
1944        );
1945
1946        // Test const_sub_shifted
1947        let mut val = TestInt::from(10u8);
1948        let one = TestInt::from(1u8);
1949        const_sub_shifted(&mut val.array, &one.array, 2); // subtract 1 << 2 = 4
1950        assert_eq!(val, TestInt::from(6u8)); // 10 - 4 = 6
1951    }
1952
1953    #[test]
1954    fn test_shifted_operations_comprehensive() {
1955        type TestInt = FixedUInt<u32, 2>;
1956
1957        // Test cmp_shifted with various word boundary cases
1958        let a = TestInt::from(0x12345678u32);
1959        let b = TestInt::from(0x12345678u32);
1960
1961        // Equal comparison
1962        assert_eq!(
1963            const_cmp_shifted(&a.array, &b.array, 0),
1964            core::cmp::Ordering::Equal
1965        );
1966
1967        // Test shifts that cross word boundaries (assuming 32-bit words)
1968        let c = TestInt::from(0x123u32); // Small number
1969        let d = TestInt::from(0x48d159e2u32); // c << 16 + some bits
1970
1971        // c << 16 should be less than d
1972        assert_eq!(
1973            const_cmp_shifted(&d.array, &c.array, 16),
1974            core::cmp::Ordering::Greater
1975        );
1976
1977        // Test large shifts (beyond bit size, so shifted value becomes 0)
1978        let e = TestInt::from(1u32);
1979        let zero = TestInt::from(0u32);
1980        assert_eq!(
1981            const_cmp_shifted(&e.array, &zero.array, 100),
1982            core::cmp::Ordering::Greater
1983        );
1984        // When shift is beyond bit size, 1 << 100 becomes 0, so 0 == 0
1985        assert_eq!(
1986            const_cmp_shifted(&zero.array, &e.array, 100),
1987            core::cmp::Ordering::Equal
1988        );
1989
1990        // Test sub_shifted with word boundary crossing
1991        let mut val = TestInt::from(0x10000u32); // 65536
1992        let one = TestInt::from(1u32);
1993        const_sub_shifted(&mut val.array, &one.array, 15); // subtract 1 << 15 = 32768
1994        assert_eq!(val, TestInt::from(0x8000u32)); // 65536 - 32768 = 32768
1995
1996        // Test sub_shifted with multi-word operations
1997        let mut big_val = TestInt::from(0x100000000u64); // 2^32
1998        const_sub_shifted(&mut big_val.array, &one.array, 31); // subtract 1 << 31 = 2^31
1999        assert_eq!(big_val, TestInt::from(0x80000000u64)); // 2^32 - 2^31 = 2^31
2000    }
2001
2002    #[test]
2003    fn test_shifted_operations_edge_cases() {
2004        type TestInt = FixedUInt<u32, 2>;
2005
2006        // Test zero shifts
2007        let a = TestInt::from(42u32);
2008        let a2 = TestInt::from(42u32);
2009        assert_eq!(
2010            const_cmp_shifted(&a.array, &a2.array, 0),
2011            core::cmp::Ordering::Equal
2012        );
2013
2014        let mut b = TestInt::from(42u32);
2015        let ten = TestInt::from(10u32);
2016        const_sub_shifted(&mut b.array, &ten.array, 0);
2017        assert_eq!(b, TestInt::from(32u32));
2018
2019        // Test massive shifts (beyond bit size)
2020        let c = TestInt::from(123u32);
2021        let large = TestInt::from(456u32);
2022        assert_eq!(
2023            const_cmp_shifted(&c.array, &large.array, 200),
2024            core::cmp::Ordering::Greater
2025        );
2026
2027        let mut d = TestInt::from(123u32);
2028        const_sub_shifted(&mut d.array, &large.array, 200); // Should be no-op
2029        assert_eq!(d, TestInt::from(123u32));
2030
2031        // Test with zero values
2032        let zero = TestInt::from(0u32);
2033        let one = TestInt::from(1u32);
2034        assert_eq!(
2035            const_cmp_shifted(&zero.array, &zero.array, 10),
2036            core::cmp::Ordering::Equal
2037        );
2038        assert_eq!(
2039            const_cmp_shifted(&one.array, &zero.array, 10),
2040            core::cmp::Ordering::Greater
2041        );
2042    }
2043
2044    #[test]
2045    fn test_shifted_operations_equivalence() {
2046        type TestInt = FixedUInt<u32, 2>;
2047
2048        // Test that optimized operations give same results as naive shift+op
2049        let test_cases = [
2050            (0x12345u32, 0x678u32, 4),
2051            (0x1000u32, 0x10u32, 8),
2052            (0xABCDu32, 0x1u32, 16),
2053            (0x80000000u32, 0x1u32, 1),
2054        ];
2055
2056        for (a_val, b_val, shift) in test_cases {
2057            let a = TestInt::from(a_val);
2058            let b = TestInt::from(b_val);
2059
2060            // Test cmp_shifted equivalence
2061            let optimized_cmp = const_cmp_shifted(&a.array, &b.array, shift);
2062            let naive_cmp = a.cmp(&(b << shift));
2063            assert_eq!(
2064                optimized_cmp, naive_cmp,
2065                "cmp_shifted mismatch: {} vs ({} << {})",
2066                a_val, b_val, shift
2067            );
2068
2069            // Test sub_shifted equivalence (if subtraction won't underflow)
2070            if a >= (b << shift) {
2071                let mut optimized_result = a;
2072                const_sub_shifted(&mut optimized_result.array, &b.array, shift);
2073
2074                let naive_result = a - (b << shift);
2075                assert_eq!(
2076                    optimized_result, naive_result,
2077                    "sub_shifted mismatch: {} - ({} << {})",
2078                    a_val, b_val, shift
2079                );
2080            }
2081        }
2082    }
2083
2084    #[test]
2085    fn test_div_assign_in_place_optimization() {
2086        type TestInt = FixedUInt<u32, 2>;
2087
2088        // Test that div_assign uses the optimized in-place algorithm
2089        let test_cases = [
2090            (100u32, 10u32, 10u32, 0u32),     // 100 / 10 = 10 remainder 0
2091            (123u32, 7u32, 17u32, 4u32),      // 123 / 7 = 17 remainder 4
2092            (1000u32, 13u32, 76u32, 12u32),   // 1000 / 13 = 76 remainder 12
2093            (65535u32, 255u32, 257u32, 0u32), // 65535 / 255 = 257 remainder 0
2094        ];
2095
2096        for (dividend_val, divisor_val, expected_quotient, expected_remainder) in test_cases {
2097            // Test div_assign
2098            let mut dividend = TestInt::from(dividend_val);
2099            let divisor = TestInt::from(divisor_val);
2100
2101            dividend /= divisor;
2102            assert_eq!(
2103                dividend,
2104                TestInt::from(expected_quotient),
2105                "div_assign: {} / {} should be {}",
2106                dividend_val,
2107                divisor_val,
2108                expected_quotient
2109            );
2110
2111            // Test div_rem directly
2112            let dividend2 = TestInt::from(dividend_val);
2113            let (quotient, remainder) = dividend2.div_rem(&divisor);
2114            assert_eq!(
2115                quotient,
2116                TestInt::from(expected_quotient),
2117                "div_rem quotient: {} / {} should be {}",
2118                dividend_val,
2119                divisor_val,
2120                expected_quotient
2121            );
2122            assert_eq!(
2123                remainder,
2124                TestInt::from(expected_remainder),
2125                "div_rem remainder: {} % {} should be {}",
2126                dividend_val,
2127                divisor_val,
2128                expected_remainder
2129            );
2130
2131            // Verify: quotient * divisor + remainder == original dividend
2132            assert_eq!(
2133                quotient * divisor + remainder,
2134                TestInt::from(dividend_val),
2135                "Property check failed for {}",
2136                dividend_val
2137            );
2138        }
2139    }
2140
2141    #[test]
2142    fn test_div_assign_stack_efficiency() {
2143        type TestInt = FixedUInt<u32, 4>; // 16 bytes each
2144
2145        // Create test values
2146        let mut dividend = TestInt::from(0x123456789ABCDEFu64);
2147        let divisor = TestInt::from(0x12345u32);
2148        let original_dividend = dividend;
2149
2150        // Perform in-place division
2151        dividend /= divisor;
2152
2153        // Verify correctness
2154        let remainder = original_dividend % divisor;
2155        assert_eq!(dividend * divisor + remainder, original_dividend);
2156    }
2157
2158    #[test]
2159    fn test_rem_assign_optimization() {
2160        type TestInt = FixedUInt<u32, 2>;
2161
2162        let test_cases = [
2163            (100u32, 10u32, 0u32),    // 100 % 10 = 0
2164            (123u32, 7u32, 4u32),     // 123 % 7 = 4
2165            (1000u32, 13u32, 12u32),  // 1000 % 13 = 12
2166            (65535u32, 255u32, 0u32), // 65535 % 255 = 0
2167        ];
2168
2169        for (dividend_val, divisor_val, expected_remainder) in test_cases {
2170            let mut dividend = TestInt::from(dividend_val);
2171            let divisor = TestInt::from(divisor_val);
2172
2173            dividend %= divisor;
2174            assert_eq!(
2175                dividend,
2176                TestInt::from(expected_remainder),
2177                "rem_assign: {} % {} should be {}",
2178                dividend_val,
2179                divisor_val,
2180                expected_remainder
2181            );
2182        }
2183    }
2184
2185    #[test]
2186    fn test_div_with_remainder_property() {
2187        type TestInt = FixedUInt<u32, 2>;
2188
2189        // Test division with remainder property verification
2190        let test_cases = [
2191            (100u32, 10u32, 10u32),     // 100 / 10 = 10
2192            (123u32, 7u32, 17u32),      // 123 / 7 = 17
2193            (1000u32, 13u32, 76u32),    // 1000 / 13 = 76
2194            (65535u32, 255u32, 257u32), // 65535 / 255 = 257
2195        ];
2196
2197        for (dividend_val, divisor_val, expected_quotient) in test_cases {
2198            let dividend = TestInt::from(dividend_val);
2199            let divisor = TestInt::from(divisor_val);
2200
2201            // Test that div operator (which uses div_impl) works correctly
2202            let quotient = dividend / divisor;
2203            assert_eq!(
2204                quotient,
2205                TestInt::from(expected_quotient),
2206                "Division: {} / {} should be {}",
2207                dividend_val,
2208                divisor_val,
2209                expected_quotient
2210            );
2211
2212            // Verify the division property still holds
2213            let remainder = dividend % divisor;
2214            assert_eq!(
2215                quotient * divisor + remainder,
2216                dividend,
2217                "Division property check failed for {}",
2218                dividend_val
2219            );
2220        }
2221    }
2222
2223    #[test]
2224    fn test_code_simplification_benefits() {
2225        type TestInt = FixedUInt<u32, 2>;
2226
2227        // Verify division property holds
2228        let dividend = TestInt::from(12345u32);
2229        let divisor = TestInt::from(67u32);
2230        let quotient = dividend / divisor;
2231        let remainder = dividend % divisor;
2232
2233        // The division property should still hold
2234        assert_eq!(quotient * divisor + remainder, dividend);
2235    }
2236
2237    #[test]
2238    fn test_rem_assign_correctness_after_fix() {
2239        type TestInt = FixedUInt<u32, 2>;
2240
2241        // Test specific case: 17 % 5 = 2
2242        let mut a = TestInt::from(17u32);
2243        let b = TestInt::from(5u32);
2244
2245        // Historical note: an old bug caused quotient corruption during remainder calculation
2246        // Now const_div_rem properly computes both without corrupting intermediate state
2247        a %= b;
2248        assert_eq!(a, TestInt::from(2u32), "17 % 5 should be 2");
2249
2250        // Test that the original RemAssign bug would have failed this
2251        let mut test_val = TestInt::from(100u32);
2252        test_val %= TestInt::from(7u32);
2253        assert_eq!(
2254            test_val,
2255            TestInt::from(2u32),
2256            "100 % 7 should be 2 (not 14, the quotient)"
2257        );
2258    }
2259
2260    #[test]
2261    fn test_div_property_based() {
2262        type TestInt = FixedUInt<u16, 2>;
2263
2264        // Property: quotient * divisor + remainder == dividend
2265        let test_pairs = [
2266            (12345u16, 67u16),
2267            (1000u16, 13u16),
2268            (65535u16, 255u16),
2269            (5000u16, 7u16),
2270        ];
2271
2272        for (dividend_val, divisor_val) in test_pairs {
2273            let dividend = TestInt::from(dividend_val);
2274            let divisor = TestInt::from(divisor_val);
2275
2276            let quotient = dividend / divisor;
2277
2278            // Property verification: quotient * divisor + remainder == dividend
2279            let remainder = dividend - (quotient * divisor);
2280            let reconstructed = quotient * divisor + remainder;
2281
2282            assert_eq!(
2283                reconstructed,
2284                dividend,
2285                "Property failed for {} / {}: {} * {} + {} != {}",
2286                dividend_val,
2287                divisor_val,
2288                quotient.to_u32().unwrap_or(0),
2289                divisor_val,
2290                remainder.to_u32().unwrap_or(0),
2291                dividend_val
2292            );
2293
2294            // Remainder should be less than divisor
2295            assert!(
2296                remainder < divisor,
2297                "Remainder {} >= divisor {} for {} / {}",
2298                remainder.to_u32().unwrap_or(0),
2299                divisor_val,
2300                dividend_val,
2301                divisor_val
2302            );
2303        }
2304    }
2305}