◐ Shell
reader mode source ↗
Skip to content

Upgrade math,cmath to 3.14.2 and totally delegate to pymath#6705

Merged
youknowone merged 2 commits into
RustPython:mainfrom
youknowone:math
Jan 13, 2026
Merged

Upgrade math,cmath to 3.14.2 and totally delegate to pymath#6705
youknowone merged 2 commits into
RustPython:mainfrom
youknowone:math

Conversation

@youknowone

@youknowone youknowone commented Jan 11, 2026

Copy link
Copy Markdown
Member

We are not shipping 3.14 libraries yet, but pymath is 3.14 compatible.

Also totally remove math calculation from RustPython, and delegate everything to pymath, which is smaller and easier to test compatibilities.

Summary by CodeRabbit

  • New Features

    • Added high-precision/combinatorics utilities: isqrt, gcd, lcm, factorial, perm, comb, and fused multiply–add (fma).
  • Bug Fixes

    • Math and complex-number functions now return consistent Python errors for invalid inputs.
    • Clarified error messaging (e.g., "zero to a negative power") and improved mixed-type edge-case handling.
  • Refactor

    • Centralized math/complex logic to a unified pathway for consistent behavior and error mapping.
  • Chores

    • Manifest updated to exclude a workspace member and pin an external math dependency with features.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 11, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Workspace excludes the pymath member; math and cmath were refactored to delegate computations to the pymath crate, adopt VM-aware signatures returning PyResult, and centralize error mapping via pymath_exception.

Changes

Cohort / File(s) Summary
Workspace / Dependency
Cargo.toml
Added exclude = ["pymath"] to [workspace]; updated pymath dependency from "0.0.2" to pymath = { version = "0.1.5", features = ["mul_add", "malachite-bigint", "complex"] }.
Complex math API
crates/stdlib/src/cmath.rs
Replaced local implementations with pymath::cmath usage; constants (E, PI, TAU, INF, NAN, INFJ, NANJ) now sourced from pymath::cmath; many functions now accept vm: &VirtualMachine, return PyResult<T>, and map pymath::Error via pymath_exception.
Standard math API
crates/stdlib/src/math.rs
Routed numerous math operations through pymath; added pymath_exception and float_repr; introduced bigint-aware branches and bigint-returning APIs (isqrt, gcd, lcm, factorial, perm, comb, fma); unified error handling and fast-paths for exact int/float/BigInt.
Float builtin message
crates/vm/src/builtins/float.rs
Minor wording change in float_pow error message for zero-to-negative-power: "zero to a negative power".

Sequence Diagram(s)

sequenceDiagram
  participant PyCaller as Python code
  participant StdLib as stdlib::math / stdlib::cmath
  participant VM as VirtualMachine
  participant PyMath as pymath crate

  PyCaller->>StdLib: call math/cmath function(args)
  StdLib->>VM: validate args / prepare context
  StdLib->>PyMath: delegate computation(args)
  PyMath-->>StdLib: result or pymath::Error
  alt result
    StdLib-->>PyCaller: PyResult<T> (value)
  else error
    StdLib->>VM: pymath_exception(err, vm)
    StdLib-->>PyCaller: PyResult (PyErr)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Remove wrong Deref #6620 — touches stdlib numeric handling and conversions; likely to overlap where math/cmath argument conversions and pymath delegation intersect.

Poem

🐰 I hopped a patch from branch to tree,
Pymath now hums with VM company,
BigInts twirl, floats find a tune,
Errors wrapped snug under the moon,
Numbers snug — a carrot jubilee 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main objective: upgrading math/cmath modules to Python 3.14.2 compatibility and delegating functionality to the pymath crate.
Docstring Coverage ✅ Passed Docstring coverage is 96.39% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jan 11, 2026

Copy link
Copy Markdown
Contributor

Code has been automatically formatted

The code in this PR has been formatted using:

  • cargo fmt --all
    Please pull the latest changes before pushing again:
git pull origin math

@youknowone youknowone force-pushed the math branch 6 times, most recently from c456550 to f133d7e Compare January 12, 2026 11:36
@youknowone youknowone marked this pull request as ready for review January 12, 2026 11:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @crates/stdlib/src/math.rs:
- Around line 851-863: The overflow error messages are inconsistent: both
branches convert to u64 (ki) but the first error says i64::MAX; update both
vm.new_overflow_error calls to reference the correct limit for the conversion
target (u64::MAX) and use consistent formatting (e.g.,
vm.new_overflow_error(format!("n must not exceed {}", u64::MAX)) and
vm.new_overflow_error(format!("k must not exceed {}", u64::MAX))). Ensure you
update the message for the n branch (when k_big is None) and keep the existing
formatted message for the k branch but with u64::MAX.
- Around line 929-937: The overflow error message for the effective_k -> u64
conversion is inconsistent: it mentions i64::MAX while the value is being
converted to u64; update the vm.new_overflow_error call in the effective_k
conversion branch (where ki: u64 is set) to report the correct limit (u64::MAX)
or otherwise match the actual conversion target, ensuring the message text
references u64::MAX and keep the surrounding function names (effective_k, ki,
vm.new_overflow_error) unchanged.
🧹 Nitpick comments (2)
crates/stdlib/src/math.rs (2)

593-598: Potential precision loss when converting large integers to float.

When a PyInt is encountered in the float fast path, it's converted to i64 first, then cast to f64. For integers that fit in i64 but exceed f64's 53-bit mantissa precision, this may lose precision. Consider using try_bigint_to_f64 for consistency with other conversions in this file.

♻️ Suggested improvement
                     if let Some(i) = item.downcast_ref::<PyInt>()
-                        && let Ok(v) = i.as_bigint().try_into() as Result<i64, _>
+                        && let Ok(v) = try_bigint_to_f64(i.as_bigint(), vm)
                     {
-                        flt_result *= v as f64;
+                        flt_result *= v;
                         continue;
                     }

632-632: Unused mutable variable flt_path_enabled.

The variable flt_path_enabled is declared as mutable but is never modified (always remains true). This makes the if flt_path_enabled check at line 686 always true. Either remove the variable and the condition, or implement the logic to disable the float path when appropriate.

♻️ Suggested fix
         // Fast path state
         let mut int_path_enabled = true;
-        let flt_path_enabled = true;
         let mut int_total: i64 = 0;

And at line 686:

-            // Float fast path - only when at least one value is exact float type
-            // (not subclasses, to preserve custom __mul__/__add__ behavior)
-            if flt_path_enabled {
+            // Float fast path - only when at least one value is exact float type
+            // (not subclasses, to preserve custom __mul__/__add__ behavior)
+            {
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69601a1 and f133d7e.

⛔ Files ignored due to path filters (5)
  • Cargo.lock is excluded by !**/*.lock
  • Lib/test/mathdata/cmath_testcases.txt is excluded by !Lib/**
  • Lib/test/mathdata/ieee754.txt is excluded by !Lib/**
  • Lib/test/test_cmath.py is excluded by !Lib/**
  • Lib/test/test_math.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/stdlib/src/cmath.rs
  • crates/stdlib/src/math.rs
  • crates/vm/src/builtins/float.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/vm/src/builtins/float.rs
  • crates/stdlib/src/math.rs
  • crates/stdlib/src/cmath.rs
🧠 Learnings (1)
📚 Learning: 2025-12-27T14:03:49.034Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.034Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • crates/stdlib/src/math.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Run rust tests (windows-2025)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check Rust code with clippy
🔇 Additional comments (12)
crates/vm/src/builtins/float.rs (1)

117-128: LGTM! Error message aligned with CPython 3.14.2.

The updated message "zero to a negative power" matches CPython's exact ZeroDivisionError message for this case, which is consistent with the PR's objective to upgrade compatibility to Python 3.14.2.

crates/stdlib/src/cmath.rs (3)

14-27: LGTM!

Constants are correctly sourced from pymath::cmath, maintaining consistency with the delegation approach.


29-146: LGTM!

All complex math functions consistently delegate to pymath::cmath with proper error mapping through pymath_exception. The pattern is uniform and maintainable.


160-169: LGTM!

The isclose implementation correctly delegates to pymath::cmath::isclose with appropriate tolerance handling. The custom error message for negative tolerances is more user-friendly than a generic domain error.

crates/stdlib/src/math.rs (7)

29-69: LGTM!

Basic math functions (fabs, isfinite, isinf, isnan, isclose, copysign) correctly delegate to pymath::math with consistent error handling.


94-123: LGTM!

The log function correctly handles BigInt inputs specially to avoid precision loss, validates the base before conversion, and provides descriptive error messages including the input value.


366-400: LGTM!

The ceil and floor implementations correctly check for __ceil__/__floor__ attribute existence before attempting to call the magic method, with appropriate fallback to float conversion.


447-463: LGTM!

The nextafter function correctly validates that steps is non-negative before casting to u64, and properly handles the optional steps parameter.


767-778: LGTM!

The fma function correctly delegates to pymath::math::fma with appropriate custom error messages for domain and range errors.


811-823: LGTM!

The factorial function correctly validates input order (negative check before overflow), provides a clear overflow message, and defensively handles errors from the underlying implementation.


944-970: LGTM!

The pymath_exception helper provides consistent error mapping, and float_repr correctly formats floats in Python style with proper handling of special values (nan, inf) and ensures integer-valued floats display with a trailing .0.

Cargo.toml (1)

187-187: The pymath dependency specification is correct. The git revision 564ebb2780a05a9460ad12a2752a67244dc5f89e is valid and accessible, and all three specified features (mul_add, malachite-bigint, complex) are properly defined in pymath's Cargo.toml at that revision. No issues found.

@ShaharNaveh ShaharNaveh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Overall it looks great!

@ShaharNaveh

Copy link
Copy Markdown
Contributor

Is there a reason why pymath is in a separate repo? shouldn't it be part of this repo (but versioned separately from the rest of the project)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Actionable comments posted: 0

🧹 Nitpick comments (3)
crates/stdlib/src/math.rs (3)

630-636: Dead variable: flt_path_enabled is never modified.

The variable flt_path_enabled is initialized to true but never set to false, unlike int_path_enabled. Since the float path check runs conditionally anyway and the generic path returns early, this doesn't cause incorrect behavior, but the variable is effectively unused.

Consider removing or using the variable
-        let flt_path_enabled = true;

Either remove the variable entirely and just use the condition directly, or properly disable it when transitioning out of the float path for consistency with int_path_enabled.


789-809: Consider reducing allocations in gcd and lcm.

Both functions create an intermediate Vec<PyRef<PyInt>> before creating Vec<&BigInt>. This is fine for correctness but involves extra allocations.

Single-pass alternative
 #[pyfunction]
 fn gcd(args: PosArgs<ArgIndex>) -> BigInt {
-    let ints: Vec<_> = args
-        .into_vec()
-        .into_iter()
-        .map(|x| x.into_int_ref())
-        .collect();
-    let refs: Vec<_> = ints.iter().map(|x| x.as_bigint()).collect();
-    pymath::math::integer::gcd(&refs)
+    let ints: Vec<_> = args.into_vec();
+    let refs: Vec<_> = ints.iter().map(|x| x.as_bigint()).collect();
+    pymath::math::integer::gcd(&refs)
 }

This assumes ArgIndex provides direct access to BigInt. If not, the current approach is necessary to keep references valid.


811-823: Unreachable error path in factorial.

Line 822's error case is unreachable since line 814-816 already handles negative values. The pymath factorial call at line 820 should only receive non-negative values.

Simplify error handling
     pymath::math::integer::factorial(n)
         .map(|r| r.into())
-        .map_err(|_| vm.new_value_error("factorial() not defined for negative values"))
+        .expect("pymath factorial should not fail for non-negative i64")

Or keep the defensive error handling if pymath might have other failure modes not documented.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f133d7e and 75cc73b.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • Cargo.toml
  • crates/stdlib/src/math.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/stdlib/src/math.rs
🧠 Learnings (1)
📚 Learning: 2025-12-27T14:03:49.034Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.034Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • crates/stdlib/src/math.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Check Rust code with clippy
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run tests under miri
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Ensure compilation on various targets
🔇 Additional comments (11)
crates/stdlib/src/math.rs (11)

1-16: LGTM!

The imports are properly organized, bringing in the necessary types for pymath delegation (pymath_exception, float_repr) and the traits needed for BigInt operations (Signed, ToPrimitive).


28-75: LGTM!

The basic math functions correctly delegate to pymath with appropriate error mapping. The isclose function's simplified error handling is acceptable since tolerance validation is the only expected error case.


94-123: LGTM!

The log function properly validates the base before calling pymath, handles BigInt values specially for precision with large integers, and provides appropriate error messages matching Python's behavior.


232-235: LGTM!

The hypot function correctly delegates to pymath. Since hypot mathematically cannot fail (it computes sqrt of sum of squares, always non-negative), returning f64 directly without error handling is appropriate.


367-400: LGTM!

The ceil and floor functions correctly prioritize the magic method (__ceil__/__floor__) when the attribute exists, falling back to float conversion only when the method is not defined. This properly propagates errors for non-callable attributes.


509-550: Integer fast path in prod is well-structured.

The overflow handling is correct: when the start value or intermediate result overflows i64, the code properly transitions to the generic PyObject path. The use of checked_mul prevents silent overflow.


767-778: LGTM!

The fma function properly delegates to pymath and maps domain errors to ValueError and range errors to OverflowError, matching Python's expected behavior.


880-941: LGTM!

The comb function correctly implements the binomial coefficient with:

  • Proper validation of non-negative inputs
  • Symmetry optimization using min(k, n-k) to reduce computation
  • Fast path for values fitting in i64
  • BigInt fallback for large values

944-970: LGTM!

The helper functions are well-implemented:

  • pymath_exception provides clean error mapping between pymath and Python exceptions
  • float_repr correctly handles special values (NaN, ±Inf) and ensures Python-style formatting with trailing .0 for whole numbers

447-463: LGTM!

The nextafter function correctly validates that steps is non-negative before casting to u64, and properly handles the optional steps parameter.


579-582: No action needed. The project's MSRV is 1.91.0, which fully supports Option::is_none_or (stabilized in Rust 1.82). The method usage is stable and appropriate for this codebase.

Likely an incorrect or invalid review comment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @crates/stdlib/src/math.rs:
- Around line 929-937: The error message in the u64 conversion block for
effective_k (where ki: u64 is assigned) incorrectly references i64::MAX; update
the new_overflow_error message in this branch to reflect the correct limit
(e.g., u64::MAX or an appropriate u64-bound) so it aligns with the attempted
to_u64() conversion; locate the match on effective_k.to_u64() that sets ki and
change the formatted string used in vm.new_overflow_error to mention the u64
limit (mirroring the fix made in the perm function).
- Around line 857-863: The error message is inconsistent: code converts k_val
via to_u64() but the overflow message mentions i64::MAX; update the
vm.new_overflow_error call in this match branch (the block handling
k_val.to_u64()) to reference u64::MAX (or otherwise the correct unsigned limit)
so the message matches the actual conversion/limit used by k_val and to_u64().
🧹 Nitpick comments (1)
crates/stdlib/src/math.rs (1)

561-577: The 0.0 fallback at line 565 is unreachable but confusing.

The condition at line 562 (if let Some(f) = obj.downcast_ref::<PyFloat>()) means the else branch at line 564-566 returning 0.0 is unreachable given the outer condition check. Consider restructuring to make this clearer or use unreachable!() to document the invariant.

♻️ Suggested clarification
-            let mut flt_result: f64 = if let Some(ref obj) = obj_result {
-                if let Some(f) = obj.downcast_ref::<PyFloat>() {
-                    f.to_f64()
-                } else {
-                    0.0 // Won't be used
-                }
+            let mut flt_result: f64 = if let Some(ref obj) = obj_result {
+                // obj_result is guaranteed to be Some(PyFloat) here due to outer condition
+                obj.downcast_ref::<PyFloat>().map(|f| f.to_f64()).unwrap_or_else(|| unreachable!())
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75cc73b and 6691f7a.

⛔ Files ignored due to path filters (5)
  • Cargo.lock is excluded by !**/*.lock
  • Lib/test/mathdata/cmath_testcases.txt is excluded by !Lib/**
  • Lib/test/mathdata/ieee754.txt is excluded by !Lib/**
  • Lib/test/test_cmath.py is excluded by !Lib/**
  • Lib/test/test_math.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/stdlib/src/cmath.rs
  • crates/stdlib/src/math.rs
  • crates/vm/src/builtins/float.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/vm/src/builtins/float.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/stdlib/src/math.rs
  • crates/stdlib/src/cmath.rs
🧠 Learnings (1)
📚 Learning: 2025-12-27T14:03:49.034Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.034Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • crates/stdlib/src/math.rs
🧬 Code graph analysis (1)
crates/stdlib/src/cmath.rs (1)
crates/stdlib/src/math.rs (8)
  • pymath_exception (944-949)
  • x (109-109)
  • x (133-133)
  • x (152-152)
  • exp (79-81)
  • log (94-123)
  • log10 (150-166)
  • isclose (61-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (windows-2025)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run tests under miri
  • GitHub Check: Check Rust code with clippy
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Check the WASM package and demo
🔇 Additional comments (13)
Cargo.toml (2)

119-119: LGTM - Workspace exclusion for local pymath directory.

This exclusion prevents Cargo from treating a local pymath directory as a workspace member, which is correct when using the crates.io dependency.


188-188: Dependency configuration is correct.

Verified that pymath 0.1.4 exists on crates.io with all three required features: mul_add, malachite-bigint, and complex. Version 0.1.4 is the latest available release.

crates/stdlib/src/math.rs (8)

1-16: LGTM - Clean import structure.

The imports are well-organized, separating module-level exports (pymath_exception, float_repr) from the inner math module's imports. The addition of AsObject and ToPrimitive aligns with the BigInt-aware implementations.


28-75: LGTM - Clean delegation pattern for basic math functions.

The delegation to pymath::math functions is consistent and the error handling via pymath_exception is correctly applied where needed. The isclose function appropriately provides a specific error message for negative tolerances.


93-166: LGTM - BigInt-aware logarithm implementations.

The log functions correctly handle:

  1. Base validation (positive, not 1.0) before processing the value
  2. BigInt special path to avoid overflow for large integers
  3. Descriptive error messages including the actual value via float_repr

This aligns with CPython's behavior for handling large integers in logarithmic functions.


232-245: LGTM - Variadic hypot and dist implementations.

The hypot function correctly delegates to pymath without error handling (hypot handles infinities gracefully). The dist function properly validates that both vectors have the same length before computing the Euclidean distance.


366-400: LGTM - Correct __ceil__/__floor__ dispatch logic.

The implementation correctly:

  1. Checks if the class defines the magic method
  2. Calls it if present (propagating errors for non-callable attributes)
  3. Falls back to float conversion only when the method is not defined

This matches the expected Python 3.14 behavior.


619-765: LGTM - Well-structured sumprod with appropriate fast paths.

The implementation correctly:

  1. Uses exact type checks (.class().is()) to preserve subclass behavior
  2. Falls back to the generic Python path on overflow or non-numeric types
  3. Leverages pymath::math::sumprod for numerically accurate float summation
  4. Handles length mismatches with proper error messages

The complexity is justified for performance with numeric sequences.


767-778: LGTM - fma with specific error messages.

The fused multiply-add correctly maps domain and range errors to appropriate Python exceptions with operation-specific messages, rather than using the generic pymath_exception.


944-970: LGTM - Well-designed helper functions.

pymath_exception provides centralized error mapping matching Python's math module conventions. float_repr correctly handles special values (nan, inf) and ensures integer-valued floats display with a trailing .0 for clarity in error messages.

crates/stdlib/src/cmath.rs (3)

11-27: LGTM - Constants sourced from pymath for consistency.

Using pymath::cmath constants ensures the cmath module's mathematical constants are consistent with the underlying implementation. The import of pymath_exception from the sibling math module correctly shares the centralized error mapping.


29-146: LGTM - Consistent delegation pattern for complex math functions.

All functions correctly:

  1. Add the vm parameter for error propagation
  2. Return PyResult<T> for proper exception handling
  3. Delegate to pymath::cmath equivalents
  4. Map errors through the centralized pymath_exception

The predicate functions (isinf, isfinite, isnan) appropriately remain non-fallible.


160-169: LGTM - Complex isclose with tolerance validation.

The implementation mirrors the real math.isclose pattern, correctly handling optional tolerances and providing a clear error message for negative tolerance values.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/stdlib/src/math.rs (1)

490-617: prod(): fast paths should only apply to exact int/float types (subclass operator semantics).

Right now start_is_int/start_is_float and per-item checks are based on downcast_ref::<PyInt/PyFloat>(). If that matches int/float subclasses in RustPython, the fast path can bypass overridden __mul__ implementations (Python requires honoring dynamic dispatch for subclasses). sumprod() takes care to check class().is(int_type/float_type)prod() should do the same.

Proposed fix (tighten to exact types for fast paths)
@@
-        let (mut obj_result, start_is_int, start_is_float) = match &start {
+        let (mut obj_result, start_is_int, start_is_float) = match &start {
             OptionalArg::Present(s) => {
-                let is_int = s.downcast_ref::<PyInt>().is_some();
-                let is_float = s.downcast_ref::<PyFloat>().is_some();
+                let is_int = s.class().is(vm.ctx.types.int_type);
+                let is_float = s.class().is(vm.ctx.types.float_type);
                 (Some(s.clone()), is_int, is_float)
             }
             OptionalArg::Missing => (None, true, false), // Default is int 1
         };
@@
-                    if let Some(int_item) = item.downcast_ref::<PyInt>()
+                    if item.class().is(vm.ctx.types.int_type)
+                        && let Some(int_item) = item.downcast_ref::<PyInt>()
                         && let Ok(b) = int_item.as_bigint().try_into() as Result<i64, _>
                         && let Some(product) = int_result.checked_mul(b)
@@
-                    if let Some(f) = item.downcast_ref::<PyFloat>() {
+                    if item.class().is(vm.ctx.types.float_type)
+                        && let Some(f) = item.downcast_ref::<PyFloat>() {
                         flt_result *= f.to_f64();
                         continue;
                     }
🤖 Fix all issues with AI agents
In @crates/stdlib/src/math.rs:
- Around line 782-941: The perm and comb implementations enforce Rust i64/u64
limits by using .to_i64()/.to_u64() and return OverflowError messages
referencing i64::MAX; remove those artificial limits so arbitrary-precision
BigInt inputs follow Python behavior: in perm (function perm) stop returning
vm.new_overflow_error when n_big.to_u64()/k_val.to_u64() or n_big.to_i64() fails
and instead route to the BigInt path (call pymath::math::perm_bigint) for large
values; likewise in comb (function comb) do not error/mention i64::MAX when
effective_k.to_u64() or n_big.to_i64() conversion fails — either fall back to
pymath::math::comb_bigint or return Ok(0) / ValueError only for negative inputs;
also remove/replace error messages that include "i64::MAX" with generic overflow
or avoid them entirely by using bigint paths.
- Around line 944-970: The custom float_repr function should be replaced to use
RustPython's canonical float formatter: remove the manual formatting logic in
float_repr and call crate::literal::float::to_string(value) (or the
crate::literal::float module's public conversion function) to produce
Python-compatible float representations; keep the function signature
float_repr(value: f64) -> String and return the result of the to_string helper
so error messages and reprs use the standard Python formatting.
🧹 Nitpick comments (2)
crates/stdlib/src/math.rs (2)

3-17: Import + helper wiring looks coherent; keep pymath_exception/float_repr usage consistent.

Minor readability nit: some call sites use super::float_repr(...) while others use float_repr(...) directly (same module import). Consider standardizing on one style. Based on learnings, macro usage is already correct.


619-765: sumprod(): avoid O(n) buffering + avoid collecting “remaining” into Vec for the generic path.

Two concerns:

  1. The float fast path buffers flt_p_values/flt_q_values for the whole run; that can be a lot of memory for large iterables.
  2. The fallback path collects all remaining elements into Vecs; you already have synchronized iteration logic, so you can compute generically in a streaming loop without materializing.
Suggested direction (streaming generic fallback)
@@
-            // Collect current + remaining elements
-            let p_remaining: Result<Vec<PyObjectRef>, _> =
-                std::iter::once(Ok(p_i)).chain(p_iter).collect();
-            let q_remaining: Result<Vec<PyObjectRef>, _> =
-                std::iter::once(Ok(q_i)).chain(q_iter).collect();
-            let (p_vec, q_vec) = (p_remaining?, q_remaining?);
-
-            if p_vec.len() != q_vec.len() {
-                return Err(vm.new_value_error("Inputs are not the same length"));
-            }
-
-            let mut total = obj_total.unwrap_or_else(|| vm.ctx.new_int(0).into());
-            for (p_item, q_item) in p_vec.into_iter().zip(q_vec) {
-                let prod = vm._mul(&p_item, &q_item)?;
-                total = vm._add(&total, &prod)?;
-            }
-            return Ok(total);
+            let mut total = obj_total.unwrap_or_else(|| vm.ctx.new_int(0).into());
+            // current pair
+            total = vm._add(&total, &vm._mul(&p_i, &q_i)?)?;
+            // remaining pairs (stream)
+            loop {
+                match (p_iter.next(), q_iter.next()) {
+                    (Some(rp), Some(rq)) => {
+                        let prod = vm._mul(&rp?, &rq?)?;
+                        total = vm._add(&total, &prod)?;
+                    }
+                    (None, None) => return Ok(total),
+                    _ => return Err(vm.new_value_error("Inputs are not the same length")),
+                }
+            }
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6691f7a and 0c8c77c.

⛔ Files ignored due to path filters (3)
  • Cargo.lock is excluded by !**/*.lock
  • Lib/test/test_cmath.py is excluded by !Lib/**
  • Lib/test/test_math.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/stdlib/src/cmath.rs
  • crates/stdlib/src/math.rs
  • crates/vm/src/builtins/float.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/vm/src/builtins/float.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/stdlib/src/math.rs
  • crates/stdlib/src/cmath.rs
🧠 Learnings (1)
📚 Learning: 2025-12-27T14:03:49.034Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.034Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • crates/stdlib/src/math.rs
🧬 Code graph analysis (1)
crates/stdlib/src/cmath.rs (1)
crates/stdlib/src/math.rs (5)
  • pymath_exception (944-949)
  • x (109-109)
  • x (133-133)
  • x (152-152)
  • isclose (61-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run rust tests (windows-2025)
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Check the WASM package and demo
  • GitHub Check: Run tests under miri
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Check Rust code with clippy
🔇 Additional comments (10)
crates/stdlib/src/math.rs (5)

366-400: ceil/floor: good “only call if defined” behavior; verify has_attr matches CPython’s lookup rules.

The change to only call __ceil__/__floor__ when the class defines it (and otherwise fall back to float conversion) seems right for propagating non-callable attribute errors. Please confirm has_attr here mirrors Python’s attribute resolution expectations (class dict vs __getattr__ etc.) for edge cases.


448-463: nextafter(steps=...): conversion to i64 then u64 is fine; confirm overflow behavior.

If steps doesn’t fit in i64, try_to_primitive likely raises (OverflowError?). Please ensure the exception type/message matches CPython for “too large steps”.


767-778: fma(): error mapping seems correct; confirm EDOM/ERANGE conditions match CPython.

Please verify when CPython raises ValueError vs OverflowError for math.fma edge cases (NaNs, infinities, overflow).


29-75: Confirm pymath error scope for tolerances.

The error message "tolerances must be non-negative" matches CPython's behavior for negative rel_tol or abs_tol. However, verify that pymath::math::isclose() only errors on negative tolerances—if pymath can error for other reasons, the current mapping may mask those issues.


408-418: The ldexp implementation correctly maps all pymath errors via pymath_exception. The error mapping for overflow (ERANGEOverflowError) is correct and matches CPython's behavior, which is validated by the comprehensive test suite in Lib/test/test_math.py. The tests confirm that huge positive exponents raise OverflowError and huge negative exponents return 0.0, with special values (infinity, NaN) handled correctly.

crates/stdlib/src/cmath.rs (3)

11-28: Constants delegated to pymath::cmath: verify exact values/const-ness across platforms.

Looks clean. Please confirm pymath::cmath::{E,PI,TAU,INF,NAN,INFJ,NANJ} exactly match CPython’s published constants (including NaN payload expectations if tests care).


29-146: Function delegation + centralized error mapping is consistent.

Signatures now vm-aware and PyResult-based, and you consistently map errors via pymath_exception. That aligns well with the math.rs pattern.


160-169: cmath.isclose: ensure error mapping doesn’t hide other pymath failures.

Same note as math.isclose: mapping all errors to “tolerances must be non-negative” assumes that’s the only failure mode. Please confirm pymath doesn’t error for other reasons here.

Cargo.toml (2)

188-188: Remove invalid feature flags from pymath dependency.

The pymath crate version 0.1.5 does not support any Cargo feature flags. The features mul_add, malachite-bigint, and complex do not exist in this crate version and will cause build failures or be silently ignored. Update the dependency to:

pymath = "0.1.5"

Likely an incorrect or invalid review comment.


113-120: Consider whether [workspace].exclude = ["pymath"] is necessary / sufficient.

The concern is valid from a Cargo workspace perspective: the exclude directive only prevents Cargo from treating a ./pymath/ path as a workspace member. If pymath exists or might be vendored elsewhere (e.g., vendor/pymath), this won't prevent accidental inclusion. However, web searches indicate pymath is a published external crate on crates.io, which suggests the exclude may be a defensive measure or addressing a specific historical layout decision. Without access to the PR description or actual repository structure, the rationale cannot be fully verified. Confirm the intent in the PR discussion or changelog.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/stdlib/src/math.rs (1)

855-868: Verify edge cases for ki <= 1 in perm fast path.

The condition ki > 1 on line 858 means cases where k is 0 or 1 skip the fast path and fall through to perm_bigint. While this should work correctly, it's less efficient for these common cases. Consider adding explicit handling:

if ki == 0 {
    return Ok(BigInt::from(1u8));
}
if ki == 1 {
    return Ok(n_big.clone());
}
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c8c77c and ace210d.

⛔ Files ignored due to path filters (5)
  • Cargo.lock is excluded by !**/*.lock
  • Lib/test/mathdata/cmath_testcases.txt is excluded by !Lib/**
  • Lib/test/mathdata/ieee754.txt is excluded by !Lib/**
  • Lib/test/test_cmath.py is excluded by !Lib/**
  • Lib/test/test_math.py is excluded by !Lib/**
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/stdlib/src/cmath.rs
  • crates/stdlib/src/math.rs
  • crates/vm/src/builtins/float.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.rs: Follow the default rustfmt code style by running cargo fmt to format Rust code
Always run clippy to lint code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass, pymodule, pyfunction, etc.) when implementing Python functionality in Rust

Files:

  • crates/vm/src/builtins/float.rs
  • crates/stdlib/src/math.rs
  • crates/stdlib/src/cmath.rs
🧠 Learnings (1)
📚 Learning: 2025-12-27T14:03:49.034Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-27T14:03:49.034Z
Learning: Applies to **/*.rs : Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust

Applied to files:

  • crates/stdlib/src/math.rs
🧬 Code graph analysis (1)
crates/stdlib/src/cmath.rs (1)
crates/stdlib/src/math.rs (5)
  • pymath_exception (934-939)
  • x (109-109)
  • x (133-133)
  • x (152-152)
  • isclose (61-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: Run rust tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (windows-2025)
  • GitHub Check: Run rust tests (windows-2025)
  • GitHub Check: Check Rust code with clippy
  • GitHub Check: Run snippets and cpython tests (macos-latest)
  • GitHub Check: Run snippets and cpython tests (ubuntu-latest)
  • GitHub Check: Run snippets and cpython tests on wasm-wasi
  • GitHub Check: Run rust tests (ubuntu-latest)
  • GitHub Check: Ensure compilation on various targets
  • GitHub Check: Check the WASM package and demo
🔇 Additional comments (15)
crates/vm/src/builtins/float.rs (1)

117-128: LGTM!

The error message update aligns with CPython 3.14.2 messaging conventions. The function logic remains correct—zero raised to a negative power correctly returns a ZeroDivisionError.

crates/stdlib/src/math.rs (10)

1-16: LGTM!

Clean import structure with proper separation between module-level and inner module imports. The pymath_exception helper import enables consistent error mapping.


28-75: LGTM!

The delegation to pymath for basic functions is clean and consistent. The isclose tolerance validation properly rejects negative values with an appropriate error message.


93-166: LGTM!

Excellent handling of BigInt inputs for logarithm functions—this preserves precision for large integers. The explicit base validation (rejecting <= 0 and == 1.0) prevents domain errors early with descriptive messages.


366-400: LGTM!

The ceil and floor implementations correctly prioritize the __ceil__/__floor__ magic methods when defined, falling back to float conversion otherwise. The comment about propagating errors for non-callable attributes is accurate.


509-552: Integer fast path overflow handling looks correct.

When checked_mul fails or encounters a non-int, the code correctly converts the current int_result to a PyObject and continues with generic multiplication. The type checks for exact int_type (excluding subclasses) properly preserve custom __mul__ behavior.


730-749: Iterator consumption during fallback is handled correctly.

When falling back to the generic path, the code properly collects both the current item and all remaining items from both iterators into vectors, then validates lengths match. This ensures no items are lost during the path transition.


799-811: LGTM!

The factorial implementation correctly validates negative inputs before attempting the i64 conversion. The overflow error message helpfully specifies the maximum allowed value.


870-931: LGTM!

The comb implementation is well-structured with proper symmetry optimization (min(k, n-k)) to reduce computation. Edge cases for ki == 0 and ki == 1 are handled explicitly in the fast path, which is efficient.


941-960: LGTM!

The float_repr helper correctly handles special float values (nan, inf) and ensures integer-like floats display with a trailing .0 for Python consistency. The check for scientific notation (e/E) prevents incorrect formatting of numbers like 1e10.


755-766: LGTM!

The fma function correctly provides context-specific error messages rather than generic "math domain error" messages, improving debuggability.

crates/stdlib/src/cmath.rs (4)

13-27: LGTM!

Referencing constants from pymath::cmath ensures consistency and simplifies maintenance when pymath is updated.


29-146: LGTM!

Clean and consistent delegation to pymath::cmath with proper error mapping. Reusing pymath_exception from the math module eliminates code duplication.


90-101: LGTM!

The log function correctly accepts an optional complex base, delegating to pymath::cmath::log. This matches CPython's cmath.log(x[, base]) signature.


160-169: LGTM!

The isclose implementation is consistent with math.isclose, properly adapted for complex numbers while maintaining the same tolerance validation.

@youknowone

Copy link
Copy Markdown
Member Author

@ShaharNaveh Not necessarily separated repository. It is quite standalone with zero dependency to any other RustPython details, so I'd like to promote it as a separated product. In the similar way, we incubated malachite-bigint out of RustPython main repository. (The project is not belong to RustPython project anymore because we found donating it to malachite project could be better.)

Hide details View details @youknowone youknowone merged commit f9e2f9d into RustPython:main Jan 13, 2026
11 of 13 checks passed
@youknowone youknowone deleted the math branch January 13, 2026 13:05
terryluan12 pushed a commit to terryluan12/RustPython that referenced this pull request Jan 15, 2026
…on#6705)

* Upgrade test_math, test_cmath from 3.14.2

* pymath 0.1.5
@coderabbitai coderabbitai Bot mentioned this pull request Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants