content: {-# OPTIONS_HADDOCK show-extensions #-}
  
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE KindSignatures #-}
  {-# LANGUAGE DeriveFunctor #-}
  {-# LANGUAGE DeriveFoldable #-}
  {-# LANGUAGE DeriveTraversable #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  {-|
  Module      : Data.FixedSize.Volume
  Description : fixed-size volumes
  Copyright   : (c) Lars Brünjes, 2016
  License     : MIT
  Maintainer  : brunjlar@gmail.com
  Stability   : experimental
  Portability : portable
  
  This module defines fixed-size /volumes/ and some basic typeclass instances and operations for them.
  A 'Volume' is a 'Matrix' with 'Vector' entries, i.e. a three-dimensional array.
  -}
  
  module Data.FixedSize.Volume
      ( Volume(..)
      , toVolume
      , slice
      , fromMatrix
      ) where
  
  import Data.MyPrelude
  import Data.FixedSize.Class
  import Data.FixedSize.Matrix
  import Data.FixedSize.Vector
  import GHC.TypeLits
  
  -- | @'Volume' m n d a@ is the type of /volumes/ with @m@ rows, @n@ columns, depth @d@ and entries of type @a@.
  --
  newtype Volume (m :: Nat) (n :: Nat) (d :: Nat) a = Volume (Matrix m n (Vector d a))
      deriving (Eq, Show, Functor, Foldable, Traversable, NFData)
  
  -- | Converts a 'Matrix' of 'Vector's to the equivalent 'Volume'.
  --
  toVolume :: Matrix m n (Vector d a) -> Volume m n d a
  toVolume = Volume
  
  instance (KnownNat m, KnownNat n, KnownNat d) => Applicative (Volume m n d) where
  
      pure x = Volume $ pure (pure x)
  
      Volume fs <*> Volume xs = Volume $ (<*>) <$> fs <*> xs
  
  instance (KnownNat m, KnownNat n, KnownNat d) => FixedSize (Volume m n d) where
  
      type Index (Volume m n d) = (Int, Int, Int)
  
      type Size (Volume m n d) = m * n * d
  
      Volume m !? (i, j, k) = m !? (i, j) >>= (!? k)
  
      generate f = Volume $ generate (\(i, j) -> generate (\k -> f (i, j, k)))
  
  -- | @'slice' v i@ gives the matrix "at depth @i@" of volume @v@ (or 'Nothing' if @i@ is invalid).
  --
  -- >>> :set -XDataKinds
  -- >>> slice (pure True :: Volume 2 2 3 Bool) 2
  -- Just (Matrix [[True,True],[True,True]])
  --
  -- >>> slice (pure True :: Volume 2 2 3 Bool) 3
  -- Nothing
  --
  slice :: KnownNat d => Volume m n d a -> Int -> Maybe (Matrix m n a)
  slice (Volume m) i = sequenceA $ (!? i) <$> m
  
  -- | Converts a 'Matrix' into a 'Volume' of depth one.
  --
  -- >>> fromMatrix (pure 0) :: Volume 2 2 1 Int
  -- Volume (Matrix [[[0],[0]],[[0],[0]]])
  --
  fromMatrix :: Matrix m n a -> Volume m n 1 a
  fromMatrix = Volume . fmap pure
  