diff --git a/day-14/src/main.rs b/day-14/src/main.rs
index 80d7c46..0b4a865 100644
--- a/day-14/src/main.rs
+++ b/day-14/src/main.rs
@@ -1,6 +1,5 @@
-use std::convert::TryFrom;
+use std::collections::HashMap;
 use std::fs;
-use std::iter;
 
 use itertools::Itertools;
 use lazy_static::lazy_static;
@@ -10,22 +9,26 @@ 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_chip1(&input)?;
+    let program = parse_program(&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 Chip1Instruction {
-    Mask(u64, u64),
-    Mem(usize, u64),
+enum Instruction {
+    Mask(u64, u64, u64),
+    Mem(u64, u64),
 }
 
-impl Chip1Instruction {
+impl Instruction {
     fn new(instruction: &str) -> Result<Self, String> {
         lazy_static! {
             static ref INSTRUCTION_RE: Regex =
@@ -44,7 +47,7 @@ impl Chip1Instruction {
                 let or = u64::from_str_radix(&caps[3].replace("X", "0"), 2)
                     .map_err(|e| format!("Could not parse or: {}", e))?;
 
-                Chip1Instruction::Mask(and, or)
+                Instruction::Mask(and, or, and ^ or)
             }
             "mem" => {
                 let location = caps[2]
@@ -54,30 +57,28 @@ impl Chip1Instruction {
                     .parse()
                     .map_err(|e| format!("Could not parse value: {}", e))?;
 
-                Chip1Instruction::Mem(location, value)
+                Instruction::Mem(location, value)
             }
             _ => Err("Invalid instruction")?,
         })
     }
 }
 
-fn parse_program_chip1(input: &str) -> Result<Vec<Chip1Instruction>, String> {
-    input.lines().map(Chip1Instruction::new).collect()
+fn parse_program(input: &str) -> Result<Vec<Instruction>, String> {
+    input.lines().map(Instruction::new).collect()
 }
 
-fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
+fn run_program_chip1(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
     let mut mask = (u64::max_value() & MASK_36_BITS, 0);
 
     program
         .iter()
         .filter_map(|instruction| match instruction {
-            Chip1Instruction::Mask(and, or) => {
+            Instruction::Mask(and, or, _) => {
                 mask = (*and, *or);
                 None
             }
-            Chip1Instruction::Mem(address, value) => {
-                Some((*address as u64, value & mask.0 | mask.1))
-            }
+            Instruction::Mem(address, value) => Some((*address as u64, value & mask.0 | mask.1)),
         })
         .collect::<Vec<(u64, u64)>>()
         .into_iter()
@@ -86,86 +87,36 @@ fn run_program_chip1(program: &Vec<Chip1Instruction>) -> Vec<(u64, u64)> {
         .collect()
 }
 
-#[derive(Clone, Debug)]
-enum Chip2Instruction {
-    Mask(u64, u64),
-    Mem(usize, u64),
-}
+fn run_program_chip2(program: &Vec<Instruction>) -> Vec<(u64, u64)> {
+    let mut memory = HashMap::new();
+    let mut mask = (0, 0);
 
-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");
+    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;
+                }
+            }
         }
-
-        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()
+    memory.drain().collect()
 }
 
 #[cfg(test)]
@@ -184,7 +135,7 @@ mod tests {
             "
         );
 
-        let program = parse_program_chip1(input)?;
+        let program = parse_program(input)?;
         let memory = run_program_chip1(&program);
 
         assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 165);
@@ -202,7 +153,7 @@ mod tests {
             "
         );
 
-        let program = parse_program_chip2(input)?;
+        let program = parse_program(input)?;
         let memory = dbg!(run_program_chip2(&program));
 
         assert_eq!(memory.iter().map(|(_, value)| value).sum::<u64>(), 208);