1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! # [Day 2: Corruption Checksum](http://adventofcode.com/2017/day/2)
//!
//! As you walk through the door, a glowing humanoid shape yells in your
//! direction. "You there! Your state appears to be idle. Come help us
//! repair the corruption in this spreadsheet - if we take another
//! millisecond, we'll have to display an hourglass cursor!"

/// The spreadsheet consists of rows of apparently-random numbers. To make
/// sure the recovery process is on the right track, they need you to
/// calculate the spreadsheet's *checksum*. For each row, determine the
/// difference between the largest value and the smallest value; the
/// checksum is the sum of all of these differences.
///
/// For example, given the following spreadsheet:
///
/// ```text
/// 5 1 9 5
/// 7 5 3
/// 2 4 6 8
/// ```
///
/// -   The first row's largest and smallest values are `9` and `1`, and
///     their difference is `8`.
/// -   The second row's largest and smallest values are `7` and `3`, and
///     their difference is `4`.
/// -   The third row's difference is `6`.
///
/// In this example, the spreadsheet's checksum would be `8 + 4 + 6 = 18`.
///
/// ```
/// # use advent_solutions::advent2017::day02::{ parse_input, part1 };
/// # let input = "5\t1\t9\t5
/// # 7\t5\t3
/// # 2\t4\t6\t8
/// # ";
/// assert_eq!(part1(&parse_input(input)), 18);
/// ```
///
/// *What is the checksum* for the spreadsheet in your puzzle input?
pub fn part1<'a, I, J>(lines: I) -> usize
    where I: IntoIterator<Item=J>,
          J: IntoIterator<Item=&'a usize>,
{
    lines.into_iter()
        .map(|line| match ::iter::min_and_max(line) {
            Some((min, Some(max))) => max - min,
            Some((_, None)) => 0,
            _ => panic!("Unexpected empty line"),
        })
        .sum::<usize>()
}

/// "Great work; looks like we're on the right track after all. Here's a
/// **star** for your effort." However, the program seems a little worried.
/// Can programs *be* worried?
///
/// "Based on what we're seeing, it looks like all the User wanted is some
/// information about the *evenly divisible values* in the spreadsheet.
/// Unfortunately, none of us are equipped for that kind of calculation -
/// most of us specialize in <span title="Bonus points if you solve this part
/// using only bitwise operations.">bitwise operations</span>."
///
/// It sounds like the goal is to find the only two numbers in each row
/// where one evenly divides the other - that is, where the result of the
/// division operation is a whole number. They would like you to find those
/// numbers on each line, divide them, and add up each line's result.
///
/// For example, given the following spreadsheet:
///
/// ```text
/// 5 9 2 8
/// 9 4 7 3
/// 3 8 6 5
/// ```
///
/// -   In the first row, the only two numbers that evenly divide are `8`
///     and `2`; the result of this division is `4`.
/// -   In the second row, the two numbers are `9` and `3`; the result is `3`.
/// -   In the third row, the result is `2`.
///
/// In this example, the sum of the results would be `4 + 3 + 2 = 9`.
///
/// ```
/// # use advent_solutions::advent2017::day02::{ parse_input, part2 };
/// # let input = "5\t9\t2\t8
/// # 9\t4\t7\t3
/// # 3\t8\t6\t5
/// # ";
/// assert_eq!(part2(&parse_input(input)), 9);
/// ```
///
/// What is the *sum of each row's result* in your puzzle input?
pub fn part2<'a, I, J>(lines: I) -> usize
    where I: IntoIterator<Item=J>,
          J: IntoIterator<Item=&'a usize> + Copy,
{
    lines.into_iter()
        .map(|line| {
            let mut divisible_pairs = line.into_iter()
                .enumerate()
                .filter_map(|(i, x)| line.into_iter()
                    .skip(i + 1)
                    .find(|&y| (x % y) == 0 || (y % x) == 0)
                    .map(|y| (x, y))
                );

            let (x, y) = divisible_pairs.next().expect("No divisible pair found");

            assert!(divisible_pairs.next().is_none(), "More than one divisible pairs");

            if x > y { x / y } else { y / x }
        })
        .sum::<usize>()
}

/// Parses input into a grid of numbers.
pub fn parse_input(input: &str) -> Vec<Vec<usize>> {
    input.lines()
        .map(|line| line.split('\t')
            .map(|x| x.parse::<usize>().expect("Unexpected non-integer in spreadsheet"))
            .collect::<Vec<_>>()
        )
        .collect::<Vec<_>>()
}

test_day!("02", 34925, 221);