5 For each row, determine the difference between the largest value and the smallest value; the checksum is the sum of all of these differences.
7 For example, given the following spreadsheet:
13 * The first row's largest and smallest values are 9 and 1, and their difference is 8.
14 * The second row's largest and smallest values are 7 and 3, and their difference is 4.
15 * The third row's difference is 6.
17 In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18.
21 from notebook_preamble import J, V, define
24 I'll assume the input is a Joy sequence of sequences of integers.
30 So, obviously, the initial form will be a `step` function:
32 AoC2017.2 == 0 swap [F +] step
34 This function `F` must get the `max` and `min` of a row of numbers and subtract. We can define a helper function `maxmin` which does this:
38 define('maxmin [max] [min] cleave')
49 Then `F` just does that then subtracts the min from the max:
57 define('AoC2017.2 [maxmin - +] step_zero')
74 ...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.
76 For example, given the following spreadsheet:
82 * In the first row, the only two numbers that evenly divide are 8 and 2; the result of this division is 4.
83 * In the second row, the two numbers are 9 and 3; the result is 3.
84 * In the third row, the result is 2.
86 In this example, the sum of the results would be 4 + 3 + 2 = 9.
88 What is the sum of each row's result in your puzzle input?
92 J('[5 9 2 8] sort reverse')
100 J('[9 8 5 2] uncons [swap [divmod] cons] dupdip')
103 [8 5 2] [9 divmod] [8 5 2]
107 [9 8 5 2] uncons [swap [divmod] cons F] dupdip G
108 [8 5 2] [9 divmod] F [8 5 2] G
114 V('[8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip')
117 • [8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip
118 [8 5 2] • [9 divmod] [uncons swap] dip dup [i not] dip
119 [8 5 2] [9 divmod] • [uncons swap] dip dup [i not] dip
120 [8 5 2] [9 divmod] [uncons swap] • dip dup [i not] dip
121 [8 5 2] • uncons swap [9 divmod] dup [i not] dip
122 8 [5 2] • swap [9 divmod] dup [i not] dip
123 [5 2] 8 • [9 divmod] dup [i not] dip
124 [5 2] 8 [9 divmod] • dup [i not] dip
125 [5 2] 8 [9 divmod] [9 divmod] • [i not] dip
126 [5 2] 8 [9 divmod] [9 divmod] [i not] • dip
127 [5 2] 8 [9 divmod] • i not [9 divmod]
128 [5 2] 8 • 9 divmod not [9 divmod]
129 [5 2] 8 9 • divmod not [9 divmod]
130 [5 2] 1 1 • not [9 divmod]
131 [5 2] 1 False • [9 divmod]
132 [5 2] 1 False [9 divmod] •
139 Given a *sorted* sequence (from highest to lowest) we want to
140 * for head, tail in sequence
142 * check if the head % term == 0
143 * if so compute head / term and terminate loop
146 ### So we want a `loop` I think
148 [a b c d] True [Q] loop
151 `Q` should either leave the result and False, or the `rest` and True.
161 This suggests that `Q` should start with:
163 [a b c d] uncons dup roll<
166 Now we just have to `pop` it if we don't need it.
168 [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec
169 [b c d] [b c d] [a P] [a T] [E] primrec
173 w/ Q == [% not] [T] [F] primrec
177 [b c d] a [b c d] uncons
178 [b c d] a b [c d] roll>
180 [b c d] [c d] a b [% not] [T] [F] primrec
183 [b c d] [c d] a b / roll> popop 0
185 [b c d] [c d] a b F Q
186 [b c d] [c d] a b pop swap uncons ... Q
187 [b c d] [c d] a swap uncons ... Q
188 [b c d] a [c d] uncons ... Q
189 [b c d] a c [d] roll> Q
192 Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec
194 uncons tuck uncons roll> Q
198 J('[8 5 3 2] 9 [swap] [% not] [cons] app2 popdd')
201 [8 5 3 2] [9 swap] [9 % not]
208 [b c d] a [b c d] [not] [popop 1] [Q] ifte
225 w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
229 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
230 a [b c d] first % not
235 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
240 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
241 a [b c d] rest [not] [popop 1] [Q] ifte
242 a [c d] [not] [popop 1] [Q] ifte
243 a [c d] [not] [popop 1] [Q] ifte
245 a [c d] [not] [popop 1] [Q] ifte
254 uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
259 ### I finally sat down with a piece of paper and blocked it out.
261 First, I made a function `G` that expects a number and a sequence of candidates and return the result or zero:
271 It's a recursive function that conditionally executes the recursive part of its recursive branch
273 [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
275 The recursive branch is the else-part of the inner `ifte`:
277 G == [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
278 == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte
280 But this is in hindsight. Going forward I derived:
284 [rest [not] [popop 0]]
287 The predicate detects if the `n` can be evenly divided by the `first` item in the list. If so, the then-part returns the result. Otherwise, we have:
289 n [m ...] rest [not] [popop 0] [G] ifte
290 n [...] [not] [popop 0] [G] ifte
292 This `ifte` guards against empty sequences and returns zero in that case, otherwise it executes `G`.
296 define('G [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec')
299 Now we need a word that uses `G` on each (head, tail) pair of a sequence until it finds a (non-zero) result. It's going to be designed to work on a stack that has some candidate `n`, a sequence of possible divisors, and a result that is zero to signal to continue (a non-zero value implies that it is the discovered result):
301 n [...] p find-result
302 ---------------------------
305 It applies `G` using `nullary` because if it fails with one candidate it needs the list to get the next one (the list is otherwise consumed by `G`.)
307 find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
309 n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
311 The base-case is trivial, return the (non-zero) result. The recursive branch...
313 n [...] p roll< popop uncons [G] nullary find-result
314 [...] p n popop uncons [G] nullary find-result
315 [...] uncons [G] nullary find-result
316 m [..] [G] nullary find-result
319 The puzzle states that the input is well-formed, meaning that we can expect a result before the row sequence empties and so do not need to guard the `uncons`.
323 define('find-result [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec')
328 J('[11 9 8 7 3 2] 0 tuck find-result')
334 In order to get the thing started, we need to `sort` the list in descending order, then prime the `find-result` function with a dummy candidate value and zero ("continue") flag.
338 define('prep-row sort reverse 0 tuck')
341 Now we can define our program.
345 define('AoC20017.2.extra [prep-row find-result +] step_zero')
354 [3 8 6 5]] AoC20017.2.extra