packages feed

witness-0.5: src/Data/Witness/Nat.hs

module Data.Witness.Nat where

import Data.Constraint (Dict(..))
import Data.Maybe
import Data.Nat
import Data.Type.Equality
import Data.Witness.Representative
import Prelude hiding ((.), id)

data NatType (t :: Nat) where
    ZeroType :: NatType 'Zero
    SuccType :: NatType t -> NatType ('Succ t)

instance TestEquality NatType where
    testEquality ZeroType ZeroType = return Refl
    testEquality (SuccType a) (SuccType b) = do
        Refl <- testEquality a b
        return Refl
    testEquality _ _ = Nothing

instance Representative NatType where
    getRepWitness ZeroType = Dict
    getRepWitness (SuccType n) =
        case getRepWitness n of
            Dict -> Dict

instance Is NatType 'Zero where
    representative = ZeroType

instance (Is NatType n) => Is NatType ('Succ n) where
    representative = SuccType representative

data GreaterEqual (a :: Nat) (b :: Nat) where
    ZeroGreaterEqual :: GreaterEqual a 'Zero
    SuccGreaterEqual :: GreaterEqual a b -> GreaterEqual ('Succ a) ('Succ b)

sameGreaterEqualWit :: NatType a -> GreaterEqual a a
sameGreaterEqualWit ZeroType = ZeroGreaterEqual
sameGreaterEqualWit (SuccType a) = SuccGreaterEqual $ sameGreaterEqualWit a

diff1GreaterEqualWit :: GreaterEqual a b -> GreaterEqual ('Succ a) b
diff1GreaterEqualWit ZeroGreaterEqual = ZeroGreaterEqual
diff1GreaterEqualWit (SuccGreaterEqual ge) = SuccGreaterEqual $ diff1GreaterEqualWit ge

natGreaterEqual :: NatType a -> NatType b -> Maybe (GreaterEqual a b)
natGreaterEqual _ ZeroType = return ZeroGreaterEqual
natGreaterEqual ZeroType _ = Nothing
natGreaterEqual (SuccType a) (SuccType b) = do
    al <- natGreaterEqual a b
    return $ SuccGreaterEqual al

type family Add (a :: Nat) (b :: Nat) :: Nat where
    Add 'Zero b = b
    Add ('Succ a) b = 'Succ (Add a b)

addWit :: NatType a -> NatType b -> NatType (Add a b)
addWit ZeroType b = b
addWit (SuccType a) b = SuccType $ addWit a b

addZeroWit :: NatType a -> Add a 'Zero :~: a
addZeroWit ZeroType = Refl
addZeroWit (SuccType a) =
    case addZeroWit a of
        Refl -> Refl

succAddWit' :: forall a b. NatType a -> 'Succ (Add a b) :~: Add a ('Succ b)
succAddWit' ZeroType = Refl
succAddWit' (SuccType a) =
    case succAddWit' @_ @b a of
        Refl -> Refl

succAddWit :: forall a b. NatType a -> NatType b -> 'Succ (Add a b) :~: Add a ('Succ b)
succAddWit a _ = succAddWit' @a @b a

addGreaterEqualWit :: NatType a -> NatType b -> GreaterEqual (Add a b) a
addGreaterEqualWit a ZeroType =
    case addZeroWit a of
        Refl -> sameGreaterEqualWit a
addGreaterEqualWit a (SuccType b) =
    case succAddWit a b of
        Refl -> diff1GreaterEqualWit $ addGreaterEqualWit a b