Skip to content

Commit

Permalink
Merge branch 'main' into proptest-cases-todo
Browse files Browse the repository at this point in the history
  • Loading branch information
prestwich authored Jun 15, 2024
2 parents ab174bb + 0a31448 commit 503b609
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 214 deletions.
60 changes: 30 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,36 +75,36 @@ jobs:
--feature-powerset --exclude-features nightly,generic_const_exprs \
--depth 1
codecov:
# See <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/source-based-code-coverage.html>
name: Coverage
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
components: llvm-tools-preview
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
env:
# Covered tests are slower, so reduce the test load. (We also shouldn't
# depend too much on case generation for coverage).
PROPTEST_CASES: 5
run: |
# Generate profiles, but do not merge them
cargo llvm-cov --no-report --workspace --all-features --all-targets -- --nocapture
cargo llvm-cov --no-report --workspace --all-features --doc -- --nocapture
- name: Merge profiles
run: cargo llvm-cov --no-run --lcov --output-path lcov.info
- name: Submit to codecov.io
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }} # Optional for public repos
files: lcov.info
fail_ci_if_error: true
verbose: true
# codecov:
# # See <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/source-based-code-coverage.html>
# name: Coverage
# runs-on: ubuntu-latest
# timeout-minutes: 30
# steps:
# - uses: actions/checkout@v4
# - uses: dtolnay/rust-toolchain@nightly
# with:
# components: llvm-tools-preview
# - uses: Swatinem/rust-cache@v2
# - uses: taiki-e/install-action@cargo-llvm-cov
# - name: Generate code coverage
# env:
# # Covered tests are slower, so reduce the test load. (We also shouldn't
# # depend too much on case generation for coverage).
# PROPTEST_CASES: 5
# run: |
# # Generate profiles, but do not merge them
# cargo llvm-cov --no-report --workspace --all-features --all-targets -- --nocapture
# cargo llvm-cov --no-report --workspace --all-features --doc -- --nocapture
# - name: Merge profiles
# run: cargo llvm-cov --no-run --lcov --output-path lcov.info
# - name: Submit to codecov.io
# uses: codecov/codecov-action@v3
# with:
# token: ${{ secrets.CODECOV_TOKEN }} # Optional for public repos
# files: lcov.info
# fail_ci_if_error: true
# verbose: true

clippy:
name: Clippy
Expand Down
3 changes: 2 additions & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ allow = [
"Unicode-DFS-2016",
"Unlicense",
"MPL-2.0",
"CC0-1.0"
"CC0-1.0",
"Unicode-3.0",
]

[sources]
Expand Down
2 changes: 1 addition & 1 deletion src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub use self::{
add::{adc_n, sbb_n},
div::div,
gcd::{gcd, gcd_extended, inv_mod, LehmerMatrix},
mul::{add_nx1, addmul, addmul_n, addmul_nx1, addmul_ref, mul_nx1, submul_nx1},
mul::{add_nx1, addmul, addmul_n, addmul_nx1, mul_nx1, submul_nx1},
ops::{adc, sbb},
shift::{shift_left_small, shift_right_small},
};
Expand Down
149 changes: 67 additions & 82 deletions src/algorithms/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,6 @@

use crate::algorithms::{ops::sbb, DoubleWord};

#[inline]
#[allow(clippy::cast_possible_truncation)] // Intentional truncation.
#[allow(dead_code)] // Used for testing
pub fn addmul_ref(result: &mut [u64], a: &[u64], b: &[u64]) -> bool {
let mut overflow = 0;
for (i, a) in a.iter().copied().enumerate() {
let mut result = result.iter_mut().skip(i);
let mut b = b.iter().copied();
let mut carry = 0_u128;
loop {
match (result.next(), b.next()) {
// Partial product.
(Some(result), Some(b)) => {
carry += u128::from(*result) + u128::from(a) * u128::from(b);
*result = carry as u64;
carry >>= 64;
}
// Carry propagation.
(Some(result), None) => {
carry += u128::from(*result);
*result = carry as u64;
carry >>= 64;
}
// Excess product.
(None, Some(b)) => {
carry += u128::from(a) * u128::from(b);
overflow |= carry as u64;
carry >>= 64;
}
// Fin.
(None, None) => {
break;
}
}
}
overflow |= carry as u64;
}
overflow != 0
}

/// ⚠️ Computes `result += a * b` and checks for overflow.
///
/// **Warning.** This function is not part of the stable API.
Expand All @@ -62,7 +22,7 @@ pub fn addmul_ref(result: &mut [u64], a: &[u64], b: &[u64]) -> bool {
/// assert_eq!(overflow, false);
/// assert_eq!(result, [12]);
/// ```
#[inline]
#[inline(always)]
pub fn addmul(mut lhs: &mut [u64], mut a: &[u64], mut b: &[u64]) -> bool {
// Trim zeros from `a`
while let [0, rest @ ..] = a {
Expand Down Expand Up @@ -116,15 +76,13 @@ pub fn addmul(mut lhs: &mut [u64], mut a: &[u64], mut b: &[u64]) -> bool {
}

/// Computes `lhs += a` and returns the carry.
#[inline]
#[inline(always)]
pub fn add_nx1(lhs: &mut [u64], mut a: u64) -> u64 {
if a == 0 {
return 0;
}
for lhs in lhs {
let sum = u128::add(*lhs, a);
*lhs = sum.low();
a = sum.high();
(*lhs, a) = u128::add(*lhs, a).split();
if a == 0 {
return 0;
}
Expand All @@ -147,28 +105,26 @@ pub fn addmul_n(lhs: &mut [u64], a: &[u64], b: &[u64]) {
2 => addmul_2(lhs, a, b),
3 => addmul_3(lhs, a, b),
4 => addmul_4(lhs, a, b),
_ => {
let _ = addmul(lhs, a, b);
}
_ => _ = addmul(lhs, a, b),
}
}

/// Computes `lhs += a * b` for 1 limb.
#[inline(always)]
fn addmul_1(lhs: &mut [u64], a: &[u64], b: &[u64]) {
assert_eq!(lhs.len(), 1);
assert_eq!(a.len(), 1);
assert_eq!(b.len(), 1);
assume!(lhs.len() == 1);
assume!(a.len() == 1);
assume!(b.len() == 1);

mac(&mut lhs[0], a[0], b[0], 0);
}

/// Computes `lhs += a * b` for 2 limbs.
#[inline(always)]
fn addmul_2(lhs: &mut [u64], a: &[u64], b: &[u64]) {
assert_eq!(lhs.len(), 2);
assert_eq!(a.len(), 2);
assert_eq!(b.len(), 2);
assume!(lhs.len() == 2);
assume!(a.len() == 2);
assume!(b.len() == 2);

let carry = mac(&mut lhs[0], a[0], b[0], 0);
mac(&mut lhs[1], a[0], b[1], carry);
Expand All @@ -179,9 +135,9 @@ fn addmul_2(lhs: &mut [u64], a: &[u64], b: &[u64]) {
/// Computes `lhs += a * b` for 3 limbs.
#[inline(always)]
fn addmul_3(lhs: &mut [u64], a: &[u64], b: &[u64]) {
assert_eq!(lhs.len(), 3);
assert_eq!(a.len(), 3);
assert_eq!(b.len(), 3);
assume!(lhs.len() == 3);
assume!(a.len() == 3);
assume!(b.len() == 3);

let carry = mac(&mut lhs[0], a[0], b[0], 0);
let carry = mac(&mut lhs[1], a[0], b[1], carry);
Expand All @@ -196,9 +152,9 @@ fn addmul_3(lhs: &mut [u64], a: &[u64], b: &[u64]) {
/// Computes `lhs += a * b` for 4 limbs.
#[inline(always)]
fn addmul_4(lhs: &mut [u64], a: &[u64], b: &[u64]) {
assert_eq!(lhs.len(), 4);
assert_eq!(a.len(), 4);
assert_eq!(b.len(), 4);
assume!(lhs.len() == 4);
assume!(a.len() == 4);
assume!(b.len() == 4);

let carry = mac(&mut lhs[0], a[0], b[0], 0);
let carry = mac(&mut lhs[1], a[0], b[1], carry);
Expand All @@ -223,13 +179,11 @@ fn mac(lhs: &mut u64, a: u64, b: u64, c: u64) -> u64 {
}

/// Computes `lhs *= a` and returns the carry.
#[inline]
#[inline(always)]
pub fn mul_nx1(lhs: &mut [u64], a: u64) -> u64 {
let mut carry = 0;
for lhs in &mut *lhs {
let product = u128::muladd(*lhs, a, carry);
*lhs = product.low();
carry = product.high();
for lhs in lhs {
(*lhs, carry) = u128::muladd(*lhs, a, carry).split();
}
carry
}
Expand All @@ -244,14 +198,12 @@ pub fn mul_nx1(lhs: &mut [u64], a: u64) -> u64 {
/// \\\\ \mathsf{carry} &= \floor{\frac{\mathsf{lhs} + \mathsf{a} ⋅ \mathsf{b}
/// }{2^{64⋅N}}} \end{aligned}
/// $$
#[inline]
#[inline(always)]
pub fn addmul_nx1(lhs: &mut [u64], a: &[u64], b: u64) -> u64 {
debug_assert_eq!(lhs.len(), a.len());
assume!(lhs.len() == a.len());
let mut carry = 0;
for (lhs, a) in lhs.iter_mut().zip(a.iter().copied()) {
let product = u128::muladd2(a, b, carry, *lhs);
*lhs = product.low();
carry = product.high();
for i in 0..a.len() {
(lhs[i], carry) = u128::muladd2(a[i], b, carry, lhs[i]).split();
}
carry
}
Expand All @@ -267,23 +219,18 @@ pub fn addmul_nx1(lhs: &mut [u64], a: &[u64], b: u64) -> u64 {
/// \mathsf{lhs}}{2^{64⋅N}}} \end{aligned}
/// $$
// OPT: `carry` and `borrow` can probably be merged into a single var.
#[inline]
#[inline(always)]
pub fn submul_nx1(lhs: &mut [u64], a: &[u64], b: u64) -> u64 {
debug_assert_eq!(lhs.len(), a.len());
assume!(lhs.len() == a.len());
let mut carry = 0;
let mut borrow = 0;
for (lhs, a) in lhs.iter_mut().zip(a.iter().copied()) {
for i in 0..a.len() {
// Compute product limbs
let limb = {
let product = u128::muladd(a, b, carry);
carry = product.high();
product.low()
};
let limb;
(limb, carry) = u128::muladd(a[i], b, carry).split();

// Subtract
let (new, b) = sbb(*lhs, limb, borrow);
*lhs = new;
borrow = b;
(lhs[i], borrow) = sbb(lhs[i], limb, borrow);
}
borrow + carry
}
Expand All @@ -293,6 +240,44 @@ mod tests {
use super::*;
use proptest::{collection, num::u64, proptest};

#[allow(clippy::cast_possible_truncation)] // Intentional truncation.
fn addmul_ref(result: &mut [u64], a: &[u64], b: &[u64]) -> bool {
let mut overflow = 0;
for (i, a) in a.iter().copied().enumerate() {
let mut result = result.iter_mut().skip(i);
let mut b = b.iter().copied();
let mut carry = 0_u128;
loop {
match (result.next(), b.next()) {
// Partial product.
(Some(result), Some(b)) => {
carry += u128::from(*result) + u128::from(a) * u128::from(b);
*result = carry as u64;
carry >>= 64;
}
// Carry propagation.
(Some(result), None) => {
carry += u128::from(*result);
*result = carry as u64;
carry >>= 64;
}
// Excess product.
(None, Some(b)) => {
carry += u128::from(a) * u128::from(b);
overflow |= carry as u64;
carry >>= 64;
}
// Fin.
(None, None) => {
break;
}
}
}
overflow |= carry as u64;
}
overflow != 0
}

#[test]
fn test_addmul() {
let any_vec = collection::vec(u64::ANY, 0..10);
Expand Down
Loading

0 comments on commit 503b609

Please sign in to comment.