1use bincode::{enc::Encoder, error::EncodeError, Encode};
2use std::{error, fmt};
3
4use crate::bits::{Bit, BitBuf};
5
6#[derive(Clone, Copy, Debug, PartialEq)]
10pub enum Operation {
11 Not,
13 And,
15 Xor,
17 PushArg(u32),
19 PushLocal(u32),
21 PopOutput,
23}
24
25impl Encode for Operation {
26 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
27 match self {
28 Operation::Not => 0u8.encode(encoder),
29 Operation::And => 1u8.encode(encoder),
30 Operation::Xor => 2u8.encode(encoder),
31 Operation::PushArg(index) => {
32 3u8.encode(encoder)?;
33 index.encode(encoder)
34 }
35 Operation::PushLocal(index) => {
36 4u8.encode(encoder)?;
37 index.encode(encoder)
38 }
39 Operation::PopOutput => 5u8.encode(encoder),
40 }
41 }
42}
43
44#[derive(Clone, Debug, PartialEq)]
46pub enum ProgramError {
47 InsufficientStack { instruction: usize, stack: usize },
49}
50
51impl fmt::Display for ProgramError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 ProgramError::InsufficientStack { instruction, stack } => {
55 write!(
56 f,
57 "instr {}: insufficient stack of size {}",
58 instruction, stack
59 )
60 }
61 }
62 }
63}
64
65impl error::Error for ProgramError {}
66
67#[derive(Clone, Debug, PartialEq)]
72pub struct Program {
73 operations: Vec<Operation>,
74}
75
76impl Program {
77 pub fn new(operations: impl Into<Vec<Operation>>) -> Self {
81 Self {
82 operations: operations.into(),
83 }
84 }
85
86 pub fn validate(self) -> Result<ValidatedProgram, ProgramError> {
94 let mut required_input: u32 = 0;
95 let mut output_count: usize = 0;
96 let mut stack: usize = 0;
97
98 for (instruction, op) in self.operations.iter().enumerate() {
99 use Operation::*;
100
101 match op {
102 Not => {
103 if stack < 1 {
104 return Err(ProgramError::InsufficientStack { instruction, stack });
105 }
106 }
107 Xor | And => {
108 if stack < 2 {
109 return Err(ProgramError::InsufficientStack { instruction, stack });
110 }
111 stack -= 1;
112 }
113 Operation::PushArg(i) => {
114 required_input = u32::max(i + 1, required_input);
115 stack += 1;
116 }
117 Operation::PushLocal(i) => {
118 if (*i as usize) >= stack {
119 return Err(ProgramError::InsufficientStack { instruction, stack });
120 }
121 stack += 1;
122 }
123 Operation::PopOutput => {
124 if stack < 1 {
125 return Err(ProgramError::InsufficientStack { instruction, stack });
126 }
127 stack -= 1;
128 output_count += 1;
129 }
130 }
131 }
132
133 Ok(ValidatedProgram {
134 input_count: required_input as usize,
135 output_count,
136 operations: self.operations,
137 })
138 }
139}
140
141#[derive(Clone, Debug)]
150pub struct ValidatedProgram {
151 pub(crate) input_count: usize,
152 pub(crate) output_count: usize,
153 pub(crate) operations: Vec<Operation>,
154}
155
156#[cfg(test)]
158impl ValidatedProgram {
159 fn io(&self) -> (usize, usize) {
160 (self.input_count, self.output_count)
161 }
162}
163
164#[derive(Clone, Debug)]
169pub(crate) struct Machine {
170 input: BitBuf,
171 stack: BitBuf,
172 output: BitBuf,
173}
174
175impl Machine {
176 pub fn new(input: BitBuf) -> Self {
178 Self {
179 input,
180 stack: BitBuf::new(),
181 output: BitBuf::new(),
182 }
183 }
184
185 pub fn pop(&mut self) -> Bit {
190 self.stack.pop().unwrap()
191 }
192
193 pub fn push(&mut self, bit: Bit) {
195 self.stack.push(bit);
196 }
197
198 pub fn not(&mut self) {
200 let top = self.pop();
201 self.push(!top);
202 }
203
204 pub fn xor(&mut self) {
206 let a = self.pop();
207 let b = self.pop();
208 self.push(a ^ b);
209 }
210
211 #[cfg(test)]
216 pub fn and(&mut self) {
217 let a = self.pop();
218 let b = self.pop();
219 self.push(a & b);
220 }
221
222 pub fn push_arg(&mut self, i: usize) {
224 let arg = self.input.get(i).unwrap();
225 self.push(arg);
226 }
227
228 pub fn push_local(&mut self, i: usize) {
230 let local = self.stack.get(i).unwrap();
231 self.push(local);
232 }
233
234 pub fn pop_output(&mut self) {
236 let pop = self.pop();
237 self.output.push(pop)
238 }
239
240 pub fn input_output(self) -> (BitBuf, BitBuf) {
242 (self.input, self.output)
243 }
244}
245
246#[cfg(test)]
248pub(crate) mod generators {
249 use super::*;
250 use proptest::collection::vec;
251 use proptest::prelude::*;
252
253 fn run_operations(ops: &[Operation], input: &[u8]) -> u8 {
254 let mut machine = Machine::new(BitBuf::from_bytes(input));
255 for op in ops {
256 match op {
257 Operation::Not => machine.not(),
258 Operation::And => machine.and(),
259 Operation::Xor => machine.xor(),
260 Operation::PushArg(i) => machine.push_arg(*i as usize),
261 Operation::PushLocal(i) => machine.push_local(*i as usize),
262 Operation::PopOutput => machine.pop_output(),
263 }
264 }
265 let (_, mut output) = machine.input_output();
266 let mut out = 0;
267 for _ in 0..8 {
268 out <<= 1;
269 out |= u64::from(output.pop().unwrap()) as u8;
270 }
271 out
272 }
273
274 fn arb_operation_except_pop_output(
275 max_arg: u32,
276 max_local: u32,
277 ) -> impl Strategy<Value = Operation> {
278 use Operation::*;
279
280 prop_oneof![
281 Just(Not),
282 Just(And),
283 Just(Xor),
284 (0..max_arg).prop_map(PushArg),
285 (0..max_local).prop_map(PushLocal),
286 ]
287 }
288
289 prop_compose! {
293 pub fn arb_program_and_inputs()(input in any::<[u8; 4]>(), extra in vec(arb_operation_except_pop_output(32, 16), 0..16)) -> (ValidatedProgram, [u8; 4], u8) {
294 use Operation::*;
295
296 let mut operations = Vec::with_capacity(extra.len() + 32 + 8);
297 for i in 0..32 {
298 operations.push(PushArg(i));
299 }
300 operations.extend(extra.iter());
301 for _ in 0..8 {
302 operations.push(PopOutput);
303 }
304
305 let output = run_operations(&operations, &input);
306 (Program::new(operations).validate().unwrap(), input, output)
307 }
308 }
309}
310
311#[cfg(test)]
312mod test {
313 use super::*;
314 use Operation::*;
315
316 #[test]
317 fn test_validating_program_with_insufficient_stack() {
318 assert!(Program::new([Not]).validate().is_err());
319 assert!(Program::new([And]).validate().is_err());
320 assert!(Program::new([PushArg(0), And]).validate().is_err());
321 assert!(Program::new([PushLocal(0)]).validate().is_err());
322 }
323
324 #[test]
325 fn test_validating_program_counts_correctly() {
326 assert_eq!(
327 Ok((2, 2)),
328 Program::new([PushArg(0), PushArg(1), Not, PopOutput, PopOutput])
329 .validate()
330 .map(|x| x.io())
331 );
332 assert_eq!(
333 Ok((1, 2)),
334 Program::new([PushArg(0), PushArg(0), Not, PopOutput, PopOutput])
335 .validate()
336 .map(|x| x.io())
337 );
338 assert_eq!(
339 Ok((1, 1)),
340 Program::new([PushArg(0), PushArg(0), Xor, PopOutput])
341 .validate()
342 .map(|x| x.io())
343 );
344 assert_eq!(
345 Ok((1, 1)),
346 Program::new([PushArg(0), PushArg(0), And, PopOutput])
347 .validate()
348 .map(|x| x.io())
349 );
350 }
351}