cairo_vm/
typed_operations.rs

1use crate::math_utils::{
2    qm31_packed_reduced_add, qm31_packed_reduced_div, qm31_packed_reduced_mul,
3    qm31_packed_reduced_sub,
4};
5use crate::stdlib::prelude::*;
6use crate::types::relocatable::MaybeRelocatable;
7use crate::types::{errors::math_errors::MathError, instruction::OpcodeExtension};
8use crate::vm::errors::vm_errors::VirtualMachineError;
9use crate::Felt252;
10
11/// Adds two MaybeRelocatable values according to the specified OpcodeExtension and returns the
12/// result as a MaybeRelocatable value.
13/// If the OpcodeExtension is Stone it adds them as MaybeRelocatable::add does.
14/// If the OpcodeExtension is QM31Operation it requires them both to be Int and it adds them
15/// as packed reduced QM31 elements.
16pub fn typed_add(
17    x: &MaybeRelocatable,
18    y: &MaybeRelocatable,
19    opcode_extension: OpcodeExtension,
20) -> Result<MaybeRelocatable, VirtualMachineError> {
21    match opcode_extension {
22        OpcodeExtension::Stone => Ok(x.add(y)?),
23        OpcodeExtension::QM31Operation => {
24            if let (MaybeRelocatable::Int(num_x), MaybeRelocatable::Int(num_y)) = (x, y) {
25                Ok(MaybeRelocatable::Int(qm31_packed_reduced_add(
26                    *num_x, *num_y,
27                )?))
28            } else {
29                Err(VirtualMachineError::Math(MathError::RelocatableQM31Add(
30                    Box::new((x.clone(), y.clone())),
31                )))
32            }
33        }
34        _ => Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(
35            "typed_add".to_owned().into_boxed_str(),
36        )),
37    }
38}
39
40/// Substracts two MaybeRelocatable values according to the specified OpcodeExtension and returns
41/// the result as a MaybeRelocatable value.
42/// If the OpcodeExtension is Stone it subtracts them as MaybeRelocatable::sub does.
43/// If the OpcodeExtension is QM31Operation it requires them both to be Int and it subtracts
44/// them as packed reduced QM31 elements.
45pub fn typed_sub(
46    x: &MaybeRelocatable,
47    y: &MaybeRelocatable,
48    opcode_extension: OpcodeExtension,
49) -> Result<MaybeRelocatable, VirtualMachineError> {
50    match opcode_extension {
51        OpcodeExtension::Stone => Ok(x.sub(y)?),
52        OpcodeExtension::QM31Operation => {
53            if let (MaybeRelocatable::Int(num_x), MaybeRelocatable::Int(num_y)) = (x, y) {
54                Ok(MaybeRelocatable::Int(qm31_packed_reduced_sub(
55                    *num_x, *num_y,
56                )?))
57            } else {
58                Err(VirtualMachineError::Math(MathError::RelocatableQM31Sub(
59                    Box::new((x.clone(), y.clone())),
60                )))
61            }
62        }
63        _ => Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(
64            "typed_sub".to_owned().into_boxed_str(),
65        )),
66    }
67}
68
69/// Multiplies two MaybeRelocatable values according to the specified OpcodeExtension and returns
70/// the result as a MaybeRelocatable value.
71/// Requires both operands to be Int.
72/// If the OpcodeExtension is Stone it multiplies them as Felts.
73/// If the OpcodeExtension is QM31Operation it multiplies them as packed reduced QM31 elements.
74pub fn typed_mul(
75    x: &MaybeRelocatable,
76    y: &MaybeRelocatable,
77    opcode_extension: OpcodeExtension,
78) -> Result<MaybeRelocatable, VirtualMachineError> {
79    if let (MaybeRelocatable::Int(num_x), MaybeRelocatable::Int(num_y)) = (x, y) {
80        match opcode_extension {
81            OpcodeExtension::Stone => Ok(MaybeRelocatable::Int(num_x * num_y)),
82            OpcodeExtension::QM31Operation => Ok(MaybeRelocatable::Int(qm31_packed_reduced_mul(
83                *num_x, *num_y,
84            )?)),
85            _ => Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(
86                "typed_mul".to_owned().into_boxed_str(),
87            )),
88        }
89    } else {
90        Err(VirtualMachineError::ComputeResRelocatableMul(Box::new((
91            x.clone(),
92            y.clone(),
93        ))))
94    }
95}
96
97/// Divides two Felt252 values according to the specified OpcodeExtension and returns the result
98/// as a Felt252 value.
99/// If the OpcodeExtension is Stone it divides them as Felts.
100/// If the OpcodeExtension is QM31Operation it divides them as packed reduced QM31 elements.
101pub fn typed_div(
102    x: &Felt252,
103    y: &Felt252,
104    opcode_extension: OpcodeExtension,
105) -> Result<Felt252, VirtualMachineError> {
106    match opcode_extension {
107        OpcodeExtension::Stone => {
108            Ok(x.field_div(&y.try_into().map_err(|_| MathError::DividedByZero)?))
109        }
110        OpcodeExtension::QM31Operation => Ok(qm31_packed_reduced_div(*x, *y)?),
111        _ => Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(
112            "typed_div".to_owned().into_boxed_str(),
113        )),
114    }
115}
116#[cfg(test)]
117mod decoder_test {
118    use super::*;
119    use assert_matches::assert_matches;
120
121    #[cfg(target_arch = "wasm32")]
122    use wasm_bindgen_test::*;
123
124    #[test]
125    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
126    fn typed_add_blake() {
127        let a = &MaybeRelocatable::from(5);
128        let b = &MaybeRelocatable::from(6);
129        let error = typed_add(a, b, OpcodeExtension::Blake);
130        assert_matches!(
131            error,
132            Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(ref message)) if message.as_ref() == "typed_add"
133        );
134    }
135
136    #[test]
137    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
138    fn typed_sub_blake() {
139        let a = &MaybeRelocatable::from(7);
140        let b = &MaybeRelocatable::from(3);
141        let error = typed_sub(a, b, OpcodeExtension::Blake);
142        assert_matches!(
143            error,
144            Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(ref message)) if message.as_ref() == "typed_sub"
145        );
146    }
147
148    #[test]
149    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
150    fn relocatable_typed_sub_q31_operation() {
151        let a = &MaybeRelocatable::from((6, 8));
152        let b = &MaybeRelocatable::from(2);
153        let error = typed_sub(a, b, OpcodeExtension::QM31Operation);
154        assert_matches!(
155            error,
156            Err(VirtualMachineError::Math(MathError::RelocatableQM31Sub(bx))) if *bx ==
157                (MaybeRelocatable::from((6, 8)), MaybeRelocatable::from(2))
158        );
159    }
160
161    #[test]
162    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
163    fn typed_mul_blake_finalize() {
164        let a = &MaybeRelocatable::from(4);
165        let b = &MaybeRelocatable::from(9);
166        let error = typed_mul(a, b, OpcodeExtension::BlakeFinalize);
167        assert_matches!(
168            error,
169            Err(VirtualMachineError::InvalidTypedOperationOpcodeExtension(ref message)) if message.as_ref() == "typed_mul"
170        );
171    }
172}