1use crate::element::Element;
2use crate::ion_data::{IonEq, IonOrd};
3use crate::result::{decoding_error, IonError};
4use num_bigint::{BigInt, BigUint, ToBigUint};
5use num_traits::{ToPrimitive, Zero};
6use std::cmp::Ordering;
7use std::fmt::{Display, Formatter};
8use std::ops::{Add, Neg};
9
10pub trait IntAccess {
12 fn as_i64(&self) -> Option<i64>;
31
32 fn as_big_int(&self) -> Option<&BigInt>;
62}
63
64#[derive(Debug, Clone)]
67pub enum UInt {
68 U64(u64),
69 BigUInt(BigUint),
70}
71
72impl UInt {
73 fn cross_representation_eq(m1: u64, m2: &BigUint) -> bool {
77 UInt::cross_representation_cmp(m1, m2) == Ordering::Equal
78 }
79
80 fn cross_representation_cmp(m1: u64, m2: &BigUint) -> Ordering {
84 if let Some(downgraded_m2) = m2.to_u64() {
86 return m1.cmp(&downgraded_m2);
88 }
89 Ordering::Less
91 }
92
93 pub(crate) fn number_of_decimal_digits(&self) -> u64 {
95 match self {
96 UInt::U64(u64_value) => super::num_decimal_digits_in_u64(*u64_value),
97 UInt::BigUInt(big_uint_value) => UInt::calculate_big_uint_digits(big_uint_value),
98 }
99 }
100
101 fn calculate_big_uint_digits(int: &BigUint) -> u64 {
102 if int.is_zero() {
103 return 1;
104 }
105 let mut digits = 0;
106 let mut dividend = int.to_owned();
107 let ten: BigUint = BigUint::from(10u64);
108 while dividend > BigUint::zero() {
109 dividend /= &ten;
110 digits += 1;
111 }
112 digits
113 }
114}
115
116impl PartialEq for UInt {
117 fn eq(&self, other: &Self) -> bool {
118 use UInt::*;
119 match (self, other) {
120 (U64(m1), U64(m2)) => m1 == m2,
121 (BigUInt(m1), BigUInt(m2)) => m1 == m2,
122 (U64(m1), BigUInt(m2)) => UInt::cross_representation_eq(*m1, m2),
123 (BigUInt(m1), U64(m2)) => UInt::cross_representation_eq(*m2, m1),
124 }
125 }
126}
127
128impl Eq for UInt {}
129
130impl PartialOrd for UInt {
131 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
132 Some(self.cmp(other))
133 }
134}
135
136impl Ord for UInt {
137 fn cmp(&self, other: &Self) -> Ordering {
138 use UInt::*;
139 match (self, other) {
140 (U64(m1), U64(m2)) => m1.cmp(m2),
141 (BigUInt(m1), BigUInt(m2)) => m1.cmp(m2),
142 (U64(m1), BigUInt(m2)) => UInt::cross_representation_cmp(*m1, m2),
143 (BigUInt(m1), U64(m2)) => UInt::cross_representation_cmp(*m2, m1).reverse(),
144 }
145 }
146}
147
148impl From<UInt> for Int {
149 fn from(value: UInt) -> Self {
150 match value {
151 UInt::U64(uint) => {
152 if let Ok(signed) = i64::try_from(uint) {
153 Int::I64(signed)
155 } else {
156 big_integer_from_u64(uint)
159 }
160 }
161 UInt::BigUInt(big_uint) => big_integer_from_big_uint(big_uint),
162 }
163 }
164}
165
166impl From<BigUint> for UInt {
167 fn from(value: BigUint) -> Self {
168 match value.to_u64() {
170 Some(unsigned) => UInt::U64(unsigned),
171 None => UInt::BigUInt(value),
172 }
173 }
174}
175
176impl From<UInt> for BigUint {
177 fn from(value: UInt) -> Self {
178 use UInt::*;
179 match value {
180 U64(m) => BigUint::from(m),
181 BigUInt(m) => m,
182 }
183 }
184}
185
186impl ToBigUint for UInt {
187 fn to_biguint(&self) -> Option<BigUint> {
188 Some(self.clone().into())
190 }
191}
192
193macro_rules! impl_uint_from_small_unsigned_int_types {
197 ($($t:ty),*) => ($(
198 impl From<$t> for UInt {
199 fn from(value: $t) -> UInt {
200 UInt::U64(value as u64)
201 }
202 }
203 )*)
204}
205
206impl_uint_from_small_unsigned_int_types!(u8, u16, u32, u64, usize);
207
208macro_rules! impl_uint_from_small_signed_int_types {
209 ($($t:ty),*) => ($(
210 impl From<$t> for UInt {
211 fn from(value: $t) -> UInt {
212 let abs_value = value.unsigned_abs();
213 UInt::U64(abs_value.try_into().unwrap())
214 }
215 }
216 )*)
217}
218
219impl_uint_from_small_signed_int_types!(i8, i16, i32, i64, isize);
220
221impl From<u128> for UInt {
222 fn from(value: u128) -> UInt {
223 UInt::BigUInt(BigUint::from(value))
224 }
225}
226
227impl From<i128> for UInt {
228 fn from(value: i128) -> UInt {
229 UInt::BigUInt(value.abs().to_biguint().unwrap())
230 }
231}
232
233impl From<Int> for UInt {
234 fn from(value: Int) -> Self {
235 match value {
236 Int::I64(i) => i.into(),
237 Int::BigInt(i) => i.into_parts().1.into(), }
242 }
243}
244
245#[inline(never)]
246fn big_integer_from_u64(value: u64) -> Int {
247 Int::BigInt(BigInt::from(value))
248}
249
250#[inline(never)]
251fn big_integer_from_big_uint(value: BigUint) -> Int {
252 Int::BigInt(BigInt::from(value))
253}
254
255impl TryFrom<&UInt> for i64 {
256 type Error = IonError;
257
258 fn try_from(value: &UInt) -> Result<Self, Self::Error> {
259 match value {
260 UInt::U64(uint) => i64::try_from(*uint).or_else(|_| {
261 decoding_error(format!(
262 "Unsigned integer {uint:?} was too large to be represented as an i64."
263 ))
264 }),
265 UInt::BigUInt(big_uint) => i64::try_from(big_uint).or_else(|_| {
266 decoding_error(format!(
267 "Unsigned integer {big_uint:?} was too large to be represented as an i64."
268 ))
269 }),
270 }
271 }
272}
273
274impl TryFrom<&UInt> for usize {
275 type Error = IonError;
276
277 fn try_from(value: &UInt) -> Result<Self, Self::Error> {
278 match value {
279 UInt::U64(uint) => usize::try_from(*uint).or_else(|_| {
280 decoding_error(format!(
281 "Unsigned integer {uint:?} was too large to be represented as an usize."
282 ))
283 }),
284 UInt::BigUInt(big_uint) => usize::try_from(big_uint).or_else(|_| {
285 decoding_error(format!(
286 "Unsigned integer {big_uint:?} was too large to be represented as an usize."
287 ))
288 }),
289 }
290 }
291}
292
293#[derive(Debug, Clone)]
298pub enum Int {
299 I64(i64),
300 BigInt(BigInt),
301}
302
303impl Int {
304 fn cross_representation_eq(m1: i64, m2: &BigInt) -> bool {
308 Int::cross_representation_cmp(m1, m2) == Ordering::Equal
309 }
310
311 fn cross_representation_cmp(m1: i64, m2: &BigInt) -> Ordering {
315 if let Some(downgraded_m2) = m2.to_i64() {
317 return m1.cmp(&downgraded_m2);
319 }
320 Ordering::Less
322 }
323}
324
325impl IntAccess for Int {
326 #[inline]
327 fn as_i64(&self) -> Option<i64> {
328 match &self {
329 Int::I64(i) => Some(*i),
330 Int::BigInt(big) => big.to_i64(),
331 }
332 }
333
334 #[inline]
335 fn as_big_int(&self) -> Option<&BigInt> {
336 match &self {
337 Int::I64(_) => None,
338 Int::BigInt(big) => Some(big),
339 }
340 }
341}
342
343impl PartialEq for Int {
344 fn eq(&self, other: &Self) -> bool {
345 use Int::*;
346 match (self, other) {
347 (I64(m1), I64(m2)) => m1 == m2,
348 (BigInt(m1), BigInt(m2)) => m1 == m2,
349 (I64(m1), BigInt(m2)) => Int::cross_representation_eq(*m1, m2),
350 (BigInt(m1), I64(m2)) => Int::cross_representation_eq(*m2, m1),
351 }
352 }
353}
354
355impl Eq for Int {}
356
357impl IonEq for Int {
358 fn ion_eq(&self, other: &Self) -> bool {
359 self == other
360 }
361}
362
363impl IonOrd for Int {
364 fn ion_cmp(&self, other: &Self) -> Ordering {
365 self.cmp(other)
366 }
367}
368
369impl Neg for Int {
370 type Output = Self;
371
372 fn neg(self) -> Self::Output {
373 use Int::*;
374 match self {
375 I64(value) => I64(-value),
376 BigInt(value) => BigInt(-value),
377 }
378 }
379}
380
381impl PartialOrd for Int {
382 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
383 Some(self.cmp(other))
384 }
385}
386
387impl Ord for Int {
388 fn cmp(&self, other: &Self) -> Ordering {
389 use Int::*;
390 match (self, other) {
391 (I64(m1), I64(m2)) => m1.cmp(m2),
392 (BigInt(m1), BigInt(m2)) => m1.cmp(m2),
393 (I64(m1), BigInt(m2)) => Int::cross_representation_cmp(*m1, m2),
394 (BigInt(m1), I64(m2)) => Int::cross_representation_cmp(*m2, m1).reverse(),
395 }
396 }
397}
398
399impl Add<Self> for Int {
400 type Output = Int;
401
402 fn add(self, rhs: Self) -> Self::Output {
403 use Int::{BigInt as Big, I64};
405 match (self, rhs) {
406 (I64(this), I64(that)) => {
407 match this.checked_add(that) {
409 Some(result) => I64(result),
410 None => Big(BigInt::from(this).add(BigInt::from(that))),
411 }
412 }
413 (I64(this), Big(that)) => Big(BigInt::from(this).add(that)),
414 (Big(this), I64(that)) => Big(this.add(&BigInt::from(that))),
415 (Big(this), Big(that)) => Big(this.add(&that)),
416 }
417 }
418}
419
420impl Zero for Int {
421 fn zero() -> Self {
422 Int::I64(0)
423 }
424
425 fn is_zero(&self) -> bool {
426 match self {
427 Int::I64(value) => *value == 0i64,
428 Int::BigInt(value) => value.is_zero(),
429 }
430 }
431}
432
433impl Display for UInt {
434 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
435 match &self {
436 UInt::U64(i) => write!(f, "{i}"),
437 UInt::BigUInt(i) => write!(f, "{i}"),
438 }
439 }
440}
441
442macro_rules! impl_int_i64_from {
444 ($($t:ty),*) => ($(
445 impl From<$t> for Int {
446 fn from(value: $t) -> Int {
447 let i64_value = i64::from(value);
448 Int::I64(i64_value)
449 }
450 }
451 )*)
452}
453impl_int_i64_from!(u8, u16, u32, i8, i16, i32, i64);
454
455macro_rules! impl_int_from {
457 ($($t:ty),*) => ($(
458 impl From<$t> for Int {
459 fn from(value: $t) -> Int {
460 match i64::try_from(value) {
461 Ok(i64_value) => Int::I64(i64_value),
462 Err(_) => Int::BigInt(BigInt::from(value))
463 }
464 }
465 }
466 )*)
467}
468
469impl_int_from!(isize, usize, u64);
470
471impl From<BigUint> for Int {
472 fn from(value: BigUint) -> Self {
473 let big_int = BigInt::from(value);
474 Int::BigInt(big_int)
475 }
476}
477
478impl From<BigInt> for Int {
479 fn from(value: BigInt) -> Self {
480 Int::BigInt(value)
481 }
482}
483
484impl IntAccess for Element {
485 fn as_i64(&self) -> Option<i64> {
486 match self.as_int() {
487 Some(any) => any.as_i64(),
488 _ => None,
489 }
490 }
491
492 fn as_big_int(&self) -> Option<&BigInt> {
493 match self.as_int() {
494 Some(any) => any.as_big_int(),
495 _ => None,
496 }
497 }
498}
499
500impl Display for Int {
501 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
502 match &self {
503 Int::I64(i) => write!(f, "{i}"),
504 Int::BigInt(i) => write!(f, "{i}"),
505 }
506 }
507}
508
509#[cfg(test)]
510mod integer_tests {
511 use num_bigint::BigInt;
512 use std::io::Write;
513
514 use num_bigint::BigUint;
515 use num_traits::Zero;
516 use crate::types::Int::{self, BigInt as Big, I64};
518 use crate::types::UInt;
519 use rstest::*;
520 use std::cmp::Ordering;
521
522 #[test]
523 fn is_zero() {
524 assert!(I64(0).is_zero());
525 assert!(Big(BigInt::from(0)).is_zero());
526 assert!(!I64(55).is_zero());
527 assert!(!Big(BigInt::from(55)).is_zero());
528 assert!(!I64(-55).is_zero());
529 assert!(!Big(BigInt::from(-55)).is_zero());
530 }
531
532 #[test]
533 fn zero() {
534 assert!(Int::zero().is_zero());
535 }
536
537 #[test]
538 fn add() {
539 assert_eq!(I64(0) + I64(0), I64(0));
540 assert_eq!(I64(5) + I64(7), I64(12));
541 assert_eq!(I64(-5) + I64(7), I64(2));
542 assert_eq!(I64(100) + Big(BigInt::from(1000)), Big(BigInt::from(1100)));
543 assert_eq!(Big(BigInt::from(100)) + I64(1000), Big(BigInt::from(1100)));
544 assert_eq!(
545 Big(BigInt::from(100)) + Big(BigInt::from(1000)),
546 Big(BigInt::from(1100))
547 );
548 }
549
550 #[rstest]
551 #[case::i64(Int::I64(5), Int::I64(4), Ordering::Greater)]
552 #[case::i64_equal(Int::I64(-5), Int::I64(-5), Ordering::Equal)]
553 #[case::i64_gt_big_int(Int::I64(4), Int::BigInt(BigInt::from(3)), Ordering::Greater)]
554 #[case::i64_eq_big_int(Int::I64(3), Int::BigInt(BigInt::from(3)), Ordering::Equal)]
555 #[case::i64_lt_big_int(Int::I64(-3), Int::BigInt(BigInt::from(5)), Ordering::Less)]
556 #[case::big_int(
557 Int::BigInt(BigInt::from(1100)),
558 Int::BigInt(BigInt::from(-1005)),
559 Ordering::Greater
560 )]
561 #[case::big_int(
562 Int::BigInt(BigInt::from(1100)),
563 Int::BigInt(BigInt::from(1100)),
564 Ordering::Equal
565 )]
566 fn integer_ordering_tests(#[case] this: Int, #[case] other: Int, #[case] expected: Ordering) {
567 assert_eq!(this.cmp(&other), expected)
568 }
569
570 #[rstest]
571 #[case::u64(UInt::U64(5), UInt::U64(4), Ordering::Greater)]
572 #[case::u64_equal(UInt::U64(5), UInt::U64(5), Ordering::Equal)]
573 #[case::u64_gt_big_uint(UInt::U64(4), UInt::BigUInt(BigUint::from(3u64)), Ordering::Greater)]
574 #[case::u64_lt_big_uint(UInt::U64(3), UInt::BigUInt(BigUint::from(5u64)), Ordering::Less)]
575 #[case::u64_eq_big_uint(UInt::U64(3), UInt::BigUInt(BigUint::from(3u64)), Ordering::Equal)]
576 #[case::big_uint(
577 UInt::BigUInt(BigUint::from(1100u64)),
578 UInt::BigUInt(BigUint::from(1005u64)),
579 Ordering::Greater
580 )]
581 #[case::big_uint(
582 UInt::BigUInt(BigUint::from(1005u64)),
583 UInt::BigUInt(BigUint::from(1005u64)),
584 Ordering::Equal
585 )]
586 fn unsigned_integer_ordering_tests(
587 #[case] this: UInt,
588 #[case] other: UInt,
589 #[case] expected: Ordering,
590 ) {
591 assert_eq!(this.cmp(&other), expected)
592 }
593
594 #[rstest]
595 #[case(UInt::U64(1), 1)] #[case(UInt::BigUInt(BigUint::from(0u64)), 1)]
597 #[case(UInt::BigUInt(BigUint::from(1u64)), 1)]
598 #[case(UInt::BigUInt(BigUint::from(10u64)), 2)]
599 #[case(UInt::BigUInt(BigUint::from(3117u64)), 4)]
600 fn uint_decimal_digits_test(#[case] uint: UInt, #[case] expected: i32) {
601 assert_eq!(uint.number_of_decimal_digits(), expected as u64)
602 }
603
604 #[rstest]
605 #[case(Int::I64(5), "5")]
606 #[case(Int::I64(-5), "-5")]
607 #[case(Int::I64(0), "0")]
608 #[case(Int::BigInt(BigInt::from(1100)), "1100")]
609 #[case(Int::BigInt(BigInt::from(-1100)), "-1100")]
610 fn int_display_test(#[case] value: Int, #[case] expect: String) {
611 let mut buf = Vec::new();
612 write!(&mut buf, "{value}").unwrap();
613 assert_eq!(expect, String::from_utf8(buf).unwrap());
614 }
615
616 #[rstest]
617 #[case(UInt::U64(5), "5")]
618 #[case(UInt::U64(0), "0")]
619 #[case(UInt::BigUInt(BigUint::from(0u64)), "0")]
620 #[case(UInt::BigUInt(BigUint::from(1100u64)), "1100")]
621 fn uint_display_test(#[case] value: UInt, #[case] expect: String) {
622 let mut buf = Vec::new();
623 write!(&mut buf, "{value}").unwrap();
624 assert_eq!(expect, String::from_utf8(buf).unwrap());
625 }
626}