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 inputs(&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 outputs(&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 ) {
133 let x0 = stack.pop().unwrap();
134 let x1 = stack.pop().unwrap();
135 let x2 = stack.pop().unwrap();
136 let x = XFieldElement::new([x0, x1, x2]);
137
138 let address = stack.pop().unwrap();
139 let coefficients = *Vec::<XFieldElement>::decode_from_memory(memory, address).unwrap();
140 let polynomial = Polynomial::new(coefficients);
141
142 let value = polynomial.evaluate_in_same_field(x);
143 stack.push(value.coefficients[2]);
144 stack.push(value.coefficients[1]);
145 stack.push(value.coefficients[0]);
146 }
147
148 fn pseudorandom_initial_state(
149 &self,
150 seed: [u8; 32],
151 bench_case: Option<BenchmarkCase>,
152 ) -> FunctionInitialState {
153 let mut rng = StdRng::from_seed(seed);
154 let num_coefficients = match bench_case {
155 Some(BenchmarkCase::CommonCase) => 256,
156 Some(BenchmarkCase::WorstCase) => 512,
157 None => rng.random_range(0..1000),
158 };
159 let coefficients = (0..num_coefficients)
160 .map(|_| rng.random::<XFieldElement>())
161 .collect_vec();
162
163 let coefficients_pointer = bfe!(rng.random_range(0..(1u64 << 35)));
164
165 let indeterminate = rng.random::<XFieldElement>();
166
167 self.prepare_state(coefficients, coefficients_pointer, indeterminate)
168 }
169
170 fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
171 let an_indeterminate = xfe!([1u64 << 45, 47, 49]);
172 let one_coefficient = self.prepare_state(xfe_vec![19991], bfe!(333), an_indeterminate);
173 let two_coefficients =
174 self.prepare_state(xfe_vec![19991, 299999992], bfe!(333), an_indeterminate);
175 let three_coefficients = self.prepare_state(
176 xfe_vec![19991, 299999992, 399999993],
177 bfe!(333),
178 an_indeterminate,
179 );
180
181 vec![one_coefficient, two_coefficients, three_coefficients]
182 }
183 }
184
185 #[test]
186 fn test() {
187 for _ in 0..10 {
188 ShadowedFunction::new(HornerEvaluationDynamicLength).test();
189 }
190 }
191}
192
193#[cfg(test)]
194mod bench {
195 use super::*;
196 use crate::test_prelude::*;
197
198 #[test]
199 fn benchmark() {
200 ShadowedFunction::new(HornerEvaluationDynamicLength).bench();
201 }
202}