module Generics.SOP.NP
  ( 
    NP(..)
  , POP(..)
  , unPOP
    
  , pure_NP
  , pure_POP
  , cpure_NP
  , cpure_POP
    
  , fromList
    
  , ap_NP
  , ap_POP
    
  , hd
  , tl
  , Projection
  , projections
  , shiftProjection
    
  , liftA_NP
  , liftA_POP
  , liftA2_NP
  , liftA2_POP
  , liftA3_NP
  , liftA3_POP
  , map_NP
  , map_POP
  , zipWith_NP
  , zipWith_POP
  , zipWith3_NP
  , zipWith3_POP
  , cliftA_NP
  , cliftA_POP
  , cliftA2_NP
  , cliftA2_POP
  , cliftA3_NP
  , cliftA3_POP
  , cmap_NP
  , cmap_POP
  , czipWith_NP
  , czipWith_POP
  , czipWith3_NP
  , czipWith3_POP
    
  , hcliftA'
  , hcliftA2'
  , hcliftA3'
  , cliftA2'_NP
    
  , collapse_NP
  , collapse_POP
    
  , sequence'_NP
  , sequence'_POP
  , sequence_NP
  , sequence_POP
    
  , cata_NP
  , ccata_NP
  , ana_NP
  , cana_NP
  ) where
#if !(MIN_VERSION_base(4,8,0))
import Control.Applicative
#endif
import Data.Proxy (Proxy(..))
import Control.DeepSeq (NFData(..))
import Generics.SOP.BasicFunctors
import Generics.SOP.Classes
import Generics.SOP.Constraint
import Generics.SOP.Sing
data NP :: (k -> *) -> [k] -> * where
  Nil  :: NP f '[]
  (:*) :: f x -> NP f xs -> NP f (x ': xs)
infixr 5 :*
deriving instance All (Show `Compose` f) xs => Show (NP f xs)
deriving instance All (Eq   `Compose` f) xs => Eq   (NP f xs)
deriving instance (All (Eq `Compose` f) xs, All (Ord `Compose` f) xs) => Ord (NP f xs)
instance All (NFData `Compose` f) xs => NFData (NP f xs) where
    rnf Nil       = ()
    rnf (x :* xs) = rnf x `seq` rnf xs
newtype POP (f :: (k -> *)) (xss :: [[k]]) = POP (NP (NP f) xss)
deriving instance (Show (NP (NP f) xss)) => Show (POP f xss)
deriving instance (Eq   (NP (NP f) xss)) => Eq   (POP f xss)
deriving instance (Ord  (NP (NP f) xss)) => Ord  (POP f xss)
instance (NFData (NP (NP f) xss)) => NFData (POP f xss) where
    rnf (POP xss) = rnf xss
unPOP :: POP f xss -> NP (NP f) xss
unPOP (POP xss) = xss
type instance AllN NP  c = All  c
type instance AllN POP c = All2 c
type instance SListIN NP  = SListI
type instance SListIN POP = SListI2
pure_NP :: forall f xs. SListI xs => (forall a. f a) -> NP f xs
pure_NP f = case sList :: SList xs of
  SNil   -> Nil
  SCons  -> f :* pure_NP f
pure_POP :: All SListI xss => (forall a. f a) -> POP f xss
pure_POP f = POP (cpure_NP sListP (pure_NP f))
sListP :: Proxy SListI
sListP = Proxy
cpure_NP :: forall c xs proxy f. All c xs
         => proxy c -> (forall a. c a => f a) -> NP f xs
cpure_NP p f = case sList :: SList xs of
  SNil   -> Nil
  SCons  -> f :* cpure_NP p f
cpure_POP :: forall c xss proxy f. All2 c xss
          => proxy c -> (forall a. c a => f a) -> POP f xss
cpure_POP p f = POP (cpure_NP (allP p) (cpure_NP p f))
allP :: proxy c -> Proxy (All c)
allP _ = Proxy
instance HPure NP where
  hpure  = pure_NP
  hcpure = cpure_NP
instance HPure POP where
  hpure  = pure_POP
  hcpure = cpure_POP
fromList :: SListI xs => [a] -> Maybe (NP (K a) xs)
fromList = go sList
  where
    go :: SList xs -> [a] -> Maybe (NP (K a) xs)
    go SNil  []     = return Nil
    go SCons (x:xs) = do ys <- go sList xs ; return (K x :* ys)
    go _     _      = Nothing
ap_NP :: NP (f -.-> g) xs -> NP f xs -> NP g xs
ap_NP Nil           Nil        = Nil
ap_NP (Fn f :* fs)  (x :* xs)  = f x :* ap_NP fs xs
#if __GLASGOW_HASKELL__ < 800
ap_NP _ _ = error "inaccessible"
#endif
ap_POP :: POP (f -.-> g) xss -> POP f xss -> POP g xss
ap_POP (POP fss') (POP xss') = POP (go fss' xss')
  where
    go :: NP (NP (f -.-> g)) xss -> NP (NP f) xss -> NP (NP g) xss
    go Nil         Nil         = Nil
    go (fs :* fss) (xs :* xss) = ap_NP fs xs :* go fss xss
#if __GLASGOW_HASKELL__ < 800
    go _           _           = error "inaccessible"
#endif
_ap_POP_spec :: SListI xss => POP (f -.-> g) xss -> POP  f xss -> POP  g xss
_ap_POP_spec (POP fs) (POP xs) = POP (liftA2_NP ap_NP fs xs)
type instance Prod NP  = NP
type instance Prod POP = POP
instance HAp NP  where hap = ap_NP
instance HAp POP where hap = ap_POP
hd :: NP f (x ': xs) -> f x
hd (x :* _xs) = x
tl :: NP f (x ': xs) -> NP f xs
tl (_x :* xs) = xs
type Projection (f :: k -> *) (xs :: [k]) = K (NP f xs) -.-> f
projections :: forall xs f . SListI xs => NP (Projection f xs) xs
projections = case sList :: SList xs of
  SNil  -> Nil
  SCons -> fn (hd . unK) :* liftA_NP shiftProjection projections
shiftProjection :: Projection f xs a -> Projection f (x ': xs) a
shiftProjection (Fn f) = Fn $ f . K . tl . unK
liftA_NP  :: SListI     xs  => (forall a. f a -> g a) -> NP  f xs  -> NP  g xs
liftA_POP :: All SListI xss => (forall a. f a -> g a) -> POP f xss -> POP g xss
liftA_NP  = hliftA
liftA_POP = hliftA
liftA2_NP  :: SListI     xs  => (forall a. f a -> g a -> h a) -> NP  f xs  -> NP  g xs  -> NP   h xs
liftA2_POP :: All SListI xss => (forall a. f a -> g a -> h a) -> POP f xss -> POP g xss -> POP  h xss
liftA2_NP  = hliftA2
liftA2_POP = hliftA2
liftA3_NP  :: SListI     xs  => (forall a. f a -> g a -> h a -> i a) -> NP  f xs  -> NP  g xs  -> NP  h xs  -> NP  i xs
liftA3_POP :: All SListI xss => (forall a. f a -> g a -> h a -> i a) -> POP f xss -> POP g xss -> POP h xss -> POP i xss
liftA3_NP  = hliftA3
liftA3_POP = hliftA3
map_NP  :: SListI     xs  => (forall a. f a -> g a) -> NP  f xs  -> NP  g xs
map_POP :: All SListI xss => (forall a. f a -> g a) -> POP f xss -> POP g xss
map_NP  = hmap
map_POP = hmap
zipWith_NP  :: SListI     xs  => (forall a. f a -> g a -> h a) -> NP  f xs  -> NP  g xs  -> NP   h xs
zipWith_POP :: All SListI xss => (forall a. f a -> g a -> h a) -> POP f xss -> POP g xss -> POP  h xss
zipWith_NP  = hzipWith
zipWith_POP = hzipWith
zipWith3_NP  :: SListI     xs  => (forall a. f a -> g a -> h a -> i a) -> NP  f xs  -> NP  g xs  -> NP  h xs  -> NP  i xs
zipWith3_POP :: All SListI xss => (forall a. f a -> g a -> h a -> i a) -> POP f xss -> POP g xss -> POP h xss -> POP i xss
zipWith3_NP  = hzipWith3
zipWith3_POP = hzipWith3
cliftA_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a) -> NP   f xs  -> NP  g xs
cliftA_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a) -> POP  f xss -> POP g xss
cliftA_NP  = hcliftA
cliftA_POP = hcliftA
cliftA2_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a -> h a) -> NP  f xs  -> NP  g xs  -> NP  h xs
cliftA2_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a -> h a) -> POP f xss -> POP g xss -> POP h xss
cliftA2_NP  = hcliftA2
cliftA2_POP = hcliftA2
cliftA3_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a -> h a -> i a) -> NP  f xs  -> NP  g xs  -> NP  h xs  -> NP  i xs
cliftA3_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a -> h a -> i a) -> POP f xss -> POP g xss -> POP h xss -> POP i xss
cliftA3_NP  = hcliftA3
cliftA3_POP = hcliftA3
cmap_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a) -> NP   f xs  -> NP  g xs
cmap_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a) -> POP  f xss -> POP g xss
cmap_NP  = hcmap
cmap_POP = hcmap
czipWith_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a -> h a) -> NP  f xs  -> NP  g xs  -> NP  h xs
czipWith_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a -> h a) -> POP f xss -> POP g xss -> POP h xss
czipWith_NP  = hczipWith
czipWith_POP = hczipWith
czipWith3_NP  :: All  c xs  => proxy c -> (forall a. c a => f a -> g a -> h a -> i a) -> NP  f xs  -> NP  g xs  -> NP  h xs  -> NP  i xs
czipWith3_POP :: All2 c xss => proxy c -> (forall a. c a => f a -> g a -> h a -> i a) -> POP f xss -> POP g xss -> POP h xss -> POP i xss
czipWith3_NP  = hczipWith3
czipWith3_POP = hczipWith3
hcliftA'  :: (All2 c xss, Prod h ~ NP, HAp h) => proxy c -> (forall xs. All c xs => f xs -> f' xs)                                                       -> h f   xss -> h f'   xss
hcliftA2' :: (All2 c xss, Prod h ~ NP, HAp h) => proxy c -> (forall xs. All c xs => f xs -> f' xs -> f'' xs)            -> Prod h f xss                  -> h f'  xss -> h f''  xss
hcliftA3' :: (All2 c xss, Prod h ~ NP, HAp h) => proxy c -> (forall xs. All c xs => f xs -> f' xs -> f'' xs -> f''' xs) -> Prod h f xss -> Prod h f' xss -> h f'' xss -> h f''' xss
hcliftA'  p = hcliftA  (allP p)
hcliftA2' p = hcliftA2 (allP p)
hcliftA3' p = hcliftA3 (allP p)
cliftA2'_NP :: All2 c xss => proxy c -> (forall xs. All c xs => f xs -> g xs -> h xs) -> NP f xss -> NP g xss -> NP h xss
cliftA2'_NP = hcliftA2'
collapse_NP  ::              NP  (K a) xs  ->  [a]
collapse_POP :: SListI xss => POP (K a) xss -> [[a]]
collapse_NP Nil         = []
collapse_NP (K x :* xs) = x : collapse_NP xs
collapse_POP = collapse_NP . hliftA (K . collapse_NP) . unPOP
type instance CollapseTo NP  a = [a]
type instance CollapseTo POP a = [[a]]
instance HCollapse NP  where hcollapse = collapse_NP
instance HCollapse POP where hcollapse = collapse_POP
sequence'_NP  ::             Applicative f  => NP  (f :.: g) xs  -> f (NP  g xs)
sequence'_POP :: (SListI xss, Applicative f) => POP (f :.: g) xss -> f (POP g xss)
sequence'_NP Nil         = pure Nil
sequence'_NP (mx :* mxs) = (:*) <$> unComp mx <*> sequence'_NP mxs
sequence'_POP = fmap POP . sequence'_NP . hliftA (Comp . sequence'_NP) . unPOP
instance HSequence NP  where hsequence' = sequence'_NP
instance HSequence POP where hsequence' = sequence'_POP
sequence_NP  :: (SListI xs,  Applicative f) => NP  f xs  -> f (NP  I xs)
sequence_POP :: (All SListI xss, Applicative f) => POP f xss -> f (POP I xss)
sequence_NP   = hsequence
sequence_POP  = hsequence
cata_NP ::
     forall r f xs .
     r '[]
  -> (forall y ys . f y -> r ys -> r (y ': ys))
  -> NP f xs
  -> r xs
cata_NP nil cons = go
  where
    go :: forall ys . NP f ys -> r ys
    go Nil       = nil
    go (x :* xs) = cons x (go xs)
ccata_NP ::
     forall c proxy r f xs . (All c xs)
  => proxy c
  -> r '[]
  -> (forall y ys . c y => f y -> r ys -> r (y ': ys))
  -> NP f xs
  -> r xs
ccata_NP _ nil cons = go
  where
    go :: forall ys . (All c ys) => NP f ys -> r ys
    go Nil       = nil
    go (x :* xs) = cons x (go xs)
ana_NP ::
     forall s f xs .
     SListI xs
  => (forall y ys . s (y ': ys) -> (f y, s ys))
  -> s xs
  -> NP f xs
ana_NP uncons = go sList
  where
    go :: forall ys . SList ys -> s ys -> NP f ys
    go SNil  _ = Nil
    go SCons s = case uncons s of
      (x, s') -> x :* go sList s'
cana_NP ::
     forall c proxy s f xs . (All c xs)
  => proxy c
  -> (forall y ys . c y => s (y ': ys) -> (f y, s ys))
  -> s xs
  -> NP f xs
cana_NP _ uncons = go sList
  where
    go :: forall ys . (All c ys) => SList ys -> s ys -> NP f ys
    go SNil  _ = Nil
    go SCons s = case uncons s of
      (x, s') -> x :* go sList s'