Year 2015 Day 10

This day doesn't really add anything new, but we will show off our new views for viewing integers as lists of digits.


import Data.String
import Data.List1
import Data.List.Lazy
import Data.Monoid.Exponentiation
import Data.Nat.Views

import Util
import Util.Digits

Solver Functions

Produce a lazy lists of the digits of a number, in descending order of significance. This effectively translates our new Descending view to a LazyList.


lazyDigits : Integer -> LazyList Integer
lazyDigits i with (descending i)
  lazyDigits i | (NegDec rec) = lazyDigits _ | rec
  lazyDigits 0 | Start = []
  lazyDigits ((digit * (10 ^ magnitude)) + rest) | (Prev _ digit rest rec) = 
    digit :: lazyDigits _ | rec

Apply the look-and-say rule to list of digits. We operate in the list-of-digits space for efficiency, this number will grow into the millions of digits, and Idris is currently lacking some needed primitive operations to perform this operation in Integer space reasonably efficiently. A LazyList is used here to avoid having to actually instantiate the entirety of these reasonably large lists.


lookAndSay : LazyList Integer -> LazyList Integer
lookAndSay digits = 
  -- Flatten the list once more
  lazyConcat
  -- Convert the produced numbers into lists of their digits
  . map lazyDigits
  -- re-flatten our list
  . lazyConcat
  -- Count the number of occurrences of each digit and emit [occurances, digit]
  . map (\xs@(head ::: tail) => 
          (the (LazyList _) [natToInteger $ length xs, head])) 
  -- Group identical digits
  . lazyGroup 
  $ digits

Apply the look-and-say rule to an integer, for repl testing


lookAndSay' : Integer -> Integer
lookAndSay' i = 
  let digits = lazyDigits i
      res = lookAndSay digits
  in unDigits res 0
  where
    unDigits : LazyList Integer -> (acc : Integer) -> Integer
    unDigits [] acc = acc
    unDigits (x :: xs) acc = unDigits xs (acc * 10 + x)

Repeatedly apply lookAndSay to a seed value, with logging


repeatLogged : Has Logger fs => 
  (count : Nat) -> (seed : LazyList Integer) -> Eff fs $ LazyList Integer
repeatLogged 0 seed = pure seed
repeatLogged (S k) seed = do
  trace "Remaining iterations: \{show (k)} digits: \{show . count (const True) $ seed}"
  repeatLogged k (lookAndSay seed) 

Part Functions

Part 1

Parse our input, convert it into a list of digits, then run our lookAndSay function on it 40 times, and count the output digits.


part1 : Eff (PartEff String) (Nat, LazyList Integer)
part1 = do
  input <- askAt "input" >>= (note "Invalid input" . parsePositive)
  let input = lazyDigits input
  info "Input: \{show input}"
  output <- repeatLogged 40 input
  pure (count (const True) output, output)

Part 2

Same thing, but 10 more times


part2 : (digits : LazyList Integer) -> Eff (PartEff String) Nat
part2 digits = do
  output <- repeatLogged 10 digits
  pure $ count (const True) output