diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ce18cc80..8ddca545 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,9 +1,5 @@ name: CI -on: - push: - branches: - - staging - - trying +on: merge_group jobs: @@ -13,17 +9,18 @@ jobs: strategy: matrix: rust: [ - 1.31.0, # 2018! - 1.34.0, # has_try_from - 1.36.0, # alloc, rand - 1.40.0, # arbitrary - 1.46.0, # quickcheck + 1.60.0, # MSRV stable, beta, nightly ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + if: startsWith(matrix.rust, '1') + with: + path: ~/.cargo/registry/index + key: cargo-${{ matrix.rust }}-git-index - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -38,7 +35,7 @@ jobs: - run: | sudo apt-get update sudo apt-get install gcc-multilib - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: stable-i686-unknown-linux-gnu @@ -50,7 +47,7 @@ jobs: name: No Std runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: target: thumbv6m-none-eabi @@ -60,8 +57,23 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.62.0 with: components: rustfmt - run: cargo fmt --all --check + + # One job that "summarizes" the success state of this pipeline. This can then be added to branch + # protection, rather than having to add each job separately. + success: + name: Success + runs-on: ubuntu-latest + needs: [test, i686, no_std, fmt] + # Github branch protection is exceedingly silly and treats "jobs skipped because a dependency + # failed" as success. So we have to do some contortions to ensure the job fails if any of its + # dependencies fails. + if: always() # make sure this is never "skipped" + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: check if any dependency failed + run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/.github/workflows/master.yaml b/.github/workflows/master.yaml index 98e8f8e0..034e3793 100644 --- a/.github/workflows/master.yaml +++ b/.github/workflows/master.yaml @@ -13,9 +13,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [1.31.0, stable] + rust: [1.60.0, stable] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + if: startsWith(matrix.rust, '1') + with: + path: ~/.cargo/registry/index + key: cargo-${{ matrix.rust }}-git-index - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index d221a73d..baac6484 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -9,9 +9,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [1.31.0, stable] + rust: [1.60.0, stable] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + if: startsWith(matrix.rust, '1') + with: + path: ~/.cargo/registry/index + key: cargo-${{ matrix.rust }}-git-index - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} @@ -22,8 +27,23 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.62.0 with: components: rustfmt - run: cargo fmt --all --check + + # One job that "summarizes" the success state of this pipeline. This can then be added to branch + # protection, rather than having to add each job separately. + success: + name: Success + runs-on: ubuntu-latest + needs: [test, fmt] + # Github branch protection is exceedingly silly and treats "jobs skipped because a dependency + # failed" as success. So we have to do some contortions to ensure the job fails if any of its + # dependencies fails. + if: always() # make sure this is never "skipped" + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: check if any dependency failed + run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/Cargo.toml b/Cargo.toml index dc99d5f0..cd1e6d2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,19 @@ categories = [ "algorithms", "data-structures", "science" ] license = "MIT OR Apache-2.0" name = "num-bigint" repository = "https://github.com/rust-num/num-bigint" -version = "0.4.4" +version = "0.4.5" readme = "README.md" -build = "build.rs" -exclude = ["/bors.toml", "/ci/*", "/.github/*"] -edition = "2018" +exclude = ["/ci/*", "/.github/*"] +edition = "2021" +rust-version = "1.60" [features] default = ["std"] std = ["num-integer/std", "num-traits/std"] +arbitrary = ["dep:arbitrary"] +quickcheck = ["dep:quickcheck"] +rand = ["dep:rand"] +serde = ["dep:serde"] [package.metadata.docs.rs] features = ["std", "serde", "rand", "quickcheck", "arbitrary"] @@ -40,12 +44,12 @@ name = "shootout-pidigits" [dependencies] [dependencies.num-integer] -version = "0.1.42" +version = "0.1.46" default-features = false features = ["i128"] [dependencies.num-traits] -version = "0.2.16" +version = "0.2.18" default-features = false features = ["i128"] @@ -68,6 +72,3 @@ default-features = false optional = true version = "1" default-features = false - -[build-dependencies] -autocfg = "1" diff --git a/README.md b/README.md index 21f7749a..cce185b9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![crate](https://img.shields.io/crates/v/num-bigint.svg)](https://crates.io/crates/num-bigint) [![documentation](https://docs.rs/num-bigint/badge.svg)](https://docs.rs/num-bigint) -[![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) +[![minimum rustc 1.60](https://img.shields.io/badge/rustc-1.60+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) [![build status](https://github.com/rust-num/num-bigint/workflows/master/badge.svg)](https://github.com/rust-num/num-bigint/actions) Big integer types for Rust, `BigInt` and `BigUint`. @@ -42,7 +42,7 @@ Release notes are available in [RELEASES.md](RELEASES.md). ## Compatibility -The `num-bigint` crate is tested for rustc 1.31 and greater. +The `num-bigint` crate is tested for rustc 1.60 and greater. ## Alternatives @@ -52,10 +52,10 @@ table offers a brief comparison to a few alternatives. | Crate | License | Min rustc | Implementation | Features | | :--------------- | :------------- | :-------- | :------------- | :------- | -| **`num-bigint`** | MIT/Apache-2.0 | 1.31 | pure rust | dynamic width, number theoretical functions | +| **`num-bigint`** | MIT/Apache-2.0 | 1.60 | pure rust | dynamic width, number theoretical functions | | [`awint`] | MIT/Apache-2.0 | 1.66 | pure rust | fixed width, heap or stack, concatenation macros | -| [`bnum`] | MIT/Apache-2.0 | 1.61 | pure rust | fixed width, parity with Rust primitives including floats | -| [`crypto-bigint`] | MIT/Apache-2.0 | 1.57 | pure rust | fixed width, stack only | +| [`bnum`] | MIT/Apache-2.0 | 1.65 | pure rust | fixed width, parity with Rust primitives including floats | +| [`crypto-bigint`] | MIT/Apache-2.0 | 1.73 | pure rust | fixed width, stack only | | [`ibig`] | MIT/Apache-2.0 | 1.49 | pure rust | dynamic width, number theoretical functions | | [`rug`] | LGPL-3.0+ | 1.65 | bundles [GMP] via [`gmp-mpfr-sys`] | all the features of GMP, MPFR, and MPC | diff --git a/RELEASES.md b/RELEASES.md index ad5dd499..f3163116 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,19 @@ +# Release 0.4.5 (2024-05-06) + +- [Upgrade to 2021 edition, **MSRV 1.60**][292] +- [Add `const ZERO` and implement `num_traits::ConstZero`][298] +- [Add `modinv` methods for the modular inverse][288] +- [Optimize multiplication with imbalanced operands][295] +- [Optimize scalar division on x86 and x86-64][236] + +**Contributors**: @cuviper, @joelonsql, @waywardmonkeys + +[236]: https://github.com/rust-num/num-bigint/pull/236 +[288]: https://github.com/rust-num/num-bigint/pull/288 +[292]: https://github.com/rust-num/num-bigint/pull/292 +[295]: https://github.com/rust-num/num-bigint/pull/295 +[298]: https://github.com/rust-num/num-bigint/pull/298 + # Release 0.4.4 (2023-08-22) - [Implemented `From` for `BigInt` and `BigUint`.][239] diff --git a/benches/bigint.rs b/benches/bigint.rs index 80ec191c..22795274 100644 --- a/benches/bigint.rs +++ b/benches/bigint.rs @@ -87,6 +87,16 @@ fn multiply_3(b: &mut Bencher) { multiply_bench(b, 1 << 16, 1 << 17); } +#[bench] +fn multiply_4(b: &mut Bencher) { + multiply_bench(b, 1 << 12, 1 << 13); +} + +#[bench] +fn multiply_5(b: &mut Bencher) { + multiply_bench(b, 1 << 12, 1 << 14); +} + #[bench] fn divide_0(b: &mut Bencher) { divide_bench(b, 1 << 8, 1 << 6); diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 163c03ab..00000000 --- a/bors.toml +++ /dev/null @@ -1,13 +0,0 @@ -status = [ - "Test (1.31.0)", - "Test (1.34.0)", - "Test (1.36.0)", - "Test (1.40.0)", - "Test (1.46.0)", - "Test (stable)", - "Test (beta)", - "Test (nightly)", - "Test (i686)", - "No Std", - "Format", -] diff --git a/build.rs b/build.rs deleted file mode 100644 index 5d5406c9..00000000 --- a/build.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::env; -use std::error::Error; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -fn main() { - let ptr_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH"); - let u64_digit = ptr_width - .as_ref() - .map(|x| x == "64" || x == "128") - .unwrap_or(false); - - if u64_digit { - autocfg::emit("u64_digit"); - } - - let ac = autocfg::new(); - let std = if ac.probe_sysroot_crate("std") { - "std" - } else { - "core" - }; - - if ac.probe_path(&format!("{}::convert::TryFrom", std)) { - autocfg::emit("has_try_from"); - } - - if let Ok(arch) = env::var("CARGO_CFG_TARGET_ARCH") { - if arch == "x86_64" || arch == "x86" { - let digit = if u64_digit { "u64" } else { "u32" }; - - let addcarry = format!("{}::arch::{}::_addcarry_{}", std, arch, digit); - if ac.probe_path(&addcarry) { - autocfg::emit("use_addcarry"); - } - } - } - - autocfg::rerun_path("build.rs"); - - write_radix_bases().unwrap(); -} - -/// Write tables of the greatest power of each radix for the given bit size. These are returned -/// from `biguint::get_radix_base` to batch the multiplication/division of radix conversions on -/// full `BigUint` values, operating on primitive integers as much as possible. -/// -/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big -/// BASES_32[3] = (3486784401, 20) -/// BASES_64[3] = (12157665459056928801, 40) -/// -/// Powers of two are not included, just zeroed, as they're implemented with shifts. -fn write_radix_bases() -> Result<(), Box> { - let out_dir = env::var("OUT_DIR")?; - let dest_path = Path::new(&out_dir).join("radix_bases.rs"); - let mut f = File::create(&dest_path)?; - - for &bits in &[16, 32, 64] { - let max = if bits < 64 { - (1 << bits) - 1 - } else { - std::u64::MAX - }; - - writeln!(f, "#[deny(overflowing_literals)]")?; - writeln!( - f, - "pub(crate) static BASES_{bits}: [(u{bits}, usize); 257] = [", - bits = bits - )?; - for radix in 0u64..257 { - let (base, power) = if radix == 0 || radix.is_power_of_two() { - (0, 0) - } else { - let mut power = 1; - let mut base = radix; - - while let Some(b) = base.checked_mul(radix) { - if b > max { - break; - } - base = b; - power += 1; - } - (base, power) - }; - writeln!(f, " ({}, {}), // {}", base, power, radix)?; - } - writeln!(f, "];")?; - } - - Ok(()) -} diff --git a/ci/big_quickcheck/src/lib.rs b/ci/big_quickcheck/src/lib.rs index 47ba2e14..41018a69 100644 --- a/ci/big_quickcheck/src/lib.rs +++ b/ci/big_quickcheck/src/lib.rs @@ -42,7 +42,7 @@ fn quickcheck_signed_eq_symmetric(a: BigInt, b: BigInt) -> bool { #[test] fn quickcheck_arith_primitive() { - let gen = Gen::new(usize::max_value()); + let gen = Gen::new(usize::MAX); let mut qc = QuickCheck::new().gen(gen); fn test_unsigned_add_primitive(a: usize, b: usize) -> TestResult { @@ -280,7 +280,7 @@ fn quickcheck_signed_conversion(a: BigInt, radix: u8) -> TestResult { #[test] fn quicktest_shift() { - let gen = Gen::new(usize::max_value()); + let gen = Gen::new(usize::MAX); let mut qc = QuickCheck::new().gen(gen); fn test_shr_unsigned(a: u64, shift: u8) -> TestResult { @@ -317,7 +317,7 @@ fn quicktest_shift() { #[test] fn quickcheck_modpow() { - let gen = Gen::new(usize::max_value()); + let gen = Gen::new(usize::MAX); let mut qc = QuickCheck::new().gen(gen); fn simple_modpow(base: &BigInt, exponent: &BigInt, modulus: &BigInt) -> BigInt { @@ -358,9 +358,66 @@ fn quickcheck_modpow() { qc.quickcheck(test_modpow as fn(i128, u128, i128) -> TestResult); } +#[test] +fn quickcheck_modinv() { + let gen = Gen::new(usize::MAX); + let mut qc = QuickCheck::new().gen(gen); + + fn test_modinv(value: i128, modulus: i128) -> TestResult { + if modulus.is_zero() { + TestResult::discard() + } else { + let value = BigInt::from(value); + let modulus = BigInt::from(modulus); + match (value.modinv(&modulus), value.gcd(&modulus).is_one()) { + (None, false) => TestResult::passed(), + (None, true) => { + eprintln!("{}.modinv({}) -> None, expected Some(_)", value, modulus); + TestResult::failed() + } + (Some(inverse), false) => { + eprintln!( + "{}.modinv({}) -> Some({}), expected None", + value, modulus, inverse + ); + TestResult::failed() + } + (Some(inverse), true) => { + // The inverse should either be in [0,m) or (m,0] + let zero = BigInt::zero(); + if (modulus.is_positive() && !(zero <= inverse && inverse < modulus)) + || (modulus.is_negative() && !(modulus < inverse && inverse <= zero)) + { + eprintln!( + "{}.modinv({}) -> Some({}) is out of range", + value, modulus, inverse + ); + return TestResult::failed(); + } + + // We don't know the expected inverse, but we can verify the product ≡ 1 + let product = (&value * &inverse).mod_floor(&modulus); + let mod_one = BigInt::one().mod_floor(&modulus); + if product != mod_one { + eprintln!("{}.modinv({}) -> Some({})", value, modulus, inverse); + eprintln!( + "{} * {} ≡ {}, expected {}", + value, inverse, product, mod_one + ); + return TestResult::failed(); + } + TestResult::passed() + } + } + } + } + + qc.quickcheck(test_modinv as fn(i128, i128) -> TestResult); +} + #[test] fn quickcheck_to_float_equals_i128_cast() { - let gen = Gen::new(usize::max_value()); + let gen = Gen::new(usize::MAX); let mut qc = QuickCheck::new().gen(gen).tests(1_000_000); fn to_f32_equals_i128_cast(value: i128) -> bool { diff --git a/ci/big_serde/src/lib.rs b/ci/big_serde/src/lib.rs index 68c2551b..4ee7b632 100644 --- a/ci/big_serde/src/lib.rs +++ b/ci/big_serde/src/lib.rs @@ -155,7 +155,7 @@ fn bad_size_hint( assert_tokens(&T::one(), &tokens); tokens[prefix.len()] = Token::Seq { - len: Some(usize::max_value()), + len: Some(usize::MAX), }; catch_unwind(|| assert_ser_tokens(&T::one(), &tokens)).unwrap_err(); diff --git a/ci/rustup.sh b/ci/rustup.sh index 607901c0..6cfe67ef 100755 --- a/ci/rustup.sh +++ b/ci/rustup.sh @@ -5,6 +5,6 @@ set -ex ci=$(dirname $0) -for version in 1.31.0 1.34.0 1.36.0 1.40.0 1.46.0 stable beta nightly; do +for version in 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 0130964b..3924be90 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -3,7 +3,7 @@ set -e CRATE=num-bigint -MSRV=1.31 +MSRV=1.60 get_rust_version() { local array=($(rustc --version)); @@ -27,18 +27,15 @@ if ! check_version $MSRV ; then exit 1 fi -STD_FEATURES=(serde) -check_version 1.36 && STD_FEATURES+=(rand) -check_version 1.36 && NO_STD_FEATURES=(serde rand) -check_version 1.40 && STD_FEATURES+=(arbitrary) -check_version 1.46 && STD_FEATURES+=(quickcheck) +STD_FEATURES=(arbitrary quickcheck rand serde) +NO_STD_FEATURES=(serde rand) echo "Testing supported features: ${STD_FEATURES[*]}" if [ -n "${NO_STD_FEATURES[*]}" ]; then echo " no_std supported features: ${NO_STD_FEATURES[*]}" fi -# arbitrary 1.0.1 added const-generic arrays, which requires Rust 1.51 -check_version 1.51.0 || cargo update -p arbitrary --precise 1.0.0 +# arbitrary 1.1.4 started using array::from_fn +check_version 1.63.0 || cargo update -p arbitrary --precise 1.1.3 set -x @@ -82,22 +79,11 @@ fi case "${STD_FEATURES[*]}" in *serde*) ( cd ci/big_serde - # serde_test updated to 2021 edition after this version - check_version 1.56.0 || ( - cargo generate-lockfile - cargo update -p serde_test --precise 1.0.175 - ) cargo test ) ;;& *rand*) cargo test --manifest-path ci/big_rand/Cargo.toml ;;& *quickcheck*) ( cd ci/big_quickcheck - # quote and proc-macro2 updated to 2021 edition after these versions - check_version 1.56.0 || ( - cargo generate-lockfile - cargo update -p quote --precise 1.0.30 - cargo update -p proc-macro2 --precise 1.0.65 - ) cargo test ) ;;& esac diff --git a/src/bigint.rs b/src/bigint.rs index 97faa834..ffd457da 100644 --- a/src/bigint.rs +++ b/src/bigint.rs @@ -8,11 +8,9 @@ use core::fmt; use core::hash; use core::ops::{Neg, Not}; use core::str; -use core::{i128, u128}; -use core::{i64, u64}; use num_integer::{Integer, Roots}; -use num_traits::{Num, One, Pow, Signed, Zero}; +use num_traits::{ConstZero, Num, One, Pow, Signed, Zero}; use self::Sign::{Minus, NoSign, Plus}; @@ -132,7 +130,7 @@ impl Ord for BigInt { impl Default for BigInt { #[inline] fn default() -> BigInt { - Zero::zero() + Self::ZERO } } @@ -211,10 +209,7 @@ impl Not for &BigInt { impl Zero for BigInt { #[inline] fn zero() -> BigInt { - BigInt { - sign: NoSign, - data: BigUint::zero(), - } + Self::ZERO } #[inline] @@ -229,6 +224,11 @@ impl Zero for BigInt { } } +impl ConstZero for BigInt { + // forward to the inherent const + const ZERO: Self = Self::ZERO; +} + impl One for BigInt { #[inline] fn one() -> BigInt { @@ -262,7 +262,7 @@ impl Signed for BigInt { #[inline] fn abs_sub(&self, other: &BigInt) -> BigInt { if *self <= *other { - Zero::zero() + Self::ZERO } else { self - other } @@ -273,7 +273,7 @@ impl Signed for BigInt { match self.sign { Plus => BigInt::one(), Minus => -BigInt::one(), - NoSign => BigInt::zero(), + NoSign => Self::ZERO, } } @@ -291,10 +291,6 @@ impl Signed for BigInt { trait UnsignedAbs { type Unsigned; - /// A convenience method for getting the absolute value of a signed primitive as unsigned - /// See also `unsigned_abs`: - fn uabs(self) -> Self::Unsigned; - fn checked_uabs(self) -> CheckedUnsignedAbs; } @@ -309,11 +305,6 @@ macro_rules! impl_unsigned_abs { impl UnsignedAbs for $Signed { type Unsigned = $Unsigned; - #[inline] - fn uabs(self) -> $Unsigned { - self.wrapping_abs() as $Unsigned - } - #[inline] fn checked_uabs(self) -> CheckedUnsignedAbs { if self >= 0 { @@ -462,7 +453,7 @@ impl Integer for BigInt { fn extended_gcd_lcm(&self, other: &BigInt) -> (num_integer::ExtendedGcd, BigInt) { let egcd = self.extended_gcd(other); let lcm = if egcd.gcd.is_zero() { - BigInt::zero() + Self::ZERO } else { BigInt::from(&self.data / &egcd.gcd.data * &other.data) }; @@ -508,6 +499,14 @@ impl Integer for BigInt { fn prev_multiple_of(&self, other: &Self) -> Self { self - self.mod_floor(other) } + + fn dec(&mut self) { + *self -= 1u32; + } + + fn inc(&mut self) { + *self += 1u32; + } } impl Roots for BigInt { @@ -567,6 +566,12 @@ pub trait ToBigInt { } impl BigInt { + /// A constant `BigInt` with value 0, useful for static initialization. + pub const ZERO: Self = BigInt { + sign: NoSign, + data: BigUint::ZERO, + }; + /// Creates and initializes a [`BigInt`]. /// /// The base 232 digits are ordered least significant digit first. @@ -922,11 +927,10 @@ impl BigInt { /// /// ``` /// use num_bigint::{BigInt, Sign}; - /// use num_traits::Zero; /// /// assert_eq!(BigInt::from(1234).sign(), Sign::Plus); /// assert_eq!(BigInt::from(-4321).sign(), Sign::Minus); - /// assert_eq!(BigInt::zero().sign(), Sign::NoSign); + /// assert_eq!(BigInt::ZERO.sign(), Sign::NoSign); /// ``` #[inline] pub fn sign(&self) -> Sign { @@ -943,7 +947,7 @@ impl BigInt { /// /// assert_eq!(BigInt::from(1234).magnitude(), &BigUint::from(1234u32)); /// assert_eq!(BigInt::from(-4321).magnitude(), &BigUint::from(4321u32)); - /// assert!(BigInt::zero().magnitude().is_zero()); + /// assert!(BigInt::ZERO.magnitude().is_zero()); /// ``` #[inline] pub fn magnitude(&self) -> &BigUint { @@ -957,11 +961,10 @@ impl BigInt { /// /// ``` /// use num_bigint::{BigInt, BigUint, Sign}; - /// use num_traits::Zero; /// /// assert_eq!(BigInt::from(1234).into_parts(), (Sign::Plus, BigUint::from(1234u32))); /// assert_eq!(BigInt::from(-4321).into_parts(), (Sign::Minus, BigUint::from(4321u32))); - /// assert_eq!(BigInt::zero().into_parts(), (Sign::NoSign, BigUint::zero())); + /// assert_eq!(BigInt::ZERO.into_parts(), (Sign::NoSign, BigUint::ZERO)); /// ``` #[inline] pub fn into_parts(self) -> (Sign, BigUint) { @@ -980,7 +983,7 @@ impl BigInt { pub fn to_biguint(&self) -> Option { match self.sign { Plus => Some(self.data.clone()), - NoSign => Some(Zero::zero()), + NoSign => Some(BigUint::ZERO), Minus => None, } } @@ -1025,6 +1028,64 @@ impl BigInt { power::modpow(self, exponent, modulus) } + /// Returns the modular multiplicative inverse if it exists, otherwise `None`. + /// + /// This solves for `x` such that `self * x ≡ 1 (mod modulus)`. + /// Note that this rounds like `mod_floor`, not like the `%` operator, + /// which makes a difference when given a negative `self` or `modulus`. + /// The solution will be in the interval `[0, modulus)` for `modulus > 0`, + /// or in the interval `(modulus, 0]` for `modulus < 0`, + /// and it exists if and only if `gcd(self, modulus) == 1`. + /// + /// ``` + /// use num_bigint::BigInt; + /// use num_integer::Integer; + /// use num_traits::{One, Zero}; + /// + /// let m = BigInt::from(383); + /// + /// // Trivial cases + /// assert_eq!(BigInt::zero().modinv(&m), None); + /// assert_eq!(BigInt::one().modinv(&m), Some(BigInt::one())); + /// let neg1 = &m - 1u32; + /// assert_eq!(neg1.modinv(&m), Some(neg1)); + /// + /// // Positive self and modulus + /// let a = BigInt::from(271); + /// let x = a.modinv(&m).unwrap(); + /// assert_eq!(x, BigInt::from(106)); + /// assert_eq!(x.modinv(&m).unwrap(), a); + /// assert_eq!((&a * x).mod_floor(&m), BigInt::one()); + /// + /// // Negative self and positive modulus + /// let b = -&a; + /// let x = b.modinv(&m).unwrap(); + /// assert_eq!(x, BigInt::from(277)); + /// assert_eq!((&b * x).mod_floor(&m), BigInt::one()); + /// + /// // Positive self and negative modulus + /// let n = -&m; + /// let x = a.modinv(&n).unwrap(); + /// assert_eq!(x, BigInt::from(-277)); + /// assert_eq!((&a * x).mod_floor(&n), &n + 1); + /// + /// // Negative self and modulus + /// let x = b.modinv(&n).unwrap(); + /// assert_eq!(x, BigInt::from(-106)); + /// assert_eq!((&b * x).mod_floor(&n), &n + 1); + /// ``` + pub fn modinv(&self, modulus: &Self) -> Option { + let result = self.data.modinv(&modulus.data)?; + // The sign of the result follows the modulus, like `mod_floor`. + let (sign, mag) = match (self.is_negative(), modulus.is_negative()) { + (false, false) => (Plus, result), + (true, false) => (Plus, &modulus.data - result), + (false, true) => (Minus, &modulus.data - result), + (true, true) => (Minus, result), + }; + Some(BigInt::from_biguint(sign, mag)) + } + /// Returns the truncated principal square root of `self` -- /// see [`num_integer::Roots::sqrt()`]. pub fn sqrt(&self) -> Self { diff --git a/src/bigint/addition.rs b/src/bigint/addition.rs index 76aeb997..0d3a0e2a 100644 --- a/src/bigint/addition.rs +++ b/src/bigint/addition.rs @@ -8,7 +8,7 @@ use core::cmp::Ordering::{Equal, Greater, Less}; use core::iter::Sum; use core::mem; use core::ops::{Add, AddAssign}; -use num_traits::{CheckedAdd, Zero}; +use num_traits::CheckedAdd; // We want to forward to BigUint::add, but it's not clear how that will go until // we compare both sign and magnitude. So we duplicate this body for every @@ -24,7 +24,7 @@ macro_rules! bigint_add { (Plus, Minus) | (Minus, Plus) => match $a.data.cmp(&$b.data) { Less => BigInt::from_biguint($b.sign, $b_data - $a_data), Greater => BigInt::from_biguint($a.sign, $a_data - $b_data), - Equal => Zero::zero(), + Equal => BigInt::ZERO, }, } }; @@ -76,7 +76,7 @@ impl Add for BigInt { impl AddAssign<&BigInt> for BigInt { #[inline] fn add_assign(&mut self, other: &BigInt) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n + other; } } @@ -97,7 +97,7 @@ impl Add for BigInt { NoSign => From::from(other), Plus => BigInt::from(self.data + other), Minus => match self.data.cmp(&From::from(other)) { - Equal => Zero::zero(), + Equal => Self::ZERO, Less => BigInt::from(other - self.data), Greater => -BigInt::from(self.data - other), }, @@ -108,7 +108,7 @@ impl Add for BigInt { impl AddAssign for BigInt { #[inline] fn add_assign(&mut self, other: u32) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n + other; } } @@ -122,7 +122,7 @@ impl Add for BigInt { NoSign => From::from(other), Plus => BigInt::from(self.data + other), Minus => match self.data.cmp(&From::from(other)) { - Equal => Zero::zero(), + Equal => Self::ZERO, Less => BigInt::from(other - self.data), Greater => -BigInt::from(self.data - other), }, @@ -133,7 +133,7 @@ impl Add for BigInt { impl AddAssign for BigInt { #[inline] fn add_assign(&mut self, other: u64) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n + other; } } @@ -147,7 +147,7 @@ impl Add for BigInt { NoSign => BigInt::from(other), Plus => BigInt::from(self.data + other), Minus => match self.data.cmp(&From::from(other)) { - Equal => BigInt::zero(), + Equal => Self::ZERO, Less => BigInt::from(other - self.data), Greater => -BigInt::from(self.data - other), }, @@ -157,7 +157,7 @@ impl Add for BigInt { impl AddAssign for BigInt { #[inline] fn add_assign(&mut self, other: u128) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n + other; } } diff --git a/src/bigint/bits.rs b/src/bigint/bits.rs index 80f4e2c1..93cd24c4 100644 --- a/src/bigint/bits.rs +++ b/src/bigint/bits.rs @@ -36,7 +36,7 @@ fn negate_carry(a: BigDigit, acc: &mut DoubleBigDigit) -> BigDigit { // + 1 & -ff = ...0 01 & ...f 01 = ...0 01 = + 1 // +ff & - 1 = ...0 ff & ...f ff = ...0 ff = +ff // answer is pos, has length of a -fn bitand_pos_neg(a: &mut Vec, b: &[BigDigit]) { +fn bitand_pos_neg(a: &mut [BigDigit], b: &[BigDigit]) { let mut carry_b = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { let twos_b = negate_carry(bi, &mut carry_b); @@ -114,7 +114,7 @@ impl BitAnd<&BigInt> for &BigInt { #[inline] fn bitand(self, other: &BigInt) -> BigInt { match (self.sign, other.sign) { - (NoSign, _) | (_, NoSign) => BigInt::zero(), + (NoSign, _) | (_, NoSign) => BigInt::ZERO, (Plus, Plus) => BigInt::from(&self.data & &other.data), (Plus, Minus) => self.clone() & other, (Minus, Plus) => other.clone() & self, @@ -202,7 +202,7 @@ fn bitor_pos_neg(a: &mut Vec, b: &[BigDigit]) { // - 1 | +ff = ...f ff | ...0 ff = ...f ff = - 1 // -ff | + 1 = ...f 01 | ...0 01 = ...f 01 = -ff // answer is neg, has length of a -fn bitor_neg_pos(a: &mut Vec, b: &[BigDigit]) { +fn bitor_neg_pos(a: &mut [BigDigit], b: &[BigDigit]) { let mut carry_a = 1; let mut carry_or = 1; for (ai, &bi) in a.iter_mut().zip(b.iter()) { diff --git a/src/bigint/convert.rs b/src/bigint/convert.rs index c4f888b9..f0c29c49 100644 --- a/src/bigint/convert.rs +++ b/src/bigint/convert.rs @@ -2,12 +2,10 @@ use super::Sign::{self, Minus, NoSign, Plus}; use super::{BigInt, ToBigInt}; use crate::std_alloc::Vec; -#[cfg(has_try_from)] use crate::TryFromBigIntError; use crate::{BigUint, ParseBigIntError, ToBigUint}; use core::cmp::Ordering::{Equal, Greater, Less}; -#[cfg(has_try_from)] use core::convert::TryFrom; use core::str::{self, FromStr}; use num_traits::{FromPrimitive, Num, One, ToPrimitive, Zero}; @@ -52,7 +50,7 @@ impl ToPrimitive for BigInt { let m: u64 = 1 << 63; match n.cmp(&m) { Less => Some(-(n as i64)), - Equal => Some(core::i64::MIN), + Equal => Some(i64::MIN), Greater => None, } } @@ -69,7 +67,7 @@ impl ToPrimitive for BigInt { let m: u128 = 1 << 127; match n.cmp(&m) { Less => Some(-(n as i128)), - Equal => Some(core::i128::MIN), + Equal => Some(i128::MIN), Greater => None, } } @@ -109,7 +107,6 @@ impl ToPrimitive for BigInt { macro_rules! impl_try_from_bigint { ($T:ty, $to_ty:path) => { - #[cfg(has_try_from)] impl TryFrom<&BigInt> for $T { type Error = TryFromBigIntError<()>; @@ -119,7 +116,6 @@ macro_rules! impl_try_from_bigint { } } - #[cfg(has_try_from)] impl TryFrom for $T { type Error = TryFromBigIntError; @@ -183,7 +179,7 @@ impl From for BigInt { if n >= 0 { BigInt::from(n as u64) } else { - let u = core::u64::MAX - (n as u64) + 1; + let u = u64::MAX - (n as u64) + 1; BigInt { sign: Minus, data: BigUint::from(u), @@ -198,7 +194,7 @@ impl From for BigInt { if n >= 0 { BigInt::from(n as u128) } else { - let u = core::u128::MAX - (n as u128) + 1; + let u = u128::MAX - (n as u128) + 1; BigInt { sign: Minus, data: BigUint::from(u), @@ -232,7 +228,7 @@ impl From for BigInt { data: BigUint::from(n), } } else { - BigInt::zero() + Self::ZERO } } } @@ -246,7 +242,7 @@ impl From for BigInt { data: BigUint::from(n), } } else { - BigInt::zero() + Self::ZERO } } } @@ -271,7 +267,7 @@ impl From for BigInt { #[inline] fn from(n: BigUint) -> Self { if n.is_zero() { - BigInt::zero() + Self::ZERO } else { BigInt { sign: Plus, @@ -292,7 +288,7 @@ impl ToBigInt for BigUint { #[inline] fn to_bigint(&self) -> Option { if self.is_zero() { - Some(Zero::zero()) + Some(BigInt::ZERO) } else { Some(BigInt { sign: Plus, @@ -307,13 +303,12 @@ impl ToBigUint for BigInt { fn to_biguint(&self) -> Option { match self.sign() { Plus => Some(self.data.clone()), - NoSign => Some(Zero::zero()), + NoSign => Some(BigUint::ZERO), Minus => None, } } } -#[cfg(has_try_from)] impl TryFrom<&BigInt> for BigUint { type Error = TryFromBigIntError<()>; @@ -325,7 +320,6 @@ impl TryFrom<&BigInt> for BigUint { } } -#[cfg(has_try_from)] impl TryFrom for BigUint { type Error = TryFromBigIntError; @@ -372,7 +366,7 @@ impl From for BigInt { if x { One::one() } else { - Zero::zero() + Self::ZERO } } } @@ -382,7 +376,7 @@ pub(super) fn from_signed_bytes_be(digits: &[u8]) -> BigInt { let sign = match digits.first() { Some(v) if *v > 0x7f => Sign::Minus, Some(_) => Sign::Plus, - None => return BigInt::zero(), + None => return BigInt::ZERO, }; if sign == Sign::Minus { @@ -400,7 +394,7 @@ pub(super) fn from_signed_bytes_le(digits: &[u8]) -> BigInt { let sign = match digits.last() { Some(v) if *v > 0x7f => Sign::Minus, Some(_) => Sign::Plus, - None => return BigInt::zero(), + None => return BigInt::ZERO, }; if sign == Sign::Minus { diff --git a/src/bigint/division.rs b/src/bigint/division.rs index 318d1fb6..0d4d23f3 100644 --- a/src/bigint/division.rs +++ b/src/bigint/division.rs @@ -358,14 +358,14 @@ impl Rem for BigInt { #[inline] fn rem(self, other: i32) -> BigInt { - self % other.uabs() + self % other.unsigned_abs() } } impl RemAssign for BigInt { #[inline] fn rem_assign(&mut self, other: i32) { - *self %= other.uabs(); + *self %= other.unsigned_abs(); } } @@ -386,14 +386,14 @@ impl Rem for BigInt { #[inline] fn rem(self, other: i64) -> BigInt { - self % other.uabs() + self % other.unsigned_abs() } } impl RemAssign for BigInt { #[inline] fn rem_assign(&mut self, other: i64) { - *self %= other.uabs(); + *self %= other.unsigned_abs(); } } @@ -414,14 +414,14 @@ impl Rem for BigInt { #[inline] fn rem(self, other: i128) -> BigInt { - self % other.uabs() + self % other.unsigned_abs() } } impl RemAssign for BigInt { #[inline] fn rem_assign(&mut self, other: i128) { - *self %= other.uabs(); + *self %= other.unsigned_abs(); } } @@ -463,6 +463,10 @@ impl CheckedEuclid for BigInt { } Some(self.rem_euclid(v)) } + + fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> { + Some(self.div_rem_euclid(v)) + } } impl Euclid for BigInt { @@ -493,4 +497,17 @@ impl Euclid for BigInt { r } } + + fn div_rem_euclid(&self, v: &Self) -> (Self, Self) { + let (q, r) = self.div_rem(v); + if r.is_negative() { + if v.is_positive() { + (q - 1, r + v) + } else { + (q + 1, r - v) + } + } else { + (q, r) + } + } } diff --git a/src/bigint/power.rs b/src/bigint/power.rs index 4b41f4f2..ef254c89 100644 --- a/src/bigint/power.rs +++ b/src/bigint/power.rs @@ -80,7 +80,7 @@ pub(super) fn modpow(x: &BigInt, exponent: &BigInt, modulus: &BigInt) -> BigInt let result = x.data.modpow(&exponent.data, &modulus.data); if result.is_zero() { - return BigInt::zero(); + return BigInt::ZERO; } // The sign of the result follows the modulus, like `mod_floor`. diff --git a/src/bigint/subtraction.rs b/src/bigint/subtraction.rs index 548f3143..ef778549 100644 --- a/src/bigint/subtraction.rs +++ b/src/bigint/subtraction.rs @@ -7,7 +7,7 @@ use crate::{IsizePromotion, UsizePromotion}; use core::cmp::Ordering::{Equal, Greater, Less}; use core::mem; use core::ops::{Sub, SubAssign}; -use num_traits::{CheckedSub, Zero}; +use num_traits::CheckedSub; // We want to forward to BigUint::sub, but it's not clear how that will go until // we compare both sign and magnitude. So we duplicate this body for every @@ -23,7 +23,7 @@ macro_rules! bigint_sub { (Plus, Plus) | (Minus, Minus) => match $a.data.cmp(&$b.data) { Less => BigInt::from_biguint(-$a.sign, $b_data - $a_data), Greater => BigInt::from_biguint($a.sign, $a_data - $b_data), - Equal => Zero::zero(), + Equal => BigInt::ZERO, }, } }; @@ -75,7 +75,7 @@ impl Sub for BigInt { impl SubAssign<&BigInt> for BigInt { #[inline] fn sub_assign(&mut self, other: &BigInt) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n - other; } } @@ -96,7 +96,7 @@ impl Sub for BigInt { NoSign => -BigInt::from(other), Minus => -BigInt::from(self.data + other), Plus => match self.data.cmp(&From::from(other)) { - Equal => Zero::zero(), + Equal => Self::ZERO, Greater => BigInt::from(self.data - other), Less => -BigInt::from(other - self.data), }, @@ -106,7 +106,7 @@ impl Sub for BigInt { impl SubAssign for BigInt { #[inline] fn sub_assign(&mut self, other: u32) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n - other; } } @@ -147,7 +147,7 @@ impl Sub for BigInt { NoSign => -BigInt::from(other), Minus => -BigInt::from(self.data + other), Plus => match self.data.cmp(&From::from(other)) { - Equal => Zero::zero(), + Equal => Self::ZERO, Greater => BigInt::from(self.data - other), Less => -BigInt::from(other - self.data), }, @@ -158,7 +158,7 @@ impl Sub for BigInt { impl SubAssign for BigInt { #[inline] fn sub_assign(&mut self, other: u64) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n - other; } } @@ -172,7 +172,7 @@ impl Sub for BigInt { NoSign => -BigInt::from(other), Minus => -BigInt::from(self.data + other), Plus => match self.data.cmp(&From::from(other)) { - Equal => Zero::zero(), + Equal => Self::ZERO, Greater => BigInt::from(self.data - other), Less => -BigInt::from(other - self.data), }, @@ -183,7 +183,7 @@ impl Sub for BigInt { impl SubAssign for BigInt { #[inline] fn sub_assign(&mut self, other: u128) { - let n = mem::replace(self, BigInt::zero()); + let n = mem::replace(self, Self::ZERO); *self = n - other; } } diff --git a/src/bigrand.rs b/src/bigrand.rs index ec032241..0b1cb23c 100644 --- a/src/bigrand.rs +++ b/src/bigrand.rs @@ -47,42 +47,42 @@ fn gen_bits(rng: &mut R, data: &mut [u32], rem: u64) { } impl RandBigInt for R { - #[cfg(not(u64_digit))] - fn gen_biguint(&mut self, bit_size: u64) -> BigUint { - let (digits, rem) = bit_size.div_rem(&32); - let len = (digits + (rem > 0) as u64) - .to_usize() - .expect("capacity overflow"); - let mut data = vec![0u32; len]; - gen_bits(self, &mut data, rem); - biguint_from_vec(data) - } - - #[cfg(u64_digit)] - fn gen_biguint(&mut self, bit_size: u64) -> BigUint { - use core::slice; - - let (digits, rem) = bit_size.div_rem(&32); - let len = (digits + (rem > 0) as u64) - .to_usize() - .expect("capacity overflow"); - let native_digits = Integer::div_ceil(&bit_size, &64); - let native_len = native_digits.to_usize().expect("capacity overflow"); - let mut data = vec![0u64; native_len]; - unsafe { - // Generate bits in a `&mut [u32]` slice for value stability - let ptr = data.as_mut_ptr() as *mut u32; - debug_assert!(native_len * 2 >= len); - let data = slice::from_raw_parts_mut(ptr, len); - gen_bits(self, data, rem); + cfg_digit!( + fn gen_biguint(&mut self, bit_size: u64) -> BigUint { + let (digits, rem) = bit_size.div_rem(&32); + let len = (digits + (rem > 0) as u64) + .to_usize() + .expect("capacity overflow"); + let mut data = vec![0u32; len]; + gen_bits(self, &mut data, rem); + biguint_from_vec(data) } - #[cfg(target_endian = "big")] - for digit in &mut data { - // swap u32 digits into u64 endianness - *digit = (*digit << 32) | (*digit >> 32); + + fn gen_biguint(&mut self, bit_size: u64) -> BigUint { + use core::slice; + + let (digits, rem) = bit_size.div_rem(&32); + let len = (digits + (rem > 0) as u64) + .to_usize() + .expect("capacity overflow"); + let native_digits = Integer::div_ceil(&bit_size, &64); + let native_len = native_digits.to_usize().expect("capacity overflow"); + let mut data = vec![0u64; native_len]; + unsafe { + // Generate bits in a `&mut [u32]` slice for value stability + let ptr = data.as_mut_ptr() as *mut u32; + debug_assert!(native_len * 2 >= len); + let data = slice::from_raw_parts_mut(ptr, len); + gen_bits(self, data, rem); + } + #[cfg(target_endian = "big")] + for digit in &mut data { + // swap u32 digits into u64 endianness + *digit = (*digit << 32) | (*digit >> 32); + } + biguint_from_vec(data) } - biguint_from_vec(data) - } + ); fn gen_bigint(&mut self, bit_size: u64) -> BigInt { loop { diff --git a/src/biguint.rs b/src/biguint.rs index 1554eb0f..41506055 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -8,10 +8,9 @@ use core::fmt; use core::hash; use core::mem; use core::str; -use core::{u32, u64, u8}; use num_integer::{Integer, Roots}; -use num_traits::{Num, One, Pow, ToPrimitive, Unsigned, Zero}; +use num_traits::{ConstZero, Num, One, Pow, ToPrimitive, Unsigned, Zero}; mod addition; mod division; @@ -101,7 +100,7 @@ fn cmp_slice(a: &[BigDigit], b: &[BigDigit]) -> Ordering { impl Default for BigUint { #[inline] fn default() -> BigUint { - Zero::zero() + Self::ZERO } } @@ -146,7 +145,7 @@ impl fmt::Octal for BigUint { impl Zero for BigUint { #[inline] fn zero() -> BigUint { - BigUint { data: Vec::new() } + Self::ZERO } #[inline] @@ -160,6 +159,11 @@ impl Zero for BigUint { } } +impl ConstZero for BigUint { + // forward to the inherent const + const ZERO: Self = Self::ZERO; // BigUint { data: Vec::new() }; +} + impl One for BigUint { #[inline] fn one() -> BigUint { @@ -255,7 +259,7 @@ impl Integer for BigUint { #[inline] fn lcm(&self, other: &BigUint) -> BigUint { if self.is_zero() && other.is_zero() { - Self::zero() + Self::ZERO } else { self / self.gcd(other) * other } @@ -267,7 +271,7 @@ impl Integer for BigUint { fn gcd_lcm(&self, other: &Self) -> (Self, Self) { let gcd = self.gcd(other); let lcm = if gcd.is_zero() { - Self::zero() + Self::ZERO } else { self / &gcd * other }; @@ -320,6 +324,14 @@ impl Integer for BigUint { fn prev_multiple_of(&self, other: &Self) -> Self { self - self.mod_floor(other) } + + fn dec(&mut self) { + *self -= 1u32; + } + + fn inc(&mut self) { + *self += 1u32; + } } #[inline] @@ -397,7 +409,7 @@ impl Roots for BigUint { _ => { // Try to guess by scaling down such that it does fit in `f64`. // With some (x * 2ⁿᵏ), its nth root ≈ (ⁿ√x * 2ᵏ) - let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1); + let extra_bits = bits - (f64::MAX_EXP as u64 - 1); let root_scale = Integer::div_ceil(&extra_bits, &n64); let scale = root_scale * n64; if scale < bits && bits - scale > n64 { @@ -445,7 +457,7 @@ impl Roots for BigUint { _ => { // Try to guess by scaling down such that it does fit in `f64`. // With some (x * 2²ᵏ), its sqrt ≈ (√x * 2ᵏ) - let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1); + let extra_bits = bits - (f64::MAX_EXP as u64 - 1); let root_scale = (extra_bits + 1) / 2; let scale = root_scale * 2; (self >> scale).sqrt() << root_scale @@ -486,7 +498,7 @@ impl Roots for BigUint { _ => { // Try to guess by scaling down such that it does fit in `f64`. // With some (x * 2³ᵏ), its cbrt ≈ (∛x * 2ᵏ) - let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1); + let extra_bits = bits - (f64::MAX_EXP as u64 - 1); let root_scale = (extra_bits + 2) / 3; let scale = root_scale * 3; (self >> scale).cbrt() << root_scale @@ -519,21 +531,23 @@ pub(crate) fn biguint_from_vec(digits: Vec) -> BigUint { } impl BigUint { + /// A constant `BigUint` with value 0, useful for static initialization. + pub const ZERO: Self = BigUint { data: Vec::new() }; + /// Creates and initializes a [`BigUint`]. /// /// The base 232 digits are ordered least significant digit first. #[inline] pub fn new(digits: Vec) -> BigUint { - let mut big = BigUint::zero(); - - #[cfg(not(u64_digit))] - { - big.data = digits; - big.normalize(); - } - - #[cfg(u64_digit)] - big.assign_from_slice(&digits); + let mut big = Self::ZERO; + + cfg_digit_expr!( + { + big.data = digits; + big.normalize(); + }, + big.assign_from_slice(&digits) + ); big } @@ -543,7 +557,7 @@ impl BigUint { /// The base 232 digits are ordered least significant digit first. #[inline] pub fn from_slice(slice: &[u32]) -> BigUint { - let mut big = BigUint::zero(); + let mut big = Self::ZERO; big.assign_from_slice(slice); big } @@ -555,11 +569,10 @@ impl BigUint { pub fn assign_from_slice(&mut self, slice: &[u32]) { self.data.clear(); - #[cfg(not(u64_digit))] - self.data.extend_from_slice(slice); - - #[cfg(u64_digit)] - self.data.extend(slice.chunks(2).map(u32_chunk_to_u64)); + cfg_digit_expr!( + self.data.extend_from_slice(slice), + self.data.extend(slice.chunks(2).map(u32_chunk_to_u64)) + ); self.normalize(); } @@ -585,7 +598,7 @@ impl BigUint { #[inline] pub fn from_bytes_be(bytes: &[u8]) -> BigUint { if bytes.is_empty() { - Zero::zero() + Self::ZERO } else { let mut v = bytes.to_vec(); v.reverse(); @@ -599,7 +612,7 @@ impl BigUint { #[inline] pub fn from_bytes_le(bytes: &[u8]) -> BigUint { if bytes.is_empty() { - Zero::zero() + Self::ZERO } else { convert::from_bitwise_digits_le(bytes, 8) } @@ -877,6 +890,86 @@ impl BigUint { power::modpow(self, exponent, modulus) } + /// Returns the modular multiplicative inverse if it exists, otherwise `None`. + /// + /// This solves for `x` in the interval `[0, modulus)` such that `self * x ≡ 1 (mod modulus)`. + /// The solution exists if and only if `gcd(self, modulus) == 1`. + /// + /// ``` + /// use num_bigint::BigUint; + /// use num_traits::{One, Zero}; + /// + /// let m = BigUint::from(383_u32); + /// + /// // Trivial cases + /// assert_eq!(BigUint::zero().modinv(&m), None); + /// assert_eq!(BigUint::one().modinv(&m), Some(BigUint::one())); + /// let neg1 = &m - 1u32; + /// assert_eq!(neg1.modinv(&m), Some(neg1)); + /// + /// let a = BigUint::from(271_u32); + /// let x = a.modinv(&m).unwrap(); + /// assert_eq!(x, BigUint::from(106_u32)); + /// assert_eq!(x.modinv(&m).unwrap(), a); + /// assert!((a * x % m).is_one()); + /// ``` + pub fn modinv(&self, modulus: &Self) -> Option { + // Based on the inverse pseudocode listed here: + // https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers + // TODO: consider Binary or Lehmer's GCD algorithms for optimization. + + assert!( + !modulus.is_zero(), + "attempt to calculate with zero modulus!" + ); + if modulus.is_one() { + return Some(Self::zero()); + } + + let mut r0; // = modulus.clone(); + let mut r1 = self % modulus; + let mut t0; // = Self::zero(); + let mut t1; // = Self::one(); + + // Lift and simplify the first iteration to avoid some initial allocations. + if r1.is_zero() { + return None; + } else if r1.is_one() { + return Some(r1); + } else { + let (q, r2) = modulus.div_rem(&r1); + if r2.is_zero() { + return None; + } + r0 = r1; + r1 = r2; + t0 = Self::one(); + t1 = modulus - q; + } + + while !r1.is_zero() { + let (q, r2) = r0.div_rem(&r1); + r0 = r1; + r1 = r2; + + // let t2 = (t0 - q * t1) % modulus; + let qt1 = q * &t1 % modulus; + let t2 = if t0 < qt1 { + t0 + (modulus - qt1) + } else { + t0 - qt1 + }; + t0 = t1; + t1 = t2; + } + + if r0.is_one() { + Some(t0) + } else { + None + } + } + /// Returns the truncated principal square root of `self` -- /// see [Roots::sqrt](https://docs.rs/num-integer/0.1/num_integer/trait.Roots.html#method.sqrt) pub fn sqrt(&self) -> Self { @@ -906,10 +999,7 @@ impl BigUint { /// Returns the number of least-significant bits that are ones. pub fn trailing_ones(&self) -> u64 { if let Some(i) = self.data.iter().position(|&digit| !digit != 0) { - // XXX u64::trailing_ones() introduced in Rust 1.46, - // but we need to be compatible further back. - // Thanks to cuviper for this workaround. - let ones: u64 = (!self.data[i]).trailing_zeros().into(); + let ones: u64 = self.data[i].trailing_ones().into(); i as u64 * u64::from(big_digit::BITS) + ones } else { self.data.len() as u64 * u64::from(big_digit::BITS) @@ -941,9 +1031,7 @@ impl BigUint { // Note: we're saturating `digit_index` and `new_len` -- any such case is guaranteed to // fail allocation, and that's more consistent than adding our own overflow panics. let bits_per_digit = u64::from(big_digit::BITS); - let digit_index = (bit / bits_per_digit) - .to_usize() - .unwrap_or(core::usize::MAX); + let digit_index = (bit / bits_per_digit).to_usize().unwrap_or(usize::MAX); let bit_mask = (1 as BigDigit) << (bit % bits_per_digit); if value { if digit_index >= self.data.len() { @@ -1025,86 +1113,77 @@ fn u32_chunk_to_u64(chunk: &[u32]) -> u64 { digit } -/// Combine four `u32`s into a single `u128`. -#[cfg(any(test, not(u64_digit)))] -#[inline] -fn u32_to_u128(a: u32, b: u32, c: u32, d: u32) -> u128 { - u128::from(d) | (u128::from(c) << 32) | (u128::from(b) << 64) | (u128::from(a) << 96) -} - -/// Split a single `u128` into four `u32`. -#[cfg(any(test, not(u64_digit)))] -#[inline] -fn u32_from_u128(n: u128) -> (u32, u32, u32, u32) { - ( - (n >> 96) as u32, - (n >> 64) as u32, - (n >> 32) as u32, - n as u32, - ) -} +cfg_32_or_test!( + /// Combine four `u32`s into a single `u128`. + #[inline] + fn u32_to_u128(a: u32, b: u32, c: u32, d: u32) -> u128 { + u128::from(d) | (u128::from(c) << 32) | (u128::from(b) << 64) | (u128::from(a) << 96) + } +); -#[cfg(not(u64_digit))] -#[test] -fn test_from_slice() { - fn check(slice: &[u32], data: &[BigDigit]) { - assert_eq!(BigUint::from_slice(slice).data, data); - } - check(&[1], &[1]); - check(&[0, 0, 0], &[]); - check(&[1, 2, 0, 0], &[1, 2]); - check(&[0, 0, 1, 2], &[0, 0, 1, 2]); - check(&[0, 0, 1, 2, 0, 0], &[0, 0, 1, 2]); - check(&[-1i32 as u32], &[-1i32 as BigDigit]); -} +cfg_32_or_test!( + /// Split a single `u128` into four `u32`. + #[inline] + fn u32_from_u128(n: u128) -> (u32, u32, u32, u32) { + ( + (n >> 96) as u32, + (n >> 64) as u32, + (n >> 32) as u32, + n as u32, + ) + } +); -#[cfg(u64_digit)] -#[test] -fn test_from_slice() { - fn check(slice: &[u32], data: &[BigDigit]) { - assert_eq!( - BigUint::from_slice(slice).data, - data, - "from {:?}, to {:?}", - slice, - data - ); +cfg_digit!( + #[test] + fn test_from_slice() { + fn check(slice: &[u32], data: &[BigDigit]) { + assert_eq!(BigUint::from_slice(slice).data, data); + } + check(&[1], &[1]); + check(&[0, 0, 0], &[]); + check(&[1, 2, 0, 0], &[1, 2]); + check(&[0, 0, 1, 2], &[0, 0, 1, 2]); + check(&[0, 0, 1, 2, 0, 0], &[0, 0, 1, 2]); + check(&[-1i32 as u32], &[-1i32 as BigDigit]); + } + + #[test] + fn test_from_slice() { + fn check(slice: &[u32], data: &[BigDigit]) { + assert_eq!( + BigUint::from_slice(slice).data, + data, + "from {:?}, to {:?}", + slice, + data + ); + } + check(&[1], &[1]); + check(&[0, 0, 0], &[]); + check(&[1, 2], &[8_589_934_593]); + check(&[1, 2, 0, 0], &[8_589_934_593]); + check(&[0, 0, 1, 2], &[0, 8_589_934_593]); + check(&[0, 0, 1, 2, 0, 0], &[0, 8_589_934_593]); + check(&[-1i32 as u32], &[(-1i32 as u32) as BigDigit]); } - check(&[1], &[1]); - check(&[0, 0, 0], &[]); - check(&[1, 2], &[8_589_934_593]); - check(&[1, 2, 0, 0], &[8_589_934_593]); - check(&[0, 0, 1, 2], &[0, 8_589_934_593]); - check(&[0, 0, 1, 2, 0, 0], &[0, 8_589_934_593]); - check(&[-1i32 as u32], &[(-1i32 as u32) as BigDigit]); -} +); #[test] fn test_u32_u128() { assert_eq!(u32_from_u128(0u128), (0, 0, 0, 0)); assert_eq!( - u32_from_u128(u128::max_value()), - ( - u32::max_value(), - u32::max_value(), - u32::max_value(), - u32::max_value() - ) + u32_from_u128(u128::MAX), + (u32::MAX, u32::MAX, u32::MAX, u32::MAX) ); - assert_eq!( - u32_from_u128(u32::max_value() as u128), - (0, 0, 0, u32::max_value()) - ); + assert_eq!(u32_from_u128(u32::MAX as u128), (0, 0, 0, u32::MAX)); - assert_eq!( - u32_from_u128(u64::max_value() as u128), - (0, 0, u32::max_value(), u32::max_value()) - ); + assert_eq!(u32_from_u128(u64::MAX as u128), (0, 0, u32::MAX, u32::MAX)); assert_eq!( - u32_from_u128((u64::max_value() as u128) + u32::max_value() as u128), - (0, 1, 0, u32::max_value() - 1) + u32_from_u128((u64::MAX as u128) + u32::MAX as u128), + (0, 1, 0, u32::MAX - 1) ); assert_eq!(u32_from_u128(36_893_488_151_714_070_528), (0, 2, 1, 0)); @@ -1116,11 +1195,11 @@ fn test_u128_u32_roundtrip() { let values = vec![ 0u128, 1u128, - u64::max_value() as u128 * 3, - u32::max_value() as u128, - u64::max_value() as u128, - (u64::max_value() as u128) + u32::max_value() as u128, - u128::max_value(), + u64::MAX as u128 * 3, + u32::MAX as u128, + u64::MAX as u128, + (u64::MAX as u128) + u32::MAX as u128, + u128::MAX, ]; for val in &values { diff --git a/src/biguint/addition.rs b/src/biguint/addition.rs index ac6c0dde..a054e66d 100644 --- a/src/biguint/addition.rs +++ b/src/biguint/addition.rs @@ -1,5 +1,3 @@ -#[cfg(not(u64_digit))] -use super::u32_from_u128; use super::{BigUint, IntDigits}; use crate::big_digit::{self, BigDigit}; @@ -7,33 +5,27 @@ use crate::UsizePromotion; use core::iter::Sum; use core::ops::{Add, AddAssign}; -use num_traits::{CheckedAdd, Zero}; - -#[cfg(all(use_addcarry, target_arch = "x86_64"))] -use core::arch::x86_64 as arch; - -#[cfg(all(use_addcarry, target_arch = "x86"))] -use core::arch::x86 as arch; +use num_traits::CheckedAdd; // Add with carry: -#[cfg(all(use_addcarry, u64_digit))] +#[cfg(target_arch = "x86_64")] #[inline] fn adc(carry: u8, a: u64, b: u64, out: &mut u64) -> u8 { // Safety: There are absolutely no safety concerns with calling `_addcarry_u64`. // It's just unsafe for API consistency with other intrinsics. - unsafe { arch::_addcarry_u64(carry, a, b, out) } + unsafe { core::arch::x86_64::_addcarry_u64(carry, a, b, out) } } -#[cfg(all(use_addcarry, not(u64_digit)))] +#[cfg(target_arch = "x86")] #[inline] fn adc(carry: u8, a: u32, b: u32, out: &mut u32) -> u8 { // Safety: There are absolutely no safety concerns with calling `_addcarry_u32`. // It's just unsafe for API consistency with other intrinsics. - unsafe { arch::_addcarry_u32(carry, a, b, out) } + unsafe { core::arch::x86::_addcarry_u32(carry, a, b, out) } } // fallback for environments where we don't have an addcarry intrinsic -#[cfg(not(use_addcarry))] +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] #[inline] fn adc(carry: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 { use crate::big_digit::DoubleBigDigit; @@ -154,38 +146,38 @@ impl Add for BigUint { } impl AddAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn add_assign(&mut self, other: u64) { - let (hi, lo) = big_digit::from_doublebigdigit(other); - if hi == 0 { - *self += lo; - } else { - while self.data.len() < 2 { - self.data.push(0); - } + cfg_digit!( + #[inline] + fn add_assign(&mut self, other: u64) { + let (hi, lo) = big_digit::from_doublebigdigit(other); + if hi == 0 { + *self += lo; + } else { + while self.data.len() < 2 { + self.data.push(0); + } - let carry = __add2(&mut self.data, &[lo, hi]); - if carry != 0 { - self.data.push(carry); + let carry = __add2(&mut self.data, &[lo, hi]); + if carry != 0 { + self.data.push(carry); + } } } - } - #[cfg(u64_digit)] - #[inline] - fn add_assign(&mut self, other: u64) { - if other != 0 { - if self.data.is_empty() { - self.data.push(0); - } + #[inline] + fn add_assign(&mut self, other: u64) { + if other != 0 { + if self.data.is_empty() { + self.data.push(0); + } - let carry = __add2(&mut self.data, &[other as BigDigit]); - if carry != 0 { - self.data.push(carry); + let carry = __add2(&mut self.data, &[other as BigDigit]); + if carry != 0 { + self.data.push(carry); + } } } - } + ); } impl Add for BigUint { @@ -199,49 +191,49 @@ impl Add for BigUint { } impl AddAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn add_assign(&mut self, other: u128) { - if other <= u128::from(u64::max_value()) { - *self += other as u64 - } else { - let (a, b, c, d) = u32_from_u128(other); - let carry = if a > 0 { - while self.data.len() < 4 { - self.data.push(0); - } - __add2(&mut self.data, &[d, c, b, a]) + cfg_digit!( + #[inline] + fn add_assign(&mut self, other: u128) { + if other <= u128::from(u64::MAX) { + *self += other as u64 } else { - debug_assert!(b > 0); - while self.data.len() < 3 { - self.data.push(0); + let (a, b, c, d) = super::u32_from_u128(other); + let carry = if a > 0 { + while self.data.len() < 4 { + self.data.push(0); + } + __add2(&mut self.data, &[d, c, b, a]) + } else { + debug_assert!(b > 0); + while self.data.len() < 3 { + self.data.push(0); + } + __add2(&mut self.data, &[d, c, b]) + }; + + if carry != 0 { + self.data.push(carry); } - __add2(&mut self.data, &[d, c, b]) - }; - - if carry != 0 { - self.data.push(carry); } } - } - #[cfg(u64_digit)] - #[inline] - fn add_assign(&mut self, other: u128) { - let (hi, lo) = big_digit::from_doublebigdigit(other); - if hi == 0 { - *self += lo; - } else { - while self.data.len() < 2 { - self.data.push(0); - } + #[inline] + fn add_assign(&mut self, other: u128) { + let (hi, lo) = big_digit::from_doublebigdigit(other); + if hi == 0 { + *self += lo; + } else { + while self.data.len() < 2 { + self.data.push(0); + } - let carry = __add2(&mut self.data, &[lo, hi]); - if carry != 0 { - self.data.push(carry); + let carry = __add2(&mut self.data, &[lo, hi]); + if carry != 0 { + self.data.push(carry); + } } } - } + ); } impl CheckedAdd for BigUint { diff --git a/src/biguint/convert.rs b/src/biguint/convert.rs index f19bc758..18c74bb3 100644 --- a/src/biguint/convert.rs +++ b/src/biguint/convert.rs @@ -4,17 +4,15 @@ use super::{biguint_from_vec, BigUint, ToBigUint}; use super::addition::add2; -use super::division::div_rem_digit; +use super::division::{div_rem_digit, FAST_DIV_WIDE}; use super::multiplication::mac_with_carry; use crate::big_digit::{self, BigDigit}; use crate::std_alloc::Vec; use crate::ParseBigIntError; -#[cfg(has_try_from)] use crate::TryFromBigIntError; use core::cmp::Ordering::{Equal, Greater, Less}; -#[cfg(has_try_from)] use core::convert::TryFrom; use core::mem; use core::str::FromStr; @@ -71,7 +69,7 @@ fn from_inexact_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint { let total_bits = (v.len() as u64).saturating_mul(bits.into()); let big_digits = Integer::div_ceil(&total_bits, &big_digit::BITS.into()) .to_usize() - .unwrap_or(core::usize::MAX); + .unwrap_or(usize::MAX); let mut data = Vec::with_capacity(big_digits); let mut d = 0; @@ -121,7 +119,7 @@ fn from_radix_digits_be(v: &[u8], radix: u32) -> BigUint { let mut data = Vec::with_capacity(big_digits.to_usize().unwrap_or(0)); - let (base, power) = get_radix_base(radix, big_digit::BITS); + let (base, power) = get_radix_base(radix); let radix = radix as BigDigit; let r = v.len() % power; @@ -161,7 +159,7 @@ pub(super) fn from_radix_be(buf: &[u8], radix: u32) -> Option { ); if buf.is_empty() { - return Some(Zero::zero()); + return Some(BigUint::ZERO); } if radix != 256 && buf.iter().any(|&b| b >= radix as u8) { @@ -192,7 +190,7 @@ pub(super) fn from_radix_le(buf: &[u8], radix: u32) -> Option { ); if buf.is_empty() { - return Some(Zero::zero()); + return Some(BigUint::ZERO); } if radix != 256 && buf.iter().any(|&b| b >= radix as u8) { @@ -247,7 +245,7 @@ impl Num for BigUint { b'a'..=b'z' => b - b'a' + 10, b'A'..=b'Z' => b - b'A' + 10, b'_' => continue, - _ => core::u8::MAX, + _ => u8::MAX, }; if d < radix as u8 { v.push(d); @@ -374,8 +372,8 @@ impl ToPrimitive for BigUint { let mantissa = high_bits_to_u64(self); let exponent = self.bits() - u64::from(fls(mantissa)); - if exponent > core::f32::MAX_EXP as u64 { - Some(core::f32::INFINITY) + if exponent > f32::MAX_EXP as u64 { + Some(f32::INFINITY) } else { Some((mantissa as f32) * 2.0f32.powi(exponent as i32)) } @@ -386,8 +384,8 @@ impl ToPrimitive for BigUint { let mantissa = high_bits_to_u64(self); let exponent = self.bits() - u64::from(fls(mantissa)); - if exponent > core::f64::MAX_EXP as u64 { - Some(core::f64::INFINITY) + if exponent > f64::MAX_EXP as u64 { + Some(f64::INFINITY) } else { Some((mantissa as f64) * 2.0f64.powi(exponent as i32)) } @@ -396,7 +394,6 @@ impl ToPrimitive for BigUint { macro_rules! impl_try_from_biguint { ($T:ty, $to_ty:path) => { - #[cfg(has_try_from)] impl TryFrom<&BigUint> for $T { type Error = TryFromBigIntError<()>; @@ -406,7 +403,6 @@ macro_rules! impl_try_from_biguint { } } - #[cfg(has_try_from)] impl TryFrom for $T { type Error = TryFromBigIntError; @@ -473,7 +469,7 @@ impl FromPrimitive for BigUint { // handle 0.x, -0.x if n.is_zero() { - return Some(BigUint::zero()); + return Some(Self::ZERO); } let (mantissa, exponent, sign) = FloatCore::integer_decode(n); @@ -495,7 +491,7 @@ impl FromPrimitive for BigUint { impl From for BigUint { #[inline] fn from(mut n: u64) -> Self { - let mut ret: BigUint = Zero::zero(); + let mut ret: BigUint = Self::ZERO; while n != 0 { ret.data.push(n as BigDigit); @@ -510,7 +506,7 @@ impl From for BigUint { impl From for BigUint { #[inline] fn from(mut n: u128) -> Self { - let mut ret: BigUint = Zero::zero(); + let mut ret: BigUint = Self::ZERO; while n != 0 { ret.data.push(n as BigDigit); @@ -539,7 +535,6 @@ impl_biguint_from_uint!(usize); macro_rules! impl_biguint_try_from_int { ($T:ty, $from_ty:path) => { - #[cfg(has_try_from)] impl TryFrom<$T> for BigUint { type Error = TryFromBigIntError<()>; @@ -598,7 +593,7 @@ impl From for BigUint { if x { One::one() } else { - Zero::zero() + Self::ZERO } } } @@ -612,7 +607,7 @@ pub(super) fn to_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec { let digits_per_big_digit = big_digit::BITS / bits; let digits = Integer::div_ceil(&u.bits(), &u64::from(bits)) .to_usize() - .unwrap_or(core::usize::MAX); + .unwrap_or(usize::MAX); let mut res = Vec::with_capacity(digits); for mut r in u.data[..last_i].iter().cloned() { @@ -638,7 +633,7 @@ fn to_inexact_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec { let mask: BigDigit = (1 << bits) - 1; let digits = Integer::div_ceil(&u.bits(), &u64::from(bits)) .to_usize() - .unwrap_or(core::usize::MAX); + .unwrap_or(usize::MAX); let mut res = Vec::with_capacity(digits); let mut r = 0; @@ -693,7 +688,13 @@ pub(super) fn to_radix_digits_le(u: &BigUint, radix: u32) -> Vec { let mut digits = u.clone(); - let (base, power) = get_radix_base(radix, big_digit::HALF_BITS); + // X86 DIV can quickly divide by a full digit, otherwise we choose a divisor + // that's suitable for `div_half` to avoid slow `DoubleBigDigit` division. + let (base, power) = if FAST_DIV_WIDE { + get_radix_base(radix) + } else { + get_half_radix_base(radix) + }; let radix = radix as BigDigit; // For very large numbers, the O(n²) loop of repeated `div_rem_digit` dominates the @@ -701,8 +702,8 @@ pub(super) fn to_radix_digits_le(u: &BigUint, radix: u32) -> Vec { // The threshold for this was chosen by anecdotal performance measurements to // approximate where this starts to make a noticeable difference. if digits.data.len() >= 64 { - let mut big_base = BigUint::from(base * base); - let mut big_power = 2usize; + let mut big_base = BigUint::from(base); + let mut big_power = 1usize; // Choose a target base length near √n. let target_len = digits.data.len().sqrt(); @@ -788,33 +789,79 @@ pub(crate) fn to_str_radix_reversed(u: &BigUint, radix: u32) -> Vec { res } -/// Returns the greatest power of the radix for the given bit size +/// Returns the greatest power of the radix for the `BigDigit` bit size #[inline] -fn get_radix_base(radix: u32, bits: u8) -> (BigDigit, usize) { - mod gen { - include! { concat!(env!("OUT_DIR"), "/radix_bases.rs") } - } +fn get_radix_base(radix: u32) -> (BigDigit, usize) { + static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::MAX); + debug_assert!(!radix.is_power_of_two()); + debug_assert!((3..256).contains(&radix)); + BASES[radix as usize] +} - debug_assert!( - 2 <= radix && radix <= 256, - "The radix must be within 2...256" - ); +/// Returns the greatest power of the radix for half the `BigDigit` bit size +#[inline] +fn get_half_radix_base(radix: u32) -> (BigDigit, usize) { + static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::HALF); debug_assert!(!radix.is_power_of_two()); - debug_assert!(bits <= big_digit::BITS); + debug_assert!((3..256).contains(&radix)); + BASES[radix as usize] +} - match bits { - 16 => { - let (base, power) = gen::BASES_16[radix as usize]; - (base as BigDigit, power) +/// Generate tables of the greatest power of each radix that is less that the given maximum. These +/// are returned from `get_radix_base` to batch the multiplication/division of radix conversions on +/// full `BigUint` values, operating on primitive integers as much as possible. +/// +/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big +/// BASES_32[3] = (3486784401, 20) +/// BASES_64[3] = (12157665459056928801, 40) +/// +/// Powers of two are not included, just zeroed, as they're implemented with shifts. +const fn generate_radix_bases(max: BigDigit) -> [(BigDigit, usize); 257] { + let mut bases = [(0, 0); 257]; + + let mut radix: BigDigit = 3; + while radix < 256 { + if !radix.is_power_of_two() { + let mut power = 1; + let mut base = radix; + + while let Some(b) = base.checked_mul(radix) { + if b > max { + break; + } + base = b; + power += 1; + } + bases[radix as usize] = (base, power) } - 32 => { - let (base, power) = gen::BASES_32[radix as usize]; - (base as BigDigit, power) + radix += 1; + } + + bases +} + +#[test] +fn test_radix_bases() { + for radix in 3u32..256 { + if !radix.is_power_of_two() { + let (base, power) = get_radix_base(radix); + let radix = BigDigit::from(radix); + let power = u32::try_from(power).unwrap(); + assert_eq!(base, radix.pow(power)); + assert!(radix.checked_pow(power + 1).is_none()); } - 64 => { - let (base, power) = gen::BASES_64[radix as usize]; - (base as BigDigit, power) + } +} + +#[test] +fn test_half_radix_bases() { + for radix in 3u32..256 { + if !radix.is_power_of_two() { + let (base, power) = get_half_radix_base(radix); + let radix = BigDigit::from(radix); + let power = u32::try_from(power).unwrap(); + assert_eq!(base, radix.pow(power)); + assert!(radix.pow(power + 1) > big_digit::HALF); } - _ => panic!("Invalid bigdigit size"), } } diff --git a/src/biguint/division.rs b/src/biguint/division.rs index 29998382..adf9db89 100644 --- a/src/biguint/division.rs +++ b/src/biguint/division.rs @@ -1,6 +1,4 @@ use super::addition::__add2; -#[cfg(not(u64_digit))] -use super::u32_to_u128; use super::{cmp_slice, BigUint}; use crate::big_digit::{self, BigDigit, DoubleBigDigit}; @@ -12,12 +10,15 @@ use core::ops::{Div, DivAssign, Rem, RemAssign}; use num_integer::Integer; use num_traits::{CheckedDiv, CheckedEuclid, Euclid, One, ToPrimitive, Zero}; +pub(super) const FAST_DIV_WIDE: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64")); + /// Divide a two digit numerator by a one digit divisor, returns quotient and remainder: /// /// Note: the caller must ensure that both the quotient and remainder will fit into a single digit. /// This is _not_ true for an arbitrary numerator/denominator. /// /// (This function also matches what the x86 divide instruction does). +#[cfg(any(miri, not(any(target_arch = "x86", target_arch = "x86_64"))))] #[inline] fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigit) { debug_assert!(hi < divisor); @@ -27,6 +28,34 @@ fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigi ((lhs / rhs) as BigDigit, (lhs % rhs) as BigDigit) } +/// x86 and x86_64 can use a real `div` instruction. +#[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))] +#[inline] +fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigit) { + // This debug assertion covers the potential #DE for divisor==0 or a quotient too large for one + // register, otherwise in release mode it will become a target-specific fault like SIGFPE. + // This should never occur with the inputs from our few `div_wide` callers. + debug_assert!(hi < divisor); + + // SAFETY: The `div` instruction only affects registers, reading the explicit operand as the + // divisor, and implicitly reading RDX:RAX or EDX:EAX as the dividend. The result is implicitly + // written back to RAX or EAX for the quotient and RDX or EDX for the remainder. No memory is + // used, and flags are not preserved. + unsafe { + let (div, rem); + + core::arch::asm!( + "div {}", + in(reg) divisor, + inout("dx") hi => rem, + inout("ax") lo => div, + options(pure, nomem, nostack), + ); + + (div, rem) + } +} + /// For small divisors, we can divide without promoting to `DoubleBigDigit` by /// using half-size pieces of digit, like long-division. #[inline] @@ -47,7 +76,7 @@ pub(super) fn div_rem_digit(mut a: BigUint, b: BigDigit) -> (BigUint, BigDigit) let mut rem = 0; - if b <= big_digit::HALF { + if !FAST_DIV_WIDE && b <= big_digit::HALF { for d in a.data.iter_mut().rev() { let (q, r) = div_half(rem, *d, b); *d = q; @@ -72,7 +101,7 @@ fn rem_digit(a: &BigUint, b: BigDigit) -> BigDigit { let mut rem = 0; - if b <= big_digit::HALF { + if !FAST_DIV_WIDE && b <= big_digit::HALF { for &digit in a.data.iter().rev() { let (_, r) = div_half(rem, digit, b); rem = r; @@ -121,12 +150,12 @@ fn div_rem(mut u: BigUint, mut d: BigUint) -> (BigUint, BigUint) { panic!("attempt to divide by zero") } if u.is_zero() { - return (Zero::zero(), Zero::zero()); + return (BigUint::ZERO, BigUint::ZERO); } if d.data.len() == 1 { if d.data == [1] { - return (u, Zero::zero()); + return (u, BigUint::ZERO); } let (div, rem) = div_rem_digit(u, d.data[0]); // reuse d @@ -137,10 +166,10 @@ fn div_rem(mut u: BigUint, mut d: BigUint) -> (BigUint, BigUint) { // Required or the q_len calculation below can underflow: match u.cmp(&d) { - Less => return (Zero::zero(), u), + Less => return (BigUint::ZERO, u), Equal => { u.set_one(); - return (u, Zero::zero()); + return (u, BigUint::ZERO); } Greater => {} // Do nothing } @@ -168,12 +197,12 @@ pub(super) fn div_rem_ref(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { panic!("attempt to divide by zero") } if u.is_zero() { - return (Zero::zero(), Zero::zero()); + return (BigUint::ZERO, BigUint::ZERO); } if d.data.len() == 1 { if d.data == [1] { - return (u.clone(), Zero::zero()); + return (u.clone(), BigUint::ZERO); } let (div, rem) = div_rem_digit(u.clone(), d.data[0]); @@ -182,8 +211,8 @@ pub(super) fn div_rem_ref(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { // Required or the q_len calculation below can underflow: match u.cmp(d) { - Less => return (Zero::zero(), u.clone()), - Equal => return (One::one(), Zero::zero()), + Less => return (BigUint::ZERO, u.clone()), + Equal => return (One::one(), BigUint::ZERO), Greater => {} // Do nothing } @@ -232,7 +261,7 @@ fn div_rem_core(mut a: BigUint, b: &[BigDigit]) -> (BigUint, BigUint) { let mut a0 = 0; // [b1, b0] are the two most significant digits of the divisor. They never change. - let b0 = *b.last().unwrap(); + let b0 = b[b.len() - 1]; let b1 = b[b.len() - 2]; let q_len = a.data.len() - b.len() + 1; @@ -360,7 +389,7 @@ impl Div for u32 { match other.data.len() { 0 => panic!("attempt to divide by zero"), 1 => From::from(self as BigDigit / other.data[0]), - _ => Zero::zero(), + _ => BigUint::ZERO, } } } @@ -378,7 +407,7 @@ impl DivAssign for BigUint { #[inline] fn div_assign(&mut self, other: u64) { // a vec of size 0 does not allocate, so this is fairly cheap - let temp = mem::replace(self, Zero::zero()); + let temp = mem::replace(self, Self::ZERO); *self = temp / other; } } @@ -386,26 +415,26 @@ impl DivAssign for BigUint { impl Div for u64 { type Output = BigUint; - #[cfg(not(u64_digit))] - #[inline] - fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / u64::from(other.data[0])), - 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), - _ => Zero::zero(), + cfg_digit!( + #[inline] + fn div(self, other: BigUint) -> BigUint { + match other.data.len() { + 0 => panic!("attempt to divide by zero"), + 1 => From::from(self / u64::from(other.data[0])), + 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), + _ => BigUint::ZERO, + } } - } - #[cfg(u64_digit)] - #[inline] - fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / other.data[0]), - _ => Zero::zero(), + #[inline] + fn div(self, other: BigUint) -> BigUint { + match other.data.len() { + 0 => panic!("attempt to divide by zero"), + 1 => From::from(self / other.data[0]), + _ => BigUint::ZERO, + } } - } + ); } impl Div for BigUint { @@ -428,33 +457,34 @@ impl DivAssign for BigUint { impl Div for u128 { type Output = BigUint; - #[cfg(not(u64_digit))] - #[inline] - fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / u128::from(other.data[0])), - 2 => From::from( - self / u128::from(big_digit::to_doublebigdigit(other.data[1], other.data[0])), - ), - 3 => From::from(self / u32_to_u128(0, other.data[2], other.data[1], other.data[0])), - 4 => From::from( - self / u32_to_u128(other.data[3], other.data[2], other.data[1], other.data[0]), - ), - _ => Zero::zero(), + cfg_digit!( + #[inline] + fn div(self, other: BigUint) -> BigUint { + use super::u32_to_u128; + match other.data.len() { + 0 => panic!("attempt to divide by zero"), + 1 => From::from(self / u128::from(other.data[0])), + 2 => From::from( + self / u128::from(big_digit::to_doublebigdigit(other.data[1], other.data[0])), + ), + 3 => From::from(self / u32_to_u128(0, other.data[2], other.data[1], other.data[0])), + 4 => From::from( + self / u32_to_u128(other.data[3], other.data[2], other.data[1], other.data[0]), + ), + _ => BigUint::ZERO, + } } - } - #[cfg(u64_digit)] - #[inline] - fn div(self, other: BigUint) -> BigUint { - match other.data.len() { - 0 => panic!("attempt to divide by zero"), - 1 => From::from(self / other.data[0] as u128), - 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), - _ => Zero::zero(), + #[inline] + fn div(self, other: BigUint) -> BigUint { + match other.data.len() { + 0 => panic!("attempt to divide by zero"), + 1 => From::from(self / other.data[0] as u128), + 2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])), + _ => BigUint::ZERO, + } } - } + ); } forward_val_ref_binop!(impl Rem for BigUint, rem); @@ -635,6 +665,10 @@ impl CheckedEuclid for BigUint { } Some(self.rem_euclid(v)) } + + fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> { + Some(self.div_rem_euclid(v)) + } } impl Euclid for BigUint { @@ -649,4 +683,9 @@ impl Euclid for BigUint { // trivially same as regular remainder self % v } + + fn div_rem_euclid(&self, v: &Self) -> (Self, Self) { + // trivially same as regular division and remainder + self.div_rem(v) + } } diff --git a/src/biguint/iter.rs b/src/biguint/iter.rs index 1e673e45..066c9c1e 100644 --- a/src/biguint/iter.rs +++ b/src/biguint/iter.rs @@ -1,268 +1,271 @@ use core::iter::FusedIterator; -#[cfg(not(u64_digit))] -use super::u32_chunk_to_u64; - -/// An iterator of `u32` digits representation of a `BigUint` or `BigInt`, -/// ordered least significant digit first. -pub struct U32Digits<'a> { - #[cfg(u64_digit)] - data: &'a [u64], - #[cfg(u64_digit)] - next_is_lo: bool, - #[cfg(u64_digit)] - last_hi_is_zero: bool, - - #[cfg(not(u64_digit))] - it: core::slice::Iter<'a, u32>, -} - -#[cfg(u64_digit)] -impl<'a> U32Digits<'a> { - #[inline] - pub(super) fn new(data: &'a [u64]) -> Self { - let last_hi_is_zero = data - .last() - .map(|&last| { - let last_hi = (last >> 32) as u32; - last_hi == 0 - }) - .unwrap_or(false); - U32Digits { - data, - next_is_lo: true, - last_hi_is_zero, - } +cfg_digit!( + /// An iterator of `u32` digits representation of a `BigUint` or `BigInt`, + /// ordered least significant digit first. + pub struct U32Digits<'a> { + it: core::slice::Iter<'a, u32>, } -} -#[cfg(u64_digit)] -impl Iterator for U32Digits<'_> { - type Item = u32; - #[inline] - fn next(&mut self) -> Option { - match self.data.split_first() { - Some((&first, data)) => { - let next_is_lo = self.next_is_lo; - self.next_is_lo = !next_is_lo; - if next_is_lo { - Some(first as u32) - } else { - self.data = data; - if data.is_empty() && self.last_hi_is_zero { - self.last_hi_is_zero = false; - None - } else { - Some((first >> 32) as u32) - } - } + /// An iterator of `u32` digits representation of a `BigUint` or `BigInt`, + /// ordered least significant digit first. + pub struct U32Digits<'a> { + data: &'a [u64], + next_is_lo: bool, + last_hi_is_zero: bool, + } +); + +cfg_digit!( + const _: () = { + impl<'a> U32Digits<'a> { + #[inline] + pub(super) fn new(data: &'a [u32]) -> Self { + Self { it: data.iter() } } - None => None, } - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) - } + impl Iterator for U32Digits<'_> { + type Item = u32; + #[inline] + fn next(&mut self) -> Option { + self.it.next().cloned() + } - #[inline] - fn last(self) -> Option { - self.data.last().map(|&last| { - if self.last_hi_is_zero { - last as u32 - } else { - (last >> 32) as u32 + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() } - }) - } - #[inline] - fn count(self) -> usize { - self.len() - } -} + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.it.nth(n).cloned() + } -#[cfg(u64_digit)] -impl DoubleEndedIterator for U32Digits<'_> { - fn next_back(&mut self) -> Option { - match self.data.split_last() { - Some((&last, data)) => { - let last_is_lo = self.last_hi_is_zero; - self.last_hi_is_zero = !last_is_lo; - if last_is_lo { - self.data = data; - if data.is_empty() && !self.next_is_lo { - self.next_is_lo = true; - None - } else { - Some(last as u32) - } - } else { - Some((last >> 32) as u32) - } + #[inline] + fn last(self) -> Option { + self.it.last().cloned() } - None => None, - } - } -} -#[cfg(u64_digit)] -impl ExactSizeIterator for U32Digits<'_> { - #[inline] - fn len(&self) -> usize { - self.data.len() * 2 - usize::from(self.last_hi_is_zero) - usize::from(!self.next_is_lo) - } -} + #[inline] + fn count(self) -> usize { + self.it.count() + } + } -#[cfg(not(u64_digit))] -impl<'a> U32Digits<'a> { - #[inline] - pub(super) fn new(data: &'a [u32]) -> Self { - Self { it: data.iter() } - } -} + impl DoubleEndedIterator for U32Digits<'_> { + fn next_back(&mut self) -> Option { + self.it.next_back().cloned() + } + } -#[cfg(not(u64_digit))] -impl Iterator for U32Digits<'_> { - type Item = u32; - #[inline] - fn next(&mut self) -> Option { - self.it.next().cloned() - } + impl ExactSizeIterator for U32Digits<'_> { + #[inline] + fn len(&self) -> usize { + self.it.len() + } + } + }; + + const _: () = { + impl<'a> U32Digits<'a> { + #[inline] + pub(super) fn new(data: &'a [u64]) -> Self { + let last_hi_is_zero = data + .last() + .map(|&last| { + let last_hi = (last >> 32) as u32; + last_hi == 0 + }) + .unwrap_or(false); + U32Digits { + data, + next_is_lo: true, + last_hi_is_zero, + } + } + } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } + impl Iterator for U32Digits<'_> { + type Item = u32; + #[inline] + fn next(&mut self) -> Option { + match self.data.split_first() { + Some((&first, data)) => { + let next_is_lo = self.next_is_lo; + self.next_is_lo = !next_is_lo; + if next_is_lo { + Some(first as u32) + } else { + self.data = data; + if data.is_empty() && self.last_hi_is_zero { + self.last_hi_is_zero = false; + None + } else { + Some((first >> 32) as u32) + } + } + } + None => None, + } + } - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.it.nth(n).cloned() - } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } - #[inline] - fn last(self) -> Option { - self.it.last().cloned() - } + #[inline] + fn last(self) -> Option { + self.data.last().map(|&last| { + if self.last_hi_is_zero { + last as u32 + } else { + (last >> 32) as u32 + } + }) + } - #[inline] - fn count(self) -> usize { - self.it.count() - } -} + #[inline] + fn count(self) -> usize { + self.len() + } + } -#[cfg(not(u64_digit))] -impl DoubleEndedIterator for U32Digits<'_> { - fn next_back(&mut self) -> Option { - self.it.next_back().copied() - } -} + impl DoubleEndedIterator for U32Digits<'_> { + fn next_back(&mut self) -> Option { + match self.data.split_last() { + Some((&last, data)) => { + let last_is_lo = self.last_hi_is_zero; + self.last_hi_is_zero = !last_is_lo; + if last_is_lo { + self.data = data; + if data.is_empty() && !self.next_is_lo { + self.next_is_lo = true; + None + } else { + Some(last as u32) + } + } else { + Some((last >> 32) as u32) + } + } + None => None, + } + } + } -#[cfg(not(u64_digit))] -impl ExactSizeIterator for U32Digits<'_> { - #[inline] - fn len(&self) -> usize { - self.it.len() - } -} + impl ExactSizeIterator for U32Digits<'_> { + #[inline] + fn len(&self) -> usize { + self.data.len() * 2 + - usize::from(self.last_hi_is_zero) + - usize::from(!self.next_is_lo) + } + } + }; +); impl FusedIterator for U32Digits<'_> {} -/// An iterator of `u64` digits representation of a `BigUint` or `BigInt`, -/// ordered least significant digit first. -pub struct U64Digits<'a> { - #[cfg(not(u64_digit))] - it: core::slice::Chunks<'a, u32>, - - #[cfg(u64_digit)] - it: core::slice::Iter<'a, u64>, -} - -#[cfg(not(u64_digit))] -impl<'a> U64Digits<'a> { - #[inline] - pub(super) fn new(data: &'a [u32]) -> Self { - U64Digits { it: data.chunks(2) } +cfg_digit!( + /// An iterator of `u64` digits representation of a `BigUint` or `BigInt`, + /// ordered least significant digit first. + pub struct U64Digits<'a> { + it: core::slice::Chunks<'a, u32>, } -} -#[cfg(not(u64_digit))] -impl Iterator for U64Digits<'_> { - type Item = u64; - #[inline] - fn next(&mut self) -> Option { - self.it.next().map(u32_chunk_to_u64) + /// An iterator of `u64` digits representation of a `BigUint` or `BigInt`, + /// ordered least significant digit first. + pub struct U64Digits<'a> { + it: core::slice::Iter<'a, u64>, } +); + +cfg_digit!( + const _: () = { + impl<'a> U64Digits<'a> { + #[inline] + pub(super) fn new(data: &'a [u32]) -> Self { + U64Digits { it: data.chunks(2) } + } + } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) - } + impl Iterator for U64Digits<'_> { + type Item = u64; + #[inline] + fn next(&mut self) -> Option { + self.it.next().map(super::u32_chunk_to_u64) + } - #[inline] - fn last(self) -> Option { - self.it.last().map(u32_chunk_to_u64) - } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.len(); + (len, Some(len)) + } - #[inline] - fn count(self) -> usize { - self.len() - } -} + #[inline] + fn last(self) -> Option { + self.it.last().map(super::u32_chunk_to_u64) + } -#[cfg(not(u64_digit))] -impl DoubleEndedIterator for U64Digits<'_> { - fn next_back(&mut self) -> Option { - self.it.next_back().map(u32_chunk_to_u64) - } -} + #[inline] + fn count(self) -> usize { + self.len() + } + } -#[cfg(u64_digit)] -impl<'a> U64Digits<'a> { - #[inline] - pub(super) fn new(data: &'a [u64]) -> Self { - Self { it: data.iter() } - } -} + impl DoubleEndedIterator for U64Digits<'_> { + fn next_back(&mut self) -> Option { + self.it.next_back().map(super::u32_chunk_to_u64) + } + } + }; -#[cfg(u64_digit)] -impl Iterator for U64Digits<'_> { - type Item = u64; - #[inline] - fn next(&mut self) -> Option { - self.it.next().cloned() - } + const _: () = { + impl<'a> U64Digits<'a> { + #[inline] + pub(super) fn new(data: &'a [u64]) -> Self { + Self { it: data.iter() } + } + } - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.it.size_hint() - } + impl Iterator for U64Digits<'_> { + type Item = u64; + #[inline] + fn next(&mut self) -> Option { + self.it.next().cloned() + } - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.it.nth(n).cloned() - } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.it.size_hint() + } - #[inline] - fn last(self) -> Option { - self.it.last().cloned() - } + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.it.nth(n).cloned() + } - #[inline] - fn count(self) -> usize { - self.it.count() - } -} + #[inline] + fn last(self) -> Option { + self.it.last().cloned() + } -#[cfg(u64_digit)] -impl DoubleEndedIterator for U64Digits<'_> { - fn next_back(&mut self) -> Option { - self.it.next_back().cloned() - } -} + #[inline] + fn count(self) -> usize { + self.it.count() + } + } + + impl DoubleEndedIterator for U64Digits<'_> { + fn next_back(&mut self) -> Option { + self.it.next_back().cloned() + } + } + }; +); impl ExactSizeIterator for U64Digits<'_> { #[inline] diff --git a/src/biguint/monty.rs b/src/biguint/monty.rs index abaca50c..484034e4 100644 --- a/src/biguint/monty.rs +++ b/src/biguint/monty.rs @@ -1,7 +1,7 @@ use crate::std_alloc::Vec; use core::mem; use core::ops::Shl; -use num_traits::{One, Zero}; +use num_traits::One; use crate::big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit}; use crate::biguint::BigUint; @@ -56,7 +56,7 @@ fn montgomery(x: &BigUint, y: &BigUint, m: &BigUint, k: BigDigit, n: usize) -> B n ); - let mut z = BigUint::zero(); + let mut z = BigUint::ZERO; z.data.resize(n * 2, 0); let mut c: BigDigit = 0; @@ -173,7 +173,7 @@ pub(super) fn monty_modpow(x: &BigUint, y: &BigUint, m: &BigUint) -> BigUint { // initialize z = 1 (Montgomery 1) let mut z = powers[0].clone(); z.data.resize(num_words, 0); - let mut zz = BigUint::zero(); + let mut zz = BigUint::ZERO; zz.data.resize(num_words, 0); // same windowed exponent, but with Montgomery multiplications diff --git a/src/biguint/multiplication.rs b/src/biguint/multiplication.rs index 4d7f1f21..c97c7e1a 100644 --- a/src/biguint/multiplication.rs +++ b/src/biguint/multiplication.rs @@ -1,7 +1,5 @@ use super::addition::{__add2, add2}; use super::subtraction::sub2; -#[cfg(not(u64_digit))] -use super::u32_from_u128; use super::{biguint_from_vec, cmp_slice, BigUint, IntDigits}; use crate::big_digit::{self, BigDigit, DoubleBigDigit}; @@ -88,9 +86,10 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { let acc = acc; let (x, y) = if b.len() < c.len() { (b, c) } else { (c, b) }; - // We use three algorithms for different input sizes. + // We use four algorithms for different input sizes. // // - For small inputs, long multiplication is fastest. + // - If y is at least least twice as long as x, split using Half-Karatsuba. // - Next we use Karatsuba multiplication (Toom-2), which we have optimized // to avoid unnecessary allocations for intermediate values. // - For the largest inputs we use Toom-3, which better optimizes the @@ -104,6 +103,65 @@ fn mac3(mut acc: &mut [BigDigit], mut b: &[BigDigit], mut c: &[BigDigit]) { for (i, xi) in x.iter().enumerate() { mac_digit(&mut acc[i..], y, *xi); } + } else if x.len() * 2 <= y.len() { + // Karatsuba Multiplication for factors with significant length disparity. + // + // The Half-Karatsuba Multiplication Algorithm is a specialized case of + // the normal Karatsuba multiplication algorithm, designed for the scenario + // where y has at least twice as many base digits as x. + // + // In this case y (the longer input) is split into high2 and low2, + // at m2 (half the length of y) and x (the shorter input), + // is used directly without splitting. + // + // The algorithm then proceeds as follows: + // + // 1. Compute the product z0 = x * low2. + // 2. Compute the product temp = x * high2. + // 3. Adjust the weight of temp by adding m2 (* NBASE ^ m2) + // 4. Add temp and z0 to obtain the final result. + // + // Proof: + // + // The algorithm can be derived from the original Karatsuba algorithm by + // simplifying the formula when the shorter factor x is not split into + // high and low parts, as shown below. + // + // Original Karatsuba formula: + // + // result = (z2 * NBASE ^ (m2 × 2)) + ((z1 - z2 - z0) * NBASE ^ m2) + z0 + // + // Substitutions: + // + // low1 = x + // high1 = 0 + // + // Applying substitutions: + // + // z0 = (low1 * low2) + // = (x * low2) + // + // z1 = ((low1 + high1) * (low2 + high2)) + // = ((x + 0) * (low2 + high2)) + // = (x * low2) + (x * high2) + // + // z2 = (high1 * high2) + // = (0 * high2) + // = 0 + // + // Simplified using the above substitutions: + // + // result = (z2 * NBASE ^ (m2 × 2)) + ((z1 - z2 - z0) * NBASE ^ m2) + z0 + // = (0 * NBASE ^ (m2 × 2)) + ((z1 - 0 - z0) * NBASE ^ m2) + z0 + // = ((z1 - z0) * NBASE ^ m2) + z0 + // = ((z1 - z0) * NBASE ^ m2) + z0 + // = (x * high2) * NBASE ^ m2 + z0 + let m2 = y.len() / 2; + let (low2, high2) = y.split_at(m2); + + // (x * high2) * NBASE ^ m2 + z0 + mac3(acc, x, low2); + mac3(&mut acc[m2..], x, high2); } else if x.len() <= 256 { // Karatsuba multiplication: // @@ -397,7 +455,7 @@ fn sub_sign(mut a: &[BigDigit], mut b: &[BigDigit]) -> (Sign, BigUint) { sub2(&mut b, a); (Minus, biguint_from_vec(b)) } - Ordering::Equal => (NoSign, Zero::zero()), + Ordering::Equal => (NoSign, BigUint::ZERO), } } @@ -410,7 +468,7 @@ macro_rules! impl_mul { fn mul(self, other: $Other) -> BigUint { match (&*self.data, &*other.data) { // multiply by zero - (&[], _) | (_, &[]) => BigUint::zero(), + (&[], _) | (_, &[]) => BigUint::ZERO, // multiply by a scalar (_, &[digit]) => self * digit, (&[digit], _) => other * digit, @@ -484,22 +542,22 @@ impl Mul for BigUint { } } impl MulAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn mul_assign(&mut self, other: u64) { - if let Some(other) = BigDigit::from_u64(other) { - scalar_mul(self, other); - } else { - let (hi, lo) = big_digit::from_doublebigdigit(other); - *self = mul3(&self.data, &[lo, hi]); + cfg_digit!( + #[inline] + fn mul_assign(&mut self, other: u64) { + if let Some(other) = BigDigit::from_u64(other) { + scalar_mul(self, other); + } else { + let (hi, lo) = big_digit::from_doublebigdigit(other); + *self = mul3(&self.data, &[lo, hi]); + } } - } - #[cfg(u64_digit)] - #[inline] - fn mul_assign(&mut self, other: u64) { - scalar_mul(self, other); - } + #[inline] + fn mul_assign(&mut self, other: u64) { + scalar_mul(self, other); + } + ); } impl Mul for BigUint { @@ -513,30 +571,30 @@ impl Mul for BigUint { } impl MulAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn mul_assign(&mut self, other: u128) { - if let Some(other) = BigDigit::from_u128(other) { - scalar_mul(self, other); - } else { - *self = match u32_from_u128(other) { - (0, 0, c, d) => mul3(&self.data, &[d, c]), - (0, b, c, d) => mul3(&self.data, &[d, c, b]), - (a, b, c, d) => mul3(&self.data, &[d, c, b, a]), - }; + cfg_digit!( + #[inline] + fn mul_assign(&mut self, other: u128) { + if let Some(other) = BigDigit::from_u128(other) { + scalar_mul(self, other); + } else { + *self = match super::u32_from_u128(other) { + (0, 0, c, d) => mul3(&self.data, &[d, c]), + (0, b, c, d) => mul3(&self.data, &[d, c, b]), + (a, b, c, d) => mul3(&self.data, &[d, c, b, a]), + }; + } } - } - #[cfg(u64_digit)] - #[inline] - fn mul_assign(&mut self, other: u128) { - if let Some(other) = BigDigit::from_u128(other) { - scalar_mul(self, other); - } else { - let (hi, lo) = big_digit::from_doublebigdigit(other); - *self = mul3(&self.data, &[lo, hi]); + #[inline] + fn mul_assign(&mut self, other: u128) { + if let Some(other) = BigDigit::from_u128(other) { + scalar_mul(self, other); + } else { + let (hi, lo) = big_digit::from_doublebigdigit(other); + *self = mul3(&self.data, &[lo, hi]); + } } - } + ); } impl CheckedMul for BigUint { diff --git a/src/biguint/power.rs b/src/biguint/power.rs index 621e1b15..fa1c4926 100644 --- a/src/biguint/power.rs +++ b/src/biguint/power.rs @@ -14,7 +14,7 @@ impl Pow<&BigUint> for BigUint { if self.is_one() || exp.is_zero() { BigUint::one() } else if self.is_zero() { - BigUint::zero() + Self::ZERO } else if let Some(exp) = exp.to_u64() { self.pow(exp) } else if let Some(exp) = exp.to_u128() { @@ -44,7 +44,7 @@ impl Pow<&BigUint> for &BigUint { if self.is_one() || exp.is_zero() { BigUint::one() } else if self.is_zero() { - BigUint::zero() + BigUint::ZERO } else { self.clone().pow(exp) } diff --git a/src/biguint/serde.rs b/src/biguint/serde.rs index 3240f093..1fadf16a 100644 --- a/src/biguint/serde.rs +++ b/src/biguint/serde.rs @@ -18,44 +18,44 @@ fn cautious(hint: Option) -> usize { } impl Serialize for BigUint { - #[cfg(not(u64_digit))] - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - // Note: do not change the serialization format, or it may break forward - // and backward compatibility of serialized data! If we ever change the - // internal representation, we should still serialize in base-`u32`. - let data: &[u32] = &self.data; - data.serialize(serializer) - } + cfg_digit!( + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Note: do not change the serialization format, or it may break forward + // and backward compatibility of serialized data! If we ever change the + // internal representation, we should still serialize in base-`u32`. + let data: &[u32] = &self.data; + data.serialize(serializer) + } - #[cfg(u64_digit)] - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use serde::ser::SerializeSeq; - - if let Some((&last, data)) = self.data.split_last() { - let last_lo = last as u32; - let last_hi = (last >> 32) as u32; - let u32_len = data.len() * 2 + 1 + (last_hi != 0) as usize; - let mut seq = serializer.serialize_seq(Some(u32_len))?; - for &x in data { - seq.serialize_element(&(x as u32))?; - seq.serialize_element(&((x >> 32) as u32))?; - } - seq.serialize_element(&last_lo)?; - if last_hi != 0 { - seq.serialize_element(&last_hi)?; + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeSeq; + + if let Some((&last, data)) = self.data.split_last() { + let last_lo = last as u32; + let last_hi = (last >> 32) as u32; + let u32_len = data.len() * 2 + 1 + (last_hi != 0) as usize; + let mut seq = serializer.serialize_seq(Some(u32_len))?; + for &x in data { + seq.serialize_element(&(x as u32))?; + seq.serialize_element(&((x >> 32) as u32))?; + } + seq.serialize_element(&last_lo)?; + if last_hi != 0 { + seq.serialize_element(&last_hi)?; + } + seq.end() + } else { + let data: &[u32] = &[]; + data.serialize(serializer) } - seq.end() - } else { - let data: &[u32] = &[]; - data.serialize(serializer) } - } + ); } impl<'de> Deserialize<'de> for BigUint { @@ -76,44 +76,44 @@ impl<'de> Visitor<'de> for U32Visitor { formatter.write_str("a sequence of unsigned 32-bit numbers") } - #[cfg(not(u64_digit))] - fn visit_seq(self, mut seq: S) -> Result - where - S: SeqAccess<'de>, - { - let len = cautious(seq.size_hint()); - let mut data = Vec::with_capacity(len); - - while let Some(value) = seq.next_element::()? { - data.push(value); - } - - Ok(biguint_from_vec(data)) - } - - #[cfg(u64_digit)] - fn visit_seq(self, mut seq: S) -> Result - where - S: SeqAccess<'de>, - { - use crate::big_digit::BigDigit; - use num_integer::Integer; - - let u32_len = cautious(seq.size_hint()); - let len = Integer::div_ceil(&u32_len, &2); - let mut data = Vec::with_capacity(len); + cfg_digit!( + fn visit_seq(self, mut seq: S) -> Result + where + S: SeqAccess<'de>, + { + let len = cautious(seq.size_hint()); + let mut data = Vec::with_capacity(len); - while let Some(lo) = seq.next_element::()? { - let mut value = BigDigit::from(lo); - if let Some(hi) = seq.next_element::()? { - value |= BigDigit::from(hi) << 32; + while let Some(value) = seq.next_element::()? { data.push(value); - } else { - data.push(value); - break; } + + Ok(biguint_from_vec(data)) } - Ok(biguint_from_vec(data)) - } + fn visit_seq(self, mut seq: S) -> Result + where + S: SeqAccess<'de>, + { + use crate::big_digit::BigDigit; + use num_integer::Integer; + + let u32_len = cautious(seq.size_hint()); + let len = Integer::div_ceil(&u32_len, &2); + let mut data = Vec::with_capacity(len); + + while let Some(lo) = seq.next_element::()? { + let mut value = BigDigit::from(lo); + if let Some(hi) = seq.next_element::()? { + value |= BigDigit::from(hi) << 32; + data.push(value); + } else { + data.push(value); + break; + } + } + + Ok(biguint_from_vec(data)) + } + ); } diff --git a/src/biguint/shift.rs b/src/biguint/shift.rs index 00326bb5..edcbc158 100644 --- a/src/biguint/shift.rs +++ b/src/biguint/shift.rs @@ -58,7 +58,7 @@ fn biguint_shr(n: Cow<'_, BigUint>, shift: T) -> BigUint { return n.into_owned(); } let bits = T::from(big_digit::BITS).unwrap(); - let digits = (shift / bits).to_usize().unwrap_or(core::usize::MAX); + let digits = (shift / bits).to_usize().unwrap_or(usize::MAX); let shift = (shift % bits).to_u8().unwrap(); biguint_shr2(n, digits, shift) } @@ -135,7 +135,7 @@ macro_rules! impl_shift { impl ShlAssign<$rhs> for BigUint { #[inline] fn shl_assign(&mut self, rhs: $rhs) { - let n = mem::replace(self, BigUint::zero()); + let n = mem::replace(self, Self::ZERO); *self = n << rhs; } } @@ -160,7 +160,7 @@ macro_rules! impl_shift { impl ShrAssign<$rhs> for BigUint { #[inline] fn shr_assign(&mut self, rhs: $rhs) { - let n = mem::replace(self, BigUint::zero()); + let n = mem::replace(self, Self::ZERO); *self = n >> rhs; } } diff --git a/src/biguint/subtraction.rs b/src/biguint/subtraction.rs index b7cf59dd..58839403 100644 --- a/src/biguint/subtraction.rs +++ b/src/biguint/subtraction.rs @@ -1,5 +1,3 @@ -#[cfg(not(u64_digit))] -use super::u32_from_u128; use super::BigUint; use crate::big_digit::{self, BigDigit}; @@ -7,33 +5,27 @@ use crate::UsizePromotion; use core::cmp::Ordering::{Equal, Greater, Less}; use core::ops::{Sub, SubAssign}; -use num_traits::{CheckedSub, Zero}; - -#[cfg(all(use_addcarry, target_arch = "x86_64"))] -use core::arch::x86_64 as arch; - -#[cfg(all(use_addcarry, target_arch = "x86"))] -use core::arch::x86 as arch; +use num_traits::CheckedSub; // Subtract with borrow: -#[cfg(all(use_addcarry, u64_digit))] +#[cfg(target_arch = "x86_64")] #[inline] fn sbb(borrow: u8, a: u64, b: u64, out: &mut u64) -> u8 { // Safety: There are absolutely no safety concerns with calling `_subborrow_u64`. // It's just unsafe for API consistency with other intrinsics. - unsafe { arch::_subborrow_u64(borrow, a, b, out) } + unsafe { core::arch::x86_64::_subborrow_u64(borrow, a, b, out) } } -#[cfg(all(use_addcarry, not(u64_digit)))] +#[cfg(target_arch = "x86")] #[inline] fn sbb(borrow: u8, a: u32, b: u32, out: &mut u32) -> u8 { // Safety: There are absolutely no safety concerns with calling `_subborrow_u32`. // It's just unsafe for API consistency with other intrinsics. - unsafe { arch::_subborrow_u32(borrow, a, b, out) } + unsafe { core::arch::x86::_subborrow_u32(borrow, a, b, out) } } // fallback for environments where we don't have a subborrow intrinsic -#[cfg(not(use_addcarry))] +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] #[inline] fn sbb(borrow: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 { use crate::big_digit::SignedDoubleBigDigit; @@ -167,27 +159,27 @@ impl SubAssign for BigUint { impl Sub for u32 { type Output = BigUint; - #[cfg(not(u64_digit))] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - if other.data.len() == 0 { - other.data.push(self); - } else { - sub2rev(&[self], &mut other.data[..]); + cfg_digit!( + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + if other.data.len() == 0 { + other.data.push(self); + } else { + sub2rev(&[self], &mut other.data[..]); + } + other.normalized() } - other.normalized() - } - #[cfg(u64_digit)] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - if other.data.is_empty() { - other.data.push(self as BigDigit); - } else { - sub2rev(&[self as BigDigit], &mut other.data[..]); + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + if other.data.is_empty() { + other.data.push(self as BigDigit); + } else { + sub2rev(&[self as BigDigit], &mut other.data[..]); + } + other.normalized() } - other.normalized() - } + ); } impl Sub for BigUint { @@ -201,47 +193,47 @@ impl Sub for BigUint { } impl SubAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn sub_assign(&mut self, other: u64) { - let (hi, lo) = big_digit::from_doublebigdigit(other); - sub2(&mut self.data[..], &[lo, hi]); - self.normalize(); - } + cfg_digit!( + #[inline] + fn sub_assign(&mut self, other: u64) { + let (hi, lo) = big_digit::from_doublebigdigit(other); + sub2(&mut self.data[..], &[lo, hi]); + self.normalize(); + } - #[cfg(u64_digit)] - #[inline] - fn sub_assign(&mut self, other: u64) { - sub2(&mut self.data[..], &[other as BigDigit]); - self.normalize(); - } + #[inline] + fn sub_assign(&mut self, other: u64) { + sub2(&mut self.data[..], &[other as BigDigit]); + self.normalize(); + } + ); } impl Sub for u64 { type Output = BigUint; - #[cfg(not(u64_digit))] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - while other.data.len() < 2 { - other.data.push(0); - } + cfg_digit!( + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + while other.data.len() < 2 { + other.data.push(0); + } - let (hi, lo) = big_digit::from_doublebigdigit(self); - sub2rev(&[lo, hi], &mut other.data[..]); - other.normalized() - } + let (hi, lo) = big_digit::from_doublebigdigit(self); + sub2rev(&[lo, hi], &mut other.data[..]); + other.normalized() + } - #[cfg(u64_digit)] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - if other.data.is_empty() { - other.data.push(self); - } else { - sub2rev(&[self], &mut other.data[..]); + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + if other.data.is_empty() { + other.data.push(self); + } else { + sub2rev(&[self], &mut other.data[..]); + } + other.normalized() } - other.normalized() - } + ); } impl Sub for BigUint { @@ -255,49 +247,49 @@ impl Sub for BigUint { } impl SubAssign for BigUint { - #[cfg(not(u64_digit))] - #[inline] - fn sub_assign(&mut self, other: u128) { - let (a, b, c, d) = u32_from_u128(other); - sub2(&mut self.data[..], &[d, c, b, a]); - self.normalize(); - } + cfg_digit!( + #[inline] + fn sub_assign(&mut self, other: u128) { + let (a, b, c, d) = super::u32_from_u128(other); + sub2(&mut self.data[..], &[d, c, b, a]); + self.normalize(); + } - #[cfg(u64_digit)] - #[inline] - fn sub_assign(&mut self, other: u128) { - let (hi, lo) = big_digit::from_doublebigdigit(other); - sub2(&mut self.data[..], &[lo, hi]); - self.normalize(); - } + #[inline] + fn sub_assign(&mut self, other: u128) { + let (hi, lo) = big_digit::from_doublebigdigit(other); + sub2(&mut self.data[..], &[lo, hi]); + self.normalize(); + } + ); } impl Sub for u128 { type Output = BigUint; - #[cfg(not(u64_digit))] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - while other.data.len() < 4 { - other.data.push(0); + cfg_digit!( + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + while other.data.len() < 4 { + other.data.push(0); + } + + let (a, b, c, d) = super::u32_from_u128(self); + sub2rev(&[d, c, b, a], &mut other.data[..]); + other.normalized() } - let (a, b, c, d) = u32_from_u128(self); - sub2rev(&[d, c, b, a], &mut other.data[..]); - other.normalized() - } + #[inline] + fn sub(self, mut other: BigUint) -> BigUint { + while other.data.len() < 2 { + other.data.push(0); + } - #[cfg(u64_digit)] - #[inline] - fn sub(self, mut other: BigUint) -> BigUint { - while other.data.len() < 2 { - other.data.push(0); + let (hi, lo) = big_digit::from_doublebigdigit(self); + sub2rev(&[lo, hi], &mut other.data[..]); + other.normalized() } - - let (hi, lo) = big_digit::from_doublebigdigit(self); - sub2rev(&[lo, hi], &mut other.data[..]); - other.normalized() - } + ); } impl CheckedSub for BigUint { @@ -305,7 +297,7 @@ impl CheckedSub for BigUint { fn checked_sub(&self, v: &BigUint) -> Option { match self.cmp(v) { Less => None, - Equal => Some(Zero::zero()), + Equal => Some(Self::ZERO), Greater => Some(self.sub(v)), } } diff --git a/src/lib.rs b/src/lib.rs index 893b7473..b807fd20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,12 +21,12 @@ //! ```rust //! # fn main() { //! use num_bigint::BigUint; -//! use num_traits::{Zero, One}; +//! use num_traits::One; //! //! // Calculate large fibonacci numbers. //! fn fib(n: usize) -> BigUint { -//! let mut f0: BigUint = Zero::zero(); -//! let mut f1: BigUint = One::one(); +//! let mut f0 = BigUint::ZERO; +//! let mut f1 = BigUint::one(); //! for _ in 0..n { //! let f2 = f0 + &f1; //! f0 = f1; @@ -81,7 +81,7 @@ //! //! ## Compatibility //! -//! The `num-bigint` crate is tested for rustc 1.31 and greater. +//! The `num-bigint` crate is tested for rustc 1.60 and greater. #![doc(html_root_url = "https://docs.rs/num-bigint/0.4")] #![warn(rust_2018_idioms)] @@ -183,13 +183,11 @@ impl Error for ParseBigIntError { } /// The error type returned when a checked conversion regarding big integer fails. -#[cfg(has_try_from)] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TryFromBigIntError { original: T, } -#[cfg(has_try_from)] impl TryFromBigIntError { fn new(original: T) -> Self { TryFromBigIntError { original } @@ -206,7 +204,7 @@ impl TryFromBigIntError { } } -#[cfg(all(feature = "std", has_try_from))] +#[cfg(feature = "std")] impl std::error::Error for TryFromBigIntError where T: fmt::Debug, @@ -216,7 +214,6 @@ where } } -#[cfg(has_try_from)] impl fmt::Display for TryFromBigIntError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.__description().fmt(f) @@ -236,36 +233,31 @@ pub use crate::bigint::ToBigInt; pub use crate::bigrand::{RandBigInt, RandomBits, UniformBigInt, UniformBigUint}; mod big_digit { - /// A [`BigDigit`] is a [`BigUint`]'s composing element. - #[cfg(not(u64_digit))] - pub(crate) type BigDigit = u32; - #[cfg(u64_digit)] - pub(crate) type BigDigit = u64; - - /// A [`DoubleBigDigit`] is the internal type used to do the computations. Its - /// size is the double of the size of [`BigDigit`]. - #[cfg(not(u64_digit))] - pub(crate) type DoubleBigDigit = u64; - #[cfg(u64_digit)] - pub(crate) type DoubleBigDigit = u128; - - /// A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`]. - #[cfg(not(u64_digit))] - pub(crate) type SignedDoubleBigDigit = i64; - #[cfg(u64_digit)] - pub(crate) type SignedDoubleBigDigit = i128; - - // [`DoubleBigDigit`] size dependent - #[cfg(not(u64_digit))] - pub(crate) const BITS: u8 = 32; - #[cfg(u64_digit)] - pub(crate) const BITS: u8 = 64; - + // A [`BigDigit`] is a [`BigUint`]'s composing element. + cfg_digit!( + pub(crate) type BigDigit = u32; + pub(crate) type BigDigit = u64; + ); + + // A [`DoubleBigDigit`] is the internal type used to do the computations. Its + // size is the double of the size of [`BigDigit`]. + cfg_digit!( + pub(crate) type DoubleBigDigit = u64; + pub(crate) type DoubleBigDigit = u128; + ); + + // A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`]. + cfg_digit!( + pub(crate) type SignedDoubleBigDigit = i64; + pub(crate) type SignedDoubleBigDigit = i128; + ); + + pub(crate) const BITS: u8 = BigDigit::BITS as u8; pub(crate) const HALF_BITS: u8 = BITS / 2; pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1; - const LO_MASK: DoubleBigDigit = (1 << BITS) - 1; - pub(crate) const MAX: BigDigit = LO_MASK as BigDigit; + pub(crate) const MAX: BigDigit = BigDigit::MAX; + const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit; #[inline] fn get_hi(n: DoubleBigDigit) -> BigDigit { diff --git a/src/macros.rs b/src/macros.rs index 16186165..f2e21e3b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,5 +1,37 @@ #![allow(unused_macros)] +macro_rules! cfg_32 { + ($($any:tt)+) => { + #[cfg(not(target_pointer_width = "64"))] $($any)+ + } +} + +macro_rules! cfg_32_or_test { + ($($any:tt)+) => { + #[cfg(any(not(target_pointer_width = "64"), test))] $($any)+ + } +} + +macro_rules! cfg_64 { + ($($any:tt)+) => { + #[cfg(target_pointer_width = "64")] $($any)+ + } +} + +macro_rules! cfg_digit { + ($item32:item $item64:item) => { + cfg_32!($item32); + cfg_64!($item64); + }; +} + +macro_rules! cfg_digit_expr { + ($expr32:expr, $expr64:expr) => { + cfg_32!($expr32); + cfg_64!($expr64); + }; +} + macro_rules! forward_val_val_binop { (impl $imp:ident for $res:ty, $method:ident) => { impl $imp<$res> for $res { @@ -418,7 +450,7 @@ macro_rules! impl_sum_iter_type { where I: Iterator, { - iter.fold(Zero::zero(), <$res>::add) + iter.fold(Self::ZERO, <$res>::add) } } }; diff --git a/tests/bigint.rs b/tests/bigint.rs index 75cf81ec..fea5232e 100644 --- a/tests/bigint.rs +++ b/tests/bigint.rs @@ -8,9 +8,6 @@ use std::hash::{BuildHasher, Hash, Hasher}; use std::iter::repeat; use std::ops::Neg; use std::{f32, f64}; -use std::{i128, u128}; -use std::{i16, i32, i64, i8, isize}; -use std::{u16, u32, u64, u8, usize}; use num_integer::Integer; use num_traits::{ diff --git a/tests/bigint_bitwise.rs b/tests/bigint_bitwise.rs index 6c1e82fe..94d92e1d 100644 --- a/tests/bigint_bitwise.rs +++ b/tests/bigint_bitwise.rs @@ -1,6 +1,5 @@ use num_bigint::{BigInt, Sign, ToBigInt}; use num_traits::ToPrimitive; -use std::{i32, i64, u32}; enum ValueVec { N, diff --git a/tests/biguint.rs b/tests/biguint.rs index c027771b..4e2c6259 100644 --- a/tests/biguint.rs +++ b/tests/biguint.rs @@ -6,12 +6,9 @@ use num_integer::Integer; use std::cmp::Ordering::{Equal, Greater, Less}; use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash, Hasher}; -use std::i64; use std::iter::repeat; use std::str::FromStr; use std::{f32, f64}; -use std::{i128, u128}; -use std::{u16, u32, u64, u8, usize}; use num_traits::{ pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Euclid, FromBytes, FromPrimitive, Num, diff --git a/tests/consts/mod.rs b/tests/consts/mod.rs index 5d0555d0..457fbd36 100644 --- a/tests/consts/mod.rs +++ b/tests/consts/mod.rs @@ -16,7 +16,7 @@ pub const SUM_TRIPLES: &[(&[u32], &[u32], &[u32])] = &[ (&[1, 2, 2, 1], &[N1, N2], &[0, 1, 3, 1]), ]; -pub const M: u32 = ::std::u32::MAX; +pub const M: u32 = u32::MAX; pub const MUL_TRIPLES: &[(&[u32], &[u32], &[u32])] = &[ (&[], &[], &[]), (&[], &[1], &[]), diff --git a/tests/roots.rs b/tests/roots.rs index cfef80c4..7110adf9 100644 --- a/tests/roots.rs +++ b/tests/roots.rs @@ -1,7 +1,6 @@ mod biguint { use num_bigint::BigUint; use num_traits::{One, Zero}; - use std::{i32, u32}; fn check>(x: T, n: u32) { let x: BigUint = x.into();