--- /dev/null
+use std::io::BufRead;
+
+use itertools::{self, Itertools};
+
+type Reports = Vec<Vec<i32>>;
+
+pub fn read_reports<R>(reader: R) -> Reports
+where
+ R: BufRead,
+{
+ let mut reports = Vec::<Vec<i32>>::new();
+ for l in reader.lines() {
+ reports.push(
+ l.unwrap()
+ .split(' ')
+ .map(|level| level.parse::<i32>().unwrap())
+ .collect(),
+ );
+ }
+ reports
+}
+
+pub fn nb_of_safe_reports(reports: &Reports) -> i32 {
+ reports.iter().fold(0, |nb, levels| {
+ let sign_first = (levels[0] - levels[1]).signum();
+ for (a, b) in levels.iter().tuple_windows() {
+ let d = a - b;
+ if d.signum() != sign_first || d.abs() == 0 || d.abs() > 3 {
+ return nb;
+ }
+ }
+ nb + 1
+ })
+}
+
+pub fn nb_of_safe_reports_with_tolerance(reports: &Reports) -> i32 {
+ reports.iter().fold(0, |nb, levels| {
+ let mut diff: Vec<i32> = levels.iter().tuple_windows().map(|(a, b)| b - a).collect();
+ if diff.iter().map(|v| v.signum()).sum::<i32>() < 0 {
+ for v in diff.iter_mut() {
+ *v = *v * -1;
+ }
+ }
+
+ let pos_negs = diff.iter().positions(|v| *v <= 0).collect::<Vec<usize>>();
+
+ if pos_negs.len() > 1 {
+ return nb;
+ }
+ if pos_negs.len() == 1 {
+ let p = pos_negs[0];
+ // First diff.
+ if p == 0 {
+ if diff[p + 1] > 3 {
+ diff[p + 1] += diff[p];
+ }
+ // Last diff.
+ } else if p == diff.len() - 1 {
+ if diff[p - 1] > 3 {
+ diff[p - 1] += diff[p];
+ }
+ } else {
+ if diff[p - 1] > diff[p + 1] {
+ diff[p - 1] += diff[p];
+ if diff[p - 1] == 0 {
+ return nb;
+ }
+ } else {
+ diff[p + 1] += diff[p];
+ if diff[p + 1] == 0 {
+ return nb;
+ }
+ }
+ }
+ }
+
+ let pos_too_big = diff.iter().positions(|v| *v > 3).collect::<Vec<usize>>();
+ if pos_too_big.len() > 1
+ || pos_too_big.len() == 1 && pos_negs.len() == 1
+ || pos_too_big.len() == 1 && pos_too_big[0] != 0 && pos_too_big[0] != diff.len() - 1
+ {
+ return nb;
+ }
+
+ nb + 1
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ static REPORTS: &str = "7 6 4 2 1
+1 2 7 8 9
+9 7 6 2 1
+1 3 2 4 5
+8 6 4 4 1
+1 3 6 7 9";
+
+ #[test]
+ fn part1() {
+ let reports = read_reports(REPORTS.as_bytes());
+ assert_eq!(nb_of_safe_reports(&reports), 2);
+ }
+
+ #[test]
+ fn part2() {
+ let reports = read_reports(REPORTS.as_bytes());
+ assert_eq!(nb_of_safe_reports_with_tolerance(&reports), 4);
+ }
+
+ static VALID_REPORTS: &str = "1 4 -10 5
+1 -10 4 5
+-10 1 4 5
+1 4 5 -10";
+
+ static INVALID_REPORTS: &str = "8 4 8 4
+4 8 4 8
+0 4 2 6
+6 2 4 0
+8 8 8 8";
+
+ #[test]
+ fn part2_additional_valid_reports() {
+ let reports = read_reports(VALID_REPORTS.as_bytes());
+ assert_eq!(nb_of_safe_reports_with_tolerance(&reports), 4);
+ }
+
+ #[test]
+ fn part2_additional_invalid_reports() {
+ let reports = read_reports(INVALID_REPORTS.as_bytes());
+ assert_eq!(nb_of_safe_reports_with_tolerance(&reports), 0);
+ }
+}
day01::similarity_score(&ids1, &ids2)
)
}
+
+pub fn day02() -> String {
+ let f = fs::File::open("data/day02.input").unwrap();
+ let reports = day02::read_reports(BufReader::new(f));
+ format!(
+ "part1: {}, part2: {}",
+ day02::nb_of_safe_reports(&reports),
+ day02::nb_of_safe_reports_with_tolerance(&reports)
+ )
+}