tasm_lib/arithmetic/u64/mul_two_u64s_to_u128.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
use triton_vm::prelude::*;
use crate::arithmetic::u64::safe_mul::SafeMul;
use crate::prelude::*;
/// Multiply two `u64`s, resulting in a `u128`.
///
/// ### Behavior
///
/// ```text
/// BEFORE: _ [right: u64] [left: u64]
/// AFTER: _ [left · right: u128]
/// ```
///
/// ### Preconditions
///
/// - all input arguments are properly [`BFieldCodec`] encoded
///
/// ### Postconditions
///
/// - the output is the product of the input
/// - the output is properly [`BFieldCodec`] encoded
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct MulTwoU64sToU128;
impl BasicSnippet for MulTwoU64sToU128 {
fn inputs(&self) -> Vec<(DataType, String)> {
SafeMul.inputs()
}
fn outputs(&self) -> Vec<(DataType, String)> {
vec![(DataType::U128, "product".to_string())]
}
fn entrypoint(&self) -> String {
"tasmlib_arithmetic_u64_mul_two_u64s_to_u128_u64".to_string()
}
fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> {
triton_asm!(
// BEFORE: _ r_hi r_lo l_hi l_lo
// AFTER: _ p_3 p_2 p_1 p_0
{self.entrypoint()}:
/*
* p_0 is low limb, c_0 high limb of l_lo·r_lo
* p_1 is low limb, c_1 high limb of (l_lo·r_hi)_lo + (l_hi·r_lo)_lo + c_0
* p_2 is low limb, c_2 high limb of (l_lo·r_hi)_hi + (l_hi·r_lo)_hi + (l_hi·r_hi)_lo + c_1
* p_3 is low limb (l_hi·r_hi)_hi + c_2
*
* There's no carry c_3 because max value of (l_hi·r_hi)_hi is 0xfffffffe.
*/
/* p_0 */
dup 0
dup 3
mul
split // _ r_hi r_lo l_hi l_lo c_0 p_0
/* p_1 */
swap 2 // _ r_hi r_lo l_hi p_0 c_0 l_lo
dup 5
mul
split // _ r_hi r_lo l_hi p_0 c_0 (l_lo·r_hi)_hi (l_lo·r_hi)_lo
pick 1
swap 5 // _ r_hi (l_lo·r_hi)_hi l_hi p_0 c_0 (l_lo·r_hi)_lo r_lo
dup 4
mul
split // _ r_hi (l_lo·r_hi)_hi l_hi p_0 c_0 (l_lo·r_hi)_lo (r_lo·l_hi)_hi (r_lo·l_hi)_lo
pick 1
place 3 // _ r_hi (l_lo·r_hi)_hi l_hi p_0 (r_lo·l_hi)_hi c_0 (l_lo·r_hi)_lo (r_lo·l_hi)_lo
add
add
split // _ r_hi (l_lo·r_hi)_hi l_hi p_0 (r_lo·l_hi)_hi c_1 p_1
/* p_2 */
swap 4 // _ r_hi (l_lo·r_hi)_hi p_1 p_0 (r_lo·l_hi)_hi c_1 l_hi
pick 6 // _ (l_lo·r_hi)_hi p_1 p_0 (r_lo·l_hi)_hi c_1 l_hi r_hi
mul
split // _ (l_lo·r_hi)_hi p_1 p_0 (r_lo·l_hi)_hi c_1 (l_hi·r_hi)_hi (l_hi·r_hi)_lo
pick 1
swap 6 // _ (l_hi·r_hi)_hi p_1 p_0 (r_lo·l_hi)_hi c_1 (l_hi·r_hi)_lo (l_lo·r_hi)_hi
add
add
add
split // _ (l_hi·r_hi)_hi p_1 p_0 c_2 p_2
/* p_3 */
swap 4 // _ p_2 p_1 p_0 c_2 (l_hi·r_hi)_hi
add // _ p_2 p_1 p_0 p_3
place 3 // _ p_3 p_2 p_1 p_0
return
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arithmetic::u64::and::And;
use crate::test_prelude::*;
impl Closure for MulTwoU64sToU128 {
type Args = (u64, u64);
fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) {
let (right, left) = pop_encodable::<Self::Args>(stack);
let product = u128::from(left) * u128::from(right);
push_encodable(stack, &product);
}
fn pseudorandom_args(
&self,
seed: [u8; 32],
bench_case: Option<BenchmarkCase>,
) -> Self::Args {
match bench_case {
Some(BenchmarkCase::CommonCase) => (1 << 63, (1 << 45) - 1),
Some(BenchmarkCase::WorstCase) => (1 << 63, (1 << 63) - 1),
None => StdRng::from_seed(seed).random(),
}
}
fn corner_case_args(&self) -> Vec<Self::Args> {
And.corner_case_args()
}
}
#[test]
fn rust_shadow() {
ShadowedClosure::new(MulTwoU64sToU128).test();
}
}
#[cfg(test)]
mod benches {
use super::*;
use crate::test_prelude::*;
#[test]
fn benchmark() {
ShadowedClosure::new(MulTwoU64sToU128).bench();
}
}