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}