content: {-# LANGUAGE BangPatterns #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE ScopedTypeVariables#-}
  {-# LANGUAGE DeriveDataTypeable #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE CPP #-}
  {-# LANGUAGE FunctionalDependencies #-}
  
  module Numerical.Array.Pure      where
  
  
  --import Numerical.Array.Address
  import qualified Numerical.Array.Layout as L
  
  import Numerical.Array.Locality
  import Numerical.Array.Shape
  import Numerical.Array.Range
  import Numerical.Array.Storage as S
  import Numerical.World
  import Data.Kind (Type)
  import qualified Data.Vector.Generic as VG
  
  
  {-
  a general question that you might ask is "what primops need have a monad constraint"
  
  ie rather than having type a -> b, why are they type Monad m => a -> m b ?
  
  the answer boils down to the following: most array types have
  a *PURE* header data structure that can't be mutated,
  that contains the Shape, extent, some handle/pointer to the associated underlying
  buffer/datastructure.  Any (even nominally pure) access to  that potentially
  mutable buffer should be mediated by a monad.
  
  I further assume that the *structure* and *extent* of this underlying buffer cannot change.
  
  That is, A valid address will always stay valid, even if after some mutation it may
      correspond to a *different* index than it did before.
  -}
  
  {-
  Fix ME, these names are lame
  
  ImmArray == immutable array
  
  -}
  data family ImmArray world rep lay (view::Locality) (rank :: Nat )   el
  
  data instance  ImmArray Native rep lay locality rank  el =
    ImMutableNativeArray {
            nativeBufferPure  :: !(S.BufferPure rep  el  )
            ,nativeFormatPure :: !(L.Format lay locality rank rep)
      }
  
  
  class  PureArray arr   (rank:: Nat)   a |  arr -> rank   where
      type PureArrayAddress (arr :: Type  -> Type ) ::  Type
  
      -- | gives the shape, a 'rank' length list of the dimensions
      basicShape :: arr   a -> Index rank
  
      --basicUnsafeRead  :: PrimMonad m => marr  (PrimState m)   a -> Shape rank Int -> m (Maybe a)
  
      --  | basicMutableSparseIndexToAddres checks if a index is present or not
      -- helpful primitive for authoring codes for (un)structured sparse array format
      -- FIXME : THIS IS A TERRIBLE NAME
      basicSparseIndexToAddress :: ( address ~ PureArrayAddress  arr) => arr a -> Index rank  -> (Maybe address)
  
      -- |
      basicAddressToIndex :: (address ~ PureArrayAddress  arr) => arr a -> address ->  (Index rank  )
  
      -- |  return the Range of valid logical addresses
      basicAddressRange :: (address ~ PureArrayAddress  arr)=>  arr a -> Maybe (Range address)
  
  
  
      -- | gives the next valid logical address
      -- undefined on invalid addresses and the greatest valid address.
      -- Note that for invalid addresses in between minAddress and maxAddress,
      -- will return the next valid address
      basicNextAddress :: (address ~ PureArrayAddress  arr)=>  arr a -> address -> Maybe address
  
      -- I think the case could be made for a basicPreviousAddress opeeration
  
      -- | gives the next valid array index
      -- undefined on invalid indices and the greatest valid index
      basicNextIndex :: (address ~ PureArrayAddress  arr)=>
        arr a ->  Index rank -> Maybe address  -> Maybe ( Index rank, address)
  
  
      -- | for a given valid address, @'basicAddressRegion' addr @ will return an AddressInterval
      -- that contains @addr@. This will be a singleton when the "maximal uniform stride interval"
      -- containing @addr@ has strictly less than 3 elements. Otherwise will return an Address range
      -- covering the maximal interval that will have cardinality at least 3.
  
  
      --basicAddressRegion :: (address ~PureArrayAddress  arr)=>  arr   a -> address ->  UniformAddressInterval address
  
      ---- | Yield the element at the given position. This method should not be
      ---- called directly, use 'unsafeRead' instead.
      basicUnsafeAddressRead  :: (Monad m , address ~ PureArrayAddress  arr)=>  arr   a -> address-> m  a
  
  
  
      -- | Yield the element at the given position. This method should not be
      -- called directly, use 'unsafeSparseRead' instead.
      basicUnsafeSparseRead  :: Monad m => arr   a -> Index rank  -> m (Maybe a)
  
  -- the catch all layout instance
  
  instance (Buffer rep el , L.Layout (L.Format  lay locality  rank rep) rank)
    =>PureArray (ImmArray Native rep lay locality rank )   rank el   where
        type PureArrayAddress (ImmArray Native rep lay locality rank )
          =L.LayoutAddress (L.Format lay locality rank rep)
  
        {-# INLINE basicShape #-}
        basicShape = L.basicLogicalShape . nativeFormatPure
  
        {-# INLINE basicSparseIndexToAddress #-}
        basicSparseIndexToAddress= L.basicToAddress . nativeFormatPure
  
        {-#  INLINE basicAddressToIndex #-}
        basicAddressToIndex = L.basicToIndex . nativeFormatPure
  
        {-# INLINE basicAddressRange #-}
        basicAddressRange = L.basicAddressRange . nativeFormatPure
  
        {-# INLINE basicNextAddress #-}
        basicNextAddress= L.basicNextAddress . nativeFormatPure
  
        {-# INLINE basicNextIndex #-}
        basicNextIndex = L.basicNextIndex . nativeFormatPure
  
        {-# INLINE basicUnsafeSparseRead #-}
        basicUnsafeSparseRead =
            \ arr ix -> case basicSparseIndexToAddress arr ix of
                            Nothing -> return Nothing
                            (Just addr) ->  basicUnsafeAddressRead arr addr >>= ( return . Just)
  
        {-# INLINE basicUnsafeAddressRead #-}
        basicUnsafeAddressRead =
            \ arr  addr ->
              VG.basicUnsafeIndexM (nativeBufferPure arr)
                  (L.basicAddressAsInt (nativeFormatPure arr) $ addr)
  
  class PureArray arr rank a => PureDenseArray arr rank a where
  
      -- | 'basicIndexInBounds' is an O(1) bounds check.
      basicIndexInBounds :: arr a -> Index rank -> Bool
  
      -- |
      basicUnsafeAddressDenseRead  :: (address ~ PureArrayAddress  arr,Monad m) => arr  a -> address-> m a
  
  
  
      -- | Yield the element at the given position. This method should not be
      -- called directly, use 'unsafeRead' instead.
      basicUnsafeDenseReadM  :: Monad m =>  arr     a -> Index rank  -> m a
  
  
  
  
  
  
  