| 488 | |
| 489 | #[pyfunction] |
| 490 | fn prod(args: ProdArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> { |
| 491 | use crate::vm::builtins::PyInt; |
| 492 | |
| 493 | let iter = args.iterable; |
| 494 | let start = args.start; |
| 495 | |
| 496 | // Check if start is provided and what type it is (exact types only, not subclasses) |
| 497 | let (mut obj_result, start_is_int, start_is_float) = match &start { |
| 498 | OptionalArg::Present(s) => { |
| 499 | let is_int = s.class().is(vm.ctx.types.int_type); |
| 500 | let is_float = s.class().is(vm.ctx.types.float_type); |
| 501 | (Some(s.clone()), is_int, is_float) |
| 502 | } |
| 503 | OptionalArg::Missing => (None, true, false), // Default is int 1 |
| 504 | }; |
| 505 | |
| 506 | let mut item_iter = iter.iter(vm)?; |
| 507 | |
| 508 | // Integer fast path |
| 509 | if start_is_int && !start_is_float { |
| 510 | let mut int_result: i64 = match &start { |
| 511 | OptionalArg::Present(s) => { |
| 512 | if let Some(i) = s.downcast_ref::<PyInt>() { |
| 513 | match i.as_bigint().try_into() { |
| 514 | Ok(v) => v, |
| 515 | Err(_) => { |
| 516 | // Start overflows i64, fall through to generic path |
| 517 | obj_result = Some(s.clone()); |
| 518 | i64::MAX // Will be ignored |
| 519 | } |
| 520 | } |
| 521 | } else { |
| 522 | 1 |
| 523 | } |
| 524 | } |
| 525 | OptionalArg::Missing => 1, |
| 526 | }; |
| 527 | |
| 528 | if obj_result.is_none() { |
| 529 | loop { |
| 530 | let item = match item_iter.next() { |
| 531 | Some(r) => r?, |
| 532 | None => return Ok(vm.ctx.new_int(int_result).into()), |
| 533 | }; |
| 534 | |
| 535 | // Only use fast path for exact int type (not subclasses) |
| 536 | if item.class().is(vm.ctx.types.int_type) |
| 537 | && let Some(int_item) = item.downcast_ref::<PyInt>() |
| 538 | && let Ok(b) = int_item.as_bigint().try_into() as Result<i64, _> |
| 539 | && let Some(product) = int_result.checked_mul(b) |
| 540 | { |
| 541 | int_result = product; |
| 542 | continue; |
| 543 | } |
| 544 | |
| 545 | // Overflow or non-int: restore to PyObject and continue |
| 546 | obj_result = Some(vm.ctx.new_int(int_result).into()); |
| 547 | let temp = vm._mul(obj_result.as_ref().unwrap(), &item)?; |