Skip to main content

dashu_int/
ibig.rs

1//! Signed big integer.
2
3use crate::{
4    repr::{Repr, TypedRepr, TypedReprRef},
5    Sign, UBig,
6};
7
8/// A signed arbitrary precision integer.
9///
10/// `IBig` represents an arbitrarily large signed integer. It shares [`UBig`]'s representation — the
11/// sign bit is folded in without extra allocation — so it has the same small-integer inlining (values
12/// that fit in a [`DoubleWord`](crate::DoubleWord) stay on the stack) and the same niche bit, making
13/// [`Option<IBig>`] the same size as [`IBig`].
14///
15/// For the full discussion — construction, parsing, printing, and the memory layout — see the
16/// [user guide](https://zyxin.xyz/dashu/types.html).
17///
18/// # Examples
19///
20/// Parsing and printing (base 2–36 is supported for string/literal parsing):
21///
22/// ```
23/// // parsing
24/// # use dashu_base::ParseError;
25/// # use dashu_int::{IBig, Word};
26/// let a = IBig::from(408580953453092208335085386466371u128);
27/// let b = IBig::from(-0x1231abcd4134i64);
28/// let c = IBig::from_str_radix("a2a123bbb127779cccc123", 32)?;
29/// let d = IBig::from_str_radix("-1231abcd4134", 16)?;
30/// assert_eq!(a, c);
31/// assert_eq!(b, d);
32///
33/// // printing
34/// assert_eq!(format!("{}", IBig::from(12)), "12");
35/// assert_eq!(format!("{:#X}", IBig::from(-0xabcd)), "-0xABCD");
36/// if Word::BITS == 64 {
37///     // number of digits to display depends on the word size
38///     assert_eq!(
39///         format!("{:?}", IBig::NEG_ONE << 1000),
40///         "-1071508607186267320..4386837205668069376"
41///     );
42/// }
43/// # Ok::<(), ParseError>(())
44/// ```
45///
46/// The niche bit makes `Option<IBig>` free, and `IBig` matches `UBig` in size:
47///
48/// ```
49/// # use dashu_int::{IBig, UBig};
50/// use core::mem::size_of;
51/// assert_eq!(size_of::<IBig>(), size_of::<UBig>());
52/// assert_eq!(size_of::<IBig>(), size_of::<Option<IBig>>());
53/// ```
54#[derive(Eq, Hash, PartialEq)]
55#[repr(transparent)]
56pub struct IBig(pub(crate) Repr);
57
58impl IBig {
59    #[inline]
60    pub(crate) const fn as_sign_repr(&self) -> (Sign, TypedReprRef<'_>) {
61        self.0.as_sign_typed()
62    }
63
64    #[inline]
65    pub(crate) fn into_sign_repr(self) -> (Sign, TypedRepr) {
66        self.0.into_sign_typed()
67    }
68
69    /// Get the raw representation in [Word][crate::Word]s.
70    ///
71    /// If the number is zero, then empty slice will be returned.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// # use dashu_int::{IBig, Sign};
77    /// assert_eq!(IBig::ZERO.as_sign_words(), (Sign::Positive, &[] as &[_]));
78    /// assert_eq!(IBig::NEG_ONE.as_sign_words().0, Sign::Negative);
79    /// assert_eq!(IBig::NEG_ONE.as_sign_words().1, &[1]);
80    /// ```
81    #[inline]
82    pub fn as_sign_words(&self) -> (Sign, &[crate::Word]) {
83        self.0.as_sign_slice()
84    }
85
86    /// Get the sign of the number. Zero value has a positive sign.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use dashu_int::{IBig, Sign};
92    /// assert_eq!(IBig::ZERO.sign(), Sign::Positive);
93    /// assert_eq!(IBig::from(2).sign(), Sign::Positive);
94    /// assert_eq!(IBig::from(-3).sign(), Sign::Negative);
95    /// ```
96    #[inline]
97    pub const fn sign(&self) -> Sign {
98        self.0.sign()
99    }
100
101    /// Convert the [IBig] into its [Sign] and [UBig] magnitude
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// # use dashu_int::{IBig, Sign, UBig};
107    /// assert_eq!(IBig::ZERO.into_parts(), (Sign::Positive, UBig::ZERO));
108    /// assert_eq!(IBig::ONE.into_parts(), (Sign::Positive, UBig::ONE));
109    /// assert_eq!(IBig::NEG_ONE.into_parts(), (Sign::Negative, UBig::ONE));
110    /// ```
111    #[inline]
112    pub fn into_parts(self) -> (Sign, UBig) {
113        let sign = self.0.sign();
114        let mag = self.0.with_sign(Sign::Positive);
115        (sign, UBig(mag))
116    }
117
118    /// Create an [IBig] from the [Sign] and [UBig] magnitude
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// # use dashu_int::{IBig, Sign, UBig};
124    /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ZERO), IBig::ZERO);
125    /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ONE), IBig::ONE);
126    /// assert_eq!(IBig::from_parts(Sign::Negative, UBig::ONE), IBig::NEG_ONE);
127    /// ```
128    #[inline]
129    pub fn from_parts(sign: Sign, magnitude: UBig) -> Self {
130        IBig(magnitude.0.with_sign(sign))
131    }
132
133    /// Create an IBig in a const context.
134    ///
135    /// The magnitude is limited to a [DoubleWord][crate::DoubleWord].
136    ///
137    /// # Examples
138    ///
139    /// ```
140    /// # use dashu_int::{IBig, Sign, UBig};
141    /// const ONE: IBig = IBig::from_parts_const(Sign::Positive, 1);
142    /// assert_eq!(ONE, IBig::ONE);
143    /// const NEG_ONE: IBig = IBig::from_parts_const(Sign::Negative, 1);
144    /// assert_eq!(NEG_ONE, IBig::NEG_ONE);
145    /// ```
146    #[inline]
147    pub const fn from_parts_const(sign: Sign, dword: crate::DoubleWord) -> Self {
148        Self(Repr::from_dword(dword).with_sign(sign))
149    }
150
151    /// Create an IBig from an i64.
152    ///
153    /// This function is const on 32-bit and 64-bit targets.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// # use dashu_int::IBig;
159    /// assert_eq!(IBig::from_i64(-42), IBig::from(-42i64));
160    /// assert_eq!(IBig::from_i64(42), IBig::from(42i64));
161    /// ```
162    #[cfg(not(any(target_pointer_width = "16", force_bits = "16")))]
163    #[inline]
164    pub const fn from_i64(n: i64) -> Self {
165        let sign = if n >= 0 {
166            Sign::Positive
167        } else {
168            Sign::Negative
169        };
170        let mag = n.unsigned_abs() as crate::DoubleWord;
171        Self(Repr::from_dword(mag).with_sign(sign))
172    }
173
174    /// Create an IBig from an i64.
175    ///
176    /// On 16-bit targets `i64` is wider than [`DoubleWord`][crate::DoubleWord], so this delegates
177    /// to `From<i64>` and is not `const`; on 32-bit and 64-bit targets the `const` constructor
178    /// above is used instead.
179    #[cfg(any(target_pointer_width = "16", force_bits = "16"))]
180    #[inline]
181    pub fn from_i64(n: i64) -> Self {
182        Self::from(n)
183    }
184
185    /// Create an IBig from a static sequence of [Word][crate::Word]s and a sign.
186    ///
187    /// See [UBig::from_static_words] for why this method is unsafe. This method
188    /// is intended for the use of static creation macros.
189    #[doc(hidden)]
190    #[inline]
191    pub const unsafe fn from_static_words(sign: Sign, words: &'static [crate::Word]) -> Self {
192        Self(Repr::from_static_words(words).with_sign(sign))
193    }
194
195    /// [IBig] with value 0
196    pub const ZERO: Self = Self(Repr::zero());
197    /// [IBig] with value 1
198    pub const ONE: Self = Self(Repr::one());
199    /// [IBig] with value -1
200    pub const NEG_ONE: Self = Self(Repr::neg_one());
201
202    /// Check whether the number is 0
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// # use dashu_int::IBig;
208    /// assert!(IBig::ZERO.is_zero());
209    /// assert!(!IBig::ONE.is_zero());
210    /// ```
211    #[inline]
212    pub const fn is_zero(&self) -> bool {
213        self.0.is_zero()
214    }
215
216    /// Check whether the number is 1
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// # use dashu_int::IBig;
222    /// assert!(!IBig::ZERO.is_one());
223    /// assert!(IBig::ONE.is_one());
224    /// ```
225    #[inline]
226    pub const fn is_one(&self) -> bool {
227        self.0.is_one()
228    }
229}
230
231// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
232impl Clone for IBig {
233    #[inline]
234    fn clone(&self) -> IBig {
235        IBig(self.0.clone())
236    }
237    #[inline]
238    fn clone_from(&mut self, source: &IBig) {
239        self.0.clone_from(&source.0)
240    }
241}