{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE CPP #-}

#include "fusion-phases.h"

module Data.Array.Parallel.Lifted.Repr (
  PArray(..),
  Void, void,
  Wrap(..),
  Enumeration(..),
  Sum2(..), Sum3(..), 

  dPA_Void,
  dPR_Void, dPR_Unit, dPR_Wrap,
  dPR_Enumeration,
  dPR_2, dPR_3, dPR_4, dPR_5, zipPA#, unzipPA#, zip3PA#,
  dPR_Sum2, dPR_Sum3,

  dPR_PArray, nested_lengthPA, concatPA#,
) where

import Data.Array.Parallel.Lifted.PArray
import Data.Array.Parallel.Lifted.Unboxed

import Data.Array.Parallel.Base ((:*:)(..), fromBool)

import qualified Data.List as L
import GHC.Exts  (Int#, Int(..), (+#), (-#), (*#))
import Debug.Trace



data Void

traceFn   = flip const -- trace
traceArgs = flip  const -- trace 

void :: Void
void = error "Data.Array.Parallel.void"

data instance PArray Void = PVoid Int#

dPR_Void :: PR Void
{-# INLINE dPR_Void #-}
dPR_Void = PR {
             lengthPR     = lengthPR_Void
           , emptyPR      = emptyPR_Void
           , replicatePR  = replicatePR_Void
           , replicatelPR = replicatelPR_Void
           , repeatPR     = repeatPR_Void
           , indexPR      = indexPR_Void
           , bpermutePR   = bpermutePR_Void
           , appPR        = appPR_Void
           , applPR       = applPR_Void
           , packPR       = packPR_Void
           , combine2PR   = combine2PR_Void
           , fromListPR   = fromListPR_Void
           , nfPR         = nfPR_Void
           }

{-# INLINE lengthPR_Void #-}
lengthPR_Void (PVoid n#) = 
              n#

{-# INLINE emptyPR_Void #-}
emptyPR_Void = traceFn "emptyPR_Void" $
             PVoid 0#

{-# INLINE replicatePR_Void #-}
replicatePR_Void n# _ = traceFn "replicatePR_Void" $
                 PVoid n#

{-# INLINE replicatelPR_Void #-}
replicatelPR_Void n# _ _ = traceFn "replicatelPR_Void" $
                  PVoid n#

{-# INLINE repeatPR_Void #-}
repeatPR_Void n# (PVoid m#) = traceFn "repeatPR_Void" $
              PVoid (n# *# m#)

indexPR_Void :: PArray Void -> Int# -> Void
{-# INLINE indexPR_Void #-}
indexPR_Void (PVoid n#) i# = traceFn "indexPR_Void" $
             void

{-# INLINE bpermutePR_Void #-}
bpermutePR_Void (PVoid _) is = traceFn "bpermutePR_Void" $
                PVoid (lengthPA_Int# is)

{-# INLINE appPR_Void #-}
appPR_Void (PVoid m#) (PVoid n#) = traceFn "appPR_Void" $
           PVoid (m# +# n#)

{-# INLINE applPR_Void #-}
applPR_Void _ (PVoid m#) _ (PVoid n#) = traceFn "applPR_Void" $
            PVoid (m# +# n#)

{-# INLINE packPR_Void #-}
packPR_Void (PVoid _) n# _ = traceFn "packPR_Void" $
            PVoid n#

{-# INLINE combine2PR_Void #-}
combine2PR_Void n# _ _ (PVoid _) (PVoid _) = traceFn "combine2PR_Void" $
                PVoid n#

{-# INLINE fromListPR_Void #-}
fromListPR_Void n# _ = PVoid n#

{-# INLINE nfPR_Void #-}
nfPR_Void (PVoid _) = ()

type instance PRepr Void = Void

dPA_Void :: PA Void
{-# INLINE_PA dPA_Void #-}
dPA_Void = PA {
             toPRepr      = id
           , fromPRepr    = id
           , toArrPRepr   = id
           , fromArrPRepr = id
           , dictPRepr    = dPR_Void
           }

data instance PArray () = PUnit Int# ()

dPR_Unit :: PR ()
{-# INLINE dPR_Unit #-}
dPR_Unit = PR {
             lengthPR     = lengthPR_Unit
           , emptyPR      = emptyPR_Unit
           , replicatePR  = replicatePR_Unit
           , replicatelPR = replicatelPR_Unit
           , repeatPR     = repeatPR_Unit
           , indexPR      = indexPR_Unit
           , bpermutePR   = bpermutePR_Unit
           , appPR        = appPR_Unit
           , applPR       = applPR_Unit
           , packPR       = packPR_Unit
           , combine2PR   = combine2PR_Unit
           , fromListPR   = fromListPR_Unit
           , nfPR         = nfPR_Unit
           }
         

{-# INLINE lengthPR_Unit #-}
lengthPR_Unit (PUnit n# _) = 
              n#

{-# INLINE emptyPR_Unit #-}
emptyPR_Unit = traceFn "emptyPR_Unit" $
             PUnit 0# ()

{-# INLINE replicatePR_Unit #-}
replicatePR_Unit n# u = 
  traceFn "replicatePR_Unit" $
  traceArgs ("replicatePR_Unit len = " ++ show (I# n#)) $
                 PUnit n# u

{-# INLINE replicatelPR_Unit #-}
replicatelPR_Unit n# _ (PUnit _ u) = 
  traceFn "replicatelPR_Unit" 
  traceArgs ("replicatelPR_Unit args len = " ++ (show (I# n#)))$
  PUnit n# u

{-# INLINE repeatPR_Unit #-}
repeatPR_Unit n# (PUnit m# u) = traceFn "repeatPR_Unit" $
              PUnit (n# *# m#) u

indexPR_Unit :: PArray () -> Int# -> ()
{-# INLINE indexPR_Unit #-}
indexPR_Unit (PUnit n# u) i# = traceFn "indexPR_Unit" $
             u

{-# INLINE bpermutePR_Unit #-}
bpermutePR_Unit (PUnit _ u) is = traceFn "bpermutePR_Unit" $
                PUnit (lengthPA_Int# is) u

{-# INLINE appPR_Unit #-}
appPR_Unit (PUnit m# u) (PUnit n# v) = traceFn "appPR_Unit" $
           PUnit (m# +# n#) (u `seq` v)

{-# INLINE applPR_Unit #-}
applPR_Unit _ (PUnit m# u) _ (PUnit n# v) = traceFn "applPR_Unit" $
            PUnit (m# +# n#) (u `seq` v)

{-# INLINE packPR_Unit #-}
packPR_Unit (PUnit _ u) n# _ = traceFn "packPR_Unit" $
            PUnit n# u

{-# INLINE combine2PR_Unit #-}
combine2PR_Unit n# _ _ (PUnit _ u1) (PUnit _ u2) = traceFn "combine2PR_Unit" $
  PUnit n# (u1 `seq` u2)

{-# INLINE fromListPR_Unit #-}
fromListPR_Unit n# xs = PUnit n# (foldr seq () xs)

{-# INLINE nfPR_Unit #-}
nfPR_Unit (PUnit _ u) = u

data Wrap a = Wrap a

data instance PArray (Wrap a) = PWrap Int# (PArray a)

dPR_Wrap :: PR a -> PR (Wrap a)
{-# INLINE dPR_Wrap #-}
dPR_Wrap pr = PR {
              lengthPR     = lengthPR_Wrap
            , emptyPR      = emptyPR_Wrap pr
            , replicatePR  = replicatePR_Wrap pr
            , replicatelPR = replicatelPR_Wrap pr
            , repeatPR     = repeatPR_Wrap pr
            , indexPR      = indexPR_Wrap pr
            , bpermutePR   = bpermutePR_Wrap pr
            , appPR        = appPR_Wrap pr
            , applPR       = applPR_Wrap pr
            , packPR       = packPR_Wrap pr
            , combine2PR   = combine2PR_Wrap pr
            }

{-# INLINE lengthPR_Wrap #-}
lengthPR_Wrap (PWrap n# _) = 
              n#

{-# INLINE emptyPR_Wrap #-}
emptyPR_Wrap pr = traceFn "emptyPR_Wrap" $
             PWrap 0# (emptyPR pr)

{-# INLINE replicatePR_Wrap #-}
replicatePR_Wrap pr n# ~(Wrap x) = traceFn "replicatePR_Wrap" $
                 PWrap n# (replicatePR pr n# x)

{-# INLINE replicatelPR_Wrap #-}
replicatelPR_Wrap pr n# ns (PWrap _ xs) = traceFn "replicatelPR_Wrap" $
                  PWrap n# (replicatelPR pr n# ns xs)

{-# INLINE repeatPR_Wrap #-}
repeatPR_Wrap pr n# (PWrap m# xs) = traceFn "repeatPR_Wrap" $
              PWrap (n# *# m#) (repeatPR pr n# xs)

{-# INLINE indexPR_Wrap #-}
indexPR_Wrap pr (PWrap n# xs) i# = traceFn "indexPR_Wrap" $
             Wrap (indexPR pr xs i#)

{-# INLINE bpermutePR_Wrap #-}
bpermutePR_Wrap pr (PWrap n# xs) is = traceFn "bpermutePR_Wrap" $
                PWrap (lengthPA_Int# is) (bpermutePR pr xs is)

{-# INLINE appPR_Wrap #-}
appPR_Wrap pr (PWrap m# xs) (PWrap n# ys) = traceFn "appPR_Wrap" $
           PWrap (m# +# n#) (appPR pr xs ys)

{-# INLINE applPR_Wrap #-}
applPR_Wrap pr is (PWrap m# xs) js (PWrap n# ys) = traceFn "applPR_Wrap" $
            PWrap (m# +# n#) (applPR pr is xs js ys)

{-# INLINE packPR_Wrap #-}
packPR_Wrap pr (PWrap _ xs) n# sel# = traceFn "packPR_Wrap" $
            PWrap n# (packPR pr xs n# sel#)

combine2PR_Wrap:: PR a -> Int# -> PArray_Int# -> PArray_Int#
                              -> PArray (Wrap a) -> PArray (Wrap a) -> PArray (Wrap a)
combine2PR_Wrap _ _ _ _ _ = traceFn "combine2PR_Wrap" $
                error "combine2PR_Wrap nyi"

data Enumeration = Enumeration Int#

data instance PArray Enumeration = PEnum Int# PArray_Int# PArray_Int#

dPR_Enumeration :: PR Enumeration
{-# INLINE dPR_Enumeration #-}
dPR_Enumeration = PR {
                    lengthPR    = lengthPR_Enumeration
                  , emptyPR     = emptyPR_Enumeration
                  , replicatePR = replicatePR_Enumeration
                  }

{-# INLINE lengthPR_Enumeration #-}
lengthPR_Enumeration (PEnum n# _ _) = n#

{-# INLINE emptyPR_Enumeration #-}
emptyPR_Enumeration = traceFn "emptyPR_Enumeration" $
                    PEnum 0# emptyPA_Int# emptyPA_Int#

{-# INLINE replicatePR_Enumeration #-}
replicatePR_Enumeration n# enum
  = traceFn "replicatePR_Enumeration" $
      PEnum n# (replicatePA_Int# n# (case enum of { Enumeration i# -> i# }))
             (upToPA_Int# n#)

data instance PArray (a,b)
  = P_2 Int# (PArray a)
             (PArray b)

data instance PArray (a,b,c)
  = P_3 Int# (PArray a)
             (PArray b)
             (PArray c)

data instance PArray (a,b,c,d)
  = P_4 Int# (PArray a)
             (PArray b)
             (PArray c)
             (PArray d)

data instance PArray (a,b,c,d,e)
  = P_5 Int# (PArray a)
             (PArray b)
             (PArray c)
             (PArray d)
             (PArray e)

dPR_2 :: PR a -> PR b -> PR (a,b)
{-# INLINE dPR_2 #-}
dPR_2 pra prb
  = PR {
      lengthPR     = lengthPR_2
    , emptyPR      = emptyPR_2 pra prb
    , replicatePR  = replicatePR_2 pra prb
    , replicatelPR = replicatelPR_2 pra prb
    , repeatPR     = repeatPR_2 pra prb
    , indexPR      = indexPR_2 pra prb
    , bpermutePR   = bpermutePR_2 pra prb
    , appPR        = appPR_2 pra prb
    , applPR       = applPR_2 pra prb
    , packPR       = packPR_2 pra prb
    , combine2PR   = combine2PR_2 pra prb
    , fromListPR   = fromListPR_2 pra prb
    , nfPR         = nfPR_2 pra prb
    }

{-# INLINE lengthPR_2 #-}
lengthPR_2 (P_2 n# _ _) = n#

{-# INLINE emptyPR_2 #-}
emptyPR_2 pra prb = traceFn "emptyPR_2" $
          P_2 0# (emptyPR pra) (emptyPR prb)

{-# INLINE replicatePR_2 #-}
replicatePR_2 pra prb n# ~(a,b) = 
  traceFn "replicatePR_2" $
  traceArgs ("replicatePR_2 args len = "  ++ (show (I# n#))) $
  P_2 n# (replicatePR pra n# a)
           (replicatePR prb n# b)

{-# INLINE replicatelPR_2 #-}
replicatelPR_2 pra prb n# ns (P_2 _ as bs)
  = traceFn "replicatelPR_2" $
  P_2 n# (replicatelPR pra n# ns as)
           (replicatelPR prb n# ns bs) 

{-# INLINE repeatPR_2 #-}
repeatPR_2 pra prb n# (P_2 m# as bs)
  = traceFn "repeatPR_2" $
  P_2 (n# *# m#) (repeatPR pra n# as)
                   (repeatPR prb n# bs)

{-# INLINE indexPR_2 #-}
indexPR_2 pra prb (P_2 _ as bs) i# = traceFn "indexPR_2" $
          (indexPR pra as i#, indexPR prb bs i#)

{-# INLINE bpermutePR_2 #-}
bpermutePR_2 pra prb (P_2 _ as bs) is
  = traceFn "bpermutePR_2" $
  P_2 (lengthPA_Int# is) (bpermutePR pra as is)
                           (bpermutePR prb bs is)

{-# INLINE appPR_2 #-}
appPR_2 pra prb (P_2 m# as1 bs1) (P_2 n# as2 bs2)
  = P_2 (m# +# n#) (appPR pra as1 as2) (appPR prb bs1 bs2)

{-# INLINE applPR_2 #-}
applPR_2 pra prb is (P_2 m# as1 bs1) js (P_2 n# as2 bs2)
  = traceFn "applPR_2" $
  P_2 (m# +# n#) (applPR pra is as1 js as2)
                   (applPR prb is bs1 js bs2)

{-# INLINE packPR_2 #-}
packPR_2 pra prb (P_2 _ as bs) n# sel# = traceFn "packPR_2" $
         P_2 n# (packPR pra as n# sel#)
                                                (packPR prb bs n# sel#)

{-# INLINE combine2PR_2 #-}
combine2PR_2 pra prb n# sel# is# (P_2 _ as1 bs1) (P_2 _ as2 bs2)
  = traceFn "combine2PR_2" $
       P_2 n# (combine2PR pra n# sel# is# as1 as2)
              (combine2PR prb n# sel# is# bs1 bs2)

{-# INLINE fromListPR_2 #-}
fromListPR_2 pra prb n# xs
  = P_2 n# (fromListPR pra n# as)
           (fromListPR prb n# bs)
  where
    (as,bs) = unzip xs

{-# INLINE nfPR_2 #-}
nfPR_2 pra prb (P_2 _ as bs)
  = nfPR pra as `seq` nfPR prb bs

zipPA# :: PA a -> PA b -> PArray a -> PArray b -> PArray (a,b)
{-# INLINE_PA zipPA# #-}
zipPA# pa pb xs ys = 
  traceFn "zipPA" $ 
  traceArgs  ("zipPA args len1:" ++ show (I# (lengthPA# pa xs)) ++
               "\nlen2:" ++ show (I# (lengthPA# pb ys))
    ) $
       P_2 (lengthPA# pa xs) xs ys

unzipPA# :: PA a -> PA b  -> PArray (a,b) -> (PArray a,  PArray b)
{-# INLINE_PA unzipPA# #-}
unzipPA# pa pb (P_2 n xs ys)  = traceFn "unzipPA" $
         (xs, ys)

dPR_3 :: PR a -> PR b -> PR c -> PR (a,b,c)
{-# INLINE dPR_3 #-}
dPR_3 pra prb prc
  = PR {
      lengthPR     = lengthPR_3
    , emptyPR      = emptyPR_3 pra prb prc
    , replicatePR  = replicatePR_3 pra prb prc
    , replicatelPR = replicatelPR_3 pra prb prc
    , repeatPR     = repeatPR_3 pra prb prc
    , indexPR      = indexPR_3 pra prb prc
    , bpermutePR   = bpermutePR_3 pra prb prc
    , appPR        = appPR_3 pra prb prc
    , applPR       = applPR_3 pra prb prc
    , packPR       = packPR_3 pra prb prc
    , combine2PR   = combine2PR_3 pra prb prc
    , fromListPR   = fromListPR_3 pra prb prc
    , nfPR         = nfPR_3 pra prb prc
    }

{-# INLINE lengthPR_3 #-}
lengthPR_3 (P_3 n# _ _ _) = n#

{-# INLINE emptyPR_3 #-}
emptyPR_3 pra prb prc = traceFn "emptyPR_3" $
          P_3 0# (emptyPR pra) (emptyPR prb) (emptyPR prc)

{-# INLINE replicatePR_3 #-}
replicatePR_3 pra prb prc n# ~(a,b,c)
  = traceFn "replicatePR_3" $
  P_3 n# (replicatePR pra n# a)
           (replicatePR prb n# b)
           (replicatePR prc n# c)

{-# INLINE replicatelPR_3 #-}
replicatelPR_3 pra prb prc n# ns (P_3 _ as bs cs)
  = traceFn "replicatelPR_3" $
  P_3 n# (replicatelPR pra n# ns as)
           (replicatelPR prb n# ns bs)
           (replicatelPR prc n# ns cs)

{-# INLINE repeatPR_3 #-}
repeatPR_3 pra prb prc n# (P_3 m# as bs cs)
  = traceFn "repeatPR_3" $
  P_3 (n# *# m#) (repeatPR pra n# as)
                   (repeatPR prb n# bs)
                   (repeatPR prc n# cs)

{-# INLINE indexPR_3 #-}
indexPR_3 pra prb prc (P_3 n# as bs cs) i#
  = traceFn "indexPR_3" $
  (indexPR pra as i#, indexPR prb bs i#, indexPR prc cs i#)

{-# INLINE bpermutePR_3 #-}
bpermutePR_3 pra prb prc (P_3 _ as bs cs) is
  = traceFn "bpermutePR_3" $
  P_3 (lengthPA_Int# is) (bpermutePR pra as is)
                           (bpermutePR prb bs is)
                           (bpermutePR prc cs is)
{-# INLINE appPR_3 #-}
appPR_3 pra prb prc (P_3 m# as1 bs1 cs1) (P_3 n# as2 bs2 cs2)
  = traceFn "appPR_3" $
  P_3 (m# +# n#) (appPR pra as1 as2) (appPR prb bs1 bs2) (appPR prc cs1 cs2)

{-# INLINE applPR_3 #-}
applPR_3 pra prb prc is (P_3 m# as1 bs1 cs1) js (P_3 n# as2 bs2 cs2)
  = traceFn "applPR_3" $
  P_3 (m# +# n#) (applPR pra is as1 js as2)
                   (applPR prb is bs1 js bs2)
                   (applPR prc is cs1 js cs2)

{-# INLINE packPR_3 #-}
packPR_3 pra prb prc (P_3 _ as bs cs) n# sel#
  = traceFn "packPR_3" $
  P_3 n# (packPR pra as n# sel#)
           (packPR prb bs n# sel#)
           (packPR prc cs n# sel#)

{-# INLINE combine2PR_3 #-}
combine2PR_3 pra prb prc n# sel# is# (P_3 _ as1 bs1 cs1)
                                     (P_3 _ as2 bs2 cs2)
  = traceFn "combine2PR_3" $
  P_3 n# (combine2PR pra n# sel# is# as1 as2)
           (combine2PR prb n# sel# is# bs1 bs2)
           (combine2PR prc n# sel# is# cs1 cs2)

{-# INLINE fromListPR_3 #-}
fromListPR_3 pra prb prc n# xs
  = P_3 n# (fromListPR pra n# as)
           (fromListPR prb n# bs)
           (fromListPR prc n# cs)
  where
    (as,bs,cs) = unzip3 xs

{-# INLINE nfPR_3 #-}
nfPR_3 pra prb prc (P_3 _ as bs cs)
  = nfPR pra as
    `seq` nfPR prb bs
    `seq` nfPR prc cs

zip3PA# :: PA a -> PA b -> PA c
        -> PArray a -> PArray b -> PArray c -> PArray (a,b,c)
{-# INLINE_PA zip3PA# #-}
zip3PA# pa pb pc xs ys zs = traceFn "zip3PA" $
        P_3 (lengthPA# pa xs) xs ys zs

dPR_4 :: PR a -> PR b -> PR c -> PR d -> PR (a,b,c,d)
{-# INLINE dPR_4 #-}
dPR_4 pra prb prc prd
  = PR {
      lengthPR     = lengthPR_4
    , emptyPR      = emptyPR_4 pra prb prc prd
    , replicatePR  = replicatePR_4 pra prb prc prd
    , replicatelPR = replicatelPR_4 pra prb prc prd
    , repeatPR     = repeatPR_4 pra prb prc prd
    , indexPR      = indexPR_4 pra prb prc prd
    , bpermutePR   = bpermutePR_4 pra prb prc prd
    , appPR        = appPR_4 pra prb prc prd
    , applPR       = applPR_4 pra prb prc prd
    , packPR       = packPR_4 pra prb prc prd
    , combine2PR   = combine2PR_4 pra prb prc prd
    , fromListPR   = fromListPR_4 pra prb prc prd
    , nfPR         = nfPR_4 pra prb prc prd
    }

{-# INLINE lengthPR_4 #-}
lengthPR_4 (P_4 n# _ _ _ _) = n#

{-# INLINE emptyPR_4 #-}
emptyPR_4 pra prb prc prd = traceFn "emptyPR_4" $
          P_4 0# (emptyPR pra)
                                   (emptyPR prb)
                                   (emptyPR prc)
                                   (emptyPR prd)

{-# INLINE replicatePR_4 #-}
replicatePR_4 pra prb prc prd n# ~(a,b,c,d)
  = traceFn "replicatePR_4" $
  P_4 n# (replicatePR pra n# a)
           (replicatePR prb n# b)
           (replicatePR prc n# c)
           (replicatePR prd n# d)

{-# INLINE replicatelPR_4 #-}
replicatelPR_4 pra prb prc prd n# ns (P_4 _ as bs cs ds)
  = traceFn "replicatelPR_4" $
  P_4 n# (replicatelPR pra n# ns as)
           (replicatelPR prb n# ns bs)
           (replicatelPR prc n# ns cs)
           (replicatelPR prd n# ns ds)

{-# INLINE repeatPR_4 #-}
repeatPR_4 pra prb prc prd n# (P_4 m# as bs cs ds)
  = traceFn "repeatPR_4" $
  P_4 (n# *# m#) (repeatPR pra n# as)
                   (repeatPR prb n# bs)
                   (repeatPR prc n# cs)
                   (repeatPR prd n# ds)

{-# INLINE indexPR_4 #-}
indexPR_4 pra prb prc prd (P_4 n# as bs cs ds) i#
  = traceFn "indexPR_4" $
  (indexPR pra as i#,
     indexPR prb bs i#,
     indexPR prc cs i#,
     indexPR prd ds i#)

{-# INLINE bpermutePR_4 #-}
bpermutePR_4 pra prb prc prd (P_4 _ as bs cs ds) is
  = traceFn "bpermutePR_4" $
  P_4 (lengthPA_Int# is) (bpermutePR pra as is)
                           (bpermutePR prb bs is)
                           (bpermutePR prc cs is)
                           (bpermutePR prd ds is)

{-# INLINE appPR_4 #-}
appPR_4 pra prb prc prd (P_4 m# as1 bs1 cs1 ds1) (P_4 n# as2 bs2 cs2 ds2)
  = traceFn "appPR_4" $
  P_4 (m# +# n#) (appPR pra as1 as2)
                   (appPR prb bs1 bs2)
                   (appPR prc cs1 cs2)
                   (appPR prd ds1 ds2)

{-# INLINE applPR_4 #-}
applPR_4 pra prb prc prd is (P_4 m# as1 bs1 cs1 ds1) js (P_4 n# as2 bs2 cs2 ds2)
  = traceFn "applPR_4" $
  P_4 (m# +# n#) (applPR pra is as1 js as2)
                   (applPR prb is bs1 js bs2)
                   (applPR prc is cs1 js cs2)
                   (applPR prd is ds1 js ds2)

{-# INLINE packPR_4 #-}
packPR_4 pra prb prc prd (P_4 _ as bs cs ds) n# sel#
  = traceFn "packPR_4" $
  P_4 n# (packPR pra as n# sel#)
           (packPR prb bs n# sel#)
           (packPR prc cs n# sel#)
           (packPR prd ds n# sel#)

{-# INLINE combine2PR_4 #-}
combine2PR_4 pra prb prc prd n# sel# is# (P_4 _ as1 bs1 cs1 ds1)
                                         (P_4 _ as2 bs2 cs2 ds2)
  = traceFn "combine2PR_4" $
  P_4 n# (combine2PR pra n# sel# is# as1 as2)
           (combine2PR prb n# sel# is# bs1 bs2)
           (combine2PR prc n# sel# is# cs1 cs2)
           (combine2PR prd n# sel# is# ds1 ds2)

{-# INLINE fromListPR_4 #-}
fromListPR_4 pra prb prc prd n# xs
  = P_4 n# (fromListPR pra n# as)
           (fromListPR prb n# bs)
           (fromListPR prc n# cs)
           (fromListPR prd n# ds)
  where
    (as,bs,cs,ds) = L.unzip4 xs

{-# INLINE nfPR_4 #-}
nfPR_4 pra prb prc prd (P_4 _ as bs cs ds)
  = nfPR pra as
    `seq` nfPR prb bs
    `seq` nfPR prc cs
    `seq` nfPR prd ds

dPR_5 :: PR a -> PR b -> PR c -> PR d -> PR e -> PR (a,b,c,d,e)
{-# INLINE dPR_5 #-}
dPR_5 pra prb prc prd pre
  = PR {
      lengthPR     = lengthPR_5
    , emptyPR      = emptyPR_5 pra prb prc prd pre
    , replicatePR  = replicatePR_5 pra prb prc prd pre
    , replicatelPR = replicatelPR_5 pra prb prc prd pre
    , repeatPR     = repeatPR_5 pra prb prc prd pre
    , indexPR      = indexPR_5 pra prb prc prd pre
    , bpermutePR   = bpermutePR_5 pra prb prc prd pre
    , appPR        = appPR_5 pra prb prc prd pre
    , applPR       = applPR_5 pra prb prc prd pre
    , packPR       = packPR_5 pra prb prc prd pre
    , combine2PR   = combine2PR_5 pra prb prc prd pre
    , fromListPR   = fromListPR_5 pra prb prc prd pre
    , nfPR         = nfPR_5 pra prb prc prd pre
    }

{-# INLINE lengthPR_5 #-}
lengthPR_5 (P_5 n# _ _ _ _ _) = n#

{-# INLINE emptyPR_5 #-}
emptyPR_5 pra prb prc prd pre
  = traceFn "emptyPR_5" $
  P_5 0# (emptyPR pra)
           (emptyPR prb)
           (emptyPR prc)
           (emptyPR prd)
           (emptyPR pre)

{-# INLINE replicatePR_5 #-}
replicatePR_5 pra prb prc prd pre n# ~(a,b,c,d,e)
  = traceFn "replicatePR_5" $
  P_5 n# (replicatePR pra n# a)
           (replicatePR prb n# b)
           (replicatePR prc n# c)
           (replicatePR prd n# d)
           (replicatePR pre n# e)

{-# INLINE replicatelPR_5 #-}
replicatelPR_5 pra prb prc prd pre n# ns (P_5 _ as bs cs ds es)
  = traceFn "replicatelPR_5" $
  P_5 n# (replicatelPR pra n# ns as)
           (replicatelPR prb n# ns bs)
           (replicatelPR prc n# ns cs)
           (replicatelPR prd n# ns ds)
           (replicatelPR pre n# ns es)

{-# INLINE repeatPR_5 #-}
repeatPR_5 pra prb prc prd pre n# (P_5 m# as bs cs ds es)
  = traceFn "repeatPR_5" $
  P_5 (n# *# m#) (repeatPR pra n# as)
                   (repeatPR prb n# bs)
                   (repeatPR prc n# cs)
                   (repeatPR prd n# ds)
                   (repeatPR pre n# es)

{-# INLINE indexPR_5 #-}
indexPR_5 pra prb prc prd pre (P_5 n# as bs cs ds es) i#
  = traceFn "indexPR_5" $
  (indexPR pra as i#,
     indexPR prb bs i#,
     indexPR prc cs i#,
     indexPR prd ds i#,
     indexPR pre es i#)

{-# INLINE bpermutePR_5 #-}
bpermutePR_5 pra prb prc prd pre (P_5 _ as bs cs ds es) is
  = traceFn "bpermutePR_5" $
  P_5 (lengthPA_Int# is) (bpermutePR pra as is)
                           (bpermutePR prb bs is)
                           (bpermutePR prc cs is)
                           (bpermutePR prd ds is)
                           (bpermutePR pre es is)

{-# INLINE appPR_5 #-}
appPR_5 pra prb prc prd pre (P_5 m# as1 bs1 cs1 ds1 es1)
                            (P_5 n# as2 bs2 cs2 ds2 es2)
  = traceFn "appPR_5" $
  P_5 (m# +# n#) (appPR pra as1 as2)
                   (appPR prb bs1 bs2)
                   (appPR prc cs1 cs2)
                   (appPR prd ds1 ds2)
                   (appPR pre es1 es2)

{-# INLINE applPR_5 #-}
applPR_5 pra prb prc prd pre is (P_5 m# as1 bs1 cs1 ds1 es1)
                             js (P_5 n# as2 bs2 cs2 ds2 es2)
  = traceFn "applPR_5" $
  P_5 (m# +# n#) (applPR pra is as1 js as2)
                   (applPR prb is bs1 js bs2)
                   (applPR prc is cs1 js cs2)
                   (applPR prd is ds1 js ds2)
                   (applPR pre is es1 js es2)

{-# INLINE packPR_5 #-}
packPR_5 pra prb prc prd pre (P_5 _ as bs cs ds es) n# sel#
  = traceFn "packPR_5" $
  P_5 n# (packPR pra as n# sel#)
           (packPR prb bs n# sel#)
           (packPR prc cs n# sel#)
           (packPR prd ds n# sel#)
           (packPR pre es n# sel#)

{-# INLINE combine2PR_5 #-}
combine2PR_5 pra prb prc prd pre n# sel# is# (P_5 _ as1 bs1 cs1 ds1 es1)
                                             (P_5 _ as2 bs2 cs2 ds2 es2)
  = traceFn "combine2PR_5" $
  P_5 n# (combine2PR pra n# sel# is# as1 as2)
           (combine2PR prb n# sel# is# bs1 bs2)
           (combine2PR prc n# sel# is# cs1 cs2)
           (combine2PR prd n# sel# is# ds1 ds2)
           (combine2PR pre n# sel# is# es1 es2)

{-# INLINE fromListPR_5 #-}
fromListPR_5 pra prb prc prd pre n# xs
  = P_5 n# (fromListPR pra n# as)
           (fromListPR prb n# bs)
           (fromListPR prc n# cs)
           (fromListPR prd n# ds)
           (fromListPR pre n# es)
  where
    (as,bs,cs,ds,es) = L.unzip5 xs

{-# INLINE nfPR_5 #-}
nfPR_5 pra prb prc prd pre (P_5 _ as bs cs ds es)
  = nfPR pra as
    `seq` nfPR prb bs
    `seq` nfPR prc cs
    `seq` nfPR prd ds
    `seq` nfPR pre es

data Sum2 a b = Alt2_1 a | Alt2_2 b
data Sum3 a b c = Alt3_1 a | Alt3_2 b | Alt3_3 c

data instance PArray (Sum2 a b)
  = PSum2 Int# PArray_Int# PArray_Int# (PArray a)
                                      (PArray b)

data instance PArray (Sum3 a b c)
  = PSum3 Int# PArray_Int# PArray_Int# (PArray a)
                                       (PArray b)
                                       (PArray c)

dPR_Sum2 :: PR a -> PR b -> PR (Sum2 a b)
{-# INLINE dPR_Sum2 #-}
dPR_Sum2 pra prb = PR {
                     lengthPR     = lengthPR_Sum2
                   , emptyPR      = emptyPR_Sum2 pra prb
                   , replicatePR  = replicatePR_Sum2 pra prb
                   , replicatelPR = replicatelPR_Sum2 pra prb
                   , repeatPR     = repeatPR_Sum2 pra prb
                   , indexPR      = indexPR_Sum2 pra prb
                   , bpermutePR   = bpermutePR_Sum2 pra prb 
                   , appPR        = appPR_Sum2 pra prb 
                   , applPR       = applPR_Sum2 pra prb 
                   , packPR       = packPR_Sum2 pra prb 
                   , combine2PR   = combine2PR_Sum2 pra prb 
                   }
 
{-# INLINE lengthPR_Sum2 #-}
lengthPR_Sum2 (PSum2 n# _ _ _ _) = n#

{-# INLINE emptyPR_Sum2 #-}
emptyPR_Sum2 pra prb
  = traceFn "emptyPR_Sum2" $
  PSum2 0# emptyPA_Int# emptyPA_Int# (emptyPR pra) (emptyPR prb)

{-# INLINE replicatePR_Sum2 #-}
replicatePR_Sum2 pra prb n# p
  = traceFn "replicatePR_Sum2" $
      PSum2 n# (replicatePA_Int# n# (case p of Alt2_1 _ -> 0#
                                               Alt2_2 _ -> 1#))
             (upToPA_Int# n#)
             (case p of Alt2_1 x -> replicatePR pra n# x
                        _        -> emptyPR pra)
             (case p of Alt2_2 y -> replicatePR prb n# y
                        _        -> emptyPR prb)

{-# INLINE replicatelPR_Sum2 #-}
replicatelPR_Sum2 pra prb n# mults (PSum2 m# sel# is# as bs)
  = traceFn "replicatelPR_Sum2" $
    PSum2 n# sel' is' as' bs'  
  where
    as'       = replicatelPR pra an1# alt1mults as
    bs'       = replicatelPR prb (n# -# an1#) alt2mults bs
    sel'      = replicatelPA_Int# n# sel# mults
    alt1mults = packPA_Int# mults n# (selectPA_Int# sel# 0#)
    alt2mults = packPA_Int# mults n# (selectPA_Int# sel# 1#)
    an1#      = sumPA_Int# alt1mults
    is'       = selectorToIndices2PA# sel'

{-# INLINE repeatPR_Sum2 #-}
repeatPR_Sum2 pra prb n# (PSum2 m# sel# is# as bs)
  = traceFn "repeatPR_Sum2" $
    PSum2 (m# *# n#) sel' is' as' bs'
  where
    as'  = repeatPR pra n# as
    bs'  = repeatPR prb n# bs
    sel' = repeatPA_Int# n# sel#
    is'  = selectorToIndices2PA# sel'

{-# INLINE indexPR_Sum2 #-}
indexPR_Sum2 pra prb (PSum2 n# sel# is# as bs) i#
  = traceFn "indexPR_Sum2" $
  case indexPA_Int# sel# i# of
      0# -> Alt2_1 (indexPR pra as (indexPA_Int# is# i#))
      _  -> Alt2_2 (indexPR prb bs (indexPA_Int# is# i#))


bpermutePR_Sum2 pra prb  is = traceFn "bpermutePR_Sum2" $
                error "bpermutePR_Sum2 nyi"

appPR_Sum2 pra prb (PSum2 n1# sel1# _ as1 bs1) (PSum2 n2# sel2# _ as2 bs2) = traceFn "appPR_Sum2" $           
  PSum2 (n1# +# n2#) (appPA_Int# sel1# sel2#) (error "ind in appPR_Sum2 nyi") (appPR pra as1 as2) (appPR prb bs1 bs2)


applPR_Sum2 pra prb _ _  = error "applPR_Sum2 nyi"
 
packPR_Sum2 :: PR a -> PR b -> PArray (Sum2 a b) -> Int# -> PArray_Bool# -> PArray (Sum2 a b)
packPR_Sum2 pra prb  (PSum2 n# sel# _ as bs) m# flags
  = traceFn "packPR_Sum2" $
    PSum2 m# sel' is as' bs'
  where
    sel'   = packPA_Int# sel# m# flags

    aFlags = packPA_Bool# flags (lengthPR pra as) (selectPA_Int# sel# 0#)
    bFlags = packPA_Bool# flags (lengthPR prb bs) (selectPA_Int# sel# 1#)
    k#     = truesPA_Bool# bFlags

    as'    = packPR pra as (m# -# k#) aFlags
    bs'    = packPR prb bs k# bFlags
    is     = error "packPR_Sum2 index not impl"

combine2PR_Sum2:: PR a -> PR b -> Int# -> PArray_Int# -> PArray_Int#
                              -> PArray (Sum2 a b) -> PArray (Sum2 a b) -> PArray (Sum2 a b)
combine2PR_Sum2 pra prb n# sel# is# (PSum2 m1# sel1# _ as1 bs1) (PSum2 m2# sel2# _ as2 bs2) = traceFn "combine2PR_Sum2" $                
     case   (sel'Bool, nsel'Bool) of
       (s1#, s2#) ->  traceArgs ("combinePR_Sum\nn  = " ++ show (I# n#)  ++ "\n" ++
                                 "m1 = " ++ show (I# m1#)  ++ "\n" ++
                                 "m2 = " ++ show (I# m2#)  ++ "\n" ++
                                 "as# = " ++ show (I# (lengthPR pra as1)) ++ " " ++ show (I# (lengthPR pra as2)) ++ "\n" ++
                                 "bs# = " ++ show (I# (lengthPR prb bs1)) ++ " " ++ show (I# (lengthPR prb bs2)) ++ "\n" ++
                                 "sel = " ++ show sel#  ++ "\n" ++
                                 "sel1 = " ++ show sel1#  ++ "\n" ++
                                 "sel2 = " ++ show sel2#  ++ "\n" ++
                                 "s1# = " ++ show s1#  ++ "\n" ++
                                 "s2# = " ++ show s2#  ++ "\n" ++
                                 "selB = " ++ show sel'Bool  ++ "\n" ++
                                 "nselB = " ++ show nsel'Bool  ++ "\n" ++
                                 "sel' = " ++ show sel'  ++ "\n"
                             )
                          $ 
                         PSum2  n# sel' (error "combine2PR_Sum2 index nyi") as' bs'
                      where     
                        as# = lengthPR pra as1 +#   lengthPR pra as2
                        bs# = lengthPR prb bs1 +#   lengthPR prb bs2
                        asel = packPA_Int# sel# as# s1#
                        bsel = packPA_Int# sel# bs# s2#
                        as' = trace ("cb1: " ++ show asel) $ combine2PR pra as# asel  is# as1 as2 
                        bs' = trace ("cb2: " ++ show bsel) $ combine2PR prb bs# bsel is# bs1 bs2
     where
       sel' = combine2PA_Int# n# sel# is#  sel1# sel2#
       sel'Bool  = selectPA_Int# sel' 0#
       nsel'Bool = selectPA_Int# sel' 1#


dPR_Sum3 :: PR a -> PR b -> PR c -> PR (Sum3 a b c)
{-# INLINE dPR_Sum3 #-}
dPR_Sum3 pra prb prc
  = PR {
     lengthPR    = lengthPR_Sum3
   , emptyPR     = emptyPR_Sum3 pra prb prc
   , replicatePR = replicatePR_Sum3 pra prb prc
   , indexPR     = indexPR_Sum3 pra prb prc
   }

{-# INLINE lengthPR_Sum3 #-}
lengthPR_Sum3 (PSum3 n# _ _ _ _ _) = n#

{-# INLINE emptyPR_Sum3 #-}
emptyPR_Sum3 pra prb prc        
  = traceFn "emptyPR_Sum3\n" $
  PSum3 0# emptyPA_Int# emptyPA_Int# (emptyPR pra)
                                       (emptyPR prb)
                                       (emptyPR prc)

{-# INLINE replicatePR_Sum3 #-}
replicatePR_Sum3 pra prb prc n# p
  = traceFn "replicatePR_Sum3\n" $
  PSum3 n# (replicatePA_Int# n# (case p of Alt3_1 _ -> 0#
                                           Alt3_2 _ -> 1#
                                           Alt3_3 _ -> 2#))
             (upToPA_Int# n#)
             (case p of Alt3_1 x -> replicatePR pra n# x
                        _        -> emptyPR pra)
             (case p of Alt3_2 x -> replicatePR prb n# x
                        _        -> emptyPR prb)
             (case p of Alt3_3 x -> replicatePR prc n# x
                        _        -> emptyPR prc)

{-# INLINE indexPR_Sum3 #-}
indexPR_Sum3 pra prb prc (PSum3 n# sel# is# as bs cs) i#
  = traceFn "indexPR_Sum3\n" $
  case indexPA_Int# sel# i# of
      0# -> Alt3_1 (indexPR pra as (indexPA_Int# is# i#))
      1# -> Alt3_2 (indexPR prb bs (indexPA_Int# is# i#))
      _  -> Alt3_3 (indexPR prc cs (indexPA_Int# is# i#))

data instance PArray (PArray a)
  = PNested Int# PArray_Int# PArray_Int# (PArray a)

dPR_PArray :: PR a -> PR (PArray a)
{-# INLINE dPR_PArray #-}
dPR_PArray pr = PR {
                  lengthPR     = lengthPR_PArray
                , emptyPR      = emptyPR_PArray pr
                , replicatePR  = replicatePR_PArray pr
                , replicatelPR = replicatelPR_PArray pr
                , repeatPR     = repeatPR_PArray pr
                , indexPR      = indexPR_PArray pr
                , bpermutePR   = bpermutePR_PArray pr
                , appPR        = appPR_PArray pr
                , applPR       = applPR_PArray pr
                , packPR       = packPR_PArray pr
                , combine2PR   = combine2PR_PArray pr
                }

{-# INLINE lengthPR_PArray #-}
lengthPR_PArray (PNested n# _ _ _) = n#

{-# INLINE nested_lengthPA #-}
nested_lengthPA xss = traceFn "nested_lengthPA\n" $
                I# (lengthPR_PArray xss)

{-# INLINE emptyPR_PArray #-}
emptyPR_PArray pr = traceFn "emptyPR_PArray\n" $
               PNested 0# emptyPA_Int# emptyPA_Int# (emptyPR pr)

{-# INLINE replicatePR_PArray #-}
replicatePR_PArray pr n# xs
  = traceFn "replicatePR_PArray\n" $
  PNested n# lens
               (unsafe_scanPA_Int# (+) 0 lens)
               (repeatPR pr n# xs)
  where
    lens = replicatePA_Int# n# (lengthPR pr xs)

{-# INLINE indexPR_PArray #-}
indexPR_PArray pr  (PNested m# lens inds xs) i#
  = bpermutePR pr xs bpinds
  where
    bpinds = enumFromToPA_Int#
               (indexPA_Int# inds i#)
               (indexPA_Int# inds i# +# indexPA_Int# lens i# -# 1#)

{- INLINE bpermutePR_PArray 3-}
bpermutePR_PArray pr (PNested n# xslens xsInds xs) is = traceFn "bpermutePR_PArray\n" $
                  PNested n# xslens' xsInds' xs'
  where
    xslens' = bpermutePA_Int# xslens is
    xsInds' = unsafe_scanPA_Int# (+) 0 xslens'
    is1     = bpermutePA_Int# xsInds is  
    is2     = unsafe_zipWithPA_Int# (\x -> \y -> x + y - 1) xslens' is1
    ps      = enumFromToEachPA_Int# (lengthPR pr xs) is1 is2
    xs'     = bpermutePR pr xs ps

{-# INLINE appPR_PArray #-}
appPR_PArray pr (PNested n# xslens xsInds xs) (PNested m# yslens ysInds ys) = traceFn "appPR_PArray\n" $             
   PNested (n# +# m#) (appPA_Int# xslens yslens) (appPA_Int# xsInds ysInds)  (appPR pr xs ys)

{-# INLINE applPR_PArray #-}
-- applPR_PArray:: PR a -> USegd -> PArray a -> USegd -> PArray a -> PArray a
applPR_PArray pr is1  xn@(PNested n# xslens xsInds xs) is2 yn@(PNested m# yslens ysInds ys) = traceFn "applPR_PArray\n" $
   traceArgs ("applPR_PArray:\n" ++
     show is1 ++ "\n" ++          
     show xslens ++ "\n" ++          
     show is2 ++ "\n" ++          
     show yslens ++ "\n" ++          
     show lens) $
   PNested (n# +# m#) lens ids xys
   where 
     lens   = appPA_Int#  xslens yslens 
     xsSegd = sumPAs_Int# is1 xslens
     ysSegd = sumPAs_Int# is2 yslens
     ids    = unsafe_scanPA_Int# (+) 0 lens
     xlen# = lengthPR pr xs
     ylen# = lengthPR pr ys
     len#  = xlen# +# ylen#
     (PNested _ _ _ xys)   = combine2PR_PArray pr len# isel (error "tmp ind nyi") 
       (PNested n# xsSegd (error "bla1") xs)  (PNested n# ysSegd (error "bla1") ys)
     isel   = unsafe_mapPA_Int# (fromBool . even)
            $ enumFromToPA_Int# 1# (2# *# lengthPA_Int# xslens)



{-# INLINE repeatPR_PArray #-}
repeatPR_PArray pr n# (PNested m# lens _ xs)
  = traceFn "repeatPR_PArray\n" $
  PNested (m# *# n#) lens'
                       (unsafe_scanPA_Int# (+) 0 lens')
                       (repeatPR pr n# xs)
  where
    lens' = repeatPA_Int# n# lens

{-# INLINE replicatelPR_PArray #-}
replicatelPR_PArray pr n# ns (PNested _ lens idxs xs)
  = traceFn "replicatelPR_PArray\n" $
  PNested n# new_lens new_idxs (bpermutePR pr xs indices)
  where
    new_lens = replicatelPA_Int# n# ns lens
    new_idxs = unsafe_scanPA_Int# (+) 0 new_lens
    starts = replicatelPA_Int# n# ns idxs
    ends   = replicatelPA_Int# n# ns
           $ unsafe_zipWithPA_Int# (\i l -> i+l-1) idxs lens

    len     = sumPA_Int# (unsafe_zipWithPA_Int# (*) ns lens)
    indices = enumFromToEachPA_Int# len starts ends

{-# INLINE packPR_PArray #-}
packPR_PArray pr (PNested _ lens _ xs) n# bs
  = traceFn "packPR_PArray\n" $
  PNested n# lens' idxs'
     (packPR pr xs (sumPA_Int# lens')
                   (replicatelPA_Bool# (lengthPR pr xs) lens bs))
  where
    lens' = packPA_Int# lens n# bs
    idxs' = unsafe_scanPA_Int# (+) 0 lens'

{-# INLINE combine2PR_PArray #-}
combine2PR_PArray pr n# sel is (PNested m1# lens1 idxs1 xs)
                               (PNested m2# lens2 idxs2 ys)
  = traceFn ("combine2PR_PArray") $
    traceArgs ("combine2PR_PArray args" ++ show (I# n#) ++ " "  
                                        ++ show (I# m1#) ++ "\n "  
                                        ++ show (I# m2#) ++ "\n "  
                                        ++ show (lens1) ++ "\n"  
                                        ++ show (lens2) ++ "\n"  
                                        ++ show (sel') ++ "\n"  
                                        ++ show (lens) ++ "\n"  
                                        ++ show sel ++ "\n") $ 
      PNested n# lens idxs xys
  where 
    xys  = combine2PR pr len# sel' is' xs ys
    lens = combine2PA_Int# (m1# +# m2#) sel is lens1 lens2
    idxs = unsafe_scanPA_Int# (+) 0 lens

    xlen# = lengthPR pr xs
    ylen# = lengthPR pr ys
    len#  = xlen# +# ylen#

    sel' = replicatelPA_Int# len# lens sel
    is'  = selectorToIndices2PA# sel'

concatPA# :: PArray (PArray a) -> PArray a
{-# INLINE_PA concatPA# #-}
concatPA# (PNested _ _ _ xs) = traceFn "concatPA\n" $
          xs