use std::{
fmt::{
self,
Display,
Formatter,
},
ops::Index,
};
use crate::Instruction;
#[derive(PartialEq, Debug, Eq, Clone)]
pub struct Program {
instructions: Vec<Instruction>,
}
impl Program {
#[must_use]
pub fn get_instruction(&self, index: usize) -> Option<Instruction> {
self.length().and_then(|length| {
if index >= length {
None
} else {
Some(self.instructions[index])
}
})
}
#[must_use]
pub fn find_matching_bracket(&self, index: usize) -> Option<usize> {
match self.get_instruction(index) {
Some(Instruction::JumpForward) => {
let mut bracket_counter = 0;
let mut index = index;
loop {
match self.instructions.get(index) {
Some(Instruction::JumpForward) => bracket_counter += 1,
Some(Instruction::JumpBackward) => bracket_counter -= 1,
_ => (),
}
if bracket_counter == 0 {
break;
}
index += 1;
}
Some(index)
}
_ => None,
}
}
#[must_use]
pub fn length(&self) -> Option<usize> {
if self.instructions.is_empty() {
None
} else {
Some(self.instructions.len())
}
}
}
impl Default for Program {
fn default() -> Self {
Self::from(vec![Instruction::NoOp; 10])
}
}
impl Display for Program {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
for (index, instruction) in self.instructions.iter().enumerate() {
writeln!(f, "{index:04}: {instruction}")?;
}
Ok(())
}
}
impl Index<usize> for Program {
type Output = Instruction;
fn index(&self, index: usize) -> &Self::Output {
&self.instructions[index]
}
}
impl From<&str> for Program {
fn from(program: &str) -> Self {
let mut instructions = Vec::new();
for c in program.chars() {
instructions.push(Instruction::from_char(c));
}
Self { instructions }
}
}
impl From<Vec<Instruction>> for Program {
fn from(instructions: Vec<Instruction>) -> Self {
Self { instructions }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_program_from() {
let instructions = vec![Instruction::NoOp];
let program = Program::from(instructions);
assert_eq!(program.instructions.len(), 1);
assert_eq!(program.length(), Some(1));
}
#[test]
fn test_program_load_from() {
let instructions = ">>++<<--";
let program = Program::from(instructions);
assert_eq!(program.instructions.len(), 8);
assert_eq!(program.length(), Some(8));
}
#[test]
fn test_program_length() {
let program = Program::from(">>++<<--");
assert_eq!(program.length(), Some(8));
let program = Program::from("");
assert_eq!(program.length(), None);
}
#[test]
fn test_program_default() {
let program = Program::default();
assert_eq!(program.instructions.len(), 10);
assert_eq!(program.length(), Some(10));
}
#[test]
fn test_program_display() {
let instructions = vec![Instruction::NoOp];
let program = Program::from(instructions);
assert_eq!(program.to_string(), "0000: NOOP\n");
let instructions = vec![Instruction::NoOp, Instruction::NoOp];
let program = Program::from(instructions);
assert_eq!(program.to_string(), "0000: NOOP\n0001: NOOP\n");
}
#[test]
fn test_program_find_matching_bracket() {
let instructions = "[]";
let program = Program::from(instructions);
assert_eq!(program.find_matching_bracket(0), Some(1));
}
#[test]
fn test_program_find_matching_bracket_nested() {
let instructions = "[[]]";
let program = Program::from(instructions);
assert_eq!(program.find_matching_bracket(0), Some(3));
}
#[test]
fn test_find_matching_bracket_not_jump_forward() {
let instructions = "]";
let program = Program::from(instructions);
assert_eq!(program.find_matching_bracket(0), None);
}
#[test]
fn test_get_instruction() {
let instructions = vec![
Instruction::IncrementPointer,
Instruction::IncrementPointer,
Instruction::IncrementValue,
Instruction::IncrementValue,
Instruction::DecrementPointer,
Instruction::DecrementPointer,
Instruction::DecrementValue,
Instruction::DecrementValue,
];
let program = Program::from(instructions);
assert_eq!(
program.get_instruction(0),
Some(Instruction::IncrementPointer)
);
assert_eq!(
program.get_instruction(1),
Some(Instruction::IncrementPointer)
);
assert_eq!(
program.get_instruction(2),
Some(Instruction::IncrementValue)
);
assert_eq!(
program.get_instruction(3),
Some(Instruction::IncrementValue)
);
assert_eq!(
program.get_instruction(4),
Some(Instruction::DecrementPointer)
);
assert_eq!(
program.get_instruction(5),
Some(Instruction::DecrementPointer)
);
assert_eq!(
program.get_instruction(6),
Some(Instruction::DecrementValue)
);
assert_eq!(
program.get_instruction(7),
Some(Instruction::DecrementValue)
);
assert_eq!(program.get_instruction(8), None);
}
#[test]
fn test_find_matching_bracket() {
let instructions = vec![
Instruction::JumpForward,
Instruction::JumpForward,
Instruction::JumpBackward,
Instruction::JumpBackward,
];
let program = Program::from(instructions);
assert_eq!(program.find_matching_bracket(0), Some(3));
assert_eq!(program.find_matching_bracket(1), Some(2));
assert_eq!(program.find_matching_bracket(2), None);
assert_eq!(program.find_matching_bracket(3), None);
}
#[test]
fn test_default() {
let program = Program::default();
assert_eq!(program.length(), Some(10));
assert_eq!(program.get_instruction(0), Some(Instruction::NoOp));
assert_eq!(program.get_instruction(9), Some(Instruction::NoOp));
}
#[test]
fn test_index() {
let program = Program::from(">>++<<--");
assert_eq!(program[0], Instruction::IncrementPointer);
assert_eq!(program[1], Instruction::IncrementPointer);
assert_eq!(program[2], Instruction::IncrementValue);
assert_eq!(program[3], Instruction::IncrementValue);
assert_eq!(program[4], Instruction::DecrementPointer);
assert_eq!(program[5], Instruction::DecrementPointer);
assert_eq!(program[6], Instruction::DecrementValue);
assert_eq!(program[7], Instruction::DecrementValue);
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_index_out_of_bounds() {
let program = Program::from(">>++<<--");
let _ = program[8];
}
}