| 607 | |
| 608 | #[pyfunction] |
| 609 | fn sumprod( |
| 610 | p: ArgIterable<PyObjectRef>, |
| 611 | q: ArgIterable<PyObjectRef>, |
| 612 | vm: &VirtualMachine, |
| 613 | ) -> PyResult<PyObjectRef> { |
| 614 | use crate::vm::builtins::PyInt; |
| 615 | |
| 616 | let mut p_iter = p.iter(vm)?; |
| 617 | let mut q_iter = q.iter(vm)?; |
| 618 | |
| 619 | // Fast path state |
| 620 | let mut int_path_enabled = true; |
| 621 | let mut int_total: i64 = 0; |
| 622 | let mut int_total_in_use = false; |
| 623 | let mut flt_p_values: Vec<f64> = Vec::new(); |
| 624 | let mut flt_q_values: Vec<f64> = Vec::new(); |
| 625 | |
| 626 | // Fallback accumulator for generic Python path |
| 627 | let mut obj_total: Option<PyObjectRef> = None; |
| 628 | |
| 629 | loop { |
| 630 | let m_p = p_iter.next(); |
| 631 | let m_q = q_iter.next(); |
| 632 | |
| 633 | let (p_i, q_i, finished) = match (m_p, m_q) { |
| 634 | (Some(r_p), Some(r_q)) => (Some(r_p?), Some(r_q?), false), |
| 635 | (None, None) => (None, None, true), |
| 636 | _ => return Err(vm.new_value_error("Inputs are not the same length")), |
| 637 | }; |
| 638 | |
| 639 | // Integer fast path (only for exact int types, not subclasses) |
| 640 | if int_path_enabled { |
| 641 | if !finished { |
| 642 | let (p_i, q_i) = (p_i.as_ref().unwrap(), q_i.as_ref().unwrap()); |
| 643 | if p_i.class().is(vm.ctx.types.int_type) |
| 644 | && q_i.class().is(vm.ctx.types.int_type) |
| 645 | && let (Some(p_int), Some(q_int)) = |
| 646 | (p_i.downcast_ref::<PyInt>(), q_i.downcast_ref::<PyInt>()) |
| 647 | && let (Ok(p_val), Ok(q_val)) = ( |
| 648 | p_int.as_bigint().try_into() as Result<i64, _>, |
| 649 | q_int.as_bigint().try_into() as Result<i64, _>, |
| 650 | ) |
| 651 | && let Some(prod) = p_val.checked_mul(q_val) |
| 652 | && let Some(new_total) = int_total.checked_add(prod) |
| 653 | { |
| 654 | int_total = new_total; |
| 655 | int_total_in_use = true; |
| 656 | continue; |
| 657 | } |
| 658 | } |
| 659 | // Finalize int path |
| 660 | int_path_enabled = false; |
| 661 | if int_total_in_use { |
| 662 | let int_obj: PyObjectRef = vm.ctx.new_int(int_total).into(); |
| 663 | obj_total = Some(match obj_total { |
| 664 | Some(total) => vm._add(&total, &int_obj)?, |
| 665 | None => int_obj, |
| 666 | }); |