tasm_lib/arithmetic/u64/
mul_two_u64s_to_u128.rs

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