-- |
-- Module      : AOC.Challenge.Day05
-- License     : BSD3
--
-- Stability   : experimental
-- Portability : non-portable
--
-- Day 5.  See "AOC.Solver" for the types used in this module!

module AOC.Challenge.Day05 (
    day05a
  , day05b
  ) where

import           AOC.Solver    ((:~>)(..))
import           Data.Bits     (complement, shiftR, (.&.))
import           Data.Char     (ord)
import           Data.List     (foldl')
import qualified Control.Foldl as F

seatId :: String -> Int
seatId :: String -> Int
seatId = (Int -> Char -> Int) -> Int -> String -> Int
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Int -> Char -> Int
iGuessWe'reDoingThis Int
0
  where
    -- heh
    iGuessWe'reDoingThis :: Int -> Char -> Int
iGuessWe'reDoingThis Int
n Char
c =
      Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int -> Int
forall a. Bits a => a -> a
complement (Char -> Int
ord Char
c) Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftR` Int
2) Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
1

-- | Find the first missing item in the collection, in a single pass
findHole :: F.Fold Int (Maybe Int)
findHole :: Fold Int (Maybe Int)
findHole = do
    Maybe Int
mn <- Fold Int (Maybe Int)
forall a. Ord a => Fold a (Maybe a)
F.minimum
    Maybe Int
mx <- Fold Int (Maybe Int)
forall a. Ord a => Fold a (Maybe a)
F.maximum
    Int
sm <- Fold Int Int
forall a. Num a => Fold a a
F.sum
    pure $
      Int -> Int -> Int -> Int
forall {a}. Integral a => a -> a -> a -> a
missingItem (Int -> Int -> Int -> Int)
-> Maybe Int -> Maybe (Int -> Int -> Int)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Int
mn Maybe (Int -> Int -> Int) -> Maybe Int -> Maybe (Int -> Int)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe Int
mx Maybe (Int -> Int) -> Maybe Int -> Maybe Int
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Int -> Maybe Int
forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
sm
  where
    missingItem :: a -> a -> a -> a
missingItem a
mn a
mx a
sm = a
totalSum a -> a -> a
forall a. Num a => a -> a -> a
- a
sm
      where
        totalSum :: a
totalSum = a
mxa -> a -> a
forall a. Num a => a -> a -> a
*(a
mxa -> a -> a
forall a. Num a => a -> a -> a
+a
1)a -> a -> a
forall a. Integral a => a -> a -> a
`div`a
2 a -> a -> a
forall a. Num a => a -> a -> a
- a
mna -> a -> a
forall a. Num a => a -> a -> a
*(a
mna -> a -> a
forall a. Num a => a -> a -> a
-a
1)a -> a -> a
forall a. Integral a => a -> a -> a
`div`a
2

day05a :: [String] :~> Int
day05a :: [String] :~> Int
day05a = MkSol :: forall a b.
(String -> Maybe a)
-> ((?dyno::DynoMap) => a -> Maybe b) -> (b -> String) -> a :~> b
MkSol
    { sParse :: String -> Maybe [String]
sParse = [String] -> Maybe [String]
forall a. a -> Maybe a
Just ([String] -> Maybe [String])
-> (String -> [String]) -> String -> Maybe [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
    , sShow :: Int -> String
sShow  = Int -> String
forall a. Show a => a -> String
show
    , sSolve :: (?dyno::DynoMap) => [String] -> Maybe Int
sSolve = Fold String (Maybe Int) -> [String] -> Maybe Int
forall (f :: * -> *) a b. Foldable f => Fold a b -> f a -> b
F.fold ((String -> Int) -> Fold Int (Maybe Int) -> Fold String (Maybe Int)
forall a b r. (a -> b) -> Fold b r -> Fold a r
F.premap String -> Int
seatId Fold Int (Maybe Int)
forall a. Ord a => Fold a (Maybe a)
F.maximum)
    }

day05b :: [String] :~> Int
day05b :: [String] :~> Int
day05b = MkSol :: forall a b.
(String -> Maybe a)
-> ((?dyno::DynoMap) => a -> Maybe b) -> (b -> String) -> a :~> b
MkSol
    { sParse :: String -> Maybe [String]
sParse = [String] -> Maybe [String]
forall a. a -> Maybe a
Just ([String] -> Maybe [String])
-> (String -> [String]) -> String -> Maybe [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
    , sShow :: Int -> String
sShow  = Int -> String
forall a. Show a => a -> String
show
    , sSolve :: (?dyno::DynoMap) => [String] -> Maybe Int
sSolve = Fold String (Maybe Int) -> [String] -> Maybe Int
forall (f :: * -> *) a b. Foldable f => Fold a b -> f a -> b
F.fold ((String -> Int) -> Fold Int (Maybe Int) -> Fold String (Maybe Int)
forall a b r. (a -> b) -> Fold b r -> Fold a r
F.premap String -> Int
seatId Fold Int (Maybe Int)
findHole)
    }