1use std::cmp::Ordering;
29use std::fmt;
30
31use num_bigint::BigInt;
32use num_traits::{Signed, ToPrimitive, Zero};
33
34#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum Int {
42 Small(i64),
44 Big(Box<BigInt>),
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct DivideByZero;
55
56impl Int {
57 pub const ZERO: Self = Int::Small(0);
59
60 #[must_use]
62 pub const fn from_i64(value: i64) -> Self {
63 Int::Small(value)
64 }
65
66 #[must_use]
71 pub fn from_big(value: BigInt) -> Self {
72 match value.to_i64() {
73 Some(small) => Int::Small(small),
74 None => Int::Big(Box::new(value)),
75 }
76 }
77
78 #[must_use]
85 pub fn parse(digits: &str, radix: u32) -> Option<Self> {
86 if digits.is_empty() || !digits.chars().all(|c| c.is_digit(radix)) {
90 return None;
91 }
92 if let Ok(small) = i64::from_str_radix(digits, radix) {
95 return Some(Int::Small(small));
96 }
97 BigInt::parse_bytes(digits.as_bytes(), radix).map(Self::from_big)
98 }
99
100 #[must_use]
102 pub fn to_i64(&self) -> Option<i64> {
103 match self {
104 Int::Small(n) => Some(*n),
105 Int::Big(_) => None,
108 }
109 }
110
111 #[must_use]
113 pub fn to_usize(&self) -> Option<usize> {
114 self.to_i64().and_then(|n| usize::try_from(n).ok())
115 }
116
117 #[must_use]
122 pub fn to_f64(&self) -> Option<f64> {
123 match self {
124 #[expect(
125 clippy::cast_precision_loss,
126 reason = "float(n) is lossy for a large n in Python too"
127 )]
128 Int::Small(n) => Some(*n as f64),
129 Int::Big(big) => big.to_f64().filter(|f| f.is_finite()),
130 }
131 }
132
133 #[must_use]
135 pub fn is_zero(&self) -> bool {
136 match self {
137 Int::Small(n) => *n == 0,
138 Int::Big(big) => big.is_zero(),
139 }
140 }
141
142 #[must_use]
143 pub fn is_negative(&self) -> bool {
144 match self {
145 Int::Small(n) => *n < 0,
146 Int::Big(big) => big.is_negative(),
147 }
148 }
149
150 #[must_use]
152 pub fn to_big(&self) -> BigInt {
153 match self {
154 Int::Small(n) => BigInt::from(*n),
155 Int::Big(big) => (**big).clone(),
156 }
157 }
158
159 #[must_use]
160 pub fn add(&self, other: &Self) -> Self {
161 self.arith(other, i64::checked_add, |a, b| a + b)
162 }
163
164 #[must_use]
165 pub fn sub(&self, other: &Self) -> Self {
166 self.arith(other, i64::checked_sub, |a, b| a - b)
167 }
168
169 #[must_use]
170 pub fn mul(&self, other: &Self) -> Self {
171 self.arith(other, i64::checked_mul, |a, b| a * b)
172 }
173
174 #[must_use]
175 pub fn bitand(&self, other: &Self) -> Self {
176 self.arith(other, |a, b| Some(a & b), |a, b| a & b)
177 }
178
179 #[must_use]
180 pub fn bitor(&self, other: &Self) -> Self {
181 self.arith(other, |a, b| Some(a | b), |a, b| a | b)
182 }
183
184 #[must_use]
185 pub fn bitxor(&self, other: &Self) -> Self {
186 self.arith(other, |a, b| Some(a ^ b), |a, b| a ^ b)
187 }
188
189 #[must_use]
191 pub fn neg(&self) -> Self {
192 match self {
193 Int::Small(n) => n
196 .checked_neg()
197 .map_or_else(|| Self::from_big(-BigInt::from(*n)), Int::Small),
198 Int::Big(big) => Self::from_big(-&**big),
199 }
200 }
201
202 #[must_use]
204 pub fn invert(&self) -> Self {
205 match self {
206 Int::Small(n) => Int::Small(!n),
208 Int::Big(big) => Self::from_big(!&**big),
209 }
210 }
211
212 #[must_use]
213 pub fn abs(&self) -> Self {
214 if self.is_negative() {
215 self.neg()
216 } else {
217 self.clone()
218 }
219 }
220
221 pub fn floor_div(&self, other: &Self) -> Result<Self, DivideByZero> {
223 Ok(self.div_mod(other)?.0)
224 }
225
226 pub fn modulo(&self, other: &Self) -> Result<Self, DivideByZero> {
228 Ok(self.div_mod(other)?.1)
229 }
230
231 pub fn div_mod(&self, other: &Self) -> Result<(Self, Self), DivideByZero> {
239 if other.is_zero() {
240 return Err(DivideByZero);
241 }
242 if let (Int::Small(a), Int::Small(b)) = (self, other) {
243 if let (Some(q), Some(r)) = (a.checked_div(*b), a.checked_rem(*b)) {
246 return Ok(if r != 0 && (r < 0) != (*b < 0) {
247 (Int::Small(q - 1), Int::Small(r + b))
248 } else {
249 (Int::Small(q), Int::Small(r))
250 });
251 }
252 }
253 let (a, b) = (self.to_big(), other.to_big());
254 let q = &a / &b;
255 let r = &a - &q * &b;
256 Ok(if !r.is_zero() && r.is_negative() != b.is_negative() {
257 (Self::from_big(q - 1), Self::from_big(r + b))
258 } else {
259 (Self::from_big(q), Self::from_big(r))
260 })
261 }
262
263 pub fn true_div(&self, other: &Self) -> Result<Option<f64>, DivideByZero> {
268 if other.is_zero() {
269 return Err(DivideByZero);
270 }
271 if let (Some(a), Some(b)) = (self.to_f64(), other.to_f64()) {
274 return Ok(Some(a / b));
275 }
276 let (q, r) = self.div_mod(other)?;
281 let Some(quotient) = q.to_f64() else {
282 return Ok(None);
283 };
284 let (Some(rem), Some(div)) = (r.to_f64(), other.to_f64()) else {
285 return Ok(Some(quotient));
286 };
287 Ok(Some(quotient + rem / div))
288 }
289
290 #[must_use]
296 pub fn pow(&self, exponent: &Self) -> Option<Self> {
297 let exponent = exponent.to_i64().filter(|e| *e >= 0)?;
298 let exponent = u32::try_from(exponent).ok()?;
299 if let Int::Small(base) = self
300 && let Some(small) = base.checked_pow(exponent)
301 {
302 return Some(Int::Small(small));
303 }
304 let bits = self.to_big().bits().saturating_mul(u64::from(exponent));
309 if bits > 10_000_000 {
310 return None;
311 }
312 Some(Self::from_big(self.to_big().pow(exponent)))
313 }
314
315 #[must_use]
321 pub fn shl(&self, places: &Self) -> Option<Self> {
322 let places = u64::try_from(places.to_i64()?).ok()?;
323 if self.is_zero() {
324 return Some(Int::ZERO);
325 }
326 if self.to_big().bits().saturating_add(places) > 10_000_000 {
327 return None;
328 }
329 Some(Self::from_big(self.to_big() << places))
330 }
331
332 #[must_use]
337 pub fn shr(&self, places: &Self) -> Option<Self> {
338 let places = u64::try_from(places.to_i64()?).ok()?;
339 if places >= self.to_big().bits().saturating_add(1) {
342 return Some(if self.is_negative() {
343 Int::Small(-1)
344 } else {
345 Int::ZERO
346 });
347 }
348 Some(Self::from_big(self.to_big() >> places))
349 }
350
351 fn arith(
357 &self,
358 other: &Self,
359 small: impl Fn(i64, i64) -> Option<i64>,
360 big: impl Fn(BigInt, BigInt) -> BigInt,
361 ) -> Self {
362 if let (Int::Small(a), Int::Small(b)) = (self, other)
363 && let Some(value) = small(*a, *b)
364 {
365 return Int::Small(value);
366 }
367 Self::from_big(big(self.to_big(), other.to_big()))
368 }
369}
370
371impl From<i64> for Int {
372 fn from(value: i64) -> Self {
373 Int::Small(value)
374 }
375}
376
377impl From<BigInt> for Int {
378 fn from(value: BigInt) -> Self {
379 Self::from_big(value)
380 }
381}
382
383impl PartialOrd for Int {
384 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
385 Some(self.cmp(other))
386 }
387}
388
389impl Ord for Int {
390 fn cmp(&self, other: &Self) -> Ordering {
391 match (self, other) {
392 (Int::Small(a), Int::Small(b)) => a.cmp(b),
393 (Int::Big(a), Int::Small(_)) => {
396 if a.is_negative() {
397 Ordering::Less
398 } else {
399 Ordering::Greater
400 }
401 }
402 (Int::Small(_), Int::Big(b)) => {
403 if b.is_negative() {
404 Ordering::Greater
405 } else {
406 Ordering::Less
407 }
408 }
409 (Int::Big(a), Int::Big(b)) => a.cmp(b),
410 }
411 }
412}
413
414impl fmt::Display for Int {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 match self {
417 Int::Small(n) => write!(f, "{n}"),
418 Int::Big(big) => write!(f, "{big}"),
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 fn int(n: i64) -> Int {
428 Int::Small(n)
429 }
430
431 fn big() -> Int {
434 Int::parse("1208925819614629174706176", 10).expect("valid digits")
435 }
436
437 #[test]
438 fn a_literal_that_fits_in_a_word_stays_in_one() {
439 assert_eq!(Int::parse("42", 10), Some(int(42)));
440 assert_eq!(Int::parse("007", 10), Some(int(7)));
441 assert_eq!(Int::parse("ff", 16), Some(int(255)));
442 assert_eq!(Int::parse("777", 8), Some(int(511)));
443 assert_eq!(Int::parse("1010", 2), Some(int(10)));
444 }
445
446 #[test]
447 fn a_literal_too_large_for_a_word_keeps_all_of_it() {
448 let huge = "9".repeat(40);
449 assert_eq!(Int::parse(&huge, 10).map(|n| n.to_string()), Some(huge));
450 assert_eq!(
451 Int::parse(&"f".repeat(20), 16).map(|n| n.to_string()),
452 Some("1208925819614629174706175".to_owned())
453 );
454 }
455
456 #[test]
459 fn digits_are_the_callers_job_to_get_right() {
460 assert_eq!(Int::parse("", 10), None);
461 assert_eq!(Int::parse("0x10", 10), None);
462 assert_eq!(Int::parse("-1", 10), None);
463 assert_eq!(Int::parse(&format!("-{}", "9".repeat(40)), 10), None);
464 assert_eq!(Int::parse("2", 2), None);
465 }
466
467 #[test]
470 fn a_value_that_fits_in_a_word_is_always_in_the_word_arm() {
471 assert!(matches!(big().sub(&big()), Int::Small(0)));
472 assert!(matches!(big().floor_div(&big()), Ok(Int::Small(1))));
473 assert!(matches!(Int::from_big(BigInt::from(7)), Int::Small(7)));
474 assert_eq!(big().sub(&big()), int(0));
475 }
476
477 #[test]
478 fn a_word_that_overflows_carries_on_in_the_other_arm() {
479 assert_eq!(
480 int(i64::MAX).add(&int(1)).to_string(),
481 "9223372036854775808"
482 );
483 assert_eq!(
484 int(i64::MIN).sub(&int(1)).to_string(),
485 "-9223372036854775809"
486 );
487 assert_eq!(
488 int(3_037_000_500).mul(&int(3_037_000_500)).to_string(),
489 "9223372037000250000"
490 );
491 assert_eq!(int(i64::MIN).neg().to_string(), "9223372036854775808");
493 assert_eq!(
494 int(i64::MIN).floor_div(&int(-1)).map(|n| n.to_string()),
495 Ok("9223372036854775808".to_owned())
496 );
497 }
498
499 #[test]
502 fn division_floors_the_way_python_floors() {
503 assert_eq!(int(-7).floor_div(&int(2)), Ok(int(-4)));
504 assert_eq!(int(7).floor_div(&int(-2)), Ok(int(-4)));
505 assert_eq!(int(-7).floor_div(&int(-2)), Ok(int(3)));
506 assert_eq!(int(7).floor_div(&int(2)), Ok(int(3)));
507 assert_eq!(int(-6).floor_div(&int(2)), Ok(int(-3)));
509 }
510
511 #[test]
514 fn the_remainder_takes_the_sign_of_the_divisor() {
515 assert_eq!(int(-7).modulo(&int(2)), Ok(int(1)));
516 assert_eq!(int(7).modulo(&int(-2)), Ok(int(-1)));
517 assert_eq!(int(-7).modulo(&int(-2)), Ok(int(-1)));
518 assert_eq!(int(7).modulo(&int(2)), Ok(int(1)));
519 assert_eq!(int(-6).modulo(&int(2)), Ok(int(0)));
520 }
521
522 #[test]
525 fn the_quotient_and_the_remainder_rebuild_what_they_came_from() {
526 let cases = [(17, 5), (-17, 5), (17, -5), (-17, -5), (0, 3), (1, -1)];
527 for (a, b) in cases {
528 let (q, r) = int(a).div_mod(&int(b)).expect("no zero divisor here");
529 assert_eq!(q.mul(&int(b)).add(&r), int(a), "{a} divmod {b}");
530 }
531 }
532
533 #[test]
535 fn the_bignum_arm_floors_and_signs_the_same_way() {
536 let huge = big();
537 let minus = huge.neg();
538 assert_eq!(minus.floor_div(&int(10)).map(|n| n.is_negative()), Ok(true));
539 let (q, r) = minus.div_mod(&int(10)).expect("no zero divisor here");
540 assert_eq!(q.mul(&int(10)).add(&r), minus);
541 assert!(
542 !r.is_negative(),
543 "a positive divisor gives a positive remainder"
544 );
545 }
546
547 #[test]
548 fn dividing_by_zero_is_refused_rather_than_answered() {
549 assert_eq!(int(1).floor_div(&int(0)), Err(DivideByZero));
550 assert_eq!(int(1).modulo(&int(0)), Err(DivideByZero));
551 assert_eq!(int(0).div_mod(&int(0)), Err(DivideByZero));
552 assert_eq!(int(1).true_div(&int(0)), Err(DivideByZero));
553 }
554
555 #[test]
556 fn dividing_with_a_slash_gives_a_float_even_when_it_comes_out_even() {
557 assert_eq!(int(6).true_div(&int(3)), Ok(Some(2.0)));
558 assert_eq!(int(7).true_div(&int(2)), Ok(Some(3.5)));
559 assert_eq!(int(-7).true_div(&int(2)), Ok(Some(-3.5)));
560 }
561
562 #[test]
565 fn a_quotient_in_range_survives_operands_that_are_not() {
566 let a = big();
567 let b = a.floor_div(&int(2)).expect("no zero divisor here");
568 assert_eq!(a.true_div(&b), Ok(Some(2.0)));
569 }
570
571 #[test]
572 fn an_integer_too_large_to_be_a_float_says_so() {
573 let huge = big().pow(&int(20)).expect("in range for an integer");
574 assert_eq!(huge.to_f64(), None);
575 assert_eq!(big().to_f64(), Some(1.208_925_819_614_629_2e24));
576 assert_eq!(int(3).to_f64(), Some(3.0));
577 }
578
579 #[test]
580 fn raising_to_a_power_grows_out_of_the_word_arm() {
581 assert_eq!(int(2).pow(&int(10)), Some(int(1024)));
582 assert_eq!(int(2).pow(&int(80)), Some(big()));
583 assert_eq!(int(-2).pow(&int(3)), Some(int(-8)));
584 assert_eq!(int(-2).pow(&int(2)), Some(int(4)));
585 assert_eq!(int(0).pow(&int(0)), Some(int(1)));
586 }
587
588 #[test]
593 fn a_power_with_no_integer_answer_declines_to_give_one() {
594 assert_eq!(int(2).pow(&int(-1)), None);
595 assert_eq!(int(2).pow(&int(1).shl(&int(40)).expect("in range")), None);
596 assert_eq!(big().pow(&int(1_000_000)), None);
597 }
598
599 #[test]
600 fn shifting_is_multiplying_and_flooring_by_a_power_of_two() {
601 assert_eq!(int(1).shl(&int(80)), Some(big()));
602 assert_eq!(big().shr(&int(80)), Some(int(1)));
603 assert_eq!(int(-7).shr(&int(1)), Some(int(-4)));
604 assert_eq!(int(0).shl(&int(1_000_000)), Some(int(0)));
605 }
606
607 #[test]
610 fn shifting_a_negative_number_right_lands_on_minus_one() {
611 assert_eq!(int(-1).shr(&int(1000)), Some(int(-1)));
612 assert_eq!(int(-1_000_000).shr(&int(1000)), Some(int(-1)));
613 assert_eq!(int(1_000_000).shr(&int(1000)), Some(int(0)));
614 }
615
616 #[test]
617 fn a_negative_shift_count_has_no_answer() {
618 assert_eq!(int(1).shl(&int(-1)), None);
619 assert_eq!(int(1).shr(&int(-1)), None);
620 assert_eq!(int(1).shl(&big()), None);
621 }
622
623 #[test]
627 fn bitwise_operations_treat_a_negative_as_infinitely_signed() {
628 assert_eq!(int(5).invert(), int(-6));
629 assert_eq!(int(-1).bitand(&int(0xFF)), int(255));
630 assert_eq!(int(-2).bitor(&int(1)), int(-1));
631 assert_eq!(int(-1).bitxor(&int(-1)), int(0));
632 assert_eq!(int(12).bitand(&int(10)), int(8));
633 assert_eq!(int(12).bitor(&int(10)), int(14));
634 assert_eq!(int(12).bitxor(&int(10)), int(6));
635 }
636
637 #[test]
638 fn ordering_crosses_the_two_arms() {
639 let huge = big();
640 assert!(huge > int(i64::MAX));
641 assert!(huge.neg() < int(i64::MIN));
642 assert!(int(i64::MAX) < huge);
643 assert!(int(i64::MIN) > huge.neg());
644 assert!(huge.neg() < huge);
645 assert!(int(-1) < int(1));
646 }
647
648 #[test]
649 fn absolute_value_and_negation_agree_on_the_edge_of_the_word() {
650 assert_eq!(int(-5).abs(), int(5));
651 assert_eq!(int(5).abs(), int(5));
652 assert_eq!(int(i64::MIN).abs().to_string(), "9223372036854775808");
653 assert_eq!(int(i64::MIN).abs().neg(), int(i64::MIN));
654 }
655}