miden_processor/fast/
horner_ops.rs

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