Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 82 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ jobs:
with:
python-version: "3.14"
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
uses: dtolnay/rust-toolchain@stable
- name: Build
run: cargo build --verbose
- name: Run tests with num-bigint
Expand All @@ -45,3 +41,84 @@ jobs:
run: cargo test --verbose --features ${{ matrix.features }},num-bigint --release
- name: Run tests with malachite-bigint
run: cargo test --verbose --features ${{ matrix.features }},malachite-bigint

# macOS-specific cross-compilation checks
- name: Setup Intel macOS target
if: runner.os == 'macOS'
run: rustup target add x86_64-apple-darwin
- name: Check Intel macOS
if: runner.os == 'macOS'
run: cargo check --target x86_64-apple-darwin --features ${{ matrix.features }},malachite-bigint

- name: Setup iOS target
if: runner.os == 'macOS'
run: rustup target add aarch64-apple-ios
- name: Check iOS
if: runner.os == 'macOS'
run: cargo check --target aarch64-apple-ios --features ${{ matrix.features }},malachite-bigint

exotic_targets:
name: Check exotic targets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable

# 32-bit Linux
- name: Setup i686-unknown-linux-gnu
run: rustup target add i686-unknown-linux-gnu
- name: Install gcc-multilib
run: sudo apt-get update && sudo apt-get install -y gcc-multilib
- name: Check i686-unknown-linux-gnu
run: cargo check --target i686-unknown-linux-gnu --features complex,malachite-bigint

# Android
- name: Setup aarch64-linux-android
run: rustup target add aarch64-linux-android
- name: Check aarch64-linux-android
run: cargo check --target aarch64-linux-android --features complex,malachite-bigint

# ARM64 Linux
- name: Setup aarch64-unknown-linux-gnu
run: rustup target add aarch64-unknown-linux-gnu
- name: Install gcc-aarch64-linux-gnu
run: sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Check aarch64-unknown-linux-gnu
run: cargo check --target aarch64-unknown-linux-gnu --features complex,malachite-bigint

# musl
- name: Setup i686-unknown-linux-musl
run: rustup target add i686-unknown-linux-musl
- name: Install musl-tools
run: sudo apt-get install -y musl-tools
- name: Check i686-unknown-linux-musl
run: cargo check --target i686-unknown-linux-musl --features complex,malachite-bigint

# FreeBSD
- name: Setup x86_64-unknown-freebsd
run: rustup target add x86_64-unknown-freebsd
- name: Check x86_64-unknown-freebsd
run: cargo check --target x86_64-unknown-freebsd --features complex,malachite-bigint

# WASM
- name: Setup wasm32-unknown-unknown
run: rustup target add wasm32-unknown-unknown
- name: Check wasm32-unknown-unknown
run: cargo check --target wasm32-unknown-unknown --features complex,malachite-bigint

# WASI
- name: Setup wasm32-wasip1
run: rustup target add wasm32-wasip1
- name: Check wasm32-wasip1
run: cargo check --target wasm32-wasip1 --features complex,malachite-bigint

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Run clippy
run: cargo clippy --features complex,malachite-bigint -- -Dwarnings
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
[package]
name = "pymath"
author = "Jeong, YunWon <[email protected]>"
authors = ["Jeong, YunWon <[email protected]>"]
repository = "https://github.com/RustPython/pymath"
description = "A binary representation compatible Rust implementation of Python's math library."
version = "0.1.2"
version = "0.1.3"
edition = "2024"
license = "PSF-2.0"
Comment on lines 1 to 8
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Patch version bump likely mismatches breaking public API changes elsewhere in the PR

0.1.2 -> 0.1.3 is a patch bump, but this PR changes public function signatures (e.g., src/math/integer.rs return types). If this crate is consumed externally, consider bumping the “minor” (0.2.0) instead to avoid surprising downstream breakage.

Proposed change
 [package]
 name = "pymath"
 authors = ["Jeong, YunWon <[email protected]>"]
 repository = "https://github.com/RustPython/pymath"
 description = "A binary representation compatible Rust implementation of Python's math library."
-version = "0.1.3"
+version = "0.2.0"
 edition = "2024"
 license = "PSF-2.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[package]
name = "pymath"
author = "Jeong, YunWon <[email protected]>"
authors = ["Jeong, YunWon <[email protected]>"]
repository = "https://github.com/RustPython/pymath"
description = "A binary representation compatible Rust implementation of Python's math library."
version = "0.1.2"
version = "0.1.3"
edition = "2024"
license = "PSF-2.0"
[package]
name = "pymath"
authors = ["Jeong, YunWon <[email protected]>"]
repository = "https://github.com/RustPython/pymath"
description = "A binary representation compatible Rust implementation of Python's math library."
version = "0.2.0"
edition = "2024"
license = "PSF-2.0"
🤖 Prompt for AI Agents
In @Cargo.toml around lines 1 - 8, The Cargo.toml version was increased only as
a patch (0.1.2 -> 0.1.3) but this PR introduces public API changes (e.g.,
modified function signatures in src/math/integer.rs), so update the package
version to a minor bump (0.2.0) instead of a patch to reflect the breaking
change; edit the version field in Cargo.toml from "0.1.3" to "0.2.0" and ensure
any related changelog or documentation is updated to indicate the API change.



[features]
default = ["complex"]
complex = ["dep:num-complex"]
Expand Down
4 changes: 2 additions & 2 deletions src/cmath/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ pub fn phase(z: Complex64) -> Result<f64> {
let phi = m::atan2(z.im, z.re);
match crate::err::get_errno() {
0 => Ok(phi),
libc::EDOM => Err(Error::EDOM),
libc::ERANGE => Err(Error::ERANGE),
e if e == Error::EDOM as i32 => Err(Error::EDOM),
e if e == Error::ERANGE as i32 => Err(Error::ERANGE),
_ => Err(Error::EDOM), // Unknown errno treated as domain error (like PyErr_SetFromErrno)
}
}
Expand Down
100 changes: 55 additions & 45 deletions src/err.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::ffi::c_int;

// The values are defined in libc
#[cfg(target_os = "windows")]
unsafe extern "C" {
safe fn _errno() -> *mut i32;
}

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
EDOM = 33,
Expand All @@ -14,8 +18,8 @@ impl TryFrom<c_int> for Error {

fn try_from(value: c_int) -> std::result::Result<Self, Self::Error> {
match value {
33 => Ok(Error::EDOM),
34 => Ok(Error::ERANGE),
x if x == Error::EDOM as c_int => Ok(Error::EDOM),
x if x == Error::ERANGE as c_int => Ok(Error::ERANGE),
_ => Err(value),
}
}
Expand All @@ -24,54 +28,60 @@ impl TryFrom<c_int> for Error {
/// Set errno to the given value.
#[inline]
pub(crate) fn set_errno(value: i32) {
#[cfg(target_os = "linux")]
unsafe {
#[cfg(target_os = "linux")]
{
*libc::__errno_location() = value;
}
#[cfg(target_os = "macos")]
{
*libc::__error() = value;
}
#[cfg(target_os = "windows")]
{
unsafe extern "C" {
safe fn _errno() -> *mut i32;
}
*_errno() = value;
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))]
{
// FreeBSD, NetBSD, OpenBSD, etc. use __error()
*libc::__error() = value;
}
*libc::__errno_location() = value;
}
#[cfg(target_os = "android")]
unsafe {
*libc::__errno() = value;
}
#[cfg(target_os = "macos")]
unsafe {
*libc::__error() = value;
}
#[cfg(target_os = "windows")]
unsafe {
*_errno() = value;
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "android", target_os = "macos"))))]
unsafe {
// FreeBSD, NetBSD, OpenBSD, etc. use __error()
*libc::__error() = value;
}
// WASM and other targets: no-op (no errno)
#[cfg(not(any(unix, windows)))]
let _ = value;
}

/// Get the current errno value.
#[inline]
pub(crate) fn get_errno() -> i32 {
#[cfg(target_os = "linux")]
unsafe {
#[cfg(target_os = "linux")]
{
*libc::__errno_location()
}
#[cfg(target_os = "macos")]
{
*libc::__error()
}
#[cfg(target_os = "windows")]
{
unsafe extern "C" {
safe fn _errno() -> *mut i32;
}
*_errno()
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "macos"))))]
{
// FreeBSD, NetBSD, OpenBSD, etc. use __error()
*libc::__error()
}
*libc::__errno_location()
}
#[cfg(target_os = "android")]
unsafe {
*libc::__errno()
}
#[cfg(target_os = "macos")]
unsafe {
*libc::__error()
}
#[cfg(target_os = "windows")]
unsafe {
*_errno()
}
#[cfg(all(unix, not(any(target_os = "linux", target_os = "android", target_os = "macos"))))]
unsafe {
// FreeBSD, NetBSD, OpenBSD, etc. use __error()
*libc::__error()
}
// WASM and other targets: no errno
#[cfg(not(any(unix, windows)))]
{
0
}
}

Expand All @@ -80,8 +90,8 @@ pub(crate) fn get_errno() -> i32 {
pub(crate) fn is_error(x: f64) -> Result<f64> {
match get_errno() {
0 => Ok(x),
libc::EDOM => Err(Error::EDOM),
libc::ERANGE => {
e if e == Error::EDOM as i32 => Err(Error::EDOM),
e if e == Error::ERANGE as i32 => {
// Underflow to zero is not an error.
// Use 1.5 threshold to handle subnormal results that don't underflow to zero
// (e.g., on Ubuntu/ia64) and to correctly detect underflows in expm1()
Expand Down
24 changes: 12 additions & 12 deletions src/math/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ pub fn log_bigint(n: &BigInt, base: Option<f64>) -> crate::Result<f64> {
}

// Try direct conversion first
if let Some(x) = n.to_f64() {
if x.is_finite() {
return super::log(x, base);
}
if let Some(x) = n.to_f64()
&& x.is_finite()
{
return super::log(x, base);
}

// Use frexp decomposition for large values
Expand Down Expand Up @@ -141,10 +141,10 @@ pub fn log2_bigint(n: &BigInt) -> crate::Result<f64> {
}

// Try direct conversion first
if let Some(x) = n.to_f64() {
if x.is_finite() {
return super::log2(x);
}
if let Some(x) = n.to_f64()
&& x.is_finite()
{
return super::log2(x);
}

// Use frexp decomposition for large values
Expand All @@ -162,10 +162,10 @@ pub fn log10_bigint(n: &BigInt) -> crate::Result<f64> {
}

// Try direct conversion first
if let Some(x) = n.to_f64() {
if x.is_finite() {
return super::log10(x);
}
if let Some(x) = n.to_f64()
&& x.is_finite()
{
return super::log10(x);
}

// Use frexp decomposition for large values
Expand Down
28 changes: 14 additions & 14 deletions src/math/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ fn approximate_isqrt(n: u64) -> u32 {

/// Return the integer part of the square root of a non-negative integer.
///
/// Returns Err if n is negative.
pub fn isqrt(n: &BigInt) -> Result<BigInt, ()> {
/// Returns Err(EDOM) if n is negative.
pub fn isqrt(n: &BigInt) -> crate::Result<BigInt> {
if n.is_negative() {
return Err(());
return Err(crate::Error::EDOM);
}
Ok(isqrt_unsigned(&n.magnitude()).into())
Ok(isqrt_unsigned(n.magnitude()).into())
}

/// Return the integer part of the square root of the input.
Expand Down Expand Up @@ -233,12 +233,12 @@ fn factorial_odd_part(n: u64) -> BigUint {

/// Return n factorial (n!).
///
/// Returns Err(()) if n is negative.
/// Returns Err(EDOM) if n is negative.
/// Uses the divide-and-conquer algorithm.
/// Based on: http://www.luschny.de/math/factorial/binarysplitfact.html
pub fn factorial(n: i64) -> Result<BigUint, ()> {
pub fn factorial(n: i64) -> crate::Result<BigUint> {
if n < 0 {
return Err(());
return Err(crate::Error::EDOM);
}
let n = n as u64;
// Use lookup table for small values
Expand Down Expand Up @@ -652,12 +652,12 @@ pub(super) fn perm_comb_small(n: u64, k: u64, is_comb: bool) -> BigUint {

/// Return the number of ways to choose k items from n items (n choose k).
///
/// Returns Err(()) if n or k is negative.
/// Returns Err(EDOM) if n or k is negative.
/// Evaluates to n! / (k! * (n - k)!) when k <= n and evaluates
/// to zero when k > n.
pub fn comb(n: i64, k: i64) -> Result<BigUint, ()> {
pub fn comb(n: i64, k: i64) -> crate::Result<BigUint> {
if n < 0 || k < 0 {
return Err(());
return Err(crate::Error::EDOM);
}
let (n, k) = (n as u64, k as u64);
if k > n {
Expand All @@ -679,19 +679,19 @@ pub fn comb(n: i64, k: i64) -> Result<BigUint, ()> {

/// Return the number of ways to arrange k items from n items.
///
/// Returns Err(()) if n or k is negative.
/// Returns Err(EDOM) if n or k is negative.
/// Evaluates to n! / (n - k)! when k <= n and evaluates
/// to zero when k > n.
///
/// If k is not specified (None), then k defaults to n
/// and the function returns n!.
pub fn perm(n: i64, k: Option<i64>) -> Result<BigUint, ()> {
pub fn perm(n: i64, k: Option<i64>) -> crate::Result<BigUint> {
if n < 0 {
return Err(());
return Err(crate::Error::EDOM);
}
let n = n as u64;
let k = match k {
Some(k) if k < 0 => return Err(()),
Some(k) if k < 0 => return Err(crate::Error::EDOM),
Some(k) => k as u64,
None => n,
};
Expand Down