tasm_lib/list/
horner_evaluation_dynamic_length.rs1use itertools::Itertools;
2use triton_vm::prelude::*;
3
4use crate::list::length::Length;
5use crate::prelude::*;
6
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
11pub struct HornerEvaluationDynamicLength;
12
13impl BasicSnippet for HornerEvaluationDynamicLength {
14 fn parameters(&self) -> Vec<(DataType, String)> {
15 vec![
16 (
17 DataType::List(Box::new(DataType::Xfe)),
18 "*coefficients".to_string(),
19 ),
20 (DataType::Xfe, "indeterminate".to_string()),
21 ]
22 }
23
24 fn return_values(&self) -> Vec<(DataType, String)> {
25 vec![(DataType::Xfe, "value".to_string())]
26 }
27
28 fn entrypoint(&self) -> String {
29 "tasmlib_list_horner_evaluation_dynamic_length".to_string()
30 }
31
32 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
33 let horner_iteration = triton_asm! {
34 dup 5 dup 5 dup 5 xx_mul dup 6 read_mem 3 swap 10 pop 1 xx_add };
44 let batch_size = 16;
45 let three_times_batch_size_plus_two = batch_size * 3 + 2;
46 let many_horner_iterations = (0..batch_size)
47 .flat_map(|_| horner_iteration.clone())
48 .collect_vec();
49
50 let entrypoint = self.entrypoint();
51 let loop_batches = format!("{entrypoint}_loop_batches");
52 let loop_remainder = format!("{entrypoint}_loop_remainder");
53 let length_of_list = library.import(Box::new(Length));
54
55 triton_asm! {
56 {entrypoint}:
59 dup 3 call {length_of_list} push 3 mul dup 4 add dup 3 dup 3 dup 3 push 0 push 0 push 0 call {loop_batches}
66 call {loop_remainder} swap 8 pop 1 swap 8 pop 1 swap 8 pop 5 pop 1 return
72
73 {loop_batches}:
75 dup 6 dup 11 push {three_times_batch_size_plus_two} add
78 push -1 mul add
80 split pop 1 skiz return
83 {&many_horner_iterations}
84 recurse
85
86 {loop_remainder}:
88 dup 6 dup 11 eq push 1 eq
92 skiz return
93 {&horner_iteration}
94 recurse
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use itertools::Itertools;
102 use twenty_first::math::polynomial::Polynomial;
103
104 use super::*;
105 use crate::test_prelude::*;
106
107 impl HornerEvaluationDynamicLength {
108 fn prepare_state(
109 &self,
110 coefficients: Vec<XFieldElement>,
111 coefficients_pointer: BFieldElement,
112 indeterminate: XFieldElement,
113 ) -> FunctionInitialState {
114 let mut memory = HashMap::default();
115 let mut stack = self.init_stack_for_isolated_run();
116 encode_to_memory(&mut memory, coefficients_pointer, &coefficients);
117
118 stack.push(coefficients_pointer);
119 stack.push(indeterminate.coefficients[2]);
120 stack.push(indeterminate.coefficients[1]);
121 stack.push(indeterminate.coefficients[0]);
122
123 FunctionInitialState { stack, memory }
124 }
125 }
126
127 impl Function for HornerEvaluationDynamicLength {
128 fn rust_shadow(
129 &self,
130 stack: &mut Vec<BFieldElement>,
131 memory: &mut HashMap<BFieldElement, BFieldElement>,
132 ) -> Result<(), RustShadowError> {
133 let x0 = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
134 let x1 = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
135 let x2 = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
136 let x = XFieldElement::new([x0, x1, x2]);
137
138 let address = stack.pop().ok_or(RustShadowError::StackUnderflow)?;
139 let coefficients = *Vec::<XFieldElement>::decode_from_memory(memory, address)
140 .map_err(|_| RustShadowError::DecodingError)?;
141 let polynomial = Polynomial::new(coefficients);
142
143 let value = polynomial.evaluate_in_same_field(x);
144 stack.push(value.coefficients[2]);
145 stack.push(value.coefficients[1]);
146 stack.push(value.coefficients[0]);
147 Ok(())
148 }
149
150 fn pseudorandom_initial_state(
151 &self,
152 seed: [u8; 32],
153 bench_case: Option<BenchmarkCase>,
154 ) -> FunctionInitialState {
155 let mut rng = StdRng::from_seed(seed);
156 let num_coefficients = match bench_case {
157 Some(BenchmarkCase::CommonCase) => 256,
158 Some(BenchmarkCase::WorstCase) => 512,
159 None => rng.random_range(0..1000),
160 };
161 let coefficients = (0..num_coefficients)
162 .map(|_| rng.random::<XFieldElement>())
163 .collect_vec();
164
165 let coefficients_pointer = bfe!(rng.random_range(0..(1u64 << 35)));
166
167 let indeterminate = rng.random::<XFieldElement>();
168
169 self.prepare_state(coefficients, coefficients_pointer, indeterminate)
170 }
171
172 fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
173 let an_indeterminate = xfe!([1u64 << 45, 47, 49]);
174 let one_coefficient = self.prepare_state(xfe_vec![19991], bfe!(333), an_indeterminate);
175 let two_coefficients =
176 self.prepare_state(xfe_vec![19991, 299999992], bfe!(333), an_indeterminate);
177 let three_coefficients = self.prepare_state(
178 xfe_vec![19991, 299999992, 399999993],
179 bfe!(333),
180 an_indeterminate,
181 );
182
183 vec![one_coefficient, two_coefficients, three_coefficients]
184 }
185 }
186
187 #[macro_rules_attr::apply(test)]
188 fn test() {
189 for _ in 0..10 {
190 ShadowedFunction::new(HornerEvaluationDynamicLength).test();
191 }
192 }
193}
194
195#[cfg(test)]
196mod bench {
197 use super::*;
198 use crate::test_prelude::*;
199
200 #[macro_rules_attr::apply(test)]
201 fn benchmark() {
202 ShadowedFunction::new(HornerEvaluationDynamicLength).bench();
203 }
204}