aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2019-10-06 21:03:41 +0200
committerReiner Herrmann <reiner@reiner-h.de>2019-10-06 21:03:41 +0200
commitb5fc5b305ab51ef5a0b893c92630581b6e68ed5a (patch)
treea71794af193aa1e4a7e22568968d684a68e3f1ac
parent0742459cb5f82c7b069a3241d52e4984dcf2761e (diff)
Optimize sequences of commands into single commands
-rw-r--r--src/main.rs158
1 files changed, 121 insertions, 37 deletions
diff --git a/src/main.rs b/src/main.rs
index 75d5d2e..396c1d2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,12 +8,12 @@ use std::fs::File;
use std::io::{self, Read, BufReader};
use std::collections::HashMap;
-#[derive(PartialEq,Debug)]
+#[derive(PartialEq,Copy,Clone,Debug)]
enum Command {
- INCPTR,
- DECPTR,
- INCVAL,
- DECVAL,
+ INCPTR { amount: isize },
+ DECPTR { amount: isize },
+ INCVAL { amount: u8 },
+ DECVAL { amount: u8 },
PUTC,
GETC,
LOOPSTART { end: usize },
@@ -34,15 +34,15 @@ impl Program {
break;
}
match self.commands[pc] {
- Command::INCPTR => pos = pos.checked_add(1).ok_or("Pointer overflow")?,
- Command::DECPTR => pos = pos.checked_sub(1).ok_or("Pointer underflow")?,
- Command::INCVAL => {
+ Command::INCPTR { amount } => pos = pos.checked_add(amount).ok_or("Pointer overflow")?,
+ Command::DECPTR { amount } => pos = pos.checked_sub(amount).ok_or("Pointer underflow")?,
+ Command::INCVAL { amount } => {
let val = memory.entry(pos).or_insert(0);
- *val = val.wrapping_add(1);
+ *val = val.wrapping_add(amount);
},
- Command::DECVAL => {
+ Command::DECVAL { amount } => {
let val = memory.entry(pos).or_insert(0);
- *val = val.wrapping_sub(1);
+ *val = val.wrapping_sub(amount);
},
Command::PUTC => {
let char_out = *memory.get(&pos).unwrap_or(&0);
@@ -98,7 +98,9 @@ fn find_loops(commands: &mut Vec<Command>) -> Result<(), String> {
Ok(())
}
-fn optimize(program: &mut Vec<Command>) {
+/// remove pairs of commands that cancel each other,
+/// like (increase_value, decrease_value)
+fn optimize_cancelling_pairs(program: &mut Vec<Command>) {
let mut remove_pair = |first: Command, second: Command| -> bool {
for i in 0 .. program.len() - 1 {
if (program[i] == first && program[i+1] == second) ||
@@ -111,8 +113,39 @@ fn optimize(program: &mut Vec<Command>) {
false
};
- while remove_pair(Command::INCPTR, Command::DECPTR) {}
- while remove_pair(Command::INCVAL, Command::DECVAL) {}
+ while remove_pair(Command::INCPTR { amount: 1 }, Command::DECPTR { amount: 1 }) {}
+ while remove_pair(Command::INCVAL { amount: 1 }, Command::DECVAL { amount: 1 }) {}
+}
+
+/// combine sequences of identical commands into a single command
+fn optimize_sequences(program: &mut Vec<Command>) {
+ let mut to_remove = Vec::new();
+ let mut seq_len = 1;
+ for i in 1 .. program.len() {
+ if program[i-1] != program[i] {
+ seq_len = 1;
+ continue;
+ }
+ // update first element of the sequence
+ match program[i-seq_len] {
+ Command::INCPTR { amount } => program[i-seq_len] = Command::INCPTR { amount: amount.wrapping_add(1) },
+ Command::DECPTR { amount } => program[i-seq_len] = Command::DECPTR { amount: amount.wrapping_add(1) },
+ Command::INCVAL { amount } => program[i-seq_len] = Command::INCVAL { amount: amount.wrapping_add(1) },
+ Command::DECVAL { amount } => program[i-seq_len] = Command::DECVAL { amount: amount.wrapping_add(1) },
+ _ => continue,
+ };
+ to_remove.push(i);
+ seq_len += 1;
+ }
+ // remove elements in reverse direction to keep indexes valid
+ for i in to_remove.iter().rev() {
+ program.remove(*i);
+ }
+}
+
+fn optimize(program: &mut Vec<Command>) {
+ optimize_cancelling_pairs(program);
+ optimize_sequences(program);
}
fn preprocess(program: &str) -> String {
@@ -123,10 +156,10 @@ fn preprocess(program: &str) -> String {
fn tokenize(input: &str) -> Vec<Command> {
input.chars().map(|token|
match token {
- '>' => Command::INCPTR,
- '<' => Command::DECPTR,
- '+' => Command::INCVAL,
- '-' => Command::DECVAL,
+ '>' => Command::INCPTR { amount: 1 },
+ '<' => Command::DECPTR { amount: 1 },
+ '+' => Command::INCVAL { amount: 1 },
+ '-' => Command::DECVAL { amount: 1 },
'.' => Command::PUTC,
',' => Command::GETC,
'[' => Command::LOOPSTART { end: 0 },
@@ -176,10 +209,10 @@ mod tests {
assert_eq!(tokenize(""), []);
assert_eq!(tokenize("><+-.,[]"),
[
- Command::INCPTR,
- Command::DECPTR,
- Command::INCVAL,
- Command::DECVAL,
+ Command::INCPTR { amount: 1 },
+ Command::DECPTR { amount: 1 },
+ Command::INCVAL { amount: 1 },
+ Command::DECVAL { amount: 1 },
Command::PUTC,
Command::GETC,
Command::LOOPSTART { end: 0 },
@@ -197,12 +230,12 @@ mod tests {
#[test]
fn test_find_loops() {
let mut commands = vec![
- Command::INCPTR,
- Command::INCVAL,
+ Command::INCPTR { amount: 1 },
+ Command::INCVAL { amount: 1 },
Command::LOOPSTART { end: 0 },
- Command::DECPTR,
+ Command::DECPTR { amount: 1 },
Command::LOOPSTART { end: 0 },
- Command::DECVAL,
+ Command::DECVAL { amount: 1 },
Command::LOOPEND { start: 0 },
Command::PUTC,
Command::LOOPEND { start: 0 },
@@ -216,10 +249,18 @@ mod tests {
#[test]
fn test_find_loop_unbalanced() {
- let mut commands = vec![Command::INCPTR, Command::LOOPSTART { end: 0 }, Command::INCVAL];
+ let mut commands = vec![
+ Command::INCPTR { amount: 1 },
+ Command::LOOPSTART { end: 0 },
+ Command::INCVAL { amount: 1 }
+ ];
assert!(find_loops(&mut commands).is_err());
- let mut commands = vec![Command::INCPTR, Command::LOOPEND { start: 0 }, Command::INCVAL];
+ let mut commands = vec![
+ Command::INCPTR { amount: 1 },
+ Command::LOOPEND { start: 0 },
+ Command::INCVAL { amount: 1 },
+ ];
assert!(find_loops(&mut commands).is_err());
}
@@ -240,17 +281,60 @@ mod tests {
}
#[test]
- fn test_optimize() {
+ fn test_optimize_pair_removal() {
+ let incv = Command::INCVAL { amount: 1 };
+ let decv = Command::DECVAL { amount: 1 };
+ let incp = Command::INCPTR { amount: 1 };
+ let decp = Command::DECPTR { amount: 1 };
let mut commands = vec![
- Command::INCPTR, Command::DECPTR,
- Command::DECPTR, Command::INCPTR,
- Command::INCVAL, Command::DECVAL,
- Command::DECVAL, Command::INCVAL,
- Command::INCPTR, Command::GETC, Command::DECPTR,
- Command::INCVAL, Command::INCVAL, Command::DECPTR, Command::INCPTR, Command::DECVAL, Command::DECVAL,
- Command::PUTC, Command::INCVAL,
+ incp, decp, decp, incp,
+ incv, decv, decv, incv,
+ incp, Command::GETC, decp,
+ incv, incv, decp, incp, decv, decv,
+ Command::PUTC, incv,
];
+ optimize_cancelling_pairs(&mut commands);
+ assert_eq!(commands, [incp, Command::GETC, decp, Command::PUTC, incv]);
+ }
+
+ #[test]
+ fn test_optimize_sequences() {
+ let incv = Command::INCVAL { amount: 1 };
+ let decv = Command::DECVAL { amount: 1 };
+ let incp = Command::INCPTR { amount: 1 };
+ let decp = Command::DECPTR { amount: 1 };
+ let mut commands = vec![incv, decv, incp, decp, incv, incp, incv, incp];
+ optimize_sequences(&mut commands);
+ assert_eq!(commands, [incv, decv, incp, decp, incv, incp, incv, incp]);
+
+ let mut commands = vec![incv, incv, incv, incv, decp, decp, incp, incp, incp, decv, decv];
+ optimize_sequences(&mut commands);
+ assert_eq!(commands, [
+ Command::INCVAL { amount: 4 },
+ Command::DECPTR { amount: 2 },
+ Command::INCPTR { amount: 3 },
+ Command::DECVAL { amount: 2 },
+ ]
+ );
+
+ let mut commands = vec![
+ Command::PUTC, Command::PUTC, Command::GETC, Command::GETC,
+ Command::LOOPSTART { end: 0 }, Command::LOOPSTART { end: 0 },
+ Command::LOOPEND { start: 0 }, Command::LOOPEND { start: 0 }
+ ];
+ let expected = commands.clone();
+ optimize_sequences(&mut commands);
+ assert_eq!(commands, expected);
+ }
+
+ #[test]
+ fn test_optimize() {
+ let incv = Command::INCVAL { amount: 1 };
+ let decv = Command::DECVAL { amount: 1 };
+ let incp = Command::INCPTR { amount: 1 };
+ let decp = Command::DECPTR { amount: 1 };
+ let mut commands = vec![incv, incp, decp, incv, incv, decv, incv];
optimize(&mut commands);
- assert_eq!(commands, [Command::INCPTR, Command::GETC, Command::DECPTR, Command::PUTC, Command::INCVAL]);
+ assert_eq!(commands, [Command::INCVAL { amount: 3 }]);
}
}