import Unsafe.Coerce ( unsafeCoerce )

type Church a = (a -> a) -> a -> a

churchZero :: Church  a
churchZero = const id

churchOne :: Church a
churchOne = id

succChurch :: Church a -> Church a
succChurch = (<*>) (.) 

addChurch :: Church a -> Church a -> Church a
addChurch = (<*>). fmap (.) 

multChurch :: Church a -> Church a -> Church a
multChurch = (.) 

expChurch :: Church a -> Church a -> Church a
expChurch basech expch = unsafeCoerce expch basech

isChurchZero :: Church a -> Church a
isChurchZero ch = unsafeCoerce ch (const churchZero) churchOne

predChurch :: Church a -> Church a
predChurch ch f x = unsafeCoerce ch (\ g h -> h (g f)) (const x) id

minusChurch :: Church a -> Church a -> Church a
minusChurch ach bch = unsafeCoerce bch predChurch ach


divChurch :: Church a -> Church a -> Church a
divChurch dvdnd dvsr =
  let divr n d =
        (\ v -> v (const $ succChurch $ divr v d) 
                  churchZero 
        ) (minusChurch n d)
  in divr (unsafeCoerce succChurch dvdnd) $ unsafeCoerce dvsr

churchFromInt :: Int -> Church a
churchFromInt 0 = churchZero
churchFromInt n = succChurch $ churchFromInt (n - 1)







intFromChurch :: Church Int -> Int
intFromChurch ch = ch succ 0


main :: IO ()
main = do
  let [cThree, cFour, cEleven, cTwelve] = churchFromInt <$> [3, 4, 11, 12]
  print $ fmap intFromChurch  [ addChurch cThree cFour
                              , multChurch cThree cFour
                              , expChurch cFour cThree
                              , expChurch cThree cFour
                              , isChurchZero churchZero
                              , predChurch cFour
                              , minusChurch cEleven cThree
                              , divChurch cEleven cThree
                              , divChurch cTwelve cThree
                              ]