Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Montgomery multiplication #402

Merged
merged 15 commits into from
Dec 16, 2024
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added `Uint::square_redc`. ([#402])
- Support for diesel @ 2.2 ([#404])
- Support for sqlx @ 0.8 ([#400])
- Support for fastrlp @ 0.4 ([#401])
Expand All @@ -21,10 +22,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for sqlx @ 0.7. This is a breaking change, outside of
regular semver policy, as 0.7 contains a security vulnerability ([#400])

### Fixed

- `Uint::mul_redc` is now alloc free ([#402])

[#399]: https://github.com/recmo/uint/pull/399
[#400]: https://github.com/recmo/uint/pull/400
[#401]: https://github.com/recmo/uint/pull/401
[#404]: https://github.com/recmo/uint/pull/404
[#402]: https://github.com/recmo/uint/pull/402

## [1.12.3] - 2024-06-03

Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ parity-scale-codec = { version = "3", optional = true, features = [
"max-encoded-len",
], default-features = false }
primitive-types = { version = "0.12", optional = true, default-features = false }
proptest = { version = "1.3", optional = true, default-features = false }
proptest = { version = "=1.5", optional = true, default-features = false }
pyo3 = { version = "0.19", optional = true, default-features = false }
quickcheck = { version = "1", optional = true, default-features = false }
rand = { version = "0.8", optional = true, default-features = false }
Expand Down Expand Up @@ -105,7 +105,7 @@ bincode = "1.3"
hex = "0.4"
hex-literal = "0.4"
postgres = "0.19"
proptest = "1.2"
proptest = "=1.5"
serde_json = "1.0"

[features]
Expand Down
11 changes: 11 additions & 0 deletions proptest-regressions/algorithms/mul_redc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc f8106a52136ed4aac61eec137e12c5da14188344055de1af2c70670a9bdcb685 # shrinks to a = 1, b = 1, m = 6096796062212595973
cc d8b943c322534ac6073b169239a6a57d92ed874bd1af77abcaca5fd6c17d0922 # shrinks to a = 1, m = 6467196249019906631
cc 7bce74ed04eba0d78a0753b45256b4fa898002bfbfdd2a45e2fa8692bddda85b # shrinks to a = 17273988827536164680, m = 4783851910396016589
cc 31a52325174b546a906b7327ea489c5df219ac159f819e2cd0240aaab7fe1da6 # shrinks to a = 14240046082810188870, b = 16896972505368501529, m = 6144969318566343923
cc 14972b6b9d20d6efe0cc3595dd60936ae21f0985b25cf47b5bf667cdf0b6f9b3 # shrinks to mut a = 194085243466426527248460240309849653567, m = 302498102704436076702507509051420483905
1 change: 1 addition & 0 deletions proptest-regressions/modular.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ cc d3df2bf31e0850f89f640c6a35c5ddb7fd15bc57c04eb60df5c04aea86d4b27a # shrinks to
cc 530d6a1671f6f937904c349f7ae6504c7cd2e05c31bf194e3aa38d7161a5fc2a # shrinks to a = 0x00_U2, b = 0x00_U2, m = 0x03_U2
cc d7d611337732de2c417788803637c596949792f5bcb942956ea0ec3e8b889d82 # shrinks to a = 0x00_U2, b = 0x00_U2, c = 0x00_U2, m = 0x03_U2
cc f3498e21378eea45e82848df9f85d19faa37874e686e4874bb1da885f3a2ac38 # shrinks to a = 0x00_U2, b = 0x00_U2, c = 0x00_U2, m = 0x03_U2
cc e40e555ad3f7103369086e7a27e810f9223e6e67d575d7b26a5a43fc8b103afc # shrinks to a = 2251333493155034715, b = 12864035474233633436, m = 17624464859391105743
29 changes: 8 additions & 21 deletions src/add.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::Uint;
use crate::{
algorithms::{borrowing_sub, carrying_add},
Uint,
};
use core::{
iter::Sum,
ops::{Add, AddAssign, Neg, Sub, SubAssign},
Expand Down Expand Up @@ -56,24 +59,16 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
#[inline]
#[must_use]
pub const fn overflowing_add(mut self, rhs: Self) -> (Self, bool) {
// TODO: Replace with `u64::carrying_add` once stable.
#[inline]
const fn u64_carrying_add(lhs: u64, rhs: u64, carry: bool) -> (u64, bool) {
let (a, b) = lhs.overflowing_add(rhs);
let (c, d) = a.overflowing_add(carry as u64);
(c, b | d)
}

if BITS == 0 {
return (Self::ZERO, false);
}
let mut carry = false;
let mut i = 0;
while i < LIMBS {
(self.limbs[i], carry) = u64_carrying_add(self.limbs[i], rhs.limbs[i], carry);
(self.limbs[i], carry) = carrying_add(self.limbs[i], rhs.limbs[i], carry);
i += 1;
}
let overflow = carry || self.limbs[LIMBS - 1] > Self::MASK;
let overflow = carry | (self.limbs[LIMBS - 1] > Self::MASK);
self.limbs[LIMBS - 1] &= Self::MASK;
(self, overflow)
}
Expand All @@ -98,24 +93,16 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
#[inline]
#[must_use]
pub const fn overflowing_sub(mut self, rhs: Self) -> (Self, bool) {
// TODO: Replace with `u64::borrowing_sub` once stable.
#[inline]
const fn u64_borrowing_sub(lhs: u64, rhs: u64, borrow: bool) -> (u64, bool) {
let (a, b) = lhs.overflowing_sub(rhs);
let (c, d) = a.overflowing_sub(borrow as u64);
(c, b | d)
}

if BITS == 0 {
return (Self::ZERO, false);
}
let mut borrow = false;
let mut i = 0;
while i < LIMBS {
(self.limbs[i], borrow) = u64_borrowing_sub(self.limbs[i], rhs.limbs[i], borrow);
(self.limbs[i], borrow) = borrowing_sub(self.limbs[i], rhs.limbs[i], borrow);
i += 1;
}
let overflow = borrow || self.limbs[LIMBS - 1] > Self::MASK;
let overflow = borrow | (self.limbs[LIMBS - 1] > Self::MASK);
self.limbs[LIMBS - 1] &= Self::MASK;
(self, overflow)
}
Expand Down
2 changes: 2 additions & 0 deletions src/algorithms/gcd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![allow(clippy::module_name_repetitions)]

// TODO: https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md

// TODO: Make these algorithms work on limb slices.
mod matrix;

Expand Down
22 changes: 19 additions & 3 deletions src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ mod add;
pub mod div;
mod gcd;
mod mul;
#[cfg(feature = "alloc")] // TODO: Make mul_redc alloc-free
mod mul_redc;
mod ops;
mod shift;
Expand All @@ -21,11 +20,10 @@ pub use self::{
div::div,
gcd::{gcd, gcd_extended, inv_mod, LehmerMatrix},
mul::{add_nx1, addmul, addmul_n, addmul_nx1, mul_nx1, submul_nx1},
mul_redc::{mul_redc, square_redc},
ops::{adc, sbb},
shift::{shift_left_small, shift_right_small},
};
#[cfg(feature = "alloc")]
pub use mul_redc::mul_redc;

trait DoubleWord<T>: Sized + Copy {
fn join(high: T, low: T) -> Self;
Expand Down Expand Up @@ -116,3 +114,21 @@ pub fn cmp(left: &[u64], right: &[u64]) -> Ordering {

left.len().cmp(&right.len())
}

// Helper while [Rust#85532](https://github.com/rust-lang/rust/issues/85532) stabilizes.
#[inline]
#[must_use]
pub const fn carrying_add(lhs: u64, rhs: u64, carry: bool) -> (u64, bool) {
let (result, carry_1) = lhs.overflowing_add(rhs);
let (result, carry_2) = result.overflowing_add(carry as u64);
(result, carry_1 | carry_2)
}

// Helper while [Rust#85532](https://github.com/rust-lang/rust/issues/85532) stabilizes.
#[inline]
#[must_use]
pub const fn borrowing_sub(lhs: u64, rhs: u64, borrow: bool) -> (u64, bool) {
let (result, borrow_1) = lhs.overflowing_sub(rhs);
let (result, borrow_2) = result.overflowing_sub(borrow as u64);
(result, borrow_1 | borrow_2)
}
Loading
Loading