Skip to main content

dashu_int/
ubig.rs

1//! Definitions of [UBig].
2//!
3//! Conversion from internal representations including [Buffer][crate::buffer::Buffer], [TypedRepr], [TypedReprRef]
4//! to [UBig] is not implemented, the designed way to construct UBig from them is first convert them
5//! into [Repr], and then directly construct from the [Repr]. This restriction is set to make
6//! the source type explicit.
7
8use crate::repr::{Repr, TypedRepr, TypedReprRef};
9
10/// An unsigned arbitrary precision integer.
11///
12/// `UBig` represents an arbitrarily large non-negative integer. Values that fit in a
13/// [`DoubleWord`](crate::DoubleWord) are inlined (no heap allocation); larger values are stored as a
14/// heap-allocated array of [`Word`](crate::Word)s. The type carries a niche bit, so
15/// [`Option<UBig>`] occupies the same space as [`UBig`].
16///
17/// For the full discussion — construction, parsing, printing, and the memory layout — see the
18/// [user guide](https://zyxin.xyz/dashu/types.html).
19///
20/// # Examples
21///
22/// Parsing and printing (base 2–36 is supported for string/literal parsing):
23///
24/// ```
25/// # use dashu_base::ParseError;
26/// # use dashu_int::{UBig, Word};
27/// // parsing
28/// let a = UBig::from(408580953453092208335085386466371u128);
29/// let b = UBig::from(0x1231abcd4134u64);
30/// let c = UBig::from_str_radix("a2a123bbb127779cccc123", 32)?;
31/// let d = UBig::from_str_radix("1231abcd4134", 16)?;
32/// assert_eq!(a, c);
33/// assert_eq!(b, d);
34///
35/// // printing
36/// assert_eq!(format!("{}", UBig::from(12u8)), "12");
37/// assert_eq!(format!("{:#X}", UBig::from(0xabcdu16)), "0xABCD");
38/// if Word::BITS == 64 {
39///     // number of digits to display depends on the word size
40///     assert_eq!(
41///         format!("{:?}", UBig::ONE << 1000),
42///         "1071508607186267320..4386837205668069376"
43///     );
44/// }
45/// # Ok::<(), ParseError>(())
46/// ```
47///
48/// The niche bit makes `Option<UBig>` free:
49///
50/// ```
51/// # use dashu_int::UBig;
52/// use core::mem::size_of;
53/// assert_eq!(size_of::<UBig>(), size_of::<Option<UBig>>());
54/// ```
55#[derive(Eq, Hash, PartialEq)]
56#[repr(transparent)]
57pub struct UBig(pub(crate) Repr);
58
59impl UBig {
60    /// Get the representation of UBig.
61    #[inline]
62    pub(crate) const fn repr(&self) -> TypedReprRef<'_> {
63        self.0.as_typed()
64    }
65
66    /// Convert into representation.
67    #[inline]
68    pub(crate) fn into_repr(self) -> TypedRepr {
69        self.0.into_typed()
70    }
71
72    /// [UBig] with value 0
73    pub const ZERO: Self = Self(Repr::zero());
74    /// [UBig] with value 1
75    pub const ONE: Self = Self(Repr::one());
76
77    /// Get the raw representation in [Word][crate::Word]s.
78    ///
79    /// If the number is zero, then empty slice will be returned.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// # use dashu_int::{UBig, Word};
85    /// assert_eq!(UBig::ZERO.as_words(), &[] as &[Word]);
86    /// assert_eq!(UBig::ONE.as_words(), &[1]);
87    /// ```
88    #[inline]
89    pub fn as_words(&self) -> &[crate::Word] {
90        let (sign, words) = self.0.as_sign_slice();
91        debug_assert!(matches!(sign, crate::Sign::Positive));
92        words
93    }
94
95    /// Create a UBig from a single [Word][crate::Word].
96    ///
97    /// # Examples
98    ///
99    /// ```
100    /// # use dashu_int::UBig;
101    /// const ZERO: UBig = UBig::from_word(0);
102    /// assert_eq!(ZERO, UBig::ZERO);
103    /// const ONE: UBig = UBig::from_word(1);
104    /// assert_eq!(ONE, UBig::ONE);
105    /// ```
106    #[inline]
107    pub const fn from_word(word: crate::Word) -> Self {
108        Self(Repr::from_word(word))
109    }
110
111    /// Create a UBig from a [DoubleWord][crate::DoubleWord].
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// # use dashu_int::UBig;
117    /// const ZERO: UBig = UBig::from_dword(0);
118    /// assert_eq!(ZERO, UBig::ZERO);
119    /// const ONE: UBig = UBig::from_dword(1);
120    /// assert_eq!(ONE, UBig::ONE);
121    /// ```
122    #[inline]
123    pub const fn from_dword(dword: crate::DoubleWord) -> Self {
124        Self(Repr::from_dword(dword))
125    }
126
127    /// Create a UBig from a u64.
128    ///
129    /// This function is const on 32-bit and 64-bit targets.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use dashu_int::UBig;
135    /// assert_eq!(UBig::from_u64(42), UBig::from(42u64));
136    /// assert_eq!(UBig::from_u64(10_939_058_860_032_000), UBig::from(10_939_058_860_032_000u64));
137    /// ```
138    #[cfg(not(any(target_pointer_width = "16", force_bits = "16")))]
139    #[inline]
140    pub const fn from_u64(n: u64) -> Self {
141        Self(Repr::from_dword(n as crate::DoubleWord))
142    }
143
144    /// Create a UBig from a u64.
145    ///
146    /// On 16-bit targets `u64` is wider than [`DoubleWord`][crate::DoubleWord], so this delegates
147    /// to `From<u64>` and is not `const`; on 32-bit and 64-bit targets the `const` constructor
148    /// above is used instead.
149    #[cfg(any(target_pointer_width = "16", force_bits = "16"))]
150    #[inline]
151    pub fn from_u64(n: u64) -> Self {
152        Self::from(n)
153    }
154
155    /// Convert a sequence of [Word][crate::Word]s into a UBig
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// # use dashu_int::{UBig, Word};
161    /// assert_eq!(UBig::from_words(&[] as &[Word]), UBig::ZERO);
162    /// assert_eq!(UBig::from_words(&[1]), UBig::ONE);
163    /// assert_eq!(UBig::from_words(&[1, 1]), (UBig::ONE << Word::BITS as usize) + UBig::ONE);
164    /// ```
165    #[inline]
166    pub fn from_words(words: &[crate::Word]) -> Self {
167        Self(Repr::from_buffer(words.into()))
168    }
169
170    /// Create an UBig from a static sequence of [Word][crate::Word]s. Similar to [from_words][UBig::from_words].
171    ///
172    /// The top word of the input word array must not be zero.
173    ///
174    /// This method is unsafe because it must be carefully handled. The generated instance
175    /// must not be mutated or dropped. Therefore the correct usage is to assign it to an
176    /// immutable static variable. Due to the risk, it's generally not recommended to use this method.
177    /// This method is intended for the use of static creation macros.
178    #[doc(hidden)]
179    #[inline]
180    pub const unsafe fn from_static_words(words: &'static [crate::Word]) -> Self {
181        Self(Repr::from_static_words(words))
182    }
183
184    /// Check whether the value is 0
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// # use dashu_int::UBig;
190    /// assert!(UBig::ZERO.is_zero());
191    /// assert!(!UBig::ONE.is_zero());
192    /// ```
193    #[inline]
194    pub const fn is_zero(&self) -> bool {
195        self.0.is_zero()
196    }
197
198    /// Check whether the value is 1
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// # use dashu_int::UBig;
204    /// assert!(!UBig::ZERO.is_one());
205    /// assert!(UBig::ONE.is_one());
206    /// ```
207    #[inline]
208    pub const fn is_one(&self) -> bool {
209        self.0.is_one()
210    }
211
212    /// Create an integer with `n` consecutive one bits (i.e. 2^n - 1).
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// # use dashu_int::UBig;
218    /// let mut n = UBig::ZERO;
219    /// n.set_bit(20);
220    /// n -= UBig::ONE;
221    /// assert_eq!(UBig::ones(20), n);
222    /// ```
223    #[inline]
224    pub fn ones(n: usize) -> Self {
225        Self(Repr::ones(n))
226    }
227}
228
229// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
230impl Clone for UBig {
231    #[inline]
232    fn clone(&self) -> UBig {
233        UBig(self.0.clone())
234    }
235    #[inline]
236    fn clone_from(&mut self, source: &UBig) {
237        self.0.clone_from(&source.0)
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::{buffer::Buffer, DoubleWord, Word};
245
246    impl UBig {
247        /// Capacity in Words.
248        #[inline]
249        fn capacity(&self) -> usize {
250            self.0.capacity()
251        }
252    }
253
254    fn gen_ubig(num_words: usize) -> UBig {
255        let mut buf = Buffer::allocate(num_words);
256        for i in 0..num_words {
257            buf.push(i as Word);
258        }
259        UBig(Repr::from_buffer(buf))
260    }
261
262    #[test]
263    fn test_buffer_to_ubig() {
264        let buf = Buffer::allocate(5);
265        let num = UBig(Repr::from_buffer(buf));
266        assert_eq!(num, UBig::ZERO);
267
268        let mut buf = Buffer::allocate(5);
269        buf.push(7);
270        let num = UBig(Repr::from_buffer(buf));
271        assert_eq!(num, UBig::from(7u8));
272
273        let mut buf = Buffer::allocate(100);
274        buf.push(7);
275        buf.push(0);
276        buf.push(0);
277        let num = UBig(Repr::from_buffer(buf));
278        assert_eq!(num, UBig::from(7u8));
279
280        let mut buf = Buffer::allocate(5);
281        buf.push(1);
282        buf.push(2);
283        buf.push(3);
284        buf.push(4);
285        let num = UBig(Repr::from_buffer(buf));
286        assert_eq!(num.capacity(), 7);
287
288        let mut buf = Buffer::allocate(100);
289        buf.push(1);
290        buf.push(2);
291        buf.push(3);
292        buf.push(4);
293        let num = UBig(Repr::from_buffer(buf));
294        assert_eq!(num.capacity(), 6);
295    }
296
297    #[test]
298    fn test_clone() {
299        let a = UBig::from(5u8);
300        assert_eq!(a.clone(), a);
301
302        let a = gen_ubig(10);
303        let b = a.clone();
304        assert_eq!(a, b);
305        assert_eq!(a.capacity(), b.capacity());
306    }
307
308    #[test]
309    fn test_clone_from() {
310        let num: UBig = gen_ubig(10);
311
312        let mut a = UBig::from(3u8);
313        a.clone_from(&num);
314        assert_eq!(a, num);
315        let b = UBig::from(7u8);
316        a.clone_from(&b);
317        assert_eq!(a, b);
318        a.clone_from(&b);
319        assert_eq!(a, b);
320
321        let mut a = gen_ubig(9);
322        let prev_cap = a.capacity();
323        a.clone_from(&num);
324        // the buffer should be reused, 9 is close enough to 10.
325        assert_eq!(a.capacity(), prev_cap);
326        assert_ne!(a.capacity(), num.capacity());
327
328        let mut a = gen_ubig(3);
329        let prev_cap = a.capacity();
330        a.clone_from(&num);
331        // the buffer should now be reallocated, it's too Small.
332        assert_ne!(a.capacity(), prev_cap);
333        assert_eq!(a.capacity(), num.capacity());
334
335        let mut a = gen_ubig(100);
336        let prev_cap = a.capacity();
337        a.clone_from(&num);
338        // the buffer should now be reallocated, it's too large.
339        assert_ne!(a.capacity(), prev_cap);
340        assert_eq!(a.capacity(), num.capacity());
341    }
342
343    #[test]
344    fn test_const_generation() {
345        const ZERO: UBig = UBig::from_word(0);
346        const ONE_SINGLE: UBig = UBig::from_word(1);
347        const ONE_DOUBLE: UBig = UBig::from_dword(1);
348        const DMAX: UBig = UBig::from_dword(DoubleWord::MAX);
349
350        const CDATA: [Word; 3] = [Word::MAX, Word::MAX, Word::MAX];
351        // SAFETY: DATA meets the requirements of from_static_words
352        static CONST_TMAX: UBig = unsafe { UBig::from_static_words(&CDATA) };
353        static DATA: [Word; 3] = [Word::MAX, Word::MAX, Word::MAX];
354        // SAFETY: DATA meets the requirements of from_static_words
355        static STATIC_TMAX: UBig = unsafe { UBig::from_static_words(&DATA) };
356
357        assert_eq!(ZERO, UBig::ZERO);
358        assert_eq!(ONE_SINGLE, UBig::ONE);
359        assert_eq!(ONE_DOUBLE, UBig::ONE);
360        assert_eq!(DMAX.capacity(), 2);
361        assert_eq!(CONST_TMAX.capacity(), 3);
362        assert_eq!(STATIC_TMAX.capacity(), 3);
363    }
364}