use num_bigint::BigUint;
use num_integer::Integer;
use crate::zisklib::fcalls_impl::utils::{biguint_from_u64_digits, n_u64_digits_from_biguint};
use crate::zisklib::ModInvResult;
pub fn fcall_uint256_inv_mod(params: &[u64], results: &mut [u64]) -> i64 {
let a = ¶ms[0..4].try_into().unwrap();
let modulus = ¶ms[4..8].try_into().unwrap();
match uint256_inv_mod(a, modulus) {
ModInvResult::Inverse(inv) => {
results[0] = 1;
results[1..5].copy_from_slice(&inv);
results[5..13].fill(0);
}
ModInvResult::NoInverse { gcd, qa, qm } => {
results[0] = 0;
results[1..5].copy_from_slice(&gcd);
results[5..9].copy_from_slice(&qa);
results[9..13].copy_from_slice(&qm);
}
}
13
}
pub fn uint256_inv_mod(a: &[u64; 4], modulus: &[u64; 4]) -> ModInvResult {
let a_big = biguint_from_u64_digits(a);
let modulus_big = biguint_from_u64_digits(modulus);
if let Some(inv) = a_big.modinv(&modulus_big) {
ModInvResult::Inverse(n_u64_digits_from_biguint::<4>(&inv))
} else {
let gcd = a_big.gcd(&modulus_big);
let qa = n_u64_digits_from_biguint::<4>(&(&a_big / &gcd));
let qm = n_u64_digits_from_biguint::<4>(&(&modulus_big / &gcd));
ModInvResult::NoInverse { gcd: n_u64_digits_from_biguint::<4>(&gcd), qa, qm }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inv_mod_modulus_one() {
let modulus = [1, 0, 0, 0];
for a in [[0, 0, 0, 0], [1, 0, 0, 0], [1, 2, 3, 4]] {
let params = [a, modulus].concat();
let mut results = [0; 13];
let n = fcall_uint256_inv_mod(¶ms, &mut results);
assert_eq!(n, 13);
assert_eq!(results, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
}
}
#[test]
fn test_inv_mod_basic() {
let modulus = [12, 0, 0, 0];
let a = [13, 0, 0, 0];
let params = [a, modulus].concat();
let mut results = [0; 13];
fcall_uint256_inv_mod(¶ms, &mut results);
assert_eq!(results, [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
let a = [6, 0, 0, 0];
let params = [a, modulus].concat();
let mut results = [0; 13];
fcall_uint256_inv_mod(¶ms, &mut results);
assert_eq!(results, [0, 6, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0]);
}
#[test]
fn test_inv_mod_rand() {
let modulus =
[0xacca9ca1b4f3b763, 0x57d556242ac9c0ed, 0x6e3d795231a618cb, 0x36835e1b448f5df6];
let a = [0x48c964556ed2d279, 0xf692d9a779303069, 0xcc8d5e70e9f03415, 0xec53e64d5abb6d04];
let params = [a, modulus].concat();
let mut results = [0; 13];
fcall_uint256_inv_mod(¶ms, &mut results);
assert_eq!(
results,
[
1,
0xcede99fad6bbe0a2,
0x2c99e1d7ed681658,
0x2a8d1689b5e7bfaf,
0x20d97a86f6e5e3a4,
0,
0,
0,
0,
0,
0,
0,
0
]
);
let a = [0x844efa1db3aaaa7d, 0xfbc4783fdfea63b7, 0xd30100f0dc1f7df6, 0x444a];
let params = [a, modulus].concat();
let mut results = [0; 13];
fcall_uint256_inv_mod(¶ms, &mut results);
assert_eq!(
results,
[
0,
0x844efa1db3aaaa7d,
0xfbc4783fdfea63b7,
0xd30100f0dc1f7df6,
0x444a,
1,
0,
0,
0,
0xcc58ffcfaf5f,
0,
0,
0
]
);
}
}