--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--Module       : Type.Nat
--Author       : Daniel Schüssler
--License      : BSD3
--Copyright    : Daniel Schüssler
--
--Maintainer   : Daniel Schüssler
--Stability    : Experimental
--Portability  : Uses various GHC extensions
--
--------------------------------------------------------------------------------
--Description  : 
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------



{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS -Wall #-}

-- | TODO
--
-- * Prove that 'Initor' is an 'NMorphism'
--
-- * Prove that it is uniquely so
--
module Type.Nat where

import Type.Set
import Type.Dummies()
import Type.Function
import Type.Logic()
import Data.Type.Equality
import Control.Monad()
import Control.Exception

#include "../Defs.hs"
         
-- | Sets equipped with a constant and a function to itself
data NStructure set z succ where
                  NStructure :: z :: set -> (set :~>: set) succ ->
                               NStructure set z succ

-- | Structure-preserving maps of 'NStructure's
data NMorphism set1 z1 succ1 set2 z2 succ2 (f :: SET) where
    NMorphism :: NStructure set1 z1 succ1 ->
                NStructure set2 z2 succ2 ->
                (set1 :~>: set2) f ->
                (f z1 :=: z2) ->
                (f :: succ1 
                 :==:
                 succ2 :: f) ->

                NMorphism set1 z1 succ1 z2 set2 succ2 f
                          
-- | Expresses that @(set1,z1,succ1)@ is initial in the cat of 'NStructure's, in other words, that it is isomorphic to the natural numbers
data NInitial set1 z1 succ1 where
    NInitial :: (forall z2 set2 succ2. ExUniq1 (NMorphism set1 z1 succ1 z2 set2 succ2))
               -> NInitial set1 z1 succ1
                 

-- data IsPeano nat z succ = 
--     IsPeano {
--       succ_fun :: (nat :~>: nat) succ
--     , succ_inj :: Injective succ
--     , not_succ_zero :: forall n. n :∈: nat -> Not ((n,z) :∈: succ)
--     , induction :: forall set. z :∈: set -> 
--                   (forall n n1. n :∈: set ->   
--                            (n,n1) :∈: succ ->
--                            n1 :∈: set) ->

--                   set :==: nat
--     }

-- | Actually any pair of (nullary type, unary type constructor) gives us a copy of the naturals; let's call these /TNats/
data TNat z s :: SET where
    IsZ :: TNat z s z
    IsS :: TNat z s n -> TNat z s (s n)
-- | Successor function made from a unary type constructor
data Succ z s :: SET where 
    Succ :: n :: TNat z s -> Succ z s (n,s n)
           
succFun :: (TNat z s :~>: TNat z s) (Succ z s)
succFun = IsFun (Subset (\(Succ n) -> n :×: IsS n))
          (ExSnd . Succ)
          (\(Succ _) (Succ _) k -> k)
          
tyconNStruct :: NStructure (TNat z s) z (Succ z s)
tyconNStruct = NStructure IsZ succFun
               
-- | The unique morphism from an 'TNat' to any 'NStructure' 
--
-- NB: @s@ is a type constructor, but @succ2@ is a Function ('IsFun')
data Initor z s z2 succ2 :: SET where
      InitorZ :: Initor z s z2 succ2 (z,z2)
      InitorS :: Initor z s z2 succ2 (n1, n2) -> 
                (n2,sn2) :: succ2 ->
                Initor z s z2 succ2 (s n1, sn2)
                       

initorFun :: forall z s set2 z2 succ2. NStructure set2 z2 succ2 -> 
            (TNat z s :~>: set2) (Initor z s z2 succ2)
                                
initorFun (NStructure z2 succ2) =

    let 
        prelation :: pair :: Initor z s z2 succ2 -> 
                         pair :: TNat z s :×: set2
                              
        prelation InitorZ = IsZ :×: z2
        prelation (InitorS i0 p) = case prelation i0 of
                                          q1 :×: _ -> 
                                                 IsS q1 
                                                    :×: 
                                                 inCod succ2 p
                                                       
        ptotal :: n :: TNat z s -> ExSnd (Initor z s z2 succ2) n
        ptotal IsZ = ExSnd InitorZ
        ptotal (IsS n) = 
            case ptotal n of
              ExSnd (inn2 :: Initor z s z2 succ2 (n,n2)) -> 
                  
                         (let
                             succ2ofn2 :: ExSnd succ2 n2
                             succ2ofn2 =
                                 case prelation inn2 of
                                   _ :×: n2 ->
                                       total succ2 n2
                          in
                           case succ2ofn2 of 
                             ExSnd e -> ExSnd (InitorS inn2 e))
                         
        psval :: Initor z s z2 succ2 (n,n2) ->
                Initor z s z2 succ2 (m,m2) ->
                    n2 :=: m2
                       
        psval InitorZ InitorZ = Refl
        psval (InitorS inn2 succ2n2) (InitorS inn2' succ2'n2') = 
            case psval inn2 inn2' of
              Refl ->
                  case sval succ2 succ2n2 succ2'n2' of
                    Refl -> Refl
                           
        -- Currently impossible without 'undefined' :-(

        -- http://www.nabble.com/Is-it-possible-to-prove-type-*non*-equality-in-Haskell--to25142999.html 
        psval (InitorS _ _) InitorZ = assert False undefined
        psval InitorZ (InitorS _ _) = assert False undefined
     in
       IsFun (Subset prelation) ptotal (\p1 p2 k -> case psval p1 p2 of 
                                                     Refl -> k)


-- tyconPeano :: IsPeano (TNat z s) z (Succ z s)
-- tyconPeano = IsPeano succFun
--              (Injective (\(Succ _) (Succ _) k -> k))
             
--              undefined undefined