7 For each row, determine the difference between the largest value and the
8 smallest value; the checksum is the sum of all of these differences.
10 For example, given the following spreadsheet:
18 - The first row's largest and smallest values are 9 and 1, and their
20 - The second row's largest and smallest values are 7 and 3, and their
22 - The third row's difference is 6.
24 In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18.
28 from notebook_preamble import J, V, define
30 I'll assume the input is a Joy sequence of sequences of integers.
38 So, obviously, the initial form will be a ``step`` function:
42 AoC2017.2 == 0 swap [F +] step
44 This function ``F`` must get the ``max`` and ``min`` of a row of numbers
45 and subtract. We can define a helper function ``maxmin`` which does
50 define('maxmin [max] [min] cleave')
62 Then ``F`` just does that then subtracts the min from the max:
72 define('AoC2017.2 [maxmin - +] step_zero')
90 ...find the only two numbers in each row where one evenly divides the
91 other - that is, where the result of the division operation is a whole
92 number. They would like you to find those numbers on each line, divide
93 them, and add up each line's result.
95 For example, given the following spreadsheet:
103 - In the first row, the only two numbers that evenly divide are 8 and
104 2; the result of this division is 4.
105 - In the second row, the two numbers are 9 and 3; the result is 3.
106 - In the third row, the result is 2.
108 In this example, the sum of the results would be 4 + 3 + 2 = 9.
110 What is the sum of each row's result in your puzzle input?
114 J('[5 9 2 8] sort reverse')
124 J('[9 8 5 2] uncons [swap [divmod] cons] dupdip')
129 [8 5 2] [9 divmod] [8 5 2]
134 [9 8 5 2] uncons [swap [divmod] cons F] dupdip G
135 [8 5 2] [9 divmod] F [8 5 2] G
139 V('[8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip')
144 • [8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip
145 [8 5 2] • [9 divmod] [uncons swap] dip dup [i not] dip
146 [8 5 2] [9 divmod] • [uncons swap] dip dup [i not] dip
147 [8 5 2] [9 divmod] [uncons swap] • dip dup [i not] dip
148 [8 5 2] • uncons swap [9 divmod] dup [i not] dip
149 8 [5 2] • swap [9 divmod] dup [i not] dip
150 [5 2] 8 • [9 divmod] dup [i not] dip
151 [5 2] 8 [9 divmod] • dup [i not] dip
152 [5 2] 8 [9 divmod] [9 divmod] • [i not] dip
153 [5 2] 8 [9 divmod] [9 divmod] [i not] • dip
154 [5 2] 8 [9 divmod] • i not [9 divmod]
155 [5 2] 8 • 9 divmod not [9 divmod]
156 [5 2] 8 9 • divmod not [9 divmod]
157 [5 2] 1 1 • not [9 divmod]
158 [5 2] 1 False • [9 divmod]
159 [5 2] 1 False [9 divmod] •
167 Given a *sorted* sequence (from highest to lowest) we want to \* for
168 head, tail in sequence \* for term in tail: \* check if the head % term
169 == 0 \* if so compute head / term and terminate loop \* else continue
171 So we want a ``loop`` I think
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
176 [a b c d] True [Q] loop
179 ``Q`` should either leave the result and False, or the ``rest`` and
192 This suggests that ``Q`` should start with:
196 [a b c d] uncons dup roll<
199 Now we just have to ``pop`` it if we don't need it.
203 [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec
204 [b c d] [b c d] [a P] [a T] [E] primrec
210 w/ Q == [% not] [T] [F] primrec
214 [b c d] a [b c d] uncons
215 [b c d] a b [c d] roll>
217 [b c d] [c d] a b [% not] [T] [F] primrec
220 [b c d] [c d] a b / roll> popop 0
222 [b c d] [c d] a b F Q
223 [b c d] [c d] a b pop swap uncons ... Q
224 [b c d] [c d] a swap uncons ... Q
225 [b c d] a [c d] uncons ... Q
226 [b c d] a c [d] roll> Q
229 Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec
231 uncons tuck uncons roll> Q
235 J('[8 5 3 2] 9 [swap] [% not] [cons] app2 popdd')
240 [8 5 3 2] [9 swap] [9 % not]
249 [b c d] a [b c d] [not] [popop 1] [Q] ifte
266 w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
270 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
271 a [b c d] first % not
276 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
281 a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
282 a [b c d] rest [not] [popop 1] [Q] ifte
283 a [c d] [not] [popop 1] [Q] ifte
284 a [c d] [not] [popop 1] [Q] ifte
286 a [c d] [not] [popop 1] [Q] ifte
295 uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
297 I finally sat down with a piece of paper and blocked it out.
298 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
300 First, I made a function ``G`` that expects a number and a sequence of
301 candidates and return the result or zero:
313 It's a recursive function that conditionally executes the recursive part
314 of its recursive branch
318 [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
320 The recursive branch is the else-part of the inner ``ifte``:
324 G == [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
325 == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte
327 But this is in hindsight. Going forward I derived:
333 [rest [not] [popop 0]]
336 The predicate detects if the ``n`` can be evenly divided by the
337 ``first`` item in the list. If so, the then-part returns the result.
342 n [m ...] rest [not] [popop 0] [G] ifte
343 n [...] [not] [popop 0] [G] ifte
345 This ``ifte`` guards against empty sequences and returns zero in that
346 case, otherwise it executes ``G``.
350 define('G [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec')
352 Now we need a word that uses ``G`` on each (head, tail) pair of a
353 sequence until it finds a (non-zero) result. It's going to be designed
354 to work on a stack that has some candidate ``n``, a sequence of possible
355 divisors, and a result that is zero to signal to continue (a non-zero
356 value implies that it is the discovered result):
360 n [...] p find-result
361 ---------------------------
364 It applies ``G`` using ``nullary`` because if it fails with one
365 candidate it needs the list to get the next one (the list is otherwise
370 find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
372 n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec
374 The base-case is trivial, return the (non-zero) result. The recursive
379 n [...] p roll< popop uncons [G] nullary find-result
380 [...] p n popop uncons [G] nullary find-result
381 [...] uncons [G] nullary find-result
382 m [..] [G] nullary find-result
385 The puzzle states that the input is well-formed, meaning that we can
386 expect a result before the row sequence empties and so do not need to
387 guard the ``uncons``.
391 define('find-result [0 >] [roll> popop] [roll< popop uncons [G] nullary] tailrec')
395 J('[11 9 8 7 3 2] 0 tuck find-result')
403 In order to get the thing started, we need to ``sort`` the list in
404 descending order, then prime the ``find-result`` function with a dummy
405 candidate value and zero ("continue") flag.
409 define('prep-row sort reverse 0 tuck')
411 Now we can define our program.
415 define('AoC20017.2.extra [prep-row find-result +] step_zero')
423 [3 8 6 5]] AoC20017.2.extra