day19: refactoring and cleanup
This commit is contained in:
parent
2b921b5fb2
commit
cdfecf821c
67
19/Cargo.lock
generated
67
19/Cargo.lock
generated
@ -12,73 +12,6 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||||||
name = "day19"
|
name = "day19"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
|
||||||
dependencies = [
|
|
||||||
"num-bigint",
|
|
||||||
"num-complex",
|
|
||||||
"num-integer",
|
|
||||||
"num-iter",
|
|
||||||
"num-rational",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-bigint"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-complex"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.45"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-iter"
|
|
||||||
version = "0.1.43"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-bigint",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3,7 +3,5 @@ name = "day19"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num = "0.4.1"
|
num-traits = "0.2.17"
|
||||||
|
182
19/src/main.rs
182
19/src/main.rs
@ -1,8 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
use std::io::{BufRead, BufReader, Lines};
|
||||||
use std::ops::Range;
|
use std::ops::{Range, IndexMut, Index};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
use num_traits::Num;
|
||||||
|
|
||||||
// BOILERPLATE
|
// BOILERPLATE
|
||||||
type InputIter = Lines<BufReader<File>>;
|
type InputIter = Lines<BufReader<File>>;
|
||||||
@ -29,15 +30,6 @@ fn main() {
|
|||||||
|
|
||||||
const INPUT_RANGE: Range<u64> = 1..4001;
|
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)]
|
#[derive(Debug)]
|
||||||
struct RulePredicate {
|
struct RulePredicate {
|
||||||
op: PredicateOperator,
|
op: PredicateOperator,
|
||||||
@ -73,7 +65,61 @@ enum PredicateOperator {
|
|||||||
Gt(u64),
|
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_start = std::cmp::max(r1.start, r2.start);
|
||||||
let new_end = std::cmp::min(r1.end - T::one(), r2.end - T::one());
|
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();
|
let mut residual = Vec::new();
|
||||||
if let Some(overlap) = range_overlap(keep, exclude) {
|
if let Some(overlap) = range_overlap(keep, exclude) {
|
||||||
if keep.start < overlap.start {
|
if keep.start < overlap.start {
|
||||||
@ -130,13 +176,11 @@ impl RulePredicate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn matching_range(&self) -> Range<u64> {
|
fn matching_range(&self) -> Range<u64> {
|
||||||
let res = match self.op {
|
match self.op {
|
||||||
PredicateOperator::Always => INPUT_RANGE,
|
PredicateOperator::Always => INPUT_RANGE,
|
||||||
PredicateOperator::Gt(val) => val + 1..INPUT_RANGE.end,
|
PredicateOperator::Gt(val) => val + 1..INPUT_RANGE.end,
|
||||||
PredicateOperator::Lt(val) => INPUT_RANGE.start..val,
|
PredicateOperator::Lt(val) => INPUT_RANGE.start..val,
|
||||||
};
|
}
|
||||||
println!(" matching range for predicate {:?}: {:?}", self, res);
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,27 +203,13 @@ 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 {
|
impl Rule {
|
||||||
// Returns (matching_ranges, unmatching_ranges for next rule)
|
fn predicate_result(&self, ranges: XmasRanges) -> (XmasRanges, XmasRanges) {
|
||||||
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 {
|
if let PredicateOperator::Always = self.pred.op {
|
||||||
// Always predicate is terminating and returns empty ranges
|
(ranges, XmasRanges::none())
|
||||||
(count_states(ranges), empty_counters())
|
|
||||||
} else {
|
} else {
|
||||||
// other predicates will pop up the stack and return unmatched ranges
|
|
||||||
let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
|
let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
|
||||||
if let Some(relevant_ranges) = ranges.get(&(self.pred.var)){
|
let relevant_ranges = &ranges[self.pred.var];
|
||||||
println!(" relevant: {:?}", relevant_ranges);
|
|
||||||
matching.insert(
|
matching.insert(
|
||||||
self.pred.var,
|
self.pred.var,
|
||||||
relevant_ranges
|
relevant_ranges
|
||||||
@ -188,67 +218,22 @@ impl Rule {
|
|||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
unmatching.insert(
|
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,
|
self.pred.var,
|
||||||
relevant_ranges
|
relevant_ranges
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|range| range_overlap(range, &self.pred.matching_range()))
|
.flat_map(|range| range_exclude(range, &self.pred.matching_range()))
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
);
|
||||||
unmatching.insert(
|
(matching, unmatching)
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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),
|
||||||
}
|
}
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,21 +262,13 @@ impl Workflow {
|
|||||||
}
|
}
|
||||||
panic!("unhandled part {:?}", part);
|
panic!("unhandled part {:?}", part);
|
||||||
}
|
}
|
||||||
fn possible_ranges(
|
fn possible_ranges(&self, wfs: &Workflows, mut ranges: XmasRanges) -> u64 {
|
||||||
&self,
|
|
||||||
wfs: &Workflows,
|
|
||||||
mut ranges: HashMap<char, Vec<Range<u64>>>,
|
|
||||||
) -> u64 {
|
|
||||||
let mut accum = 0u64;
|
let mut accum = 0u64;
|
||||||
println!("Entering {} with ranges {:?}", self.name, ranges);
|
|
||||||
for r in &self.rules {
|
for r in &self.rules {
|
||||||
println!(" evaluating rule: {:?} with {:?}", r, ranges);
|
let count;
|
||||||
let (count, next_ranges) = r.possible_ranges(wfs, ranges);
|
(count, ranges) = r.possible_ranges(wfs, ranges);
|
||||||
ranges = next_ranges;
|
|
||||||
println!(" result of {:?}: count<{}> remaining<{:?}>", r, count, ranges);
|
|
||||||
accum += count
|
accum += count
|
||||||
}
|
}
|
||||||
println!("Count of {}: {}", self.name, accum);
|
|
||||||
accum
|
accum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,16 +288,7 @@ impl Workflows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn count_possible_states(&self) -> u64 {
|
fn count_possible_states(&self) -> u64 {
|
||||||
let ranges = HashMap::from([
|
self.0["in".into()].possible_ranges(self, XmasRanges::all())
|
||||||
('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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user