From 3ce90c5ec2237537de0e49a8f24984c5c43336c7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 3 May 2024 16:54:33 -0700 Subject: [PATCH 1/9] Fix older CI for num-traits 0.2.19 --- .github/workflows/ci.yaml | 2 ++ ci/rustup.sh | 2 +- ci/test_full.sh | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 44bcde8..1e0517a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,6 +10,8 @@ jobs: matrix: rust: [ 1.31.0, # MSRV + 1.51.0, + 1.60.0, stable, beta, nightly, diff --git a/ci/rustup.sh b/ci/rustup.sh index fed3e45..18015d2 100755 --- a/ci/rustup.sh +++ b/ci/rustup.sh @@ -5,6 +5,6 @@ set -ex ci=$(dirname $0) -for version in 1.31.0 stable beta nightly; do +for version in 1.31.0 1.51.0 1.60.0 stable beta nightly; do rustup run "$version" "$ci/test_full.sh" done diff --git a/ci/test_full.sh b/ci/test_full.sh index c03cec7..57c5ac0 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -30,6 +30,12 @@ fi FEATURES=() echo "Testing supported features: ${FEATURES[*]}" +cargo generate-lockfile + +# num-traits 0.2.19 started using dep: features, which requires 1.60 and is +# otherwise ignored down to 1.51, but we need a manual downgrade before that. +check_version 1.51 || cargo update -p num-traits --precise 0.2.18 + set -x # test the default From ce7b81eea0236e6fae94516d14773f9250757fd3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 3 May 2024 16:59:49 -0700 Subject: [PATCH 2/9] Manual builds aren't needed before `test_full.sh` --- .github/workflows/ci.yaml | 1 - .github/workflows/master.yaml | 1 - .github/workflows/pr.yaml | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1e0517a..9823d24 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,6 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - run: cargo build - run: ./ci/test_full.sh # try a target that doesn't have std at all diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 68c4900..f6487bf 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -24,5 +24,4 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - run: cargo build - run: ./ci/test_full.sh diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 787f908..4e6aa37 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -20,7 +20,6 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - run: cargo build - run: ./ci/test_full.sh fmt: From a979659d327459cfe6c5c39e59ed7f0aeffecdeb Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 28 Feb 2025 12:19:39 -0500 Subject: [PATCH 3/9] test_is_multiple_of: call is_multiple_of from trait is_multiple_of was just stabilized for unsigned integer types[0], so this fails to compile on recent nightlies without this fix. 0: https://github.com/rust-lang/rust/pull/137383 --- src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 30fce25..5d84a3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1018,14 +1018,14 @@ macro_rules! impl_integer_for_usize { #[test] fn test_is_multiple_of() { - assert!((0 as $T).is_multiple_of(&(0 as $T))); - assert!((6 as $T).is_multiple_of(&(6 as $T))); - assert!((6 as $T).is_multiple_of(&(3 as $T))); - assert!((6 as $T).is_multiple_of(&(1 as $T))); - - assert!(!(42 as $T).is_multiple_of(&(5 as $T))); - assert!(!(5 as $T).is_multiple_of(&(3 as $T))); - assert!(!(42 as $T).is_multiple_of(&(0 as $T))); + assert!(<$T as Integer>::is_multiple_of(&(0 as $T), &(0 as $T))); + assert!(<$T as Integer>::is_multiple_of(&(6 as $T), &(6 as $T))); + assert!(<$T as Integer>::is_multiple_of(&(6 as $T), &(3 as $T))); + assert!(<$T as Integer>::is_multiple_of(&(6 as $T), &(1 as $T))); + + assert!(!<$T as Integer>::is_multiple_of(&(42 as $T), &(5 as $T))); + assert!(!<$T as Integer>::is_multiple_of(&(5 as $T), &(3 as $T))); + assert!(!<$T as Integer>::is_multiple_of(&(42 as $T), &(0 as $T))); } #[test] From f9279a28a956499a1c1420c8f386a6f6a51a3854 Mon Sep 17 00:00:00 2001 From: Mike Maley Date: Tue, 10 Feb 2026 18:00:49 -0500 Subject: [PATCH 4/9] non-negative --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 5d84a3c..42d4614 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,8 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq { /// Greatest Common Divisor (GCD). /// + /// The result should always be non-negative. + /// /// # Examples /// /// ~~~ From 3e242f52ab9b9db4656d35f4f3136806a87bb07a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 7 Jul 2026 12:19:11 -0700 Subject: [PATCH 5/9] Rename master to main --- .github/workflows/{master.yaml => main.yaml} | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{master.yaml => main.yaml} (95%) diff --git a/.github/workflows/master.yaml b/.github/workflows/main.yaml similarity index 95% rename from .github/workflows/master.yaml rename to .github/workflows/main.yaml index f6487bf..0ce4f57 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/main.yaml @@ -1,8 +1,8 @@ -name: master +name: main on: push: branches: - - master + - main schedule: - cron: '0 0 * * 0' # 00:00 Sunday diff --git a/README.md b/README.md index aefe368..01c656b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![crate](https://img.shields.io/crates/v/num-integer.svg)](https://crates.io/crates/num-integer) [![documentation](https://docs.rs/num-integer/badge.svg)](https://docs.rs/num-integer) [![minimum rustc 1.31](https://img.shields.io/badge/rustc-1.31+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) -[![build status](https://github.com/rust-num/num-integer/workflows/master/badge.svg)](https://github.com/rust-num/num-integer/actions) +[![build status](https://github.com/rust-num/num-integer/actions/workflows/main.yaml/badge.svg)](https://github.com/rust-num/num-integer/actions/workflows/main.yaml) `Integer` trait and functions for Rust. From dcaece19c04de6187ea8a77ee7a72f9192dc11a7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 7 Jul 2026 12:21:56 -0700 Subject: [PATCH 6/9] ci: use the fallback resolver for deps --- ci/test_full.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ci/test_full.sh b/ci/test_full.sh index 57c5ac0..7a8e043 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -21,6 +21,14 @@ check_version() { ]] } +export CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback +generate_lockfile() { + cargo generate-lockfile + if ! check_version 1.85 ; then + cargo +stable update + fi +} + echo "Testing $CRATE on rustc $RUST_VERSION" if ! check_version $MSRV ; then echo "The minimum for $CRATE is rustc $MSRV" @@ -30,11 +38,7 @@ fi FEATURES=() echo "Testing supported features: ${FEATURES[*]}" -cargo generate-lockfile - -# num-traits 0.2.19 started using dep: features, which requires 1.60 and is -# otherwise ignored down to 1.51, but we need a manual downgrade before that. -check_version 1.51 || cargo update -p num-traits --precise 0.2.18 +generate_lockfile set -x From e16458846a9fdc4c09a2c83543c13b230623cc0e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 11 Aug 2026 15:41:28 -0700 Subject: [PATCH 7/9] implement Karatsuba Square Root --- src/roots.rs | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/src/roots.rs b/src/roots.rs index aed3802..5e23986 100644 --- a/src/roots.rs +++ b/src/roots.rs @@ -196,6 +196,44 @@ fn log2(x: T) -> u32 { bits::() - 1 - x.leading_zeros() } +/// 128-bit Karatsuba Square Root, using b = 2³² +/// +/// Reference: +/// Paul Zimmermann. Karatsuba Square Root. [Research Report] RR-3805, INRIA. 1999, pp.8. +/// +#[inline] +fn karatsuba_sqrt(n: u128) -> u128 { + // Algorithm SqrtRem(n = a₃b³ + a₂b² + a₁b + a₀) + // Input: 0 ≤ aᵢ < b with a₃ ≥ b/4 + // Output: (s,r) such that s² ≤ n = s² + r < (s+1)² + debug_assert!(n.leading_zeros() < 2); + let a0 = n as u32 as u128; + let a1 = (n >> 32) as u32 as u128; + let a23 = n >> 64; + + // (s',r') ← SqrtRem(a₃b + a₂) + let s1 = (a23 as u64).sqrt() as u128; + let r1 = a23 - s1 * s1; + + // (q,u) ← DivRem(r'b + a₁, 2s') + let (q, u) = ((r1 << 32) | a1).div_rem(&(2 * s1)); + + // s ← s'b + q + let mut s = (s1 << 32) + q; + + // r ← ub + a₀ - q² + // if r < 0 then + // r ← r + 2s - 1 + // s ← s - 1 + // + // but to avoid negatives, we compare and adjust before subtraction, + // and in this case we don't care about the actual remainder. + if ((u << 32) | a0) < q * q { + s -= 1; + } + s +} + macro_rules! unsigned_roots { ($T:ident) => { impl Roots for $T { @@ -275,17 +313,14 @@ macro_rules! unsigned_roots { fn sqrt(&self) -> Self { fn go(a: $T) -> $T { if bits::<$T>() > 64 { - // 128-bit division is slow, so do a bitwise `sqrt` until it's small enough. + // 128-bit division is slow in the Babylonian method, + // so use 64-bit sqrt and Karatsuba if needed. return if a <= core::u64::MAX as $T { (a as u64).sqrt() as $T } else { - let lo = (a >> 2u32).sqrt() << 1; - let hi = lo + 1; - if hi * hi <= a { - hi - } else { - lo - } + let shift = a.leading_zeros() / 2; + let n = a << (shift * 2); + (karatsuba_sqrt(n as u128) >> shift) as $T }; } From 6c3bec6c600b0783e326522b98bc1bd913a23e23 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 11 Aug 2026 16:38:25 -0700 Subject: [PATCH 8/9] Release 0.1.47 --- Cargo.toml | 2 +- RELEASES.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8c5fc92..a22ea3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ categories = ["algorithms", "science", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-num/num-integer" name = "num-integer" -version = "0.1.46" +version = "0.1.47" readme = "README.md" exclude = ["/ci/*", "/.github/*"] edition = "2018" diff --git a/RELEASES.md b/RELEASES.md index 9aeca2d..f8abd3a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +# Release 0.1.47 (2025-08-11) + +- [Implement Karatsuba Square Root for 128-bit `Roots::sqrt`][80] +- Miscellaneous other improvements to docs and testing. + +**Contributors**: @durin42, @cuviper, @mikem8891 + +[80]: https://github.com/rust-num/num-integer/pull/80 + # Release 0.1.46 (2024-02-07) - [Upgrade to 2018 edition, **MSRV 1.31**][51] From 765fd9b3eb9397471d41b9f613e530f6d9d6383f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 11 Aug 2026 17:11:23 -0700 Subject: [PATCH 9/9] Fix the release date --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index f8abd3a..aa2ed42 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,4 +1,4 @@ -# Release 0.1.47 (2025-08-11) +# Release 0.1.47 (2026-08-11) - [Implement Karatsuba Square Root for 128-bit `Roots::sqrt`][80] - Miscellaneous other improvements to docs and testing.