-- | Dense primitive arrays where the lower index is zero (or the
-- equivalent of zero for newtypes and enumerations).
--
-- Actual @write@s to data structures use a more safe @write@ instead of
-- the unsafe @unsafeWrite@. Writes also tend to occur much less in DP
-- algorithms (say, N^2 writes for an N^3 time algorithm -- mostly reads
-- are being executed).
--
-- TODO consider if we want to force the lower index to be zero, or allow
-- non-zero lower indices. Will have to be considered together with the
-- @Index.Class@ module!
--
-- TODO while @Unboxed@ is, in princile, @Hashable@, we'd need the
-- corresponding @VU.Vector@ instances ...
--
-- TODO rename to Dense.Vector, since there are other possibilities to store,
-- without basing on vector.

module Data.PrimitiveArray.Dense where

import           Control.DeepSeq
import           Control.Exception (assert)
import           Control.Monad (liftM, forM_, zipWithM_)
import           Control.Monad.Primitive (PrimState)
import           Data.Aeson (ToJSON,FromJSON)
import           Data.Binary (Binary)
import           Data.Data
import           Data.Hashable (Hashable)
import           Data.Serialize (Serialize)
import           Data.Typeable (Typeable)
import           Data.Vector.Binary
import           Data.Vector.Generic.Mutable as GM hiding (length)
import           Data.Vector.Serialize
import           Debug.Trace
import           GHC.Generics (Generic)
import qualified Data.Vector as V
import qualified Data.Vector.Fusion.Stream.Monadic as SM
import qualified Data.Vector.Generic as VG
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Unboxed as VU

import           Data.PrimitiveArray.Class
import           Data.PrimitiveArray.Index.Class



data Dense v sh e = Dense !(LimitType sh) !(v e)

type Unboxed sh e = Dense VU.Vector sh e

type Storable sh e = Dense VS.Vector sh e

type Boxed sh e = Dense V.Vector sh e



deriving instance (Eq      (LimitType sh), Eq (v e)     )  Eq      (Dense v sh e)
deriving instance (Generic (LimitType sh), Generic (v e))  Generic (Dense v sh e)
deriving instance (Read    (LimitType sh), Read (v e)   )  Read    (Dense v sh e)
deriving instance (Show    (LimitType sh), Show (v e)   )  Show    (Dense v sh e)

deriving instance Typeable (Dense v sh e)

deriving instance (Data (v e), Data (LimitType sh), Data e, Data sh, Typeable sh, Typeable e, Typeable v)  Data (Dense v sh e)

instance (Binary    (LimitType sh), Binary    (v e), Generic (LimitType sh), Generic (v e)) => Binary    (Dense v sh e)
instance (Serialize (LimitType sh), Serialize (v e), Generic (LimitType sh), Generic (v e)) => Serialize (Dense v sh e)
instance (ToJSON    (LimitType sh), ToJSON    (v e), Generic (LimitType sh), Generic (v e)) => ToJSON    (Dense v sh e)
instance (FromJSON  (LimitType sh), FromJSON  (v e), Generic (LimitType sh), Generic (v e)) => FromJSON  (Dense v sh e)
instance (Hashable  (LimitType sh), Hashable  (v e), Generic (LimitType sh), Generic (v e)) => Hashable  (Dense v sh e)

instance (NFData (LimitType sh), NFData (v e))  NFData (Dense v sh e) where
  rnf (Dense h xs) = rnf h `seq` rnf xs
  {-# Inline rnf #-}



data instance MutArr m (Dense v sh e) = MDense !(LimitType sh) !(VG.Mutable v (PrimState m) e)
  deriving (Generic,Typeable)

instance (Show (LimitType sh), Show (VG.Mutable v (PrimState m) e), VG.Mutable v (PrimState m) e ~ mv)  Show (MutArr m (Dense v sh e)) where
  show (MDense sh mv) = show (sh,mv)

instance (NFData (LimitType sh), NFData (VG.Mutable v (PrimState m) e), VG.Mutable v (PrimState m) e ~ mv)  NFData (MutArr m (Dense v sh e)) where
  rnf (MDense h xs) = rnf h `seq` rnf xs
  {-# Inline rnf #-}

instance
  ( Index sh, MutArr m (Dense v sh e) ~ mv
  , GM.MVector (VG.Mutable v) e
#if ADPFUSION_DEBUGOUTPUT
  , Show sh, Show (LimitType sh), Show e
#endif
  )  MPrimArrayOps (Dense v) sh e where
  {-# Inline upperBoundM #-}
  upperBoundM (MDense h _) = h
  {-# Inline fromListM #-}
  fromListM h xs = do
    ma  newM h
    let (MDense _ mba) = ma
    SM.zipWithM_ (\k x  assert (length xs == size h) $ unsafeWrite mba k x) (SM.enumFromTo 0 (size h -1)) (SM.fromList xs)
    return ma
  {-# Inline newM #-}     -- TODO was NoInline, check if anything breaks!
  newM h = MDense h `liftM` new (size h)
  {-# Inline newWithM #-}
  newWithM h def = do
    ma  newM h
    let (MDense _ mba) = ma
    SM.mapM_ (\k  unsafeWrite mba k def) $ SM.enumFromTo 0 (size h -1)
    return ma
  {-# Inline readM #-}
  readM  (MDense h mba) idx     = assert (inBounds h idx) $ unsafeRead  mba (linearIndex h idx)
  {-# Inline writeM #-}
  writeM (MDense h mba) idx elm =
#if ADPFUSION_DEBUGOUTPUT
    (if inBounds h idx then id else traceShow ("writeM", h, idx, elm, size h, linearIndex h idx, inBounds h idx))
#endif
    assert (inBounds h idx) $ unsafeWrite mba (linearIndex h idx) elm

instance (Index sh, VG.Vector v e)  PrimArrayOps (Dense v) sh e where
  {-# Inline upperBound #-}
  upperBound (Dense h _) = h
  {-# Inline unsafeFreeze #-}
  unsafeFreeze (MDense h mba) = Dense h `liftM` VG.unsafeFreeze mba
  {-# Inline unsafeThaw #-}
  unsafeThaw   (Dense h ba) = MDense h `liftM` VG.unsafeThaw ba
  {-# Inline unsafeIndex #-}
  unsafeIndex  (Dense h ba) idx = VG.unsafeIndex ba (linearIndex h idx)
  {-# Inline transformShape #-}
  transformShape tr (Dense h ba) = Dense (tr h) ba

instance (Index sh, VG.Vector v e, VG.Vector v e')  PrimArrayMap (Dense v) sh e e' where
  map f (Dense h xs) = Dense h (VG.map f xs)
  {-# Inline map #-}



{-
 -
 - This stuff tells us how to write efficient generics on large data
 - constructors like the Turner and Vienna ctors.
 -

import qualified Data.Generics.TH as T

data Unboxed sh e = Unboxed !sh !(VU.Vector e)
  deriving (Show,Eq,Ord)

data X e = X (Unboxed DIM1 e) (Unboxed DIM1 e)
  deriving (Show,Eq,Ord)

x :: X Int
x = X z z where z = (Unboxed (Z:.10) (VU.fromList [ 0 .. 10] ))

pot :: X Int -> X Double
pot = $( T.thmapT (T.mkTs ['f]) [t| X Int |] ) where
  f :: Unboxed DIM1 Int -> Unboxed DIM1 Double
  f (Unboxed sh xs) = Unboxed sh (VU.map fromIntegral xs)

-}