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