1 {-# LANGUAGE BangPatterns #-}
2 {-# OPTIONS_HADDOCK ignore-exports #-}
3 -- module Day15 (day15a, day15b) where
5 import Control.Arrow ((***))
6 import Data.Bits ((.&.))
7 import Data.Function (on)
8 import Data.List (stripPrefix)
9 import Data.Word (Word64)
11 -- | Returns the initial values for generators A and B.
12 parse :: String -> (Word64, Word64)
14 -- parse input = (read a, read b) where
15 -- [line1, line2] = lines input
16 -- Just a = stripPrefix "Generator A starts with " line1
17 -- Just b = stripPrefix "Generator B starts with " line2
26 -- | One step of generator A.
27 genA :: Word64 -> Word64
28 genA x = x * 16807 `mod` 2147483647
30 -- | One step of generator B.
31 genB :: Word64 -> Word64
32 genB x = x * 48271 `mod` 2147483647
34 -- | Step generator A until a multiple of 4.
35 genA' :: Word64 -> Word64
36 genA' x = let y = genA x in if y .&. 3 == 0 then y else genA' y
38 -- | Step generator A until a multiple of 8.
39 genB' :: Word64 -> Word64
40 genB' x = let y = genB x in if y .&. 7 == 0 then y else genB' y
42 -- | One step of both generators A and B.
43 gen :: (Word64, Word64) -> (Word64, Word64)
46 -- | Step both generators A and B until a multiple of 4 and 8 respectively.
47 gen' :: (Word64, Word64) -> (Word64, Word64)
48 gen' = genA' *** genB'
50 -- | prop> count p f x n == length (filter p . take n . tail $ iterate f x)
51 count :: (a -> Bool) -> (a -> a) -> a -> Int -> Int
52 count p f = count' 0 where
54 count' !k x n = count' (if p y then k + 1 else k) y (n - 1) where y = f x
56 day15a :: String -> Int
58 count (uncurry ((==) `on` (.&. 0xffff))) gen (parse input) 40000000
60 day15b :: String -> Int
62 count (uncurry ((==) `on` (.&. 0xffff))) gen' (parse input) 5000000