{-# OPTIONS_GHC -fno-warn-unticked-promoted-constructors #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Singleton-typed natural numbers and arithmetic.
-- 
--   Used for indexing into hetrogenous list types.
--
module Data.Repa.Scalar.Singleton.Nat
        ( N   (..)
        , Nat (..)
        , Add (..)
        , Mul (..)

        -- * Literals
        , nat0, nat1, nat2, nat3, nat4, nat5, nat6, nat7, nat8, nat9)
where


-- | Peano natural numbers.
data N  = Z | S N


deriving instance Show N


-- | Peano natural number singletons.
data Nat (n :: N) where
        Zero    :: Nat Z
        Succ    :: Nat n -> Nat (S n)

deriving instance Show (Nat n)


---------------------------------------------------------------------------------------------------
class Add x y where
 type AddR x y :: N
 -- | Addition of singleton typed natural numbers.
 add  :: Nat x -> Nat y -> Nat (AddR x y)

instance Add Z x where
 type AddR Z y   = y
 add Zero y      = y
 {-# INLINE add #-}

instance Add x (S y) => Add (S x) y where
 type AddR (S x) y = AddR x (S y)
 add (Succ x) y    = add x  (Succ y)
 {-# INLINE add #-}


---------------------------------------------------------------------------------------------------
class Mul x y where
 type MulR x y :: N
 -- | Multiplication of singleton typed natural numbers.
 mul  :: Nat x -> Nat y -> Nat (MulR x y)

instance Mul Z x where
 type MulR  Z       y = Z
 mul        Zero    _ = Zero

instance (Mul x y, Add (MulR x y) y) 
       => Mul (S x) y where
 type MulR (S x)    y = AddR (MulR x y) y
 mul       (Succ x) y = add  (mul  x y) y


---------------------------------------------------------------------------------------------------
nat0    :: Nat Z
nat0    =  Zero
{-# INLINE nat0 #-}

nat1    :: Nat (S Z)
nat1    =  Succ Zero
{-# INLINE nat1 #-}

nat2    :: Nat (S (S Z))
nat2    =  Succ $ Succ Zero
{-# INLINE nat2 #-}

nat3    :: Nat (S (S (S Z)))
nat3    =  Succ $ Succ $ Succ Zero
{-# INLINE nat3 #-}

nat4    :: Nat (S (S (S (S Z))))
nat4    =  Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat4 #-}

nat5    :: Nat (S (S (S (S (S Z)))))
nat5    =  Succ $ Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat5 #-}

nat6    :: Nat (S (S (S (S (S (S Z))))))
nat6    =  Succ $ Succ $ Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat6 #-}

nat7    :: Nat (S (S (S (S (S (S (S Z)))))))
nat7    =  Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat7 #-}

nat8    :: Nat (S (S (S (S (S (S (S (S Z))))))))
nat8    =  Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat8 #-}

nat9    :: Nat (S (S (S (S (S (S (S (S (S Z)))))))))
nat9    =  Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ $ Succ Zero
{-# INLINE nat9 #-}