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 byte::bytes_from_object,
7 class::{PyClassDef, 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, PyArithmeticValue, PyComparisonValue,
17 PySsize,
18 },
19 protocol::{PyNumberMethods, handle_bytes_to_int_err, numeric_literal_from_str},
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
53thread_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 let owned;
121 let int = if let Some(int) = obj.downcast_ref::<PyInt>() {
122 int
123 } else {
124 owned = obj.try_index(vm)?;
125 &owned
126 };
127 int.try_to_primitive(vm)
128 }
129 }
130 )*};
131}
132
133impl_try_from_object_int!(
134 (isize, to_isize),
135 (i8, to_i8),
136 (i16, to_i16),
137 (i32, to_i32),
138 (i64, to_i64),
139 (i128, to_i128),
140 (usize, to_usize),
141 (u8, to_u8),
142 (u16, to_u16),
143 (u32, to_u32),
144 (u64, to_u64),
145 (u128, to_u128),
146);
147
148fn inner_pow(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
149 if int2.is_negative() {
150 let v1 = try_to_float(int1, vm)?;
151 let v2 = try_to_float(int2, vm)?;
152 float::float_pow(v1, v2, vm)
153 } else {
154 let value = if let Some(v2) = int2.to_u64() {
155 let base_bits = int1.bits();
158 if base_bits > 1 {
159 let bits = if int1.trailing_zeros() == Some(base_bits - 1) {
160 (base_bits - 1)
161 .checked_mul(v2)
162 .and_then(|b| b.checked_add(1))
163 } else {
164 base_bits.checked_mul(v2).and_then(|b| b.checked_mul(2))
165 };
166 reserve_result_bits(bits, vm)?;
167 }
168 return Ok(vm.ctx.new_int(Pow::pow(int1, v2)).into());
169 } else if int1.is_one() {
170 1
171 } else if int1.is_zero() {
172 0
173 } else if int1 == &BigInt::from(-1) {
174 if int2.is_odd() { -1 } else { 1 }
175 } else {
176 return Ok(vm.ctx.not_implemented());
179 };
180 Ok(vm.ctx.new_int(value).into())
181 }
182}
183
184fn inner_mod(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
185 if int2.is_zero() {
186 Err(vm.new_zero_division_error("division by zero"))
187 } else {
188 Ok(vm.ctx.new_int(int1.mod_floor(int2)).into())
189 }
190}
191
192fn inner_floordiv(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
193 if int2.is_zero() {
194 Err(vm.new_zero_division_error("division by zero"))
195 } else {
196 Ok(vm.ctx.new_int(int1.div_floor(int2)).into())
197 }
198}
199
200fn inner_divmod(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
201 if int2.is_zero() {
202 return Err(vm.new_zero_division_error("division by zero"));
203 }
204 let (div, modulo) = int1.div_mod_floor(int2);
205 Ok(vm.new_tuple((div, modulo)).into())
206}
207
208fn inner_lshift(base: &BigInt, bits: &BigInt, vm: &VirtualMachine) -> PyResult {
209 inner_shift(
210 base,
211 bits,
212 |base, bits| base << bits,
213 |bits, vm| {
214 const MAX_DIGITS: u128 = (i64::MAX as u128 - 1) / 30;
216 let digits = bits.to_u128().map(|shift| {
217 u128::from(base.bits().div_ceil(30)) + shift / 30 + u128::from(shift % 30 != 0)
218 });
219 if digits.is_none_or(|digits| digits > MAX_DIGITS) {
220 return Err(vm.new_overflow_error("too many digits in integer"));
221 }
222 let shift = bits.to_u64().ok_or_else(|| vm.no_memory_error())?;
223 reserve_result_bits(base.bits().checked_add(shift), vm)?;
224 usize::try_from(shift).map_err(|_| vm.no_memory_error())
225 },
226 vm,
227 )
228}
229
230fn reserve_result_bits(bits: Option<u64>, vm: &VirtualMachine) -> PyResult<()> {
234 const CHECK_FROM_BITS: u64 = 1 << 26;
236 let bits = bits.ok_or_else(|| vm.no_memory_error())?;
237 if bits < CHECK_FROM_BITS {
238 return Ok(());
239 }
240 let words =
241 usize::try_from(bits.div_ceil(u64::BITS.into())).map_err(|_| vm.no_memory_error())?;
242 Vec::<u64>::new()
243 .try_reserve_exact(words)
244 .map_err(|_| vm.no_memory_error())
245}
246
247fn inner_rshift(base: &BigInt, bits: &BigInt, vm: &VirtualMachine) -> PyResult {
248 inner_shift(
249 base,
250 bits,
251 |base, bits| base >> bits,
252 |bits, _vm| Ok(bits.to_usize().unwrap_or(usize::MAX)),
253 vm,
254 )
255}
256
257fn inner_shift<F, S>(
258 base: &BigInt,
259 bits: &BigInt,
260 shift_op: F,
261 shift_bits: S,
262 vm: &VirtualMachine,
263) -> PyResult
264where
265 F: Fn(&BigInt, usize) -> BigInt,
266 S: Fn(&BigInt, &VirtualMachine) -> PyResult<usize>,
267{
268 if bits.is_negative() {
269 Err(vm.new_value_error("negative shift count"))
270 } else if base.is_zero() {
271 Ok(vm.ctx.new_int(0).into())
272 } else {
273 shift_bits(bits, vm).map(|bits| vm.ctx.new_int(shift_op(base, bits)).into())
274 }
275}
276
277fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult {
278 if i2.is_zero() {
279 return Err(vm.new_zero_division_error("division by zero"));
280 }
281
282 let float = true_div(i1, i2);
283
284 if float.is_infinite() {
285 Err(vm.new_overflow_error("integer division result too large for a float"))
286 } else {
287 Ok(vm.ctx.new_float(float).into())
288 }
289}
290
291impl Constructor for PyInt {
292 type Args = FuncArgs;
293
294 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
295 if cls.is(vm.ctx.types.bool_type) {
296 return Err(vm.new_type_error("int.__new__(bool) is not safe, use bool.__new__()"));
297 }
298
299 if cls.is(vm.ctx.types.int_type)
301 && args.args.len() == 1
302 && args.kwargs.is_empty()
303 && args.args[0].class().is(vm.ctx.types.int_type)
304 {
305 return Ok(args.args[0].clone());
306 }
307
308 let options: IntOptions = args.bind_for(vm, Self::NAME)?;
309 let value = if let OptionalArg::Present(val) = options.val_options {
310 if let OptionalArg::Present(base) = options.base {
311 let base = base
312 .try_index(vm)?
313 .as_bigint()
314 .to_u32()
315 .filter(|&v| v == 0 || (2..=36).contains(&v))
316 .ok_or_else(|| vm.new_value_error("int() base must be >= 2 and <= 36, or 0"))?;
317 try_int_radix(&val, base, vm)
318 } else {
319 val.try_int(vm).map(|x| x.as_bigint().clone())
320 }
321 } else if let OptionalArg::Present(_) = options.base {
322 Err(vm.new_type_error("int() missing string argument"))
323 } else {
324 Ok(Zero::zero())
325 }?;
326
327 Self::with_value(cls, value, vm).map(Into::into)
328 }
329
330 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
331 unimplemented!("use slot_new")
332 }
333}
334
335impl PyInt {
336 fn with_value<T>(cls: PyTypeRef, value: T, vm: &VirtualMachine) -> PyResult<PyRef<Self>>
337 where
338 T: Into<BigInt> + ToPrimitive,
339 {
340 if cls.is(vm.ctx.types.int_type) {
341 Ok(vm.ctx.new_int(value))
342 } else if cls.is(vm.ctx.types.bool_type) {
343 Ok(vm.ctx.new_bool(!value.into().eq(&BigInt::zero())).upcast())
344 } else {
345 Self::from(value).into_ref_with_type(vm, cls)
346 }
347 }
348
349 #[must_use]
350 pub const fn as_bigint(&self) -> &BigInt {
351 &self.value
352 }
353}
354
355impl Py<PyInt> {
356 #[must_use]
357 #[inline]
358 pub const fn as_bigint(&self) -> &BigInt {
359 self.payload.as_bigint()
360 }
361
362 #[inline(always)]
364 pub(crate) fn try_to_i64_fast(&self) -> Option<i64> {
365 let bits = self.as_bigint().bits();
366 if bits > i64::BITS as u64 {
367 return None;
368 }
369 let magnitude = self.as_bigint().iter_u64_digits().next().unwrap_or(0);
370 let signed_magnitude = i64::try_from(magnitude).ok();
371 match self.as_bigint().sign() {
372 Sign::Minus if magnitude == 1u64 << 63 => Some(i64::MIN),
373 Sign::Minus => signed_magnitude.map(|value| -value),
374 Sign::NoSign | Sign::Plus => signed_magnitude,
375 }
376 }
377
378 #[inline]
380 #[must_use]
381 pub fn to_str_radix_10(&self) -> String {
382 match self.as_bigint().to_i64() {
383 Some(i) => itoa::Buffer::new().format(i).to_owned(),
384 None => self.as_bigint().to_string(),
385 }
386 }
387
388 #[must_use]
390 pub fn as_u32_mask(&self) -> u32 {
391 let v = self.as_bigint();
392 let out = v.iter_u32_digits().next().unwrap_or(0);
393 match v.sign() {
394 Sign::Minus => out.wrapping_neg(),
395 _ => out,
396 }
397 }
398
399 #[must_use]
401 pub fn as_u64_mask(&self) -> u64 {
402 let v = self.as_bigint();
403 let mut digits = v.iter_u32_digits();
404 let out =
405 u64::from(digits.next().unwrap_or(0)) | (u64::from(digits.next().unwrap_or(0)) << 32);
406 match v.sign() {
407 Sign::Minus => out.wrapping_neg(),
408 _ => out,
409 }
410 }
411
412 pub fn try_to_primitive<I>(&self, vm: &VirtualMachine) -> PyResult<I>
415 where
416 I: PrimInt + for<'a> TryFrom<&'a BigInt>,
417 {
418 self.to_primitive(true, vm)
419 }
420
421 pub fn try_to_primitive_in_range<I>(&self, vm: &VirtualMachine) -> PyResult<I>
424 where
425 I: PrimInt + for<'a> TryFrom<&'a BigInt>,
426 {
427 self.to_primitive(false, vm)
428 }
429
430 fn to_primitive<I>(&self, reject_negative: bool, vm: &VirtualMachine) -> PyResult<I>
432 where
433 I: PrimInt + for<'a> TryFrom<&'a BigInt>,
434 {
435 if reject_negative && I::min_value() == I::zero() && self.as_bigint().sign() == Sign::Minus
436 {
437 return Err(vm.new_value_error("can't convert negative number to unsigned"));
438 }
439 I::try_from(self.as_bigint()).map_err(|_| {
440 vm.new_overflow_error(format!(
441 "Python int too large to convert to Rust {}",
442 core::any::type_name::<I>()
443 ))
444 })
445 }
446
447 pub(crate) fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
448 self.int_op(&other, |a, b| a ^ b)
449 }
450
451 pub(crate) fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
452 self.int_op(&other, |a, b| a | b)
453 }
454
455 pub(crate) fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue<BigInt> {
456 self.int_op(&other, |a, b| a & b)
457 }
458
459 #[inline]
460 fn int_op<F>(&self, other: &PyObject, op: F) -> PyArithmeticValue<BigInt>
461 where
462 F: Fn(&BigInt, &BigInt) -> BigInt,
463 {
464 let r = other
465 .downcast_ref::<PyInt>()
466 .map(|other| op(self.as_bigint(), other.as_bigint()));
467 PyArithmeticValue::from_option(r)
468 }
469
470 #[inline]
471 fn general_op<F>(&self, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult
472 where
473 F: Fn(&BigInt, &BigInt) -> PyResult,
474 {
475 if let Some(other) = other.downcast_ref::<PyInt>() {
476 op(self.as_bigint(), other.as_bigint())
477 } else {
478 Ok(vm.ctx.not_implemented())
479 }
480 }
481
482 fn modpow(&self, other: &PyObject, modulus: &PyObject, vm: &VirtualMachine) -> PyResult {
483 if other.downcast_ref::<PyInt>().is_none() {
484 return Ok(vm.ctx.not_implemented());
485 }
486 let modulus = match modulus.downcast_ref::<PyInt>() {
487 Some(val) => val.as_bigint(),
488 None => return Ok(vm.ctx.not_implemented()),
489 };
490 if modulus.is_zero() {
491 return Err(vm.new_value_error("pow() 3rd argument cannot be 0"));
492 }
493
494 self.general_op(
495 other,
496 |a, b| {
497 let i = if b.is_negative() {
498 fn normalize(a: BigInt, n: &BigInt) -> BigInt {
501 let a = a % n;
502 if a.is_negative() { a + n } else { a }
503 }
504 fn inverse(a: BigInt, n: &BigInt) -> Option<BigInt> {
505 let ExtendedGcd { gcd, x: c, .. } = a.extended_gcd(n);
506 if gcd.is_one() {
507 Some(normalize(c, n))
508 } else {
509 None
510 }
511 }
512 let a = inverse(a % modulus, modulus).ok_or_else(|| {
513 vm.new_value_error("base is not invertible for the given modulus")
514 })?;
515 let b = -b;
516 a.modpow(&b, modulus)
517 } else {
518 a.modpow(b, modulus)
519 };
520 Ok(vm.ctx.new_int(i).into())
521 },
522 vm,
523 )
524 }
525}
526
527#[derive(FromArgs)]
528struct RoundArgs {
529 #[pyarg(positional, optional)]
530 ndigits: Option<PyIntRef>,
531}
532
533#[pyclass(
534 itemsize = 4,
535 flags(BASETYPE, _MATCH_SELF),
536 with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable)
537)]
538impl Py<PyInt> {
539 #[pymethod]
540 fn __round__(zelf: PyRef<PyInt>, args: RoundArgs, vm: &VirtualMachine) -> PyRef<PyInt> {
541 if let Some(ndigits) = args.ndigits {
542 let ndigits = ndigits.as_bigint();
543 if let Some(ndigits) = ndigits.neg().to_u32()
546 && ndigits > 0
547 {
548 let sign = if zelf.as_bigint().is_negative() {
550 BigInt::from(-1)
551 } else {
552 BigInt::from(1)
553 };
554 let value = zelf.as_bigint().abs();
555
556 let pow10 = BigInt::from(10).pow(ndigits);
558 let quotient = &value / &pow10;
559 let rounded = "ient * &pow10;
560
561 let remainder = &value - &rounded;
563 let half_pow10 = &pow10 / BigInt::from(2);
564 let correction =
565 if remainder > half_pow10 || (remainder == half_pow10 && quotient.is_odd()) {
566 pow10
567 } else {
568 BigInt::from(0)
569 };
570 let rounded = (rounded + correction) * sign;
571 return vm.ctx.new_int(rounded);
572 }
573 }
574 zelf.__int__(vm).into_pyref()
577 }
578
579 #[pymethod]
580 fn __trunc__(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
581 zelf.__int__(vm)
582 }
583
584 #[pymethod]
585 fn __floor__(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
586 zelf.__int__(vm)
587 }
588
589 #[pymethod]
590 fn __ceil__(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
591 zelf.__int__(vm)
592 }
593
594 #[pymethod]
595 fn __format__(
596 zelf: &Self,
597 format_spec: PyUtf8StrRef,
598 vm: &VirtualMachine,
599 ) -> PyResult<Wtf8Buf> {
600 if format_spec.is_empty() && !zelf.class().is(vm.ctx.types.int_type) {
602 return Ok(zelf.as_object().str(vm)?.as_wtf8().to_owned());
603 }
604 let format_spec =
605 FormatSpec::parse(format_spec.as_str()).map_err(|err| err.into_pyexception(vm))?;
606 if format_spec.is_decimal_int_format() {
607 check_int_to_str_digits(zelf.as_bigint(), vm)?;
608 }
609 let result = if format_spec.has_locale_format() {
610 let locale = crate::format::get_locale_info();
611 format_spec.format_int_locale(zelf.as_bigint(), &locale)
612 } else {
613 format_spec.format_int(zelf.as_bigint())
614 };
615 result
616 .map(Wtf8Buf::from_string)
617 .map_err(|err| err.into_pyexception(vm))
618 }
619
620 #[pymethod]
621 fn __sizeof__(&self) -> usize {
622 core::mem::size_of::<PyInt>() + (((self.value.bits() + 7) & !7) / 8) as usize
623 }
624
625 #[pymethod]
626 fn as_integer_ratio(&self, vm: &VirtualMachine) -> (PyRef<PyInt>, i32) {
627 (vm.ctx.new_bigint(&self.value), 1)
628 }
629
630 #[pymethod]
631 fn bit_length(&self) -> u64 {
632 self.value.bits()
633 }
634
635 #[pymethod]
636 fn conjugate(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
637 zelf.__int__(vm)
638 }
639
640 #[pyclassmethod]
641 fn from_bytes(
642 cls: PyTypeRef,
643 args: IntFromByteArgs,
644 vm: &VirtualMachine,
645 ) -> PyResult<PyRef<PyInt>> {
646 let signed = args.signed.into();
647 let bytes = bytes_from_object(vm, &args.bytes)?;
649 let value = match (args.byteorder, signed) {
650 (ArgByteOrder::Big, true) => BigInt::from_signed_bytes_be(&bytes),
651 (ArgByteOrder::Big, false) => BigInt::from_bytes_be(Sign::Plus, &bytes),
652 (ArgByteOrder::Little, true) => BigInt::from_signed_bytes_le(&bytes),
653 (ArgByteOrder::Little, false) => BigInt::from_bytes_le(Sign::Plus, &bytes),
654 };
655 PyInt::with_value(cls, value, vm)
656 }
657
658 #[pymethod]
659 fn to_bytes(&self, args: IntToByteArgs, vm: &VirtualMachine) -> PyResult<PyBytes> {
660 let signed: bool = args.signed.into();
661 let byte_len = args.length;
664 let byte_len = usize::try_from(byte_len)
665 .map_err(|_| vm.new_value_error("length argument must be non-negative"))?;
666
667 let value = self.as_bigint();
668 match value.sign() {
669 Sign::Minus if !signed => {
670 return Err(vm.new_overflow_error("can't convert negative int to unsigned"));
671 }
672 Sign::NoSign => return Ok(vm.new_zeroed_bytes(byte_len)?.into()),
673 _ => {}
674 }
675
676 let mut origin_bytes = match (args.byteorder, signed) {
677 (ArgByteOrder::Big, true) => value.to_signed_bytes_be(),
678 (ArgByteOrder::Big, false) => value.to_bytes_be().1,
679 (ArgByteOrder::Little, true) => value.to_signed_bytes_le(),
680 (ArgByteOrder::Little, false) => value.to_bytes_le().1,
681 };
682
683 let origin_len = origin_bytes.len();
684 if origin_len > byte_len {
685 return Err(vm.new_overflow_error("int too big to convert"));
686 }
687
688 let mut append_bytes = vm.new_zeroed_bytes(byte_len - origin_len)?;
689 if value.sign() == Sign::Minus {
690 append_bytes.fill(255);
691 }
692
693 let bytes = match args.byteorder {
694 ArgByteOrder::Big => {
695 let mut bytes = append_bytes;
696 bytes.append(&mut origin_bytes);
697 bytes
698 }
699 ArgByteOrder::Little => {
700 let mut bytes = origin_bytes;
701 bytes.append(&mut append_bytes);
702 bytes
703 }
704 };
705 Ok(bytes.into())
706 }
707
708 #[pygetset]
709 fn real(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
710 zelf.__int__(vm)
711 }
712
713 #[pygetset]
714 const fn imag(&self) -> usize {
715 0
716 }
717
718 #[pygetset]
719 fn numerator(zelf: PyRef<PyInt>, vm: &VirtualMachine) -> PyRefExact<PyInt> {
720 zelf.__int__(vm)
721 }
722
723 #[pygetset]
724 const fn denominator(&self) -> usize {
725 1
726 }
727
728 #[pymethod]
729 const fn is_integer(&self) -> bool {
730 true
731 }
732
733 #[pymethod]
734 fn bit_count(&self) -> u32 {
735 self.value.iter_u32_digits().map(|n| n.count_ones()).sum()
736 }
737
738 #[pymethod]
739 fn __getnewargs__(&self, vm: &VirtualMachine) -> PyObjectRef {
740 (self.value.clone(),).to_pyobject(vm)
741 }
742}
743
744#[pyclass]
745impl PyRef<PyInt> {
746 pub(crate) fn __int__(self, vm: &VirtualMachine) -> PyRefExact<PyInt> {
747 self.into_exact_or(&vm.ctx, |zelf| unsafe {
748 PyRefExact::new_unchecked(vm.ctx.new_bigint(zelf.as_bigint()))
750 })
751 }
752}
753
754impl Comparable for PyInt {
755 fn cmp(
756 zelf: &Py<Self>,
757 other: &PyObject,
758 op: PyComparisonOp,
759 _vm: &VirtualMachine,
760 ) -> PyResult<PyComparisonValue> {
761 let r = other
762 .downcast_ref::<Self>()
763 .map(|other| op.eval_ord(zelf.as_bigint().cmp(other.as_bigint())));
764 Ok(PyComparisonValue::from_option(r))
765 }
766}
767
768pub(crate) fn check_int_to_str_digits(value: &BigInt, vm: &VirtualMachine) -> PyResult<()> {
772 let limit = vm.state.int_max_str_digits.load();
773 if limit == 0 {
774 return Ok(());
775 }
776 let bits = value.bits();
777 if bits < 1500 {
779 return Ok(());
780 }
781 let digits_upper = bits.saturating_mul(30103) / 100_000 + 1;
786 if digits_upper > u64::try_from(limit).unwrap_or(u64::MAX) {
787 return Err(vm.new_value_error(format!(
788 "Exceeds the limit ({limit} digits) for integer string conversion; \
789 use sys.set_int_max_str_digits() to increase the limit"
790 )));
791 }
792 Ok(())
793}
794
795impl Representable for PyInt {
796 #[inline]
797 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
798 check_int_to_str_digits(zelf.as_bigint(), vm)?;
799 Ok(zelf.to_str_radix_10())
800 }
801}
802
803impl Hashable for PyInt {
804 #[inline]
805 fn hash(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<hash::PyHash> {
806 Ok(hash::hash_bigint(zelf.as_bigint()))
807 }
808}
809
810impl AsNumber for PyInt {
811 fn as_number() -> &'static PyNumberMethods {
812 static AS_NUMBER: PyNumberMethods = PyInt::AS_NUMBER;
813 &AS_NUMBER
814 }
815
816 #[inline]
817 fn clone_exact(zelf: &Py<Self>, vm: &VirtualMachine) -> PyRef<Self> {
818 vm.ctx.new_bigint(zelf.as_bigint())
819 }
820}
821
822impl PyInt {
823 pub(super) const AS_NUMBER: PyNumberMethods = PyNumberMethods {
824 add: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a + b, vm)),
825 subtract: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a - b, vm)),
826 multiply: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a * b, vm)),
827 remainder: Some(|a, b, vm| Self::number_op(a, b, inner_mod, vm)),
828 divmod: Some(|a, b, vm| Self::number_op(a, b, inner_divmod, vm)),
829 power: Some(|a, b, c, vm| {
830 if let Some(a) = a.downcast_ref::<Self>() {
831 if vm.is_none(c) {
832 a.general_op(b, |a, b| inner_pow(a, b, vm), vm)
833 } else {
834 a.modpow(b, c, vm)
835 }
836 } else {
837 Ok(vm.ctx.not_implemented())
838 }
839 }),
840 negative: Some(|num, vm| Self::number_downcast(num).as_bigint().neg().to_pyresult(vm)),
841 positive: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
842 absolute: Some(|num, vm| Self::number_downcast(num).as_bigint().abs().to_pyresult(vm)),
843 boolean: Some(|num, _vm| Ok(!Self::number_downcast(num).as_bigint().is_zero())),
844 invert: Some(|num, vm| Self::number_downcast(num).as_bigint().not().to_pyresult(vm)),
845 lshift: Some(|a, b, vm| Self::number_op(a, b, inner_lshift, vm)),
846 rshift: Some(|a, b, vm| Self::number_op(a, b, inner_rshift, vm)),
847 and: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a & b, vm)),
848 xor: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a ^ b, vm)),
849 or: Some(|a, b, vm| Self::number_op(a, b, |a, b, _vm| a | b, vm)),
850 int: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
851 float: Some(|num, vm| {
852 let zelf = Self::number_downcast(num);
853 try_to_float(zelf.as_bigint(), vm).map(|x| vm.ctx.new_float(x).into())
854 }),
855 floor_divide: Some(|a, b, vm| Self::number_op(a, b, inner_floordiv, vm)),
856 true_divide: Some(|a, b, vm| Self::number_op(a, b, inner_truediv, vm)),
857 index: Some(|num, vm| Ok(Self::number_downcast_exact(num, vm).into())),
858 ..PyNumberMethods::NOT_IMPLEMENTED
859 };
860
861 fn number_op<F, R>(a: &PyObject, b: &PyObject, op: F, vm: &VirtualMachine) -> PyResult
862 where
863 F: FnOnce(&BigInt, &BigInt, &VirtualMachine) -> R,
864 R: ToPyResult,
865 {
866 if let (Some(a), Some(b)) = (a.downcast_ref::<Self>(), b.downcast_ref::<Self>()) {
867 op(a.as_bigint(), b.as_bigint(), vm).to_pyresult(vm)
868 } else {
869 Ok(vm.ctx.not_implemented())
870 }
871 }
872}
873
874#[derive(FromArgs)]
875pub(crate) struct IntOptions {
876 #[pyarg(positional, optional, py_default = "0")]
878 val_options: OptionalArg<PyObjectRef>,
879 #[pyarg(any, optional, py_default = "10")]
881 base: OptionalArg<PyObjectRef>,
882}
883
884#[derive(FromArgs)]
885struct IntFromByteArgs {
886 bytes: PyObjectRef,
887 #[pyarg(any, default = ArgByteOrder::Big)]
888 byteorder: ArgByteOrder,
889 #[pyarg(named, default = ArgIntoBool::FALSE)]
890 signed: ArgIntoBool,
891}
892
893#[derive(FromArgs)]
894struct IntToByteArgs {
895 #[pyarg(any, default = 1)]
896 length: PySsize,
897 #[pyarg(any, default = ArgByteOrder::Big)]
898 byteorder: ArgByteOrder,
899 #[pyarg(named, default = ArgIntoBool::FALSE)]
900 signed: ArgIntoBool,
901}
902
903fn try_int_radix(obj: &PyObject, base: u32, vm: &VirtualMachine) -> PyResult<BigInt> {
904 match_class!(match obj.to_owned() {
905 string @ PyStr => {
906 let s = numeric_literal_from_str(&string);
907 bytes_to_int(s.as_bytes(), base, vm.state.int_max_str_digits.load())
908 .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
909 }
910 bytes @ PyBytes => {
911 bytes_to_int(bytes.as_bytes(), base, vm.state.int_max_str_digits.load())
912 .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
913 }
914 bytearray @ PyByteArray => {
915 let inner = bytearray.borrow_buf();
916 bytes_to_int(&inner, base, vm.state.int_max_str_digits.load())
917 .map_err(|e| handle_bytes_to_int_err(e, obj, vm))
918 }
919 _ => Err(vm.new_type_error("int() can't convert non-string with explicit base")),
920 })
921}
922
923pub(crate) fn get_value(obj: &PyObject) -> &BigInt {
925 obj.downcast_ref::<PyInt>().unwrap().as_bigint()
926}
927
928pub fn try_to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
929 bigint_to_finite_float(int)
930 .ok_or_else(|| vm.new_overflow_error("int too large to convert to float"))
931}
932
933fn vectorcall_int(
934 zelf_obj: &PyObject,
935 args: Vec<PyObjectRef>,
936 nargs: usize,
937 kwnames: Option<&[PyObjectRef]>,
938 vm: &VirtualMachine,
939) -> PyResult {
940 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
941 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
942 (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
943}
944
945pub(crate) fn init(context: &'static Context) {
946 PyInt::extend_class(context, context.types.int_type);
947 context
948 .types
949 .int_type
950 .slots
951 .vectorcall
952 .store(Some(vectorcall_int));
953}