<repo_name>atimos/advent2019<file_sep>/src/day2/error.rs
use std::{
    error::Error as StdError,
    fmt::{Display, Formatter, Result as FmtResult},
    num::ParseIntError,
    ops::{Add, Mul},
    result::Result as StdResult,
    string::ToString,
};

#[derive(Debug, PartialEq)]
pub enum Error {
    Parse(ParseIntError),
    Op { op: usize, pos: usize },
    ArgCount(usize),
    Eof,
}

pub type Result<T> = StdResult<T, Error>;

impl StdError for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Parse(err) => Some(err),
            _ => None,
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        match self {
            Self::Parse(err) => err.fmt(f),
            Self::Op { op, pos } => write!(f, "Unknown op {}, in position {}", op, pos),
            Self::ArgCount(pos) => write!(f, "Invalid argument count for op in position {}", pos),
            Self::Eof => write!(f, "Unexpected end of file"),
        }
    }
}

impl From<ParseIntError> for Error {
    fn from(err: ParseIntError) -> Self {
        Self::Parse(err)
    }
}

pub fn run(program: &str) -> Result<String> {
    let mut pos = 0;
    let mut program = program.split(',').map(str::parse).collect::<StdResult<Vec<usize>, _>>()?;

    loop {
        pos += match program.get(pos) {
            Some(1) => apply(Add::add, pos, &mut program)?,
            Some(2) => apply(Mul::mul, pos, &mut program)?,
            Some(99) => break,
            Some(op) => return Err(Error::Op { op: *op, pos }),
            None => return Err(Error::Eof),
        }
    }

    Ok(program.iter().map(ToString::to_string).collect::<Vec<String>>().join(","))
}

fn apply(op: impl Fn(usize, usize) -> usize, pos: usize, program: &mut [usize]) -> Result<usize> {
    if let Some([first, second, output]) = &program.get(pos + 1..=pos + 3) {
        program[*output] = op(program[*first], program[*second]);
        Ok(4)
    } else {
        Err(Error::ArgCount(pos))
    }
}
<file_sep>/src/day2/panic.rs
pub fn step1<R: std::io::Read>(input: R) {
    println!("day 2 step 1 {}", run(parse(input))[0]);
}

pub fn step2<R: std::io::Read>(input: R) {
    let program = parse(input);
    let (verb, noun) = (0..100)
        .flat_map(|verb| (0..100).map(move |noun| (verb, noun)))
        .find(|(verb, noun)| {
            let mut program = program.clone();
            program[1] = *verb;
            program[2] = *noun;
            run(program)[0] == 19690720
        })
        .expect("Did not find result");

    println!("day 2 step 2 {}", 100 * verb + noun);
}

pub fn parse<R: std::io::Read>(mut input: R) -> Vec<usize> {
    let mut program = String::new();
    input.read_to_string(&mut program).expect("Could not load program");

    program.split(',').map(str::parse).collect::<Result<Vec<usize>, _>>().expect("Invalid program")
}

pub fn run(mut program: Vec<usize>) -> Vec<usize> {
    let mut pos = 0;
    loop {
        if let Some(99) | None = program.get(pos) {
            break;
        }

        match program.get(pos..=pos + 3) {
            Some(&[1, in1, in2, out]) => program[out] = program[in1] + program[in2],
            Some(&[2, in1, in2, out]) => program[out] = program[in1] * program[in2],
            Some(&[op, _, _, _]) => panic!("Invalid operation {}", op),
            _ => panic!("Unexpected end of file"),
        };
        pos += 4;
    }
    program
}
<file_sep>/src/day2/enum_.rs
use std::{
    num::ParseIntError,
    ops::{Add, Mul},
    result::Result as StdResult,
    string::ToString,
};

#[derive(Debug, PartialEq)]
pub enum Error {
    Parse(ParseIntError),
    Op { op: usize, pos: usize },
    ArgCount(usize),
    Eof,
}

pub type Result<T> = StdResult<T, Error>;

pub fn run(program: &str) -> Result<String> {
    let mut pos = 0;
    let mut program = program
        .split(',')
        .map(str::parse)
        .collect::<StdResult<Vec<usize>, _>>()
        .map_err(Error::Parse)?;

    loop {
        pos += match program.get(pos) {
            Some(1) => apply(Add::add, pos, &mut program)?,
            Some(2) => apply(Mul::mul, pos, &mut program)?,
            Some(99) => break,
            Some(op) => return Err(Error::Op { op: *op, pos }),
            None => return Err(Error::Eof),
        }
    }

    Ok(program.iter().map(ToString::to_string).collect::<Vec<String>>().join(","))
}

fn apply(op: impl Fn(usize, usize) -> usize, pos: usize, program: &mut [usize]) -> Result<usize> {
    if let Some([first, second, output]) = &program.get(pos + 1..=pos + 3) {
        program[*output] = op(program[*first], program[*second]);
        Ok(4)
    } else {
        Err(Error::ArgCount(pos))
    }
}
<file_sep>/src/day2/thiserror.rs
use joinery::Joinable;

#[derive(Debug, PartialEq, thiserror::Error, displaydoc::Display)]
pub enum Error {
    /// {0}
    Parse(#[source] std::num::ParseIntError),
    /// Unknown op {op} in position {pos}
    Op { op: usize, pos: usize },
    /// Unexpected end of file
    Eof,
    /// Address {0} out of bounds
    OutOfBounds(usize),
    /// Could not finish program, max operations {0}
    ToManyOperations(usize),
}

pub type Result<T> = std::result::Result<T, Error>;

const MAX_OPERATIONS: usize = 1000;

pub fn run(program: &str) -> Result<String> {
    program
        .split(',')
        .map(str::parse)
        .collect::<std::result::Result<_, _>>()
        .map_err(Error::Parse)
        .and_then(|mut program: Vec<usize>| {
            let mut pos = 0;
            for _ in 0..=MAX_OPERATIONS {
                match program.get(pos) {
                    Some(99) | None => return Ok(program),
                    Some(op) if *op != 1 && *op != 2 => return Err(Error::Op { op: *op, pos }),
                    _ => {}
                }

                match program.get(pos..=pos + 3) {
                    Some(&[1, in1, in2, out]) => {
                        apply(std::ops::Add::add, in1, in2, out, &mut program)?
                    }
                    Some(&[2, in1, in2, out]) => {
                        apply(std::ops::Mul::mul, in1, in2, out, &mut program)?
                    }
                    None => return Err(Error::Eof),
                    Some(_) => unreachable!(),
                };
                dbg!(pos);
                pos += 4;
            }
            Err(Error::ToManyOperations(MAX_OPERATIONS))
        })
        .map(|program| program.join_with(",").to_string())
}

fn apply(
    op: impl Fn(usize, usize) -> usize,
    in1: usize,
    in2: usize,
    out: usize,
    program: &mut [usize],
) -> Result<()> {
    let in1 = *program.get(in1).ok_or(Error::OutOfBounds(in1))?;
    let in2 = *program.get(in2).ok_or(Error::OutOfBounds(in2))?;
    let out = program.get_mut(out).ok_or(Error::OutOfBounds(out))?;
    *out = op(in1, in2);
    Ok(())
}
<file_sep>/Cargo.toml
[package]
name = "advent"
version = "0.1.0"
authors = ["atimos"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
thiserror = "1.0.6"
displaydoc = "0.1"
joinery = "2.0.0"
<file_sep>/src/main.rs
#[allow(clippy::all)]
mod day2;
mod day3;
mod day4;

fn main() {
    day4::step1();
    day4::step2();
}
<file_sep>/src/day4.rs
pub fn step1() {
    let result = (245318..=765747)
        .map(|number| number.to_string().chars().collect::<Vec<char>>())
        .filter(|d| d.windows(2).all(is_not_decr))
        .filter(|d| d.iter().group_count().any(|c| c > 1))
        .count();
    dbg!(result);
}

pub fn step2() {
    let result = (245318..=765747)
        .map(|number| number.to_string().chars().collect::<Vec<char>>())
        .filter(|d| d.windows(2).all(is_not_decr))
        .filter(|d| d.iter().group_count().any(|c| c == 2))
        .count();
    dbg!(result);
}

fn is_not_decr(numbers: &[char]) -> bool {
    match numbers {
        &[n1, n2] => n1 <= n2,
        _ => true,
    }
}

struct GroupCount<I: Iterator>(std::iter::Peekable<I>);

impl<Iter: Iterator> Iterator for GroupCount<Iter>
where
    Iter::Item: std::cmp::PartialEq,
{
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        let mut count = 1;

        while let Some(item) = self.0.next() {
            match self.0.peek() {
                Some(next) if next == &item => count += 1,
                _ => return Some(count),
            }
        }

        None
    }
}

trait GroupCountIter: Iterator {
    fn group_count(self) -> GroupCount<Self>
    where
        Self: Sized,
    {
        GroupCount(self.peekable())
    }
}

impl<T> GroupCountIter for T where T: Iterator {}
