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