|
1 | 1 | use std::collections::HashMap;
|
2 | 2 |
|
3 |
| -use num::Zero; |
4 |
| -use rand::prelude::*; |
5 | 3 | use triton_vm::prelude::*;
|
6 |
| -use twenty_first::prelude::U32s; |
7 | 4 |
|
8 |
| -use crate::empty_stack; |
9 | 5 | use crate::prelude::*;
|
10 |
| -use crate::push_encodable; |
11 |
| -use crate::traits::deprecated_snippet::DeprecatedSnippet; |
12 |
| -use crate::InitVmState; |
13 |
| - |
14 |
| -#[derive(Clone, Debug)] |
| 6 | +use crate::traits::basic_snippet::Reviewer; |
| 7 | +use crate::traits::basic_snippet::SignOffFingerprint; |
| 8 | + |
| 9 | +/// [Bitwise xor][bitxor] of two `u64`s. |
| 10 | +/// |
| 11 | +/// ### Behavior |
| 12 | +/// |
| 13 | +/// ```text |
| 14 | +/// BEFORE: _ [right: u64] [left: u64] |
| 15 | +/// AFTER: _ [xor: u64] |
| 16 | +/// ``` |
| 17 | +/// |
| 18 | +/// ### Preconditions |
| 19 | +/// |
| 20 | +/// - all input arguments are properly [`BFieldCodec`] encoded |
| 21 | +/// |
| 22 | +/// ### Postconditions |
| 23 | +/// |
| 24 | +/// - the output is the bitwise “xor” of the input |
| 25 | +/// - the output is properly [`BFieldCodec`] encoded |
| 26 | +/// |
| 27 | +/// [bitxor]: core::ops::BitXor |
| 28 | +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] |
15 | 29 | pub struct Xor;
|
16 | 30 |
|
17 |
| -impl DeprecatedSnippet for Xor { |
18 |
| - fn entrypoint_name(&self) -> String { |
19 |
| - "tasmlib_arithmetic_u64_xor".to_string() |
| 31 | +impl BasicSnippet for Xor { |
| 32 | + fn inputs(&self) -> Vec<(DataType, String)> { |
| 33 | + ["right", "left"] |
| 34 | + .map(|side| (DataType::U64, side.to_string())) |
| 35 | + .to_vec() |
20 | 36 | }
|
21 | 37 |
|
22 |
| - fn input_field_names(&self) -> Vec<String> { |
23 |
| - vec![ |
24 |
| - "rhs_hi".to_string(), |
25 |
| - "rhs_lo".to_string(), |
26 |
| - "lhs_hi".to_string(), |
27 |
| - "lhs_lo".to_string(), |
28 |
| - ] |
| 38 | + fn outputs(&self) -> Vec<(DataType, String)> { |
| 39 | + vec![(DataType::U64, "xor".to_string())] |
29 | 40 | }
|
30 | 41 |
|
31 |
| - fn input_types(&self) -> Vec<DataType> { |
32 |
| - vec![DataType::U64, DataType::U64] |
33 |
| - } |
34 |
| - |
35 |
| - fn output_field_names(&self) -> Vec<String> { |
36 |
| - vec!["(lhs ^ rhs)_hi".to_string(), "(lhs ^ rhs)_lo".to_string()] |
37 |
| - } |
38 |
| - |
39 |
| - fn output_types(&self) -> Vec<DataType> { |
40 |
| - vec![DataType::U64] |
41 |
| - } |
42 |
| - |
43 |
| - fn stack_diff(&self) -> isize { |
44 |
| - -2 |
| 42 | + fn entrypoint(&self) -> String { |
| 43 | + "tasmlib_arithmetic_u64_xor".to_string() |
45 | 44 | }
|
46 | 45 |
|
47 |
| - fn function_code(&self, _library: &mut Library) -> String { |
48 |
| - let entrypoint = self.entrypoint_name(); |
49 |
| - format!( |
50 |
| - " |
51 |
| - // BEFORE: rhs_hi rhs_lo lhs_hi lhs_lo |
52 |
| - // AFTER: (rhs ^ lhs)_hi (rhs ^ lhs)_lo |
53 |
| - {entrypoint}: |
| 46 | + fn code(&self, _: &mut Library) -> Vec<LabelledInstruction> { |
| 47 | + triton_asm!( |
| 48 | + // BEFORE: _ rhs_hi rhs_lo lhs_hi lhs_lo |
| 49 | + // AFTER: _ (rhs ^ lhs)_hi (rhs ^ lhs)_lo |
| 50 | + {self.entrypoint()}: |
54 | 51 | swap 3
|
55 | 52 | xor
|
56 |
| - // stack: _ lhs_lo rhs_lo (lhs_hi ^ rhs_hi) |
| 53 | + // _ lhs_lo rhs_lo (lhs_hi ^ rhs_hi) |
57 | 54 |
|
58 | 55 | swap 2
|
59 | 56 | xor
|
60 |
| - // stack: _ (lhs_hi ^ rhs_hi) (rhs_lo ^ lhs_lo) |
| 57 | + // _ (lhs_hi ^ rhs_hi) (lhs_lo ^ rhs_lo) |
| 58 | + // _ (lhs ^ rhs)_hi (lhs ^ rhs)_lo |
61 | 59 |
|
62 | 60 | return
|
63 |
| - " |
64 | 61 | )
|
65 | 62 | }
|
66 | 63 |
|
67 |
| - fn crash_conditions(&self) -> Vec<String> { |
68 |
| - vec!["any input is not u32".to_string()] |
69 |
| - } |
70 |
| - |
71 |
| - fn gen_input_states(&self) -> Vec<InitVmState> { |
72 |
| - let mut rng = rand::thread_rng(); |
73 |
| - let lhs = U32s::<2>::try_from(rng.next_u64()).unwrap(); |
74 |
| - let rhs = U32s::<2>::try_from(rng.next_u64()).unwrap(); |
75 |
| - let mut stack = empty_stack(); |
76 |
| - push_encodable(&mut stack, &lhs); |
77 |
| - push_encodable(&mut stack, &rhs); |
78 |
| - vec![InitVmState::with_stack(stack)] |
79 |
| - } |
80 |
| - |
81 |
| - fn common_case_input_state(&self) -> InitVmState { |
82 |
| - InitVmState::with_stack( |
83 |
| - [ |
84 |
| - empty_stack(), |
85 |
| - vec![BFieldElement::zero(), BFieldElement::new((1 << 31) - 1)], |
86 |
| - vec![BFieldElement::zero(), BFieldElement::new((1 << 10) - 1)], |
87 |
| - ] |
88 |
| - .concat(), |
89 |
| - ) |
90 |
| - } |
91 |
| - |
92 |
| - fn worst_case_input_state(&self) -> InitVmState { |
93 |
| - InitVmState::with_stack( |
94 |
| - [ |
95 |
| - empty_stack(), |
96 |
| - vec![BFieldElement::new(1 << 31), BFieldElement::new(1 << 31)], |
97 |
| - vec![ |
98 |
| - BFieldElement::new(1 << 30), |
99 |
| - BFieldElement::new((1 << 31) + 10), |
100 |
| - ], |
101 |
| - ] |
102 |
| - .concat(), |
103 |
| - ) |
104 |
| - } |
105 |
| - |
106 |
| - fn rust_shadowing( |
107 |
| - &self, |
108 |
| - stack: &mut Vec<BFieldElement>, |
109 |
| - _std_in: Vec<BFieldElement>, |
110 |
| - _secret_in: Vec<BFieldElement>, |
111 |
| - _memory: &mut HashMap<BFieldElement, BFieldElement>, |
112 |
| - ) { |
113 |
| - // top element on stack |
114 |
| - let a_lo: u32 = stack.pop().unwrap().try_into().unwrap(); |
115 |
| - let a_hi: u32 = stack.pop().unwrap().try_into().unwrap(); |
116 |
| - |
117 |
| - // second element on stack |
118 |
| - let b_lo: u32 = stack.pop().unwrap().try_into().unwrap(); |
119 |
| - let b_hi: u32 = stack.pop().unwrap().try_into().unwrap(); |
120 |
| - |
121 |
| - // Perform calculation and write the result back to the stack |
122 |
| - let xor_res = U32s::<2>::new([a_lo ^ b_lo, a_hi ^ b_hi]); |
123 |
| - let mut res = xor_res.encode(); |
124 |
| - for _ in 0..res.len() { |
125 |
| - stack.push(res.pop().unwrap()); |
126 |
| - } |
| 64 | + fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> { |
| 65 | + let mut sign_offs = HashMap::new(); |
| 66 | + sign_offs.insert(Reviewer("ferdinand"), 0x7ed1b73a89177516.into()); |
| 67 | + sign_offs |
127 | 68 | }
|
128 | 69 | }
|
129 | 70 |
|
130 | 71 | #[cfg(test)]
|
131 | 72 | mod tests {
|
132 |
| - use num::BigUint; |
133 |
| - |
134 | 73 | use super::*;
|
135 |
| - use crate::empty_stack; |
136 |
| - use crate::test_helpers::test_rust_equivalence_given_input_values_deprecated; |
137 |
| - use crate::test_helpers::test_rust_equivalence_multiple_deprecated; |
| 74 | + use crate::test_prelude::*; |
138 | 75 |
|
139 |
| - #[test] |
140 |
| - fn xor_u64_test() { |
141 |
| - test_rust_equivalence_multiple_deprecated(&Xor, true); |
142 |
| - } |
| 76 | + impl Closure for Xor { |
| 77 | + type Args = (u64, u64); |
143 | 78 |
|
144 |
| - #[test] |
145 |
| - fn xor_test_simple() { |
146 |
| - prop_xor(4, 3); |
147 |
| - prop_xor(4, 4); |
148 |
| - prop_xor(u64::MAX, u64::MAX); |
149 |
| - prop_xor(0, u64::MAX); |
150 |
| - prop_xor(u64::MAX, 0); |
151 |
| - } |
152 |
| - |
153 |
| - #[test] |
154 |
| - fn xor_test_pbt() { |
155 |
| - let mut rng = thread_rng(); |
156 |
| - for _ in 0..100 { |
157 |
| - let lhs = rng.next_u64(); |
158 |
| - let rhs = rng.next_u64(); |
159 |
| - prop_xor(lhs, rhs); |
160 |
| - } |
161 |
| - } |
162 |
| - |
163 |
| - fn prop_xor(lhs: u64, rhs: u64) { |
164 |
| - let mut init_stack = empty_stack(); |
165 |
| - |
166 |
| - let rhs_u32_2 = U32s::<2>::new([(rhs & u32::MAX as u64) as u32, (rhs >> 32) as u32]); |
167 |
| - for elem in rhs_u32_2.encode().into_iter().rev() { |
168 |
| - init_stack.push(elem); |
| 79 | + fn rust_shadow(&self, stack: &mut Vec<BFieldElement>) { |
| 80 | + let (right, left) = pop_encodable::<Self::Args>(stack); |
| 81 | + push_encodable(stack, &(left ^ right)); |
169 | 82 | }
|
170 | 83 |
|
171 |
| - let lhs_u32_2 = U32s::<2>::new([(lhs & u32::MAX as u64) as u32, (lhs >> 32) as u32]); |
172 |
| - for elem in lhs_u32_2.encode().into_iter().rev() { |
173 |
| - init_stack.push(elem); |
174 |
| - } |
175 |
| - |
176 |
| - let expected_res: BigUint = (lhs ^ rhs).into(); |
177 |
| - let expected_u32_2: U32s<2> = expected_res.into(); |
178 |
| - let mut expected_end_stack = empty_stack(); |
179 |
| - for elem in expected_u32_2.encode().into_iter().rev() { |
180 |
| - expected_end_stack.push(elem); |
| 84 | + fn pseudorandom_args( |
| 85 | + &self, |
| 86 | + seed: [u8; 32], |
| 87 | + bench_case: Option<BenchmarkCase>, |
| 88 | + ) -> Self::Args { |
| 89 | + match bench_case { |
| 90 | + Some(BenchmarkCase::CommonCase) => (0x7fff_ffff, 0x3ff), |
| 91 | + Some(BenchmarkCase::WorstCase) => (0x8000_0000_8000_0000, 0x4000_0000_8000_000a), |
| 92 | + None => StdRng::from_seed(seed).gen(), |
| 93 | + } |
181 | 94 | }
|
| 95 | + } |
182 | 96 |
|
183 |
| - test_rust_equivalence_given_input_values_deprecated( |
184 |
| - &Xor, |
185 |
| - &init_stack, |
186 |
| - &[], |
187 |
| - HashMap::default(), |
188 |
| - Some(&expected_end_stack), |
189 |
| - ); |
| 97 | + #[test] |
| 98 | + fn rust_shadow() { |
| 99 | + ShadowedClosure::new(Xor).test(); |
190 | 100 | }
|
191 | 101 | }
|
192 | 102 |
|
193 | 103 | #[cfg(test)]
|
194 | 104 | mod benches {
|
195 | 105 | use super::*;
|
196 |
| - use crate::snippet_bencher::bench_and_write; |
| 106 | + use crate::test_prelude::*; |
197 | 107 |
|
198 | 108 | #[test]
|
199 |
| - fn xor_u64_benchmark() { |
200 |
| - bench_and_write(Xor); |
| 109 | + fn benchmark() { |
| 110 | + ShadowedClosure::new(Xor).bench(); |
201 | 111 | }
|
202 | 112 | }
|
0 commit comments