diff --git a/day-14/src/main.rs b/day-14/src/main.rs
index 0b4a865..80d7c46 100644
--- a/day-14/src/main.rs
+++ b/day-14/src/main.rs
@@ -1,5 +1,6 @@
-use std::collections::HashMap;
+use std::convert::TryFrom;
 use std::fs;
+use std::iter;
 
 use itertools::Itertools;
 use lazy_static::lazy_static;
@@ -9,26 +10,22 @@ const MASK_36_BITS: u64 = 0xFFFFFFFFF;
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
     let input = fs::read_to_string("input")?;
-    let program = parse_program(&input)?;
+    let program = parse_program_chip1(&input)?;
 
     // Part 1
     let memory = run_program_chip1(&program);
     println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
 
-    // Part 2
-    let memory = run_program_chip2(&program);
-    println!("{}", memory.iter().map(|(_, value)| value).sum::<u64>());
-
     Ok(())
 }
 
 #[derive(Copy, Clone, Debug)]
-enum Instruction {
-    Mask(u64, u64, u64),
-    Mem(u64, u64),
+enum Chip1Instruction {
+    Mask(u64, u64),
+    Mem(usize, u64),
 }
 
-impl Instruction {
+impl Chip1Instruction {
     fn new(instruction: &str) -> Result<Self, String> {
         lazy_static! {
             static ref INSTRUCTION_RE: Regex =
@@ -47,7 +44,7 @@ impl Instruction {
                 let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
                     .map_err(|e| format!("Could not parse or: {}", e))?;
 
-                Instruction::Mask(and, or, and ^ or)
+                Chip1Instruction::Mask(and, or)
             }
             "mem" => {
                 let location = caps[2]
@@ -57,28 +54,30 @@ impl Instruction {
                     .parse()
                     .map_err(|e| format!("Could not parse value: {}", e))?;
 
-                Instruction::Mem(location, value)
+                Chip1Instruction::Mem(location, value)
             }
             _ => Err("Invalid instruction")?,
         })
     }
 }
 
-fn parse_program(input: &str) -> Result<Vec<Instruction>, String> {
-    input.lines().map(Instruction::new).collect()
+fn parse_program_chip1(input: &str) -> Result<Vec<Chip1Instruction>, String> {
+    input.lines().map(Chip1Instruction::new).collect()
 }
 
-fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
+fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
     let mut mask = (u64::max_value() & MASK_36_BITS, 0);
 
     program
         .iter()
         .filter_map(|instruction| match instruction {
-            Instruction::Mask(and, or, _) => {
+            Chip1Instruction::Mask(and, or) => {
                 mask = (*and, *or);
                 None
             }
-            Instruction::Mem(address, value) => Some((*address as u64, value & mask.0 | mask.1)),
+            Chip1Instruction::Mem(address, value) => {
+                Some((*address as u64, value & mask.0 | mask.1))
+            }
         })
         .collect::<Vec<(u64, u64)>>()
         .into_iter()
@@ -87,36 +86,86 @@ fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
         .collect()
 }
 
-fn run_program_chip2(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
-    let mut memory = HashMap::new();
-    let mut mask = (0, 0);
+#[derive(Clone, Debug)]
+enum Chip2Instruction {
+    Mask(u64, u64),
+    Mem(usize, u64),
+}
 
-    for instruction in program {
-        match instruction {
-            Instruction::Mask(_, or, floating) => {
-                mask = (*or, *floating);
-            }
-            Instruction::Mem(address, value) => {
-                // We set floating bits to 0 here, so that they can be
-                // toggled with addition
-                let base = (address | mask.0) & !mask.1 & MASK_36_BITS;
-
-                let mut floating = !mask.1 & MASK_36_BITS;
-                while floating <= MASK_36_BITS {
-                    // For each unset bit in the floating mask, we
-                    // toggle the bit in the address
-                    memory.insert(base | (!floating & MASK_36_BITS), *value);
-                    floating += 1;
-
-                    // Since add does overflows, we reset 1s to 0s if
-                    // they're unset in the original mask
-                    floating |= !mask.1 & MASK_36_BITS;
-                }
-            }
+impl Chip2Instruction {
+    fn new(instruction: &str) -> Result<Self, String> {
+        lazy_static! {
+            static ref INSTRUCTION_RE: Regex =
+                Regex::new(r"([[:alpha:]]+?)\[?(\d+?)?\]? = ([0-9X]+)")
+                    .expect("Regex should compile");
         }
-    }
 
-    memory.drain().collect()
+        let caps = INSTRUCTION_RE
+            .captures(instruction)
+            .ok_or("Instruction did not match")?;
+
+        Ok(match &caps[1] {
+            "mask" => {
+                let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
+                    .map_err(|e| format!("Could not parse or: {}", e))?;
+
+                let floating = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
+                    .map_err(|e| format!("Could not parse floating: {}", e))?;
+
+                Chip2Instruction::Mask(or, floating)
+            }
+            "mem" => {
+                let location = caps[2]
+                    .parse()
+                    .map_err(|e| format!("Could not parse location: {}", e))?;
+                let value = caps[3]
+                    .parse()
+                    .map_err(|e| format!("Could not parse value: {}", e))?;
+
+                Chip2Instruction::Mem(location, value)
+            }
+            _ => Err("Invalid instruction")?,
+        })
+    }
+}
+
+fn parse_program_chip2(input: &str) -> Result<Vec<Chip2Instruction>, String> {
+    input.lines().map(Chip2Instruction::new).collect()
+}
+
+fn run_program_chip2(program: &Vec<Chip2Instruction>) -> Vec<(u64, u64)> {
+    let mut mask = (
+        u64::max_value() & MASK_36_BITS,
+        u64::max_value() & MASK_36_BITS,
+    );
+
+    program
+        .iter()
+        .filter_map(|instruction| match instruction {
+            Chip2Instruction::Mask(and, or) => {
+                mask = (*and, *or);
+                None
+            }
+            Chip2Instruction::Mem(address, value) => Some(
+                mask.1
+                    .iter()
+                    .flat_map(|index| {
+                        let address = u64::try_from(*address).expect("<=64 bit usize required");
+
+                        iter::once((address | 1 << index | mask.0) & MASK_36_BITS).chain(
+                            iter::once((address & !(1 << index) | mask.0) & MASK_36_BITS),
+                        )
+                    })
+                    .map(|address| (address, *value))
+                    .collect::<Vec<(u64, u64)>>(),
+            ),
+        })
+        .flatten()
+        .collect::<Vec<(u64, u64)>>()
+        .into_iter()
+        .rev()
+        .unique_by(|address| address.0)
+        .collect()
 }
 
 #[cfg(test)]
@@ -135,7 +184,7 @@ mod tests {
             "
         );
 
-        let program = parse_program(input)?;
+        let program = parse_program_chip1(input)?;
         let memory = run_program_chip1(&program);
 
         assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165);
@@ -153,7 +202,7 @@ mod tests {
             "
         );
 
-        let program = parse_program(input)?;
+        let program = parse_program_chip2(input)?;
         let memory = dbg!(run_program_chip2(&program));
 
         assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);