{-# Language RankNTypes, QuasiQuotes #-}
module Main (main) where
import Advent (format)
import Control.Applicative ((<|>))
import Data.Semigroup (stimes)
import GHC.Natural (Natural)
import GHC.TypeNats (KnownNat, SomeNat(..), someNatVal)
import Math.NumberTheory.Moduli.Class (Mod, getNatVal)
data Technique
= Cut Integer
| DealInc Integer
| DealNew
deriving Int -> Technique -> ShowS
[Technique] -> ShowS
Technique -> String
(Int -> Technique -> ShowS)
-> (Technique -> String)
-> ([Technique] -> ShowS)
-> Show Technique
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Technique -> ShowS
showsPrec :: Int -> Technique -> ShowS
$cshow :: Technique -> String
show :: Technique -> String
$cshowList :: [Technique] -> ShowS
showList :: [Technique] -> ShowS
Show
toTechnique :: Maybe (Either Integer Integer) -> Technique
toTechnique (Just (Left Integer
n)) = Integer -> Technique
Cut Integer
n
toTechnique (Just (Right Integer
n)) = Integer -> Technique
DealInc Integer
n
toTechnique Maybe (Either Integer Integer)
Nothing = Technique
DealNew
techToLinearFn :: KnownNat n => Technique -> LinearFn (Mod n)
techToLinearFn :: forall (n :: Natural). KnownNat n => Technique -> LinearFn (Mod n)
techToLinearFn Technique
DealNew = Mod n -> Mod n -> LinearFn (Mod n)
forall a. a -> a -> LinearFn a
LinearFn (-Mod n
1) (-Mod n
1)
techToLinearFn (Cut Integer
i) = Mod n -> Mod n -> LinearFn (Mod n)
forall a. a -> a -> LinearFn a
LinearFn Mod n
1 (-Integer -> Mod n
forall a. Num a => Integer -> a
fromInteger Integer
i)
techToLinearFn (DealInc Integer
i) = Mod n -> Mod n -> LinearFn (Mod n)
forall a. a -> a -> LinearFn a
LinearFn (Integer -> Mod n
forall a. Num a => Integer -> a
fromInteger Integer
i) Mod n
0
techsToLinearFn :: KnownNat n => [Technique] -> LinearFn (Mod n)
techsToLinearFn :: forall (n :: Natural).
KnownNat n =>
[Technique] -> LinearFn (Mod n)
techsToLinearFn = (Technique -> LinearFn (Mod n)) -> [Technique] -> LinearFn (Mod n)
forall m a. Monoid m => (a -> m) -> [a] -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap Technique -> LinearFn (Mod n)
forall (n :: Natural). KnownNat n => Technique -> LinearFn (Mod n)
techToLinearFn
data LinearFn a = LinearFn !a !a
deriving Int -> LinearFn a -> ShowS
[LinearFn a] -> ShowS
LinearFn a -> String
(Int -> LinearFn a -> ShowS)
-> (LinearFn a -> String)
-> ([LinearFn a] -> ShowS)
-> Show (LinearFn a)
forall a. Show a => Int -> LinearFn a -> ShowS
forall a. Show a => [LinearFn a] -> ShowS
forall a. Show a => LinearFn a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> LinearFn a -> ShowS
showsPrec :: Int -> LinearFn a -> ShowS
$cshow :: forall a. Show a => LinearFn a -> String
show :: LinearFn a -> String
$cshowList :: forall a. Show a => [LinearFn a] -> ShowS
showList :: [LinearFn a] -> ShowS
Show
apply :: Num a => LinearFn a -> a -> a
apply :: forall a. Num a => LinearFn a -> a -> a
apply (LinearFn a
a a
b) a
x = a
a a -> a -> a
forall a. Num a => a -> a -> a
* a
x a -> a -> a
forall a. Num a => a -> a -> a
+ a
b
invert :: Fractional a => LinearFn a -> LinearFn a
invert :: forall a. Fractional a => LinearFn a -> LinearFn a
invert (LinearFn a
a a
b) = a -> a -> LinearFn a
forall a. a -> a -> LinearFn a
LinearFn (a
1a -> a -> a
forall a. Fractional a => a -> a -> a
/a
a) (-a
ba -> a -> a
forall a. Fractional a => a -> a -> a
/a
a)
instance Num a => Semigroup (LinearFn a) where
LinearFn a
c a
d <> :: LinearFn a -> LinearFn a -> LinearFn a
<> LinearFn a
a a
b = a -> a -> LinearFn a
forall a. a -> a -> LinearFn a
LinearFn (a
aa -> a -> a
forall a. Num a => a -> a -> a
*a
c) (a
b a -> a -> a
forall a. Num a => a -> a -> a
+ a
aa -> a -> a
forall a. Num a => a -> a -> a
*a
d)
instance Num a => Monoid (LinearFn a) where
mempty :: LinearFn a
mempty = a -> a -> LinearFn a
forall a. a -> a -> LinearFn a
LinearFn a
1 a
0
main :: IO ()
IO ()
main =
do [Technique]
techniques <- (Maybe (Either Integer Integer) -> Technique)
-> [Maybe (Either Integer Integer)] -> [Technique]
forall a b. (a -> b) -> [a] -> [b]
map Maybe (Either Integer Integer) -> Technique
toTechnique ([Maybe (Either Integer Integer)] -> [Technique])
-> IO [Maybe (Either Integer Integer)] -> IO [Technique]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [format|2019 22
(cut %ld%n
|deal with increment %ld%n
|deal into new stack%n
)*|]
let shuffle :: KnownNat n => LinearFn (Mod n)
shuffle :: forall (n :: Natural). KnownNat n => LinearFn (Mod n)
shuffle = [Technique] -> LinearFn (Mod n)
forall (n :: Natural).
KnownNat n =>
[Technique] -> LinearFn (Mod n)
techsToLinearFn [Technique]
techniques
Natural -> IO ()
forall a. Show a => a -> IO ()
print ((LinearFn (Mod n)
forall (n :: Natural). KnownNat n => LinearFn (Mod n)
shuffle (forall (n :: Natural). KnownNat n => LinearFn (Mod n))
-> Natural -> Natural -> Natural
`withModulus` Natural
10007) Natural
2019)
let iterations :: Int
iterations = Int
101741582076661 :: Int
decksize :: Natural
decksize = Natural
119315717514047
Natural -> IO ()
forall a. Show a => a -> IO ()
print ((Int -> LinearFn (Mod n) -> LinearFn (Mod n)
forall b. Integral b => b -> LinearFn (Mod n) -> LinearFn (Mod n)
forall a b. (Semigroup a, Integral b) => b -> a -> a
stimes Int
iterations (LinearFn (Mod n) -> LinearFn (Mod n)
forall a. Fractional a => LinearFn a -> LinearFn a
invert LinearFn (Mod n)
forall (n :: Natural). KnownNat n => LinearFn (Mod n)
shuffle) (forall (n :: Natural). KnownNat n => LinearFn (Mod n))
-> Natural -> Natural -> Natural
`withModulus` Natural
decksize) Natural
2020)
withModulus ::
(forall n. KnownNat n => LinearFn (Mod n)) ->
Natural -> Natural -> Natural
forall (n :: Natural). KnownNat n => LinearFn (Mod n)
f withModulus :: (forall (n :: Natural). KnownNat n => LinearFn (Mod n))
-> Natural -> Natural -> Natural
`withModulus` Natural
modulus =
case Natural -> SomeNat
someNatVal Natural
modulus of
SomeNat Proxy n
p -> Mod n -> Natural
forall (m :: Natural). Mod m -> Natural
getNatVal (Mod n -> Natural) -> (Natural -> Mod n) -> Natural -> Natural
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Proxy n -> Mod n -> Mod n
forall (proxy :: Natural -> *) (n :: Natural).
proxy n -> Mod n -> Mod n
asMod Proxy n
p (Mod n -> Mod n) -> (Natural -> Mod n) -> Natural -> Mod n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LinearFn (Mod n) -> Mod n -> Mod n
forall a. Num a => LinearFn a -> a -> a
apply LinearFn (Mod n)
forall (n :: Natural). KnownNat n => LinearFn (Mod n)
f (Mod n -> Mod n) -> (Natural -> Mod n) -> Natural -> Mod n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Natural -> Mod n
forall a b. (Integral a, Num b) => a -> b
fromIntegral
asMod :: proxy n -> Mod n -> Mod n
asMod :: forall (proxy :: Natural -> *) (n :: Natural).
proxy n -> Mod n -> Mod n
asMod proxy n
_ Mod n
x = Mod n
x