miden_processor/fast/
horner_ops.rs

1use core::array;
2
3use miden_core::{Felt, QuadFelt};
4
5use super::FastProcessor;
6use crate::{ErrorContext, ExecutionError};
7
8// CONSTANTS
9// ================================================================================================
10
11const ALPHA_ADDR_INDEX: usize = 13;
12const ACC_HIGH_INDEX: usize = 14;
13const ACC_LOW_INDEX: usize = 15;
14
15impl FastProcessor {
16    /// Analogous to `Process::op_horner_eval_base`.
17    #[inline(always)]
18    pub fn op_horner_eval_base(
19        &mut self,
20        err_ctx: &impl ErrorContext,
21    ) -> Result<(), ExecutionError> {
22        // read the values of the coefficients, over the base field, from the stack
23        let coeffs = self.get_coeffs_as_base_elements();
24
25        // read the evaluation point alpha from memory
26        let alpha = self.get_evaluation_point(err_ctx)?;
27
28        // compute the updated accumulator value
29        let (acc_new1, acc_new0) = {
30            let acc_old = self.get_accumulator();
31            let acc_new = coeffs
32                .iter()
33                .rev()
34                .fold(acc_old, |acc, coef| QuadFelt::from(*coef) + alpha * acc);
35
36            let acc_new_base_elements = acc_new.to_base_elements();
37
38            (acc_new_base_elements[1], acc_new_base_elements[0])
39        };
40
41        // Update the accumulator values
42        self.stack_write(ACC_HIGH_INDEX, acc_new1);
43        self.stack_write(ACC_LOW_INDEX, acc_new0);
44
45        Ok(())
46    }
47
48    /// Analogous to `Process::op_horner_eval_ext`.
49    #[inline(always)]
50    pub fn op_horner_eval_ext(
51        &mut self,
52        err_ctx: &impl ErrorContext,
53    ) -> Result<(), ExecutionError> {
54        // read the values of the coefficients, over the base field, from the stack
55        let coef = self.get_coeffs_as_quad_ext_elements();
56
57        // read the evaluation point alpha from memory
58        let alpha = self.get_evaluation_point(err_ctx)?;
59
60        // compute the updated accumulator value
61        let (acc_new1, acc_new0) = {
62            let acc_old = self.get_accumulator();
63            let acc_new = coef.iter().rev().fold(acc_old, |acc, coef| *coef + alpha * acc);
64
65            let acc_new_base_elements = acc_new.to_base_elements();
66
67            (acc_new_base_elements[1], acc_new_base_elements[0])
68        };
69
70        // Update the accumulator values
71        self.stack_write(ACC_HIGH_INDEX, acc_new1);
72        self.stack_write(ACC_LOW_INDEX, acc_new0);
73
74        Ok(())
75    }
76
77    // HELPER METHODS
78    //// ------------------------------------------------------------------------------------------
79
80    /// Returns the top 8 elements of the operand stack, such that the element on top of the stack
81    /// appears first in the return array.
82    fn get_coeffs_as_base_elements(&self) -> [Felt; 8] {
83        array::from_fn(|i| self.stack_get(i))
84    }
85
86    /// Returns the top 8 elements of the operand stack.
87    fn get_coeffs_as_quad_ext_elements(&self) -> [QuadFelt; 4] {
88        let c0 = [self.stack_get(0), self.stack_get(1)];
89        let c1 = [self.stack_get(2), self.stack_get(3)];
90        let c2 = [self.stack_get(4), self.stack_get(5)];
91        let c3 = [self.stack_get(6), self.stack_get(7)];
92
93        [
94            QuadFelt::new(c0[1], c0[0]),
95            QuadFelt::new(c1[1], c1[0]),
96            QuadFelt::new(c2[1], c2[0]),
97            QuadFelt::new(c3[1], c3[0]),
98        ]
99    }
100
101    /// Returns the evaluation point.
102    fn get_evaluation_point(
103        &mut self,
104        err_ctx: &impl ErrorContext,
105    ) -> Result<QuadFelt, ExecutionError> {
106        let (alpha_0, alpha_1) = {
107            let addr = self.stack_get(ALPHA_ADDR_INDEX);
108            let word = self
109                .memory
110                .read_word(self.ctx, addr, self.clk, err_ctx)
111                .map_err(ExecutionError::MemoryError)?;
112
113            (word[0], word[1])
114        };
115
116        Ok(QuadFelt::new(alpha_0, alpha_1))
117    }
118
119    /// Reads the accumulator values.
120    fn get_accumulator(&self) -> QuadFelt {
121        let acc1 = self.stack_get(ACC_HIGH_INDEX);
122        let acc0 = self.stack_get(ACC_LOW_INDEX);
123
124        QuadFelt::new(acc0, acc1)
125    }
126}