tasm_lib/hashing/algebraic_hasher/
sample_scalars_static_length_dyn_malloc.rs1use triton_vm::prelude::*;
2use twenty_first::math::x_field_element::EXTENSION_DEGREE;
3use twenty_first::tip5::RATE;
4
5use crate::data_type::ArrayType;
6use crate::hashing::squeeze_repeatedly_static_number::SqueezeRepeatedlyStaticNumber;
7use crate::prelude::*;
8
9#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub struct SampleScalarsStaticLengthDynMalloc {
12 pub num_elements: usize,
13}
14
15impl SampleScalarsStaticLengthDynMalloc {
16 pub(super) fn num_squeezes(num_elements: usize) -> usize {
17 (num_elements * EXTENSION_DEGREE).div_ceil(RATE)
18 }
19}
20
21impl BasicSnippet for SampleScalarsStaticLengthDynMalloc {
22 fn inputs(&self) -> Vec<(DataType, String)> {
23 vec![]
24 }
25
26 fn outputs(&self) -> Vec<(DataType, String)> {
27 vec![(
28 DataType::Array(Box::new(ArrayType {
29 element_type: DataType::Xfe,
30 length: self.num_elements,
31 })),
32 "*scalars".to_owned(),
33 )]
34 }
35
36 fn entrypoint(&self) -> String {
37 format!(
38 "tasmlib_hashing_algebraic_hasher_sample_scalars_static_length_dyn_malloc_{}",
39 self.num_elements
40 )
41 }
42
43 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
44 assert_eq!(10, RATE, "Code assumes Tip5's RATE is 10");
45 assert_eq!(3, EXTENSION_DEGREE, "Code assumes extension degree 3");
46 let num_squeezes = Self::num_squeezes(self.num_elements);
47
48 debug_assert!(
49 self.num_elements * EXTENSION_DEGREE <= num_squeezes * RATE,
50 "need {} elements but getting {}",
51 self.num_elements * EXTENSION_DEGREE,
52 num_squeezes * RATE
53 );
54
55 let entrypoint = self.entrypoint();
56 let squeeze_repeatedly_static_number =
57 library.import(Box::new(SqueezeRepeatedlyStaticNumber { num_squeezes }));
58 let dyn_malloc = library.import(Box::new(DynMalloc));
59
60 triton_asm!(
61 {entrypoint}:
62 call {dyn_malloc}
66 dup 0
69 call {squeeze_repeatedly_static_number}
73 return
76 )
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use twenty_first::prelude::*;
83
84 use super::*;
85 use crate::memory::dyn_malloc::DYN_MALLOC_FIRST_ADDRESS;
86 use crate::rust_shadowing_helper_functions;
87 use crate::test_helpers::tasm_final_state;
88 use crate::test_prelude::*;
89
90 impl Procedure for SampleScalarsStaticLengthDynMalloc {
91 fn rust_shadow(
92 &self,
93 stack: &mut Vec<BFieldElement>,
94 memory: &mut std::collections::HashMap<BFieldElement, BFieldElement>,
95 _nondeterminism: &NonDeterminism,
96 _public_input: &[BFieldElement],
97 sponge: &mut Option<Tip5>,
98 ) -> Vec<BFieldElement> {
99 let sponge = sponge.as_mut().expect("sponge must be initialized");
100 let num_squeezes = Self::num_squeezes(self.num_elements);
101 let pseudorandomness = (0..num_squeezes)
102 .flat_map(|_| sponge.squeeze().to_vec())
103 .collect_vec();
104 let scalars_pointer = DYN_MALLOC_FIRST_ADDRESS;
105 stack.push(scalars_pointer);
106
107 for (i, pr) in pseudorandomness.iter().enumerate() {
109 memory.insert(BFieldElement::new(i as u64) + scalars_pointer, *pr);
110 }
111
112 vec![]
113 }
114
115 fn pseudorandom_initial_state(
116 &self,
117 seed: [u8; 32],
118 _: Option<BenchmarkCase>,
119 ) -> ProcedureInitialState {
120 let mut rng = StdRng::from_seed(seed);
121 let stack = self.init_stack_for_isolated_run();
122 let sponge = Tip5 {
123 state: rng.random(),
124 };
125
126 ProcedureInitialState {
127 stack,
128 sponge: Some(sponge),
129 ..Default::default()
130 }
131 }
132 }
133
134 #[test]
135 fn sample_scalars_static_length_pbt() {
136 for i in 0..100 {
137 ShadowedProcedure::new(SampleScalarsStaticLengthDynMalloc { num_elements: i }).test();
138 }
139 }
140
141 #[test]
142 fn verify_agreement_with_tip5_sample_scalars() {
143 let empty_sponge = Tip5::init();
144 let mut non_empty_sponge = Tip5::init();
145 non_empty_sponge.absorb([BFieldElement::new(100); Tip5::RATE]);
146
147 for init_sponge in [empty_sponge, non_empty_sponge] {
148 for num_elements in 0..30 {
149 let snippet = SampleScalarsStaticLengthDynMalloc { num_elements };
150 let init_stack = snippet.init_stack_for_isolated_run();
151 let tasm = tasm_final_state(
152 &ShadowedProcedure::new(snippet),
153 &init_stack,
154 &[],
155 NonDeterminism::default(),
156 &Some(init_sponge.clone()),
157 );
158
159 let final_ram = tasm.ram;
160 let snippet_output_scalar_pointer =
161 tasm.op_stack.stack[tasm.op_stack.stack.len() - 1];
162
163 let scalars_from_tip5 =
164 Tip5::sample_scalars(&mut init_sponge.clone(), num_elements);
165
166 for (i, expected_scalar) in scalars_from_tip5.into_iter().enumerate() {
167 assert_eq!(
168 expected_scalar.coefficients.to_vec(),
169 rust_shadowing_helper_functions::array::array_get(
170 snippet_output_scalar_pointer,
171 i,
172 &final_ram,
173 EXTENSION_DEGREE,
174 )
175 );
176 }
177 }
178 }
179 }
180}
181
182#[cfg(test)]
183mod bench {
184 use super::*;
185 use crate::test_prelude::*;
186
187 #[test]
188 fn bench_10() {
189 ShadowedProcedure::new(SampleScalarsStaticLengthDynMalloc { num_elements: 10 }).bench();
190 }
191
192 #[test]
193 fn bench_100() {
194 ShadowedProcedure::new(SampleScalarsStaticLengthDynMalloc { num_elements: 100 }).bench();
195 }
196
197 #[test]
198 fn bench_63() {
199 ShadowedProcedure::new(SampleScalarsStaticLengthDynMalloc { num_elements: 63 }).bench();
200 }
201}