miden_processor/fast/
field_ops.rs

1use miden_core::{Felt, FieldElement, ONE, ZERO};
2
3use super::{ExecutionError, FastProcessor};
4use crate::{ErrorContext, operations::utils::assert_binary};
5
6const TWO: Felt = Felt::new(2);
7
8impl FastProcessor {
9    /// Analogous to `Process::op_add`.
10    pub fn op_add(&mut self) -> Result<(), ExecutionError> {
11        self.pop2_applyfn_push(|a, b| Ok(a + b))
12    }
13
14    /// Analogous to `Process::op_neg`.
15    pub fn op_neg(&mut self) -> Result<(), ExecutionError> {
16        let element = self.stack_get(0);
17        self.stack_write(0, -element);
18        Ok(())
19    }
20
21    /// Analogous to `Process::op_mul`.
22    pub fn op_mul(&mut self) -> Result<(), ExecutionError> {
23        self.pop2_applyfn_push(|a, b| Ok(a * b))
24    }
25
26    /// Analogous to `Process::op_inv`.
27    #[inline(always)]
28    pub fn op_inv(
29        &mut self,
30        op_idx: usize,
31        err_ctx: &impl ErrorContext,
32    ) -> Result<(), ExecutionError> {
33        let top = self.stack_get_mut(0);
34        if (*top) == ZERO {
35            return Err(ExecutionError::divide_by_zero(self.clk + op_idx, err_ctx));
36        }
37        *top = top.inv();
38        Ok(())
39    }
40
41    /// Analogous to `Process::op_inc`.
42    pub fn op_incr(&mut self) -> Result<(), ExecutionError> {
43        *self.stack_get_mut(0) += ONE;
44        Ok(())
45    }
46
47    /// Analogous to `Process::op_and`.
48    #[inline(always)]
49    pub fn op_and(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
50        self.pop2_applyfn_push(|a, b| {
51            assert_binary(b, err_ctx)?;
52            assert_binary(a, err_ctx)?;
53
54            if a == ONE && b == ONE { Ok(ONE) } else { Ok(ZERO) }
55        })
56    }
57
58    /// Analogous to `Process::op_or`.
59    #[inline(always)]
60    pub fn op_or(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
61        self.pop2_applyfn_push(|a, b| {
62            assert_binary(b, err_ctx)?;
63            assert_binary(a, err_ctx)?;
64
65            if a == ONE || b == ONE { Ok(ONE) } else { Ok(ZERO) }
66        })
67    }
68
69    /// Analogous to `Process::op_not`.
70    #[inline(always)]
71    pub fn op_not(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
72        let top = self.stack_get_mut(0);
73        if *top == ZERO {
74            *top = ONE;
75        } else if *top == ONE {
76            *top = ZERO;
77        } else {
78            return Err(ExecutionError::not_binary_value_op(*top, err_ctx));
79        }
80        Ok(())
81    }
82
83    /// Analogous to `Process::op_eq`.
84    pub fn op_eq(&mut self) -> Result<(), ExecutionError> {
85        self.pop2_applyfn_push(|a, b| if a == b { Ok(ONE) } else { Ok(ZERO) })
86    }
87
88    /// Analogous to `Process::op_eqz`.
89    pub fn op_eqz(&mut self) -> Result<(), ExecutionError> {
90        let top = self.stack_get_mut(0);
91        if (*top) == ZERO {
92            *top = ONE;
93        } else {
94            *top = ZERO;
95        }
96        Ok(())
97    }
98
99    /// Analogous to `Process::op_expacc`.
100    pub fn op_expacc(&mut self) {
101        let old_base = self.stack_get(1);
102        let old_acc = self.stack_get(2);
103        let old_exp_int = self.stack_get(3).as_int();
104
105        // Compute new exponent.
106        let new_exp = Felt::new(old_exp_int >> 1);
107
108        // Compute new accumulator. We update the accumulator only when the least significant bit of
109        // the exponent is 1.
110        let exp_lsb = old_exp_int & 1;
111        let acc_update_val = if exp_lsb == 1 { old_base } else { ONE };
112        let new_acc = old_acc * acc_update_val;
113
114        // Compute the new base.
115        let new_base = old_base * old_base;
116
117        self.stack_write(0, Felt::new(exp_lsb));
118        self.stack_write(1, new_base);
119        self.stack_write(2, new_acc);
120        self.stack_write(3, new_exp);
121    }
122
123    /// Analogous to `Process::op_ext2mul`.
124    ///
125    /// Gets the top four values from the stack [b1, b0, a1, a0], where a = (a1, a0) and
126    /// b = (b1, b0) are elements of the extension field, and outputs the product c = (c1, c0)
127    /// where c0 = b0 * a0 - 2 * b1 * a1 and c1 = (b0 + b1) * (a1 + a0) - b0 * a0. It pushes 0 to
128    /// the first and second positions on the stack, c1 and c2 to the third and fourth positions,
129    /// and leaves the rest of the stack unchanged.
130    pub fn op_ext2mul(&mut self) {
131        let [a0, a1, b0, b1] = self.stack_get_word(0).into();
132
133        /* top 2 elements remain unchanged */
134
135        let b0_times_a0 = b0 * a0;
136        self.stack_write(2, (b0 + b1) * (a1 + a0) - b0_times_a0);
137        self.stack_write(3, b0_times_a0 - TWO * b1 * a1);
138    }
139
140    // HELPERS
141    // ----------------------------------------------------------------------------------------------
142
143    /// Pops the top two elements from the stack, applies the given function to them, and pushes the
144    /// result back onto the stack.
145    ///
146    /// The size of the stack is decremented by 1.
147    #[inline(always)]
148    fn pop2_applyfn_push(
149        &mut self,
150        f: impl FnOnce(Felt, Felt) -> Result<Felt, ExecutionError>,
151    ) -> Result<(), ExecutionError> {
152        let b = self.stack_get(0);
153        let a = self.stack_get(1);
154
155        self.decrement_stack_size();
156        self.stack_write(0, f(a, b)?);
157
158        Ok(())
159    }
160}