{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances,
  FlexibleContexts, UndecidableInstances #-}
{-
   The HList library

   (C) 2004, Oleg Kiselyov, Ralf Laemmel, Keean Schupke

   Array-like access to HLists.
 -}

module Data.HList.HArray where

import Data.HList.FakePrelude
import Data.HList.HListPrelude


{-----------------------------------------------------------------------------}

-- A lookup operation

class HNat n => HLookupByHNat n l e | n l -> e
 where
  hLookupByHNat :: n -> l -> e

instance HLookupByHNat HZero (HCons e l) e
 where
  hLookupByHNat _ (HCons e _) = e

instance (HLookupByHNat n l e', HNat n)
      => HLookupByHNat (HSucc n) (HCons e l) e'
 where
  hLookupByHNat n (HCons _ l) = hLookupByHNat (hPred n) l


{-----------------------------------------------------------------------------}

-- A delete operation

class HNat n => HDeleteAtHNat n l l' | n l -> l'
 where
  hDeleteAtHNat :: n -> l -> l'

instance HDeleteAtHNat HZero (HCons e l) l
 where
  hDeleteAtHNat _ (HCons _ l) = l

instance (HDeleteAtHNat n l l', HNat n)
      => HDeleteAtHNat (HSucc n) (HCons e l) (HCons e l')
 where
  hDeleteAtHNat n (HCons e l) = HCons e (hDeleteAtHNat (hPred n) l)


{-----------------------------------------------------------------------------}

-- An update operation

class HNat n => HUpdateAtHNat n e l l' | n e l -> l', l' n -> e
 where
  hUpdateAtHNat :: n -> e -> l -> l'

instance HUpdateAtHNat HZero e' (HCons e l) (HCons e' l)
 where
  hUpdateAtHNat _ e' (HCons _ l) = HCons e' l

instance (HUpdateAtHNat n e' l l', HNat n)
      => HUpdateAtHNat (HSucc n) e' (HCons e l) (HCons e l')
 where
  hUpdateAtHNat n e' (HCons e l)
   = HCons e (hUpdateAtHNat (hPred n) e' l)


{-----------------------------------------------------------------------------}

-- Splitting an array according to indices
hSplitByHNats :: (HSplitByHNats' ns l' l'1 l'', HMap (HAddTag HTrue) l l') =>
                ns -> l -> (l'1, l'')
hSplitByHNats ns l = hSplitByHNats' ns (hFlag l)

class HNats ns => HSplitByHNats' ns l l' l'' | ns l -> l' l''
 where
  hSplitByHNats' :: ns -> l -> (l',l'')

instance HSplit l l' l''
      => HSplitByHNats' HNil l HNil l'
 where
  hSplitByHNats' HNil l = (HNil,l')
   where
    (l',_) = hSplit l

instance ( HLookupByHNat n l (e,b)
         , HUpdateAtHNat n (e,HFalse) l l'''
         , HSplitByHNats' ns l''' l' l''
         )
      =>   HSplitByHNats' (HCons n ns) l (HCons e l') l''
 where
  hSplitByHNats' (HCons n ns) l = (HCons e l',l'')
   where
    (e,_)    = hLookupByHNat  n l
    l'''     = hUpdateAtHNat  n (e,hFalse) l
    (l',l'') = hSplitByHNats' ns l'''


{-----------------------------------------------------------------------------}

-- Another projection operation

class HNats ns => HProjectByHNats ns l l' | ns l -> l'
 where
  hProjectByHNats :: ns -> l -> l'

instance HProjectByHNats HNil HNil HNil
 where
  hProjectByHNats _ _ = HNil

instance HProjectByHNats HNil (HCons e l) HNil
 where
  hProjectByHNats _ _ = HNil

instance ( HLookupByHNat n (HCons e l) e'
         , HProjectByHNats ns (HCons e l) l'
         )
         => HProjectByHNats (HCons n ns) (HCons e l) (HCons e' l')
 where
  hProjectByHNats (HCons n ns) l = HCons e' l'
   where e' = hLookupByHNat n l
         l' = hProjectByHNats ns l


{-----------------------------------------------------------------------------}

-- The complement of projection

class HProjectAwayByHNats ns l l' | ns l -> l'
 where
  hProjectAwayByHNats :: ns -> l -> l'

instance ( HLength l len
         , HBetween len nats
         , HDiff nats ns ns'
         , HProjectByHNats ns' l l'
         )
           => HProjectAwayByHNats ns l l'
 where
  hProjectAwayByHNats ns l = l'
   where
    len  = hLength l
    nats = hBetween len
    ns'  = hDiff nats ns
    l'   = hProjectByHNats ns' l


{-----------------------------------------------------------------------------}

-- Generate naturals from 1 to x - 1

class HBetween x y | x -> y
 where
  hBetween :: x -> y

instance HBetween (HSucc HZero) (HCons HZero HNil)
 where
  hBetween _ = HCons hZero HNil

instance ( HNat x
         , HBetween (HSucc x) y
         , HAppend y (HCons (HSucc x) HNil) z
         , HList y
         )
           => HBetween (HSucc (HSucc x)) z
 where
  hBetween x = hBetween (hPred x) `hAppend` HCons (hPred x) HNil


-- Set-difference on naturals

class HDiff x y z | x y -> z
 where
  hDiff :: x -> y -> z

instance HDiff HNil x HNil
 where
  hDiff _ _ = HNil

instance ( HOrdMember e y b
         , HDiff x y z
         , HCond b z (HCons e z) z'
         )
           => HDiff (HCons e x) y z'
 where
  hDiff (HCons e x) y = z'
   where z' = hCond b z (HCons e z)
         b  = hOrdMember e y
         z  = hDiff x y


-- Membership test for types with HOrd instances
-- This special type equality/comparison is entirely pure!

class HOrdMember e l b | e l -> b
 where
  hOrdMember :: e -> l -> b

instance HOrdMember e HNil HFalse
 where
  hOrdMember _ _ = hFalse

instance ( HEq e e' b1
         , HOrdMember e l b2
         , HOr b1 b2 b
         )
           => HOrdMember e (HCons e' l) b
 where
  hOrdMember e (HCons e' l) = hOr b1 b2
   where
    b1 = hEq e e'
    b2 = hOrdMember e l


{-----------------------------------------------------------------------------}

-- Length operation

class (HList l, HNat n) => HLength l n | l -> n
instance HLength HNil HZero
instance (HLength l n, HNat n, HList l)
      => HLength (HCons a l) (HSucc n)

hLength   :: HLength l n => l -> n
hLength _ =  undefined


{-----------------------------------------------------------------------------}

-- Bounded lists

class HMaxLength l s
instance (HLength l s', HLt s' (HSucc s) HTrue) => HMaxLength l s

class HMinLength l s
instance (HLength l s', HLt s (HSucc s') HTrue) => HMinLength l s

class HSingleton l
instance HLength l (HSucc HZero) => HSingleton l

hSingle :: (HSingleton l, HHead l e) => l -> e
hSingle = hHead


{-----------------------------------------------------------------------------}