mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-26 21:30:31 +01:00
191 lines
4.3 KiB
Rust
191 lines
4.3 KiB
Rust
use std::collections::HashMap;
|
|
use std::fmt::Debug;
|
|
use std::hash::Hash;
|
|
use std::io;
|
|
use std::io::Read;
|
|
use std::ops::Add;
|
|
use std::ops::Sub;
|
|
use std::str::FromStr;
|
|
|
|
/// Utility trait for things representing coordinates.
|
|
///
|
|
/// This is implemented by default for any simple pair-tuple
|
|
pub trait Point {
|
|
type CoordType;
|
|
|
|
/// Compute the manhattan distance between this and another.
|
|
///
|
|
/// The distance will always be >= 0.
|
|
fn manhattan(self, other: Self) -> Self::CoordType;
|
|
}
|
|
|
|
impl<T> Point for (T, T)
|
|
where
|
|
T: Add<Output = T> + Sub<Output = T> + Copy + Ord,
|
|
{
|
|
type CoordType = T;
|
|
|
|
fn manhattan(self, other: Self) -> T {
|
|
let (xa, ya) = self;
|
|
let (xb, yb) = other;
|
|
xa.max(xb) + ya.max(yb) - xa.min(xb) - ya.min(yb)
|
|
}
|
|
}
|
|
|
|
impl<T> Point for (T, T, T)
|
|
where
|
|
T: Add<Output = T> + Sub<Output = T> + Copy + Ord,
|
|
{
|
|
type CoordType = T;
|
|
|
|
fn manhattan(self, other: Self) -> T {
|
|
let (xa, ya, za) = self;
|
|
let (xb, yb, zb) = other;
|
|
xa.max(xb) + ya.max(yb) + za.max(zb) - xa.min(xb) - ya.min(yb) - za.min(zb)
|
|
}
|
|
}
|
|
|
|
impl<T> Point for [T; 4]
|
|
where
|
|
T: Default + Add<Output = T> + Sub<Output = T> + Copy + Ord,
|
|
{
|
|
type CoordType = T;
|
|
|
|
fn manhattan(self, other: Self) -> T {
|
|
let mut dist = T::default();
|
|
|
|
for (&a, b) in self.iter().zip(other.iter()) {
|
|
dist = dist + a.max(*b) - a.min(*b);
|
|
}
|
|
dist
|
|
}
|
|
}
|
|
|
|
/// Apply Erathostenes's sieve to the supplied array
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `dest` - the destination slice to fill with the sieve. This is
|
|
/// assumed to be filled with "true" before being handed to this
|
|
/// method.
|
|
pub fn prime_sieve(dest: &mut [bool]) {
|
|
if !dest.is_empty() {
|
|
dest[0] = false;
|
|
}
|
|
|
|
if dest.len() >= 2 {
|
|
dest[1] = false;
|
|
}
|
|
|
|
let limit = (dest.len() as f64).sqrt() as usize;
|
|
|
|
for i in 1..=limit {
|
|
if !dest[i] {
|
|
continue;
|
|
}
|
|
|
|
for j in ((i * i)..(dest.len())).step_by(i) {
|
|
dest[j] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Trim ascii whitespace from a byte vector.
|
|
///
|
|
/// This method does no allocations, guaranteed.
|
|
pub fn trim_back(input: &mut Vec<u8>) {
|
|
let mut to_truncate = 0;
|
|
for b in input.iter().rev() {
|
|
if b.is_ascii_whitespace() {
|
|
to_truncate += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if to_truncate > 0 {
|
|
let new_len = input.len() - to_truncate;
|
|
input.truncate(new_len);
|
|
}
|
|
}
|
|
|
|
/// Read the entire input as one value.
|
|
///
|
|
/// This function loads the input into a string and then attempts to parse it.
|
|
pub fn read_single_input<T>(input: &mut dyn Read) -> T
|
|
where
|
|
T: FromStr,
|
|
<T as FromStr>::Err: Debug,
|
|
{
|
|
let mut buf = String::new();
|
|
input.read_to_string(&mut buf).unwrap();
|
|
|
|
buf.trim().parse().unwrap()
|
|
}
|
|
|
|
/// An interface to count elements in particular categories.
|
|
pub trait GroupingCount {
|
|
/// The type of the categories under inspection
|
|
type Type;
|
|
|
|
/// Count the occurrence of all possible values.
|
|
///
|
|
/// This method will return a map from a value to its occurrence rate.
|
|
fn grouping_count(&mut self) -> HashMap<Self::Type, usize>;
|
|
}
|
|
|
|
impl<T> GroupingCount for T
|
|
where
|
|
T: Iterator,
|
|
T::Item: Eq + Hash,
|
|
{
|
|
type Type = T::Item;
|
|
|
|
fn grouping_count(&mut self) -> HashMap<Self::Type, usize> {
|
|
let mut counts = HashMap::new();
|
|
|
|
for element in self {
|
|
*counts.entry(element).or_insert(0) += 1;
|
|
}
|
|
|
|
counts
|
|
}
|
|
}
|
|
|
|
/// Solution trait
|
|
///
|
|
/// Every day's solution should implement this function so that it can
|
|
/// be easily run from the main program.
|
|
pub trait Solution {
|
|
/// Solve the first part of the day
|
|
fn part1(&mut self, input: &mut dyn io::Read) -> String;
|
|
|
|
/// Solve the second part of the day
|
|
fn part2(&mut self, input: &mut dyn io::Read) -> String;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_prime_sieve() {
|
|
let mut input = [true; 10];
|
|
prime_sieve(&mut input);
|
|
|
|
let output = [
|
|
false, false, true, true, false, true, false, true, false, false,
|
|
];
|
|
|
|
assert_eq!(output, input);
|
|
}
|
|
|
|
#[test]
|
|
fn test_grouping_count() {
|
|
let result = [1, 1, 2, 2, 3, 1].iter().grouping_count();
|
|
assert_eq!(3, result[&1]);
|
|
assert_eq!(2, result[&2]);
|
|
assert_eq!(1, result[&3]);
|
|
}
|
|
}
|