1use alloc::string::ToString;
2use alloc::vec::Vec;
3use core::cmp::Ordering;
4use core::fmt;
5use core::str::FromStr;
6
7use crate::error::Error;
8use crate::event::Atom;
9use crate::ext::Extension;
10use crate::ext::known::{WellKnown, impl_well_known, invalid};
11
12#[derive(Clone, Default)]
40pub struct BigInt {
41 pub negative: bool,
43 pub magnitude: Vec<u8>,
45}
46
47impl BigInt {
48 pub fn significant_magnitude(&self) -> &[u8] {
50 let skip = self.magnitude.iter().take_while(|&&x| x == 0).count();
51 &self.magnitude[skip..]
52 }
53
54 pub fn is_zero(&self) -> bool {
56 self.significant_magnitude().is_empty()
57 }
58
59 pub fn is_negative(&self) -> bool {
61 self.negative && !self.is_zero()
62 }
63
64 fn magnitude_u128(&self) -> Option<u128> {
66 let significant = self.significant_magnitude();
67 if significant.len() > 16 {
68 return None;
69 }
70 let mut buf = [0u8; 16];
71 buf[16 - significant.len()..].copy_from_slice(significant);
72 Some(u128::from_be_bytes(buf))
73 }
74
75 pub fn to_u128(&self) -> Option<u128> {
77 if self.is_negative() {
78 None
79 } else {
80 self.magnitude_u128()
81 }
82 }
83
84 pub fn to_i128(&self) -> Option<i128> {
86 let magnitude = self.magnitude_u128()?;
87 if self.is_negative() {
88 0i128.checked_sub_unsigned(magnitude)
89 } else {
90 i128::try_from(magnitude).ok()
91 }
92 }
93
94 pub fn into_atom(self) -> Atom<'static> {
100 use crate::ext::ExtValue;
101 if let Some(value) = self.to_u128() {
102 match u64::try_from(value) {
103 Ok(value) => Atom::U64(value),
104 Err(_) => Atom::Ext(ExtValue::owned(value)),
105 }
106 } else if let Some(value) = self.to_i128() {
107 match i64::try_from(value) {
108 Ok(value) => Atom::I64(value),
109 Err(_) => Atom::Ext(ExtValue::owned(value)),
110 }
111 } else {
112 Atom::Ext(ExtValue::owned(self))
113 }
114 }
115}
116
117impl PartialEq for BigInt {
118 fn eq(&self, other: &BigInt) -> bool {
119 self.is_negative() == other.is_negative()
120 && self.significant_magnitude() == other.significant_magnitude()
121 }
122}
123
124impl Eq for BigInt {}
125
126impl core::hash::Hash for BigInt {
127 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
128 self.is_negative().hash(state);
129 self.significant_magnitude().hash(state);
130 }
131}
132
133impl PartialOrd for BigInt {
134 fn partial_cmp(&self, other: &BigInt) -> Option<Ordering> {
135 Some(self.cmp(other))
136 }
137}
138
139impl Ord for BigInt {
140 fn cmp(&self, other: &BigInt) -> Ordering {
141 let (a, b) = (self.significant_magnitude(), other.significant_magnitude());
142 let magnitude = a.len().cmp(&b.len()).then_with(|| a.cmp(b));
143 match (self.is_negative(), other.is_negative()) {
144 (false, false) => magnitude,
145 (true, true) => magnitude.reverse(),
146 (false, true) => Ordering::Greater,
147 (true, false) => Ordering::Less,
148 }
149 }
150}
151
152impl From<i128> for BigInt {
153 fn from(value: i128) -> BigInt {
154 let mut rv = BigInt::from(value.unsigned_abs());
155 rv.negative = value < 0;
156 rv
157 }
158}
159
160impl From<u128> for BigInt {
161 fn from(value: u128) -> BigInt {
162 let bytes = value.to_be_bytes();
163 let skip = (value.leading_zeros() / 8) as usize;
164 BigInt {
165 negative: false,
166 magnitude: bytes[skip..].to_vec(),
167 }
168 }
169}
170
171impl From<i64> for BigInt {
172 fn from(value: i64) -> BigInt {
173 BigInt::from(i128::from(value))
174 }
175}
176
177impl From<u64> for BigInt {
178 fn from(value: u64) -> BigInt {
179 BigInt::from(u128::from(value))
180 }
181}
182
183impl fmt::Display for BigInt {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 let mut value = self.significant_magnitude().to_vec();
187 let mut chunks = Vec::new();
188 while !value.is_empty() {
189 let mut remainder = 0u64;
190 for byte in value.iter_mut() {
191 let current = remainder << 8 | u64::from(*byte);
192 *byte = (current / 1_000_000_000) as u8;
193 remainder = current % 1_000_000_000;
194 }
195 chunks.push(remainder as u32);
196 let skip = value.iter().take_while(|&&x| x == 0).count();
197 value.drain(..skip);
198 }
199 if self.is_negative() {
200 f.write_str("-")?;
201 }
202 match chunks.pop() {
203 None => f.write_str("0"),
204 Some(first) => {
205 write!(f, "{}", first)?;
206 for chunk in chunks.iter().rev() {
207 write!(f, "{:09}", chunk)?;
208 }
209 Ok(())
210 }
211 }
212 }
213}
214
215impl fmt::Debug for BigInt {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 write!(f, "BigInt({})", self)
218 }
219}
220
221impl FromStr for BigInt {
222 type Err = Error;
223
224 fn from_str(s: &str) -> Result<BigInt, Error> {
226 let (negative, digits) = match s.as_bytes().first() {
227 Some(b'-') => (true, &s[1..]),
228 Some(b'+') => (false, &s[1..]),
229 _ => (false, s),
230 };
231 if digits.is_empty() || !digits.bytes().all(|x| x.is_ascii_digit()) {
232 return Err(invalid("invalid integer"));
233 }
234 let mut magnitude: Vec<u8> = Vec::new();
236 let first = digits.len() % 9;
237 let chunks = (first != 0).then(|| &digits[..first]).into_iter().chain(
238 digits.as_bytes()[first..].chunks(9).map(|x| {
239 core::str::from_utf8(x).unwrap()
241 }),
242 );
243 for chunk in chunks {
244 let factor = 10u64.pow(chunk.len() as u32);
245 let mut carry: u64 = chunk.parse().unwrap();
246 for byte in magnitude.iter_mut().rev() {
247 let current = u64::from(*byte) * factor + carry;
248 *byte = current as u8;
249 carry = current >> 8;
250 }
251 while carry != 0 {
252 magnitude.insert(0, carry as u8);
253 carry >>= 8;
254 }
255 }
256 let rv = BigInt {
257 negative,
258 magnitude,
259 };
260 Ok(BigInt {
261 negative: rv.is_negative(),
262 ..rv
263 })
264 }
265}
266
267impl Extension for BigInt {
268 fn name(&self) -> &str {
269 "big integer"
270 }
271
272 fn fallback(&self) -> Atom<'_> {
273 Atom::Str(self.to_string().into())
274 }
275}
276
277impl WellKnown for BigInt {
278 const EXPECTING: &'static str = "integer";
279
280 fn from_atom(atom: &Atom) -> Result<Option<BigInt>, Error> {
282 Ok(Some(match *atom {
283 Atom::Ext(ref ext) => {
284 if let Some(value) = ext.downcast_ref::<BigInt>() {
285 value.clone()
286 } else if let Some(&value) = ext.downcast_ref::<u128>() {
287 BigInt::from(value)
288 } else if let Some(&value) = ext.downcast_ref::<i128>() {
289 BigInt::from(value)
290 } else if let Some(value) = ext
291 .downcast_value_ref::<crate::ext::Number>()
292 .filter(|x| x.is_integer())
293 {
294 value.as_str().parse()?
296 } else {
297 return Ok(None);
298 }
299 }
300 Atom::U64(value) => BigInt::from(value),
301 Atom::I64(value) => BigInt::from(value),
302 Atom::Str(ref value) => value.parse()?,
303 _ => return Ok(None),
304 }))
305 }
306}
307
308impl_well_known!(BigInt);
309
310#[test]
311fn test_bigint() {
312 for s in [
313 "0",
314 "1",
315 "-1",
316 "255",
317 "256",
318 "999999999",
319 "1000000000",
320 "-18446744073709551616",
321 "340282366920938463463374607431768211456",
322 "-123456789012345678901234567890123456789012345678901234567890",
323 ] {
324 let value: BigInt = s.parse().unwrap();
325 assert_eq!(value.to_string(), s);
326 }
327 assert_eq!("-0".parse::<BigInt>().unwrap().to_string(), "0");
328 assert_eq!("+007".parse::<BigInt>().unwrap().to_string(), "7");
329 assert_eq!(BigInt::from(i128::MIN).to_i128(), Some(i128::MIN));
330 assert_eq!(BigInt::from(u128::MAX).to_u128(), Some(u128::MAX));
331 assert_eq!(BigInt::from(u128::MAX).to_i128(), None);
332 assert_eq!(BigInt::from(-1i64).to_u128(), None);
333 assert_eq!(BigInt::from(-1i64).into_atom(), Atom::I64(-1));
334 for invalid in ["", "-", "1.0", "1e5", " 1", "0x10"] {
335 assert!(invalid.parse::<BigInt>().is_err());
336 }
337 let n = |s: &str| s.parse::<BigInt>().unwrap();
338 assert_eq!(
339 BigInt {
340 negative: true,
341 magnitude: vec![0, 0]
342 },
343 n("0")
344 );
345 assert_eq!(
346 BigInt {
347 negative: false,
348 magnitude: vec![0, 1]
349 },
350 n("1")
351 );
352 let mut values = vec![n("256"), n("-1"), n("0"), n("-256"), n("255"), n("1")];
353 values.sort();
354 assert_eq!(
355 values,
356 [n("-256"), n("-1"), n("0"), n("1"), n("255"), n("256")]
357 );
358}