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 /// Convert a sequence of [Word][crate::Word]s into a UBig
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// # use dashu_int::{UBig, Word};
148 /// assert_eq!(UBig::from_words(&[] as &[Word]), UBig::ZERO);
149 /// assert_eq!(UBig::from_words(&[1]), UBig::ONE);
150 /// assert_eq!(UBig::from_words(&[1, 1]), (UBig::ONE << Word::BITS as usize) + UBig::ONE);
151 /// ```
152 #[inline]
153 pub fn from_words(words: &[crate::Word]) -> Self {
154 Self(Repr::from_buffer(words.into()))
155 }
156
157 /// Create an UBig from a static sequence of [Word][crate::Word]s. Similar to [from_words][UBig::from_words].
158 ///
159 /// The top word of the input word array must not be zero.
160 ///
161 /// This method is unsafe because it must be carefully handled. The generated instance
162 /// must not be mutated or dropped. Therefore the correct usage is to assign it to an
163 /// immutable static variable. Due to the risk, it's generally not recommended to use this method.
164 /// This method is intended for the use of static creation macros.
165 #[doc(hidden)]
166 #[inline]
167 pub const unsafe fn from_static_words(words: &'static [crate::Word]) -> Self {
168 Self(Repr::from_static_words(words))
169 }
170
171 /// Check whether the value is 0
172 ///
173 /// # Examples
174 ///
175 /// ```
176 /// # use dashu_int::UBig;
177 /// assert!(UBig::ZERO.is_zero());
178 /// assert!(!UBig::ONE.is_zero());
179 /// ```
180 #[inline]
181 pub const fn is_zero(&self) -> bool {
182 self.0.is_zero()
183 }
184
185 /// Check whether the value is 1
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// # use dashu_int::UBig;
191 /// assert!(!UBig::ZERO.is_one());
192 /// assert!(UBig::ONE.is_one());
193 /// ```
194 #[inline]
195 pub const fn is_one(&self) -> bool {
196 self.0.is_one()
197 }
198
199 /// Create an integer with `n` consecutive one bits (i.e. 2^n - 1).
200 ///
201 /// # Examples
202 ///
203 /// ```
204 /// # use dashu_int::UBig;
205 /// let mut n = UBig::ZERO;
206 /// n.set_bit(20);
207 /// n -= UBig::ONE;
208 /// assert_eq!(UBig::ones(20), n);
209 /// ```
210 #[inline]
211 pub fn ones(n: usize) -> Self {
212 Self(Repr::ones(n))
213 }
214}
215
216// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
217impl Clone for UBig {
218 #[inline]
219 fn clone(&self) -> UBig {
220 UBig(self.0.clone())
221 }
222 #[inline]
223 fn clone_from(&mut self, source: &UBig) {
224 self.0.clone_from(&source.0)
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use crate::{buffer::Buffer, DoubleWord, Word};
232
233 impl UBig {
234 /// Capacity in Words.
235 #[inline]
236 fn capacity(&self) -> usize {
237 self.0.capacity()
238 }
239 }
240
241 fn gen_ubig(num_words: u16) -> UBig {
242 let mut buf = Buffer::allocate(num_words.into());
243 for i in 0..num_words {
244 buf.push(i.into());
245 }
246 UBig(Repr::from_buffer(buf))
247 }
248
249 #[test]
250 fn test_buffer_to_ubig() {
251 let buf = Buffer::allocate(5);
252 let num = UBig(Repr::from_buffer(buf));
253 assert_eq!(num, UBig::ZERO);
254
255 let mut buf = Buffer::allocate(5);
256 buf.push(7);
257 let num = UBig(Repr::from_buffer(buf));
258 assert_eq!(num, UBig::from(7u8));
259
260 let mut buf = Buffer::allocate(100);
261 buf.push(7);
262 buf.push(0);
263 buf.push(0);
264 let num = UBig(Repr::from_buffer(buf));
265 assert_eq!(num, UBig::from(7u8));
266
267 let mut buf = Buffer::allocate(5);
268 buf.push(1);
269 buf.push(2);
270 buf.push(3);
271 buf.push(4);
272 let num = UBig(Repr::from_buffer(buf));
273 assert_eq!(num.capacity(), 7);
274
275 let mut buf = Buffer::allocate(100);
276 buf.push(1);
277 buf.push(2);
278 buf.push(3);
279 buf.push(4);
280 let num = UBig(Repr::from_buffer(buf));
281 assert_eq!(num.capacity(), 6);
282 }
283
284 #[test]
285 fn test_clone() {
286 let a = UBig::from(5u8);
287 assert_eq!(a.clone(), a);
288
289 let a = gen_ubig(10);
290 let b = a.clone();
291 assert_eq!(a, b);
292 assert_eq!(a.capacity(), b.capacity());
293 }
294
295 #[test]
296 fn test_clone_from() {
297 let num: UBig = gen_ubig(10);
298
299 let mut a = UBig::from(3u8);
300 a.clone_from(&num);
301 assert_eq!(a, num);
302 let b = UBig::from(7u8);
303 a.clone_from(&b);
304 assert_eq!(a, b);
305 a.clone_from(&b);
306 assert_eq!(a, b);
307
308 let mut a = gen_ubig(9);
309 let prev_cap = a.capacity();
310 a.clone_from(&num);
311 // the buffer should be reused, 9 is close enough to 10.
312 assert_eq!(a.capacity(), prev_cap);
313 assert_ne!(a.capacity(), num.capacity());
314
315 let mut a = gen_ubig(3);
316 let prev_cap = a.capacity();
317 a.clone_from(&num);
318 // the buffer should now be reallocated, it's too Small.
319 assert_ne!(a.capacity(), prev_cap);
320 assert_eq!(a.capacity(), num.capacity());
321
322 let mut a = gen_ubig(100);
323 let prev_cap = a.capacity();
324 a.clone_from(&num);
325 // the buffer should now be reallocated, it's too large.
326 assert_ne!(a.capacity(), prev_cap);
327 assert_eq!(a.capacity(), num.capacity());
328 }
329
330 #[test]
331 fn test_const_generation() {
332 const ZERO: UBig = UBig::from_word(0);
333 const ONE_SINGLE: UBig = UBig::from_word(1);
334 const ONE_DOUBLE: UBig = UBig::from_dword(1);
335 const DMAX: UBig = UBig::from_dword(DoubleWord::MAX);
336
337 const CDATA: [Word; 3] = [Word::MAX, Word::MAX, Word::MAX];
338 // SAFETY: DATA meets the requirements of from_static_words
339 static CONST_TMAX: UBig = unsafe { UBig::from_static_words(&CDATA) };
340 static DATA: [Word; 3] = [Word::MAX, Word::MAX, Word::MAX];
341 // SAFETY: DATA meets the requirements of from_static_words
342 static STATIC_TMAX: UBig = unsafe { UBig::from_static_words(&DATA) };
343
344 assert_eq!(ZERO, UBig::ZERO);
345 assert_eq!(ONE_SINGLE, UBig::ONE);
346 assert_eq!(ONE_DOUBLE, UBig::ONE);
347 assert_eq!(DMAX.capacity(), 2);
348 assert_eq!(CONST_TMAX.capacity(), 3);
349 assert_eq!(STATIC_TMAX.capacity(), 3);
350 }
351}