snarkvm_synthesizer_program/logic/instruction/operation/
is.rs1use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait, register_types_equivalent};
17use console::{
18 network::prelude::*,
19 program::{Literal, LiteralType, Plaintext, PlaintextType, Register, RegisterType, Value},
20 types::Boolean,
21};
22
23pub type IsEq<N> = IsInstruction<N, { Variant::IsEq as u8 }>;
25pub type IsNeq<N> = IsInstruction<N, { Variant::IsNeq as u8 }>;
27
28enum Variant {
29 IsEq,
30 IsNeq,
31}
32
33#[derive(Clone, PartialEq, Eq, Hash)]
35pub struct IsInstruction<N: Network, const VARIANT: u8> {
36 operands: Vec<Operand<N>>,
38 destination: Register<N>,
40}
41
42impl<N: Network, const VARIANT: u8> IsInstruction<N, VARIANT> {
43 pub fn new(operands: Vec<Operand<N>>, destination: Register<N>) -> Result<Self> {
45 ensure!(operands.len() == 2, "Instruction '{}' must have two operands", Self::opcode());
47 Ok(Self { operands, destination })
49 }
50
51 pub const fn opcode() -> Opcode {
53 match VARIANT {
54 0 => Opcode::Is("is.eq"),
55 1 => Opcode::Is("is.neq"),
56 _ => panic!("Invalid 'is' instruction opcode"),
57 }
58 }
59
60 #[inline]
62 pub fn operands(&self) -> &[Operand<N>] {
63 debug_assert!(self.operands.len() == 2, "Instruction '{}' must have two operands", Self::opcode());
65 &self.operands
67 }
68
69 #[inline]
71 pub fn destinations(&self) -> Vec<Register<N>> {
72 vec![self.destination.clone()]
73 }
74
75 #[inline]
77 pub fn contains_external_struct(&self) -> bool {
78 false
79 }
80}
81
82impl<N: Network, const VARIANT: u8> IsInstruction<N, VARIANT> {
83 pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
85 if self.operands.len() != 2 {
87 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
88 }
89
90 let input_a = registers.load(stack, &self.operands[0])?;
92 let input_b = registers.load(stack, &self.operands[1])?;
93
94 let output = match VARIANT {
96 0 => Literal::Boolean(Boolean::new(input_a == input_b)),
97 1 => Literal::Boolean(Boolean::new(input_a != input_b)),
98 _ => bail!("Invalid 'is' variant: {VARIANT}"),
99 };
100 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(output)))
102 }
103
104 pub fn execute<A: circuit::Aleo<Network = N>>(
106 &self,
107 stack: &impl StackTrait<N>,
108 registers: &mut impl RegistersCircuit<N, A>,
109 ) -> Result<()> {
110 if self.operands.len() != 2 {
112 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
113 }
114
115 let input_a = registers.load_circuit(stack, &self.operands[0])?;
117 let input_b = registers.load_circuit(stack, &self.operands[1])?;
118
119 let output = match VARIANT {
121 0 => circuit::Literal::Boolean(input_a.is_equal(&input_b)),
122 1 => circuit::Literal::Boolean(input_a.is_not_equal(&input_b)),
123 _ => bail!("Invalid 'is' variant: {VARIANT}"),
124 };
125 let output = circuit::Value::Plaintext(circuit::Plaintext::Literal(output, Default::default()));
127 registers.store_circuit(stack, &self.destination, output)
129 }
130
131 #[inline]
133 pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
134 self.evaluate(stack, registers)
135 }
136
137 pub fn output_types(
139 &self,
140 stack: &impl StackTrait<N>,
141 input_types: &[RegisterType<N>],
142 ) -> Result<Vec<RegisterType<N>>> {
143 if input_types.len() != 2 {
145 bail!("Instruction '{}' expects 2 inputs, found {} inputs", Self::opcode(), input_types.len())
146 }
147 if !register_types_equivalent(stack, &input_types[0], stack, &input_types[1])? {
149 bail!(
150 "Instruction '{}' expects inputs of the same type. Found inputs of type '{}' and '{}'",
151 Self::opcode(),
152 input_types[0],
153 input_types[1]
154 )
155 }
156 if self.operands.len() != 2 {
158 bail!("Instruction '{}' expects 2 operands, found {} operands", Self::opcode(), self.operands.len())
159 }
160
161 match VARIANT {
162 0 | 1 => Ok(vec![RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Boolean))]),
163 _ => bail!("Invalid 'is' variant: {VARIANT}"),
164 }
165 }
166}
167
168impl<N: Network, const VARIANT: u8> Parser for IsInstruction<N, VARIANT> {
169 fn parse(string: &str) -> ParserResult<Self> {
171 let (string, _) = tag(*Self::opcode())(string)?;
173 let (string, _) = Sanitizer::parse_whitespaces(string)?;
175 let (string, first) = Operand::parse(string)?;
177 let (string, _) = Sanitizer::parse_whitespaces(string)?;
179 let (string, second) = Operand::parse(string)?;
181 let (string, _) = Sanitizer::parse_whitespaces(string)?;
183 let (string, _) = tag("into")(string)?;
185 let (string, _) = Sanitizer::parse_whitespaces(string)?;
187 let (string, destination) = Register::parse(string)?;
189
190 Ok((string, Self { operands: vec![first, second], destination }))
191 }
192}
193
194impl<N: Network, const VARIANT: u8> FromStr for IsInstruction<N, VARIANT> {
195 type Err = Error;
196
197 fn from_str(string: &str) -> Result<Self> {
199 match Self::parse(string) {
200 Ok((remainder, object)) => {
201 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
203 Ok(object)
205 }
206 Err(error) => bail!("Failed to parse string. {error}"),
207 }
208 }
209}
210
211impl<N: Network, const VARIANT: u8> Debug for IsInstruction<N, VARIANT> {
212 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
214 Display::fmt(self, f)
215 }
216}
217
218impl<N: Network, const VARIANT: u8> Display for IsInstruction<N, VARIANT> {
219 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
221 if self.operands.len() != 2 {
223 return Err(fmt::Error);
224 }
225 write!(f, "{} ", Self::opcode())?;
227 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
228 write!(f, "into {}", self.destination)
229 }
230}
231
232impl<N: Network, const VARIANT: u8> FromBytes for IsInstruction<N, VARIANT> {
233 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
235 let mut operands = Vec::with_capacity(2);
237 for _ in 0..2 {
239 operands.push(Operand::read_le(&mut reader)?);
240 }
241 let destination = Register::read_le(&mut reader)?;
243
244 Ok(Self { operands, destination })
246 }
247}
248
249impl<N: Network, const VARIANT: u8> ToBytes for IsInstruction<N, VARIANT> {
250 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
252 if self.operands.len() != 2 {
254 return Err(error(format!("The number of operands must be 2, found {}", self.operands.len())));
255 }
256 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
258 self.destination.write_le(&mut writer)
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use console::network::MainnetV0;
267
268 type CurrentNetwork = MainnetV0;
269
270 #[test]
271 fn test_parse() {
272 let (string, is) = IsEq::<CurrentNetwork>::parse("is.eq r0 r1 into r2").unwrap();
273 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
274 assert_eq!(is.operands.len(), 2, "The number of operands is incorrect");
275 assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
276 assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
277 assert_eq!(is.destination, Register::Locator(2), "The destination register is incorrect");
278
279 let (string, is) = IsNeq::<CurrentNetwork>::parse("is.neq r0 r1 into r2").unwrap();
280 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
281 assert_eq!(is.operands.len(), 2, "The number of operands is incorrect");
282 assert_eq!(is.operands[0], Operand::Register(Register::Locator(0)), "The first operand is incorrect");
283 assert_eq!(is.operands[1], Operand::Register(Register::Locator(1)), "The second operand is incorrect");
284 assert_eq!(is.destination, Register::Locator(2), "The destination register is incorrect");
285 }
286}