miden_processor/fast/
field_ops.rs

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