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    #[rustversion::attr(since(1.64), const)]
60    #[inline]
61    pub(crate) fn as_sign_repr(&self) -> (Sign, TypedReprRef<'_>) {
62        self.0.as_sign_typed()
63    }
64
65    #[inline]
66    pub(crate) fn into_sign_repr(self) -> (Sign, TypedRepr) {
67        self.0.into_sign_typed()
68    }
69
70    /// Get the raw representation in [Word][crate::Word]s.
71    ///
72    /// If the number is zero, then empty slice will be returned.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use dashu_int::{IBig, Sign};
78    /// assert_eq!(IBig::ZERO.as_sign_words(), (Sign::Positive, &[] as &[_]));
79    /// assert_eq!(IBig::NEG_ONE.as_sign_words().0, Sign::Negative);
80    /// assert_eq!(IBig::NEG_ONE.as_sign_words().1, &[1]);
81    /// ```
82    #[inline]
83    pub fn as_sign_words(&self) -> (Sign, &[crate::Word]) {
84        self.0.as_sign_slice()
85    }
86
87    /// Get the sign of the number. Zero value has a positive sign.
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// # use dashu_int::{IBig, Sign};
93    /// assert_eq!(IBig::ZERO.sign(), Sign::Positive);
94    /// assert_eq!(IBig::from(2).sign(), Sign::Positive);
95    /// assert_eq!(IBig::from(-3).sign(), Sign::Negative);
96    /// ```
97    #[inline]
98    pub const fn sign(&self) -> Sign {
99        self.0.sign()
100    }
101
102    /// Convert the [IBig] into its [Sign] and [UBig] magnitude
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// # use dashu_int::{IBig, Sign, UBig};
108    /// assert_eq!(IBig::ZERO.into_parts(), (Sign::Positive, UBig::ZERO));
109    /// assert_eq!(IBig::ONE.into_parts(), (Sign::Positive, UBig::ONE));
110    /// assert_eq!(IBig::NEG_ONE.into_parts(), (Sign::Negative, UBig::ONE));
111    /// ```
112    #[inline]
113    pub fn into_parts(self) -> (Sign, UBig) {
114        let sign = self.0.sign();
115        let mag = self.0.with_sign(Sign::Positive);
116        (sign, UBig(mag))
117    }
118
119    /// Create an [IBig] from the [Sign] and [UBig] magnitude
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # use dashu_int::{IBig, Sign, UBig};
125    /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ZERO), IBig::ZERO);
126    /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ONE), IBig::ONE);
127    /// assert_eq!(IBig::from_parts(Sign::Negative, UBig::ONE), IBig::NEG_ONE);
128    /// ```
129    #[inline]
130    pub fn from_parts(sign: Sign, magnitude: UBig) -> Self {
131        IBig(magnitude.0.with_sign(sign))
132    }
133
134    /// Create an IBig in a const context.
135    ///
136    /// The magnitude is limited to a [DoubleWord][crate::DoubleWord].
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// # use dashu_int::{IBig, Sign, UBig};
142    /// const ONE: IBig = IBig::from_parts_const(Sign::Positive, 1);
143    /// assert_eq!(ONE, IBig::ONE);
144    /// const NEG_ONE: IBig = IBig::from_parts_const(Sign::Negative, 1);
145    /// assert_eq!(NEG_ONE, IBig::NEG_ONE);
146    /// ```
147    #[inline]
148    pub const fn from_parts_const(sign: Sign, dword: crate::DoubleWord) -> Self {
149        Self(Repr::from_dword(dword).with_sign(sign))
150    }
151
152    /// Create an IBig from an i64.
153    ///
154    /// This function is const on 32-bit and 64-bit targets.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// # use dashu_int::IBig;
160    /// assert_eq!(IBig::from_i64(-42), IBig::from(-42i64));
161    /// assert_eq!(IBig::from_i64(42), IBig::from(42i64));
162    /// ```
163    #[cfg(not(any(target_pointer_width = "16", force_bits = "16")))]
164    #[inline]
165    pub const fn from_i64(n: i64) -> Self {
166        let sign = if n >= 0 {
167            Sign::Positive
168        } else {
169            Sign::Negative
170        };
171        let mag = n.unsigned_abs() as crate::DoubleWord;
172        Self(Repr::from_dword(mag).with_sign(sign))
173    }
174
175    /// Create an IBig from an i64.
176    ///
177    /// On 16-bit targets `i64` is wider than [`DoubleWord`][crate::DoubleWord], so this delegates
178    /// to `From<i64>` and is not `const`; on 32-bit and 64-bit targets the `const` constructor
179    /// above is used instead.
180    #[cfg(any(target_pointer_width = "16", force_bits = "16"))]
181    #[inline]
182    pub fn from_i64(n: i64) -> Self {
183        Self::from(n)
184    }
185
186    /// Create an IBig from a static sequence of [Word][crate::Word]s and a sign.
187    ///
188    /// See [UBig::from_static_words] for why this method is unsafe. This method
189    /// is intended for the use of static creation macros.
190    #[doc(hidden)]
191    #[inline]
192    pub const unsafe fn from_static_words(sign: Sign, words: &'static [crate::Word]) -> Self {
193        Self(Repr::from_static_words(words).with_sign(sign))
194    }
195
196    /// [IBig] with value 0
197    pub const ZERO: Self = Self(Repr::zero());
198    /// [IBig] with value 1
199    pub const ONE: Self = Self(Repr::one());
200    /// [IBig] with value -1
201    pub const NEG_ONE: Self = Self(Repr::neg_one());
202
203    /// Check whether the number is 0
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// # use dashu_int::IBig;
209    /// assert!(IBig::ZERO.is_zero());
210    /// assert!(!IBig::ONE.is_zero());
211    /// ```
212    #[inline]
213    pub const fn is_zero(&self) -> bool {
214        self.0.is_zero()
215    }
216
217    /// Check whether the number is 1
218    ///
219    /// # Examples
220    ///
221    /// ```
222    /// # use dashu_int::IBig;
223    /// assert!(!IBig::ZERO.is_one());
224    /// assert!(IBig::ONE.is_one());
225    /// ```
226    #[inline]
227    pub const fn is_one(&self) -> bool {
228        self.0.is_one()
229    }
230}
231
232// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
233impl Clone for IBig {
234    #[inline]
235    fn clone(&self) -> IBig {
236        IBig(self.0.clone())
237    }
238    #[inline]
239    fn clone_from(&mut self, source: &IBig) {
240        self.0.clone_from(&source.0)
241    }
242}