Skip to main content

kohebi_core/
int.rs

1//! Python integers, which have no upper bound.
2//!
3//! Two representations behind one type. Almost every integer a program touches
4//! is a loop counter, an index or a length and fits in a machine word, so that
5//! is the arm it takes and it costs nothing. Anything larger spills to a
6//! bignum. The two are kept normalized, so a value that fits in an `i64` is
7//! always [`Int::Small`] whichever operation produced it, and that is what lets
8//! equality and ordering be decided without asking which arm either side is in.
9//!
10//! `docs/spec/03-object-model.md` puts small integers in the tagged word itself
11//! rather than in a heap object, and that is still the plan. This type is the
12//! shape of the arithmetic, not the shape of the storage, and moving the small
13//! arm into a tag later does not change a line of what is below.
14//!
15//! ## Where Python and Rust disagree
16//!
17//! Division. Rust truncates toward zero and Python floors toward negative
18//! infinity, so `-7 // 2` is `-3` in Rust and `-4` in Python. The remainder
19//! follows: Python's `%` takes the sign of the divisor, so `-7 % 2` is `1` and
20//! `7 % -2` is `-1`. Both are computed here from the truncating pair rather
21//! than taken from whatever the underlying type happens to do, so the two arms
22//! cannot drift apart.
23//!
24//! Bitwise operations on a negative integer are defined on its infinite two's
25//! complement expansion, so `~5` is `-6` and `-1 & 0xFF` is `255`. `i64` and
26//! `BigInt` both already agree with Python there.
27
28use std::cmp::Ordering;
29use std::fmt;
30
31use num_bigint::BigInt;
32use num_traits::{Signed, ToPrimitive, Zero};
33
34/// A Python integer.
35///
36/// Cloning a big one copies its digits. That is deliberate rather than a
37/// missing `Rc`: keeping this plain data is what makes it `Send` and `Sync`, a
38/// literal is cloned about as often as it is created, and the object model this
39/// is a stand-in for gives every heap value a refcounted header of its own.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum Int {
42    /// Fits in a machine word, which nearly everything does.
43    Small(i64),
44    /// Does not. Never holds a value that would fit in [`Int::Small`].
45    Big(Box<BigInt>),
46}
47
48/// What a division by zero produces, since this crate has no exceptions yet.
49///
50/// The interpreter turns it into `ZeroDivisionError`. It is a type rather than
51/// an `Option` so that a caller cannot read the `None` as "no answer" and carry
52/// on, which for `//` and `%` would be a wrong answer rather than a missing one.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct DivideByZero;
55
56impl Int {
57    /// Zero.
58    pub const ZERO: Self = Int::Small(0);
59
60    /// An integer from a machine word.
61    #[must_use]
62    pub const fn from_i64(value: i64) -> Self {
63        Int::Small(value)
64    }
65
66    /// An integer from a bignum, narrowed if it fits.
67    ///
68    /// Every path that can produce a big value goes through here, which is what
69    /// keeps the invariant that a `Big` never holds something an `i64` could.
70    #[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    /// The digits of an integer literal in some radix, without a sign.
79    ///
80    /// Returns `None` if `digits` is empty or holds anything the radix does not
81    /// allow, which is a caller bug rather than a syntax error: the lexer has
82    /// already decided what a number looks like. Leading zeros are not an
83    /// error, since `007` and `7` are the same integer.
84    #[must_use]
85    pub fn parse(digits: &str, radix: u32) -> Option<Self> {
86        // Checked before either parser rather than left to them, because both
87        // accept a leading `+` or `-` and a literal has no sign. `-1` is a
88        // unary minus applied to `1`, and the parser needs that distinction.
89        if digits.is_empty() || !digits.chars().all(|c| c.is_digit(radix)) {
90            return None;
91        }
92        // The fast path is the whole point: a literal that fits in a word never
93        // touches the bignum parser, and nearly every literal fits.
94        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    /// This as a machine word, if it fits.
101    #[must_use]
102    pub fn to_i64(&self) -> Option<i64> {
103        match self {
104            Int::Small(n) => Some(*n),
105            // Unreachable while the invariant holds, and cheaper to answer than
106            // to assert.
107            Int::Big(_) => None,
108        }
109    }
110
111    /// This as a `usize`, for the places an index or a count is wanted.
112    #[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    /// This as a double, or `None` when it is too large to be one.
118    ///
119    /// Python raises `OverflowError` for that case rather than returning
120    /// infinity, which is why this is not a plain `f64`.
121    #[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    /// Whether this is zero, which is also whether it is falsey.
134    #[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    /// This as a bignum, whichever arm it is in.
151    #[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    /// `-self`.
190    #[must_use]
191    pub fn neg(&self) -> Self {
192        match self {
193            // `-i64::MIN` is the one negation that does not fit, which is why
194            // this is not just `Int::Small(-n)`.
195            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    /// `~self`, which is `-self - 1` on the infinite two's complement.
203    #[must_use]
204    pub fn invert(&self) -> Self {
205        match self {
206            // `!n` never overflows, since the range is symmetric around -1.
207            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    /// `self // other`, flooring toward negative infinity as Python does.
222    pub fn floor_div(&self, other: &Self) -> Result<Self, DivideByZero> {
223        Ok(self.div_mod(other)?.0)
224    }
225
226    /// `self % other`, taking the sign of the divisor as Python does.
227    pub fn modulo(&self, other: &Self) -> Result<Self, DivideByZero> {
228        Ok(self.div_mod(other)?.1)
229    }
230
231    /// `divmod(self, other)`, which is the quotient and remainder together.
232    ///
233    /// Both are derived from the truncating pair rather than taken from the
234    /// underlying type, so a change of arm cannot change the answer. The
235    /// correction is the same in either arm: when the remainder is non-zero and
236    /// its sign disagrees with the divisor, the truncating quotient is one too
237    /// large and the remainder is a whole divisor short.
238    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            // `i64::MIN / -1` is the one division that overflows, and it is
244            // exactly the case the bignum path is here for.
245            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    /// `self / other`, which in Python is always a float.
264    ///
265    /// `None` means the true quotient is out of range for a double, which
266    /// Python reports as `OverflowError` rather than as infinity.
267    pub fn true_div(&self, other: &Self) -> Result<Option<f64>, DivideByZero> {
268        if other.is_zero() {
269            return Err(DivideByZero);
270        }
271        // Dividing the two doubles first is right whenever both survive the
272        // conversion, and both surviving is the overwhelmingly common case.
273        if let (Some(a), Some(b)) = (self.to_f64(), other.to_f64()) {
274            return Ok(Some(a / b));
275        }
276        // One of them did not fit, so the ratio has to come from the integers.
277        // The quotient carries the magnitude and the remainder the rest of the
278        // precision, which is enough for a correctly signed finite answer or a
279        // clean overflow.
280        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    /// `self ** exponent` for a non-negative exponent.
291    ///
292    /// `None` for a negative one, which Python answers with a float rather than
293    /// an integer, and for an exponent so large that the result could not be
294    /// held. Both are the caller's to turn into the right thing.
295    #[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        // Guard against a request that would exhaust memory rather than
305        // returning from it hours later. Ten million bits is a number with
306        // three million digits, which is past anything a program means to ask
307        // for and still cheap to reject.
308        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    /// `self << places`, which needs a non-negative count.
316    ///
317    /// `None` for a negative one, which Python reports as
318    /// `ValueError: negative shift count`, and for a count large enough that
319    /// the result would not fit in memory.
320    #[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    /// `self >> places`, which needs a non-negative count.
333    ///
334    /// An arithmetic shift, so a negative number shifted far enough lands on
335    /// `-1` rather than on zero, which is what flooring means here.
336    #[must_use]
337    pub fn shr(&self, places: &Self) -> Option<Self> {
338        let places = u64::try_from(places.to_i64()?).ok()?;
339        // A shift wider than the number is the sign bit repeated, and asking
340        // the bignum to do it would allocate for an answer already known.
341        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    /// One operation, written once for both arms.
352    ///
353    /// `small` returns `None` when the word arm overflows, which is the signal
354    /// to redo the whole thing in the bignum arm rather than to patch up a
355    /// wrapped result.
356    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            // A big is out of the word range by construction, so its sign
394            // settles it against any small without comparing digits.
395            (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    /// The digits of `2**80`, which is past a word and is the value several of
432    /// the tests below build from.
433    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    /// A literal has no sign, since `-1` is a unary minus applied to `1`, and
457    /// the bignum parser would happily accept one.
458    #[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    /// The invariant everything else rests on. Two integers that are equal have
468    /// to be in the same arm, or `==` on the enum would say they are not.
469    #[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        // The two that overflow without looking like they should.
492        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    /// Rust truncates toward zero and Python floors toward negative infinity.
500    /// Every one of these is a different answer in the two languages.
501    #[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        // An exact division has nothing to floor and is the same either way.
508        assert_eq!(int(-6).floor_div(&int(2)), Ok(int(-3)));
509    }
510
511    /// The remainder takes the sign of the divisor, which is what makes
512    /// `x % n` land in `range(n)` for a positive `n` whatever `x` is.
513    #[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    /// The identity that has to hold for every pair, and the reason the
523    /// quotient and the remainder are corrected together rather than apart.
524    #[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    /// The same correction, in the arm where the word path could not answer.
534    #[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    /// An integer past the range of a double still divides, as long as the
563    /// answer is in range. `2**80 / 2**79` is `2.0` and neither side is a float.
564    #[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    /// A negative exponent has no integer answer, and one that would need
589    /// gigabytes has no answer worth computing. Both are the caller's to turn
590    /// into the right thing, which is a float for the first and an error for
591    /// the second.
592    #[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    /// A right shift floors, so a negative number never reaches zero however
608    /// far it goes. `-1 >> 1000` is `-1`.
609    #[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    /// Python's bitwise operators are defined on the infinite two's complement
624    /// expansion, so a negative operand behaves as though the sign bit repeated
625    /// forever to the left.
626    #[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}