Skip to main content

comnum_math/
bigint.rs

1//! Unsigned integers of arbitrary size, with the few operations that enumerative coding
2//! needs: products and exact quotients by machine integers, sums, differences,
3//! comparisons, and conversions from and to bit strings.
4//!
5//! # Examples
6//! ```
7//! use comnum_math::bigint::BigUint;
8//! // C(100, 50) = 100891344545564193334812497256, beyond 64 bits
9//! let mut c = BigUint::from(1);
10//! for i in 1..=50u64 {
11//!     c = c.mul_small(50 + i).div_small(i).0;
12//! }
13//! assert_eq!(c.bits(), 97);
14//! let back = BigUint::from_bits(&c.to_bits(97));
15//! assert_eq!(back, c);
16//! ```
17
18use std::cmp::Ordering;
19
20/// An unsigned integer of arbitrary size (64-bit limbs, least significant first).
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
22pub struct BigUint {
23    limbs: Vec<u64>,
24}
25
26impl From<u64> for BigUint {
27    fn from(v: u64) -> BigUint {
28        let mut b = BigUint { limbs: vec![v] };
29        b.trim();
30        b
31    }
32}
33
34impl BigUint {
35    fn trim(&mut self) {
36        while self.limbs.last() == Some(&0) {
37            self.limbs.pop();
38        }
39    }
40
41    /// Whether the value is zero.
42    pub fn is_zero(&self) -> bool {
43        self.limbs.is_empty()
44    }
45
46    /// The number of significant bits (0 for zero): `⌊log2 v⌋ + 1`.
47    pub fn bits(&self) -> usize {
48        self.limbs.last().map_or(0, |top| 64 * (self.limbs.len() - 1) + (64 - top.leading_zeros() as usize))
49    }
50
51    /// The product by a machine integer.
52    #[must_use]
53    pub fn mul_small(&self, k: u64) -> BigUint {
54        let mut out = Vec::with_capacity(self.limbs.len() + 1);
55        let mut carry = 0u128;
56        for &l in &self.limbs {
57            let v = u128::from(l) * u128::from(k) + carry;
58            out.push(v as u64);
59            carry = v >> 64;
60        }
61        if carry > 0 {
62            out.push(carry as u64);
63        }
64        let mut b = BigUint { limbs: out };
65        b.trim();
66        b
67    }
68
69    /// The quotient and the remainder of the division by a machine integer.
70    ///
71    /// # Panics
72    /// If `k` is zero.
73    #[must_use]
74    pub fn div_small(&self, k: u64) -> (BigUint, u64) {
75        assert!(k != 0, "division by zero");
76        let mut out = vec![0u64; self.limbs.len()];
77        let mut rem = 0u128;
78        for (i, &l) in self.limbs.iter().enumerate().rev() {
79            let v = (rem << 64) | u128::from(l);
80            out[i] = (v / u128::from(k)) as u64;
81            rem = v % u128::from(k);
82        }
83        let mut b = BigUint { limbs: out };
84        b.trim();
85        (b, rem as u64)
86    }
87
88    /// The sum.
89    #[must_use]
90    pub fn add(&self, o: &BigUint) -> BigUint {
91        let n = self.limbs.len().max(o.limbs.len());
92        let mut out = Vec::with_capacity(n + 1);
93        let mut carry = 0u128;
94        for i in 0..n {
95            let v = u128::from(*self.limbs.get(i).unwrap_or(&0)) + u128::from(*o.limbs.get(i).unwrap_or(&0)) + carry;
96            out.push(v as u64);
97            carry = v >> 64;
98        }
99        if carry > 0 {
100            out.push(carry as u64);
101        }
102        let mut b = BigUint { limbs: out };
103        b.trim();
104        b
105    }
106
107    /// The difference `self - o`.
108    ///
109    /// # Panics
110    /// If `o > self`.
111    #[must_use]
112    pub fn sub(&self, o: &BigUint) -> BigUint {
113        assert!(*self >= *o, "negative difference");
114        let mut out = Vec::with_capacity(self.limbs.len());
115        let mut borrow = 0u64;
116        for (i, &l) in self.limbs.iter().enumerate() {
117            let r = *o.limbs.get(i).unwrap_or(&0);
118            let (d1, b1) = l.overflowing_sub(r);
119            let (d2, b2) = d1.overflowing_sub(borrow);
120            out.push(d2);
121            borrow = u64::from(b1 || b2);
122        }
123        let mut b = BigUint { limbs: out };
124        b.trim();
125        b
126    }
127
128    /// The integer whose binary digits are `bits`, most significant first.
129    pub fn from_bits(bits: &[u8]) -> BigUint {
130        let mut limbs = vec![0u64; bits.len().div_ceil(64)];
131        for (i, &b) in bits.iter().rev().enumerate() {
132            if b != 0 {
133                limbs[i / 64] |= 1 << (i % 64);
134            }
135        }
136        let mut v = BigUint { limbs };
137        v.trim();
138        v
139    }
140
141    /// The `n` lowest binary digits, most significant first.
142    pub fn to_bits(&self, n: usize) -> Vec<u8> {
143        (0..n).rev().map(|i| self.limbs.get(i / 64).map_or(0, |l| ((l >> (i % 64)) & 1) as u8)).collect()
144    }
145}
146
147impl PartialOrd for BigUint {
148    fn partial_cmp(&self, o: &BigUint) -> Option<Ordering> {
149        Some(self.cmp(o))
150    }
151}
152
153impl Ord for BigUint {
154    fn cmp(&self, o: &BigUint) -> Ordering {
155        self.limbs.len().cmp(&o.limbs.len()).then_with(|| self.limbs.iter().rev().cmp(o.limbs.iter().rev()))
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn arithmetic_agrees_with_u128() {
165        let vals: [u128; 5] = [0, 1, 0xFFFF_FFFF_FFFF_FFFF, 0x1_0000_0000_0000_0001, 12_345_678_901_234_567_890_123];
166        let big = |v: u128| BigUint::from_bits(&(0..128).rev().map(|i| ((v >> i) & 1) as u8).collect::<Vec<_>>());
167        let val = |b: &BigUint| b.to_bits(128).iter().fold(0u128, |a, &x| (a << 1) | u128::from(x));
168        for &a in &vals {
169            for &b in &vals {
170                assert_eq!(val(&big(a).add(&big(b))), a.wrapping_add(b));
171                if a >= b {
172                    assert_eq!(val(&big(a).sub(&big(b))), a - b);
173                }
174                assert_eq!(big(a).cmp(&big(b)), a.cmp(&b));
175            }
176            assert_eq!(val(&big(a).mul_small(977)), a * 977);
177            let (q, r) = big(a).div_small(977);
178            assert_eq!((val(&q), u128::from(r)), (a / 977, a % 977));
179        }
180    }
181}