Skip to main content

qualia_core_db/solvers/exact/
bigint.rs

1//! Arbitrary-precision signed integer (`BigInt`).
2//!
3//! This is part of the §3.1 "exact computation" foundation. It is a real,
4//! self-contained big-integer implementation — sign + little-endian `u32`
5//! magnitude limbs with schoolbook multiply and long division. It is **not** a
6//! wrapper around `i128`.
7//!
8//! Heap-side is fine here: this is exact arbitrary-precision arithmetic, not a
9//! zero-heap hot path. Operations that can fail (division by zero) return an
10//! `Option`/`Result` and **fail closed** — they never fabricate a value.
11//!
12//! Internal representation invariants:
13//! - `mag` holds base-2^32 limbs, little-endian (least significant first).
14//! - `mag` is always *normalised*: no trailing zero limbs. Zero is the empty
15//!   vector with `sign == 0`.
16//! - `sign` is `-1`, `0`, or `+1`. `sign == 0` **iff** `mag` is empty.
17
18use core::cmp::Ordering;
19use core::fmt;
20
21const BASE_BITS: u32 = 32;
22const BASE: u64 = 1u64 << BASE_BITS; // 2^32
23
24/// Arbitrary-precision signed integer.
25#[derive(Clone, PartialEq, Eq)]
26pub struct BigInt {
27    /// `-1`, `0`, or `+1`. Zero iff `mag` is empty.
28    sign: i8,
29    /// Base-2^32 magnitude limbs, little-endian, no trailing zeros.
30    mag: Vec<u32>,
31}
32
33impl BigInt {
34    /// The integer zero.
35    pub fn zero() -> Self {
36        BigInt {
37            sign: 0,
38            mag: Vec::new(),
39        }
40    }
41
42    /// The integer one.
43    pub fn one() -> Self {
44        BigInt {
45            sign: 1,
46            mag: vec![1],
47        }
48    }
49
50    /// Construct from an `i64`.
51    pub fn from_i64(mut v: i64) -> Self {
52        if v == 0 {
53            return BigInt::zero();
54        }
55        let sign: i8 = if v < 0 { -1 } else { 1 };
56        // Use unsigned magnitude to handle i64::MIN safely.
57        let mut uv: u64 = if v < 0 {
58            // negate in u64 space (handles MIN without overflow)
59            (v as i128).unsigned_abs() as u64
60        } else {
61            v as u64
62        };
63        v = 0; // silence unused-assignment lints in some toolchains
64        let _ = v;
65        let mut mag = Vec::new();
66        while uv > 0 {
67            mag.push((uv & 0xFFFF_FFFF) as u32);
68            uv >>= BASE_BITS;
69        }
70        let mut out = BigInt { sign, mag };
71        out.normalize();
72        out
73    }
74
75    /// Construct from a `u64`.
76    pub fn from_u64(mut v: u64) -> Self {
77        if v == 0 {
78            return BigInt::zero();
79        }
80        let mut mag = Vec::new();
81        while v > 0 {
82            mag.push((v & 0xFFFF_FFFF) as u32);
83            v >>= BASE_BITS;
84        }
85        BigInt { sign: 1, mag }
86    }
87
88    /// Parse a decimal string (optional leading `+`/`-`). Fails closed on any
89    /// non-digit character or empty input.
90    pub fn from_str(s: &str) -> Option<Self> {
91        let s = s.trim();
92        if s.is_empty() {
93            return None;
94        }
95        let (sign, digits) = match s.as_bytes()[0] {
96            b'+' => (1i8, &s[1..]),
97            b'-' => (-1i8, &s[1..]),
98            _ => (1i8, s),
99        };
100        if digits.is_empty() {
101            return None;
102        }
103        let mut acc = BigInt::zero();
104        let ten = BigInt::from_u64(10);
105        for ch in digits.bytes() {
106            if !ch.is_ascii_digit() {
107                return None;
108            }
109            let d = BigInt::from_u64((ch - b'0') as u64);
110            acc = acc.mul(&ten).add(&d);
111        }
112        if acc.is_zero() {
113            // "-0" / "0000" → canonical zero
114            return Some(BigInt::zero());
115        }
116        acc.sign = sign;
117        Some(acc)
118    }
119
120    /// Render as a decimal string with a leading `-` for negatives.
121    pub fn to_string(&self) -> String {
122        if self.is_zero() {
123            return "0".to_string();
124        }
125        // Repeatedly divmod by 10^9 (a chunk that fits in u32 output) and emit
126        // groups of 9 decimal digits.
127        let chunk = 1_000_000_000u64; // 10^9 < 2^32
128        let mut limbs = self.mag.clone();
129        let mut groups: Vec<u32> = Vec::new();
130        while !limbs.is_empty() {
131            let mut rem: u64 = 0;
132            // divide magnitude (little-endian) by chunk, MSB-first
133            for i in (0..limbs.len()).rev() {
134                let cur = (rem << BASE_BITS) | limbs[i] as u64;
135                limbs[i] = (cur / chunk) as u32;
136                rem = cur % chunk;
137            }
138            // strip trailing zero limbs
139            while let Some(&0) = limbs.last() {
140                limbs.pop();
141            }
142            groups.push(rem as u32);
143        }
144        let mut out = String::new();
145        if self.sign < 0 {
146            out.push('-');
147        }
148        // most-significant group printed without leading zeros
149        let last = groups.len() - 1;
150        out.push_str(&groups[last].to_string());
151        for i in (0..last).rev() {
152            out.push_str(&format!("{:09}", groups[i]));
153        }
154        out
155    }
156
157    /// True if this is zero.
158    pub fn is_zero(&self) -> bool {
159        self.sign == 0
160    }
161
162    /// True if this is negative.
163    pub fn is_negative(&self) -> bool {
164        self.sign < 0
165    }
166
167    /// Sign: `-1`, `0`, or `+1`.
168    pub fn signum(&self) -> i8 {
169        self.sign
170    }
171
172    /// Absolute value.
173    pub fn abs(&self) -> Self {
174        BigInt {
175            sign: if self.sign == 0 { 0 } else { 1 },
176            mag: self.mag.clone(),
177        }
178    }
179
180    /// Arithmetic negation.
181    pub fn neg(&self) -> Self {
182        BigInt {
183            sign: -self.sign,
184            mag: self.mag.clone(),
185        }
186    }
187
188    // ── normalisation ──────────────────────────────────────────────────────
189
190    fn normalize(&mut self) {
191        while let Some(&0) = self.mag.last() {
192            self.mag.pop();
193        }
194        if self.mag.is_empty() {
195            self.sign = 0;
196        } else if self.sign == 0 {
197            self.sign = 1;
198        }
199    }
200
201    // ── magnitude helpers (ignore sign) ────────────────────────────────────
202
203    /// Compare two magnitudes (little-endian limb vectors).
204    fn cmp_mag(a: &[u32], b: &[u32]) -> Ordering {
205        if a.len() != b.len() {
206            return a.len().cmp(&b.len());
207        }
208        for i in (0..a.len()).rev() {
209            if a[i] != b[i] {
210                return a[i].cmp(&b[i]);
211            }
212        }
213        Ordering::Equal
214    }
215
216    /// Add two magnitudes.
217    fn add_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
218        let (long, short) = if a.len() >= b.len() { (a, b) } else { (b, a) };
219        let mut out = Vec::with_capacity(long.len() + 1);
220        let mut carry: u64 = 0;
221        for i in 0..long.len() {
222            let mut sum = long[i] as u64 + carry;
223            if i < short.len() {
224                sum += short[i] as u64;
225            }
226            out.push((sum & 0xFFFF_FFFF) as u32);
227            carry = sum >> BASE_BITS;
228        }
229        if carry > 0 {
230            out.push(carry as u32);
231        }
232        out
233    }
234
235    /// Subtract `b` from `a` where `a >= b` (magnitudes). Result is normalised.
236    fn sub_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
237        debug_assert!(Self::cmp_mag(a, b) != Ordering::Less);
238        let mut out = Vec::with_capacity(a.len());
239        let mut borrow: i64 = 0;
240        for i in 0..a.len() {
241            let bi = if i < b.len() { b[i] as i64 } else { 0 };
242            let mut diff = a[i] as i64 - bi - borrow;
243            if diff < 0 {
244                diff += BASE as i64;
245                borrow = 1;
246            } else {
247                borrow = 0;
248            }
249            out.push(diff as u32);
250        }
251        while let Some(&0) = out.last() {
252            out.pop();
253        }
254        out
255    }
256
257    /// Multiply two magnitudes (schoolbook).
258    fn mul_mag(a: &[u32], b: &[u32]) -> Vec<u32> {
259        if a.is_empty() || b.is_empty() {
260            return Vec::new();
261        }
262        let mut out = vec![0u32; a.len() + b.len()];
263        for i in 0..a.len() {
264            let mut carry: u64 = 0;
265            let ai = a[i] as u64;
266            for j in 0..b.len() {
267                let idx = i + j;
268                let cur = out[idx] as u64 + ai * b[j] as u64 + carry;
269                out[idx] = (cur & 0xFFFF_FFFF) as u32;
270                carry = cur >> BASE_BITS;
271            }
272            // propagate remaining carry
273            let mut idx = i + b.len();
274            while carry > 0 {
275                let cur = out[idx] as u64 + carry;
276                out[idx] = (cur & 0xFFFF_FFFF) as u32;
277                carry = cur >> BASE_BITS;
278                idx += 1;
279            }
280        }
281        while let Some(&0) = out.last() {
282            out.pop();
283        }
284        out
285    }
286
287    // ── arithmetic ─────────────────────────────────────────────────────────
288
289    /// Sum `self + other`.
290    pub fn add(&self, other: &BigInt) -> BigInt {
291        if self.is_zero() {
292            return other.clone();
293        }
294        if other.is_zero() {
295            return self.clone();
296        }
297        if self.sign == other.sign {
298            let mag = Self::add_mag(&self.mag, &other.mag);
299            let mut r = BigInt {
300                sign: self.sign,
301                mag,
302            };
303            r.normalize();
304            r
305        } else {
306            // different signs → subtract smaller magnitude from larger
307            match Self::cmp_mag(&self.mag, &other.mag) {
308                Ordering::Equal => BigInt::zero(),
309                Ordering::Greater => {
310                    let mag = Self::sub_mag(&self.mag, &other.mag);
311                    let mut r = BigInt {
312                        sign: self.sign,
313                        mag,
314                    };
315                    r.normalize();
316                    r
317                }
318                Ordering::Less => {
319                    let mag = Self::sub_mag(&other.mag, &self.mag);
320                    let mut r = BigInt {
321                        sign: other.sign,
322                        mag,
323                    };
324                    r.normalize();
325                    r
326                }
327            }
328        }
329    }
330
331    /// Difference `self - other`.
332    pub fn sub(&self, other: &BigInt) -> BigInt {
333        self.add(&other.neg())
334    }
335
336    /// Product `self * other`.
337    pub fn mul(&self, other: &BigInt) -> BigInt {
338        if self.is_zero() || other.is_zero() {
339            return BigInt::zero();
340        }
341        let mag = Self::mul_mag(&self.mag, &other.mag);
342        let mut r = BigInt {
343            sign: self.sign * other.sign,
344            mag,
345        };
346        r.normalize();
347        r
348    }
349
350    /// Truncated division and remainder: returns `(quotient, remainder)` such
351    /// that `self == quotient * divisor + remainder`, with the remainder taking
352    /// the sign of `self` (truncation toward zero, matching Rust's `/` and `%`
353    /// on primitive integers). Fails closed (`None`) on division by zero.
354    pub fn divmod(&self, divisor: &BigInt) -> Option<(BigInt, BigInt)> {
355        if divisor.is_zero() {
356            return None; // fail closed — never fabricate
357        }
358        if self.is_zero() {
359            return Some((BigInt::zero(), BigInt::zero()));
360        }
361        // |self| < |divisor| → quotient 0, remainder self
362        if Self::cmp_mag(&self.mag, &divisor.mag) == Ordering::Less {
363            return Some((BigInt::zero(), self.clone()));
364        }
365        let (q_mag, r_mag) = Self::divmod_mag(&self.mag, &divisor.mag);
366        let mut q = BigInt {
367            sign: self.sign * divisor.sign,
368            mag: q_mag,
369        };
370        let mut r = BigInt {
371            sign: self.sign,
372            mag: r_mag,
373        };
374        q.normalize();
375        r.normalize();
376        Some((q, r))
377    }
378
379    /// Quotient only (truncated toward zero). Fails closed on zero divisor.
380    pub fn div(&self, divisor: &BigInt) -> Option<BigInt> {
381        self.divmod(divisor).map(|(q, _)| q)
382    }
383
384    /// Remainder only (sign of `self`). Fails closed on zero divisor.
385    pub fn rem(&self, divisor: &BigInt) -> Option<BigInt> {
386        self.divmod(divisor).map(|(_, r)| r)
387    }
388
389    /// Knuth-style long division of magnitudes. Returns `(quotient, remainder)`
390    /// magnitudes. Requires `a >= b` in magnitude and `b` non-empty.
391    fn divmod_mag(a: &[u32], b: &[u32]) -> (Vec<u32>, Vec<u32>) {
392        // Single-limb divisor: fast path.
393        if b.len() == 1 {
394            let d = b[0] as u64;
395            let mut q = vec![0u32; a.len()];
396            let mut rem: u64 = 0;
397            for i in (0..a.len()).rev() {
398                let cur = (rem << BASE_BITS) | a[i] as u64;
399                q[i] = (cur / d) as u32;
400                rem = cur % d;
401            }
402            while let Some(&0) = q.last() {
403                q.pop();
404            }
405            let r = if rem == 0 {
406                Vec::new()
407            } else {
408                vec![rem as u32]
409            };
410            return (q, r);
411        }
412
413        // Normalize so the divisor's top limb has its high bit set (Knuth D1).
414        let shift = b[b.len() - 1].leading_zeros();
415        let bn = Self::shl_bits(b, shift);
416        let mut an = Self::shl_bits(a, shift);
417        // Ensure `an` has one extra high limb to simplify indexing.
418        if an.len() == a.len() {
419            an.push(0);
420        }
421        let n = bn.len();
422        let m = an.len() - n; // number of quotient limbs (an has n+m limbs)
423        let mut q = vec![0u32; m];
424
425        let b_high = bn[n - 1] as u64;
426        let b_second = bn[n - 2] as u64;
427
428        for j in (0..m).rev() {
429            // Estimate q_hat from the top two limbs of the current remainder.
430            let top = ((an[j + n] as u64) << BASE_BITS) | an[j + n - 1] as u64;
431            let mut q_hat = top / b_high;
432            let mut r_hat = top % b_high;
433            // Refine q_hat (Knuth D3).
434            while q_hat >= BASE || q_hat * b_second > (r_hat << BASE_BITS) | an[j + n - 2] as u64 {
435                q_hat -= 1;
436                r_hat += b_high;
437                if r_hat >= BASE {
438                    break;
439                }
440            }
441
442            // Multiply and subtract q_hat * bn from an[j..=j+n].
443            let mut borrow: i64 = 0;
444            let mut carry: u64 = 0;
445            for i in 0..n {
446                let p = q_hat * bn[i] as u64 + carry;
447                carry = p >> BASE_BITS;
448                let sub = an[j + i] as i64 - (p & 0xFFFF_FFFF) as i64 - borrow;
449                if sub < 0 {
450                    an[j + i] = (sub + BASE as i64) as u32;
451                    borrow = 1;
452                } else {
453                    an[j + i] = sub as u32;
454                    borrow = 0;
455                }
456            }
457            let sub = an[j + n] as i64 - carry as i64 - borrow;
458            if sub < 0 {
459                // q_hat was one too big: add back (Knuth D6).
460                an[j + n] = (sub + BASE as i64) as u32;
461                q_hat -= 1;
462                let mut carry2: u64 = 0;
463                for i in 0..n {
464                    let s = an[j + i] as u64 + bn[i] as u64 + carry2;
465                    an[j + i] = (s & 0xFFFF_FFFF) as u32;
466                    carry2 = s >> BASE_BITS;
467                }
468                an[j + n] = (an[j + n] as u64 + carry2) as u32;
469            } else {
470                an[j + n] = sub as u32;
471            }
472            q[j] = q_hat as u32;
473        }
474
475        while let Some(&0) = q.last() {
476            q.pop();
477        }
478        // Remainder = (top n limbs of an) >> shift.
479        let mut rem = an[..n].to_vec();
480        while let Some(&0) = rem.last() {
481            rem.pop();
482        }
483        let rem = Self::shr_bits(&rem, shift);
484        (q, rem)
485    }
486
487    /// Shift a magnitude left by `bits` (0..32).
488    fn shl_bits(a: &[u32], bits: u32) -> Vec<u32> {
489        if bits == 0 || a.is_empty() {
490            return a.to_vec();
491        }
492        let mut out = Vec::with_capacity(a.len() + 1);
493        let mut carry: u32 = 0;
494        for &limb in a {
495            let v = ((limb as u64) << bits) | carry as u64;
496            out.push((v & 0xFFFF_FFFF) as u32);
497            carry = (v >> BASE_BITS) as u32;
498        }
499        if carry > 0 {
500            out.push(carry);
501        }
502        out
503    }
504
505    /// Shift a magnitude right by `bits` (0..32).
506    fn shr_bits(a: &[u32], bits: u32) -> Vec<u32> {
507        if bits == 0 || a.is_empty() {
508            return a.to_vec();
509        }
510        let mut out = vec![0u32; a.len()];
511        let mut carry: u32 = 0;
512        for i in (0..a.len()).rev() {
513            let v = a[i];
514            out[i] = (v >> bits) | carry;
515            carry = v << (BASE_BITS - bits);
516        }
517        while let Some(&0) = out.last() {
518            out.pop();
519        }
520        out
521    }
522
523    /// Raise to a non-negative integer power (exponentiation by squaring).
524    pub fn pow(&self, exp: u32) -> BigInt {
525        let mut result = BigInt::one();
526        let mut base = self.clone();
527        let mut e = exp;
528        while e > 0 {
529            if e & 1 == 1 {
530                result = result.mul(&base);
531            }
532            e >>= 1;
533            if e > 0 {
534                base = base.mul(&base);
535            }
536        }
537        result
538    }
539
540    /// Greatest common divisor (always non-negative). `gcd(0,0) == 0`.
541    pub fn gcd(&self, other: &BigInt) -> BigInt {
542        let mut a = self.abs();
543        let mut b = other.abs();
544        while !b.is_zero() {
545            // a, b non-negative ⇒ rem is non-negative
546            let r = a.rem(&b).expect("b non-zero in gcd loop");
547            a = b;
548            b = r;
549        }
550        a
551    }
552}
553
554impl PartialOrd for BigInt {
555    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
556        Some(self.cmp(other))
557    }
558}
559
560impl Ord for BigInt {
561    fn cmp(&self, other: &Self) -> Ordering {
562        match self.sign.cmp(&other.sign) {
563            Ordering::Equal => {}
564            non_eq => return non_eq,
565        }
566        // same sign
567        match self.sign {
568            0 => Ordering::Equal,
569            1 => Self::cmp_mag(&self.mag, &other.mag),
570            _ => Self::cmp_mag(&other.mag, &self.mag), // both negative: reverse
571        }
572    }
573}
574
575impl fmt::Debug for BigInt {
576    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577        write!(f, "BigInt({})", self.to_string())
578    }
579}
580
581impl fmt::Display for BigInt {
582    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583        f.write_str(&self.to_string())
584    }
585}
586
587#[cfg(test)]
588mod tests {
589    use super::*;
590
591    #[test]
592    fn from_and_to_i64_roundtrip() {
593        for v in [
594            0i64,
595            1,
596            -1,
597            42,
598            -42,
599            1_000_000,
600            -1_000_000,
601            i64::MAX,
602            i64::MIN,
603        ] {
604            let b = BigInt::from_i64(v);
605            assert_eq!(b.to_string(), v.to_string(), "value {v}");
606        }
607    }
608
609    #[test]
610    fn from_str_roundtrip_and_failclosed() {
611        assert_eq!(
612            BigInt::from_str("12345678901234567890")
613                .unwrap()
614                .to_string(),
615            "12345678901234567890"
616        );
617        assert_eq!(
618            BigInt::from_str("-99999999999999999999")
619                .unwrap()
620                .to_string(),
621            "-99999999999999999999"
622        );
623        assert_eq!(BigInt::from_str("+7").unwrap().to_string(), "7");
624        assert_eq!(BigInt::from_str("-0").unwrap().to_string(), "0");
625        assert!(BigInt::from_str("").is_none());
626        assert!(BigInt::from_str("12a3").is_none());
627        assert!(BigInt::from_str("--3").is_none());
628    }
629
630    #[test]
631    fn add_sub_signs() {
632        let a = BigInt::from_i64(100);
633        let b = BigInt::from_i64(-30);
634        assert_eq!(a.add(&b).to_string(), "70");
635        assert_eq!(b.add(&a).to_string(), "70");
636        assert_eq!(a.sub(&b).to_string(), "130");
637        assert_eq!(b.sub(&a).to_string(), "-130");
638        assert_eq!(a.add(&a.neg()).to_string(), "0");
639    }
640
641    #[test]
642    fn mul_and_pow_2_to_100() {
643        // 2^100 known value
644        let two = BigInt::from_i64(2);
645        let p = two.pow(100);
646        assert_eq!(p.to_string(), "1267650600228229401496703205376");
647    }
648
649    #[test]
650    fn factorial_100_known_value() {
651        let mut acc = BigInt::one();
652        for k in 1..=100u64 {
653            acc = acc.mul(&BigInt::from_u64(k));
654        }
655        // 100! — a well-known 158-digit constant.
656        let expected = "93326215443944152681699238856266700490715968264381621468592963895217\
65759999322991560894146397615651828625369792082722375825118521091686400\
6580000000000000000000000";
659        assert_eq!(acc.to_string(), expected);
660    }
661
662    #[test]
663    fn divmod_known_values() {
664        let a = BigInt::from_i64(100);
665        let b = BigInt::from_i64(7);
666        let (q, r) = a.divmod(&b).unwrap();
667        assert_eq!(q.to_string(), "14");
668        assert_eq!(r.to_string(), "2");
669
670        // Reconstruct: q*b + r == a
671        assert_eq!(q.mul(&b).add(&r), a);
672
673        // Negative dividend → truncation toward zero, remainder sign of dividend
674        let (q2, r2) = BigInt::from_i64(-100).divmod(&BigInt::from_i64(7)).unwrap();
675        assert_eq!(q2.to_string(), "-14");
676        assert_eq!(r2.to_string(), "-2");
677    }
678
679    #[test]
680    fn divmod_multilimb() {
681        // (2^100) / (2^50) == 2^50 exactly
682        let num = BigInt::from_i64(2).pow(100);
683        let den = BigInt::from_i64(2).pow(50);
684        let (q, r) = num.divmod(&den).unwrap();
685        assert!(r.is_zero());
686        assert_eq!(q, BigInt::from_i64(2).pow(50));
687
688        // big % big with remainder, verify reconstruction
689        let x = BigInt::from_str("123456789012345678901234567890").unwrap();
690        let y = BigInt::from_str("98765432109876543").unwrap();
691        let (q, r) = x.divmod(&y).unwrap();
692        assert_eq!(q.mul(&y).add(&r), x);
693        assert!(r.abs() < y.abs());
694    }
695
696    #[test]
697    fn divide_by_zero_fails_closed() {
698        assert!(BigInt::from_i64(5).divmod(&BigInt::zero()).is_none());
699        assert!(BigInt::from_i64(5).div(&BigInt::zero()).is_none());
700        assert!(BigInt::from_i64(5).rem(&BigInt::zero()).is_none());
701    }
702
703    #[test]
704    fn ordering() {
705        assert!(BigInt::from_i64(-5) < BigInt::from_i64(-3));
706        assert!(BigInt::from_i64(-3) < BigInt::from_i64(0));
707        assert!(BigInt::from_i64(0) < BigInt::from_i64(3));
708        assert!(BigInt::from_i64(3) < BigInt::from_i64(5));
709        assert!(BigInt::from_i64(2).pow(100) > BigInt::from_i64(2).pow(99));
710        assert_eq!(
711            BigInt::from_i64(7).cmp(&BigInt::from_i64(7)),
712            Ordering::Equal
713        );
714    }
715
716    #[test]
717    fn gcd_known() {
718        assert_eq!(
719            BigInt::from_i64(48).gcd(&BigInt::from_i64(36)).to_string(),
720            "12"
721        );
722        assert_eq!(
723            BigInt::from_i64(-48).gcd(&BigInt::from_i64(36)).to_string(),
724            "12"
725        );
726        assert_eq!(
727            BigInt::from_i64(17).gcd(&BigInt::from_i64(5)).to_string(),
728            "1"
729        );
730        assert_eq!(
731            BigInt::from_i64(0).gcd(&BigInt::from_i64(9)).to_string(),
732            "9"
733        );
734    }
735
736    #[test]
737    fn abs_neg() {
738        assert_eq!(BigInt::from_i64(-42).abs().to_string(), "42");
739        assert_eq!(BigInt::from_i64(42).neg().to_string(), "-42");
740        assert!(BigInt::zero().neg().is_zero());
741    }
742}