Skip to main content

decimal_scaled/int/types/
mod.rs

1// SPDX-FileCopyrightText: 2026 John Moxley
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Const-generic fixed-width integers.
5//!
6//! A single `Uint<const LIMBS: usize>` / `Int<const LIMBS: usize>` pair
7//! parameterised by the number of 64-bit little-endian limbs, replacing
8//! the family of named `IntXXXX` / `UintXXXX` types. Bit width is
9//! derived (`BITS = LIMBS * 64`); every width this crate uses
10//! (256 … 16384 bits) is a clean multiple of 64, so the limb count is
11//! the natural single parameter — it sidesteps the
12//! `LIMBS = ceil(BITS / 64)` derivation that a `BITS`-parameterised type
13//! cannot express on stable Rust.
14//!
15//! Storage is `[u64; LIMBS]`, matching the limb representation the
16//! arithmetic primitives already use. Methods delegate to the
17//! width-matched limb algorithms; because `LIMBS` is a compile-time
18//! constant, the limb loops unroll and any `match LIMBS` arms const-fold
19//! per monomorphisation — no runtime dispatch.
20
21pub(crate) mod traits;
22pub(crate) mod compute_limbs;
23
24pub use traits::BigInt;
25
26use crate::int::algos::div::div_fixed::div_rem_mag_fixed;
27use crate::int::algos::div::div_rem::div_rem;
28use crate::int::algos::support::limbs::{
29    add_assign_fixed, bit_len_fixed, cmp_cross, cmp_fixed, is_zero_fixed, max_n_limbs, shl,
30    shl_fixed, shr_fixed, sub_assign_fixed,
31};
32use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook};
33use crate::int::policy::add::dispatch as add_dispatch;
34use crate::int::policy::cmp::dispatch as cmp_dispatch;
35use crate::int::policy::cube::dispatch as cube_dispatch;
36use crate::int::policy::div_rem::dispatch as div_rem_dispatch;
37use crate::int::policy::eq::dispatch as eq_dispatch;
38use crate::int::policy::icbrt::dispatch as icbrt_dispatch;
39use crate::int::policy::hypot::dispatch as hypot_dispatch;
40use crate::int::policy::isqrt::dispatch as isqrt_dispatch;
41use crate::int::policy::mul::dispatch as mul_fast;
42use crate::int::policy::neg::dispatch as neg_dispatch;
43use crate::int::policy::sum_sq::dispatch as sum_sq_dispatch;
44use crate::int::policy::pow::dispatch as pow_dispatch;
45use crate::int::policy::rem::dispatch as rem_dispatch;
46use crate::int::policy::sqr::dispatch as sqr_dispatch;
47use crate::int::policy::sub::dispatch as sub_dispatch;
48use crate::support::int_fmt::fmt_into;
49use core::cmp::Ordering;
50use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub};
51
52/// Unsigned fixed-width integer of `N` little-endian 64-bit limbs.
53#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
54pub struct Uint<const N: usize> {
55    limbs: [u64; N],
56}
57
58/// Signed (two's-complement) fixed-width integer of `N` little-endian
59/// 64-bit limbs.
60//
61// `PartialEq` / `PartialOrd` / `Ord` are NOT derived: a single generic
62// `impl<N, M>` cross-width surface (below) covers same-width comparison
63// too, so a derived same-width impl would collide. `Eq` is still derived
64// (it only needs the `PartialEq<Self>` the generic impl provides).
65#[derive(Clone, Copy, Eq, Hash, Debug)]
66pub struct Int<const N: usize> {
67    limbs: [u64; N],
68}
69
70impl<const N: usize> Uint<N> {
71    /// Number of 64-bit limbs.
72    pub const LIMBS: usize = N;
73    /// Bit width (`LIMBS * 64`). `u32` so it composes directly with the
74    /// `leading_zeros` / `count_ones` `u32` surface and matches the
75    /// historic named-type `BITS` constant.
76    pub const BITS: u32 = (N as u32) * 64;
77
78    /// Additive identity.
79    pub const ZERO: Self = Self { limbs: [0; N] };
80    /// Multiplicative identity.
81    pub const ONE: Self = {
82        let mut limbs = [0u64; N];
83        limbs[0] = 1;
84        Self { limbs }
85    };
86    /// Largest representable value (all limbs set).
87    pub const MAX: Self = Self {
88        limbs: [u64::MAX; N],
89    };
90
91    /// Constructs from raw little-endian limbs.
92    #[inline]
93    pub const fn from_limbs(limbs: [u64; N]) -> Self {
94        Self { limbs }
95    }
96
97    /// Borrows the raw little-endian limbs.
98    #[inline]
99    pub const fn as_limbs(&self) -> &[u64; N] {
100        &self.limbs
101    }
102
103    /// Wrapping addition (modulo `2^BITS`).
104    #[inline]
105    pub fn wrapping_add(mut self, rhs: Self) -> Self {
106        add_assign_fixed(&mut self.limbs, &rhs.limbs);
107        self
108    }
109
110    /// Wrapping subtraction (modulo `2^BITS`).
111    #[inline]
112    pub fn wrapping_sub(mut self, rhs: Self) -> Self {
113        sub_assign_fixed(&mut self.limbs, &rhs.limbs);
114        self
115    }
116
117    /// Wrapping multiplication (modulo `2^BITS`).
118    ///
119    /// Schoolbook multiply truncated to the low `N` limbs. Only the
120    /// product limbs that land below `2^BITS` are kept, so no
121    /// `[u64; 2*N]` output buffer is needed — the higher partial
122    /// products are simply never written. Carries that would land at or
123    /// above limb `N` are discarded, which is exactly the modular
124    /// reduction.
125    #[inline]
126    pub fn wrapping_mul(self, rhs: Self) -> Self {
127        let mut out = [0u64; N];
128        mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
129        Self { limbs: out }
130    }
131
132    /// Wrapping square (`self²` modulo `2^BITS`). Named entry point for
133    /// the open-coded `x * x` pattern. Thin delegator DOWN to the sqr
134    /// policy (`sqr_dispatch`): half-product squaring via the
135    /// `sqr_low_fixed` kernel — each cross term is formed once and doubled,
136    /// so the limb-multiply count is `N(N+1)/2` rather than the `N²` of a
137    /// general multiply.
138    #[inline]
139    pub const fn wrapping_sqr(self) -> Self {
140        sqr_dispatch(self)
141    }
142
143    /// Wrapping cube (`self³` modulo `2^BITS`). Named entry point for the
144    /// open-coded `x * x * x` pattern. Thin delegator DOWN to the cube
145    /// policy (`cube_dispatch`): `sqr` then one multiply via the const
146    /// kernels — no cheaper form exists below two multiplies.
147    #[inline]
148    pub const fn wrapping_cube(self) -> Self {
149        cube_dispatch(self)
150    }
151
152    /// Checked addition: `None` on overflow past `2^BITS`.
153    #[inline]
154    pub fn checked_add(mut self, rhs: Self) -> Option<Self> {
155        let carry = add_assign_fixed(&mut self.limbs, &rhs.limbs);
156        if carry { None } else { Some(self) }
157    }
158
159    /// Checked subtraction: `None` if the result would be negative.
160    #[inline]
161    pub fn checked_sub(mut self, rhs: Self) -> Option<Self> {
162        let borrow = sub_assign_fixed(&mut self.limbs, &rhs.limbs);
163        if borrow { None } else { Some(self) }
164    }
165
166    /// Checked multiplication: `None` if the true product does not fit
167    /// `2^BITS`.
168    #[inline]
169    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
170        let (a, b) = (&self.limbs, &rhs.limbs);
171        let mut out = [0u64; N];
172        let mut overflow = false;
173        let mut i = 0;
174        while i < N {
175            let ai = a[i];
176            if ai != 0 {
177                let mut carry: u64 = 0;
178                let mut j = 0;
179                while j < N {
180                    let prod = (ai as u128) * (b[j] as u128);
181                    if i + j < N {
182                        let v = prod + (out[i + j] as u128) + (carry as u128);
183                        out[i + j] = v as u64;
184                        carry = (v >> 64) as u64;
185                    } else if prod != 0 || carry != 0 {
186                        // Any product or carry landing at/above limb `N`
187                        // means the true product exceeds the width.
188                        overflow = true;
189                        carry = 0;
190                    }
191                    j += 1;
192                }
193                if carry != 0 {
194                    // Row carry would spill into limb `i + N >= N`.
195                    overflow = true;
196                }
197            }
198            i += 1;
199        }
200        if overflow {
201            None
202        } else {
203            Some(Self { limbs: out })
204        }
205    }
206
207    /// Wrapping division. Panics on a zero divisor, matching the
208    /// behaviour of the primitive unsigned integer types.
209    #[inline]
210    pub fn wrapping_div(self, rhs: Self) -> Self {
211        assert!(!rhs.is_zero(), "attempt to divide by zero");
212        let mut quot = [0u64; N];
213        let mut rem = [0u64; N];
214        div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
215        Self { limbs: quot }
216    }
217
218    /// Wrapping remainder. Panics on a zero divisor, matching the
219    /// behaviour of the primitive unsigned integer types.
220    #[inline]
221    pub fn wrapping_rem(self, rhs: Self) -> Self {
222        assert!(
223            !rhs.is_zero(),
224            "attempt to calculate the remainder with a divisor of zero"
225        );
226        let mut quot = [0u64; N];
227        let mut rem = [0u64; N];
228        div_rem(&self.limbs, &rhs.limbs, &mut quot, &mut rem);
229        Self { limbs: rem }
230    }
231
232    /// Bitwise AND.
233    // Operator-named inherent kernels (bitand/bitor/bitxor/not/shl/shr):
234    // deliberately NOT the std ops traits — const-usable, and `shl`/`shr` take
235    // a `u32` shift rather than the `Shl<Rhs>`/`Shr<Rhs>` shape (the std ops
236    // are implemented separately). Allow the look-alike-trait-method lint.
237    #[allow(clippy::should_implement_trait)]
238    #[inline]
239    pub fn bitand(self, rhs: Self) -> Self {
240        let mut out = [0u64; N];
241        let mut i = 0;
242        while i < N {
243            out[i] = self.limbs[i] & rhs.limbs[i];
244            i += 1;
245        }
246        Self { limbs: out }
247    }
248
249    /// Bitwise OR.
250    #[allow(clippy::should_implement_trait)]
251    #[inline]
252    pub fn bitor(self, rhs: Self) -> Self {
253        let mut out = [0u64; N];
254        let mut i = 0;
255        while i < N {
256            out[i] = self.limbs[i] | rhs.limbs[i];
257            i += 1;
258        }
259        Self { limbs: out }
260    }
261
262    /// Bitwise XOR.
263    #[allow(clippy::should_implement_trait)]
264    #[inline]
265    pub fn bitxor(self, rhs: Self) -> Self {
266        let mut out = [0u64; N];
267        let mut i = 0;
268        while i < N {
269            out[i] = self.limbs[i] ^ rhs.limbs[i];
270            i += 1;
271        }
272        Self { limbs: out }
273    }
274
275    /// Bitwise NOT (ones' complement).
276    #[allow(clippy::should_implement_trait)]
277    #[inline]
278    pub fn not(self) -> Self {
279        let mut out = [0u64; N];
280        let mut i = 0;
281        while i < N {
282            out[i] = !self.limbs[i];
283            i += 1;
284        }
285        Self { limbs: out }
286    }
287
288    /// Logical left shift by `shift` bits (modulo `2^BITS`).
289    #[allow(clippy::should_implement_trait)]
290    #[inline]
291    pub fn shl(self, shift: u32) -> Self {
292        let mut out = [0u64; N];
293        shl_fixed(&self.limbs, shift, &mut out);
294        Self { limbs: out }
295    }
296
297    /// Logical right shift by `shift` bits.
298    #[allow(clippy::should_implement_trait)]
299    #[inline]
300    pub fn shr(self, shift: u32) -> Self {
301        let mut out = [0u64; N];
302        shr_fixed(&self.limbs, shift, &mut out);
303        Self { limbs: out }
304    }
305
306    /// `true` when every limb is zero.
307    #[inline]
308    pub fn is_zero(&self) -> bool {
309        is_zero_fixed(&self.limbs)
310    }
311
312    /// Bit length (significant bits): `0` for zero, else
313    /// `floor(log2(self)) + 1`, equivalently `BITS - leading_zeros`.
314    ///
315    /// For an unsigned value there is no sign, so magnitude and stored
316    /// representation coincide — `bit_length` and the `uN`-contract
317    /// bit-count methods below agree by construction.
318    #[inline]
319    pub const fn bit_length(&self) -> u32 {
320        bit_len_fixed(&self.limbs)
321    }
322
323    /// Number of leading zero bits in the `BITS`-wide representation,
324    /// matching the primitive `uN::leading_zeros` contract:
325    /// `BITS - bit_length`, and `BITS` for zero.
326    #[inline]
327    pub const fn leading_zeros(&self) -> u32 {
328        Self::BITS - self.bit_length()
329    }
330
331    /// `true` when the value equals one.
332    #[inline]
333    pub fn is_one(&self) -> bool {
334        if N == 0 || self.limbs[0] != 1 {
335            return false;
336        }
337        let mut i = 1;
338        while i < N {
339            if self.limbs[i] != 0 {
340                return false;
341            }
342            i += 1;
343        }
344        true
345    }
346
347    /// Wrapping exponentiation by squaring (`self^exp` modulo `2^BITS`).
348    /// `self^0 == 1`. Thin delegator DOWN to the pow policy
349    /// (`pow_dispatch`): binary square-and-multiply over the const
350    /// kernels; optimal for the small fixed exponents the root iteration
351    /// needs (`k-1`, `k`).
352    #[inline]
353    pub const fn wrapping_pow(self, exp: u32) -> Self {
354        pow_dispatch(self, exp)
355    }
356
357    /// Exponentiation by squaring, returning `None` if the true power
358    /// overflows `2^BITS`. `self^0 == 1`.
359    #[inline]
360    pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
361        let mut acc = Self::ONE;
362        let mut base = self;
363        loop {
364            if exp & 1 == 1 {
365                acc = acc.checked_mul(base)?;
366            }
367            exp >>= 1;
368            if exp == 0 {
369                break;
370            }
371            base = base.checked_mul(base)?;
372        }
373        Some(acc)
374    }
375
376    /// Exponentiation by squaring. Routes through the pow policy
377    /// (`pow_dispatch`): binary square-and-multiply at every `N`.
378    /// Result is `self^exp` modulo `2^BITS`; `self^0 == 1`.
379    #[inline]
380    pub fn pow(self, exp: u32) -> Self {
381        pow_dispatch(self, exp)
382    }
383
384    /// Integer square root: the largest `r` with `r² <= self`.
385    /// Routes through the isqrt policy (`isqrt_dispatch`):
386    /// `N ∈ {1, 2}` takes the hardware native path; `N >= 3` takes
387    /// the Newton limb kernel.
388    #[inline]
389    pub fn isqrt(self) -> Self {
390        isqrt_dispatch(self)
391    }
392
393    /// Integer square: `self²` modulo `2^BITS`. Routes through the sqr
394    /// policy (`sqr_dispatch`): half-product squaring kernel at every `N`.
395    #[inline]
396    pub fn sqr(self) -> Self {
397        sqr_dispatch(self)
398    }
399
400    /// Integer cube: `self³` modulo `2^BITS`. Routes through the cube
401    /// policy (`cube_dispatch`): sqr-then-multiply at every `N`.
402    #[inline]
403    pub fn cube(self) -> Self {
404        cube_dispatch(self)
405    }
406
407    /// Integer cube root: `floor(self^(1/3))`. Routes through the icbrt
408    /// policy (`icbrt_dispatch`):
409    /// `N ∈ {1, 2}` takes the narrow path; `N >= 3` takes the Newton
410    /// limb kernel with an `f64::cbrt` seed.
411    #[inline]
412    pub fn icbrt(self) -> Self {
413        icbrt_dispatch(self)
414    }
415
416    /// Integer `k`th root: returns `(root, exact)` where
417    /// `root = floor(self^(1/k))` and `exact` is `true` iff
418    /// `root^k == self`. `k` must be `>= 1`.
419    ///
420    /// Brent–Zimmermann RootInt (Modern Computer Arithmetic §1.5.2): the
421    /// integer projection of Newton's iteration
422    /// `u = ((k-1)·s + m / s^(k-1)) / k`, started from an upper bound on
423    /// the root and run until the monotone-decreasing sequence first
424    /// fails to decrease (`u >= s`), at which point `s` is the floor
425    /// root. The seed is the no_std-safe bit-length bound
426    /// `2^ceil(bit_length / k)` — a clean upper bound since
427    /// `(2^ceil(L/k))^k >= 2^L > m`. `k == 2` reuses the dedicated
428    /// [`Self::isqrt`]; `k == 3` is the cube root.
429    pub fn root_int(self, k: u32) -> (Self, bool) {
430        debug_assert!(k >= 1, "root_int requires k >= 1");
431        // Degenerate / trivial roots.
432        if k == 1 {
433            return (self, true);
434        }
435        if self.is_zero() {
436            return (Self::ZERO, true);
437        }
438        if self.is_one() {
439            return (Self::ONE, true);
440        }
441        if k == 2 {
442            let r = self.isqrt();
443            return (r, r.wrapping_sqr() == self);
444        }
445
446        // Seed: 2^ceil(bit_length / k) is an upper bound on the root.
447        let len = self.bit_length();
448        let seed_shift = len.div_ceil(k);
449        // ceil(len/k) <= len for k >= 2, so the seed fits the width.
450        let mut s = Self::ONE.shl(seed_shift);
451
452        // Newton: s decreases monotonically while above the root.
453        loop {
454            // t = (k-1)*s + m / s^(k-1)
455            let pow_km1 = s.wrapping_pow(k - 1);
456            // pow_km1 is non-zero (s >= 1), so the divide is defined.
457            let quot = self.wrapping_div(pow_km1);
458            let mut t = Self::ZERO;
459            let mut c = 0;
460            while c < k - 1 {
461                t = t.wrapping_add(s);
462                c += 1;
463            }
464            t = t.wrapping_add(quot);
465            // u = t / k
466            let u = t.wrapping_div(Self::from_u64(k as u64));
467            if u >= s {
468                break;
469            }
470            s = u;
471        }
472
473        let exact = s.checked_pow(k).is_some_and(|p| p == self);
474        (s, exact)
475    }
476
477    /// Constructs from a `u64`, zero-extending into the high limbs.
478    #[inline]
479    pub(crate) fn from_u64(value: u64) -> Self {
480        let mut limbs = [0u64; N];
481        if N > 0 {
482            limbs[0] = value;
483        }
484        Self { limbs }
485    }
486
487    /// Builds from an unsigned 128-bit value, zero-extending the upper
488    /// limbs. **Truncating** for `Uint<1>` (the high 64 bits of `v` are
489    /// discarded); use the checked `TryFrom` conversion when `v` may
490    /// not fit.
491    #[inline]
492    pub(crate) const fn from_u128(v: u128) -> Self {
493        let mut limbs = [0u64; N];
494        if N > 0 {
495            limbs[0] = v as u64;
496        }
497        if N > 1 {
498            limbs[1] = (v >> 64) as u64;
499        }
500        Self { limbs }
501    }
502
503    /// Exact value conversion from `u128`, or `None` if `v` does not fit
504    /// `Uint<N>` (only possible for `N < 2`). For `N >= 2` every `u128` fits.
505    #[inline]
506    pub(crate) const fn try_from_u128(v: u128) -> Option<Self> {
507        if N >= 2 || v <= u64::MAX as u128 {
508            Some(Self::from_u128(v))
509        } else {
510            None
511        }
512    }
513
514    /// Reinterprets the bit pattern as the signed sibling.
515    #[inline]
516    pub const fn cast_signed(self) -> Int<N> {
517        Int::from_limbs(self.limbs)
518    }
519
520    /// Approximate `f64` value (positive; truncated toward zero on
521    /// overflow).
522    pub fn as_f64(self) -> f64 {
523        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
524        let mut acc = 0.0f64;
525        let mut i = N;
526        while i > 0 {
527            i -= 1;
528            acc = acc * radix + self.limbs[i] as f64;
529        }
530        acc
531    }
532
533    /// Set-bit count across all limbs, matching the primitive
534    /// `uN::count_ones` contract.
535    #[inline]
536    pub const fn count_ones(self) -> u32 {
537        let mut total = 0;
538        let mut i = 0;
539        while i < N {
540            total += self.limbs[i].count_ones();
541            i += 1;
542        }
543        total
544    }
545
546    /// `true` when exactly one bit is set.
547    #[inline]
548    pub const fn is_power_of_two(self) -> bool {
549        self.count_ones() == 1
550    }
551
552    /// Smallest power of two `>= self` (`1` for zero), wrapping on
553    /// overflow.
554    pub fn next_power_of_two(self) -> Self {
555        if self.is_zero() {
556            return Self::ONE;
557        }
558        if self.is_power_of_two() {
559            return self;
560        }
561        let bits = self.bit_length();
562        let mut out = [0u64; N];
563        if (bits as usize) < N * 64 {
564            out[(bits / 64) as usize] = 1u64 << (bits % 64);
565        }
566        Self { limbs: out }
567    }
568
569    /// Parses an unsigned decimal string. Only base 10 is supported.
570    pub const fn from_str_radix(s: &str, radix: u32) -> Result<Self, ()> {
571        if radix != 10 {
572            return Err(());
573        }
574        let bytes = s.as_bytes();
575        if bytes.is_empty() {
576            return Err(());
577        }
578        let mut acc = [0u64; N];
579        let mut k = 0;
580        while k < bytes.len() {
581            let ch = bytes[k];
582            if ch < b'0' || ch > b'9' {
583                return Err(());
584            }
585            let d = (ch - b'0') as u64;
586            let mut carry: u64 = d;
587            let mut j = 0;
588            while j < N {
589                let p = (acc[j] as u128) * 10u128 + (carry as u128);
590                acc[j] = p as u64;
591                carry = (p >> 64) as u64;
592                j += 1;
593            }
594            k += 1;
595        }
596        Ok(Self { limbs: acc })
597    }
598}
599
600impl<const N: usize> Add for Uint<N> {
601    type Output = Self;
602    #[inline]
603    fn add(self, rhs: Self) -> Self {
604        self.wrapping_add(rhs)
605    }
606}
607
608impl<const N: usize> Sub for Uint<N> {
609    type Output = Self;
610    #[inline]
611    fn sub(self, rhs: Self) -> Self {
612        self.wrapping_sub(rhs)
613    }
614}
615
616impl<const N: usize> Mul for Uint<N> {
617    type Output = Self;
618    #[inline]
619    fn mul(self, rhs: Self) -> Self {
620        self.wrapping_mul(rhs)
621    }
622}
623
624impl<const N: usize> BitAnd for Uint<N> {
625    type Output = Self;
626    #[inline]
627    fn bitand(self, rhs: Self) -> Self {
628        Uint::bitand(self, rhs)
629    }
630}
631
632impl<const N: usize> BitOr for Uint<N> {
633    type Output = Self;
634    #[inline]
635    fn bitor(self, rhs: Self) -> Self {
636        Uint::bitor(self, rhs)
637    }
638}
639
640impl<const N: usize> BitXor for Uint<N> {
641    type Output = Self;
642    #[inline]
643    fn bitxor(self, rhs: Self) -> Self {
644        Uint::bitxor(self, rhs)
645    }
646}
647
648impl<const N: usize> Not for Uint<N> {
649    type Output = Self;
650    #[inline]
651    fn not(self) -> Self {
652        Uint::not(self)
653    }
654}
655
656impl<const N: usize> Shl<u32> for Uint<N> {
657    type Output = Self;
658    #[inline]
659    fn shl(self, shift: u32) -> Self {
660        Uint::shl(self, shift)
661    }
662}
663
664impl<const N: usize> Shr<u32> for Uint<N> {
665    type Output = Self;
666    #[inline]
667    fn shr(self, shift: u32) -> Self {
668        Uint::shr(self, shift)
669    }
670}
671
672// Truncating unsigned division / remainder via the dispatching divmod
673// (Knuth / Burnikel–Ziegler), matching the macro `$U` operators so the
674// const-generic and named unsigned types share one divide algorithm.
675
676impl<const N: usize> Div for Uint<N> {
677    type Output = Self;
678    #[inline]
679    fn div(self, rhs: Self) -> Self {
680        let mut q = [0u64; N];
681        let mut r = [0u64; N];
682        div_rem_dispatch(&self.limbs, &rhs.limbs, &mut q, &mut r);
683        Self { limbs: q }
684    }
685}
686
687impl<const N: usize> Rem for Uint<N> {
688    type Output = Self;
689    #[inline]
690    fn rem(self, rhs: Self) -> Self {
691        let mut q = [0u64; N];
692        let mut r = [0u64; N];
693        div_rem_dispatch(&self.limbs, &rhs.limbs, &mut q, &mut r);
694        Self { limbs: r }
695    }
696}
697
698impl<const N: usize> PartialOrd for Uint<N> {
699    #[inline]
700    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
701        Some(self.cmp(other))
702    }
703}
704
705impl<const N: usize> Ord for Uint<N> {
706    #[inline]
707    fn cmp(&self, other: &Self) -> Ordering {
708        match cmp_fixed(&self.limbs, &other.limbs) {
709            -1 => Ordering::Less,
710            1 => Ordering::Greater,
711            _ => Ordering::Equal,
712        }
713    }
714}
715
716impl<const N: usize> Int<N> {
717    /// Number of 64-bit limbs.
718    pub const LIMBS: usize = N;
719    /// Bit width (`LIMBS * 64`). `u32` so it composes directly with the
720    /// `leading_zeros` / `count_ones` `u32` surface and matches the
721    /// historic named-type `BITS` constant.
722    pub const BITS: u32 = (N as u32) * 64;
723
724    /// Additive identity.
725    pub const ZERO: Self = Self { limbs: [0; N] };
726    /// Multiplicative identity.
727    pub const ONE: Self = {
728        let mut limbs = [0u64; N];
729        limbs[0] = 1;
730        Self { limbs }
731    };
732    /// Most positive representable value (`2^(BITS-1) - 1`).
733    pub const MAX: Self = {
734        let mut limbs = [u64::MAX; N];
735        limbs[N - 1] = i64::MAX as u64;
736        Self { limbs }
737    };
738    /// Most negative representable value (`-2^(BITS-1)`).
739    pub const MIN: Self = {
740        let mut limbs = [0u64; N];
741        limbs[N - 1] = 1u64 << 63;
742        Self { limbs }
743    };
744
745    /// Constructs from raw little-endian two's-complement limbs.
746    #[inline]
747    pub const fn from_limbs(limbs: [u64; N]) -> Self {
748        Self { limbs }
749    }
750
751    /// Borrows the raw little-endian limbs.
752    #[inline]
753    pub const fn as_limbs(&self) -> &[u64; N] {
754        &self.limbs
755    }
756
757    /// `true` when every limb is zero.
758    #[inline]
759    pub const fn is_zero(&self) -> bool {
760        is_zero_fixed(&self.limbs)
761    }
762
763    /// `true` when the value is strictly negative (top bit set).
764    #[inline]
765    pub const fn is_negative(&self) -> bool {
766        N > 0 && (self.limbs[N - 1] >> 63) == 1
767    }
768
769    /// `true` when the value is strictly positive (non-zero and the
770    /// sign bit clear).
771    #[inline]
772    pub const fn is_positive(&self) -> bool {
773        !self.is_negative() && !self.is_zero()
774    }
775
776    /// Two's-complement wrapping negation (`!self + 1`). `MIN` negates
777    /// to itself, as with the primitive signed integers.
778    #[inline]
779    pub const fn wrapping_neg(self) -> Self {
780        neg_dispatch(self)
781    }
782
783    /// Wrapping addition (modulo `2^BITS`). Identical bit pattern to the
784    /// unsigned add — two's-complement makes signed and unsigned
785    /// addition the same operation.
786    #[inline]
787    pub const fn wrapping_add(self, rhs: Self) -> Self {
788        add_dispatch(self, rhs)
789    }
790
791    /// Wrapping subtraction (modulo `2^BITS`).
792    #[inline]
793    pub const fn wrapping_sub(self, rhs: Self) -> Self {
794        sub_dispatch(self, rhs)
795    }
796
797    /// Wrapping multiplication (modulo `2^BITS`). The low `N` limbs of a
798    /// two's-complement product are independent of the operand signs, so
799    /// this is the same truncated schoolbook the unsigned type uses.
800    #[inline]
801    pub const fn wrapping_mul(self, rhs: Self) -> Self {
802        let mut out = [0u64; N];
803        mul_low_fixed(&self.limbs, &rhs.limbs, &mut out);
804        Self { limbs: out }
805    }
806
807    /// Absolute value (wrapping: `MIN.abs() == MIN`).
808    #[inline]
809    pub const fn abs(self) -> Self {
810        if self.is_negative() {
811            self.wrapping_neg()
812        } else {
813            self
814        }
815    }
816
817    /// Sign: `-1`, `0`, or `1` as the value is negative, zero, or
818    /// positive.
819    #[inline]
820    pub fn signum(&self) -> i32 {
821        if self.is_zero() {
822            0
823        } else if self.is_negative() {
824            -1
825        } else {
826            1
827        }
828    }
829
830    /// Constructs from an `i64`, sign-extending into the high limbs.
831    #[inline]
832    pub(crate) const fn from_i64(value: i64) -> Self {
833        // Negative values fill the upper limbs with all-ones so the
834        // two's-complement representation matches at every width.
835        let fill = if value < 0 { u64::MAX } else { 0 };
836        let mut limbs = [fill; N];
837        if N > 0 {
838            limbs[0] = value as u64;
839        }
840        Self { limbs }
841    }
842
843    /// Constructs from an `i8` (always representable; sign-extends).
844    #[inline]
845    pub(crate) const fn from_i8(value: i8) -> Self {
846        Self::from_i64(value as i64)
847    }
848
849    /// Constructs from an `i16` (always representable; sign-extends).
850    #[inline]
851    pub(crate) const fn from_i16(value: i16) -> Self {
852        Self::from_i64(value as i64)
853    }
854
855    /// Constructs from an `i32` (always representable; sign-extends).
856    #[inline]
857    pub(crate) const fn from_i32(value: i32) -> Self {
858        Self::from_i64(value as i64)
859    }
860
861    /// Constructs from a `u8` (always representable; zero-extends).
862    #[inline]
863    pub(crate) const fn from_u8(value: u8) -> Self {
864        Self::from_u64_unsigned(value as u64)
865    }
866
867    /// Constructs from a `u16` (always representable; zero-extends).
868    #[inline]
869    pub(crate) const fn from_u16(value: u16) -> Self {
870        Self::from_u64_unsigned(value as u64)
871    }
872
873    /// Constructs from a `u32` (always representable; zero-extends).
874    #[inline]
875    pub(crate) const fn from_u32(value: u32) -> Self {
876        Self::from_u64_unsigned(value as u64)
877    }
878
879    /// Zero-extends an unsigned 64-bit value into limb 0. Internal helper
880    /// for the unsigned `from_*` family; the public fitting check is in
881    /// [`Self::try_from_u64`].
882    #[inline]
883    const fn from_u64_unsigned(value: u64) -> Self {
884        let mut limbs = [0u64; N];
885        if N > 0 {
886            limbs[0] = value;
887        }
888        Self { limbs }
889    }
890
891    /// Exact conversion from a `u64`, or `None` if it does not fit
892    /// `Int<N>`. Only `N == 1` (the `i64` floor) can fail, when the value
893    /// exceeds `i64::MAX`; every wider tier holds all of `u64`.
894    #[inline]
895    pub(crate) const fn try_from_u64(value: u64) -> Option<Self> {
896        if N >= 2 || value <= i64::MAX as u64 {
897            Some(Self::from_u64_unsigned(value))
898        } else {
899            None
900        }
901    }
902
903    /// Exact conversion from an `i128`, or `None` if it does not fit
904    /// `Int<N>`. Only `N == 1` (64-bit storage) can fail; `N >= 2` holds
905    /// every `i128`.
906    #[inline]
907    pub(crate) const fn try_from_i128(v: i128) -> Option<Self> {
908        let mag = v.unsigned_abs();
909        let built = Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], v < 0);
910        if N >= 2 || built.as_i128() == v {
911            Some(built)
912        } else {
913            None
914        }
915    }
916
917    /// Exact conversion from a `u128`, or `None` if it does not fit
918    /// `Int<N>`. `N == 1` fails above `i64::MAX`; `N == 2` fails above
919    /// `i128::MAX` (the sign bit); `N >= 3` holds every `u128`.
920    #[inline]
921    pub(crate) const fn try_from_u128(v: u128) -> Option<Self> {
922        let built = Self::from_mag_limbs(&[v as u64, (v >> 64) as u64], false);
923        if N >= 3 {
924            Some(built)
925        } else if built.is_negative() {
926            // Magnitude landed in the sign bit of the N-limb storage.
927            None
928        } else if N >= 2 || built.as_i128() as u128 == v {
929            Some(built)
930        } else {
931            None
932        }
933    }
934
935    /// Lossless `i64` value, valid on the `N == 1` tier where `Int<N>`
936    /// *is* an `i64`. The trait form is `From<Int<1>> for i64`.
937    #[inline]
938    pub(crate) const fn to_i64(self) -> i64 {
939        self.as_i128() as i64
940    }
941
942    /// Lossless `i128` value, valid on the `N <= 2` tiers where every
943    /// `Int<N>` fits an `i128`. The trait forms are `From<Int<1>>` /
944    /// `From<Int<2>> for i128`.
945    #[inline]
946    pub(crate) const fn to_i128(self) -> i128 {
947        self.as_i128()
948    }
949
950    /// Exact `i32` value, or `None` if out of range.
951    #[inline]
952    pub(crate) fn try_to_i32(self) -> Option<i32> {
953        match self.to_i128_checked() {
954            Some(v) if v >= i32::MIN as i128 && v <= i32::MAX as i128 => Some(v as i32),
955            _ => None,
956        }
957    }
958
959    /// Exact `u32` value, or `None` if negative or out of range.
960    #[inline]
961    pub(crate) fn try_to_u32(self) -> Option<u32> {
962        match self.to_u128_checked() {
963            Some(v) if v <= u32::MAX as u128 => Some(v as u32),
964            _ => None,
965        }
966    }
967
968    /// Exact `i64` value, or `None` if out of range.
969    #[inline]
970    pub(crate) fn try_to_i64(self) -> Option<i64> {
971        match self.to_i128_checked() {
972            Some(v) if v >= i64::MIN as i128 && v <= i64::MAX as i128 => Some(v as i64),
973            _ => None,
974        }
975    }
976
977    /// Exact `u64` value, or `None` if negative or out of range.
978    #[inline]
979    pub(crate) fn try_to_u64(self) -> Option<u64> {
980        match self.to_u128_checked() {
981            Some(v) if v <= u64::MAX as u128 => Some(v as u64),
982            _ => None,
983        }
984    }
985
986    /// Exact `i128` value, or `None` if out of range. Surface alias of
987    /// [`Self::to_i128_checked`].
988    #[inline]
989    pub(crate) fn try_to_i128(self) -> Option<i128> {
990        self.to_i128_checked()
991    }
992
993    /// Exact `u128` value, or `None` if negative or out of range. Surface
994    /// alias of [`Self::to_u128_checked`].
995    #[inline]
996    pub(crate) fn try_to_u128(self) -> Option<u128> {
997        self.to_u128_checked()
998    }
999
1000    /// `true` when the value equals one.
1001    #[inline]
1002    pub fn is_one(&self) -> bool {
1003        if N == 0 || self.limbs[0] != 1 {
1004            return false;
1005        }
1006        let mut i = 1;
1007        while i < N {
1008            if self.limbs[i] != 0 {
1009                return false;
1010            }
1011            i += 1;
1012        }
1013        true
1014    }
1015
1016    /// Most negative representable value (`-2^(BITS-1)`).
1017    #[inline]
1018    pub fn min_value() -> Self {
1019        let mut limbs = [0u64; N];
1020        if N > 0 {
1021            limbs[N - 1] = 1u64 << 63;
1022        }
1023        Self { limbs }
1024    }
1025
1026    /// Most positive representable value (`2^(BITS-1) - 1`).
1027    #[inline]
1028    pub fn max_value() -> Self {
1029        let mut limbs = [u64::MAX; N];
1030        if N > 0 {
1031            limbs[N - 1] = u64::MAX >> 1;
1032        }
1033        Self { limbs }
1034    }
1035
1036    /// Checked signed addition: `None` on two's-complement overflow.
1037    /// Overflow happens only when both operands share a sign and the
1038    /// result's sign differs from it. Routes through the add policy's
1039    /// checked door to the FUSED single-pass kernel (ripple + overflow
1040    /// verdict in one traversal) — the previous layered shape
1041    /// (`wrapping_add` then three sign reads then an `Option` rewrap)
1042    /// measured ≈2× the bare loop at 24 limbs in inter-layer moves.
1043    #[inline]
1044    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
1045        crate::int::policy::add::dispatch_checked(self, rhs)
1046    }
1047
1048    /// Checked signed subtraction: `None` on two's-complement overflow
1049    /// (the operands' signs differ and the result takes the subtrahend's
1050    /// sign). Routes through the sub policy's checked door to the fused
1051    /// single-pass kernel — see [`Self::checked_add`].
1052    #[inline]
1053    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
1054        crate::int::policy::sub::dispatch_checked(self, rhs)
1055    }
1056
1057    /// Checked signed multiplication: `None` if the true product does
1058    /// not fit the signed range. Computed via magnitudes so it reuses
1059    /// the unsigned overflow check, then re-signs.
1060    #[inline]
1061    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
1062        if self.is_zero() || rhs.is_zero() {
1063            return Some(Self::ZERO);
1064        }
1065        let neg = self.is_negative() ^ rhs.is_negative();
1066        let ma = Uint::<N>::from_limbs(*self.abs().as_limbs());
1067        let mb = Uint::<N>::from_limbs(*rhs.abs().as_limbs());
1068        let prod = ma.checked_mul(mb)?;
1069        let signed = Self::from_limbs(*prod.as_limbs());
1070        if neg {
1071            let r = signed.wrapping_neg();
1072            // Negative magnitude must not exceed |MIN|; the round-trip
1073            // through wrapping_neg detects the single MIN-magnitude case.
1074            if r.is_negative() || r.is_zero() {
1075                Some(r)
1076            } else {
1077                None
1078            }
1079        } else if signed.is_negative() {
1080            // Positive magnitude landed in the sign bit → overflow.
1081            None
1082        } else {
1083            Some(signed)
1084        }
1085    }
1086
1087    /// Wrapping exponentiation by squaring (`self^exp` modulo `2^BITS`).
1088    /// `self^0 == 1`. Thin delegator DOWN to the pow policy
1089    /// (`pow_dispatch`) on the unsigned reinterpretation: binary
1090    /// square-and-multiply over the const kernels. The low `N` limbs of a
1091    /// power are sign-independent, so reinterpreting as `Uint<N>` and back
1092    /// preserves the two's-complement result.
1093    #[inline]
1094    pub const fn wrapping_pow(self, exp: u32) -> Self {
1095        Self::from_limbs(*pow_dispatch(self.cast_unsigned(), exp).as_limbs())
1096    }
1097
1098    /// Exponentiation by squaring, returning `None` on signed overflow.
1099    #[inline]
1100    pub fn checked_pow(self, mut exp: u32) -> Option<Self> {
1101        let mut acc = Self::ONE;
1102        let mut base = self;
1103        loop {
1104            if exp & 1 == 1 {
1105                acc = acc.checked_mul(base)?;
1106            }
1107            exp >>= 1;
1108            if exp == 0 {
1109                break;
1110            }
1111            base = base.checked_mul(base)?;
1112        }
1113        Some(acc)
1114    }
1115
1116    /// Wrapping square (`self²` modulo `2^BITS`). Thin delegator DOWN to
1117    /// the sqr policy (`sqr_dispatch`) on the unsigned reinterpretation:
1118    /// half-product squaring via the const kernel. The low `N` limbs of a
1119    /// square are sign-independent, so reinterpreting as `Uint<N>` and back
1120    /// preserves the two's-complement result.
1121    #[inline]
1122    pub const fn wrapping_sqr(self) -> Self {
1123        Self::from_limbs(*sqr_dispatch(self.cast_unsigned()).as_limbs())
1124    }
1125
1126    /// Wrapping cube (`self³` modulo `2^BITS`). Thin delegator DOWN to the
1127    /// cube policy (`cube_dispatch`) on the unsigned reinterpretation:
1128    /// `sqr` then one multiply via the const kernels. The low `N` limbs are
1129    /// sign-independent, so reinterpreting as `Uint<N>` and back preserves
1130    /// the two's-complement result.
1131    #[inline]
1132    pub const fn wrapping_cube(self) -> Self {
1133        Self::from_limbs(*cube_dispatch(self.cast_unsigned()).as_limbs())
1134    }
1135
1136    /// MAGNITUDE bit length: `0` for zero, else `floor(log2|self|) + 1`
1137    /// — the number of significant bits of the absolute value `|self|`.
1138    ///
1139    /// This is a *distinct concept* from the primitive bit-count methods
1140    /// ([`Self::leading_zeros`], [`Self::trailing_zeros`],
1141    /// [`Self::count_ones`], [`Self::count_zeros`]), which read the
1142    /// two's-complement representation. `bit_length` ignores the sign:
1143    /// `(-5).bit_length() == 5.bit_length() == 3`. It is kept public for
1144    /// internal normalisation use (root/reciprocal seeds, shift amounts).
1145    ///
1146    /// At `MIN` the magnitude is `2^(BITS-1)`, so
1147    /// `MIN.bit_length() == BITS` (one more than `MAX.bit_length()`,
1148    /// which is `BITS - 1`).
1149    #[inline]
1150    pub const fn bit_length(&self) -> u32 {
1151        bit_len_fixed(self.abs().as_limbs())
1152    }
1153
1154    /// Leading zero bits of the two's-complement representation, matching
1155    /// the primitive `iN::leading_zeros` contract. A negative value has its
1156    /// sign bit (the MSB) set, so it has zero leading zeros; a non-negative
1157    /// value's leading-zero count is `BITS - bit_length` (`BITS` for zero).
1158    ///
1159    /// `MIN` is negative, so `MIN.leading_zeros() == 0`. `MAX` is the
1160    /// largest non-negative value (`0b0111…1`), so `MAX.leading_zeros()
1161    /// == 1`. Note this is the two's-complement count, NOT a magnitude
1162    /// count — contrast [`Self::bit_length`].
1163    #[inline]
1164    pub const fn leading_zeros(&self) -> u32 {
1165        if self.is_negative() {
1166            0
1167        } else {
1168            Self::BITS - self.bit_length()
1169        }
1170    }
1171
1172    // ── Int<N> / Uint<N> parity surface ─────────────────────────
1173    //
1174    // The methods below give the const-generic `Int<N>` the surface the
1175    // kernel-facing `BigInt` trait and the public `IntXXXX` type aliases
1176    // expect. Most delegate to the existing inherent methods or the
1177    // `Uint<N>` twin.
1178
1179    /// Integer constant `10`, used by decimal-scale `10^scale`
1180    /// rescaling.
1181    pub const TEN: Self = {
1182        let mut limbs = [0u64; N];
1183        if N > 0 {
1184            limbs[0] = 10;
1185        }
1186        Self { limbs }
1187    };
1188
1189    /// `|self|` as the unsigned twin. `MIN` maps to `2^(BITS-1)`.
1190    #[inline]
1191    pub const fn unsigned_abs(self) -> Uint<N> {
1192        Uint::from_limbs(*self.abs().as_limbs())
1193    }
1194
1195    /// Two's-complement negation. Alias of [`Self::wrapping_neg`].
1196    #[inline]
1197    pub fn negate(self) -> Self {
1198        self.wrapping_neg()
1199    }
1200
1201    /// Truncating quotient and remainder `(self / rhs, self % rhs)` in a
1202    /// single divmod call. The quotient truncates toward zero and the
1203    /// remainder takes the sign of the dividend. Routes through the
1204    /// const-`N` fast-arm (`div_rem_mag_fixed`): native `u64` idiv at
1205    /// `N == 1`, native `u128` divide at `N == 2`, and the dispatching
1206    /// divmod (Knuth / Burnikel–Ziegler) for wider `N`. Panics on a zero
1207    /// divisor.
1208    #[inline]
1209    pub fn div_rem(self, rhs: Self) -> (Self, Self) {
1210        assert!(!rhs.is_zero(), "attempt to divide by zero");
1211        let neg_q = self.is_negative() ^ rhs.is_negative();
1212        let neg_r = self.is_negative();
1213        let mut quot = [0u64; N];
1214        let mut rem = [0u64; N];
1215        div_rem_mag_fixed::<N>(
1216            self.unsigned_abs().as_limbs(),
1217            rhs.unsigned_abs().as_limbs(),
1218            &mut quot,
1219            &mut rem,
1220        );
1221        let q = Self::from_mag_limbs(&quot, neg_q);
1222        let r = Self::from_mag_limbs(&rem, neg_r);
1223        (q, r)
1224    }
1225
1226    /// Builds a signed value from a non-negative magnitude limb slice
1227    /// and a sign, truncating the magnitude into `N` limbs.
1228    #[inline]
1229    pub(crate) const fn from_mag_limbs(mag: &[u64], negative: bool) -> Self {
1230        let mut out = [0u64; N];
1231        let n = if mag.len() < N { mag.len() } else { N };
1232        let mut i = 0;
1233        while i < n {
1234            out[i] = mag[i];
1235            i += 1;
1236        }
1237        let v = Self { limbs: out };
1238        // Inherent `const` zero-check (avoids the non-const `BigInt`
1239        // trait method that name-resolution would otherwise pick here).
1240        if negative && !is_zero_fixed(&v.limbs) {
1241            v.wrapping_neg()
1242        } else {
1243            v
1244        }
1245    }
1246
1247    /// `true` if bit `idx` of the two's-complement representation is set.
1248    #[inline]
1249    pub const fn bit(self, idx: u32) -> bool {
1250        let limb = (idx / 64) as usize;
1251        if limb >= N {
1252            return self.is_negative();
1253        }
1254        (self.limbs[limb] >> (idx % 64)) & 1 == 1
1255    }
1256
1257    /// Builds from a signed 128-bit value. **Truncating** for `Int<1>`
1258    /// (the high 64 bits of `v` are discarded); use the checked `TryFrom`
1259    /// conversion when `v` may not fit.
1260    #[inline]
1261    pub(crate) const fn from_i128(v: i128) -> Self {
1262        // Narrow non-limb fast path (const-folds): for N<=2 write the
1263        // two's-complement limbs directly (truncating for N==1), skipping the
1264        // magnitude split + `wrapping_neg` re-sign. Bit-identical to the
1265        // magnitude path (two's-complement negation commutes with low-64
1266        // truncation).
1267        if N <= 2 {
1268            let mut limbs = [0u64; N];
1269            limbs[0] = v as u64;
1270            if N == 2 {
1271                limbs[1] = (v >> 64) as u64;
1272            }
1273            return Self { limbs };
1274        }
1275        let mag = v.unsigned_abs();
1276        Self::from_mag_limbs(&[mag as u64, (mag >> 64) as u64], v < 0)
1277    }
1278
1279    /// Builds from an unsigned 128-bit value.
1280    #[inline]
1281    pub(crate) const fn from_u128(v: u128) -> Self {
1282        Self::from_mag_limbs(&[v as u64, (v >> 64) as u64], false)
1283    }
1284
1285    /// Builds directly from the little-endian u64 limb array. Alias of
1286    /// [`Self::from_limbs`] under the historic `from_limbs_le` public name.
1287    #[inline]
1288    pub const fn from_limbs_le(limbs: [u64; N]) -> Self {
1289        Self { limbs }
1290    }
1291
1292    /// Returns the little-endian u64 limbs by value. Symmetric with
1293    /// [`Self::from_limbs_le`].
1294    #[inline]
1295    pub const fn limbs_le(self) -> [u64; N] {
1296        self.limbs
1297    }
1298
1299    /// `self · (n as Self)` with the sign of `self`, panicking on
1300    /// overflow (the default-form contract). Computes the n-by-1-word
1301    /// product (the same limb recurrence as `mul_schoolbook_into`) and
1302    /// rejects a non-zero top carry.
1303    #[inline]
1304    pub fn mul_u64(self, n: u64) -> Self {
1305        let mag = *self.unsigned_abs().as_limbs();
1306        let mut prod = [0u64; N];
1307        let mut carry: u64 = 0;
1308        let mut i = 0;
1309        while i < N {
1310            let p = (mag[i] as u128) * (n as u128) + (carry as u128);
1311            prod[i] = p as u64;
1312            carry = (p >> 64) as u64;
1313            i += 1;
1314        }
1315        if carry != 0 {
1316            panic!("Int: mul overflow");
1317        }
1318        let negative = self.is_negative();
1319        let r = Self::from_mag_limbs(&prod, negative);
1320        // `from_mag_limbs` only mishandles the `mag == 2^(BITS-1)` edge:
1321        // legal as MIN for `negative`, overflow otherwise.
1322        if !r.is_zero() && r.is_negative() != negative {
1323            panic!("Int: mul overflow");
1324        }
1325        r
1326    }
1327
1328    /// Exact `i128` value, or `None` if it does not fit.
1329    pub fn to_i128_checked(self) -> Option<i128> {
1330        let negative = self.is_negative();
1331        let mag = *self.unsigned_abs().as_limbs();
1332        // First two u64 limbs make up the low u128; the rest must be 0.
1333        let mut i = 2;
1334        while i < N {
1335            if mag[i] != 0 {
1336                return None;
1337            }
1338            i += 1;
1339        }
1340        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1341        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1342        let lo_u128 = lo | (hi << 64);
1343        if negative {
1344            if lo_u128 <= (i128::MAX as u128) + 1 {
1345                Some((lo_u128 as i128).wrapping_neg())
1346            } else {
1347                None
1348            }
1349        } else if lo_u128 <= i128::MAX as u128 {
1350            Some(lo_u128 as i128)
1351        } else {
1352            None
1353        }
1354    }
1355
1356    /// Exact `u128` value, or `None` if negative / too large.
1357    pub fn to_u128_checked(self) -> Option<u128> {
1358        if self.is_negative() {
1359            return None;
1360        }
1361        let mut i = 2;
1362        while i < N {
1363            if self.limbs[i] != 0 {
1364                return None;
1365            }
1366            i += 1;
1367        }
1368        let lo = if N > 0 { self.limbs[0] as u128 } else { 0 };
1369        let hi = if N > 1 { self.limbs[1] as u128 } else { 0 };
1370        Some(lo | (hi << 64))
1371    }
1372
1373    /// Approximate `f64` value of `self` (lossy above 53 significant
1374    /// bits).
1375    pub fn to_f64(self) -> f64 {
1376        let mag = *self.unsigned_abs().as_limbs();
1377        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
1378        let mut acc = 0.0f64;
1379        let mut i = N;
1380        while i > 0 {
1381            i -= 1;
1382            acc = acc * radix + mag[i] as f64;
1383        }
1384        if self.is_negative() { -acc } else { acc }
1385    }
1386
1387    /// Approximate `f32` value of `self` (round-to-nearest; lossy above
1388    /// 24 significant bits). Routes through the `f64` accumulation to keep
1389    /// one summation path.
1390    pub fn to_f32(self) -> f32 {
1391        self.to_f64() as f32
1392    }
1393
1394    /// Exact conversion from an `f64`, or `None` when `v` is NaN, ±inf,
1395    /// has a fractional part, or lies outside the `Int<N>` range.
1396    ///
1397    /// `const`: classification and decomposition go through the const
1398    /// `f64::to_bits` plus integer/bit ops only — never the non-const
1399    /// `is_finite` / `fract` / float-`as` paths.
1400    pub(crate) const fn try_from_f64(v: f64) -> Option<Self> {
1401        let bits = v.to_bits();
1402        let negative = (bits >> 63) & 1 == 1;
1403        let exp = ((bits >> 52) & 0x7ff) as i32;
1404        let mant = bits & 0x000f_ffff_ffff_ffff;
1405        if exp == 0x7ff {
1406            // NaN / ±inf.
1407            return None;
1408        }
1409        if exp == 0 {
1410            // ±0 is exact zero; a subnormal is a non-zero |v| < 1, i.e.
1411            // never an integer.
1412            return if mant == 0 { Some(Self::ZERO) } else { None };
1413        }
1414        let significand = (1u64 << 52) | mant; // 53-bit normalized value
1415        let shift = exp - 1075; // (exp - 1023) - 52
1416        Self::from_significand_shift(significand, shift, negative)
1417    }
1418
1419    /// Exact conversion from an `f32`, or `None` on NaN, ±inf, a
1420    /// fractional part, or out-of-range. Decomposes via the const
1421    /// `f32::to_bits` (8-bit exponent, 23-bit mantissa, bias 127, 24-bit
1422    /// significand) with integer/bit ops only — `const` for the same
1423    /// reason as [`Self::try_from_f64`].
1424    pub(crate) const fn try_from_f32(v: f32) -> Option<Self> {
1425        let bits = v.to_bits();
1426        let negative = (bits >> 31) & 1 == 1;
1427        let exp = ((bits >> 23) & 0xff) as i32;
1428        let mant = (bits & 0x007f_ffff) as u64;
1429        if exp == 0xff {
1430            // NaN / ±inf.
1431            return None;
1432        }
1433        if exp == 0 {
1434            return if mant == 0 { Some(Self::ZERO) } else { None };
1435        }
1436        let significand = (1u64 << 23) | mant; // 24-bit normalized value
1437        let shift = exp - 150; // (exp - 127) - 23
1438        Self::from_significand_shift(significand, shift, negative)
1439    }
1440
1441    /// Builds `Int<N>` from `(-1)^sign * significand * 2^shift`, where
1442    /// `significand` fits in 64 bits. Returns `None` when the value has a
1443    /// fractional part (`shift < 0` with low bits set) or does not fit the
1444    /// signed `Int<N>` range. Pure const integer/bit arithmetic — no float
1445    /// ops — so both float entry points are `const`.
1446    const fn from_significand_shift(significand: u64, shift: i32, negative: bool) -> Option<Self> {
1447        if shift < 0 {
1448            let s = (-shift) as u32;
1449            if s >= 64 {
1450                // Whole value is fractional (|v| < 1, non-zero) — not an
1451                // integer.
1452                return None;
1453            }
1454            if significand & ((1u64 << s) - 1) != 0 {
1455                // Fractional bits set — not an integer.
1456                return None;
1457            }
1458            let mag = significand >> s; // integral, fits a single limb
1459            let built = Self::from_mag_limbs(&[mag], negative);
1460            Self::accept_signed(built, negative)
1461        } else {
1462            // Place `significand` left-shifted by `shift` into an N-limb
1463            // magnitude, rejecting any bit that lands beyond limb N-1.
1464            let s = shift as u32;
1465            let limb_off = (s / 64) as usize;
1466            let bit_off = s % 64;
1467            let mut mag = [0u64; N];
1468            // Low chunk into limb `limb_off`; carry into the next limb.
1469            let lo = significand << bit_off;
1470            // The part of `significand` pushed past this limb's top.
1471            let hi = if bit_off == 0 {
1472                0
1473            } else {
1474                significand >> (64 - bit_off)
1475            };
1476            if limb_off < N {
1477                mag[limb_off] = lo;
1478            } else if lo != 0 {
1479                return None;
1480            }
1481            if hi != 0 {
1482                if limb_off + 1 < N {
1483                    mag[limb_off + 1] = hi;
1484                } else {
1485                    return None;
1486                }
1487            }
1488            let built = Self::from_mag_limbs(&mag, negative);
1489            Self::accept_signed(built, negative)
1490        }
1491    }
1492
1493    /// Sign-overflow gate shared by the float builders: a zero is always
1494    /// fine; otherwise the built sign must match the requested one, which
1495    /// rejects a positive magnitude that wrapped into the sign bit while
1496    /// still admitting the exact `MIN` edge (magnitude `2^(bits-1)` with
1497    /// the sign set, which `wrapping_neg` reproduces as itself).
1498    #[inline]
1499    const fn accept_signed(built: Self, negative: bool) -> Option<Self> {
1500        // Inherent const zero-check (the `BigInt::is_zero` trait method
1501        // name-resolution would otherwise pick is not const).
1502        if is_zero_fixed(&built.limbs) {
1503            Some(built)
1504        } else if built.is_negative() != negative {
1505            None
1506        } else {
1507            Some(built)
1508        }
1509    }
1510
1511    /// Builds from an `f64`, truncating toward zero. Saturates to
1512    /// `MIN` / `MAX` on out-of-range; non-finite maps to `ZERO`.
1513    pub fn from_f64(v: f64) -> Self {
1514        if !v.is_finite() {
1515            return Self::ZERO;
1516        }
1517        let negative = v < 0.0;
1518        let mut m = if negative { -v } else { v };
1519        let radix: f64 = 18_446_744_073_709_551_616.0; // 2^64
1520        let mut limbs = [0u64; N];
1521        let mut i = 0;
1522        while m >= 1.0 && i < N {
1523            let rem = m % radix;
1524            limbs[i] = rem as u64;
1525            m = (m - rem) / radix;
1526            i += 1;
1527        }
1528        if m >= 1.0 {
1529            return if negative {
1530                Self::min_value()
1531            } else {
1532                Self::max_value()
1533            };
1534        }
1535        Self::from_mag_limbs(&limbs, negative)
1536    }
1537
1538    /// Parses a signed decimal magnitude from `s`. Accepts an optional
1539    /// leading `-`, then ASCII digits. Only `radix == 10` is supported;
1540    /// any other value returns `Err(())`.
1541    pub const fn from_str_radix(s: &str, radix: u32) -> Result<Self, ()> {
1542        if radix != 10 {
1543            return Err(());
1544        }
1545        let bytes = s.as_bytes();
1546        let (negative, start): (bool, usize) = if !bytes.is_empty() && bytes[0] == b'-' {
1547            (true, 1)
1548        } else {
1549            (false, 0)
1550        };
1551        if start >= bytes.len() {
1552            return Err(());
1553        }
1554        // acc = acc * 10 + d per digit, truncating into N limbs — the
1555        // same Horner recurrence the macro runs through `mul_schoolbook`
1556        // + `add_assign`, but the low-N-limb multiply-by-10 is
1557        // folded into one n-by-1-word pass (no `2*N` staging buffer).
1558        let mut acc = [0u64; N];
1559        let mut k = start;
1560        while k < bytes.len() {
1561            let ch = bytes[k];
1562            if ch < b'0' || ch > b'9' {
1563                return Err(());
1564            }
1565            let d = (ch - b'0') as u64;
1566            let mut carry: u64 = d;
1567            let mut j = 0;
1568            while j < N {
1569                let p = (acc[j] as u128) * 10u128 + (carry as u128);
1570                acc[j] = p as u64;
1571                carry = (p >> 64) as u64;
1572                j += 1;
1573            }
1574            k += 1;
1575        }
1576        Ok(Self::from_mag_limbs(&acc, negative))
1577    }
1578
1579    // ── Named-type API parity (the `IntXXXX` alias surface) ─────
1580    //
1581    // The methods below complete the inherent surface the `IntXXXX = Int<N>`
1582    // type aliases expose, so every named-type call site keeps resolving.
1583
1584    /// Integer power: `self^exp` (wrapping on overflow). Alias of
1585    /// [`Self::wrapping_pow`] under the `pow` name.
1586    #[inline]
1587    pub const fn pow(self, exp: u32) -> Self {
1588        self.wrapping_pow(exp)
1589    }
1590
1591    /// Integer square root of the magnitude (`floor(sqrt(|self|))`),
1592    /// returned non-negative. Delegates to the unsigned sibling which
1593    /// routes through `isqrt_dispatch`.
1594    #[inline]
1595    pub fn isqrt(self) -> Self {
1596        Self::from_limbs(*self.unsigned_abs().isqrt().as_limbs())
1597    }
1598
1599    /// Integer square: `self²` modulo `2^BITS`. Routes through the sqr
1600    /// policy (`sqr_dispatch`) on the unsigned reinterpretation, then
1601    /// reinterprets back as signed. Equivalent to `wrapping_sqr`.
1602    #[inline]
1603    pub fn sqr(self) -> Self {
1604        self.wrapping_sqr()
1605    }
1606
1607    /// Integer cube: `self³` modulo `2^BITS`. Routes through the cube
1608    /// policy (`cube_dispatch`) on the unsigned reinterpretation, then
1609    /// reinterprets back as signed. Equivalent to `wrapping_cube`.
1610    #[inline]
1611    pub fn cube(self) -> Self {
1612        self.wrapping_cube()
1613    }
1614
1615    /// Integer cube root of the magnitude (`floor(cbrt(|self|))`),
1616    /// returned non-negative. Delegates to the unsigned sibling which
1617    /// routes through `icbrt_dispatch`.
1618    #[inline]
1619    pub fn icbrt(self) -> Self {
1620        Self::from_limbs(*self.unsigned_abs().icbrt().as_limbs())
1621    }
1622
1623    /// Integer hypotenuse: `round(sqrt(self^2 + other^2))` without
1624    /// intermediate overflow of the radicand. Routes through the hypot
1625    /// policy (`hypot_dispatch`): the radicand `self^2 + other^2` is formed
1626    /// in a wider limb scratch buffer and the floor root is taken via the
1627    /// integer slice `isqrt`, then a single round step under `mode` lands
1628    /// the result. Returns [`None`] when the rounded result does not fit
1629    /// the signed range of `Int<N>` (true overflow). The sign of each
1630    /// operand drops out of the squaring; `hypot(0, 0) = 0` and
1631    /// `hypot(0, x) = |x|`.
1632    #[inline]
1633    #[must_use]
1634    pub(crate) fn hypot(
1635        self,
1636        other: Self,
1637        mode: crate::support::rounding::RoundingMode,
1638    ) -> Option<Self>
1639    where
1640        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1641    {
1642        hypot_dispatch::<N>(self, other, mode)
1643    }
1644
1645    /// Sum of squares: `self^2 + other^2`, the sqrt-free magnitude
1646    /// primitive. Routes through the sum-of-squares policy
1647    /// (`sum_sq_dispatch`): the radicand is formed in a wider limb scratch
1648    /// buffer (the same former [`Self::hypot`] roots), so there is no
1649    /// intermediate truncation. Returns [`None`] when `self^2 + other^2`
1650    /// exceeds the signed range of `Int<N>` (true overflow). The sign of
1651    /// each operand drops out of the squaring, so the result is always
1652    /// non-negative. Comparing two `sum_sq` values orders the same as
1653    /// comparing the corresponding [`Self::hypot`] values (the root is
1654    /// monotonic), which is why a distance comparison can skip the root.
1655    #[inline]
1656    #[must_use]
1657    pub(crate) fn sum_sq(self, other: Self) -> Option<Self>
1658    where
1659        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1660    {
1661        sum_sq_dispatch::<N>(self, other)
1662    }
1663
1664    /// Reinterprets the bit pattern as the unsigned sibling.
1665    #[inline]
1666    pub const fn cast_unsigned(self) -> Uint<N> {
1667        Uint::from_limbs(self.limbs)
1668    }
1669
1670    /// Approximate `f64` value. Alias of [`Self::to_f64`], matching the
1671    /// macro's `as_f64` name.
1672    #[inline]
1673    pub fn as_f64(self) -> f64 {
1674        self.to_f64()
1675    }
1676
1677    /// Count of set bits across the two's-complement representation,
1678    /// matching the primitive `iN::count_ones` contract — the limbs are
1679    /// read as stored, so negative values count their sign-extended
1680    /// one-bits. `(-1).count_ones() == BITS` (all-ones), and
1681    /// `MIN.count_ones() == 1` (only the sign bit). This is a
1682    /// two's-complement count, not a magnitude count.
1683    #[inline]
1684    pub const fn count_ones(self) -> u32 {
1685        let mut total = 0;
1686        let mut i = 0;
1687        while i < N {
1688            total += self.limbs[i].count_ones();
1689            i += 1;
1690        }
1691        total
1692    }
1693
1694    /// Count of clear bits across the two's-complement representation,
1695    /// matching the primitive `iN::count_zeros` contract
1696    /// (`BITS - count_ones`). `(-1).count_zeros() == 0` and
1697    /// `MIN.count_zeros() == BITS - 1`.
1698    #[inline]
1699    pub const fn count_zeros(self) -> u32 {
1700        Self::BITS - self.count_ones()
1701    }
1702
1703    /// Number of trailing zero bits of the two's-complement
1704    /// representation, matching the primitive `iN::trailing_zeros`
1705    /// contract; `BITS` for zero. `MIN` has only its sign bit set, so
1706    /// `MIN.trailing_zeros() == BITS - 1`. This reads the stored limbs,
1707    /// not the magnitude — contrast [`Self::bit_length`].
1708    #[inline]
1709    pub const fn trailing_zeros(self) -> u32 {
1710        let mut i = 0;
1711        while i < N {
1712            if self.limbs[i] != 0 {
1713                return i as u32 * 64 + self.limbs[i].trailing_zeros();
1714            }
1715            i += 1;
1716        }
1717        Self::BITS
1718    }
1719
1720    /// Checked negation: `None` exactly at `MIN` (whose negation
1721    /// overflows the signed range).
1722    #[inline]
1723    pub const fn checked_neg(self) -> Option<Self> {
1724        if eq_dispatch(self, Self::MIN) {
1725            None
1726        } else {
1727            Some(self.wrapping_neg())
1728        }
1729    }
1730
1731    /// Checked division: `None` on a zero divisor.
1732    #[inline]
1733    pub const fn checked_div(self, rhs: Self) -> Option<Self> {
1734        if is_zero_fixed(&rhs.limbs) {
1735            None
1736        } else {
1737            Some(self.wrapping_div(rhs))
1738        }
1739    }
1740
1741    /// Checked remainder: `None` on a zero divisor, and `None` for the
1742    /// `MIN % -1` overflow case (the paired division `MIN / -1` overflows
1743    /// the signed range), matching the primitive integer contract.
1744    #[inline]
1745    pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
1746        if is_zero_fixed(&rhs.limbs) || self.is_min_neg_one(rhs) {
1747            // Zero divisor, or the `MIN % -1` overflow case (its paired
1748            // `MIN / -1` division overflows the signed range).
1749            None
1750        } else {
1751            Some(self.wrapping_rem(rhs))
1752        }
1753    }
1754
1755    /// `true` when `self == MIN` and `rhs == -1` — the remainder/division
1756    /// overflow case where `MIN / -1` exceeds the signed range.
1757    #[inline]
1758    const fn is_min_neg_one(self, rhs: Self) -> bool {
1759        cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0
1760            && cmp_fixed(&rhs.wrapping_neg().limbs, &Self::ONE.limbs) == 0
1761    }
1762
1763    /// Euclidean division: the quotient that leaves a non-negative
1764    /// remainder.
1765    #[inline]
1766    pub const fn div_euclid(self, rhs: Self) -> Self {
1767        let q = self.wrapping_div(rhs);
1768        let r = self.wrapping_rem(rhs);
1769        if r.is_negative() {
1770            if rhs.is_negative() {
1771                q.wrapping_add(Self::ONE)
1772            } else {
1773                q.wrapping_sub(Self::ONE)
1774            }
1775        } else {
1776            q
1777        }
1778    }
1779
1780    /// Euclidean remainder — always non-negative.
1781    #[inline]
1782    pub const fn rem_euclid(self, rhs: Self) -> Self {
1783        let r = self.wrapping_rem(rhs);
1784        if r.is_negative() {
1785            r.wrapping_add(rhs.abs())
1786        } else {
1787            r
1788        }
1789    }
1790
1791    /// Wrapping addition paired with the two's-complement overflow flag.
1792    #[inline]
1793    pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
1794        let r = self.wrapping_add(rhs);
1795        let sa = self.is_negative();
1796        let sb = rhs.is_negative();
1797        let sr = r.is_negative();
1798        (r, sa == sb && sr != sa)
1799    }
1800
1801    /// Wrapping subtraction paired with the two's-complement overflow
1802    /// flag.
1803    #[inline]
1804    pub const fn overflowing_sub(self, rhs: Self) -> (Self, bool) {
1805        let r = self.wrapping_sub(rhs);
1806        let sa = self.is_negative();
1807        let sb = rhs.is_negative();
1808        let sr = r.is_negative();
1809        (r, sa != sb && sr != sa)
1810    }
1811
1812    /// Wrapping negation paired with the overflow flag (`true` only at
1813    /// `MIN`).
1814    #[inline]
1815    pub const fn overflowing_neg(self) -> (Self, bool) {
1816        let ov = cmp_fixed(&self.limbs, &Self::MIN.limbs) == 0;
1817        (self.wrapping_neg(), ov)
1818    }
1819
1820    /// Wrapping remainder paired with an overflow flag. The flag is `true`
1821    /// only for `MIN % -1` (whose paired division overflows the signed
1822    /// range), in which case the remainder is `0`; otherwise `false`.
1823    #[inline]
1824    pub const fn overflowing_rem(self, rhs: Self) -> (Self, bool) {
1825        if self.is_min_neg_one(rhs) {
1826            (Self::ZERO, true)
1827        } else {
1828            (self.wrapping_rem(rhs), false)
1829        }
1830    }
1831
1832    /// Saturating addition: clamps to `MIN` / `MAX` on overflow.
1833    #[inline]
1834    pub const fn saturating_add(self, rhs: Self) -> Self {
1835        match self.checked_add(rhs) {
1836            Some(v) => v,
1837            None => {
1838                if self.is_negative() {
1839                    Self::MIN
1840                } else {
1841                    Self::MAX
1842                }
1843            }
1844        }
1845    }
1846
1847    /// Saturating subtraction: clamps to `MIN` / `MAX` on overflow.
1848    #[inline]
1849    pub const fn saturating_sub(self, rhs: Self) -> Self {
1850        match self.checked_sub(rhs) {
1851            Some(v) => v,
1852            None => {
1853                if self.is_negative() {
1854                    Self::MIN
1855                } else {
1856                    Self::MAX
1857                }
1858            }
1859        }
1860    }
1861
1862    /// Saturating negation: `MIN` saturates to `MAX`.
1863    #[inline]
1864    pub const fn saturating_neg(self) -> Self {
1865        match self.checked_neg() {
1866            Some(v) => v,
1867            None => Self::MAX,
1868        }
1869    }
1870
1871    /// Rotates the bits left by `n` (modulo `BITS`).
1872    #[inline]
1873    pub fn rotate_left(self, n: u32) -> Self {
1874        let bits = Self::BITS;
1875        let n = n % bits;
1876        if n == 0 {
1877            return self;
1878        }
1879        let u = self.cast_unsigned();
1880        Self::from_limbs(((u.shl(n)) | (u.shr(bits - n))).limbs)
1881    }
1882
1883    /// Rotates the bits right by `n` (modulo `BITS`).
1884    #[inline]
1885    pub fn rotate_right(self, n: u32) -> Self {
1886        self.rotate_left(Self::BITS - (n % Self::BITS))
1887    }
1888
1889    /// Truncating cast to `u128` (low 128 magnitude bits, sign ignored).
1890    /// **Truncating** — discards any higher limbs and the sign; use
1891    /// [`Self::to_u128_checked`] (or `TryFrom`) when the value may not
1892    /// fit or may be negative.
1893    #[inline]
1894    pub(crate) const fn as_u128(self) -> u128 {
1895        let mag = *self.unsigned_abs().as_limbs();
1896        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1897        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1898        lo | (hi << 64)
1899    }
1900
1901    /// Truncating cast to `i128` (low 128 bits, sign-applied).
1902    /// **Truncating** — for `Int<3+>` any value outside the `i128` range
1903    /// loses its high limbs; use [`Self::to_i128_checked`] (or `TryFrom`)
1904    /// when the value may not fit.
1905    #[inline]
1906    pub(crate) const fn as_i128(self) -> i128 {
1907        // Narrow non-limb fast path (const-folds per monomorphisation): for
1908        // N<=2 the limbs ARE the i64/i128 two's-complement value, so skip the
1909        // `unsigned_abs`/`wrapping_neg` sign-magnitude round trip (which costs
1910        // two `neg_dispatch` calls on a negative operand). Always fits i128
1911        // for N<=2; bit-identical to the magnitude path below.
1912        if N <= 2 {
1913            if N == 1 {
1914                return (self.limbs[0] as i64) as i128;
1915            }
1916            let lo = self.limbs[0] as u128;
1917            let hi = self.limbs[1] as u128;
1918            return (lo | (hi << 64)) as i128;
1919        }
1920        let mag = *self.unsigned_abs().as_limbs();
1921        let lo = if N > 0 { mag[0] as u128 } else { 0 };
1922        let hi = if N > 1 { mag[1] as u128 } else { 0 };
1923        let combined = lo | (hi << 64);
1924        if self.is_negative() {
1925            (combined as i128).wrapping_neg()
1926        } else {
1927            combined as i128
1928        }
1929    }
1930
1931    /// Widening / narrowing cast to any other [`BigInt`] type, via the
1932    /// shared magnitude + sign bridge. Matches the macro's
1933    /// `resize::<T>()` signature so the decimal-tier code that calls
1934    /// `storage.resize::<$Wider>()` resolves against `Int<N>`.
1935    #[inline]
1936    pub(crate) fn resize<T: crate::int::types::traits::BigInt>(self) -> T {
1937        use crate::int::types::traits::BigInt as _;
1938        self.resize_to::<T>()
1939    }
1940
1941    /// Truncating division toward zero. Panics on a zero divisor.
1942    /// Matches the macro's `wrapping_div` (single-limb-aware
1943    /// `div_rem`, not the dispatching `div_rem`).
1944    #[inline]
1945    pub const fn wrapping_div(self, rhs: Self) -> Self {
1946        if is_zero_fixed(&rhs.limbs) {
1947            panic!("attempt to divide by zero");
1948        }
1949        let negative = self.is_negative() ^ rhs.is_negative();
1950        let mut q = [0u64; N];
1951        let mut r = [0u64; N];
1952        div_rem(
1953            self.unsigned_abs().as_limbs(),
1954            rhs.unsigned_abs().as_limbs(),
1955            &mut q,
1956            &mut r,
1957        );
1958        Self::from_mag_limbs(&q, negative)
1959    }
1960
1961    /// Truncating remainder; result carries the sign of `self`. Panics
1962    /// on a zero divisor. Matches the macro's `wrapping_rem`.
1963    #[inline]
1964    pub const fn wrapping_rem(self, rhs: Self) -> Self {
1965        if is_zero_fixed(&rhs.limbs) {
1966            panic!("attempt to calculate the remainder with a divisor of zero");
1967        }
1968        let mut q = [0u64; N];
1969        let mut r = [0u64; N];
1970        div_rem(
1971            self.unsigned_abs().as_limbs(),
1972            rhs.unsigned_abs().as_limbs(),
1973            &mut q,
1974            &mut r,
1975        );
1976        Self::from_mag_limbs(&r, self.is_negative())
1977    }
1978
1979    /// Full `self · rhs` product widened into a `W: BigInt`, in one
1980    /// step (no double trip through the magnitude staging buffer). Used
1981    /// by the wide-tier `Mul` operator to compute
1982    /// `Storage * Storage → Wider`. Matches the macro's `widen_mul`.
1983    #[inline]
1984    pub(crate) fn widen_mul<W>(self, rhs: Self) -> W
1985    where
1986        W: crate::int::types::traits::BigInt,
1987        W::Scratch: crate::int::types::compute_limbs::ComputeLimbs,
1988        crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
1989    {
1990        use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
1991        let negative = self.is_negative() ^ rhs.is_negative();
1992        let a = *self.unsigned_abs().as_limbs();
1993        let b = *rhs.unsigned_abs().as_limbs();
1994        // Full product spans 2·N u64 limbs — sized exactly by the source's
1995        // `ComputeLimbs::double_buffered_u64()` (no build-max blanket). Route through
1996        // the equal-length multiply dispatcher: both operands are `[u64; N]`,
1997        // so this is the single site every wide tier's full product flows
1998        // through. The dispatcher base-cases to schoolbook below
1999        // `KARATSUBA_THRESHOLD_U64` (every shipped tier) and engages the
2000        // non-allocating Karatsuba kernel at or above it.
2001        let mut prod_buf = <Limbs<N> as ComputeLimbs>::double_buffered_u64();
2002        let prod = prod_buf.as_mut();
2003        mul_fast::<N>(&a, &b, &mut prod[..2 * N]);
2004        // Pack the `2·N`-u64 product into `N` u128 limbs for the kept
2005        // `BigInt::from_mag_sign_u128` bridge. The result `W` holds the
2006        // product, so its scratch carrier's `single_u128()` buffer (`≥ N`)
2007        // sizes the packed magnitude exactly.
2008        let mut u128_buf = <W::Scratch as ComputeLimbs>::single_u128();
2009        let u128_prod = u128_buf.as_mut();
2010        let mut i = 0;
2011        while i < N {
2012            u128_prod[i] = (prod[2 * i] as u128) | ((prod[2 * i + 1] as u128) << 64);
2013            i += 1;
2014        }
2015        W::from_mag_sign_u128(&u128_prod[..N], negative)
2016    }
2017}
2018
2019impl<const N: usize> Int<N> {
2020    /// Const cross-width signed comparison `Int<N>` vs `Int<M>`, returning
2021    /// `core::cmp::Ordering`. No widening copy is made: the sign is
2022    /// compared first (a negative value is always less than a non-negative
2023    /// one); when the signs agree the magnitudes are compared
2024    /// length-aware via [`cmp_cross`] over the `unsigned_abs`
2025    /// limbs (the longer magnitude's surplus high limbs must be zero for
2026    /// equality, else it is the larger). For two negatives the larger
2027    /// magnitude is the smaller value, so the magnitude order is flipped.
2028    #[inline]
2029    pub(crate) const fn cmp_cross<const M: usize>(self, other: Int<M>) -> Ordering {
2030        let sn = self.is_negative();
2031        let so = other.is_negative();
2032        if sn && !so {
2033            return Ordering::Less;
2034        }
2035        if !sn && so {
2036            return Ordering::Greater;
2037        }
2038        // Same sign: compare magnitudes length-aware.
2039        let a = self.unsigned_abs();
2040        let b = other.unsigned_abs();
2041        let c = cmp_cross(a.as_limbs(), b.as_limbs());
2042        // For two negatives the larger magnitude is the smaller value.
2043        let c = if sn { -c } else { c };
2044        if c < 0 {
2045            Ordering::Less
2046        } else if c > 0 {
2047            Ordering::Greater
2048        } else {
2049            Ordering::Equal
2050        }
2051    }
2052}
2053
2054impl<const N: usize> Int<N> {
2055    /// Const cross-width, cross-*scale* signed comparison: compares
2056    /// `self` against `other · 10^scale_diff`, returning
2057    /// [`core::cmp::Ordering`]. Used by the decimal layer to compare two
2058    /// `D<Int<_>, S>` values at *different* `SCALE`s without materialising
2059    /// a widened product (no `generic_const_exprs`, no computed type).
2060    ///
2061    /// # Approach (scale-down-with-remainder, overflow-free)
2062    ///
2063    /// Rather than scale `other` *up* by `10^scale_diff` (which can
2064    /// overflow `M`'s width), this scales `self` *down* by the same
2065    /// factor — division can never overflow. With magnitudes
2066    /// `|self| = q · 10^scale_diff + r` (`0 ≤ r < 10^scale_diff`):
2067    ///
2068    /// * compare the quotient `q` against `|other|`;
2069    /// * on a magnitude tie, a nonzero remainder `r` means `|self|` is the
2070    ///   larger magnitude (it carries extra low digits `other` lacks).
2071    ///
2072    /// Signs are resolved first (a negative is always less than a
2073    /// non-negative); for two negatives the larger magnitude is the
2074    /// smaller value, so the magnitude order is flipped. `scale_diff == 0`
2075    /// degenerates to a plain cross-width magnitude compare.
2076    ///
2077    /// `const`, `core`-only, no allocation: the `10^scale_diff` divisor and
2078    /// the quotient/remainder are built in fixed staging buffers (the same
2079    /// `max_n_limbs(4)` width the wide tiers stage products through — `288`
2080    /// limbs at xx-wide, scaling down with `MAX_WORK_N`), and the division
2081    /// reuses [`div_rem`].
2082    pub(crate) const fn cmp_cross_scaled<const M: usize>(
2083        self,
2084        other: Int<M>,
2085        scale_diff: u32,
2086    ) -> Ordering {
2087        let sn = self.is_negative();
2088        let so = other.is_negative();
2089        if sn && !so {
2090            return Ordering::Less;
2091        }
2092        if !sn && so {
2093            return Ordering::Greater;
2094        }
2095
2096        // Same sign (or one/both zero): compare magnitudes. `mag_cmp` is
2097        // the i32 sign of `|self|` − `|other| · 10^scale_diff`.
2098        let a = self.unsigned_abs();
2099        let b = other.unsigned_abs();
2100
2101        let mag_cmp = if scale_diff == 0 {
2102            cmp_cross(a.as_limbs(), b.as_limbs())
2103        } else {
2104            // Build the divisor 10^scale_diff in a fixed staging buffer.
2105            // `max_n_limbs(4)` (= 288 at xx-wide) is the wide-tier staging
2106            // width, tracking MAX_WORK_N so a narrower build does not carry
2107            // the widest tier's buffer; it covers every shipped width/scale.
2108            let mut pow = [0u64; max_n_limbs(4)];
2109            pow[0] = 1;
2110            let mut e = 0;
2111            while e < scale_diff {
2112                // pow *= 10, propagating carry across limbs.
2113                let mut carry: u128 = 0;
2114                let mut i = 0;
2115                while i < pow.len() {
2116                    let prod = (pow[i] as u128) * 10u128 + carry;
2117                    pow[i] = prod as u64;
2118                    carry = prod >> 64;
2119                    i += 1;
2120                }
2121                e += 1;
2122            }
2123
2124            // |self| = q · 10^scale_diff + r.
2125            let mut q = [0u64; max_n_limbs(4)];
2126            let mut r = [0u64; max_n_limbs(4)];
2127            div_rem(a.as_limbs(), &pow, &mut q, &mut r);
2128
2129            // Compare quotient against |other|; tie-break on remainder.
2130            let c = cmp_cross(&q, b.as_limbs());
2131            if c != 0 {
2132                c
2133            } else {
2134                // Quotients equal: a nonzero remainder makes |self| larger.
2135                let mut rk = 0;
2136                let mut nz = false;
2137                while rk < r.len() {
2138                    if r[rk] != 0 {
2139                        nz = true;
2140                        break;
2141                    }
2142                    rk += 1;
2143                }
2144                if nz {
2145                    1
2146                } else {
2147                    0
2148                }
2149            }
2150        };
2151
2152        // For two negatives, the larger magnitude is the smaller value.
2153        let c = if sn { -mag_cmp } else { mag_cmp };
2154        if c < 0 {
2155            Ordering::Less
2156        } else if c > 0 {
2157            Ordering::Greater
2158        } else {
2159            Ordering::Equal
2160        }
2161    }
2162}
2163
2164impl<const N: usize> Int<N> {
2165    /// Const EXACT comparison of the decimal value `self / 10^scale`
2166    /// against the exact dyadic value of an `f64`, returning
2167    /// [`core::cmp::Ordering`]. This is *value* equality, NOT a lossy
2168    /// round-trip: an `f64` has an exact rational value `m · 2^e`, and
2169    /// this compares `self / 10^scale` against it by cross-multiplying to
2170    /// integers (overflow-free in fixed staging buffers, riding
2171    /// [`cmp_cross`]).
2172    ///
2173    /// `value` MUST be finite — the caller rejects `NaN` / `±inf` before
2174    /// calling (those compare unequal to every decimal).
2175    ///
2176    /// # Approach
2177    ///
2178    /// Decompose `value = (-1)^sign · m · 2^e` from its IEEE-754 bits
2179    /// (`m` the 53-bit-or-subnormal integer mantissa, `e` the unbiased
2180    /// power-of-two exponent). The magnitudes satisfy
2181    /// `|self| / 10^scale  ==  m · 2^e` iff:
2182    ///
2183    /// * `e ≥ 0`:  `|self|          ==  m · 2^e · 10^scale`
2184    /// * `e < 0`:  `|self| · 2^(−e)  ==  m · 10^scale`
2185    ///
2186    /// both sides being non-negative integers. Signs are resolved first
2187    /// (a negative is always less than a non-negative; for two negatives
2188    /// the magnitude order is flipped). `m == 0` is the float zero.
2189    pub(crate) const fn cmp_f64_exact(self, scale: u32, value: f64) -> Ordering {
2190        let bits = value.to_bits();
2191        let fsign = (bits >> 63) != 0;
2192        let exp_field = ((bits >> 52) & 0x7ff) as i32;
2193        let frac = bits & 0x000f_ffff_ffff_ffff;
2194        // Mantissa `m` and unbiased power-of-two exponent `e`.
2195        let (m, e): (u64, i32) = if exp_field == 0 {
2196            // Subnormal (or zero): no implicit leading bit.
2197            (frac, -1074)
2198        } else {
2199            (frac | 0x0010_0000_0000_0000, exp_field - 1075)
2200        };
2201
2202        let sn = self.is_negative();
2203        let fzero = m == 0;
2204        // Resolve signs. The float's zero has no sign for ordering.
2205        if fzero {
2206            // value == 0: compare against self's sign / zeroness.
2207            let self_limbs = self.as_limbs();
2208            let mut nz = false;
2209            let mut k = 0;
2210            while k < self_limbs.len() {
2211                if self_limbs[k] != 0 {
2212                    nz = true;
2213                    break;
2214                }
2215                k += 1;
2216            }
2217            if !nz {
2218                return Ordering::Equal;
2219            }
2220            return if sn { Ordering::Less } else { Ordering::Greater };
2221        }
2222        if sn && !fsign {
2223            return Ordering::Less;
2224        }
2225        if !sn && fsign {
2226            return Ordering::Greater;
2227        }
2228
2229        // Same sign, both nonzero: compare magnitudes.
2230        // Build the two non-negative integer sides in fixed staging
2231        // buffers (288 u64 limbs covers every shipped width / scale /
2232        // f64 exponent: D307 magnitude ≤ 16 limbs, 10^scale ≤ 16 limbs,
2233        // 2^1074 ≤ 17 limbs — products stay well under 288).
2234        let a = self.unsigned_abs();
2235        let a_limbs = a.as_limbs();
2236
2237        // 10^scale in a buffer.
2238        let mut pow10 = [0u64; 288];
2239        pow10[0] = 1;
2240        let mut pe = 0;
2241        while pe < scale {
2242            let mut carry: u128 = 0;
2243            let mut i = 0;
2244            while i < pow10.len() {
2245                let prod = (pow10[i] as u128) * 10u128 + carry;
2246                pow10[i] = prod as u64;
2247                carry = prod >> 64;
2248                i += 1;
2249            }
2250            pe += 1;
2251        }
2252
2253        let m_limbs = [m];
2254
2255        let mut lhs = [0u64; 288];
2256        let mut rhs = [0u64; 288];
2257
2258        if e >= 0 {
2259            // lhs = |self|; rhs = m · 2^e · 10^scale.
2260            let mut i = 0;
2261            while i < a_limbs.len() {
2262                lhs[i] = a_limbs[i];
2263                i += 1;
2264            }
2265            // tmp = m · 10^scale
2266            let mut tmp = [0u64; 288];
2267            mul_schoolbook(&m_limbs, &pow10, &mut tmp);
2268            // rhs = tmp << e
2269            shl(&tmp, e as u32, &mut rhs);
2270        } else {
2271            // lhs = |self| · 2^(−e); rhs = m · 10^scale.
2272            shl(a_limbs, (-e) as u32, &mut lhs);
2273            mul_schoolbook(&m_limbs, &pow10, &mut rhs);
2274        }
2275
2276        let mag_cmp = cmp_cross(&lhs, &rhs);
2277        // For two negatives the larger magnitude is the smaller value.
2278        let c = if sn { -mag_cmp } else { mag_cmp };
2279        if c < 0 {
2280            Ordering::Less
2281        } else if c > 0 {
2282            Ordering::Greater
2283        } else {
2284            Ordering::Equal
2285        }
2286    }
2287}
2288
2289// One generic comparison surface across widths. The `N == M` case is
2290// covered here too, so `Int<N>` carries no separate same-width
2291// `PartialEq` / `PartialOrd` / `Ord` impl (a derived or hand-written
2292// same-width comparison would collide — E0119). `Eq` is still derived:
2293// it only requires the `PartialEq<Self>` this generic impl provides.
2294impl<const N: usize, const M: usize> PartialEq<Int<M>> for Int<N> {
2295    #[inline]
2296    fn eq(&self, other: &Int<M>) -> bool {
2297        self.cmp_cross(*other) == Ordering::Equal
2298    }
2299}
2300
2301impl<const N: usize, const M: usize> PartialOrd<Int<M>> for Int<N> {
2302    #[inline]
2303    fn partial_cmp(&self, other: &Int<M>) -> Option<Ordering> {
2304        Some(self.cmp_cross(*other))
2305    }
2306}
2307
2308// `i128` interop for the 128-bit storage `Int<2>` (D38's backend).
2309// `Int<2>` *is* an `i128`, so the comparison is the direct `as_i128`
2310// value — no widening round-trip. This lets `to_bits()` results compare
2311// against `i128` literals without an explicit conversion at the call
2312// site. Deliberately `Int<2>`-only: for wider tiers an `i128` comparison
2313// would be a lossy narrowing and is not offered.
2314// `Int<2>` *is* a 128-bit integer, so the conversion to `i128` is exact
2315// (the trait form of `as_i128`). Enables `i128::from(int2)` / `.into()`.
2316impl From<Int<2>> for i128 {
2317    #[inline]
2318    fn from(v: Int<2>) -> i128 {
2319        v.as_i128()
2320    }
2321}
2322
2323impl PartialEq<i128> for Int<2> {
2324    #[inline]
2325    fn eq(&self, other: &i128) -> bool {
2326        self.as_i128() == *other
2327    }
2328}
2329impl PartialEq<Int<2>> for i128 {
2330    #[inline]
2331    fn eq(&self, other: &Int<2>) -> bool {
2332        *self == other.as_i128()
2333    }
2334}
2335impl PartialOrd<i128> for Int<2> {
2336    #[inline]
2337    fn partial_cmp(&self, other: &i128) -> Option<Ordering> {
2338        self.as_i128().partial_cmp(other)
2339    }
2340}
2341impl PartialOrd<Int<2>> for i128 {
2342    #[inline]
2343    fn partial_cmp(&self, other: &Int<2>) -> Option<Ordering> {
2344        self.partial_cmp(&other.as_i128())
2345    }
2346}
2347
2348// `i64` interop for the 64-bit storage `Int<1>` (D18's backend). `Int<1>`
2349// *is* an `i64`, so the bridge is the direct value (its `as_i128()` is the
2350// sign-extended `i64`) — letting `to_bits()` results compare against `i64`
2351// literals without an explicit conversion. Deliberately `Int<1>`-only.
2352impl From<Int<1>> for i64 {
2353    #[inline]
2354    fn from(v: Int<1>) -> i64 {
2355        v.as_i128() as i64
2356    }
2357}
2358// `Int<1>` is 64-bit, so widening to `i128` is exact.
2359impl From<Int<1>> for i128 {
2360    #[inline]
2361    fn from(v: Int<1>) -> i128 {
2362        v.as_i128()
2363    }
2364}
2365impl PartialEq<i64> for Int<1> {
2366    #[inline]
2367    fn eq(&self, other: &i64) -> bool {
2368        self.as_i128() == *other as i128
2369    }
2370}
2371impl PartialEq<Int<1>> for i64 {
2372    #[inline]
2373    fn eq(&self, other: &Int<1>) -> bool {
2374        *self as i128 == other.as_i128()
2375    }
2376}
2377impl PartialOrd<i64> for Int<1> {
2378    #[inline]
2379    fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
2380        self.as_i128().partial_cmp(&(i128::from(*other)))
2381    }
2382}
2383impl PartialOrd<Int<1>> for i64 {
2384    #[inline]
2385    fn partial_cmp(&self, other: &Int<1>) -> Option<Ordering> {
2386        i128::from(*self).partial_cmp(&other.as_i128())
2387    }
2388}
2389
2390impl<const N: usize> Ord for Int<N> {
2391    #[inline]
2392    fn cmp(&self, other: &Self) -> Ordering {
2393        // Same-width total order, routing through the cmp policy dispatcher
2394        // so the algorithm seam exists at a single point and the `const {
2395        // select::<N>() }` block folds the choice per monomorphisation.
2396        cmp_dispatch(*self, *other)
2397    }
2398}
2399
2400impl<const N: usize> Add for Int<N> {
2401    type Output = Self;
2402    #[inline]
2403    fn add(self, rhs: Self) -> Self {
2404        add_dispatch(self, rhs)
2405    }
2406}
2407
2408impl<const N: usize> Sub for Int<N> {
2409    type Output = Self;
2410    #[inline]
2411    fn sub(self, rhs: Self) -> Self {
2412        self.wrapping_sub(rhs)
2413    }
2414}
2415
2416impl<const N: usize> Mul for Int<N> {
2417    type Output = Self;
2418    #[inline]
2419    fn mul(self, rhs: Self) -> Self {
2420        self.wrapping_mul(rhs)
2421    }
2422}
2423
2424impl<const N: usize> BitAnd for Int<N> {
2425    type Output = Self;
2426    #[inline]
2427    fn bitand(self, rhs: Self) -> Self {
2428        let mut out = [0u64; N];
2429        let mut i = 0;
2430        while i < N {
2431            out[i] = self.limbs[i] & rhs.limbs[i];
2432            i += 1;
2433        }
2434        Self { limbs: out }
2435    }
2436}
2437
2438impl<const N: usize> BitOr for Int<N> {
2439    type Output = Self;
2440    #[inline]
2441    fn bitor(self, rhs: Self) -> Self {
2442        let mut out = [0u64; N];
2443        let mut i = 0;
2444        while i < N {
2445            out[i] = self.limbs[i] | rhs.limbs[i];
2446            i += 1;
2447        }
2448        Self { limbs: out }
2449    }
2450}
2451
2452impl<const N: usize> BitXor for Int<N> {
2453    type Output = Self;
2454    #[inline]
2455    fn bitxor(self, rhs: Self) -> Self {
2456        let mut out = [0u64; N];
2457        let mut i = 0;
2458        while i < N {
2459            out[i] = self.limbs[i] ^ rhs.limbs[i];
2460            i += 1;
2461        }
2462        Self { limbs: out }
2463    }
2464}
2465
2466impl<const N: usize> Not for Int<N> {
2467    type Output = Self;
2468    #[inline]
2469    fn not(self) -> Self {
2470        let mut out = [0u64; N];
2471        let mut i = 0;
2472        while i < N {
2473            out[i] = !self.limbs[i];
2474            i += 1;
2475        }
2476        Self { limbs: out }
2477    }
2478}
2479
2480impl<const N: usize> Shl<u32> for Int<N> {
2481    type Output = Self;
2482    #[inline]
2483    fn shl(self, shift: u32) -> Self {
2484        let mut out = [0u64; N];
2485        shl_fixed(&self.limbs, shift, &mut out);
2486        Self { limbs: out }
2487    }
2488}
2489
2490impl<const N: usize> Shr<u32> for Int<N> {
2491    type Output = Self;
2492    #[inline]
2493    fn shr(self, shift: u32) -> Self {
2494        // Arithmetic (sign-preserving) right shift — matches Rust's signed
2495        // `>>` and the prior macro `Int*` types the transcendental range
2496        // reduction relies on. Two's-complement: x >> s == !((!x) >> s) for x < 0.
2497        let neg = self.is_negative();
2498        let src = if neg { !self } else { self };
2499        let mut out = [0u64; N];
2500        shr_fixed(&src.limbs, shift, &mut out);
2501        let shifted = Self { limbs: out };
2502        if neg { !shifted } else { shifted }
2503    }
2504}
2505
2506impl<const N: usize> Neg for Int<N> {
2507    type Output = Self;
2508    #[inline]
2509    fn neg(self) -> Self {
2510        self.wrapping_neg()
2511    }
2512}
2513
2514// ── Div / Rem ───────────────────────────────────────────────────────
2515//
2516// Truncating signed division / remainder, delegating to the dispatching
2517// `div_rem` so the operators share the macro types' divide algorithm
2518// (`div_rem_dispatch`: Knuth / Burnikel–Ziegler for multi-limb
2519// divisors). These supertraits are what `BigInt` requires.
2520
2521impl<const N: usize> Div for Int<N> {
2522    type Output = Self;
2523    #[inline]
2524    fn div(self, rhs: Self) -> Self {
2525        self.div_rem(rhs).0
2526    }
2527}
2528
2529impl<const N: usize> Rem for Int<N> {
2530    type Output = Self;
2531    #[inline]
2532    fn rem(self, rhs: Self) -> Self {
2533        rem_dispatch(self, rhs)
2534    }
2535}
2536
2537// ── Display / FromStr ───────────────────────────────────────────────
2538//
2539// Delegate to the shared limb fmt / parse path, so the const-generic
2540// surface round-trips identically across every width.
2541
2542impl<const N: usize> core::fmt::Display for Uint<N>
2543where
2544    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2545{
2546    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2547        // Exact per-`N` decimal output buffer (`20N + 2` bytes), drawn from
2548        // the `Limbs<N>` scratch carrier; the formatter writes the base-10
2549        // digits from its tail.
2550        let mut buf =
2551            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2552        let s = fmt_into::<N>(&self.limbs, 10, true, buf.as_mut());
2553        f.pad_integral(true, "", s)
2554    }
2555}
2556
2557impl<const N: usize> core::fmt::Display for Int<N>
2558where
2559    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2560{
2561    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2562        let mag = *self.unsigned_abs().as_limbs();
2563        // Exact per-`N` decimal output buffer (`20N + 2` bytes): an `Int<N>`
2564        // is `64N` bits, so its base-10 form is `⌈64·N·log10(2)⌉ ≈ 19.27·N`
2565        // digits, comfortably within `20N + 2`. Drawn from the `Limbs<N>`
2566        // scratch carrier; the formatter writes the digits from its tail.
2567        let mut buf =
2568            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2569        let s = fmt_into::<N>(&mag, 10, true, buf.as_mut());
2570        f.pad_integral(!self.is_negative() || self.is_zero(), "", s)
2571    }
2572}
2573
2574impl<const N: usize> core::str::FromStr for Int<N> {
2575    type Err = ();
2576    #[inline]
2577    fn from_str(s: &str) -> Result<Self, ()> {
2578        Self::from_str_radix(s, 10)
2579    }
2580}
2581
2582// ── Radix formatting (raw two's-complement bit pattern) ─────────────
2583//
2584// `LowerHex` / `UpperHex` / `Octal` / `Binary` print the raw limb bit
2585// pattern (not a signed magnitude), matching the macro `$S` impls. Each
2586// draws its output buffer exact-per-`N` from the `Limbs<N>` scratch carrier:
2587// hex (`16N` digits) fits the `digit_formatting_limbs_u8` decimal buffer
2588// (`20N + 2`); octal (`⌈64N/3⌉`) and binary (`64N`, one byte per bit) take
2589// the wider `bit_formatting_limbs_u8` buffer (`64N + 2`).
2590
2591impl<const N: usize> core::fmt::LowerHex for Int<N>
2592where
2593    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2594{
2595    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2596        let mut buf =
2597            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2598        let s = fmt_into::<N>(&self.limbs, 16, true, buf.as_mut());
2599        f.pad_integral(true, "0x", s)
2600    }
2601}
2602
2603impl<const N: usize> core::fmt::UpperHex for Int<N>
2604where
2605    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2606{
2607    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2608        let mut buf =
2609            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::digit_formatting_limbs_u8();
2610        let s = fmt_into::<N>(&self.limbs, 16, false, buf.as_mut());
2611        f.pad_integral(true, "0x", s)
2612    }
2613}
2614
2615impl<const N: usize> core::fmt::Octal for Int<N>
2616where
2617    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2618{
2619    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2620        let mut buf =
2621            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
2622        let s = fmt_into::<N>(&self.limbs, 8, true, buf.as_mut());
2623        f.pad_integral(true, "0o", s)
2624    }
2625}
2626
2627impl<const N: usize> core::fmt::Binary for Int<N>
2628where
2629    crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
2630{
2631    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2632        let mut buf =
2633            <crate::int::types::compute_limbs::Limbs<N> as crate::int::types::compute_limbs::ComputeLimbs>::bit_formatting_limbs_u8();
2634        let s = fmt_into::<N>(&self.limbs, 2, true, buf.as_mut());
2635        f.pad_integral(true, "0b", s)
2636    }
2637}
2638
2639// ── Width conversion: widen (lossless) / narrow (fallible) ─────────
2640//
2641// `Uint<N>` and `Uint<M>` are different-sized stack types, so a value
2642// conversion builds a fresh `[u64; M]` — there is no heap allocation,
2643// and reinterpreting across widths via `transmute` would be unsound
2644// (different size and layout). `resize` writes each destination limb
2645// exactly once via `array::from_fn`; `widen` is the infallible extend
2646// and `narrow` the information-preserving truncation. Stable Rust
2647// cannot constrain `M >= N` / `M <= N` in the type system, so the
2648// direction is enforced by `debug_assert!` plus, for `narrow`, the
2649// `Option` return.
2650
2651impl<const N: usize> Uint<N> {
2652    /// Resizes to `M` limbs: zero-extends when widening, drops the high
2653    /// limbs when narrowing. Direction-agnostic and infallible.
2654    ///
2655    /// Named `resize_n` (not `resize`) so the const-generic width bridge
2656    /// does not collide with the type-generic `Int::resize` the named-
2657    /// type API expects.
2658    #[inline]
2659    pub const fn resize_n<const M: usize>(self) -> Uint<M> {
2660        let mut out = [0u64; M];
2661        let mut i = 0;
2662        while i < M {
2663            if i < N {
2664                out[i] = self.limbs[i];
2665            }
2666            i += 1;
2667        }
2668        Uint::from_limbs(out)
2669    }
2670
2671    /// Widens to a wider `Uint<M>` (`M >= N`), zero-extending the new
2672    /// high limbs. Lossless.
2673    #[inline]
2674    pub fn widen<const M: usize>(self) -> Uint<M> {
2675        debug_assert!(M >= N, "widen requires M >= N");
2676        self.resize_n::<M>()
2677    }
2678
2679    /// Narrows to a narrower `Uint<M>` (`M <= N`). Returns `None` if any
2680    /// discarded high limb is non-zero (the value does not fit `M`).
2681    #[inline]
2682    pub fn narrow<const M: usize>(self) -> Option<Uint<M>> {
2683        debug_assert!(M <= N, "narrow requires M <= N");
2684        let keep = if M < N { M } else { N };
2685        let mut i = keep;
2686        while i < N {
2687            if self.limbs[i] != 0 {
2688                return None;
2689            }
2690            i += 1;
2691        }
2692        Some(self.resize_n::<M>())
2693    }
2694}
2695
2696impl<const N: usize> Int<N> {
2697    /// Resizes to `M` limbs: sign-extends when widening, drops the high
2698    /// limbs when narrowing. Direction-agnostic and infallible
2699    /// (narrowing may change the represented value).
2700    ///
2701    /// Named `resize_n` (not `resize`) so the const-generic width bridge
2702    /// does not collide with the type-generic `Int::resize` the named-
2703    /// type API expects (the magnitude-bridge cast over any `BigInt`).
2704    ///
2705    /// Infallible two's-complement width conversion `Int<N> -> Int<M>`,
2706    /// const and direction-agnostic. Copies the overlapping limbs; when
2707    /// widening, fills the new high limbs with the sign (all-ones if the
2708    /// source is negative, else zero); when narrowing, drops the surplus
2709    /// high limbs (which may change the represented value). This is the
2710    /// canonical internal resize that the const fallible `try_narrow` and
2711    /// the `Int<->Int` magnitude bridge build on.
2712    #[inline]
2713    pub(crate) const fn resize_n<const M: usize>(self) -> Int<M> {
2714        let fill = if self.is_negative() { u64::MAX } else { 0 };
2715        let mut out = [0u64; M];
2716        let mut i = 0;
2717        while i < M {
2718            out[i] = if i < N { self.limbs[i] } else { fill };
2719            i += 1;
2720        }
2721        Int::from_limbs(out)
2722    }
2723
2724    /// Const fallible narrow `Int<N> -> Int<M>` (`1 <= M <= N`). Returns
2725    /// `Some` only when every discarded high limb is a pure sign-extension
2726    /// of the narrowed value's top bit — i.e. the value fits `M` limbs as
2727    /// two's complement — and `None` otherwise. The const base for the
2728    /// future public fallible narrow.
2729    #[inline]
2730    pub(crate) const fn try_narrow<const M: usize>(self) -> Option<Int<M>> {
2731        debug_assert!(M >= 1 && M <= N, "try_narrow requires 1 <= M <= N");
2732        let sign_fill = if (self.limbs[M - 1] >> 63) == 1 {
2733            u64::MAX
2734        } else {
2735            0
2736        };
2737        let mut i = M;
2738        while i < N {
2739            if self.limbs[i] != sign_fill {
2740                return None;
2741            }
2742            i += 1;
2743        }
2744        Some(self.resize_n::<M>())
2745    }
2746
2747    /// Widens to a wider `Int<M>` (`M >= N`), sign-extending. Lossless.
2748    #[inline]
2749    pub fn widen<const M: usize>(self) -> Int<M> {
2750        debug_assert!(M >= N, "widen requires M >= N");
2751        self.resize_n::<M>()
2752    }
2753
2754    /// Narrows to a narrower `Int<M>` (`1 <= M <= N`). Returns `None`
2755    /// unless every discarded high limb is a pure sign-extension of the
2756    /// narrowed value's top bit — i.e. the value fits `M` limbs as
2757    /// two's complement.
2758    #[inline]
2759    pub fn narrow<const M: usize>(self) -> Option<Int<M>> {
2760        self.try_narrow::<M>()
2761    }
2762}
2763
2764
2765
2766// ── std-aligned primitive conversions ───────────────────────────────
2767// Infallible widening (the source always fits `Int<N>` / `Uint<N>` for
2768// N >= 1, judged against the `N == 1` = `i64` floor) is `From`. Fallible
2769// narrowing (the value may exceed the target range) is `TryFrom`,
2770// returning [`ConvertError::Overflow`]. Each impl is a thin one-line
2771// delegate to the inherent const base. `Into` / `TryInto` come for free.
2772
2773// ── IN: primitive → Int<N> ───────────────────────────────────────────
2774macro_rules! int_from_signed {
2775    ($($prim:ty => $base:ident),+) => {$(
2776        impl<const N: usize> From<$prim> for Int<N> {
2777            #[inline]
2778            fn from(v: $prim) -> Self { Self::$base(v) }
2779        }
2780    )+};
2781}
2782int_from_signed!(i8 => from_i8, i16 => from_i16, i32 => from_i32, i64 => from_i64);
2783
2784macro_rules! int_from_unsigned {
2785    ($($prim:ty => $base:ident),+) => {$(
2786        impl<const N: usize> From<$prim> for Int<N> {
2787            #[inline]
2788            fn from(v: $prim) -> Self { Self::$base(v) }
2789        }
2790    )+};
2791}
2792int_from_unsigned!(u8 => from_u8, u16 => from_u16, u32 => from_u32);
2793
2794macro_rules! uint_from_unsigned {
2795    ($($prim:ty),+) => {$(
2796        impl<const N: usize> From<$prim> for Uint<N> {
2797            #[inline]
2798            fn from(v: $prim) -> Self { Self::from_u64(v as u64) }
2799        }
2800    )+};
2801}
2802uint_from_unsigned!(u8, u16, u32, u64);
2803
2804// Fallible IN: `u64` can overflow `Int<1>`; `i128` / `u128` can overflow
2805// the narrow tiers (see the inherent `try_from_*`).
2806impl<const N: usize> TryFrom<u64> for Int<N> {
2807    type Error = crate::support::error::ConvertError;
2808    #[inline]
2809    fn try_from(v: u64) -> Result<Self, Self::Error> {
2810        Self::try_from_u64(v).ok_or(crate::support::error::ConvertError::Overflow)
2811    }
2812}
2813
2814impl<const N: usize> TryFrom<i128> for Int<N> {
2815    type Error = crate::support::error::ConvertError;
2816    #[inline]
2817    fn try_from(v: i128) -> Result<Self, Self::Error> {
2818        Self::try_from_i128(v).ok_or(crate::support::error::ConvertError::Overflow)
2819    }
2820}
2821
2822impl<const N: usize> TryFrom<u128> for Int<N> {
2823    type Error = crate::support::error::ConvertError;
2824    #[inline]
2825    fn try_from(v: u128) -> Result<Self, Self::Error> {
2826        Self::try_from_u128(v).ok_or(crate::support::error::ConvertError::Overflow)
2827    }
2828}
2829
2830// ── OUT: Int<N> → primitive ──────────────────────────────────────────
2831// `i64` / `i128` are always representable on the fitting tiers, so those
2832// are infallible `From` (declared above: `From<Int<1>> for i64`,
2833// `From<Int<1>>` / `From<Int<2>> for i128`). The std blanket
2834// `impl<T,U: From<T>> TryFrom<T> for U` then derives the matching
2835// `TryFrom` for those tiers, so a generic `TryFrom<Int<N>> for i64`/`i128`
2836// is omitted to avoid the coherence conflict. The remaining widths /
2837// targets are genuinely fallible:
2838impl<const N: usize> TryFrom<Int<N>> for i32 {
2839    type Error = crate::support::error::ConvertError;
2840    #[inline]
2841    fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
2842        v.try_to_i32().ok_or(crate::support::error::ConvertError::Overflow)
2843    }
2844}
2845
2846impl<const N: usize> TryFrom<Int<N>> for u32 {
2847    type Error = crate::support::error::ConvertError;
2848    #[inline]
2849    fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
2850        v.try_to_u32().ok_or(crate::support::error::ConvertError::Overflow)
2851    }
2852}
2853
2854impl<const N: usize> TryFrom<Int<N>> for u64 {
2855    type Error = crate::support::error::ConvertError;
2856    #[inline]
2857    fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
2858        v.try_to_u64().ok_or(crate::support::error::ConvertError::Overflow)
2859    }
2860}
2861
2862impl<const N: usize> TryFrom<Int<N>> for u128 {
2863    type Error = crate::support::error::ConvertError;
2864    #[inline]
2865    fn try_from(v: Int<N>) -> Result<Self, Self::Error> {
2866        v.try_to_u128().ok_or(crate::support::error::ConvertError::Overflow)
2867    }
2868}
2869
2870impl<const N: usize> TryFrom<u128> for Uint<N> {
2871    type Error = crate::support::error::ConvertError;
2872    #[inline]
2873    fn try_from(v: u128) -> Result<Self, Self::Error> {
2874        Self::try_from_u128(v).ok_or(crate::support::error::ConvertError::Overflow)
2875    }
2876}
2877
2878// ── Floats ───────────────────────────────────────────────────────────
2879// Float → Int<N> is fallible (NaN, ±inf, non-integer, out-of-range), so
2880// `TryFrom`. There is no `From<Int<N>> for f64/f32`: that direction is
2881// lossy above the mantissa width and is offered only as the inherent
2882// `to_f64` / `to_f32` round-to-nearest methods.
2883impl<const N: usize> TryFrom<f64> for Int<N> {
2884    type Error = crate::support::error::ConvertError;
2885    #[inline]
2886    fn try_from(v: f64) -> Result<Self, Self::Error> {
2887        Self::try_from_f64(v).ok_or(crate::support::error::ConvertError::Overflow)
2888    }
2889}
2890
2891impl<const N: usize> TryFrom<f32> for Int<N> {
2892    type Error = crate::support::error::ConvertError;
2893    #[inline]
2894    fn try_from(v: f32) -> Result<Self, Self::Error> {
2895        Self::try_from_f32(v).ok_or(crate::support::error::ConvertError::Overflow)
2896    }
2897}
2898
2899#[cfg(test)]
2900mod tests {
2901    use super::*;
2902    use crate::int::algos::mul::mul_schoolbook::{mul_low_fixed, mul_schoolbook_fixed};
2903
2904    #[test]
2905    fn try_from_i128_detects_narrow_overflow() {
2906        // Int<2> (128-bit) holds every i128.
2907        assert_eq!(Int::<2>::try_from_i128(i128::MAX), Some(Int::<2>::from_i128(i128::MAX)));
2908        assert_eq!(Int::<2>::try_from_i128(i128::MIN), Some(Int::<2>::from_i128(i128::MIN)));
2909        // Int<1> (64-bit) holds only the i64 range.
2910        assert_eq!(Int::<1>::try_from_i128(i64::MAX as i128 + 1), None);
2911        assert_eq!(Int::<1>::try_from_i128(i64::MIN as i128 - 1), None);
2912        assert_eq!(
2913            Int::<1>::try_from_i128(i64::MAX as i128),
2914            Some(Int::<1>::from_i64(i64::MAX))
2915        );
2916        // TryFrom mirrors it.
2917        assert!(<Int<1> as TryFrom<i128>>::try_from(i64::MAX as i128 + 1).is_err());
2918        assert_eq!(<Int<2> as TryFrom<i128>>::try_from(-5_i128), Ok(Int::<2>::from_i64(-5)));
2919    }
2920
2921    #[test]
2922    fn try_from_u128_detects_narrow_overflow() {
2923        // Uint twin: every u128 fits Uint<2+>; Uint<1> only holds u64.
2924        assert_eq!(Uint::<2>::try_from_u128(u128::MAX), Some(Uint::<2>::from_u128(u128::MAX)));
2925        assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128 + 1), None);
2926        assert_eq!(Uint::<1>::try_from_u128(u64::MAX as u128), Some(Uint::<1>::from_u64(u64::MAX)));
2927        assert!(<Uint<1> as TryFrom<u128>>::try_from(u64::MAX as u128 + 1).is_err());
2928        // Int<N> u128 entry: Int<1> fails above i64::MAX, Int<2> above i128::MAX.
2929        assert_eq!(Int::<1>::try_from_u128(u64::MAX as u128), None);
2930        assert_eq!(Int::<2>::try_from_u128(u128::MAX), None);
2931        assert_eq!(Int::<3>::try_from_u128(u128::MAX), Some(Int::<3>::from_u128(u128::MAX)));
2932        assert!(<Int<2> as TryFrom<u128>>::try_from(u128::MAX).is_err());
2933    }
2934
2935    #[test]
2936    fn try_from_u64_and_out_conversions_at_edges() {
2937        // u64 entry: Int<1> rejects above i64::MAX, wider tiers accept.
2938        assert!(<Int<1> as TryFrom<u64>>::try_from(u64::MAX).is_err());
2939        assert_eq!(<Int<2> as TryFrom<u64>>::try_from(u64::MAX), Ok(Int::<2>::from_u128(u64::MAX as u128)));
2940        // OUT lossless: Int<1> -> i64, Int<2> -> i128 are exact From.
2941        assert_eq!(i128::from(Int::<2>::MAX), i128::MAX);
2942        assert_eq!(i128::from(Int::<2>::MIN), i128::MIN);
2943        assert_eq!(i64::from(Int::<1>::from_i64(-123)), -123_i64);
2944        // OUT fallible: a wide value out of i128 range rejects (inherent
2945        // base; i128 has no generic TryFrom — it is From-derived on N<=2).
2946        let big = Int::<3>::from_u128(u128::MAX);
2947        assert_eq!(big.try_to_i128(), None);
2948        assert_eq!(<i32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-7)), Ok(-7_i32));
2949        assert!(<u32 as TryFrom<Int<4>>>::try_from(Int::<4>::from_i64(-1)).is_err());
2950    }
2951
2952    #[test]
2953    fn float_conversions_reject_and_round_trip() {
2954        // Rejections.
2955        assert_eq!(Int::<2>::try_from_f64(f64::NAN), None);
2956        assert_eq!(Int::<2>::try_from_f64(f64::INFINITY), None);
2957        assert_eq!(Int::<2>::try_from_f64(f64::NEG_INFINITY), None);
2958        assert_eq!(Int::<2>::try_from_f64(0.5), None);
2959        assert_eq!(Int::<2>::try_from_f64(3.5), None);
2960        assert_eq!(Int::<2>::try_from_f32(f32::NAN), None);
2961        assert_eq!(Int::<2>::try_from_f32(f32::INFINITY), None);
2962        assert_eq!(Int::<2>::try_from_f32(0.5), None);
2963        assert_eq!(Int::<2>::try_from_f32(3.5), None);
2964        assert_eq!(Int::<1>::try_from_f64(1e30), None); // out of i64 range
2965        // ±0 maps to ZERO.
2966        assert_eq!(Int::<2>::try_from_f64(0.0), Some(Int::<2>::ZERO));
2967        assert_eq!(Int::<2>::try_from_f64(-0.0), Some(Int::<2>::ZERO));
2968        assert_eq!(Int::<2>::try_from_f32(0.0), Some(Int::<2>::ZERO));
2969        assert_eq!(Int::<2>::try_from_f32(-0.0), Some(Int::<2>::ZERO));
2970        // In-range integers round-trip exactly.
2971        assert_eq!(Int::<4>::try_from_f64(42.0), Some(Int::<4>::from_i64(42)));
2972        assert_eq!(Int::<4>::try_from_f64(-42.0), Some(Int::<4>::from_i64(-42)));
2973        assert_eq!(Int::<4>::try_from_f32(7.0), Some(Int::<4>::from_i64(7)));
2974        assert_eq!(Int::<4>::try_from_f32(-7.0), Some(Int::<4>::from_i64(-7)));
2975        // TryFrom mirrors the helper.
2976        assert!(<Int<2> as TryFrom<f64>>::try_from(f64::NAN).is_err());
2977        assert_eq!(<Int<2> as TryFrom<f64>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
2978        assert_eq!(<Int<2> as TryFrom<f32>>::try_from(-9.0), Ok(Int::<2>::from_i64(-9)));
2979        // OUT to_f64 / to_f32 round-trip small values.
2980        assert_eq!(Int::<4>::from_i64(123).to_f64(), 123.0);
2981        assert_eq!(Int::<4>::from_i64(-123).to_f64(), -123.0);
2982        assert_eq!(Int::<4>::from_i64(123).to_f32(), 123.0_f32);
2983    }
2984
2985    #[test]
2986    fn float_conversions_wide_and_boundaries() {
2987        // A large exact integer (2^64) needs two limbs: it overflows
2988        // Int<1> but fits Int<2>.
2989        let big = 2f64.powi(64); // exactly representable
2990        assert_eq!(Int::<1>::try_from_f64(big), None);
2991        assert_eq!(
2992            Int::<2>::try_from_f64(big),
2993            Some(Int::<2>::from_u128(1u128 << 64))
2994        );
2995
2996        // 2^63 is exactly i64::MAX + 1 — just over the Int<1> positive
2997        // range, so it must reject.
2998        let two_63 = 2f64.powi(63);
2999        assert_eq!(Int::<1>::try_from_f64(two_63), None);
3000        // i64::MAX itself (2^63 - 1) is not f64-exact, but 2^63 - 2^10 is;
3001        // confirm a representable value strictly below the cap fits.
3002        let near_max = (i64::MAX - 1023) as f64; // exact in f64
3003        assert_eq!(near_max as i64, i64::MAX - 1023);
3004        assert_eq!(
3005            Int::<1>::try_from_f64(near_max),
3006            Some(Int::<1>::from_i64(i64::MAX - 1023))
3007        );
3008
3009        // The MIN edge: -2^63 == i64::MIN fits Int<1> exactly.
3010        let min_edge = -(2f64.powi(63));
3011        assert_eq!(
3012            Int::<1>::try_from_f64(min_edge),
3013            Some(Int::<1>::from_i64(i64::MIN))
3014        );
3015        // One magnitude bit past the negative edge does not fit.
3016        assert_eq!(Int::<1>::try_from_f64(-(2f64.powi(64))), None);
3017
3018        // f32 analogues: 2^24 fits Int<1>; the round-trip is exact.
3019        let two_24 = 2f32.powi(24);
3020        assert_eq!(
3021            Int::<1>::try_from_f32(two_24),
3022            Some(Int::<1>::from_i64(1 << 24))
3023        );
3024    }
3025
3026    #[test]
3027    fn float_conversions_are_const() {
3028        // Proves the float→int path is usable in const context.
3029        const X: Option<Int<2>> = Int::<2>::try_from_f64(42.0);
3030        const Y: Option<Int<2>> = Int::<2>::try_from_f32(42.0);
3031        assert_eq!(X, Some(Int::<2>::from_i64(42)));
3032        assert_eq!(Y, Some(Int::<2>::from_i64(42)));
3033    }
3034
3035    #[test]
3036    fn from_traits_are_infallible_for_fitting_prims() {
3037        assert_eq!(Int::<1>::from(-7_i32), Int::<1>::from_i64(-7));
3038        assert_eq!(Int::<4>::from(42_i64), Int::<4>::from_i64(42));
3039        assert_eq!(Uint::<1>::from(7_u32), Uint::<1>::from_u64(7));
3040        assert_eq!(Uint::<4>::from(u64::MAX), Uint::<4>::from_u64(u64::MAX));
3041    }
3042
3043    /// The `BigInt` / `BigInt` surface must compile and
3044    /// behave through a fully generic bound, for both signed and
3045    /// unsigned fixed-width integers.
3046    #[test]
3047    fn fixed_int_trait_surface() {
3048        fn exercises<T: BigInt>(seven: T, three: T) {
3049            assert_eq!(T::LIMBS as u32 * 64, T::BITS);
3050            assert!(T::ZERO.is_zero());
3051            assert!(T::ONE.is_one());
3052            assert!(!T::ZERO.is_one());
3053
3054            // Operators via the supertrait bounds.
3055            let ten = seven + three;
3056            assert_eq!(ten - three, seven);
3057            assert_eq!(ten, seven.wrapping_add(three));
3058            assert_eq!(seven.wrapping_sub(three) + three, seven);
3059
3060            // Bitwise / shift surface.
3061            let _ = (seven & three) | (seven ^ three);
3062            let _ = !T::ZERO;
3063            assert_eq!((T::ONE << 4) >> 4, T::ONE);
3064
3065            // Checked arithmetic returns Some for in-range work.
3066            assert_eq!(seven.checked_add(three), Some(ten));
3067            assert!(seven.checked_mul(three).is_some());
3068
3069            // Powers and optimisable functions agree with each other.
3070            assert_eq!(three.sqr(), three * three);
3071            assert_eq!(three.cube(), three * three * three);
3072            assert_eq!(three.pow(2), three.sqr());
3073            assert_eq!(three.wrapping_pow(3), three.cube());
3074            assert_eq!(three.checked_pow(2), Some(three.sqr()));
3075
3076            // bit_length / leading_zeros consistency.
3077            assert_eq!(T::ONE.bit_length(), 1);
3078            assert_eq!(T::ZERO.bit_length(), 0);
3079            assert_eq!(T::ONE.leading_zeros(), T::BITS - 1);
3080
3081            // Reductions and the limb round-trip.
3082            let items = [T::ONE, T::ONE, T::ONE];
3083            assert_eq!(T::sum(items), three);
3084            assert_eq!(T::product([three, T::ONE]), three);
3085            assert_eq!(T::from_limbs(seven.to_limbs()), seven);
3086        }
3087
3088        exercises(Int::<4>::from_i64(7), Int::<4>::from_i64(3));
3089        exercises(Int::<6>::from_i64(7), Int::<6>::from_i64(3));
3090    }
3091
3092    /// The truncated low-`N` product must equal the low `N` limbs of
3093    /// the full `2N`-limb schoolbook product, across widths and edges.
3094    #[test]
3095    fn limbs_mul_low_matches_full_product_low_half() {
3096        fn check<const N: usize, const D: usize>(a: [u64; N], b: [u64; N]) {
3097            debug_assert!(D == 2 * N);
3098            let mut full = [0u64; D];
3099            mul_schoolbook_fixed::<N, D>(&a, &b, &mut full);
3100            let mut low = [0u64; N];
3101            mul_low_fixed::<N>(&a, &b, &mut low);
3102            let mut expected = [0u64; N];
3103            expected.copy_from_slice(&full[..N]);
3104            assert_eq!(low, expected, "low-half mismatch for {a:?} * {b:?}");
3105        }
3106
3107        // Width 4 (256-bit): zero, one, MAX, cross-limb spans.
3108        check::<4, 8>([0, 0, 0, 0], [0, 0, 0, 0]);
3109        check::<4, 8>([1, 0, 0, 0], [u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
3110        check::<4, 8>([u64::MAX; 4], [u64::MAX; 4]);
3111        check::<4, 8>([0, 1, 0, 0], [0, 1, 0, 0]); // 2^64 * 2^64
3112        check::<4, 8>(
3113            [0xDEAD_BEEF, 0xCAFE_F00D, 0x1234, 0x5678_9ABC],
3114            [0xFEED_FACE, 0x0BAD_C0DE, 0x9999, 0x0000_0001],
3115        );
3116        // Width 2 and width 6 to exercise other monomorphisations.
3117        check::<2, 4>([u64::MAX, u64::MAX], [3, 0]);
3118        check::<6, 12>([7, 8, 9, 10, 11, 12], [1, 2, 3, 4, 5, 6]);
3119    }
3120
3121    /// The dedicated squaring kernel must be bit-exact against the
3122    /// general truncated product `x · x` at every width and edge case.
3123    #[test]
3124    fn dedicated_sqr_matches_general_mul() {
3125        fn check<const N: usize>(x: [u64; N]) {
3126            let a = Uint::<N>::from_limbs(x);
3127            assert_eq!(
3128                a.wrapping_sqr(),
3129                a.wrapping_mul(a),
3130                "sqr != mul(self,self) for {x:?}"
3131            );
3132        }
3133
3134        // Width 4: 0, 1, MAX, single-limb, cross-limb, full-width.
3135        check::<4>([0, 0, 0, 0]);
3136        check::<4>([1, 0, 0, 0]);
3137        check::<4>([u64::MAX; 4]);
3138        check::<4>([0x1234_5678, 0, 0, 0]);
3139        check::<4>([u64::MAX, u64::MAX, 0, 0]);
3140        check::<4>([0xDEAD_BEEF_CAFE_F00D, 0x0123_4567_89AB_CDEF, 0xFEDC, 0x99]);
3141        // Carry-heavy: every limb all-ones but the top.
3142        check::<4>([u64::MAX, u64::MAX, u64::MAX, 0]);
3143        // Other widths / monomorphisations.
3144        check::<1>([u64::MAX]);
3145        check::<2>([u64::MAX, u64::MAX]);
3146        check::<6>([7, 8, 9, 10, 11, 12]);
3147        check::<8>([u64::MAX, 1, u64::MAX, 2, u64::MAX, 3, u64::MAX, 4]);
3148        // A pseudo-random sweep across width 5.
3149        let mut state = 0x9E37_79B9_7F4A_7C15u64;
3150        let mut next = || {
3151            state ^= state << 13;
3152            state ^= state >> 7;
3153            state ^= state << 17;
3154            state
3155        };
3156        for _ in 0..64 {
3157            check::<5>([next(), next(), next(), next(), next()]);
3158        }
3159    }
3160
3161    #[test]
3162    fn uint_sqr_cube_match_naive() {
3163        let x = Uint::<4>::from_limbs([123_456_789, 0, 0, 0]);
3164        assert_eq!(x.wrapping_sqr(), x.wrapping_mul(x));
3165        assert_eq!(x.wrapping_cube(), x.wrapping_mul(x).wrapping_mul(x));
3166
3167        // A value spanning two limbs, to exercise cross-limb products.
3168        let y = Uint::<4>::from_limbs([0xDEAD_BEEF_CAFE, 0x1234_5678, 0, 0]);
3169        assert_eq!(y.wrapping_sqr(), y.wrapping_mul(y));
3170        assert_eq!(y.wrapping_cube(), y.wrapping_mul(y).wrapping_mul(y));
3171    }
3172
3173    /// `root_int(k)` must return `floor(m^(1/k))` with the correct
3174    /// exactness flag: `root^k <= m < (root+1)^k`, and `exact` iff
3175    /// `root^k == m`. Checked against a brute-force reference for small
3176    /// `m` and `k in {2,3,5}` across widths, plus cross-checking k=2
3177    /// against the shipped isqrt and large perfect-power exactness.
3178    // Exercises Int<8> roots (isqrt/icbrt work widths) beyond the narrow
3179    // build's int-kernel scratch; runs where the wide tiers are compiled.
3180    #[cfg(feature = "_wide-support")]
3181    #[test]
3182    fn root_int_floor_and_exactness() {
3183        // Brute-force floor kth root of a small u128.
3184        fn brute(m: u128, k: u32) -> (u128, bool) {
3185            if m == 0 {
3186                return (0, true);
3187            }
3188            let mut r: u128 = 0;
3189            // Smallest r with (r+1)^k > m, capped to avoid overflow.
3190            while {
3191                let next = r + 1;
3192                next.checked_pow(k).is_some_and(|p| p <= m)
3193            } {
3194                r += 1;
3195            }
3196            (r, r.pow(k) == m)
3197        }
3198
3199        fn check<const N: usize>(m: u128, k: u32) {
3200            let lo = (m & 0xFFFF_FFFF_FFFF_FFFF) as u64;
3201            let hi = (m >> 64) as u64;
3202            let mut limbs = [0u64; N];
3203            limbs[0] = lo;
3204            if N > 1 {
3205                limbs[1] = hi;
3206            }
3207            let n = Uint::<N>::from_limbs(limbs);
3208            let (root, exact) = n.root_int(k);
3209            let (eroot, eexact) = brute(m, k);
3210            let root_lo = root.as_limbs()[0] as u128
3211                | ((if N > 1 { root.as_limbs()[1] as u128 } else { 0 }) << 64);
3212            assert_eq!(root_lo, eroot, "root mismatch for m={m}, k={k}");
3213            assert_eq!(exact, eexact, "exact flag mismatch for m={m}, k={k}");
3214
3215            // The defining bracket, computed in-width.
3216            let rk = root.pow(k);
3217            assert!(rk <= n, "root^k > m for m={m}, k={k}");
3218            let next = root.wrapping_add(Uint::<N>::ONE);
3219            // (root+1)^k overflowing the width still satisfies > m.
3220            if let Some(p) = next.checked_pow(k) { assert!(p > n, "(root+1)^k <= m for m={m}, k={k}") }
3221        }
3222
3223        let samples: [u128; 14] = [
3224            0,
3225            1,
3226            2,
3227            7,
3228            8,
3229            9,
3230            26,
3231            27,
3232            28,
3233            1000,
3234            1023,
3235            1024,
3236            1_000_000,
3237            u64::MAX as u128,
3238        ];
3239        for &m in &samples {
3240            for k in [2u32, 3, 5] {
3241                check::<4>(m, k);
3242                check::<2>(m, k);
3243                check::<8>(m, k);
3244            }
3245        }
3246
3247        // k=2 cross-check against shipped isqrt for a wide value.
3248        let big = Uint::<4>::from_limbs([0xFFFF_FFFF_FFFF_FFFF, 0x1234_5678, 0, 0]);
3249        assert_eq!(big.root_int(2).0, big.isqrt());
3250
3251        // Large exact perfect cube: (2^40)^3 = 2^120.
3252        let base = Uint::<4>::ONE.shl(40);
3253        let cube = base.wrapping_cube();
3254        let (r, exact) = cube.root_int(3);
3255        assert_eq!(r, base);
3256        assert!(exact);
3257        // One less than a perfect cube is not exact and floors down.
3258        let (r2, exact2) = cube.wrapping_sub(Uint::<4>::ONE).root_int(3);
3259        assert_eq!(r2, base.wrapping_sub(Uint::<4>::ONE));
3260        assert!(!exact2);
3261    }
3262
3263    #[test]
3264    fn uint_widen_zero_extends() {
3265        let a = Uint::<2>::from_limbs([7, 8]);
3266        let w = a.widen::<4>();
3267        assert_eq!(*w.as_limbs(), [7, 8, 0, 0]);
3268        assert_eq!(a.resize_n::<4>(), w);
3269    }
3270
3271    #[test]
3272    fn uint_narrow_checks_dropped_limbs() {
3273        let fits = Uint::<4>::from_limbs([7, 8, 0, 0]);
3274        assert_eq!(*fits.narrow::<2>().unwrap().as_limbs(), [7, 8]);
3275
3276        let too_big = Uint::<4>::from_limbs([7, 8, 1, 0]);
3277        assert!(too_big.narrow::<2>().is_none());
3278
3279        // widen → narrow round-trips losslessly.
3280        let a = Uint::<2>::from_limbs([3, 4]);
3281        assert_eq!(a.widen::<4>().narrow::<2>().unwrap(), a);
3282    }
3283
3284    #[test]
3285    fn int_widen_sign_extends() {
3286        // -1 is all-ones; sign-extension keeps it all-ones at any width.
3287        let neg = Int::<2>::from_i64(-1);
3288        assert_eq!(*neg.widen::<4>().as_limbs(), [u64::MAX; 4]);
3289        assert_eq!(neg.widen::<4>(), Int::<4>::from_i64(-1));
3290        // Positive values zero-extend.
3291        let pos = Int::<2>::from_i64(5);
3292        assert_eq!(*pos.widen::<4>().as_limbs(), [5, 0, 0, 0]);
3293    }
3294
3295    #[test]
3296    fn int_narrow_requires_sign_consistency() {
3297        // Negative value whose dropped limbs are all the sign fill: fits.
3298        let neg = Int::<4>::from_i64(-1);
3299        assert_eq!(neg.narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
3300        // Positive magnitude with a non-sign high limb: does not fit.
3301        let big = Int::<4>::from_limbs([0, 0, 1, 0]);
3302        assert!(big.narrow::<2>().is_none());
3303        // Negative top bit but a dropped limb that isn't all-ones: no fit.
3304        let weird = Int::<4>::from_limbs([1, 0, 0, u64::MAX]);
3305        assert!(weird.narrow::<2>().is_none());
3306        // Small value round-trips.
3307        let p = Int::<2>::from_i64(42);
3308        assert_eq!(p.widen::<4>().narrow::<2>().unwrap(), p);
3309    }
3310
3311    #[test]
3312    fn int_resize_const_two_complement() {
3313        // Widen round-trips: positive zero-extends, value preserved.
3314        let pos = Int::<2>::from_i64(123);
3315        let wide: Int<4> = pos.resize_n::<4>();
3316        assert_eq!(*wide.as_limbs(), [123, 0, 0, 0]);
3317        assert_eq!(wide.resize_n::<2>(), pos);
3318
3319        // Sign-extend of negatives: high limbs all-ones, value preserved.
3320        let neg = Int::<2>::from_i64(-7);
3321        let wneg: Int<4> = neg.resize_n::<4>();
3322        assert_eq!(*wneg.as_limbs(), [(-7i64) as u64, u64::MAX, u64::MAX, u64::MAX]);
3323        assert_eq!(wneg, Int::<4>::from_i64(-7));
3324
3325        // Truncate drops the surplus high limbs.
3326        let v = Int::<4>::from_limbs([1, 2, 3, 4]);
3327        assert_eq!(*v.resize_n::<2>().as_limbs(), [1, 2]);
3328
3329        // try_narrow Some/None at the signed boundary.
3330        // -1 narrows: dropped limbs are the sign fill.
3331        assert_eq!(Int::<4>::from_i64(-1).try_narrow::<2>().unwrap(), Int::<2>::from_i64(-1));
3332        // A positive value with a set high limb does not fit.
3333        assert!(Int::<4>::from_limbs([0, 0, 1, 0]).try_narrow::<2>().is_none());
3334        // 2^63 is positive and fits Int<2> but NOT Int<1>: narrowing to
3335        // one limb would set the top bit, flipping it negative, and the
3336        // dropped high limb (0) is not the negative sign fill — so None.
3337        let too_big = Int::<2>::from_limbs([0x8000_0000_0000_0000, 0]);
3338        assert!(too_big.try_narrow::<1>().is_none());
3339        // i64::MIN genuinely fits Int<1>: try_narrow returns Some.
3340        let min2 = Int::<2>::from_i64(i64::MIN);
3341        assert_eq!(min2.try_narrow::<1>().unwrap(), Int::<1>::from_i64(i64::MIN));
3342        // Its widening then narrowing round-trips.
3343        assert_eq!(min2.resize_n::<4>().try_narrow::<2>().unwrap(), min2);
3344
3345        // Const evaluation smoke: resize_n is usable in const context.
3346        const W: Int<4> = Int::<2>::from_i64(-7).resize_n::<4>();
3347        assert_eq!(W, Int::<4>::from_i64(-7));
3348    }
3349
3350    #[test]
3351    fn bit_count_is_const() {
3352        // const-smoke: these compile only if the inherent methods are
3353        // `const fn`. Called fully-qualified so the inherent `&self` impl
3354        // is selected over the (non-const) `BigInt` trait method of the
3355        // same name, which takes `self` by value and would otherwise win
3356        // value-receiver method resolution.
3357        const Z: u32 = Int::<2>::leading_zeros(&Int::<2>::MAX);
3358        const BL: u32 = Int::<2>::bit_length(&Int::<2>::MAX);
3359        const UZ: u32 = Uint::<2>::leading_zeros(&Uint::<2>::MAX);
3360        const UBL: u32 = Uint::<2>::bit_length(&Uint::<2>::MAX);
3361        // Int<2>::MAX is positive with the sign bit clear: 1 leading zero,
3362        // bit_length = 127.
3363        assert_eq!(Z, 1);
3364        assert_eq!(BL, 127);
3365        // Uint<2>::MAX is all-ones: 0 leading zeros, full 128-bit length.
3366        assert_eq!(UZ, 0);
3367        assert_eq!(UBL, 128);
3368    }
3369
3370    #[test]
3371    fn int_cross_width_comparison() {
3372        // Equal values across widths compare ==.
3373        let a = Int::<1>::from_i64(5);
3374        let b = Int::<2>::from_i64(5);
3375        assert!(a == b);
3376        assert!(b == a);
3377        assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
3378
3379        // Ordering across widths.
3380        let small = Int::<1>::from_i64(3);
3381        let big = Int::<2>::from_i64(100);
3382        assert!(small < big);
3383        assert!(big > small);
3384
3385        // A value only fitting the wider type still orders right: 2^70
3386        // lives in Int<2> (and up) but exceeds Int<1>; it must compare
3387        // greater than any Int<1>.
3388        let huge = Int::<2>::from_limbs([0, 64]); // 64 * 2^64 = 2^70
3389        assert!(huge > Int::<1>::MAX);
3390        assert!(Int::<1>::from_i64(-1) < huge);
3391
3392        // Negatives: -1 (Int<1>) vs 1 (Int<2>) — neg < pos.
3393        assert!(Int::<1>::from_i64(-1) < Int::<2>::from_i64(1));
3394        // Two negatives across widths: larger magnitude is the smaller.
3395        assert!(Int::<1>::from_i64(-100) < Int::<2>::from_i64(-3));
3396        assert!(Int::<2>::from_i64(-3) > Int::<1>::from_i64(-100));
3397        // Equal negatives across widths.
3398        assert!(Int::<1>::from_i64(-7) == Int::<2>::from_i64(-7));
3399
3400        // Same-type Ord still sorts a Vec.
3401        let mut v = vec![
3402            Int::<2>::from_i64(3),
3403            Int::<2>::from_i64(-10),
3404            Int::<2>::from_i64(0),
3405            Int::<2>::from_i64(7),
3406        ];
3407        v.sort();
3408        assert_eq!(
3409            v,
3410            vec![
3411                Int::<2>::from_i64(-10),
3412                Int::<2>::from_i64(0),
3413                Int::<2>::from_i64(3),
3414                Int::<2>::from_i64(7),
3415            ]
3416        );
3417    }
3418
3419    #[test]
3420    fn consts_and_round_trip() {
3421        assert_eq!(Uint::<4>::LIMBS, 4);
3422        assert_eq!(Uint::<4>::BITS, 256);
3423        assert_eq!(*Uint::<4>::ZERO.as_limbs(), [0, 0, 0, 0]);
3424        assert_eq!(*Uint::<4>::ONE.as_limbs(), [1, 0, 0, 0]);
3425        assert_eq!(*Uint::<4>::MAX.as_limbs(), [u64::MAX; 4]);
3426
3427        let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
3428        assert_eq!(*v.as_limbs(), [7, 8, 9, 10]);
3429    }
3430
3431    #[test]
3432    fn widths_have_expected_bits() {
3433        assert_eq!(Int::<4>::BITS, 256);
3434        assert_eq!(Int::<64>::BITS, 4096);
3435        assert_eq!(Uint::<16>::LIMBS, 16);
3436    }
3437
3438    #[test]
3439    fn wrapping_sub_borrows_across_limbs() {
3440        // 2^64 - 1 ... computed as 0 - 1 wrapping, then check a clean borrow.
3441        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3442        let d = a.wrapping_sub(Uint::<4>::ONE);
3443        assert_eq!(*d.as_limbs(), [u64::MAX, 0, 0, 0]);
3444
3445        // 0 - 1 wraps to all-ones (modulo 2^256).
3446        let wrap = Uint::<4>::ZERO.wrapping_sub(Uint::<4>::ONE);
3447        assert_eq!(*wrap.as_limbs(), [u64::MAX; 4]);
3448    }
3449
3450    #[test]
3451    fn unsigned_ordering() {
3452        let small = Uint::<4>::from_limbs([5, 0, 0, 0]);
3453        let big = Uint::<4>::from_limbs([0, 1, 0, 0]); // 2^64 > 5
3454        assert!(small < big);
3455        assert!(big > small);
3456        assert_eq!(small, small);
3457        assert!(Uint::<4>::ZERO < Uint::<4>::MAX);
3458        // round-trips through derived PartialOrd helpers
3459        assert_eq!(small.max(big), big);
3460    }
3461
3462    #[test]
3463    fn wrapping_add_carries_across_limbs() {
3464        // (2^64 - 1) + 1 carries into the next limb.
3465        let a = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
3466        let sum = a.wrapping_add(Uint::<4>::ONE);
3467        assert_eq!(*sum.as_limbs(), [0, 1, 0, 0]);
3468
3469        // All-ones + 1 wraps to zero (modulo 2^256).
3470        let wrap = Uint::<4>::MAX.wrapping_add(Uint::<4>::ONE);
3471        assert_eq!(*wrap.as_limbs(), [0, 0, 0, 0]);
3472    }
3473
3474    #[test]
3475    fn uint_wrapping_mul_cross_limb_product() {
3476        // 2^64 * 2^64 = 2^128 — lands exactly in limb 2.
3477        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3478        let p = a.wrapping_mul(a);
3479        assert_eq!(*p.as_limbs(), [0, 0, 1, 0]);
3480
3481        // (2^64 - 1) * 3 = 3*2^64 - 3 = [u64::MAX - 2, 2, 0, 0].
3482        let m = Uint::<4>::from_limbs([u64::MAX, 0, 0, 0]);
3483        let three = Uint::<4>::from_limbs([3, 0, 0, 0]);
3484        let q = m.wrapping_mul(three);
3485        assert_eq!(*q.as_limbs(), [u64::MAX - 2, 2, 0, 0]);
3486
3487        // Multiply by one is identity.
3488        let v = Uint::<4>::from_limbs([7, 8, 9, 10]);
3489        assert_eq!(v.wrapping_mul(Uint::<4>::ONE), v);
3490    }
3491
3492    #[test]
3493    fn uint_wrapping_mul_truncates_modulo_width() {
3494        // 2^192 * 2^192 = 2^384, fully above 2^256 → wraps to zero.
3495        let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
3496        assert_eq!(hi.wrapping_mul(hi), Uint::<4>::ZERO);
3497
3498        // MAX * MAX mod 2^256: (2^256 - 1)^2 = 2^512 - 2^257 + 1.
3499        // mod 2^256 that is 1 (since 2^512 and 2^257 are both 0 mod
3500        // 2^256). So the low limb is 1, the rest zero.
3501        let r = Uint::<4>::MAX.wrapping_mul(Uint::<4>::MAX);
3502        assert_eq!(*r.as_limbs(), [1, 0, 0, 0]);
3503    }
3504
3505    #[test]
3506    fn uint_checked_add_sub_overflow() {
3507        assert_eq!(
3508            Uint::<4>::ONE.checked_add(Uint::<4>::ONE),
3509            Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
3510        );
3511        // MAX + 1 overflows.
3512        assert_eq!(Uint::<4>::MAX.checked_add(Uint::<4>::ONE), None);
3513
3514        assert_eq!(
3515            Uint::<4>::from_limbs([5, 0, 0, 0]).checked_sub(Uint::<4>::from_limbs([3, 0, 0, 0])),
3516            Some(Uint::<4>::from_limbs([2, 0, 0, 0]))
3517        );
3518        // 0 - 1 underflows.
3519        assert_eq!(Uint::<4>::ZERO.checked_sub(Uint::<4>::ONE), None);
3520    }
3521
3522    #[test]
3523    fn uint_checked_mul_overflow() {
3524        // 2^64 * 2^64 = 2^128 fits in 256 bits.
3525        let a = Uint::<4>::from_limbs([0, 1, 0, 0]);
3526        assert_eq!(a.checked_mul(a), Some(Uint::<4>::from_limbs([0, 0, 1, 0])));
3527        // 2^192 * 2^192 overflows 256 bits.
3528        let hi = Uint::<4>::from_limbs([0, 0, 0, 1]);
3529        assert_eq!(hi.checked_mul(hi), None);
3530        // MAX * 2 overflows.
3531        assert_eq!(
3532            Uint::<4>::MAX.checked_mul(Uint::<4>::from_limbs([2, 0, 0, 0])),
3533            None
3534        );
3535    }
3536
3537    #[test]
3538    fn uint_div_rem_with_remainder() {
3539        // 1000 / 7 = 142 r 6.
3540        let n = Uint::<4>::from_limbs([1000, 0, 0, 0]);
3541        let d = Uint::<4>::from_limbs([7, 0, 0, 0]);
3542        assert_eq!(*n.wrapping_div(d).as_limbs(), [142, 0, 0, 0]);
3543        assert_eq!(*n.wrapping_rem(d).as_limbs(), [6, 0, 0, 0]);
3544
3545        // 2^128 / 2^64 = 2^64, remainder 0.
3546        let big = Uint::<4>::from_limbs([0, 0, 1, 0]);
3547        let by = Uint::<4>::from_limbs([0, 1, 0, 0]);
3548        assert_eq!(*big.wrapping_div(by).as_limbs(), [0, 1, 0, 0]);
3549        assert!(big.wrapping_rem(by).is_zero());
3550    }
3551
3552    #[test]
3553    #[should_panic(expected = "divide by zero")]
3554    fn uint_div_by_zero_panics() {
3555        let _ = Uint::<4>::ONE.wrapping_div(Uint::<4>::ZERO);
3556    }
3557
3558    #[test]
3559    fn uint_bitwise_ops() {
3560        let a = Uint::<4>::from_limbs([0b1100, 0xFF, 0, 0]);
3561        let b = Uint::<4>::from_limbs([0b1010, 0x0F, 0, 0]);
3562        assert_eq!(*(a & b).as_limbs(), [0b1000, 0x0F, 0, 0]);
3563        assert_eq!(*(a | b).as_limbs(), [0b1110, 0xFF, 0, 0]);
3564        assert_eq!(*(a ^ b).as_limbs(), [0b0110, 0xF0, 0, 0]);
3565        assert_eq!(*(!Uint::<4>::ZERO).as_limbs(), [u64::MAX; 4]);
3566    }
3567
3568    #[test]
3569    fn uint_shifts() {
3570        let one = Uint::<4>::ONE;
3571        // 1 << 64 lands in limb 1.
3572        assert_eq!(*(one << 64).as_limbs(), [0, 1, 0, 0]);
3573        // 1 << 130 = limb 2 bit 2.
3574        assert_eq!(*(one << 130).as_limbs(), [0, 0, 0b100, 0]);
3575        // Right shift back.
3576        let v = Uint::<4>::from_limbs([0, 0, 0b100, 0]);
3577        assert_eq!(*(v >> 130).as_limbs(), [1, 0, 0, 0]);
3578        // Shift past the width drops everything.
3579        assert_eq!(one << 256, Uint::<4>::ZERO);
3580    }
3581
3582    #[test]
3583    fn uint_is_zero_bitlen_leading_zeros() {
3584        assert!(Uint::<4>::ZERO.is_zero());
3585        assert!(!Uint::<4>::ONE.is_zero());
3586        assert_eq!(Uint::<4>::ZERO.bit_length(), 0);
3587        assert_eq!(Uint::<4>::ONE.bit_length(), 1);
3588        // 2^64 has bit length 65.
3589        let b = Uint::<4>::from_limbs([0, 1, 0, 0]);
3590        assert_eq!(b.bit_length(), 65);
3591        assert_eq!(b.leading_zeros(), 256 - 65);
3592        assert_eq!(Uint::<4>::ZERO.leading_zeros(), 256);
3593        assert_eq!(Uint::<4>::MAX.leading_zeros(), 0);
3594    }
3595
3596    #[test]
3597    fn uint_operator_traits_delegate() {
3598        let a = Uint::<4>::from_limbs([10, 0, 0, 0]);
3599        let b = Uint::<4>::from_limbs([3, 0, 0, 0]);
3600        assert_eq!(*(a + b).as_limbs(), [13, 0, 0, 0]);
3601        assert_eq!(*(a - b).as_limbs(), [7, 0, 0, 0]);
3602        assert_eq!(*(a * b).as_limbs(), [30, 0, 0, 0]);
3603    }
3604
3605    #[test]
3606    fn int_from_i64_sign_extends() {
3607        // Positive: only the low limb is set.
3608        assert_eq!(*Int::<4>::from_i64(5).as_limbs(), [5, 0, 0, 0]);
3609        // -1 sign-extends to all-ones.
3610        assert_eq!(*Int::<4>::from_i64(-1).as_limbs(), [u64::MAX; 4]);
3611        // -2 → low limb u64::MAX - 1, upper limbs all-ones.
3612        assert_eq!(
3613            *Int::<4>::from_i64(-2).as_limbs(),
3614            [u64::MAX - 1, u64::MAX, u64::MAX, u64::MAX]
3615        );
3616        assert!(Int::<4>::from_i64(-1).is_negative());
3617        assert!(Int::<4>::from_i64(1).is_positive());
3618        assert!(Int::<4>::from_i64(0).is_zero());
3619    }
3620
3621    #[test]
3622    fn int_wrapping_neg_two_complement() {
3623        let five = Int::<4>::from_i64(5);
3624        let neg_five = five.wrapping_neg();
3625        assert_eq!(neg_five, Int::<4>::from_i64(-5));
3626        // Negating twice returns the original.
3627        assert_eq!(neg_five.wrapping_neg(), five);
3628        // -1 negates to 1.
3629        assert_eq!(Int::<4>::from_i64(-1).wrapping_neg(), Int::<4>::ONE);
3630        // Neg operator delegates.
3631        assert_eq!(-five, neg_five);
3632    }
3633
3634    #[test]
3635    fn int_add_sub_mul_with_signs() {
3636        let a = Int::<4>::from_i64(7);
3637        let b = Int::<4>::from_i64(-3);
3638        // 7 + (-3) = 4
3639        assert_eq!(a.wrapping_add(b), Int::<4>::from_i64(4));
3640        // 7 - (-3) = 10
3641        assert_eq!(a.wrapping_sub(b), Int::<4>::from_i64(10));
3642        // 7 * (-3) = -21
3643        assert_eq!(a.wrapping_mul(b), Int::<4>::from_i64(-21));
3644        // (-3) * (-3) = 9
3645        assert_eq!(b.wrapping_mul(b), Int::<4>::from_i64(9));
3646        // operator delegation
3647        assert_eq!(a + b, Int::<4>::from_i64(4));
3648        assert_eq!(a - b, Int::<4>::from_i64(10));
3649        assert_eq!(a * b, Int::<4>::from_i64(-21));
3650    }
3651
3652    #[test]
3653    fn int_mul_crosses_limbs_with_sign() {
3654        // 2^64 * (-1) = -2^64.
3655        let big = Int::<4>::from_i64(0).wrapping_add(Int::<4>::from_limbs([0, 1, 0, 0]));
3656        let neg = big.wrapping_mul(Int::<4>::from_i64(-1));
3657        assert_eq!(neg, big.wrapping_neg());
3658        // -2^64 should be [0, u64::MAX, u64::MAX, u64::MAX].
3659        assert_eq!(*neg.as_limbs(), [0, u64::MAX, u64::MAX, u64::MAX]);
3660    }
3661
3662    #[test]
3663    fn int_abs_signum() {
3664        assert_eq!(Int::<4>::from_i64(-9).abs(), Int::<4>::from_i64(9));
3665        assert_eq!(Int::<4>::from_i64(9).abs(), Int::<4>::from_i64(9));
3666        assert_eq!(Int::<4>::from_i64(-9).signum(), -1);
3667        assert_eq!(Int::<4>::from_i64(9).signum(), 1);
3668        assert_eq!(Int::<4>::from_i64(0).signum(), 0);
3669    }
3670
3671    #[test]
3672    fn int_from_i128_round_trips() {
3673        for v in [
3674            0i128,
3675            1,
3676            -1,
3677            42,
3678            -42,
3679            i64::MAX as i128,
3680            i64::MIN as i128,
3681            i128::MAX,
3682            i128::MIN,
3683            123_456_789_012_345_678,
3684        ] {
3685            let a = Int::<4>::from_i128(v);
3686            assert_eq!(a.to_i128_checked(), Some(v), "round-trip i128 {v}");
3687            let b = Int::<8>::from_i128(v);
3688            assert_eq!(b.to_i128_checked(), Some(v), "round-trip i128 {v} (N=8)");
3689        }
3690        // from_u128 of a value above i128::MAX is not representable as i128.
3691        let big = Int::<4>::from_u128(u128::MAX);
3692        assert_eq!(big.to_i128_checked(), None);
3693        assert_eq!(big.to_u128_checked(), Some(u128::MAX));
3694        // Negative has no u128.
3695        assert_eq!(Int::<4>::from_i128(-1).to_u128_checked(), None);
3696    }
3697
3698    #[test]
3699    fn int_from_str_radix_and_display_round_trip() {
3700        let cases = [
3701            "0",
3702            "1",
3703            "-1",
3704            "42",
3705            "-42",
3706            "1000000000000000000000",
3707            "-340282366920938463463374607431768211455",
3708        ];
3709        for s in cases {
3710            let v = Int::<4>::from_str_radix(s, 10).unwrap();
3711            assert_eq!(format!("{v}"), s, "display round-trip {s}");
3712            // FromStr delegates to from_str_radix(_, 10).
3713            let v2: Int<4> = s.parse().unwrap();
3714            assert_eq!(v, v2);
3715        }
3716        // Non-base-10 and malformed input reject.
3717        assert!(Int::<4>::from_str_radix("10", 16).is_err());
3718        assert!(Int::<4>::from_str_radix("12x", 10).is_err());
3719        assert!(Int::<4>::from_str_radix("", 10).is_err());
3720        assert!(Int::<4>::from_str_radix("-", 10).is_err());
3721    }
3722
3723    #[test]
3724    fn int_div_rem_signs_match_truncating() {
3725        // Truncating division: quotient truncates toward zero, remainder
3726        // carries the sign of the dividend.
3727        let cases: [(i128, i128); 6] = [
3728            (1000, 7),
3729            (-1000, 7),
3730            (1000, -7),
3731            (-1000, -7),
3732            (7, 1000),
3733            (-7, 1000),
3734        ];
3735        for (a, b) in cases {
3736            let (q, r) = Int::<4>::from_i128(a).div_rem(Int::<4>::from_i128(b));
3737            assert_eq!(q.to_i128_checked(), Some(a / b), "quot {a}/{b}");
3738            assert_eq!(r.to_i128_checked(), Some(a % b), "rem {a}%{b}");
3739        }
3740    }
3741
3742    #[test]
3743    #[should_panic(expected = "divide by zero")]
3744    fn int_div_rem_by_zero_panics() {
3745        let _ = Int::<4>::ONE.div_rem(Int::<4>::ZERO);
3746    }
3747
3748    #[test]
3749    fn int_bit_reads_twos_complement() {
3750        let v = Int::<4>::from_i128(0b1010);
3751        assert!(!v.bit(0));
3752        assert!(v.bit(1));
3753        assert!(!v.bit(2));
3754        assert!(v.bit(3));
3755        // Above the value's set bits: clear for positive.
3756        assert!(!v.bit(200));
3757        // Negative: high bits read as the sign extension (all ones).
3758        let neg = Int::<4>::from_i128(-1);
3759        assert!(neg.bit(0));
3760        assert!(neg.bit(255));
3761        assert!(neg.bit(1000)); // out of range → sign bit
3762    }
3763
3764    #[test]
3765    fn int_mul_u64_matches_wide_mul() {
3766        let v = Int::<4>::from_i128(123_456_789);
3767        assert_eq!(
3768            v.mul_u64(1000),
3769            Int::<4>::from_i128(123_456_789_000)
3770        );
3771        // Sign preserved.
3772        let n = Int::<4>::from_i128(-123_456_789);
3773        assert_eq!(
3774            n.mul_u64(1000),
3775            Int::<4>::from_i128(-123_456_789_000)
3776        );
3777        // Times zero / one.
3778        assert_eq!(v.mul_u64(0), Int::<4>::ZERO);
3779        assert_eq!(v.mul_u64(1), v);
3780    }
3781
3782    #[test]
3783    #[should_panic(expected = "mul overflow")]
3784    fn int_mul_u64_overflow_panics() {
3785        // max_value * 2 overflows the signed range.
3786        let _ = Int::<4>::max_value().mul_u64(2);
3787    }
3788
3789    #[test]
3790    fn int_div_rem_operators_match_div_rem() {
3791        // The Div / Rem operators must agree with the inherent div_rem,
3792        // which is what BigInt requires as supertraits.
3793        let a = Int::<4>::from_i128(-1000);
3794        let b = Int::<4>::from_i128(7);
3795        assert_eq!(a / b, Int::<4>::from_i128(-1000 / 7));
3796        assert_eq!(a % b, Int::<4>::from_i128(-1000 % 7));
3797        let (q, r) = a.div_rem(b);
3798        assert_eq!(a / b, q);
3799        assert_eq!(a % b, r);
3800    }
3801
3802    // Exercises Int<8> isqrt (work width 9) beyond the narrow build's scratch.
3803    #[cfg(feature = "_wide-support")]
3804    #[test]
3805    fn int_wide_storage_surface() {
3806        use crate::int::types::traits::BigInt;
3807        fn exercises<T: BigInt>() {
3808            assert!(<T as BigInt>::ZERO == <T as BigInt>::from_i128(0));
3809            assert!(<T as BigInt>::ONE == <T as BigInt>::from_i128(1));
3810            assert!(<T as BigInt>::TEN == <T as BigInt>::from_i128(10));
3811
3812            let twelve = <T as BigInt>::from_i128(12);
3813            let three = <T as BigInt>::from_i128(3);
3814            // pow / isqrt
3815            assert!(three.pow(3) == <T as BigInt>::from_i128(27));
3816            assert!(<T as BigInt>::from_i128(144).isqrt() == <T as BigInt>::from_i128(12));
3817            // div_rem
3818            let (q, r) = twelve.div_rem(<T as BigInt>::from_i128(5));
3819            assert!(q == <T as BigInt>::from_i128(2));
3820            assert!(r == <T as BigInt>::from_i128(2));
3821            // bit / leading_zeros
3822            assert!(twelve.bit(2) && twelve.bit(3) && !twelve.bit(0));
3823            assert!(<T as BigInt>::ONE.leading_zeros() == <T as BigInt>::BITS - 1);
3824            // mul_u64 / f64 round-trips
3825            assert!(twelve.mul_u64(10) == <T as BigInt>::from_i128(120));
3826            assert!(twelve.to_f64() == 12.0);
3827            assert!(<T as BigInt>::from_f64_val(7.9) == <T as BigInt>::from_i128(7));
3828        }
3829        exercises::<Int<4>>();
3830        exercises::<Int<8>>();
3831
3832        // resize_to widens/narrows across the family.
3833        let v = Int::<4>::from_i128(-123_456_789);
3834        let w: Int<8> = BigInt::resize_to(v);
3835        assert_eq!(w.to_i128_checked(), Some(-123_456_789));
3836        let back: Int<4> = BigInt::resize_to(w);
3837        assert_eq!(back, v);
3838    }
3839
3840    // Perfect-square round-trip at Int<8> exceeds the narrow build's scratch.
3841    #[cfg(feature = "_wide-support")]
3842    #[test]
3843    fn int_isqrt_matches_uint_magnitude() {
3844        use crate::int::types::traits::BigInt;
3845        // Signed isqrt is the magnitude isqrt (macro parity).
3846        let n = Int::<4>::from_i128(1_000_000_000_000);
3847        let r = BigInt::isqrt(n);
3848        assert_eq!(r, Int::<4>::from_i128(1_000_000));
3849        // Perfect square round-trip at width 8.
3850        let big = Int::<8>::from_i128(987_654_321);
3851        let sq = big.checked_mul(big).unwrap();
3852        assert_eq!(BigInt::isqrt(sq), big);
3853    }
3854
3855    /// `Uint<N>::sqr` must equal `x * x` at every tested width; the policy
3856    /// dispatcher must agree with `wrapping_sqr`.
3857    #[test]
3858    fn uint_sqr_policy_matches_wrapping_sqr() {
3859        fn check<const N: usize>(limbs: [u64; N]) {
3860            let x = Uint::<N>::from_limbs(limbs);
3861            assert_eq!(x.sqr(), x.wrapping_sqr(), "sqr mismatch at {limbs:?}");
3862            assert_eq!(x.sqr(), x.wrapping_mul(x), "sqr != x*x at {limbs:?}");
3863        }
3864        // Width 1
3865        check::<1>([0]);
3866        check::<1>([1]);
3867        check::<1>([u64::MAX]);
3868        check::<1>([12345]);
3869        // Width 2
3870        check::<2>([0, 0]);
3871        check::<2>([1, 0]);
3872        check::<2>([u64::MAX, u64::MAX]);
3873        check::<2>([0xDEAD_BEEF, 0x1234]);
3874        // Width 4
3875        check::<4>([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
3876        check::<4>([0x1234_5678_9ABC_DEF0, 0xFEDC_BA98, 0, 0]);
3877        // Width 6
3878        check::<6>([7, 8, 9, 10, 11, 12]);
3879    }
3880
3881    /// `Uint<N>::cube` must equal `x * x * x` at every tested width.
3882    #[test]
3883    fn uint_cube_policy_matches_wrapping_cube() {
3884        fn check<const N: usize>(limbs: [u64; N]) {
3885            let x = Uint::<N>::from_limbs(limbs);
3886            assert_eq!(x.cube(), x.wrapping_cube(), "cube mismatch at {limbs:?}");
3887            assert_eq!(
3888                x.cube(),
3889                x.wrapping_mul(x).wrapping_mul(x),
3890                "cube != x*x*x at {limbs:?}"
3891            );
3892        }
3893        check::<1>([0]);
3894        check::<1>([1]);
3895        check::<1>([3]);
3896        check::<1>([u64::MAX]);
3897        check::<2>([0, 0]);
3898        check::<2>([1, 0]);
3899        check::<2>([100, 0]);
3900        check::<2>([u64::MAX, u64::MAX]);
3901        check::<4>([5, 0, 0, 0]);
3902        check::<4>([0x1234, 0x5678, 0, 0]);
3903    }
3904
3905    /// `Int<N>::sqr` and `Int<N>::cube` must match their wrapping siblings.
3906    #[test]
3907    fn int_sqr_cube_match_wrapping() {
3908        fn check<const N: usize>(v: i128) {
3909            let x = Int::<N>::from_i128(v);
3910            assert_eq!(x.sqr(), x.wrapping_sqr(), "int sqr mismatch v={v}");
3911            assert_eq!(x.cube(), x.wrapping_cube(), "int cube mismatch v={v}");
3912        }
3913        for v in [0i128, 1, -1, 12, -12, 100, -100, 1_000_000, -1_000_000] {
3914            check::<4>(v);
3915            check::<8>(v);
3916        }
3917    }
3918
3919    /// `Uint<N>::icbrt` must return `floor(x^(1/3))` for a range of
3920    /// values: perfect cubes, non-cubes, 0, 1, and large values.
3921    #[test]
3922    fn uint_icbrt_floor_correctness() {
3923        // Brute-force floor cube-root of a u64, used as oracle for small inputs.
3924        fn brute_cbrt(n: u64) -> u64 {
3925            if n == 0 {
3926                return 0;
3927            }
3928            let mut r: u64 = 0;
3929            while (r + 1).checked_mul((r + 1) * (r + 1) + r + 1)
3930                .is_some_and(|p| p <= n)
3931                || (r + 1).checked_pow(3).is_some_and(|p| p <= n)
3932            {
3933                r += 1;
3934            }
3935            r
3936        }
3937
3938        fn check<const N: usize>(n: u64) {
3939            let x = Uint::<N>::from_u64(n);
3940            let root = x.icbrt();
3941            let root_u64 = root.as_limbs()[0];
3942            let expected = brute_cbrt(n);
3943            assert_eq!(root_u64, expected, "icbrt({n}) = {root_u64}, expected {expected}");
3944            // Higher limbs of root must be zero for a u64 input.
3945            for i in 1..N {
3946                assert_eq!(root.as_limbs()[i], 0, "icbrt({n}) high limb {i} nonzero");
3947            }
3948        }
3949
3950        // Perfect cubes.
3951        for n in [0u64, 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
3952                  1_000_000_000u64, 8_000_000_000u64] {
3953            check::<1>(n);
3954            check::<2>(n);
3955            check::<4>(n);
3956        }
3957
3958        // Non-cubes (floor must be correct).
3959        for n in [2u64, 3, 4, 5, 6, 7, 9, 10, 26, 28, 63, 65, 999,
3960                  1_000_000_001u64] {
3961            check::<1>(n);
3962            check::<2>(n);
3963            check::<4>(n);
3964        }
3965
3966        // Boundary cases: 0 and 1.
3967        check::<1>(0);
3968        check::<1>(1);
3969        check::<4>(0);
3970        check::<4>(1);
3971
3972        // Large 64-bit values.
3973        for n in [u64::MAX, u64::MAX - 1, u64::MAX - 100, 1 << 60, 1 << 48] {
3974            check::<2>(n);
3975            check::<4>(n);
3976        }
3977    }
3978
3979    /// `Uint<N>::icbrt` for values spanning multiple limbs (N >= 3 path).
3980    /// The cube root of a 2-limb value still fits in a single u64; we
3981    /// verify the floor identity `r³ <= x < (r+1)³`.
3982    #[test]
3983    fn uint_icbrt_wide_floor_identity() {
3984        fn check_wide<const N: usize>(limbs: [u64; N]) {
3985            let x = Uint::<N>::from_limbs(limbs);
3986            let r = x.icbrt();
3987            // Verify r³ <= x.
3988            let r3 = r.wrapping_pow(3);
3989            assert!(
3990                r3 <= x,
3991                "icbrt violated: r³ > x for {limbs:?}"
3992            );
3993            // Verify (r+1)³ > x (i.e. r is the floor).
3994            let r1 = r.wrapping_add(Uint::<N>::ONE);
3995            let r1_3 = r1.wrapping_pow(3);
3996            // (r+1)³ wraps to 0 only if r+1 itself wraps (i.e. r == MAX),
3997            // which for a cube root of a representable value cannot happen.
3998            assert!(
3999                r1_3 > x || r1 == Uint::<N>::ZERO,
4000                "icbrt not floor: (r+1)³ <= x for {limbs:?}"
4001            );
4002        }
4003
4004        // Perfect cubes in 2 limbs (root fits one limb).
4005        // 10^18 cubed = 10^54 — too large; use modest values.
4006        // 1_000_000^3 = 10^18, which fits 2 limbs (u64 goes to ~1.8 * 10^19).
4007        let m = 1_000_000u64;
4008        let m3 = m * m * m; // 10^18, fits u64
4009        check_wide::<4>([m3, 0, 0, 0]);
4010        check_wide::<4>([m3 + 1, 0, 0, 0]);
4011        check_wide::<4>([m3 - 1, 0, 0, 0]);
4012
4013        // A 2-limb value: combine two u64 halves.
4014        let hi = 1u64;
4015        let lo = 0u64;
4016        check_wide::<4>([lo, hi, 0, 0]); // 2^64
4017
4018        check_wide::<4>([u64::MAX, u64::MAX, 0, 0]); // near 2^128
4019
4020        // Width 6 (N >= 3, Newton path for sure).
4021        check_wide::<6>([m3, 0, 0, 0, 0, 0]);
4022        check_wide::<6>([u64::MAX, u64::MAX, u64::MAX, 0, 0, 0]); // near 2^192
4023    }
4024
4025    /// `Int<N>::icbrt` must return the unsigned cube root of the magnitude.
4026    #[test]
4027    fn int_icbrt_magnitude() {
4028        for v in [0i128, 1, -1, 8, -8, 27, -27, 1000, -1000] {
4029            let x = Int::<4>::from_i128(v);
4030            let expected = Int::<4>::from_i128((v.unsigned_abs() as f64).cbrt() as i128);
4031            let got = x.icbrt();
4032            assert_eq!(got, expected, "int icbrt({v}) mismatch");
4033        }
4034    }
4035
4036    #[test]
4037    fn int_wideint_mag_sign_round_trips() {
4038        use crate::int::types::traits::BigInt;
4039        // mag_into_u128 / from_mag_sign_u128 round-trip for signed
4040        // values, including the magnitude + sign split.
4041        for v in [0i128, 1, -1, 123_456_789_012_345_678, -987_654_321] {
4042            let a = Int::<4>::from_i128(v);
4043            let mut mag = [0u128; 2];
4044            let neg = a.mag_into_u128(&mut mag);
4045            assert_eq!(neg, a.is_negative());
4046            assert_eq!(Int::<4>::from_mag_sign_u128(&mag, neg), a, "mag/sign {v}");
4047        }
4048        // U128_LIMBS = ceil(N/2): even and odd N.
4049        assert_eq!(<Int<4> as BigInt>::U128_LIMBS, 2);
4050        assert_eq!(<Int<3> as BigInt>::U128_LIMBS, 2);
4051        assert_eq!(<Int<8> as BigInt>::U128_LIMBS, 4);
4052
4053        // mag_into_u128 / from_mag_sign_u128 round-trip (the hot-path
4054        // buffer bypass), including the odd-N tail at N=3.
4055        let v3 = Int::<3>::from_i128(-170_141_183_460_469_231_731);
4056        let mut buf = [0u128; 2];
4057        let neg = v3.mag_into_u128(&mut buf);
4058        assert_eq!(Int::<3>::from_mag_sign_u128(&buf, neg), v3);
4059
4060        let v4 = Int::<4>::from_i128(i128::MIN);
4061        let mut buf4 = [0u128; 2];
4062        let neg4 = v4.mag_into_u128(&mut buf4);
4063        assert_eq!(Int::<4>::from_mag_sign_u128(&buf4, neg4), v4);
4064    }
4065
4066    #[test]
4067    fn int_to_from_f64_and_negate_ten() {
4068        assert_eq!(Int::<4>::from_i64(5).to_f64(), 5.0);
4069        assert_eq!(Int::<4>::from_i64(-5).to_f64(), -5.0);
4070        assert_eq!(Int::<4>::from_f64(42.9), Int::<4>::from_i64(42));
4071        assert_eq!(Int::<4>::from_f64(-42.9), Int::<4>::from_i64(-42));
4072        // Non-finite maps to ZERO.
4073        assert_eq!(Int::<4>::from_f64(f64::NAN), Int::<4>::ZERO);
4074        // negate is wrapping_neg; TEN const is 10.
4075        assert_eq!(Int::<4>::from_i64(5).negate(), Int::<4>::from_i64(-5));
4076        assert_eq!(Int::<4>::TEN, Int::<4>::from_i64(10));
4077        // from_limbs_le / limbs_le round-trip.
4078        let v = Int::<4>::from_i128(-9_876_543_210);
4079        assert_eq!(Int::<4>::from_limbs_le(v.limbs_le()), v);
4080    }
4081
4082    #[test]
4083    fn int_signed_ordering() {
4084        let neg = Int::<4>::from_i64(-5);
4085        let zero = Int::<4>::ZERO;
4086        let pos = Int::<4>::from_i64(5);
4087        // Negative < zero < positive even though -5's limbs are larger
4088        // unsigned than 5's.
4089        assert!(neg < zero);
4090        assert!(zero < pos);
4091        assert!(neg < pos);
4092        // Two negatives compare by magnitude with sign accounted for.
4093        assert!(Int::<4>::from_i64(-10) < Int::<4>::from_i64(-1));
4094        assert_eq!(neg.max(pos), pos);
4095        assert_eq!(neg.min(pos), neg);
4096        assert_eq!(pos.cmp(&pos), Ordering::Equal);
4097    }
4098}
4099
4100/// Feasibility proof for the unified narrow-tier divide path: the same
4101/// `widen_mul` → `div_wide_pow10` pipeline the wide tiers already
4102/// run must produce the correct `(a · b) / 10^scale` at the narrow
4103/// limb widths `N = 1` (`Int64`) and `N = 2` (`Int128`) that the
4104/// D18/D38-unify steps will rewire onto. This locks in the
4105/// `widen_mul::<wider>` then `div_wide_pow10::<wider>`
4106/// composition before any decimal type is rewired; it is additive and
4107/// asserts only — no behaviour is changed here.
4108#[cfg(all(test, feature = "wide"))]
4109mod unified_mg_feasibility {
4110    use super::Int;
4111    use crate::algos::support::mg_divide::div_wide_pow10;
4112    use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
4113    use crate::int::types::traits::BigInt;
4114    use crate::support::rounding::RoundingMode;
4115
4116    /// `(a · b) / 10^scale` through the unified pipeline, computed as
4117    /// `Int<N>::widen_mul::<Int<M>>` (full product into the wider type)
4118    /// then `div_wide_pow10::<Int<M>>`. The wider type's u128 magnitude
4119    /// width is read from its scratch carrier's [`ComputeLimbs`] buffer
4120    /// (`single_u128`) inside the divide — the `Limbs<N>` carrier IS the
4121    /// mechanism for the wider work intermediate, so no work-width const
4122    /// parameter is named. Returns the scaled wider-width quotient.
4123    fn scaled<const N: usize, const M: usize>(a: Int<N>, b: Int<N>, scale: u32) -> Int<M>
4124    where
4125        Limbs<N>: ComputeLimbs,
4126        Int<M>: BigInt,
4127        Limbs<M>: ComputeLimbs,
4128        <Int<M> as BigInt>::Scratch: ComputeLimbs,
4129    {
4130        let prod: Int<M> = a.widen_mul::<Int<M>>(b);
4131        div_wide_pow10::<Int<M>>(prod, scale, RoundingMode::HalfToEven)
4132    }
4133
4134    /// N = 2 → widen to N = 4, scale 5 (the plan's anchor case).
4135    #[test]
4136    fn n2_widen4_scale5() {
4137        let a = Int::<2>::from_i64(123456789);
4138        let b = Int::<2>::from_i64(987654321);
4139        let got = scaled::<2, 4>(a, b, 5);
4140        assert_eq!(got, Int::<4>::from_i64(1219326311126));
4141    }
4142
4143    /// N = 1 → widen to N = 2, scale 3 (the plan's anchor case).
4144    #[test]
4145    fn n1_widen2_scale3() {
4146        let a = Int::<1>::from_i64(123456);
4147        let b = Int::<1>::from_i64(654321);
4148        let got = scaled::<1, 2>(a, b, 3);
4149        assert_eq!(got, Int::<2>::from_i64(80779853));
4150    }
4151
4152    /// Round-trip: `(x · 10^k) / 10^k == x` at both narrow widths. The
4153    /// widen_mul forms `x · 10^k` exactly in the wider type and the MG
4154    /// divide undoes it with no residue.
4155    #[test]
4156    fn round_trip_mul_then_div() {
4157        // N = 1 → 2: x = 4242, k = 4.
4158        let x1 = 4242i64;
4159        let ten_pow_4 = Int::<1>::from_i64(10_000);
4160        let rt1 = scaled::<1, 2>(Int::<1>::from_i64(x1), ten_pow_4, 4);
4161        assert_eq!(rt1, Int::<2>::from_i64(x1));
4162
4163        // N = 2 → 4: x = 9_876_543_210, k = 7.
4164        let x2 = 9_876_543_210i64;
4165        let ten_pow_7 = Int::<2>::from_i64(10_000_000);
4166        let rt2 = scaled::<2, 4>(Int::<2>::from_i64(x2), ten_pow_7, 7);
4167        assert_eq!(rt2, Int::<4>::from_i64(x2));
4168    }
4169
4170    /// Scale-0 identity: callers short-circuit `scale == 0` as a no-op
4171    /// (`div_wide_pow10` is only ever invoked for `1..=38`), so the
4172    /// scaled value at scale 0 is exactly the full widen_mul product.
4173    /// This locks that contract for the narrow widths.
4174    #[test]
4175    fn scale_zero_is_widen_mul_identity() {
4176        // N = 1 → 2.
4177        let a1 = Int::<1>::from_i64(123456);
4178        let b1 = Int::<1>::from_i64(654321);
4179        let prod1: Int<2> = a1.widen_mul::<Int<2>>(b1);
4180        assert_eq!(prod1, Int::<2>::from_i64(123456 * 654321));
4181
4182        // N = 2 → 4.
4183        let a2 = Int::<2>::from_i64(123456789);
4184        let b2 = Int::<2>::from_i64(987654321);
4185        let prod2: Int<4> = a2.widen_mul::<Int<4>>(b2);
4186        assert_eq!(prod2, Int::<4>::from_i64(123456789i64 * 987654321i64));
4187    }
4188
4189    /// Max-operand case at each narrow width: the widest product the
4190    /// source width can hold must widen and scale without truncation.
4191    #[test]
4192    fn max_operand_each_width() {
4193        // N = 1: u64::MAX-ish operands. Use the largest i64 magnitudes
4194        // whose product the widen_mul (into N = 2) holds exactly, then
4195        // scale by 10^9 and check against the u128 reference.
4196        let a1 = Int::<1>::from_i64(i64::MAX);
4197        let b1 = Int::<1>::from_i64(i64::MAX);
4198        let got1 = scaled::<1, 2>(a1, b1, 9);
4199        let exact1 = (i64::MAX as i128) * (i64::MAX as i128);
4200        let pow9 = 1_000_000_000i128;
4201        let q1 = exact1 / pow9;
4202        let r1 = exact1 % pow9;
4203        // HalfToEven bump when the remainder is past the halfway point.
4204        let half = pow9 / 2;
4205        let expect1 = if r1 > half || (r1 == half && (q1 & 1) == 1) {
4206            q1 + 1
4207        } else {
4208            q1
4209        };
4210        assert_eq!(got1, Int::<2>::from_i128(expect1));
4211
4212        // N = 2: the most-positive signed operand (2^127 - 1) squared,
4213        // widened into N = 4, then scaled by 10^20. Compare against the
4214        // exact reference reconstructed from the full 256-bit product.
4215        let big = Int::<2>::MAX; // 2^127 - 1
4216        let got2 = scaled::<2, 4>(big, big, 20);
4217        let prod2: Int<4> = big.widen_mul::<Int<4>>(big);
4218        // Exact (2^127 - 1)^2 / 10^20, HalfToEven, via the wider type's
4219        // own div_rem against 10^20 built in Int<4>.
4220        let pow20 = Int::<4>::TEN.pow(20);
4221        let (q2, r2) = prod2.div_rem(pow20);
4222        let half20 = pow20.div_rem(Int::<4>::from_i64(2)).0;
4223        let expect2 = match r2.cmp(&half20) {
4224            core::cmp::Ordering::Greater => q2.wrapping_add(Int::<4>::ONE),
4225            core::cmp::Ordering::Equal => {
4226                if (q2.as_limbs()[0] & 1) == 1 {
4227                    q2.wrapping_add(Int::<4>::ONE)
4228                } else {
4229                    q2
4230                }
4231            }
4232            core::cmp::Ordering::Less => q2,
4233        };
4234        assert_eq!(got2, expect2);
4235    }
4236}
4237
4238#[cfg(test)]
4239mod bit_count_contract_tests {
4240    use super::{Int, Uint};
4241
4242    /// `Int<N>`'s four bit-count methods must match the `iN`
4243    /// two's-complement contract at every edge, and `bit_length` must
4244    /// stay magnitude-based. Where the value fits a primitive we assert
4245    /// against `i64`/`i128`; otherwise against the known answer.
4246    #[test]
4247    fn int_bit_counts_match_signed_contract_at_edges() {
4248        // ── Int<1> (64-bit): cross-check directly against i64. ──
4249        for v in [0_i64, 1, -1, i64::MAX, i64::MIN, 42, -42, 1 << 40, -(1 << 40)] {
4250            let x = Int::<1>::from_i64(v);
4251            assert_eq!(x.leading_zeros(), v.leading_zeros(), "i64 lz {v}");
4252            assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i64 tz {v}");
4253            assert_eq!(x.count_ones(), v.count_ones(), "i64 popcount {v}");
4254            assert_eq!(x.count_zeros(), v.count_zeros(), "i64 cz {v}");
4255            // bit_length is magnitude-based: significant bits of |v|.
4256            let mag_bits = 64 - v.unsigned_abs().leading_zeros();
4257            assert_eq!(x.bit_length(), mag_bits, "i64 bit_length {v}");
4258        }
4259
4260        // ── Int<2> (128-bit): cross-check directly against i128. ──
4261        for v in [
4262            0_i128,
4263            1,
4264            -1,
4265            i128::MAX,
4266            i128::MIN,
4267            (1_i128 << 64),       // 2^64 — limb boundary
4268            (1_i128 << 64) - 1,   // 2^64 - 1 — fills the low limb
4269            -(1_i128 << 64),
4270            i64::MAX as i128,
4271            i64::MIN as i128,
4272        ] {
4273            let x = Int::<2>::from_i128(v);
4274            assert_eq!(x.leading_zeros(), v.leading_zeros(), "i128 lz {v}");
4275            assert_eq!(x.trailing_zeros(), v.trailing_zeros(), "i128 tz {v}");
4276            assert_eq!(x.count_ones(), v.count_ones(), "i128 popcount {v}");
4277            assert_eq!(x.count_zeros(), v.count_zeros(), "i128 cz {v}");
4278            let mag_bits = 128 - v.unsigned_abs().leading_zeros();
4279            assert_eq!(x.bit_length(), mag_bits, "i128 bit_length {v}");
4280        }
4281
4282        // ── Int<4> (256-bit): values beyond i128 — assert the known
4283        //    two's-complement answer directly. ──
4284        const B: u32 = Int::<4>::BITS; // 256
4285
4286        // Zero: BITS leading zeros, BITS trailing zeros, no ones,
4287        // BITS clear bits, zero magnitude bits.
4288        let z = Int::<4>::ZERO;
4289        assert_eq!(z.leading_zeros(), B);
4290        assert_eq!(z.trailing_zeros(), B);
4291        assert_eq!(z.count_ones(), 0);
4292        assert_eq!(z.count_zeros(), B);
4293        assert_eq!(z.bit_length(), 0);
4294
4295        // One.
4296        let one = Int::<4>::ONE;
4297        assert_eq!(one.leading_zeros(), B - 1);
4298        assert_eq!(one.trailing_zeros(), 0);
4299        assert_eq!(one.count_ones(), 1);
4300        assert_eq!(one.count_zeros(), B - 1);
4301        assert_eq!(one.bit_length(), 1);
4302
4303        // -1 == all ones in two's complement: sign bit set so zero
4304        // leading zeros, every bit a one, zero trailing zeros.
4305        // Magnitude is 1, so bit_length is 1.
4306        let neg_one = Int::<4>::from_i64(-1);
4307        assert_eq!(*neg_one.as_limbs(), [u64::MAX; 4]);
4308        assert_eq!(neg_one.leading_zeros(), 0);
4309        assert_eq!(neg_one.trailing_zeros(), 0);
4310        assert_eq!(neg_one.count_ones(), B);
4311        assert_eq!(neg_one.count_zeros(), 0);
4312        assert_eq!(neg_one.bit_length(), 1);
4313
4314        // MAX == 0b0111…1: top bit clear, the rest set.
4315        let max = Int::<4>::MAX;
4316        assert_eq!(max.leading_zeros(), 1);
4317        assert_eq!(max.trailing_zeros(), 0);
4318        assert_eq!(max.count_ones(), B - 1);
4319        assert_eq!(max.count_zeros(), 1);
4320        assert_eq!(max.bit_length(), B - 1); // magnitude is 2^(B-1) - 1
4321
4322        // MIN == 0b1000…0: only the sign bit set. Negative → zero
4323        // leading zeros; the single set bit is the top one, so
4324        // trailing_zeros == BITS-1; magnitude |MIN| == 2^(B-1).
4325        let min = Int::<4>::MIN;
4326        assert_eq!(min.leading_zeros(), 0);
4327        assert_eq!(min.trailing_zeros(), B - 1);
4328        assert_eq!(min.count_ones(), 1);
4329        assert_eq!(min.count_zeros(), B - 1);
4330        assert_eq!(min.bit_length(), B); // |MIN| = 2^(B-1) ⇒ B significant bits
4331
4332        // Limb-boundary magnitudes: 2^64 and 2^64 - 1 in Int<4>.
4333        let two_64 = Int::<4>::from_u128(1u128 << 64);
4334        assert_eq!(two_64.trailing_zeros(), 64);
4335        assert_eq!(two_64.count_ones(), 1);
4336        assert_eq!(two_64.leading_zeros(), B - 65);
4337        assert_eq!(two_64.bit_length(), 65);
4338
4339        let two_64_m1 = Int::<4>::from_u128((1u128 << 64) - 1);
4340        assert_eq!(two_64_m1.trailing_zeros(), 0);
4341        assert_eq!(two_64_m1.count_ones(), 64);
4342        assert_eq!(two_64_m1.leading_zeros(), B - 64);
4343        assert_eq!(two_64_m1.bit_length(), 64);
4344    }
4345
4346    /// `Uint<N>`'s bit-count surface (`leading_zeros`, `count_ones`,
4347    /// `bit_length`) must match the `uN` contract at the edges.
4348    #[test]
4349    fn uint_bit_counts_match_unsigned_contract_at_edges() {
4350        // ── Uint<1> (64-bit): cross-check against u64. ──
4351        for v in [0_u64, 1, u64::MAX, 42, 1 << 40] {
4352            let x = Uint::<1>::from_u64(v);
4353            assert_eq!(x.leading_zeros(), v.leading_zeros(), "u64 lz {v}");
4354            assert_eq!(x.count_ones(), v.count_ones(), "u64 popcount {v}");
4355            // For unsigned, bit_length == BITS - leading_zeros.
4356            assert_eq!(x.bit_length(), 64 - v.leading_zeros(), "u64 bit_length {v}");
4357        }
4358
4359        // ── Uint<2> (128-bit): cross-check against u128, incl. limb
4360        //    boundary 2^64 / 2^64 - 1. ──
4361        for v in [0_u128, 1, u128::MAX, 1u128 << 64, (1u128 << 64) - 1] {
4362            let x = Uint::<2>::from_u128(v);
4363            assert_eq!(x.leading_zeros(), v.leading_zeros(), "u128 lz {v}");
4364            assert_eq!(x.count_ones(), v.count_ones(), "u128 popcount {v}");
4365            assert_eq!(x.bit_length(), 128 - v.leading_zeros(), "u128 bit_length {v}");
4366        }
4367
4368        // ── Uint<4> (256-bit): values beyond u128 — known answers. ──
4369        const B: u32 = Uint::<4>::BITS; // 256
4370        let zero = Uint::<4>::ZERO;
4371        assert_eq!(zero.leading_zeros(), B);
4372        assert_eq!(zero.count_ones(), 0);
4373        assert_eq!(zero.bit_length(), 0);
4374
4375        // All-ones (Uint MAX): no leading zeros, every bit set.
4376        let umax = Uint::<4>::from_limbs([u64::MAX; 4]);
4377        assert_eq!(umax.leading_zeros(), 0);
4378        assert_eq!(umax.count_ones(), B);
4379        assert_eq!(umax.bit_length(), B);
4380
4381        // 2^64 across the low limb boundary.
4382        let two_64 = Uint::<4>::from_u128(1u128 << 64);
4383        assert_eq!(two_64.count_ones(), 1);
4384        assert_eq!(two_64.leading_zeros(), B - 65);
4385        assert_eq!(two_64.bit_length(), 65);
4386    }
4387}
4388
4389/// Behavioural sanity checks for the const-generic `Int<N>` / `Uint<N>`
4390/// public surface, exercising the limb kernels end-to-end through the
4391/// type API (add/sub/neg, mul/div/rem, overflow, parse/pow, ordering /
4392/// resize, isqrt / f64, the unsigned bit helpers, and `as_u128`).
4393#[cfg(test)]
4394mod hint_tests {
4395    use super::{Int, Uint};
4396
4397    #[test]
4398    fn signed_add_sub_neg() {
4399        let a = Int::<4>::from_i128(5);
4400        let b = Int::<4>::from_i128(3);
4401        assert_eq!(a.wrapping_add(b), Int::<4>::from_i128(8));
4402        assert_eq!(a.wrapping_sub(b), Int::<4>::from_i128(2));
4403        assert_eq!(b.wrapping_sub(a), Int::<4>::from_i128(-2));
4404        assert_eq!(a.negate(), Int::<4>::from_i128(-5));
4405        assert_eq!(Int::<4>::ZERO.negate(), Int::<4>::ZERO);
4406    }
4407
4408    #[test]
4409    fn signed_mul_div_rem() {
4410        let six = Int::<8>::from_i128(6);
4411        let two = Int::<8>::from_i128(2);
4412        let three = Int::<8>::from_i128(3);
4413        assert_eq!(six.wrapping_mul(three), Int::<8>::from_i128(18));
4414        assert_eq!(six.wrapping_div(two), three);
4415        assert_eq!(
4416            Int::<8>::from_i128(7).wrapping_rem(three),
4417            Int::<8>::from_i128(1)
4418        );
4419        assert_eq!(
4420            Int::<8>::from_i128(-7).wrapping_rem(three),
4421            Int::<8>::from_i128(-1)
4422        );
4423        assert_eq!(six.negate().wrapping_mul(three), Int::<8>::from_i128(-18));
4424    }
4425
4426    #[test]
4427    fn checked_overflow() {
4428        assert_eq!(Int::<4>::MAX.checked_add(Int::<4>::ONE), None);
4429        assert_eq!(Int::<4>::MIN.checked_sub(Int::<4>::ONE), None);
4430        assert_eq!(Int::<4>::MIN.checked_neg(), None);
4431        assert_eq!(
4432            Int::<4>::from_i128(2).checked_add(Int::<4>::from_i128(3)),
4433            Some(Int::<4>::from_i128(5))
4434        );
4435    }
4436
4437    #[test]
4438    fn from_str_and_pow() {
4439        let ten = Int::<16>::from_str_radix("10", 10).unwrap();
4440        assert_eq!(ten, Int::<16>::from_i128(10));
4441        assert_eq!(ten.pow(3), Int::<16>::from_i128(1000));
4442        let big = Int::<16>::from_str_radix("10", 10).unwrap().pow(40);
4443        let from_str =
4444            Int::<16>::from_str_radix("10000000000000000000000000000000000000000", 10).unwrap();
4445        assert_eq!(big, from_str);
4446        assert_eq!(
4447            Int::<4>::from_str_radix("-42", 10).unwrap(),
4448            Int::<4>::from_i128(-42)
4449        );
4450    }
4451
4452    #[test]
4453    fn ordering_and_resize() {
4454        assert!(Int::<4>::from_i128(-1) < Int::<4>::ZERO);
4455        assert!(Int::<4>::MIN < Int::<4>::MAX);
4456        let v = Int::<4>::from_i128(-123_456_789);
4457        let wide: Int<16> = v.resize();
4458        let back: Int<4> = wide.resize();
4459        assert_eq!(back, v);
4460        assert_eq!(wide, Int::<16>::from_i128(-123_456_789));
4461    }
4462
4463    // Int<8> isqrt (work width 9) exceeds the narrow build's int scratch.
4464    #[cfg(feature = "_wide-support")]
4465    #[test]
4466    fn isqrt_and_f64() {
4467        assert_eq!(Int::<8>::from_i128(144).isqrt(), Int::<8>::from_i128(12));
4468        assert_eq!(Int::<4>::from_i128(1_000_000).as_f64(), 1_000_000.0);
4469        assert_eq!(Int::<4>::from_f64(-2_500.0), Int::<4>::from_i128(-2500));
4470    }
4471
4472    /// `Uint<4>` (the unsigned macro emission) supports the same
4473    /// bit/sign-manipulation surface as the signed sibling.
4474    #[test]
4475    fn uint256_is_zero_and_bit_helpers() {
4476        let zero = Uint::<4>::ZERO;
4477        let one = Uint::<4>::from_str_radix("1", 10).unwrap();
4478        let two = Uint::<4>::from_str_radix("2", 10).unwrap();
4479        assert!(zero.is_zero());
4480        assert!(!one.is_zero());
4481        assert!(one.is_power_of_two());
4482        assert!(two.is_power_of_two());
4483        let three = Uint::<4>::from_str_radix("3", 10).unwrap();
4484        assert!(!three.is_power_of_two());
4485        assert_eq!(zero.next_power_of_two(), one);
4486        assert_eq!(one.next_power_of_two(), one);
4487        let four = Uint::<4>::from_str_radix("4", 10).unwrap();
4488        assert_eq!(three.next_power_of_two(), four);
4489        assert_eq!(zero.count_ones(), 0);
4490        assert_eq!(one.count_ones(), 1);
4491        assert_eq!(zero.leading_zeros(), Uint::<4>::BITS);
4492        assert_eq!(one.leading_zeros(), Uint::<4>::BITS - 1);
4493    }
4494
4495    #[test]
4496    fn uint256_parse_arithmetic_and_pow() {
4497        assert!(Uint::<4>::from_str_radix("10", 2).is_err());
4498        assert!(Uint::<4>::from_str_radix("1a", 10).is_err());
4499        let two = Uint::<4>::from_str_radix("2", 10).unwrap();
4500        let three = Uint::<4>::from_str_radix("3", 10).unwrap();
4501        let six = Uint::<4>::from_str_radix("6", 10).unwrap();
4502        let seven = Uint::<4>::from_str_radix("7", 10).unwrap();
4503        assert_eq!(three - two, Uint::<4>::from_str_radix("1", 10).unwrap());
4504        assert_eq!(six / two, three);
4505        assert_eq!(seven % three, Uint::<4>::from_str_radix("1", 10).unwrap());
4506        let five = Uint::<4>::from_str_radix("5", 10).unwrap();
4507        let four = Uint::<4>::from_str_radix("4", 10).unwrap();
4508        let one = Uint::<4>::from_str_radix("1", 10).unwrap();
4509        assert_eq!(five & four, four);
4510        assert_eq!(five | one, five);
4511        assert_eq!(five ^ four, one);
4512        let p10 = two.pow(10);
4513        assert_eq!(p10, Uint::<4>::from_str_radix("1024", 10).unwrap());
4514        let signed = three.cast_signed();
4515        assert_eq!(signed, Int::<4>::from_i128(3));
4516    }
4517
4518    /// `Int::<4>::bit` reports the two's-complement bit at any index.
4519    #[test]
4520    fn signed_bit_and_trailing_zeros() {
4521        let v = Int::<4>::from_i128(0b1100);
4522        assert!(v.bit(2));
4523        assert!(v.bit(3));
4524        assert!(!v.bit(0));
4525        assert!(!v.bit(1));
4526        assert!(!v.bit(1000));
4527        let n = Int::<4>::from_i128(-1);
4528        assert!(n.bit(1000));
4529        assert_eq!(Int::<4>::from_i128(8).trailing_zeros(), 3);
4530        assert_eq!(Int::<4>::ZERO.trailing_zeros(), Int::<4>::BITS);
4531    }
4532
4533    /// `Int::<4>::as_u128` returns the low 128 magnitude bits.
4534    #[test]
4535    fn as_u128_returns_low_magnitude_bits() {
4536        let src = Int::<4>::from_i128(123_456_789);
4537        let dst: u128 = src.as_u128();
4538        assert_eq!(dst, 123_456_789);
4539        let dst: u128 = Int::<4>::ZERO.as_u128();
4540        assert_eq!(dst, 0);
4541    }
4542}