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