day19: refactoring and cleanup
This commit is contained in:
		
							
								
								
									
										220
									
								
								19/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								19/src/main.rs
									
									
									
									
									
								
							@@ -1,8 +1,9 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::io::{BufRead, BufReader, Lines};
 | 
			
		||||
use std::ops::Range;
 | 
			
		||||
use std::ops::{Range, IndexMut, Index};
 | 
			
		||||
use std::time::Instant;
 | 
			
		||||
use num_traits::Num;
 | 
			
		||||
 | 
			
		||||
// BOILERPLATE
 | 
			
		||||
type InputIter = Lines<BufReader<File>>;
 | 
			
		||||
@@ -29,15 +30,6 @@ fn main() {
 | 
			
		||||
 | 
			
		||||
const INPUT_RANGE: Range<u64> = 1..4001;
 | 
			
		||||
 | 
			
		||||
fn empty_counters() -> HashMap<char, Vec<Range<u64>>> {
 | 
			
		||||
    HashMap::from([
 | 
			
		||||
        ('x', vec![INPUT_RANGE]),
 | 
			
		||||
        ('m', vec![INPUT_RANGE]),
 | 
			
		||||
        ('a', vec![INPUT_RANGE]),
 | 
			
		||||
        ('s', vec![INPUT_RANGE]),
 | 
			
		||||
    ])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct RulePredicate {
 | 
			
		||||
    op: PredicateOperator,
 | 
			
		||||
@@ -73,7 +65,61 @@ enum PredicateOperator {
 | 
			
		||||
    Gt(u64),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn range_overlap<T: num::Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
struct XmasRanges {
 | 
			
		||||
    ranges: [Vec<Range<u64>>; 4],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl XmasRanges {
 | 
			
		||||
    fn none() -> XmasRanges {
 | 
			
		||||
        Self {
 | 
			
		||||
            ranges: [vec![0..0], vec![0..0], vec![0..0], vec![0..0]],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn all() -> XmasRanges {
 | 
			
		||||
        Self {
 | 
			
		||||
            ranges: [
 | 
			
		||||
                vec![INPUT_RANGE],
 | 
			
		||||
                vec![INPUT_RANGE],
 | 
			
		||||
                vec![INPUT_RANGE],
 | 
			
		||||
                vec![INPUT_RANGE],
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn idx(c: char) -> usize {
 | 
			
		||||
        match c {
 | 
			
		||||
            'x' => 0,
 | 
			
		||||
            'm' => 1,
 | 
			
		||||
            'a' => 2,
 | 
			
		||||
            's' => 3,
 | 
			
		||||
            c => panic!("`{}` is not a valid xmas char", c),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn insert(&mut self, c: char, v: Vec<Range<u64>>) {
 | 
			
		||||
        self.ranges[Self::idx(c)] = v;
 | 
			
		||||
    }
 | 
			
		||||
    fn count_states(&self) -> u64 {
 | 
			
		||||
        self.ranges
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|ranges| ranges.iter().map(|range| range.end - range.start).sum::<u64>())
 | 
			
		||||
            .product()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Index<char> for XmasRanges {
 | 
			
		||||
    type Output = Vec<Range<u64>>;
 | 
			
		||||
    fn index(&self, index: char) -> &Self::Output {
 | 
			
		||||
        &self.ranges[Self::idx(index)]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IndexMut<char> for XmasRanges {
 | 
			
		||||
    fn index_mut(&mut self, index: char) -> &mut Self::Output {
 | 
			
		||||
        &mut self.ranges[Self::idx(index)]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn range_overlap<T: Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
 | 
			
		||||
    let new_start = std::cmp::max(r1.start, r2.start);
 | 
			
		||||
    let new_end = std::cmp::min(r1.end - T::one(), r2.end - T::one());
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +131,7 @@ fn range_overlap<T: num::Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Opti
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn range_exclude<T: num::Num + Ord + Copy>(keep: &Range<T>, exclude: &Range<T>) -> Vec<Range<T>> {
 | 
			
		||||
fn range_exclude<T: Num + Ord + Copy>(keep: &Range<T>, exclude: &Range<T>) -> Vec<Range<T>> {
 | 
			
		||||
    let mut residual = Vec::new();
 | 
			
		||||
    if let Some(overlap) = range_overlap(keep, exclude) {
 | 
			
		||||
        if keep.start < overlap.start {
 | 
			
		||||
@@ -130,13 +176,11 @@ impl RulePredicate {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn matching_range(&self) -> Range<u64> {
 | 
			
		||||
        let res = match self.op {
 | 
			
		||||
        match self.op {
 | 
			
		||||
            PredicateOperator::Always => INPUT_RANGE,
 | 
			
		||||
            PredicateOperator::Gt(val) => val + 1..INPUT_RANGE.end,
 | 
			
		||||
            PredicateOperator::Lt(val) => INPUT_RANGE.start..val,
 | 
			
		||||
        };
 | 
			
		||||
        println!("   matching range for predicate {:?}: {:?}", self, res);
 | 
			
		||||
        res
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -159,96 +203,37 @@ impl From<&str> for Rule {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn count_states(ranges: HashMap<char, Vec<Range<u64>>>) -> u64 {
 | 
			
		||||
    ['x', 'm', 'a', 's'].iter().map(|c| ranges[c].iter().map(|r| r.end - r.start).sum::<u64>()).product()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Rule {
 | 
			
		||||
    // Returns (matching_ranges, unmatching_ranges for next rule)
 | 
			
		||||
    fn possible_ranges(
 | 
			
		||||
        &self,
 | 
			
		||||
        wfs: &Workflows,
 | 
			
		||||
        ranges: HashMap<char, Vec<Range<u64>>>,
 | 
			
		||||
    ) -> (u64, HashMap<char, Vec<Range<u64>>>) {
 | 
			
		||||
        return match &self.action {
 | 
			
		||||
            RuleAction::Terminate(true) => {
 | 
			
		||||
                if let PredicateOperator::Always = self.pred.op {
 | 
			
		||||
                    // Always predicate is terminating and returns empty ranges
 | 
			
		||||
                    (count_states(ranges), empty_counters())
 | 
			
		||||
                } else {
 | 
			
		||||
                    // other predicates will pop up the stack and return unmatched ranges
 | 
			
		||||
                    let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
 | 
			
		||||
                    if let Some(relevant_ranges) = ranges.get(&(self.pred.var)){
 | 
			
		||||
                        println!("  relevant: {:?}", relevant_ranges);
 | 
			
		||||
                        matching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges
 | 
			
		||||
                                .iter()
 | 
			
		||||
                                .filter_map(|range| range_overlap(range, &self.pred.matching_range()))
 | 
			
		||||
                                .collect(),
 | 
			
		||||
                        );
 | 
			
		||||
                        unmatching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges.iter().flat_map(|range| range_exclude(range, &self.pred.matching_range())).collect());
 | 
			
		||||
                        println!("  matching: {:?}", matching);
 | 
			
		||||
                        (count_states(matching), unmatching)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // relevant_ranges is empty so this is a failed state with no possibilities for one of the values
 | 
			
		||||
                        // probably we should never get here
 | 
			
		||||
                        (0, empty_counters())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RuleAction::Terminate(false) => {
 | 
			
		||||
                if let PredicateOperator::Always = self.pred.op {
 | 
			
		||||
                    // Always predicate is terminating, with false returns 0 count and empty ranges
 | 
			
		||||
                    (0, empty_counters())
 | 
			
		||||
                } else {
 | 
			
		||||
                    let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
 | 
			
		||||
                    if let Some(relevant_ranges) = ranges.get(&(self.pred.var)){
 | 
			
		||||
                        matching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges
 | 
			
		||||
                                .iter()
 | 
			
		||||
                                .filter_map(|range| range_overlap(range, &self.pred.matching_range()))
 | 
			
		||||
                                .collect(),
 | 
			
		||||
                        );
 | 
			
		||||
                        unmatching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges.iter().flat_map(|range| range_exclude(range, &self.pred.matching_range())).collect());
 | 
			
		||||
                        (0, unmatching)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // relevant_ranges is empty so this is a failed state with no possibilities for one of the values
 | 
			
		||||
                        // probably we should never get here
 | 
			
		||||
                        (0, empty_counters())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RuleAction::Jump(wf) => {
 | 
			
		||||
                if let PredicateOperator::Always = self.pred.op {
 | 
			
		||||
                    // always predicate before a jump will always jump, so has no unmatching ranges
 | 
			
		||||
                    (wfs.0[wf].possible_ranges(wfs, ranges), empty_counters())
 | 
			
		||||
                } else {
 | 
			
		||||
                    let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
 | 
			
		||||
                    if let Some(relevant_ranges) = ranges.get(&(self.pred.var)){
 | 
			
		||||
                        matching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges
 | 
			
		||||
                                .iter()
 | 
			
		||||
                                .filter_map(|range| range_overlap(range, &self.pred.matching_range()))
 | 
			
		||||
                                .collect(),
 | 
			
		||||
                        );
 | 
			
		||||
                        unmatching.insert(
 | 
			
		||||
                            self.pred.var,
 | 
			
		||||
                            relevant_ranges.iter().flat_map(|range| range_exclude(range, &self.pred.matching_range())).collect());
 | 
			
		||||
                        (wfs.0[wf].possible_ranges(wfs, matching), unmatching)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // no relevant ranges = no possible continuations
 | 
			
		||||
                        (0, empty_counters())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    fn predicate_result(&self, ranges: XmasRanges) -> (XmasRanges, XmasRanges) {
 | 
			
		||||
        if let PredicateOperator::Always = self.pred.op {
 | 
			
		||||
            (ranges, XmasRanges::none())
 | 
			
		||||
        } else {
 | 
			
		||||
            let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
 | 
			
		||||
            let relevant_ranges = &ranges[self.pred.var];
 | 
			
		||||
            matching.insert(
 | 
			
		||||
                self.pred.var,
 | 
			
		||||
                relevant_ranges
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .filter_map(|range| range_overlap(range, &self.pred.matching_range()))
 | 
			
		||||
                    .collect(),
 | 
			
		||||
            );
 | 
			
		||||
            unmatching.insert(
 | 
			
		||||
                self.pred.var,
 | 
			
		||||
                relevant_ranges
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .flat_map(|range| range_exclude(range, &self.pred.matching_range()))
 | 
			
		||||
                    .collect(),
 | 
			
		||||
            );
 | 
			
		||||
            (matching, unmatching)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn possible_ranges(&self, wfs: &Workflows, ranges: XmasRanges) -> (u64, XmasRanges) {
 | 
			
		||||
        let (matching, unmatching) = self.predicate_result(ranges);
 | 
			
		||||
        match &self.action {
 | 
			
		||||
            RuleAction::Terminate(true) => (matching.count_states(), unmatching),
 | 
			
		||||
            RuleAction::Terminate(false) => (0, unmatching),
 | 
			
		||||
            RuleAction::Jump(wf) => (wfs.0[wf].possible_ranges(wfs, matching), unmatching),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -277,21 +262,13 @@ impl Workflow {
 | 
			
		||||
        }
 | 
			
		||||
        panic!("unhandled part {:?}", part);
 | 
			
		||||
    }
 | 
			
		||||
    fn possible_ranges(
 | 
			
		||||
        &self,
 | 
			
		||||
        wfs: &Workflows,
 | 
			
		||||
        mut ranges: HashMap<char, Vec<Range<u64>>>,
 | 
			
		||||
    ) -> u64 {
 | 
			
		||||
    fn possible_ranges(&self, wfs: &Workflows, mut ranges: XmasRanges) -> u64 {
 | 
			
		||||
        let mut accum = 0u64;
 | 
			
		||||
        println!("Entering {} with ranges {:?}", self.name, ranges);
 | 
			
		||||
        for r in &self.rules {
 | 
			
		||||
            println!(" evaluating rule: {:?} with {:?}", r, ranges);
 | 
			
		||||
            let (count, next_ranges) = r.possible_ranges(wfs, ranges);
 | 
			
		||||
            ranges = next_ranges;
 | 
			
		||||
            println!(" result of {:?}: count<{}> remaining<{:?}>", r, count, ranges);
 | 
			
		||||
            let count;
 | 
			
		||||
            (count, ranges) = r.possible_ranges(wfs, ranges);
 | 
			
		||||
            accum += count
 | 
			
		||||
        }
 | 
			
		||||
        println!("Count of {}: {}", self.name, accum);
 | 
			
		||||
        accum
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -311,16 +288,7 @@ impl Workflows {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn count_possible_states(&self) -> u64 {
 | 
			
		||||
        let ranges = HashMap::from([
 | 
			
		||||
            ('x', vec![INPUT_RANGE]),
 | 
			
		||||
            ('m', vec![INPUT_RANGE]),
 | 
			
		||||
            ('a', vec![INPUT_RANGE]),
 | 
			
		||||
            ('s', vec![INPUT_RANGE]),
 | 
			
		||||
        ]);
 | 
			
		||||
        let possible_ranges = self.0["in".into()].possible_ranges(self, ranges);
 | 
			
		||||
        println!("possible_ranges: {:?}", possible_ranges);
 | 
			
		||||
 | 
			
		||||
        possible_ranges
 | 
			
		||||
        self.0["in".into()].possible_ranges(self, XmasRanges::all())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user