1use core::cmp::Ordering;
23use core::fmt;
24use core::str::FromStr;
25
26use num_bigint::BigInt;
27use num_integer::Integer;
28use num_traits::{FromPrimitive, ToPrimitive, Zero};
29
30#[derive(Clone)]
35pub enum AverInt {
36 Small(i64),
38 Big(Box<BigInt>),
41}
42
43impl AverInt {
44 #[inline]
46 pub const fn from_i64(n: i64) -> Self {
47 AverInt::Small(n)
48 }
49
50 #[inline]
52 pub const fn zero() -> Self {
53 AverInt::Small(0)
54 }
55
56 #[inline]
63 pub fn from_bigint(b: BigInt) -> Self {
64 match b.to_i64() {
65 Some(n) => AverInt::Small(n),
66 None => AverInt::Big(Box::new(b)),
67 }
68 }
69
70 #[inline]
72 fn to_bigint(&self) -> BigInt {
73 match self {
74 AverInt::Small(n) => BigInt::from(*n),
75 AverInt::Big(b) => (**b).clone(),
76 }
77 }
78
79 #[inline]
81 pub fn is_zero(&self) -> bool {
82 match self {
83 AverInt::Small(n) => *n == 0,
84 AverInt::Big(b) => b.is_zero(),
85 }
86 }
87
88 pub fn add(&self, rhs: &AverInt) -> AverInt {
97 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
98 if let Some(s) = a.checked_add(*b) {
99 return AverInt::Small(s);
100 }
101 }
102 AverInt::from_bigint(self.to_bigint() + rhs.to_bigint())
103 }
104
105 pub fn sub(&self, rhs: &AverInt) -> AverInt {
107 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
108 if let Some(s) = a.checked_sub(*b) {
109 return AverInt::Small(s);
110 }
111 }
112 AverInt::from_bigint(self.to_bigint() - rhs.to_bigint())
113 }
114
115 pub fn mul(&self, rhs: &AverInt) -> AverInt {
117 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
118 if let Some(s) = a.checked_mul(*b) {
119 return AverInt::Small(s);
120 }
121 }
122 AverInt::from_bigint(self.to_bigint() * rhs.to_bigint())
123 }
124
125 pub fn neg(&self) -> AverInt {
127 match self {
128 AverInt::Small(n) => match n.checked_neg() {
129 Some(v) => AverInt::Small(v),
130 None => AverInt::from_bigint(-BigInt::from(*n)),
131 },
132 AverInt::Big(b) => AverInt::from_bigint(-(**b).clone()),
133 }
134 }
135
136 pub fn div_euclid(&self, rhs: &AverInt) -> Option<AverInt> {
142 if rhs.is_zero() {
143 return None;
144 }
145 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
146 if let Some(q) = a.checked_div_euclid(*b) {
149 return Some(AverInt::Small(q));
150 }
151 }
152 let (q, _r) = euclid_div_rem(&self.to_bigint(), &rhs.to_bigint());
153 Some(AverInt::from_bigint(q))
154 }
155
156 pub fn rem_euclid(&self, rhs: &AverInt) -> Option<AverInt> {
161 if rhs.is_zero() {
162 return None;
163 }
164 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
165 if let Some(r) = a.checked_rem_euclid(*b) {
166 return Some(AverInt::Small(r));
167 }
168 }
169 let (_q, r) = euclid_div_rem(&self.to_bigint(), &rhs.to_bigint());
170 Some(AverInt::from_bigint(r))
171 }
172
173 pub fn div_trunc(&self, rhs: &AverInt) -> Option<AverInt> {
178 if rhs.is_zero() {
179 return None;
180 }
181 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
182 if let Some(q) = a.checked_div(*b) {
183 return Some(AverInt::Small(q));
184 }
185 }
186 Some(AverInt::from_bigint(self.to_bigint() / rhs.to_bigint()))
187 }
188
189 pub fn rem_trunc(&self, rhs: &AverInt) -> Option<AverInt> {
192 if rhs.is_zero() {
193 return None;
194 }
195 if let (AverInt::Small(a), AverInt::Small(b)) = (self, rhs) {
196 if let Some(r) = a.checked_rem(*b) {
197 return Some(AverInt::Small(r));
198 }
199 }
200 Some(AverInt::from_bigint(self.to_bigint() % rhs.to_bigint()))
201 }
202
203 pub fn abs(&self) -> AverInt {
205 match self {
206 AverInt::Small(n) => match n.checked_abs() {
207 Some(v) => AverInt::Small(v),
208 None => AverInt::from_bigint(BigInt::from(*n).magnitude().clone().into()),
209 },
210 AverInt::Big(b) => AverInt::from_bigint(BigInt::from(b.magnitude().clone())),
211 }
212 }
213
214 pub fn min_ref(&self, other: &AverInt) -> AverInt {
217 if self <= other {
218 self.clone()
219 } else {
220 other.clone()
221 }
222 }
223
224 pub fn max_ref(&self, other: &AverInt) -> AverInt {
226 if self >= other {
227 self.clone()
228 } else {
229 other.clone()
230 }
231 }
232
233 #[inline]
242 pub fn to_i64(&self) -> Option<i64> {
243 match self {
244 AverInt::Small(n) => Some(*n),
245 AverInt::Big(b) => b.to_i64(),
246 }
247 }
248
249 #[inline]
251 pub fn to_usize(&self) -> Option<usize> {
252 match self {
253 AverInt::Small(n) => usize::try_from(*n).ok(),
254 AverInt::Big(b) => b.to_usize(),
255 }
256 }
257
258 #[inline]
260 pub fn to_u16(&self) -> Option<u16> {
261 match self {
262 AverInt::Small(n) => u16::try_from(*n).ok(),
263 AverInt::Big(b) => b.to_u16(),
264 }
265 }
266
267 #[inline]
269 pub fn to_u32(&self) -> Option<u32> {
270 match self {
271 AverInt::Small(n) => u32::try_from(*n).ok(),
272 AverInt::Big(b) => b.to_u32(),
273 }
274 }
275
276 #[inline]
280 pub fn to_f64(&self) -> f64 {
281 match self {
282 AverInt::Small(n) => *n as f64,
283 AverInt::Big(b) => b.to_f64().unwrap_or(f64::INFINITY),
286 }
287 }
288
289 pub fn from_f64_trunc(f: f64) -> AverInt {
299 if !f.is_finite() {
300 return AverInt::zero();
301 }
302 let truncated = f.trunc();
303 match truncated.to_i64() {
304 Some(n) => AverInt::Small(n),
305 None => match BigInt::from_f64(truncated) {
307 Some(b) => AverInt::from_bigint(b),
308 None => AverInt::zero(),
309 },
310 }
311 }
312}
313
314fn euclid_div_rem(a: &BigInt, b: &BigInt) -> (BigInt, BigInt) {
326 let (q, r) = a.div_rem(b);
327 if r.sign() == num_bigint::Sign::Minus {
328 if b.sign() == num_bigint::Sign::Plus {
329 (q - 1, r + b)
330 } else {
331 (q + 1, r - b)
332 }
333 } else {
334 (q, r)
335 }
336}
337
338impl PartialEq for AverInt {
344 #[inline]
345 fn eq(&self, other: &Self) -> bool {
346 match (self, other) {
347 (AverInt::Small(a), AverInt::Small(b)) => a == b,
348 (AverInt::Big(a), AverInt::Big(b)) => a == b,
349 _ => false,
351 }
352 }
353}
354
355impl Eq for AverInt {}
356
357impl Ord for AverInt {
358 fn cmp(&self, other: &Self) -> Ordering {
359 match (self, other) {
360 (AverInt::Small(a), AverInt::Small(b)) => a.cmp(b),
361 (AverInt::Big(a), AverInt::Big(b)) => a.cmp(b),
362 (AverInt::Small(_), AverInt::Big(b)) => {
365 if b.sign() == num_bigint::Sign::Minus {
366 Ordering::Greater
367 } else {
368 Ordering::Less
369 }
370 }
371 (AverInt::Big(a), AverInt::Small(_)) => {
372 if a.sign() == num_bigint::Sign::Minus {
373 Ordering::Less
374 } else {
375 Ordering::Greater
376 }
377 }
378 }
379 }
380}
381
382impl PartialOrd for AverInt {
383 #[inline]
384 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
385 Some(self.cmp(other))
386 }
387}
388
389impl core::hash::Hash for AverInt {
390 #[inline]
391 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
392 match self {
396 AverInt::Small(n) => n.hash(state),
397 AverInt::Big(b) => b.hash(state),
398 }
399 }
400}
401
402impl fmt::Display for AverInt {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 match self {
405 AverInt::Small(n) => write!(f, "{}", n),
406 AverInt::Big(b) => write!(f, "{}", b),
407 }
408 }
409}
410
411impl fmt::Debug for AverInt {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 match self {
416 AverInt::Small(n) => write!(f, "{}", n),
417 AverInt::Big(b) => write!(f, "{}", b),
418 }
419 }
420}
421
422impl FromStr for AverInt {
423 type Err = ();
424
425 fn from_str(s: &str) -> Result<Self, Self::Err> {
428 if let Ok(n) = s.parse::<i64>() {
430 return Ok(AverInt::Small(n));
431 }
432 match BigInt::from_str(s) {
433 Ok(b) => Ok(AverInt::from_bigint(b)),
434 Err(_) => Err(()),
435 }
436 }
437}
438
439impl From<i64> for AverInt {
440 #[inline]
441 fn from(n: i64) -> Self {
442 AverInt::Small(n)
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449
450 fn big(s: &str) -> AverInt {
451 AverInt::from_str(s).unwrap()
452 }
453
454 #[test]
455 fn canonical_form_demotes_to_small() {
456 let a = AverInt::from_i64(i64::MAX);
458 let doubled = a.add(&a);
459 assert!(matches!(doubled, AverInt::Big(_)));
460 let halved = doubled.div_euclid(&AverInt::from_i64(2)).unwrap();
461 assert_eq!(halved, AverInt::from_i64(i64::MAX));
462 assert!(matches!(halved, AverInt::Small(_)));
463 }
464
465 #[test]
466 fn square_is_non_negative_past_i64() {
467 let a = AverInt::from_i64(i64::MAX);
469 let sq = a.mul(&a);
470 assert!(matches!(sq, AverInt::Big(_)));
471 assert!(sq >= AverInt::zero());
472 }
473
474 #[test]
475 fn equal_bigs_built_differently_are_equal_and_hash_equal() {
476 use std::collections::hash_map::DefaultHasher;
477 use std::hash::{Hash, Hasher};
478 let big_a = AverInt::from_i64(i64::MAX).add(&AverInt::from_i64(1));
479 let big_b = big("9223372036854775808");
480 assert_eq!(big_a, big_b);
481 let mut ha = DefaultHasher::new();
482 let mut hb = DefaultHasher::new();
483 big_a.hash(&mut ha);
484 big_b.hash(&mut hb);
485 assert_eq!(ha.finish(), hb.finish());
486 }
487
488 #[test]
489 fn from_bigint_demotes_in_range_value_to_small() {
490 use std::collections::hash_map::DefaultHasher;
491 use std::hash::{Hash, Hasher};
492 let from_big = AverInt::from_bigint(BigInt::from(5));
497 let small = AverInt::from_i64(5);
498 assert!(matches!(from_big, AverInt::Small(5)));
499 assert_eq!(from_big, small);
500 let mut hb = DefaultHasher::new();
501 let mut hs = DefaultHasher::new();
502 from_big.hash(&mut hb);
503 small.hash(&mut hs);
504 assert_eq!(hb.finish(), hs.finish());
505
506 assert!(matches!(
508 AverInt::from_bigint(BigInt::from(i64::MAX)),
509 AverInt::Small(i64::MAX)
510 ));
511 assert!(matches!(
512 AverInt::from_bigint(BigInt::from(i64::MIN)),
513 AverInt::Small(i64::MIN)
514 ));
515 let past = AverInt::from_bigint(BigInt::from(i64::MAX) + 1);
517 assert!(matches!(past, AverInt::Big(_)));
518 }
519
520 #[test]
521 fn euclidean_div_mod_match_i64_in_range() {
522 for a in [-7i64, -1, 0, 1, 7, 100] {
523 for b in [-3i64, -1, 1, 3, 5] {
524 let ai = AverInt::from_i64(a);
525 let bi = AverInt::from_i64(b);
526 assert_eq!(
527 ai.div_euclid(&bi).unwrap(),
528 AverInt::from_i64(a.div_euclid(b))
529 );
530 assert_eq!(
531 ai.rem_euclid(&bi).unwrap(),
532 AverInt::from_i64(a.rem_euclid(b))
533 );
534 }
535 }
536 }
537
538 #[test]
539 fn euclidean_div_mod_big_branch_negative_divisor() {
540 let two_63 = AverInt::from_i64(i64::MAX).add(&AverInt::from_i64(1));
544 assert!(matches!(two_63, AverInt::Big(_)));
545
546 let neg3 = AverInt::from_i64(-3);
547 let q = two_63.div_euclid(&neg3).unwrap();
548 let r = two_63.rem_euclid(&neg3).unwrap();
549 assert_eq!(q, big("-3074457345618258602"));
551 assert_eq!(r, AverInt::from_i64(2));
552 assert!(r >= AverInt::zero());
553 }
554
555 #[test]
556 fn euclidean_div_mod_big_full_sign_matrix() {
557 let pos = big("9223372036854775808"); let neg = big("-9223372036854775809"); let dpos = big("100000000000000000000"); let dneg = big("-100000000000000000000"); for a in [&pos, &neg] {
565 for b in [&dpos, &dneg] {
566 assert!(matches!(*a, AverInt::Big(_)));
567 assert!(matches!(*b, AverInt::Big(_)));
568 let q = a.div_euclid(b).unwrap();
569 let r = a.rem_euclid(b).unwrap();
570 assert!(r >= AverInt::zero(), "remainder negative for {a}/{b}");
572 assert!(r < b.abs(), "remainder >= |divisor| for {a}/{b}");
574 assert_eq!(&b.mul(&q).add(&r), a, "identity broken for {a}/{b}");
576 }
577 }
578 }
579
580 #[test]
581 fn div_by_zero_is_none() {
582 assert!(AverInt::from_i64(5).div_euclid(&AverInt::zero()).is_none());
583 assert!(AverInt::from_i64(5).rem_euclid(&AverInt::zero()).is_none());
584 }
585
586 #[test]
587 fn min_div_neg_one_promotes_not_panics() {
588 let min = AverInt::from_i64(i64::MIN);
589 let q = min.div_euclid(&AverInt::from_i64(-1)).unwrap();
590 assert!(matches!(q, AverInt::Big(_)));
591 assert_eq!(q, big("9223372036854775808"));
592 }
593
594 #[test]
595 fn abs_of_min_promotes() {
596 let q = AverInt::from_i64(i64::MIN).abs();
597 assert_eq!(q, big("9223372036854775808"));
598 }
599
600 #[test]
601 fn checked_conversions_reject_out_of_range() {
602 let huge = big("99999999999999999999999999");
603 assert_eq!(huge.to_i64(), None);
604 assert_eq!(huge.to_usize(), None);
605 assert_eq!(huge.to_u32(), None);
606 assert_eq!(huge.to_u16(), None);
607 assert_eq!(AverInt::from_i64(-1).to_usize(), None);
608 assert_eq!(AverInt::from_i64(70000).to_u16(), None);
609 }
610
611 #[test]
612 fn to_f64_saturates_to_infinity() {
613 let huge = big("1").mul(&big("10").mul(&big("10"))); assert_eq!(huge.to_f64(), 100.0);
615 let enormous = AverInt::from_i64(10).mul(&AverInt::from_i64(10));
616 assert_eq!(enormous.to_f64(), 100.0);
617 let mut p = AverInt::from_i64(1);
619 let ten = AverInt::from_i64(10);
620 for _ in 0..400 {
621 p = p.mul(&ten);
622 }
623 assert!(p.to_f64().is_infinite() && p.to_f64() > 0.0);
624 assert!(p.neg().to_f64().is_infinite() && p.neg().to_f64() < 0.0);
625 }
626
627 #[test]
628 fn parse_roundtrip_past_i64() {
629 let s = "170141183460469231731687303715884105727"; let v = big(s);
631 assert!(matches!(v, AverInt::Big(_)));
632 assert_eq!(v.to_string(), s);
633 }
634
635 #[test]
636 fn from_str_rejects_garbage() {
637 assert!(AverInt::from_str("").is_err());
638 assert!(AverInt::from_str("12x").is_err());
639 assert!(AverInt::from_str("1.5").is_err());
640 }
641
642 #[test]
643 fn from_f64_trunc_preserves_huge_finite_magnitudes() {
644 let v = AverInt::from_f64_trunc(1e20);
648 assert!(matches!(v, AverInt::Big(_)));
649 assert_eq!(v.to_string(), "100000000000000000000");
650 let n = AverInt::from_f64_trunc(-1e20);
652 assert_eq!(n.to_string(), "-100000000000000000000");
653 }
654
655 #[test]
656 fn from_f64_trunc_truncates_toward_zero_in_range() {
657 assert_eq!(AverInt::from_f64_trunc(3.9), AverInt::from_i64(3));
658 assert_eq!(AverInt::from_f64_trunc(-3.9), AverInt::from_i64(-3));
659 assert_eq!(AverInt::from_f64_trunc(0.0), AverInt::zero());
660 }
661
662 #[test]
663 fn from_f64_trunc_non_finite_is_zero() {
664 assert_eq!(AverInt::from_f64_trunc(f64::NAN), AverInt::zero());
666 assert_eq!(AverInt::from_f64_trunc(f64::INFINITY), AverInt::zero());
667 assert_eq!(AverInt::from_f64_trunc(f64::NEG_INFINITY), AverInt::zero());
668 }
669}