Skip to content

Commit d3dafda

Browse files
committed
Implement LCM and GCD from Py3.9; merged from TheAnyKey/p39_math
1 parent 15c88c7 commit d3dafda

File tree

1 file changed

+51
-4
lines changed

1 file changed

+51
-4
lines changed

vm/src/stdlib/math.rs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use statrs::function::gamma::{gamma, ln_gamma};
99
use num_bigint::BigInt;
1010
use num_traits::{One, Zero};
1111

12-
use crate::function::OptionalArg;
12+
use crate::function::{OptionalArg, PyFuncArgs};
1313
use crate::obj::objfloat::{self, IntoPyFloat, PyFloatRef};
14-
use crate::obj::objint::{self, PyIntRef};
14+
use crate::obj::objint::{self, PyInt, PyIntRef};
1515
use crate::obj::objtype;
1616
use crate::pyobject::{Either, PyObjectRef, PyResult, TypeProtocol};
1717
use crate::vm::VirtualMachine;
@@ -272,9 +272,55 @@ fn math_ldexp(
272272
Ok(value * (2_f64).powf(objint::try_float(i.as_bigint(), vm)?))
273273
}
274274

275-
fn math_gcd(a: PyIntRef, b: PyIntRef) -> BigInt {
275+
fn math_perf_arb_len_int_op<F>(
276+
args: PyFuncArgs,
277+
vm: &VirtualMachine,
278+
op: F,
279+
default: BigInt,
280+
) -> PyResult<BigInt>
281+
where
282+
F: Fn(&BigInt, &PyInt) -> BigInt,
283+
{
284+
if !args.kwargs.is_empty() {
285+
Err(vm.new_type_error("Takes no keyword arguments".to_owned()))
286+
} else if args.args.is_empty() {
287+
Ok(default)
288+
} else if args.args.len() == 1 {
289+
let a: PyObjectRef = args.args[0].clone();
290+
if let Some(aa) = a.payload_if_subclass::<PyInt>(vm) {
291+
let res = op(aa.as_bigint(), aa);
292+
Ok(res)
293+
} else {
294+
Err(vm.new_type_error("Only integer arguments are supported".to_owned()))
295+
}
296+
} else {
297+
let a = args.args[0].clone();
298+
if let Some(aa) = a.payload_if_subclass::<PyInt>(vm) {
299+
let mut res = aa.as_bigint().clone();
300+
for b in args.args[1..].iter() {
301+
if let Some(bb) = b.payload_if_subclass::<PyInt>(vm) {
302+
res = op(&res, bb);
303+
} else {
304+
return Err(
305+
vm.new_type_error("Only integer arguments are supported".to_owned())
306+
);
307+
}
308+
}
309+
Ok(res)
310+
} else {
311+
Err(vm.new_type_error("Only integer arguments are supported".to_owned()))
312+
}
313+
}
314+
}
315+
316+
fn math_gcd(args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<BigInt> {
317+
use num_integer::Integer;
318+
math_perf_arb_len_int_op(args, vm, |x, y| x.gcd(y.as_bigint()), BigInt::zero())
319+
}
320+
321+
fn math_lcm(args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<BigInt> {
276322
use num_integer::Integer;
277-
a.as_bigint().gcd(b.as_bigint())
323+
math_perf_arb_len_int_op(args, vm, |x, y| x.lcm(y.as_bigint()), BigInt::one())
278324
}
279325

280326
fn math_factorial(value: PyIntRef, vm: &VirtualMachine) -> PyResult<BigInt> {
@@ -436,6 +482,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
436482

437483
// Gcd function
438484
"gcd" => ctx.new_function(math_gcd),
485+
"lcm" => ctx.new_function(math_lcm),
439486

440487
// Factorial function
441488
"factorial" => ctx.new_function(math_factorial),

0 commit comments

Comments
 (0)