Skip to main content

malachite_bigint/
bigint.rs

1// Copyright © 2026 Steve Shi
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use alloc::string::String;
10use alloc::vec::Vec;
11use core::{
12    cmp::Ordering,
13    fmt::Debug,
14    iter::{Product, Sum},
15    ops::{
16        Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div,
17        DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
18        SubAssign,
19    },
20    str::FromStr,
21};
22use malachite_base::{
23    num::{
24        arithmetic::traits::{
25            Abs, DivRem, DivRound, DivisibleBy, FloorRoot, Mod, Parity, UnsignedAbs,
26        },
27        conversion::traits::{RoundingInto, ToStringBase},
28        logic::traits::BitAccess,
29    },
30    rounding_modes::RoundingMode,
31};
32use malachite_nz::integer::Integer;
33use num_integer::Roots;
34use num_traits::{
35    CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, Num, One, Pow, Signed,
36    ToPrimitive, Zero,
37};
38use paste::paste;
39
40use crate::{
41    BigUint, ParseBigIntError,
42    Sign::{Minus, NoSign, Plus},
43    ToBigUint, TryFromBigIntError, U32Digits, U64Digits,
44};
45
46pub trait ToBigInt {
47    fn to_bigint(&self) -> Option<BigInt>;
48}
49
50apply_to_primitives!(impl_primitive_convert{BigInt, _});
51impl_primitive_convert!(BigInt, f32);
52impl_primitive_convert!(BigInt, f64);
53
54#[derive(PartialEq, PartialOrd, Eq, Ord, Copy, Clone, Debug, Hash)]
55pub enum Sign {
56    Minus,
57    NoSign,
58    Plus,
59}
60
61impl Neg for Sign {
62    type Output = Self;
63
64    #[inline]
65    fn neg(self) -> Self::Output {
66        match self {
67            Minus => Plus,
68            NoSign => NoSign,
69            Plus => Minus,
70        }
71    }
72}
73
74#[repr(transparent)]
75#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
76pub struct BigInt(Integer);
77
78apply_to_primitives!(forward_from{BigInt, _});
79apply_to_primitives!(forward_try_into{BigInt, _});
80
81impl_from!(BigInt, Integer);
82
83forward_unary_op!(BigInt, Not, not);
84forward_unary_op!(BigInt, Neg, neg);
85
86forward_binary_self!(BigInt, Add, add);
87forward_binary_self!(BigInt, Sub, sub);
88forward_binary_self!(BigInt, Mul, mul);
89forward_binary_self!(BigInt, Div, div);
90forward_binary_self!(BigInt, Rem, rem);
91forward_binary_self!(BigInt, BitAnd, bitand);
92forward_binary_self!(BigInt, BitOr, bitor);
93forward_binary_self!(BigInt, BitXor, bitxor);
94
95forward_assign_self!(BigInt, AddAssign, add_assign);
96forward_assign_self!(BigInt, SubAssign, sub_assign);
97forward_assign_self!(BigInt, MulAssign, mul_assign);
98forward_assign_self!(BigInt, DivAssign, div_assign);
99forward_assign_self!(BigInt, RemAssign, rem_assign);
100forward_assign_self!(BigInt, BitAndAssign, bitand_assign);
101forward_assign_self!(BigInt, BitOrAssign, bitor_assign);
102forward_assign_self!(BigInt, BitXorAssign, bitxor_assign);
103
104forward_pow_biguint!(BigInt);
105
106apply_to_primitives!(forward_binary_right_primitive_into{BigInt, _, Add, add});
107apply_to_primitives!(forward_binary_right_primitive_into{BigInt, _, Sub, sub});
108apply_to_primitives!(forward_binary_right_primitive_into{BigInt, _, Mul, mul});
109apply_to_primitives!(forward_binary_right_primitive_into{BigInt, _, Div, div});
110apply_to_primitives!(forward_binary_right_primitive_into{BigInt, _, Rem, rem});
111
112apply_to_primitives!(forward_binary_left_primitive_into{_, BigInt, Add, add});
113apply_to_primitives!(forward_binary_left_primitive_into{_, BigInt, Sub, sub});
114apply_to_primitives!(forward_binary_left_primitive_into{_, BigInt, Mul, mul});
115apply_to_primitives!(forward_binary_left_primitive_into{_, BigInt, Div, div});
116apply_to_primitives!(forward_binary_left_primitive_into{_, BigInt, Rem, rem});
117
118apply_to_primitives!(forward_binary_right_primitive{BigInt, _, Shl, shl});
119apply_to_primitives!(forward_binary_right_primitive{BigInt, _, Shr, shr});
120
121apply_to_primitives!(forward_assign_primitive_into{BigInt, _, AddAssign, add_assign});
122apply_to_primitives!(forward_assign_primitive_into{BigInt, _, SubAssign, sub_assign});
123apply_to_primitives!(forward_assign_primitive_into{BigInt, _, MulAssign, mul_assign});
124apply_to_primitives!(forward_assign_primitive_into{BigInt, _, DivAssign, div_assign});
125apply_to_primitives!(forward_assign_primitive_into{BigInt, _, RemAssign, rem_assign});
126
127apply_to_primitives!(forward_assign_primitive{BigInt, _, ShlAssign, shl_assign});
128apply_to_primitives!(forward_assign_primitive{BigInt, _, ShrAssign, shr_assign});
129
130apply_to_unsigneds!(forward_pow_primitive{BigInt, _});
131
132impl_product_iter_type!(BigInt);
133impl_sum_iter_type!(BigInt);
134
135forward_fmt!(BigInt, Debug, Display, Binary, Octal, LowerHex, UpperHex);
136
137impl CheckedAdd for BigInt {
138    #[inline]
139    fn checked_add(&self, v: &Self) -> Option<Self> {
140        Some(self.add(v))
141    }
142}
143
144impl CheckedSub for BigInt {
145    #[inline]
146    fn checked_sub(&self, v: &Self) -> Option<Self> {
147        Some(self.sub(v))
148    }
149}
150
151impl CheckedMul for BigInt {
152    #[inline]
153    fn checked_mul(&self, v: &Self) -> Option<Self> {
154        Some(self.mul(v))
155    }
156}
157
158impl CheckedDiv for BigInt {
159    #[inline]
160    fn checked_div(&self, v: &Self) -> Option<Self> {
161        (!v.is_zero()).then(|| self.div(v))
162    }
163}
164
165impl ToBigInt for BigInt {
166    #[inline]
167    fn to_bigint(&self) -> Option<BigInt> {
168        Some(self.clone())
169    }
170}
171
172impl ToBigUint for BigInt {
173    #[inline]
174    fn to_biguint(&self) -> Option<BigUint> {
175        (!self.is_negative()).then(|| self.magnitude().clone())
176    }
177}
178
179impl ToPrimitive for BigInt {
180    apply_to_primitives!(impl_to_primitive_fn_try_into{_});
181    impl_to_primitive_fn_float!(f32);
182    impl_to_primitive_fn_float!(f64);
183}
184
185impl FromPrimitive for BigInt {
186    apply_to_primitives!(impl_from_primitive_fn_infallible{_});
187    impl_from_primitive_fn_float!(f32);
188    impl_from_primitive_fn_float!(f64);
189}
190
191impl From<BigUint> for BigInt {
192    #[inline]
193    fn from(value: BigUint) -> Self {
194        Integer::from(value.0).into()
195    }
196}
197
198impl Zero for BigInt {
199    #[inline]
200    fn zero() -> Self {
201        Self(<Integer as malachite_base::num::basic::traits::Zero>::ZERO)
202    }
203
204    #[inline]
205    fn is_zero(&self) -> bool {
206        self.sign() == NoSign
207    }
208}
209
210impl One for BigInt {
211    #[inline]
212    fn one() -> Self {
213        Self(<Integer as malachite_base::num::basic::traits::One>::ONE)
214    }
215}
216
217impl Signed for BigInt {
218    #[inline]
219    fn abs(&self) -> Self {
220        (&self.0).abs().into()
221    }
222
223    #[inline]
224    fn abs_sub(&self, other: &Self) -> Self {
225        if self <= other {
226            Self::zero()
227        } else {
228            self - other
229        }
230    }
231
232    #[inline]
233    fn signum(&self) -> Self {
234        match self.sign() {
235            Minus => -Self::one(),
236            NoSign => Self::zero(),
237            Plus => Self::one(),
238        }
239    }
240
241    #[inline]
242    fn is_positive(&self) -> bool {
243        self.sign() == Plus
244    }
245
246    #[inline]
247    fn is_negative(&self) -> bool {
248        self.sign() == Minus
249    }
250}
251
252impl Num for BigInt {
253    type FromStrRadixErr = ParseBigIntError;
254
255    #[inline]
256    fn from_str_radix(mut s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
257        let sign = if s.starts_with('-') {
258            let tail = &s[1..];
259            if !tail.starts_with('+') {
260                s = tail;
261            }
262            Minus
263        } else {
264            Plus
265        };
266        let u = BigUint::from_str_radix(s, radix)?;
267        Ok(Self::from_biguint(sign, u))
268    }
269}
270
271impl num_integer::Integer for BigInt {
272    #[inline]
273    fn div_floor(&self, other: &Self) -> Self {
274        (&self.0).div_round(&other.0, RoundingMode::Floor).0.into()
275    }
276
277    #[inline]
278    fn mod_floor(&self, other: &Self) -> Self {
279        (&self.0).mod_op(&other.0).into()
280    }
281
282    #[inline]
283    fn gcd(&self, other: &Self) -> Self {
284        self.magnitude().gcd(other.magnitude()).into()
285    }
286
287    #[inline]
288    fn lcm(&self, other: &Self) -> Self {
289        self.magnitude().lcm(other.magnitude()).into()
290    }
291
292    #[inline]
293    fn divides(&self, other: &Self) -> bool {
294        Self::is_multiple_of(self, other)
295    }
296
297    #[inline]
298    fn is_multiple_of(&self, other: &Self) -> bool {
299        (&self.0).divisible_by(&other.0)
300    }
301
302    #[inline]
303    fn is_even(&self) -> bool {
304        self.0.even()
305    }
306
307    #[inline]
308    fn is_odd(&self) -> bool {
309        self.0.odd()
310    }
311
312    #[inline]
313    fn div_rem(&self, other: &Self) -> (Self, Self) {
314        let (div, rem) = (&self.0).div_rem(&other.0);
315        (div.into(), rem.into())
316    }
317}
318
319impl Roots for BigInt {
320    #[inline]
321    fn nth_root(&self, n: u32) -> Self {
322        (&self.0).floor_root(u64::from(n)).into()
323    }
324}
325
326impl FromStr for BigInt {
327    type Err = ParseBigIntError;
328
329    #[inline]
330    fn from_str(s: &str) -> Result<Self, Self::Err> {
331        Self::from_str_radix(s, 10)
332    }
333}
334
335impl BigInt {
336    #[inline]
337    pub fn new(sign: Sign, digits: Vec<u32>) -> Self {
338        Self::from_biguint(sign, BigUint::new(digits))
339    }
340
341    #[inline]
342    pub fn from_biguint(sign: Sign, mut abs: BigUint) -> Self {
343        if sign == NoSign {
344            abs = BigUint::zero();
345        }
346
347        Integer::from_sign_and_abs(sign != Minus, abs.0).into()
348    }
349
350    #[inline]
351    pub fn from_slice(sign: Sign, slice: &[u32]) -> Self {
352        Self::from_biguint(sign, BigUint::from_slice(slice))
353    }
354
355    #[inline]
356    pub fn assign_from_slice(&mut self, sign: Sign, slice: &[u32]) {
357        if sign == NoSign {
358            self.set_zero();
359        } else {
360            *self = Self::from_slice(sign, slice);
361        }
362    }
363
364    #[inline]
365    pub fn from_bytes_be(sign: Sign, bytes: &[u8]) -> Self {
366        Self::from_biguint(sign, BigUint::from_bytes_be(bytes))
367    }
368
369    #[inline]
370    pub fn from_bytes_le(sign: Sign, bytes: &[u8]) -> Self {
371        Self::from_biguint(sign, BigUint::from_bytes_le(bytes))
372    }
373
374    #[inline]
375    pub fn from_signed_bytes_be(digits: &[u8]) -> Self {
376        let is_negative = match digits.first().copied() {
377            Some(x) => x > 0x7f,
378            None => return Self::zero(),
379        };
380
381        if is_negative {
382            let mut v = Vec::from(digits);
383            twos_complement_be(&mut v);
384            let u = BigUint::from_bytes_be(v.as_slice());
385            Self::from_biguint(Minus, u)
386        } else {
387            let u = BigUint::from_bytes_be(digits);
388            Self::from_biguint(Plus, u)
389        }
390    }
391
392    #[inline]
393    pub fn from_signed_bytes_le(digits: &[u8]) -> Self {
394        let is_negative = match digits.last().copied() {
395            Some(x) => x > 0x7f,
396            None => return Self::zero(),
397        };
398
399        if is_negative {
400            let mut v = Vec::from(digits);
401            twos_complement_le(&mut v);
402            let u = BigUint::from_bytes_le(v.as_slice());
403            Self::from_biguint(Minus, u)
404        } else {
405            let u = BigUint::from_bytes_le(digits);
406            Self::from_biguint(Plus, u)
407        }
408    }
409
410    #[inline]
411    pub fn parse_bytes(bytes: &[u8], radix: u32) -> Option<Self> {
412        let s = core::str::from_utf8(bytes).ok()?;
413        Self::from_str_radix(s, radix).ok()
414    }
415
416    #[inline]
417    pub fn from_radix_be(sign: Sign, buf: &[u8], radix: u32) -> Option<Self> {
418        BigUint::from_radix_be(buf, radix).map(|u| Self::from_biguint(sign, u))
419    }
420
421    #[inline]
422    pub fn from_radix_le(sign: Sign, buf: &[u8], radix: u32) -> Option<Self> {
423        BigUint::from_radix_le(buf, radix).map(|u| Self::from_biguint(sign, u))
424    }
425
426    #[inline]
427    pub fn to_bytes_be(&self) -> (Sign, Vec<u8>) {
428        (self.sign(), self.magnitude().to_bytes_be())
429    }
430
431    #[inline]
432    pub fn to_bytes_le(&self) -> (Sign, Vec<u8>) {
433        (self.sign(), self.magnitude().to_bytes_le())
434    }
435
436    #[inline]
437    pub fn to_u32_digits(&self) -> (Sign, Vec<u32>) {
438        (self.sign(), self.magnitude().to_u32_digits())
439    }
440
441    #[inline]
442    pub fn to_u64_digits(&self) -> (Sign, Vec<u64>) {
443        (self.sign(), self.magnitude().to_u64_digits())
444    }
445
446    #[inline]
447    pub fn iter_u32_digits(&self) -> U32Digits<'_> {
448        self.magnitude().iter_u32_digits()
449    }
450
451    #[inline]
452    pub fn iter_u64_digits(&self) -> U64Digits<'_> {
453        self.magnitude().iter_u64_digits()
454    }
455
456    #[inline]
457    pub fn to_signed_bytes_be(&self) -> Vec<u8> {
458        let mut bytes = self.magnitude().to_bytes_be();
459        let first_byte = bytes.first().copied().unwrap_or(0);
460        let is_negative = self.is_negative();
461        if first_byte > 0x7f
462            && !(first_byte == 0x80 && bytes.iter().skip(1).all(Zero::is_zero) && is_negative)
463        {
464            // msb used by magnitude, extend by 1 byte
465            bytes.insert(0, 0);
466        }
467        if self.is_negative() {
468            twos_complement_be(&mut bytes);
469        }
470        bytes
471    }
472
473    #[inline]
474    pub fn to_signed_bytes_le(&self) -> Vec<u8> {
475        let mut bytes = self.magnitude().to_bytes_le();
476        let is_negative = self.is_negative();
477        let last_byte = bytes.last().copied().unwrap_or(0);
478        if last_byte > 0x7f
479            && !(last_byte == 0x80 && bytes.iter().rev().skip(1).all(Zero::is_zero) && is_negative)
480        {
481            // msb used by magnitude, extend by 1 byte
482            bytes.push(0);
483        }
484        if self.is_negative() {
485            twos_complement_le(&mut bytes);
486        }
487        bytes
488    }
489
490    #[inline]
491    pub fn to_str_radix(&self, radix: u32) -> String {
492        self.0.to_string_base(radix as u8)
493    }
494
495    #[inline]
496    pub fn to_radix_be(&self, radix: u32) -> (Sign, Vec<u8>) {
497        (self.sign(), self.magnitude().to_radix_be(radix))
498    }
499
500    #[inline]
501    pub fn to_radix_le(&self, radix: u32) -> (Sign, Vec<u8>) {
502        (self.sign(), self.magnitude().to_radix_le(radix))
503    }
504
505    #[inline]
506    pub fn sign(&self) -> Sign {
507        match <_ as malachite_base::num::arithmetic::traits::Sign>::sign(&self.0) {
508            Ordering::Less => Minus,
509            Ordering::Equal => NoSign,
510            Ordering::Greater => Plus,
511        }
512    }
513
514    #[inline]
515    pub fn magnitude(&self) -> &BigUint {
516        unsafe { core::mem::transmute(self.0.unsigned_abs_ref()) }
517    }
518
519    #[inline]
520    pub fn into_parts(self) -> (Sign, BigUint) {
521        (self.sign(), self.0.unsigned_abs().into())
522    }
523
524    #[inline]
525    pub fn bits(&self) -> u64 {
526        self.magnitude().bits()
527    }
528
529    #[inline]
530    pub fn to_biguint(&self) -> Option<BigUint> {
531        match self.sign() {
532            Plus => Some(self.magnitude().clone()),
533            NoSign => Some(BigUint::zero()),
534            Minus => None,
535        }
536    }
537
538    #[inline]
539    pub fn checked_add(&self, v: &Self) -> Option<Self> {
540        Some(self + v)
541    }
542
543    #[inline]
544    pub fn checked_sub(&self, v: &Self) -> Option<Self> {
545        Some(self - v)
546    }
547
548    #[inline]
549    pub fn checked_mul(&self, v: &Self) -> Option<Self> {
550        Some(self * v)
551    }
552
553    #[inline]
554    pub fn checked_div(&self, v: &Self) -> Option<Self> {
555        if v.is_zero() {
556            return None;
557        }
558        Some(self / v)
559    }
560
561    #[inline]
562    pub fn pow(&self, exponent: u32) -> Self {
563        Pow::pow(self, exponent)
564    }
565
566    #[inline]
567    pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
568        assert!(
569            !exponent.is_negative(),
570            "negative exponentiation is not supported!"
571        );
572        assert!(
573            !modulus.is_zero(),
574            "attempt to calculate with zero modulus!"
575        );
576
577        let mut abs = self
578            .magnitude()
579            .modpow(exponent.magnitude(), modulus.magnitude());
580
581        if abs.is_zero() {
582            return Self::zero();
583        }
584
585        if (self.is_negative() && exponent.0.odd()) != modulus.is_negative() {
586            abs = modulus.magnitude() - abs;
587        }
588
589        Self::from_biguint(modulus.sign(), abs)
590    }
591
592    #[inline]
593    pub fn sqrt(&self) -> Self {
594        Roots::sqrt(self)
595    }
596
597    #[inline]
598    pub fn cbrt(&self) -> Self {
599        Roots::cbrt(self)
600    }
601
602    #[inline]
603    pub fn nth_root(&self, n: u32) -> Self {
604        Roots::nth_root(self, n)
605    }
606
607    #[inline]
608    pub fn trailing_zeros(&self) -> Option<u64> {
609        self.magnitude().trailing_zeros()
610    }
611
612    #[inline]
613    pub fn bit(&self, bit: u64) -> bool {
614        self.0.get_bit(bit)
615    }
616
617    #[inline]
618    pub fn set_bit(&mut self, bit: u64, value: bool) {
619        if value {
620            self.0.set_bit(bit);
621        } else {
622            self.0.clear_bit(bit);
623        }
624    }
625}
626/// Perform in-place two's complement of the given binary representation, in little-endian byte
627/// order.
628#[inline]
629fn twos_complement_le(digits: &mut [u8]) {
630    twos_complement(digits);
631}
632
633/// Perform in-place two's complement of the given binary representation in big-endian byte order.
634#[inline]
635fn twos_complement_be(digits: &mut [u8]) {
636    twos_complement(digits.iter_mut().rev());
637}
638
639/// Perform in-place two's complement of the given digit iterator starting from the least
640/// significant byte.
641#[inline]
642fn twos_complement<'a, I>(digits: I)
643where
644    I: IntoIterator<Item = &'a mut u8>,
645{
646    let mut carry = true;
647    for d in digits {
648        *d = !*d;
649        if carry {
650            *d = d.wrapping_add(1);
651            carry = d.is_zero();
652        }
653    }
654}
655
656#[cfg(test)]
657mod test {
658    use super::*;
659    use alloc::{format, string::ToString};
660
661    #[test]
662    fn test_float_convert_nearest() {
663        let n25 = "10000000000000000000000000";
664        let val = BigInt::from_str(n25).unwrap();
665        let f = val.to_f64().unwrap();
666        assert_eq!(f.to_string(), n25);
667    }
668
669    #[test]
670    fn test_to_f64() {
671        use num_traits::ToPrimitive as NumToPrimitive;
672
673        let test_cases = [
674            "123456789012345678901234567890",
675            "999999999999999999999999999999",
676            "170141183460469231731687303715884105727",
677            "340282366920938463463374607431768211455",
678            "12345678901234567890123456789012345678901234567890",
679            "-123456789012345678901234567890",
680            "-999999999999999999999999999999",
681            "-170141183460469231731687303715884105728",
682            "-12345678901234567890123456789012345678901234567890",
683            "1208925819614629174706176",
684            "1329227995784915872903807060280344576",
685            "-1208925819614629174706176",
686            "-1329227995784915872903807060280344576",
687            // Overflow cases
688            "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
689            99999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
690            99999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
691            9999999999999999999999999999999999999999999999999999",
692            "-999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
693            99999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
694            99999999999999999999999999999999999999999999999999999999999999999999999999999999999999\
695            99999999999999999999999999999999999999999999999999999",
696        ];
697
698        for test_str in &test_cases {
699            let malachite_val = BigInt::from_str(test_str).unwrap();
700            let num_bigint_val = num_bigint::BigInt::from_str(test_str).unwrap();
701
702            assert_eq!(
703                malachite_val.to_f64(),
704                num_bigint_val.to_f64(),
705                "to_f64 mismatch for {test_str}",
706            );
707        }
708    }
709
710    #[test]
711    fn test_to_f32() {
712        use num_traits::ToPrimitive as NumToPrimitive;
713
714        let test_cases = [
715            "12345678901234567890",
716            "999999999999999999999999",
717            "-12345678901234567890",
718            "-999999999999999999999999",
719            "340282366920938463463374607431768211455",
720        ];
721
722        for test_str in &test_cases {
723            let malachite_val = BigInt::from_str(test_str).unwrap();
724            let num_bigint_val = num_bigint::BigInt::from_str(test_str).unwrap();
725
726            assert_eq!(
727                malachite_val.to_f32(),
728                num_bigint_val.to_f32(),
729                "to_f32 mismatch for {test_str}",
730            );
731        }
732    }
733
734    #[test]
735    fn test_to_i64() {
736        use num_traits::ToPrimitive as NumToPrimitive;
737
738        let test_cases = [
739            "0",
740            "123",
741            "-456",
742            "9223372036854775807",  // i64::MAX
743            "-9223372036854775808", // i64::MIN
744            "9223372036854775808",  // overflow
745            "-9223372036854775809", // overflow
746        ];
747
748        for test_str in &test_cases {
749            let malachite_val = BigInt::from_str(test_str).unwrap();
750            let num_bigint_val = num_bigint::BigInt::from_str(test_str).unwrap();
751
752            assert_eq!(
753                malachite_val.to_i64(),
754                num_bigint_val.to_i64(),
755                "to_i64 mismatch for {test_str}",
756            );
757        }
758    }
759
760    #[test]
761    fn test_to_u64() {
762        use num_traits::ToPrimitive as NumToPrimitive;
763
764        let test_cases = [
765            "0",
766            "123",
767            "18446744073709551615", // u64::MAX
768            "18446744073709551616", // overflow
769            "-1",                   // negative
770        ];
771
772        for test_str in &test_cases {
773            let malachite_val = BigInt::from_str(test_str).unwrap();
774            let num_bigint_val = num_bigint::BigInt::from_str(test_str).unwrap();
775
776            assert_eq!(
777                malachite_val.to_u64(),
778                num_bigint_val.to_u64(),
779                "to_u64 mismatch for {test_str}",
780            );
781        }
782    }
783
784    #[test]
785    fn test_arithmetic() {
786        let test_cases = [
787            ("123456789", "987654321"),
788            ("999999999999999999", "1"),
789            ("-123456789", "987654321"),
790            ("123456789", "-987654321"),
791            ("-123456789", "-987654321"),
792        ];
793
794        for (a_str, b_str) in &test_cases {
795            let ma = BigInt::from_str(a_str).unwrap();
796            let mb = BigInt::from_str(b_str).unwrap();
797            let na = num_bigint::BigInt::from_str(a_str).unwrap();
798            let nb = num_bigint::BigInt::from_str(b_str).unwrap();
799
800            assert_eq!((&ma + &mb).to_string(), (&na + &nb).to_string(), "add");
801            assert_eq!((&ma - &mb).to_string(), (&na - &nb).to_string(), "sub");
802            assert_eq!((&ma * &mb).to_string(), (&na * &nb).to_string(), "mul");
803            if *b_str != "0" {
804                assert_eq!((&ma / &mb).to_string(), (&na / &nb).to_string(), "div");
805                assert_eq!((&ma % &mb).to_string(), (&na % &nb).to_string(), "rem");
806            }
807        }
808    }
809
810    #[test]
811    fn test_checked_arithmetic() {
812        let test_cases = [
813            ("123456789", "987654321"),
814            ("999999999999999999", "1"),
815            ("-123456789", "987654321"),
816        ];
817
818        for (a_str, b_str) in &test_cases {
819            let ma = BigInt::from_str(a_str).unwrap();
820            let mb = BigInt::from_str(b_str).unwrap();
821            let na = num_bigint::BigInt::from_str(a_str).unwrap();
822            let nb = num_bigint::BigInt::from_str(b_str).unwrap();
823
824            assert_eq!(
825                ma.checked_add(&mb).map(|v| v.to_string()),
826                na.checked_add(&nb).map(|v| v.to_string()),
827                "checked_add"
828            );
829            assert_eq!(
830                ma.checked_sub(&mb).map(|v| v.to_string()),
831                na.checked_sub(&nb).map(|v| v.to_string()),
832                "checked_sub"
833            );
834            assert_eq!(
835                ma.checked_mul(&mb).map(|v| v.to_string()),
836                na.checked_mul(&nb).map(|v| v.to_string()),
837                "checked_mul"
838            );
839            assert_eq!(
840                ma.checked_div(&mb).map(|v| v.to_string()),
841                na.checked_div(&nb).map(|v| v.to_string()),
842                "checked_div"
843            );
844        }
845    }
846
847    #[test]
848    fn test_sign() {
849        use num_traits::Signed;
850
851        let test_cases = [
852            "0",
853            "123",
854            "-456",
855            "999999999999999999",
856            "-999999999999999999",
857        ];
858
859        for test_str in &test_cases {
860            let ma = BigInt::from_str(test_str).unwrap();
861            let na = num_bigint::BigInt::from_str(test_str).unwrap();
862
863            assert_eq!(
864                ma.is_positive(),
865                na.is_positive(),
866                "is_positive for {test_str}",
867            );
868            assert_eq!(
869                ma.is_negative(),
870                na.is_negative(),
871                "is_negative for {test_str}",
872            );
873            assert_eq!(
874                ma.abs().to_string(),
875                na.abs().to_string(),
876                "abs for {test_str}",
877            );
878        }
879    }
880
881    #[test]
882    fn test_pow() {
883        use num_traits::Pow;
884
885        let test_cases = [("2", 10u32), ("10", 20u32), ("-3", 5u32), ("123", 4u32)];
886
887        for (base_str, exp) in &test_cases {
888            let ma = BigInt::from_str(base_str).unwrap();
889            let na = num_bigint::BigInt::from_str(base_str).unwrap();
890
891            assert_eq!(
892                ma.pow(*exp).to_string(),
893                na.pow(*exp).to_string(),
894                "pow for {base_str}^{exp}",
895            );
896        }
897    }
898
899    #[test]
900    fn test_to_signed_bytes() {
901        let sysmax = i64::MAX;
902        let i = BigInt::from(sysmax);
903        let b = i.to_signed_bytes_le();
904        let i2 = BigInt::from_signed_bytes_le(&b);
905        assert_eq!(i, i2);
906    }
907
908    #[test]
909    fn test_display_bigint() {
910        let n = BigInt::from_str("1234567890").unwrap();
911        assert_eq!(format!("{n}"), "1234567890");
912    }
913}