Skip to content

Commit

Permalink
mod optimisations (#239)
Browse files Browse the repository at this point in the history
<!--- Please provide a general summary of your changes in the title
above -->

## Pull Request type

Optimisations for mod arithmetics.

Please check the type of change your PR introduces:

- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [x] Refactoring (no functional changes, no API changes)
- [ ] Build-related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

Mod operations not as fast as they can be.

Issue Number: N/A

## What is the new behavior?

> ⚠️ `div_mod` test included here is added in PR #238

```
DIVISION OPTIMISATION (from core::math functions)
// div_mod_test     BEFORE gas usage est.: 97351914
// div_mod_test     BEFORE gas usage est.: 277900

MINOR optimisations (from inline)
// add_mod_1_test   BEFORE gas usage est.: 163180
// add_mod_1_test   AFTER gas usage est.:  159880
// add_mod_2_test   BEFORE gas usage est.: 109020
// add_mod_2_test   AFTER gas usage est.:  106820
// add_mod_p_test   BEFORE gas usage est.: 433980
// add_mod_p_test   AFTER gas usage est.:  425180
// mult_mod_1_test  BEFORE gas usage est.: 455340
// mult_mod_1_test  AFTER gas usage est.:  444940
// mult_mod_2_test  BEFORE gas usage est.: 455340
// mult_mod_2_test  AFTER gas usage est.:  444940
// mult_mod_test    BEFORE gas usage est.: 455340
// mult_mod_test    AFTER gas usage est.:  444940
// sub_mod_1_test   BEFORE gas usage est.: 330380
// sub_mod_1_test   AFTER gas usage est.:  319980
// sub_mod_2_test   BEFORE gas usage est.: 330380
// sub_mod_2_test   AFTER gas usage est.:  319980
// sub_mod_test     BEFORE gas usage est.: 356100
// sub_mod_test     AFTER gas usage est.:  346300
```

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this does introduce a breaking change, please describe the
impact and migration path for existing applications below. -->

## Other information

<!-- Any other information that is important to this PR, such as
screenshots of how the component looks before and after the change. -->
  • Loading branch information
shramee authored Jan 12, 2024
1 parent 52abb0d commit 9fe154f
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions src/math/src/mod_arithmetics.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use integer::u512;
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - result of modular addition
#[inline(always)]
fn add_mod(a: u256, b: u256, modulo: u256) -> u256 {
let mod_non_zero: NonZero<u256> = integer::u256_try_as_non_zero(modulo).unwrap();
let low: u256 = a.low.into() + b.low.into();
Expand All @@ -25,10 +26,12 @@ fn add_mod(a: u256, b: u256, modulo: u256) -> u256 {
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - modular multiplicative inverse
#[inline(always)]
fn mult_inverse(b: u256, modulo: u256) -> u256 {
// From Fermat's little theorem, a ^ (p - 1) = 1 when p is prime and a != 0. Since a ^ (p - 1) = a · a ^ (p - 2) we have that
// a ^ (p - 2) is the multiplicative inverse of a modulo p.
pow_mod(b, modulo - 2, modulo)
match math::u256_guarantee_inv_mod_n(b, modulo.try_into().expect('inverse non zero')) {
Result::Ok((inv_a, _, _, _, _, _, _, _, _)) => inv_a.into(),
Result::Err(_) => 0
}
}

/// Function that return the modular additive inverse.
Expand All @@ -37,6 +40,7 @@ fn mult_inverse(b: u256, modulo: u256) -> u256 {
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - modular additive inverse
#[inline(always)]
fn add_inverse_mod(b: u256, modulo: u256) -> u256 {
modulo - b
}
Expand All @@ -48,6 +52,7 @@ fn add_inverse_mod(b: u256, modulo: u256) -> u256 {
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - result of modular substraction
#[inline(always)]
fn sub_mod(mut a: u256, mut b: u256, modulo: u256) -> u256 {
// reduce values
a = a % modulo;
Expand All @@ -65,6 +70,7 @@ fn sub_mod(mut a: u256, mut b: u256, modulo: u256) -> u256 {
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - result of modular multiplication
#[inline(always)]
fn mult_mod(a: u256, b: u256, modulo: u256) -> u256 {
let mult: u512 = integer::u256_wide_mul(a, b);
let mod_non_zero: NonZero<u256> = integer::u256_try_as_non_zero(modulo).unwrap();
Expand All @@ -79,8 +85,11 @@ fn mult_mod(a: u256, b: u256, modulo: u256) -> u256 {
/// * `modulo` - modulo.
/// # Returns
/// * `u256` - result of modular division
#[inline(always)]
fn div_mod(a: u256, b: u256, modulo: u256) -> u256 {
mult_mod(a, mult_inverse(b, modulo), modulo)
let modulo_nz = modulo.try_into().expect('0 modulo');
let inv = math::u256_inv_mod(b, modulo_nz).unwrap().into();
math::u256_mul_mod_n(a, inv, modulo_nz)
}

/// Function that performs modular exponentiation.
Expand Down

0 comments on commit 9fe154f

Please sign in to comment.