#[cfg(zisk_guest)]
use crate::alloc_extern::vec;
#[cfg(zisk_guest)]
use crate::alloc_extern::vec::Vec;
use crate::syscalls::{
syscall_add256, syscall_arith256, SyscallAdd256Params, SyscallArith256Params,
};
use super::{mul_short, rem_long, LongScratch, U256};
pub fn mul_long(
a: &[U256],
b: &[U256],
out: &mut [U256],
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> usize {
let len_a = a.len();
let len_b = b.len();
#[cfg(debug_assertions)]
{
assert_ne!(len_a, 0, "Input 'a' must have at least one limb");
assert_ne!(len_b, 0, "Input 'b' must have at least one limb");
if len_a > 1 {
assert!(!a[len_a - 1].is_zero(), "Input 'a' must not have leading zeros");
}
if len_b > 1 {
assert!(!b[len_b - 1].is_zero(), "Input 'b' must not have leading zeros");
}
}
let mut params = SyscallArith256Params {
a: a[0].as_limbs(),
b: b[0].as_limbs(),
c: U256::ZERO.as_limbs(),
dl: out[0].as_limbs_mut(),
dh: &mut [0, 0, 0, 0],
};
syscall_arith256(
&mut params,
#[cfg(feature = "hints")]
hints,
);
out[1] = U256::from_u64s(params.dh);
for j in 1..len_b {
let out_j = out[j];
let mut params = SyscallArith256Params {
a: a[0].as_limbs(),
b: b[j].as_limbs(),
c: out_j.as_limbs(),
dl: out[j].as_limbs_mut(),
dh: &mut [0, 0, 0, 0],
};
syscall_arith256(
&mut params,
#[cfg(feature = "hints")]
hints,
);
out[j + 1] = U256::from_u64s(params.dh);
}
let last_b_idx = len_b - 1;
for i in 1..len_a {
let mut carry = 0u64;
#[allow(clippy::needless_range_loop)]
for j in 0..last_b_idx {
let k = i + j;
let out_ij = out[k];
let mut params_arith = SyscallArith256Params {
a: a[i].as_limbs(),
b: b[j].as_limbs(),
c: out_ij.as_limbs(),
dl: &mut [0, 0, 0, 0],
dh: &mut [0, 0, 0, 0],
};
syscall_arith256(
&mut params_arith,
#[cfg(feature = "hints")]
hints,
);
out[k] = U256::from_u64s(params_arith.dl);
let out_ij1 = out[k + 1];
let mut params_add = SyscallAdd256Params {
a: out_ij1.as_limbs(),
b: params_arith.dh,
cin: carry,
c: out[k + 1].as_limbs_mut(),
};
carry = syscall_add256(
&mut params_add,
#[cfg(feature = "hints")]
hints,
);
}
let k = i + last_b_idx;
let out_ilb1 = out[k];
let mut params_arith = SyscallArith256Params {
a: a[i].as_limbs(),
b: b[last_b_idx].as_limbs(),
c: out_ilb1.as_limbs(),
dl: out[k].as_limbs_mut(),
dh: &mut [0, 0, 0, 0],
};
syscall_arith256(
&mut params_arith,
#[cfg(feature = "hints")]
hints,
);
if carry == 1 {
let a_in = *params_arith.dh;
let mut params_add = SyscallAdd256Params {
a: &a_in,
b: U256::ZERO.as_limbs(),
cin: 1,
c: params_arith.dh,
};
let _carry = syscall_add256(
&mut params_add,
#[cfg(feature = "hints")]
hints,
);
debug_assert!(_carry == 0, "Unexpected carry in intermediate addition");
}
out[i + len_b] = U256::from_u64s(params_arith.dh);
}
if out[len_a + len_b - 1].is_zero() {
len_a + len_b - 1
} else {
len_a + len_b
}
}
pub fn mul_and_reduce_long(
a: &[U256],
b: &[U256],
modulus: &[U256],
scratch: &mut LongScratch,
#[cfg(feature = "hints")] hints: &mut Vec<u64>,
) -> Vec<U256> {
#[cfg(debug_assertions)]
{
let len_m = modulus.len();
assert_ne!(len_m, 0, "Input 'modulus' must have at least one limb");
assert!(!modulus[len_m - 1].is_zero(), "Input 'modulus' must not have leading zeros");
}
let mul_len = if b.len() == 1 {
mul_short(
a,
&b[0],
&mut scratch.mul,
#[cfg(feature = "hints")]
hints,
)
} else {
mul_long(
a,
b,
&mut scratch.mul,
#[cfg(feature = "hints")]
hints,
)
};
rem_long(
&scratch.mul[..mul_len],
modulus,
&mut scratch.rem,
#[cfg(feature = "hints")]
hints,
)
}