tasm_lib/hashing/algebraic_hasher/
sample_indices.rs1use triton_vm::prelude::*;
2
3use crate::list::length::Length;
4use crate::list::new::New;
5use crate::list::push::Push;
6use crate::prelude::*;
7
8#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
11pub struct SampleIndices;
12
13impl BasicSnippet for SampleIndices {
14 fn inputs(&self) -> Vec<(DataType, String)> {
15 vec![
16 (DataType::U32, "number".to_string()),
17 (DataType::U32, "upper_bound".to_string()),
18 ]
19 }
20
21 fn outputs(&self) -> Vec<(DataType, String)> {
22 vec![(
23 DataType::List(Box::new(DataType::U32)),
24 "*indices".to_string(),
25 )]
26 }
27
28 fn entrypoint(&self) -> String {
29 "tasmlib_hashing_algebraic_hasher_sample_indices".into()
30 }
31
32 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
33 let entrypoint = self.entrypoint();
34 let main_loop = format!("{entrypoint}_main_loop");
35 let then_reduce_and_save = format!("{entrypoint}_then_reduce_and_save");
36 let else_drop_tip = format!("{entrypoint}_else_drop_tip");
37
38 let new_list = library.import(Box::new(New));
39 let length = library.import(Box::new(Length));
40 let push_element = library.import(Box::new(Push::new(DataType::U32)));
41
42 let if_can_sample = triton_asm! (
43 dup 0 call {length} dup 3 eq push 0 eq dup 4 push -1 eq push 0 eq mul dup 0 push 0 eq swap 1 );
55
56 triton_asm! (
57 {entrypoint}:
60 call {new_list} swap 1 push -1 add swap 1 call {main_loop} swap 2 pop 2
70 return
71
72 {main_loop}:
74 dup 0 call {length} dup 3 eq skiz return sponge_squeeze dup 12 dup 12 dup 12 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
86 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
87 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
88 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
89 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
90 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
91 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
92 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
93 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
94 {&if_can_sample} skiz call {then_reduce_and_save} skiz call {else_drop_tip}
95 pop 3 recurse
100
101 {then_reduce_and_save}:
104 pop 1 swap 2 swap 3 split dup 2 and swap 1 pop 1 swap 1 swap 2 swap 1 dup 1 swap 1 call {push_element}
113
114 push 0
115 return
116
117 {else_drop_tip}:
120 swap 2 swap 3 pop 1 swap 1 return
123
124 )
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::empty_stack;
132 use crate::rust_shadowing_helper_functions;
133 use crate::test_prelude::*;
134
135 impl Procedure for SampleIndices {
136 fn rust_shadow(
137 &self,
138 stack: &mut Vec<BFieldElement>,
139 memory: &mut HashMap<BFieldElement, BFieldElement>,
140 _: &NonDeterminism,
141 _: &[BFieldElement],
142 sponge: &mut Option<Tip5>,
143 ) -> Vec<BFieldElement> {
144 let sponge = sponge.as_mut().expect("sponge must be initialized");
145
146 let upper_bound = stack.pop().unwrap().value() as u32;
148 let number = stack.pop().unwrap().value() as usize;
149
150 println!("sampling {number} indices between 0 and {upper_bound}");
151 println!("sponge before: {}", sponge.state.iter().join(","));
152
153 let indices = sponge.sample_indices(upper_bound, number);
154
155 let list_pointer =
157 rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator(memory);
158 rust_shadowing_helper_functions::list::list_new(list_pointer, memory);
159
160 for index in indices.iter() {
162 rust_shadowing_helper_functions::list::list_push(
163 list_pointer,
164 vec![BFieldElement::new(*index as u64)],
165 memory,
166 );
167 }
168 println!("sponge after: {}", sponge.state.iter().join(","));
169
170 stack.push(list_pointer);
171
172 vec![]
173 }
174
175 fn pseudorandom_initial_state(
176 &self,
177 seed: [u8; 32],
178 bench_case: Option<BenchmarkCase>,
179 ) -> ProcedureInitialState {
180 let mut rng = StdRng::from_seed(seed);
181 let number = if let Some(case) = bench_case {
182 match case {
183 BenchmarkCase::CommonCase => 40,
185
186 BenchmarkCase::WorstCase => 80,
188 }
189 } else {
190 rng.random_range(0..20)
191 };
192 let upper_bound = if let Some(case) = bench_case {
193 match case {
194 BenchmarkCase::CommonCase => 1 << 12,
195 BenchmarkCase::WorstCase => 1 << 23,
196 }
197 } else {
198 1 << rng.random_range(0..20)
199 };
200
201 let mut stack = empty_stack();
202 stack.push(BFieldElement::new(number as u64));
203 stack.push(BFieldElement::new(upper_bound as u64));
204
205 let public_input: Vec<BFieldElement> = vec![];
206 let state = Tip5 {
207 state: rng.random(),
208 };
209
210 ProcedureInitialState {
211 stack,
212 nondeterminism: NonDeterminism::default(),
213 public_input,
214 sponge: Some(state),
215 }
216 }
217 }
218
219 #[test]
220 fn test() {
221 ShadowedProcedure::new(SampleIndices).test();
222 }
223}
224
225#[cfg(test)]
226mod bench {
227 use super::*;
228 use crate::test_prelude::*;
229
230 #[test]
231 fn benchmark() {
232 ShadowedProcedure::new(SampleIndices).bench();
233 }
234}