dashu_int/ibig.rs
1//! Signed big integer.
2
3use crate::{
4 repr::{Repr, TypedRepr, TypedReprRef},
5 Sign, UBig,
6};
7
8/// An signed arbitrary precision integer.
9///
10/// This struct represents an arbitrarily large signed integer. Technically the size of the integer
11/// is bounded by the memory size, but it's enough for practical use on modern devices.
12///
13/// # Parsing and printing
14///
15/// There are four ways to create an [IBig] instance:
16/// 1. Use predifined constants (e.g. [IBig::ZERO], [IBig::NEG_ONE]).
17/// 1. Use the literal macro `ibig!` defined in the [`dashu-macro`](https://docs.rs/dashu-macros/latest/dashu_macros/) crate.
18/// 1. Construct from a [Sign] and a [UBig] instance.
19/// 1. Parse from a string.
20///
21/// Parsing from either literal or string supports representation with base 2~36.
22///
23/// For printing, the [IBig] type supports common formatting traits ([Display][core::fmt::Display],
24/// [Debug][core::fmt::Debug], [LowerHex][core::fmt::LowerHex], etc.). Specially, printing huge number
25/// using [Debug][core::fmt::Debug] will conveniently omit the middle digits of the number, only print
26/// the least and most significant (decimal) digits.
27///
28/// ```
29/// // parsing
30/// # use dashu_base::ParseError;
31/// # use dashu_int::{IBig, Word};
32/// let a = IBig::from(408580953453092208335085386466371u128);
33/// let b = IBig::from(-0x1231abcd4134i64);
34/// let c = IBig::from_str_radix("a2a123bbb127779cccc123", 32)?;
35/// let d = IBig::from_str_radix("-1231abcd4134", 16)?;
36/// assert_eq!(a, c);
37/// assert_eq!(b, d);
38///
39/// // printing
40/// assert_eq!(format!("{}", IBig::from(12)), "12");
41/// assert_eq!(format!("{:#X}", IBig::from(-0xabcd)), "-0xABCD");
42/// if Word::BITS == 64 {
43/// // number of digits to display depends on the word size
44/// assert_eq!(
45/// format!("{:?}", IBig::NEG_ONE << 1000),
46/// "-1071508607186267320..4386837205668069376"
47/// );
48/// }
49/// # Ok::<(), ParseError>(())
50/// ```
51///
52/// # Memory
53///
54/// The internal representation of [IBig] is exactly the same as [UBig]. It just use a
55/// small trick to store the sign bit without additional memory allocation. This means that
56/// [IBig] also has the small integer optimization and the niche bit to use with simple
57/// enums.
58///
59/// ```
60/// # use dashu_int::{IBig, UBig};
61/// use core::mem::size_of;
62/// assert_eq!(size_of::<IBig>(), size_of::<UBig>());
63/// assert_eq!(size_of::<IBig>(), size_of::<Option<IBig>>());
64/// ```
65///
66#[derive(Eq, Hash, PartialEq)]
67#[repr(transparent)]
68pub struct IBig(pub(crate) Repr);
69
70impl IBig {
71 #[rustversion::attr(since(1.64), const)]
72 #[inline]
73 pub(crate) fn as_sign_repr(&self) -> (Sign, TypedReprRef<'_>) {
74 self.0.as_sign_typed()
75 }
76
77 #[inline]
78 pub(crate) fn into_sign_repr(self) -> (Sign, TypedRepr) {
79 self.0.into_sign_typed()
80 }
81
82 /// Get the raw representation in [Word][crate::Word]s.
83 ///
84 /// If the number is zero, then empty slice will be returned.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// # use dashu_int::{IBig, Sign};
90 /// assert_eq!(IBig::ZERO.as_sign_words(), (Sign::Positive, &[] as &[_]));
91 /// assert_eq!(IBig::NEG_ONE.as_sign_words().0, Sign::Negative);
92 /// assert_eq!(IBig::NEG_ONE.as_sign_words().1, &[1]);
93 /// ```
94 #[inline]
95 pub fn as_sign_words(&self) -> (Sign, &[crate::Word]) {
96 self.0.as_sign_slice()
97 }
98
99 /// Get the sign of the number. Zero value has a positive sign.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// # use dashu_int::{IBig, Sign};
105 /// assert_eq!(IBig::ZERO.sign(), Sign::Positive);
106 /// assert_eq!(IBig::from(2).sign(), Sign::Positive);
107 /// assert_eq!(IBig::from(-3).sign(), Sign::Negative);
108 /// ```
109 #[inline]
110 pub const fn sign(&self) -> Sign {
111 self.0.sign()
112 }
113
114 /// Convert the [IBig] into its [Sign] and [UBig] magnitude
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// # use dashu_int::{IBig, Sign, UBig};
120 /// assert_eq!(IBig::ZERO.into_parts(), (Sign::Positive, UBig::ZERO));
121 /// assert_eq!(IBig::ONE.into_parts(), (Sign::Positive, UBig::ONE));
122 /// assert_eq!(IBig::NEG_ONE.into_parts(), (Sign::Negative, UBig::ONE));
123 /// ```
124 #[inline]
125 pub fn into_parts(self) -> (Sign, UBig) {
126 let sign = self.0.sign();
127 let mag = self.0.with_sign(Sign::Positive);
128 (sign, UBig(mag))
129 }
130
131 /// Create an [IBig] from the [Sign] and [UBig] magnitude
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// # use dashu_int::{IBig, Sign, UBig};
137 /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ZERO), IBig::ZERO);
138 /// assert_eq!(IBig::from_parts(Sign::Positive, UBig::ONE), IBig::ONE);
139 /// assert_eq!(IBig::from_parts(Sign::Negative, UBig::ONE), IBig::NEG_ONE);
140 /// ```
141 #[inline]
142 pub fn from_parts(sign: Sign, magnitude: UBig) -> Self {
143 IBig(magnitude.0.with_sign(sign))
144 }
145
146 /// Create an IBig in a const context.
147 ///
148 /// The magnitude is limited to a [DoubleWord][crate::DoubleWord].
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// # use dashu_int::{IBig, Sign, UBig};
154 /// const ONE: IBig = IBig::from_parts_const(Sign::Positive, 1);
155 /// assert_eq!(ONE, IBig::ONE);
156 /// const NEG_ONE: IBig = IBig::from_parts_const(Sign::Negative, 1);
157 /// assert_eq!(NEG_ONE, IBig::NEG_ONE);
158 /// ```
159 #[inline]
160 pub const fn from_parts_const(sign: Sign, dword: crate::DoubleWord) -> Self {
161 Self(Repr::from_dword(dword).with_sign(sign))
162 }
163
164 /// Create an IBig from an i64.
165 ///
166 /// This function is const on 32-bit and 64-bit targets.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// # use dashu_int::IBig;
172 /// assert_eq!(IBig::from_i64(-42), IBig::from(-42i64));
173 /// assert_eq!(IBig::from_i64(42), IBig::from(42i64));
174 /// ```
175 #[cfg(not(any(target_pointer_width = "16", force_bits = "16")))]
176 #[inline]
177 pub const fn from_i64(n: i64) -> Self {
178 let sign = if n >= 0 {
179 Sign::Positive
180 } else {
181 Sign::Negative
182 };
183 let mag = n.unsigned_abs() as crate::DoubleWord;
184 Self(Repr::from_dword(mag).with_sign(sign))
185 }
186
187 #[cfg(any(target_pointer_width = "16", force_bits = "16"))]
188 #[inline]
189 pub fn from_i64(n: i64) -> Self {
190 Self::from(n)
191 }
192
193 /// Create an IBig from a static sequence of [Word][crate::Word]s and a sign.
194 ///
195 /// See [UBig::from_static_words] for why this method is unsafe. This method
196 /// is intended for the use of static creation macros.
197 #[doc(hidden)]
198 #[inline]
199 pub const unsafe fn from_static_words(sign: Sign, words: &'static [crate::Word]) -> Self {
200 Self(Repr::from_static_words(words).with_sign(sign))
201 }
202
203 /// [IBig] with value 0
204 pub const ZERO: Self = Self(Repr::zero());
205 /// [IBig] with value 1
206 pub const ONE: Self = Self(Repr::one());
207 /// [IBig] with value -1
208 pub const NEG_ONE: Self = Self(Repr::neg_one());
209
210 /// Check whether the number is 0
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// # use dashu_int::IBig;
216 /// assert!(IBig::ZERO.is_zero());
217 /// assert!(!IBig::ONE.is_zero());
218 /// ```
219 #[inline]
220 pub const fn is_zero(&self) -> bool {
221 self.0.is_zero()
222 }
223
224 /// Check whether the number is 1
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// # use dashu_int::IBig;
230 /// assert!(!IBig::ZERO.is_one());
231 /// assert!(IBig::ONE.is_one());
232 /// ```
233 #[inline]
234 pub const fn is_one(&self) -> bool {
235 self.0.is_one()
236 }
237}
238
239// This custom implementation is necessary due to https://github.com/rust-lang/rust/issues/98374
240impl Clone for IBig {
241 #[inline]
242 fn clone(&self) -> IBig {
243 IBig(self.0.clone())
244 }
245 #[inline]
246 fn clone_from(&mut self, source: &IBig) {
247 self.0.clone_from(&source.0)
248 }
249}