diff --git a/day7/Cargo.toml b/day7/Cargo.toml index 3812153..1cac87e 100644 --- a/day7/Cargo.toml +++ b/day7/Cargo.toml @@ -6,5 +6,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +test = [] + [dependencies] generational-arena = "0.2.8" diff --git a/day7/src/bag.rs b/day7/src/bag.rs index 3ce43fc..93d181e 100644 --- a/day7/src/bag.rs +++ b/day7/src/bag.rs @@ -1,103 +1,91 @@ use std::{ - mem, - ops, hash::{Hash, Hasher,}, + collections::HashSet, + borrow::Borrow, }; -use generational_arena::{ - Arena, - Index, -}; -#[derive(Debug, Clone, Eq)] -pub struct Bag +type Bags = HashSet; +type BagRef = String; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Rule { - name: String, - contains: Vec, + bag: BagRef, + contains: Vec<(usize, BagRef)>, } -impl Hash for Bag { - #[inline] fn hash(&self, state: &mut H) { - self.as_ref().hash(state) +impl Borrow for Rule +{ + fn borrow(&self) -> &String { + &self.bag } } -impl PartialEq for Bag -where T: AsRef -{ - fn eq(&self, other: &T) -> bool - { - self.as_ref() == other.as_ref() +impl Hash for Rule { + fn hash(&self, state: &mut H) { + self.bag.hash(state) } } -impl Bag +impl Rule { - pub fn new(name: String) -> Self + #[inline] pub fn name(&self) -> &str { - Self { - name, - contains: Vec::new(), - } + &self.bag[..] } - pub fn push_contents(&mut self, idx: Index) + + /// Find the rules for each inner bag within this context + pub fn inner_rules<'a>(&'a self, hashes: &'a Bags) -> impl Iterator + 'a { - self.contains.push(idx) + self.contains.iter().filter_map(move |(_, re)| hashes.get(re)) } - pub fn bags_in<'a>(&'a self, w: &'a Arena) -> impl Iterator + 'a + #[inline] pub fn new(bag: impl Into, contains: impl IntoIterator) -> Self { - self.contains.iter().filter_map(move |x| w.get(x.clone())).map(Self::as_ref) + return Self::new_ex(bag.into(), contains.into_iter().collect()); } - pub fn contains_in(&self, w: &Arena, bag: impl AsRef) -> bool + pub fn new_ex(bag: String, contains: Vec<(usize, String)>) -> Self { - for x in self.bags_in(w) - { - if x == bag.as_ref() { - return true; - } - } - false + Self {bag, contains} } - -} -impl AsRef for Bag -{ - #[inline] fn as_ref(&self) -> &BagRef + pub fn all_rules<'a>(&'a self, hashes: &'a Bags) -> RuleIterator<'a> { - BagRef::new_unchecked(&self.name[..]) - } -} - -impl ops::Deref for Bag -{ - type Target = BagRef; - #[inline] fn deref(&self) -> &Self::Target { - self.as_ref() + RuleIterator + { + base: self.contains.iter(), + hashes, + held: Vec::with_capacity(self.contains.len()), + } } } -#[derive(Debug, PartialEq, Eq, Hash)] -#[repr(transparent)] -pub struct BagRef +pub struct RuleIterator<'a> { - name: str + base: std::slice::Iter<'a, (usize, BagRef)>, + hashes: &'a Bags, + held: Vec<&'a Rule>, } -impl AsRef for BagRef +impl<'a> Iterator for RuleIterator<'a> { - #[inline] fn as_ref(&self) -> &BagRef + type Item = &'a Rule; + fn next(&mut self) -> Option { - self + if self.held.is_empty() { + match self.base.next() { + Some((_, re)) => { + self.held.push(self.hashes.get(re).unwrap()); + }, + None => return None, + } + } + let ret = self.held.remove(0); + self.held.extend(ret.inner_rules(self.hashes)); + Some(ret) } -} - -impl BagRef -{ - #[inline] fn new_unchecked<'a>(from: &'a str) -> &'a BagRef - { - unsafe { - mem::transmute(from) - } + fn size_hint(&self) -> (usize, Option) { + (self.base.len(), None) } } +impl<'a> std::iter::FusedIterator for RuleIterator<'a>{} diff --git a/day7/src/main.rs b/day7/src/main.rs index 1c0436f..6b68161 100644 --- a/day7/src/main.rs +++ b/day7/src/main.rs @@ -2,9 +2,28 @@ #![allow(dead_code)] +use std::{ + io::BufReader, + fs::OpenOptions, + collections::HashSet, +}; + +#[cfg(feature="test")] +const INPUT: &str ="input-test"; +#[cfg(not(feature="test"))] +const INPUT: &str = "input"; + + mod parse; mod bag; -fn main() { - println!("Hello, world!"); +fn main() -> Result<(), Box> { + let parsed: HashSet<_> = parse::parse(BufReader::new(OpenOptions::new().read(true).open(INPUT)?)).collect(); + #[cfg(debug_assertions)] + for x in parsed.iter() { + eprintln!("{:?}", x); + } + + + Ok(()) } diff --git a/day7/src/parse.rs b/day7/src/parse.rs index 4d7ac98..ec306cf 100644 --- a/day7/src/parse.rs +++ b/day7/src/parse.rs @@ -1,109 +1,48 @@ -use std::{ - io::{ - self, BufRead, - }, - sync::{ - mpsc, - }, - thread, - marker::*, - collections::{HashMap, HashSet,}, -}; -use generational_arena::{ - Arena, Index, -}; +use std::io::BufRead; use super::bag; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct Rule +fn parse_rule(from: impl AsRef) -> Option { - contents: Vec<(usize, String)>, -} - -fn parse_rest(from: mpsc::Receiver) -> HashMap -{ - let mut out = HashMap::new(); - while let Ok(line) = from.recv() { - - } - out -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct UnlinkedBag<'a> -{ - name: &'a str, - cont: Rule, -} - -enum MaybeLinked<'a> -{ - Unlinked(UnlinkedBag<'a>), - Linked(bag::Bag), -} - -pub fn parse(from: R) -> Result, io::Error> -{ - let mut all_possible = HashSet::::new(); - let (tx, rx) = mpsc::channel(); - let w = thread::spawn(move || parse_rest(rx)); + let from = from.as_ref(); + let mut ps = from.split("bag"); - macro_rules! unwrap { - (? $opt:expr) => { - match $opt { - Some(v) => v, - _ => continue, - } - }; - ($res:expr) => { - unwrap!(? $res.ok()) - } - } - for line in from.lines() - { - let mut line = line?; - - let irest = { - const SPLIT: &str = "bags contain"; - let bn = { - let idx = unwrap!(? line.find(SPLIT)); - &line[..idx] + let name = ps.next()?.trim(); + Some(bag::Rule::new(name, (0..).zip(ps).filter_map(|(i, s)| { + let (n, s) = if i == 0 { + const JUNK: &str = "s contain "; + + let (spec, rest) = (&s[JUNK.len()..]).split_once(char::is_whitespace).unwrap(); + let n: usize = match spec { + "no" => 0, + x => x.parse().unwrap(), }; - all_possible.insert(bn.trim().to_owned()); + (n, rest.trim()) + } else { + if s.contains(".") { return None; } + + let s = if s.starts_with("s") { + &s[3..] + } else if s.starts_with(",") { + &s[2..] + } else { + s + }.trim(); + //if s.starts_with(".") { return None; } + + let (spec, rest) = s.split_once(char::is_whitespace).unwrap(); + (spec.parse().unwrap(), rest.trim()) }; - unwrap!(tx.send(line)); - } - let (mut unlinked, nref) = { - let mut ulinks = w.join().unwrap(); - let mut unlinked = Arena::with_capacity(all_possible.len()); - let mut nref = HashMap::new(); - for name in all_possible.iter() - { - let urule = ulinks.remove(name).unwrap(); - let idx = unlinked.insert(MaybeLinked::Unlinked(UnlinkedBag{name, cont: urule})); - nref.insert(name, idx); + if n < 1 { + None + } else { + Some((n, s.to_owned())) } - (unlinked, nref) - }; - - let indecies: Vec<_> = unlinked.iter().map(|(i, _)| i).collect(); - for idx in indecies.into_iter() - { - let current = unlinked.get_mut(idx).unwrap(); - let linked = match current { - MaybeLinked::Unlinked(UnlinkedBag{name, cont: rule}) => { - let mut linking = bag::Bag::new(name.to_owned()); - for (_, cont) in rule.contents.iter() { - linking.push_contents(*nref.get(cont).unwrap()); - } - linking - }, - _=> continue, - }; - *current = MaybeLinked::Linked(linked); - } + }))) +} - //TODO: how tf can we convert from Arena> into Arena<_>????? - - todo!() +pub fn parse(buf: R) -> impl Iterator +{ + buf.lines().filter_map(|x| { + x.ok().map(parse_rule).flatten() + }) }