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