module Numeric.Nat.Zeroless
  ( D0(..), D1(..), D2(..), (:+:), (:*:), Zeroless(..)
  , Succ, Pred
  , LT, GT, EQ
  , Compare
  , N1, N8, N16, N32, N64
  , Nat(..), nat 
  , Fin(..)
  , Reverse
  ) where
import Data.Function (on)
import Prelude hiding (lookup)
infixl 7 :*:
infixl 6 :+: 
data D0 = D0 
data D1 n = D1 n 
data D2 n = D2 n 
type N1 = D1 D0
type N8  = D2 (D1 (D1 D0))
type N16 = D2 (D1 (D1 (D1 D0)))
type N32 = D2 (D1 (D1 (D1 (D1 D0))))
type N64 = D2 (D1 (D1 (D1 (D1 (D1 D0)))))
type family Succ n
type instance Succ D0 = D1 D0
type instance Succ (D1 n) = D2 n
type instance Succ (D2 n) = D1 (Succ n)
type family Pred n
type instance Pred (D1 D0) = D0
type instance Pred (D1 (D1 n)) = D2 (Pred (D1 n))
type instance Pred (D1 (D2 n)) = D2 (D1 n)
type instance Pred (D2 n) = D1 n
data C0
data C1
data C2
type family Add c n m
type instance Add C0 D0 n = n
type instance Add C1 D0 D0 = D1 D0
type instance Add C2 D0 D0 = D2 D0
type instance Add C1 D0 (D1 n) = D2 n
type instance Add C1 D0 (D2 n) = D1 (Add C1 D0 n) 
type instance Add C2 D0 (D1 n) = D1 (Add C1 D0 n)
type instance Add C2 D0 (D2 n) = D2 (Add C1 D0 n)
type instance Add C0 (D1 n) D0 = D1 n
type instance Add C1 (D1 n) D0 = D2 n
type instance Add C2 (D1 n) D0 = D1 (Add C1 D0 n)
type instance Add C0 (D1 n) (D1 m) = D2 (Add C0 n m)
type instance Add C1 (D1 n) (D1 m) = D1 (Add C1 n m)
type instance Add C2 (D1 n) (D1 m) = D2 (Add C1 n m)
type instance Add C0 (D1 n) (D2 m) = D1 (Add C1 n m)
type instance Add C1 (D1 n) (D2 m) = D2 (Add C1 n m)
type instance Add C2 (D1 n) (D2 m) = D1 (Add C2 n m)
type instance Add C0 (D2 n) D0 = D2 n
type instance Add C1 (D2 n) D0 = D1 (Add C1 D0 n)
type instance Add C2 (D2 n) D0 = D2 (Add C1 D0 n)
type instance Add C0 (D2 n) (D1 m) = D1 (Add C1 n m)
type instance Add C1 (D2 n) (D1 m) = D2 (Add C1 n m)
type instance Add C2 (D2 n) (D1 m) = D1 (Add C2 n m)
type instance Add C0 (D2 n) (D2 m) = D2 (Add C1 n m)
type instance Add C1 (D2 n) (D2 m) = D1 (Add C2 n m)
type instance Add C2 (D2 n) (D2 m) = D2 (Add C2 n m)
type n :+: m = Add C0 n m
data LT
data EQ
data GT
type family   Compare' a l      r
type instance Compare' a D0     D0     = a
type instance Compare' a D0     (D1 r) = LT
type instance Compare' a D0     (D2 r) = LT
type instance Compare' a (D1 r) D0     = GT
type instance Compare' a (D1 l) (D1 r) = Compare' a l r
type instance Compare' a (D1 l) (D2 r) = Compare' LT l r
type instance Compare' a (D2 l) D0     = GT
type instance Compare' a (D2 l) (D1 r) = Compare' GT l r
type instance Compare' a (D2 l) (D2 r) = Compare' a l r
type Compare m n = Compare' EQ m n 
type family n :*: m
type instance D0 :*: m = D0
type instance D1 n :*: m = (n :*: m) :+: (n :*: m) :+: m
type instance D2 n :*: m = (n :*: m) :+: (n :*: m) :+: m :+: m
type family Digits n
type instance Digits D0 = D0
type instance Digits (D1 n) = Succ (Digits n)
type instance Digits (D2 n) = Succ (Digits n)
type family Reverse' n m
type instance Reverse' m D0     = m 
type instance Reverse' m (D1 n) = Reverse' (D1 m) n 
type instance Reverse' m (D2 n) = Reverse' (D2 m) n
type Reverse n = Reverse' D0 n
class Zeroless n where
  ind :: f D0 
      -> (forall m. Zeroless m => f m -> f (D1 m)) 
      -> (forall m. Zeroless m => f m -> f (D2 m))
      -> f n
  caseNat
    :: ((n ~ D0) => r) 
    -> (forall x. (n ~ D1 x, Zeroless x) => x -> r)
    -> (forall x. (n ~ D2 x, Zeroless x) => x -> r)
    -> n -> r
instance Zeroless D0 where
  ind z _ _ = z 
  caseNat z _ _ _ = z
instance Zeroless n => Zeroless (D1 n) where
  ind z f g = f (ind z f g)
  caseNat _ f _ (D1 x) = f x
instance Zeroless n => Zeroless (D2 n) where
  ind z f g = g (ind z f g)
  caseNat _ _ g (D2 x) = g x
class Zeroless n => Positive n
instance Zeroless n => Positive (D1 n)
instance Zeroless n => Positive (D2 n)
newtype Nat n = Nat { fromNat :: Int }
instance Zeroless n => Eq (Nat n) where
  _ == _ = True
instance Zeroless n => Ord (Nat n) where
  compare _ _ = EQ
instance Zeroless n => Show (Nat n) where
  showsPrec d (Nat n) = showsPrec d n
instance Zeroless n => Bounded (Nat n) where
  minBound = nat
  maxBound = nat
instance Zeroless n => Enum (Nat n) where
  fromEnum (Nat n) = n
  toEnum _ = nat
nat :: Zeroless n => Nat n 
nat = ind (Nat 0) 
          (Nat . (+1) . (*2) . fromNat) 
          (Nat . (+2) . (*2) . fromNat)
newtype Fin n = Fin { fromFin :: Int } 
instance Show (Fin n) where
  showsPrec d = showsPrec d . fromFin
instance Eq (Fin n) where
  (==) = (==) `on` fromFin
instance Ord (Fin n) where
  compare = compare `on` fromFin 
instance Positive n => Num (Fin n) where
  fromInteger = toEnum . fromInteger
  a + b = toEnum (fromFin a + fromFin b)
  a * b = toEnum (fromFin a * fromFin b)
  a  b = toEnum (fromFin a  fromFin b)
  abs a = a
  signum 0 = 0
  signum _ = 1
inFin :: (Int -> Int) -> Fin n -> Fin n
inFin f = Fin . f . fromFin
instance Positive n => Bounded (Fin n) where
  minBound = Fin 0
  maxBound = inFin (subtract 1) $ 
             ind (Fin 0) 
                 (Fin . ((+1) . (*2)) . fromFin)
                 (Fin . ((+2) . (*2)) . fromFin)
instance Positive n => Enum (Fin n) where
  fromEnum = fromFin
  toEnum n = r where
    r | n < 0 = error "Fin.toEnum: negative number"
      | Fin n <= b = Fin n `asTypeOf` b
      | otherwise = error "Fin.toEnum: index out of range"
    b = maxBound