Skip to main content

rustpython_vm/builtins/
int.rs

1use super::{PyByteArray, PyBytes, PyStr, PyType, PyTypeRef, float};
2use crate::{
3    AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
4    TryFromBorrowedObject, VirtualMachine,
5    builtins::PyUtf8StrRef,
6    bytes_inner::PyBytesInner,
7    class::PyClassImpl,
8    common::{
9        format::FormatSpec,
10        hash,
11        int::{bigint_to_finite_float, bytes_to_int, true_div},
12        wtf8::Wtf8Buf,
13    },
14    convert::{IntoPyException, ToPyObject, ToPyResult},
15    function::{
16        ArgByteOrder, ArgIntoBool, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue,
17        PyComparisonValue,
18    },
19    protocol::{PyNumberMethods, handle_bytes_to_int_err},
20    types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable},
21};
22use alloc::fmt;
23use core::cell::Cell;
24use core::ops::{Neg, Not};
25use core::ptr::NonNull;
26use malachite_bigint::{BigInt, Sign};
27use num_integer::{ExtendedGcd, Integer};
28use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero};
29
30#[pyclass(module = false, name = "int")]
31#[derive(Debug)]
32pub struct PyInt {
33    value: BigInt,
34}
35
36impl fmt::Display for PyInt {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        BigInt::fmt(&self.value, f)
39    }
40}
41
42pub type PyIntRef = PyRef<PyInt>;
43
44impl<T> From<T> for PyInt
45where
46    T: Into<BigInt>,
47{
48    fn from(v: T) -> Self {
49        Self { value: v.into() }
50    }
51}
52
53// spell-checker:ignore MAXFREELIST
54thread_local! {
55    static INT_FREELIST: Cell<crate::object::FreeList<PyInt>> = const { Cell::new(crate::object::FreeList::new()) };
56}
57
58impl PyPayload for PyInt {
59    const MAX_FREELIST: usize = 100;
60    const HAS_FREELIST: bool = true;
61
62    #[inline]
63    fn class(ctx: &Context) -> &'static Py<PyType> {
64        ctx.types.int_type
65    }
66
67    fn into_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
68        vm.ctx.new_int(self.value).into()
69    }
70
71    #[inline]
72    unsafe fn freelist_push(obj: *mut PyObject) -> bool {
73        INT_FREELIST
74            .try_with(|fl| {
75                let mut list = fl.take();
76                let stored = if list.len() < Self::MAX_FREELIST {
77                    list.push(obj);
78                    true
79                } else {
80                    false
81                };
82                fl.set(list);
83                stored
84            })
85            .unwrap_or(false)
86    }
87
88    #[inline]
89    unsafe fn freelist_pop(_payload: &Self) -> Option<NonNull<PyObject>> {
90        INT_FREELIST
91            .try_with(|fl| {
92                let mut list = fl.take();
93                let result = list.pop().map(|p| unsafe { NonNull::new_unchecked(p) });
94                fl.set(list);
95                result
96            })
97            .ok()
98            .flatten()
99    }
100}
101
102macro_rules! impl_into_pyobject_int {
103    ($($t:ty)*) => {$(
104        impl ToPyObject for $t {
105            fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
106                vm.ctx.new_int(self).into()
107            }
108        }
109    )*};
110}
111
112impl_into_pyobject_int!(isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 BigInt);
113
114macro_rules! impl_try_from_object_int {
115    ($(($t:ty, $to_prim:ident),)*) => {$(
116        impl<'a> TryFromBorrowedObject<'a> for $t {
117            fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
118                obj.try_value_with(|int: &PyInt| {
119                    int.try_to_primitive(vm)
120                }, vm)
121            }
122        }
123    )*};
124}
125
126impl_try_from_object_int!(
127    (isize, to_isize),
128    (i8, to_i8),
129    (i16, to_i16),
130    (i32, to_i32),
131    (i64, to_i64),
132    (i128, to_i128),
133    (usize, to_usize),
134    (u8, to_u8),
135    (u16, to_u16),
136    (u32, to_u32),
137    (u64, to_u64),
138    (u128, to_u128),
139);
140
141fn inner_pow(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
142    if int2.is_negative() {
143        let v1 = try_to_float(int1, vm)?;
144        let v2 = try_to_float(int2, vm)?;
145        float::float_pow(v1, v2, vm)
146    } else {
147        let value = if let Some(v2) = int2.to_u64() {
148            return Ok(vm.ctx.new_int(Pow::pow(int1, v2)).into());
149        } else if int1.is_one() {
150            1
151        } else if int1.is_zero() {
152            0
153        } else if int1 == &BigInt::from(-1) {
154            if int2.is_odd() { -1 } else { 1 }
155        } else {
156            // missing feature: BigInt exp
157            // practically, exp over u64 is not possible to calculate anyway
158            return Ok(vm.ctx.not_implemented());
159        };
160        Ok(vm.ctx.new_int(value).into())
161    }
162}
163
164fn inner_mod(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
165    if int2.is_zero() {
166        Err(vm.new_zero_division_error("division by zero"))
167    } else {
168        Ok(vm.ctx.new_int(int1.mod_floor(int2)).into())
169    }
170}
171
172fn inner_floordiv(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
173    if int2.is_zero() {
174        Err(vm.new_zero_division_error("division by zero"))
175    } else {
176        Ok(vm.ctx.new_int(int1.div_floor(int2)).into())
177    }
178}
179
180fn inner_divmod(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
181    if int2.is_zero() {
182        return Err(vm.new_zero_division_error("division by zero"));
183    }
184    let (div, modulo) = int1.div_mod_floor(int2);
185    Ok(vm.new_tuple((div, modulo)).into())
186}
187
188fn inner_lshift(base: &BigInt, bits: &BigInt, vm: &VirtualMachine) -> PyResult {
189    inner_shift(
190        base,
191        bits,
192        |base, bits| base << bits,
193        |bits, vm| {
194            bits.to_usize()
195                .ok_or_else(|| vm.new_overflow_error("the number is too large to convert to int"))
196        },
197        vm,
198    )
199}
200
201fn inner_rshift(base: &BigInt, bits: &BigInt, vm: &VirtualMachine) -> PyResult {
202    inner_shift(
203        base,
204        bits,
205        |base, bits| base >> bits,
206        |bits, _vm| Ok(bits.to_usize().unwrap_or(usize::MAX)),
207        vm,
208    )
209}
210
211fn inner_shift<F, S>(
212    base: &BigInt,
213    bits: &BigInt,
214    shift_op: F,
215    shift_bits: S,
216    vm: &VirtualMachine,
217) -> PyResult
218where
219    F: Fn(&BigInt, usize) -> BigInt,
220    S: Fn(&BigInt, &VirtualMachine) -> PyResult<usize>,
221{
222    if bits.is_negative() {
223        Err(vm.new_value_error("negative shift count"))
224    } else if base.is_zero() {
225        Ok(vm.ctx.new_int(0).into())
226    } else {
227        shift_bits(bits, vm).map(|bits| vm.ctx.new_int(shift_op(base, bits)).into())
228    }
229}
230
231fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult {
232    if i2.is_zero() {
233        return Err(vm.new_zero_division_error("division by zero"));
234    }
235
236    let float = true_div(i1, i2);
237
238    if float.is_infinite() {
239        Err(vm.new_exception_msg(
240            vm.ctx.exceptions.overflow_error.to_owned(),
241            "integer division result too large for a float".into(),
242        ))
243    } else {
244        Ok(vm.ctx.new_float(float).into())
245    }
246}
247
248impl Constructor for PyInt {
249    type Args = FuncArgs;
250
251    fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
252        if cls.is(vm.ctx.types.bool_type) {
253            return Err(vm.new_type_error("int.__new__(bool) is not safe, use bool.__new__()"));
254        }
255
256        // Optimization: return exact int as-is (only for exact int type, not subclasses)
257        if cls.is(vm.ctx.types.int_type)
258            && args.args.len() == 1
259            && args.kwargs.is_empty()
260            && args.args[0].class().is(vm.ctx.types.int_type)
261        {
262            return Ok(args.args[0].clone());
263        }
264
265        let options: IntOptions = args.bind(vm)?;
266        let value = if let OptionalArg::Present(val) = options.val_options {
267            if let OptionalArg::Present(base) = options.base {
268                let base = base
269                    .try_index(vm)?
270                    .as_bigint()
271                    .to_u32()
272                    .filter(|&v| v == 0 || (2..=36).contains(&v))
273                    .ok_or_else(|| vm.new_value_error("int() base must be >= 2 and <= 36, or 0"))?;
274                try_int_radix(&val, base, vm)
275            } else {
276                val.try_int(vm).map(|x| x.as_bigint().clone())
277            }
278        } else if let OptionalArg::Present(_) = options.base {
279            Err(vm.new_type_error("int() missing string argument"))
280        } else {
281            Ok(Zero::zero())
282        }?;
283
284        Self::with_value(cls, value, vm).map(Into::into)
285    }
286
287    fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
288        unimplemented!("use slot_new")
289    }
290}
291
292impl PyInt {
293    fn with_value<T>(cls: PyTypeRef, value: T, vm: &VirtualMachine) -> PyResult<PyRef<Self>>
294    where
295        T: Into<BigInt> + ToPrimitive,
296    {
297        if cls.is(vm.ctx.types.int_type) {
298            Ok(vm.ctx.new_int(value))
299        } else if cls.is(vm.ctx.types.bool_type) {
300            Ok(vm.ctx.new_bool(!value.into().eq(&BigInt::zero())).upcast())
301        } else {
302            Self::from(value).into_ref_with_type(vm, cls)
303        }
304    }
305
306    pub const fn as_bigint(&self) -> &BigInt {
307        &self.value
308    }
309
310    /// Fast decimal string conversion, using i64 path when possible.
311    #[inline]
312    pub fn to_str_radix_10(&self) -> String {
313        match self.value.to_i64() {
314            Some(i) => i.to_string(),
315            None => self.value.to_string(),
316        }
317    }
318
319    // _PyLong_AsUnsignedLongMask
320    pub fn as_u32_mask(&self) -> u32 {
321        let v = self.as_bigint();
322        v.to_u32()
323            .or_else(|| v.to_i32().map(|i| i as u32))
324            .unwrap_or_else(|| {
325                let mut out = 0u32;
326                for digit in v.iter_u32_digits() {
327                    out = out.wrapping_shl(32) | digit;
328                }
329                match v.sign() {
330                    Sign::Minus => out * -1i32 as u32,
331                    _ => out,
332                }
333            })
334    }
335
336    pub fn try_to_primitive<'a, I>(&'a self, vm: &VirtualMachine) -> PyResult<I>
337    where
338        I: PrimInt + TryFrom<&'a BigInt>,
339    {
340        // TODO: Python 3.14+: ValueError for negative int to unsigned type
341        // See stdlib_socket.py socket.htonl(-1)
342        //
343        // if I::min_value() == I::zero() && self.as_bigint().sign() == Sign::Minus {
344        //     return Err(vm.new_value_error("Cannot convert negative int".to_owned()));
345        // }
346
347        I::try_from(self.as_bigint()).map_err(|_| {
348            vm.new_overflow_error(format!(
349                "Python int too large to convert to Rust {}",
350                core::any::type_name::<I>()
351            ))
352        })
353    }
354
355    #[inline]
356    fn int_op<F>(&self, other: PyObjectRef, op: F) -> PyArithmeticValue<BigInt>
357    where
358        F: Fn(&BigInt, &BigInt) -> BigInt,
359    {
360        let r = other
361            .downcast_ref::<Self>()
362            .map(|other| op(&self.value, &other.value));
363        PyArithmeticValue::from_option(r)
364    }
365
366    #[inline]
367    fn general_op<F>(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult
368    where
369        F: Fn(&BigInt, &BigInt) -> PyResult,
370    {
371        if let Some(other) = other.downcast_ref::<Self>() {
372            op(&self.value, &other.value)
373        } else {
374            Ok(vm.ctx.not_implemented())
375        }
376    }
377}
378
379#[pyclass(
380    itemsize = 4,
381    flags(BASETYPE, _MATCH_SELF),
382    with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable)
383)]
384impl PyInt {
385    pub(crate) fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
386        self.int_op(other, |a, b| a ^ b)
387    }
388
389    pub(crate) fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
390        self.int_op(other, |a, b| a | b)
391    }
392
393    pub(crate) fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
394        self.int_op(other, |a, b| a & b)
395    }
396
397    fn modpow(&self, other: PyObjectRef, modulus: PyObjectRef, vm: &VirtualMachine) -> PyResult {
398        let modulus = match modulus.downcast_ref::<Self>() {
399            Some(val) => val.as_bigint(),
400            None => return Ok(vm.ctx.not_implemented()),
401        };
402        if modulus.is_zero() {
403            return Err(vm.new_value_error("pow() 3rd argument cannot be 0"));
404        }
405
406        self.general_op(
407            other,
408            |a, b| {
409                let i = if b.is_negative() {
410                    // modular multiplicative inverse
411                    // based on rust-num/num-integer#10, should hopefully be published soon
412                    fn normalize(a: BigInt, n: &BigInt) -> BigInt {
413                        let a = a % n;
414                        if a.is_negative() { a + n } else { a }
415                    }
416                    fn inverse(a: BigInt, n: &BigInt) -> Option<BigInt> {
417                        let ExtendedGcd { gcd, x: c, .. } = a.extended_gcd(n);
418                        if gcd.is_one() {
419                            Some(normalize(c, n))
420                        } else {
421                            None
422                        }
423                    }
424                    let a = inverse(a % modulus, modulus).ok_or_else(|| {
425                        vm.new_value_error("base is not invertible for the given modulus")
426                    })?;
427                    let b = -b;
428                    a.modpow(&b, modulus)
429                } else {
430                    a.modpow(b, modulus)
431                };
432                Ok(vm.ctx.new_int(i).into())
433            },
434            vm,
435        )
436    }
437
438    #[pymethod]
439    fn __round__(
440        zelf: PyRef<Self>,
441        ndigits: OptionalOption<PyIntRef>,
442        vm: &VirtualMachine,
443    ) -> PyResult<PyRef<Self>> {
444        if let Some(ndigits) = ndigits.flatten() {
445            let ndigits = ndigits.as_bigint();
446            // round(12345, -2) == 12300
447            // If precision >= 0, then any integer is already rounded correctly
448            if let Some(ndigits) = ndigits.neg().to_u32()
449                && ndigits > 0
450            {
451                // Work with positive integers and negate at the end if necessary
452                let sign = if zelf.value.is_negative() {
453                    BigInt::from(-1)
454                } else {
455                    BigInt::from(1)
456                };
457                let value = zelf.value.abs();
458
459                // Divide and multiply by the power of 10 to get the approximate answer
460                let pow10 = BigInt::from(10).pow(ndigits);
461                let quotient = &value / &pow10;
462                let rounded = &quotient * &pow10;
463
464                // Malachite division uses floor rounding, Python uses half-even
465                let remainder = &value - &rounded;
466                let half_pow10 = &pow10 / BigInt::from(2);
467                let correction =
468                    if remainder > half_pow10 || (remainder == half_pow10 && quotient.is_odd()) {
469                        pow10
470                    } else {
471                        BigInt::from(0)
472                    };
473                let rounded = (rounded + correction) * sign;
474                return Ok(vm.ctx.new_int(rounded));
475            }
476        }
477        Ok(zelf)
478    }
479
480    #[pymethod]
481    fn __trunc__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
482        zelf.__int__(vm)
483    }
484
485    #[pymethod]
486    fn __floor__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
487        zelf.__int__(vm)
488    }
489
490    #[pymethod]
491    fn __ceil__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
492        zelf.__int__(vm)
493    }
494
495    #[pymethod]
496    fn __format__(zelf: &Py<Self>, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
497        // Empty format spec on a subclass: equivalent to str(self)
498        if spec.is_empty() && !zelf.class().is(vm.ctx.types.int_type) {
499            return Ok(zelf.as_object().str(vm)?.as_wtf8().to_owned());
500        }
501        let format_spec =
502            FormatSpec::parse(spec.as_str()).map_err(|err| err.into_pyexception(vm))?;
503        let result = if format_spec.has_locale_format() {
504            let locale = crate::format::get_locale_info();
505            format_spec.format_int_locale(&zelf.value, &locale)
506        } else {
507            format_spec.format_int(&zelf.value)
508        };
509        result
510            .map(Wtf8Buf::from_string)
511            .map_err(|err| err.into_pyexception(vm))
512    }
513
514    #[pymethod]
515    fn __sizeof__(&self) -> usize {
516        core::mem::size_of::<Self>() + (((self.value.bits() + 7) & !7) / 8) as usize
517    }
518
519    #[pymethod]
520    fn as_integer_ratio(&self, vm: &VirtualMachine) -> (PyRef<Self>, i32) {
521        (vm.ctx.new_bigint(&self.value), 1)
522    }
523
524    #[pymethod]
525    fn bit_length(&self) -> u64 {
526        self.value.bits()
527    }
528
529    #[pymethod]
530    fn conjugate(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
531        zelf.__int__(vm)
532    }
533
534    #[pyclassmethod]
535    fn from_bytes(
536        cls: PyTypeRef,
537        args: IntFromByteArgs,
538        vm: &VirtualMachine,
539    ) -> PyResult<PyRef<Self>> {
540        let signed = args.signed.map_or(false, Into::into);
541        let value = match (args.byteorder, signed) {
542            (ArgByteOrder::Big, true) => BigInt::from_signed_bytes_be(args.bytes.as_bytes()),
543            (ArgByteOrder::Big, false) => BigInt::from_bytes_be(Sign::Plus, args.bytes.as_bytes()),
544            (ArgByteOrder::Little, true) => BigInt::from_signed_bytes_le(args.bytes.as_bytes()),
545            (ArgByteOrder::Little, false) => {
546                BigInt::from_bytes_le(Sign::Plus, args.bytes.as_bytes())
547            }
548        };
549        Self::with_value(cls, value, vm)
550    }
551
552    #[pymethod]
553    fn to_bytes(&self, args: IntToByteArgs, vm: &VirtualMachine) -> PyResult<PyBytes> {
554        let signed = args.signed.map_or(false, Into::into);
555        let byte_len = args.length;
556
557        let value = self.as_bigint();
558        match value.sign() {
559            Sign::Minus if !signed => {
560                return Err(vm.new_overflow_error("can't convert negative int to unsigned"));
561            }
562            Sign::NoSign => return Ok(vec![0u8; byte_len].into()),
563            _ => {}
564        }
565
566        let mut origin_bytes = match (args.byteorder, signed) {
567            (ArgByteOrder::Big, true) => value.to_signed_bytes_be(),
568            (ArgByteOrder::Big, false) => value.to_bytes_be().1,
569            (ArgByteOrder::Little, true) => value.to_signed_bytes_le(),
570            (ArgByteOrder::Little, false) => value.to_bytes_le().1,
571        };
572
573        let origin_len = origin_bytes.len();
574        if origin_len > byte_len {
575            return Err(vm.new_overflow_error("int too big to convert"));
576        }
577
578        let mut append_bytes = match value.sign() {
579            Sign::Minus => vec![255u8; byte_len - origin_len],
580            _ => vec![0u8; byte_len - origin_len],
581        };
582
583        let bytes = match args.byteorder {
584            ArgByteOrder::Big => {
585                let mut bytes = append_bytes;
586                bytes.append(&mut origin_bytes);
587                bytes
588            }
589            ArgByteOrder::Little => {
590                let mut bytes = origin_bytes;
591                bytes.append(&mut append_bytes);
592                bytes
593            }
594        };
595        Ok(bytes.into())
596    }
597
598    #[pygetset]
599    fn real(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
600        zelf.__int__(vm)
601    }
602
603    #[pygetset]
604    const fn imag(&self) -> usize {
605        0
606    }
607
608    #[pygetset]
609    fn numerator(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyRefExact<Self> {
610        zelf.__int__(vm)
611    }
612
613    #[pygetset]
614    const fn denominator(&self) -> usize {
615        1
616    }
617
618    #[pymethod]
619    const fn is_integer(&self) -> bool {
620        true
621    }
622
623    #[pymethod]
624    fn bit_count(&self) -> u32 {
625        self.value.iter_u32_digits().map(|n| n.count_ones()).sum()
626    }
627
628    #[pymethod]
629    fn __getnewargs__(&self, vm: &VirtualMachine) -> PyObjectRef {
630        (self.value.clone(),).to_pyobject(vm)
631    }
632}
633
634#[pyclass]
635impl PyRef<PyInt> {
636    pub(crate) fn __int__(self, vm: &VirtualMachine) -> PyRefExact<PyInt> {
637        self.into_exact_or(&vm.ctx, |zelf| unsafe {
638            // TODO: this is actually safe. we need better interface
639            PyRefExact::new_unchecked(vm.ctx.new_bigint(&zelf.value))
640        })
641    }
642}
643
644impl Comparable for PyInt {
645    fn cmp(
646        zelf: &Py<Self>,
647        other: &PyObject,
648        op: PyComparisonOp,
649        _vm: &VirtualMachine,
650    ) -> PyResult<PyComparisonValue> {
651        let r = other
652            .downcast_ref::<Self>()
653            .map(|other| op.eval_ord(zelf.value.cmp(&other.value)));
654        Ok(PyComparisonValue::from_option(r))
655    }
656}
657
658impl Representable for PyInt {
659    #[inline]
660    fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
661        Ok(zelf.to_str_radix_10())
662    }
663}
664
665impl Hashable for PyInt {
666    #[inline]
667    fn hash(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<hash::PyHash> {
668        Ok(hash::hash_bigint(zelf.as_bigint()))
669    }
670}
671
672impl AsNumber for PyInt {
673    fn as_number() -> &'static PyNumberMethods {
674        static AS_NUMBER: PyNumberMethods = PyInt::AS_NUMBER;
675        &AS_NUMBER
676    }
677
678    #[inline]
679    fn clone_exact(zelf: &Py<Self>, vm: &VirtualMachine) -> PyRef<Self> {
680        vm.ctx.new_bigint(&zelf.value)
681    }
682}
683
684impl PyInt {
685    pub(super) const AS_NUMBER: PyNumberMethods = PyNumberMethods {
686        add: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a + b, vm)),
687        subtract: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a - b, vm)),
688        multiply: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a * b, vm)),
689        remainder: Some(|a, b, vm| Self::number_op(a, b, inner_mod, vm)),
690        divmod: Some(|a, b, vm| Self::number_op(a, b, inner_divmod, vm)),
691        power: Some(|a, b, c, vm| {
692            if let Some(a) = a.downcast_ref::<Self>() {
693                if vm.is_none(c) {
694                    a.general_op(b.to_owned(), |a, b| inner_pow(a, b, vm), vm)
695                } else {
696                    a.modpow(b.to_owned(), c.to_owned(), vm)
697                }
698            } else {
699                Ok(vm.ctx.not_implemented())
700            }
701        }),
702        negative: Some(|num, vm| (&Self::number_downcast(num).value).neg().to_pyresult(vm)),
703        positive: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
704        absolute: Some(|num, vm| Self::number_downcast(num).value.abs().to_pyresult(vm)),
705        boolean: Some(|num, _vm| Ok(!Self::number_downcast(num).value.is_zero())),
706        invert: Some(|num, vm| (&Self::number_downcast(num).value).not().to_pyresult(vm)),
707        lshift: Some(|a, b, vm| Self::number_op(a, b, inner_lshift, vm)),
708        rshift: Some(|a, b, vm| Self::number_op(a, b, inner_rshift, vm)),
709        and: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a & b, vm)),
710        xor: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a ^ b, vm)),
711        or: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a | b, vm)),
712        int: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
713        float: Some(|num, vm| {
714            let zelf = Self::number_downcast(num);
715            try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x).into())
716        }),
717        floor_divide: Some(|a, b, vm| Self::number_op(a, b, inner_floordiv, vm)),
718        true_divide: Some(|a, b, vm| Self::number_op(a, b, inner_truediv, vm)),
719        index: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
720        ..PyNumberMethods::NOT_IMPLEMENTED
721    };
722
723    fn number_op<F, R>(a: &PyObject, b: &PyObject, op: F, vm: &VirtualMachine) -> PyResult
724    where
725        F: FnOnce(&BigInt, &BigInt, &VirtualMachine) -> R,
726        R: ToPyResult,
727    {
728        if let (Some(a), Some(b)) = (a.downcast_ref::<Self>(), b.downcast_ref::<Self>()) {
729            op(&a.value, &b.value, vm).to_pyresult(vm)
730        } else {
731            Ok(vm.ctx.not_implemented())
732        }
733    }
734}
735
736#[derive(FromArgs)]
737pub struct IntOptions {
738    #[pyarg(positional, optional)]
739    val_options: OptionalArg<PyObjectRef>,
740    #[pyarg(any, optional)]
741    base: OptionalArg<PyObjectRef>,
742}
743
744#[derive(FromArgs)]
745struct IntFromByteArgs {
746    bytes: PyBytesInner,
747    #[pyarg(any, default = ArgByteOrder::Big)]
748    byteorder: ArgByteOrder,
749    #[pyarg(named, optional)]
750    signed: OptionalArg<ArgIntoBool>,
751}
752
753#[derive(FromArgs)]
754struct IntToByteArgs {
755    #[pyarg(any, default = 1)]
756    length: usize,
757    #[pyarg(any, default = ArgByteOrder::Big)]
758    byteorder: ArgByteOrder,
759    #[pyarg(named, optional)]
760    signed: OptionalArg<ArgIntoBool>,
761}
762
763fn try_int_radix(obj: &PyObject, base: u32, vm: &VirtualMachine) -> PyResult<BigInt> {
764    match_class!(match obj.to_owned() {
765        string @ PyStr => {
766            let s = string.as_wtf8().trim();
767            bytes_to_int(s.as_bytes(), base, vm.state.int_max_str_digits.load())
768                .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
769        }
770        bytes @ PyBytes => {
771            bytes_to_int(bytes.as_bytes(), base, vm.state.int_max_str_digits.load())
772                .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
773        }
774        bytearray @ PyByteArray => {
775            let inner = bytearray.borrow_buf();
776            bytes_to_int(&inner, base, vm.state.int_max_str_digits.load())
777                .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
778        }
779        _ => Err(vm.new_type_error("int() can't convert non-string with explicit base")),
780    })
781}
782
783// Retrieve inner int value:
784pub(crate) fn get_value(obj: &PyObject) -> &BigInt {
785    &obj.downcast_ref::<PyInt>().unwrap().value
786}
787
788pub fn try_to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
789    bigint_to_finite_float(int)
790        .ok_or_else(|| vm.new_overflow_error("int too large to convert to float"))
791}
792
793fn vectorcall_int(
794    zelf_obj: &PyObject,
795    args: Vec<PyObjectRef>,
796    nargs: usize,
797    kwnames: Option<&[PyObjectRef]>,
798    vm: &VirtualMachine,
799) -> PyResult {
800    let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
801    let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
802    (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
803}
804
805pub(crate) fn init(context: &'static Context) {
806    PyInt::extend_class(context, context.types.int_type);
807    context
808        .types
809        .int_type
810        .slots
811        .vectorcall
812        .store(Some(vectorcall_int));
813}