diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 269317b..e8163f1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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 @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 317cf18..c6751ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "pymath" -author = "Jeong, YunWon " +authors = ["Jeong, YunWon "] 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" - [features] default = ["complex"] complex = ["dep:num-complex"] diff --git a/src/cmath/misc.rs b/src/cmath/misc.rs index 3380649..69514e2 100644 --- a/src/cmath/misc.rs +++ b/src/cmath/misc.rs @@ -22,8 +22,8 @@ pub fn phase(z: Complex64) -> Result { 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) } } diff --git a/src/err.rs b/src/err.rs index 59d802c..65be9bb 100644 --- a/src/err.rs +++ b/src/err.rs @@ -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, @@ -14,8 +18,8 @@ impl TryFrom for Error { fn try_from(value: c_int) -> std::result::Result { 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), } } @@ -24,54 +28,60 @@ impl TryFrom 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 } } @@ -80,8 +90,8 @@ pub(crate) fn get_errno() -> i32 { pub(crate) fn is_error(x: f64) -> Result { 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() diff --git a/src/math/bigint.rs b/src/math/bigint.rs index 2ba4488..1bb157f 100644 --- a/src/math/bigint.rs +++ b/src/math/bigint.rs @@ -110,10 +110,10 @@ pub fn log_bigint(n: &BigInt, base: Option) -> crate::Result { } // 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 @@ -141,10 +141,10 @@ pub fn log2_bigint(n: &BigInt) -> crate::Result { } // 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 @@ -162,10 +162,10 @@ pub fn log10_bigint(n: &BigInt) -> crate::Result { } // 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 diff --git a/src/math/integer.rs b/src/math/integer.rs index cf08eb9..3638ac9 100644 --- a/src/math/integer.rs +++ b/src/math/integer.rs @@ -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 { +/// Returns Err(EDOM) if n is negative. +pub fn isqrt(n: &BigInt) -> crate::Result { 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. @@ -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 { +pub fn factorial(n: i64) -> crate::Result { if n < 0 { - return Err(()); + return Err(crate::Error::EDOM); } let n = n as u64; // Use lookup table for small values @@ -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 { +pub fn comb(n: i64, k: i64) -> crate::Result { if n < 0 || k < 0 { - return Err(()); + return Err(crate::Error::EDOM); } let (n, k) = (n as u64, k as u64); if k > n { @@ -679,19 +679,19 @@ pub fn comb(n: i64, k: i64) -> Result { /// 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) -> Result { +pub fn perm(n: i64, k: Option) -> crate::Result { 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, };