use crate::{
error::{EvalResult, InterpreterError},
value::Value,
};
fn round_ndigits(args: &[Value]) -> Result<Option<i64>, crate::error::EvalError> {
match args.first() {
None | Some(Value::None) => Ok(None),
Some(Value::Int(n)) => Ok(Some(*n)),
Some(Value::Bool(b)) => Ok(Some(i64::from(*b))),
Some(other) => Err(InterpreterError::TypeError(format!(
"'{}' object cannot be interpreted as an integer",
other.type_name()
))
.into()),
}
}
pub(crate) fn dispatch_int_method(
i: i64,
method: &str,
args: &[Value],
kwargs: &indexmap::IndexMap<String, Value>,
) -> EvalResult {
crate::eval::functions::reject_kwargs(method, kwargs)?;
match method {
"__round__" => Ok(crate::eval::functions::round_int(i, round_ndigits(args)?)),
"bit_length" => {
let n = i.unsigned_abs();
let bits = if n == 0 { 0 } else { i64::from(u64::BITS - n.leading_zeros()) };
Ok(Value::Int(bits))
}
"bit_count" => {
let n = i.unsigned_abs();
Ok(Value::Int(i64::from(n.count_ones())))
}
"conjugate" | "real" | "numerator" => Ok(Value::Int(i)),
"imag" => Ok(Value::Int(0)),
"denominator" => Ok(Value::Int(1)),
"is_integer" => Ok(Value::Bool(true)),
"as_integer_ratio" => Ok(Value::Tuple(vec![Value::Int(i), Value::Int(1)])),
"__index__" | "__int__" | "__trunc__" | "__floor__" | "__ceil__" | "__pos__" => {
Ok(Value::Int(i))
}
"__float__" =>
{
#[allow(clippy::cast_precision_loss)]
Ok(Value::Float(i as f64))
}
"__bool__" => Ok(Value::Bool(i != 0)),
"__invert__" => Ok(Value::Int(!i)),
"__neg__" => Ok(i.checked_neg().map_or_else(
|| crate::value::int_from_bigint(-num_bigint::BigInt::from(i)),
Value::Int,
)),
"__abs__" => Ok(i.checked_abs().map_or_else(
|| crate::value::int_from_bigint(-num_bigint::BigInt::from(i)),
Value::Int,
)),
_ => Err(InterpreterError::AttributeError(format!(
"'int' object has no attribute '{method}'"
))
.into()),
}
}
pub(crate) fn dispatch_bigint_method(
b: &num_bigint::BigInt,
method: &str,
args: &[Value],
kwargs: &indexmap::IndexMap<String, Value>,
) -> EvalResult {
use num_traits::ToPrimitive;
crate::eval::functions::reject_kwargs(method, kwargs)?;
let big = |n: num_bigint::BigInt| Ok(crate::value::int_from_bigint(n));
match method {
"__round__" => big(crate::eval::functions::round_bigint(b, round_ndigits(args)?)),
"bit_length" => Ok(Value::Int(i64::try_from(b.bits()).unwrap_or(i64::MAX))),
"bit_count" => {
let ones: u64 = b.iter_u64_digits().map(u64::count_ones).map(u64::from).sum();
Ok(Value::Int(i64::try_from(ones).unwrap_or(i64::MAX)))
}
"conjugate" | "real" | "numerator" | "__index__" | "__int__" | "__trunc__"
| "__floor__" | "__ceil__" | "__pos__" => big(b.clone()),
"imag" => Ok(Value::Int(0)),
"denominator" => Ok(Value::Int(1)),
"is_integer" => Ok(Value::Bool(true)),
"as_integer_ratio" => {
Ok(Value::Tuple(vec![crate::value::int_from_bigint(b.clone()), Value::Int(1)]))
}
"__neg__" => big(-b.clone()),
"__abs__" => big(if b.sign() == num_bigint::Sign::Minus { -b.clone() } else { b.clone() }),
"__invert__" => big(-b.clone() - 1),
"__float__" => Ok(Value::Float(b.to_f64().unwrap_or(f64::INFINITY))),
"__bool__" => Ok(Value::Bool(!num_traits::Zero::is_zero(b))),
_ => Err(InterpreterError::AttributeError(format!(
"'int' object has no attribute '{method}'"
))
.into()),
}
}