{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} -- | Tuples of unboxed values with type level length encoding. module Raaz.Core.Types.Tuple ( -- * Length encoded tuples Tuple, Dimension, dimension, initial, diagonal , repeatM, zipWith -- ** Unsafe operations , unsafeFromList ) where import Control.Applicative import qualified Data.List as L import Data.Monoid #if MIN_VERSION_base(4,7,0) import Data.Proxy #endif import qualified Data.Vector.Unboxed as V import GHC.TypeLits import Foreign.Ptr ( castPtr, Ptr ) import Foreign.Storable ( Storable(..) ) import Prelude hiding ( length, zipWith ) import Raaz.Core.Types.Equality import Raaz.Core.Types.Endian import Raaz.Core.Transfer import Raaz.Core.Parse.Applicative -- | Tuples that encode their length in their types. For tuples, we call -- the length its dimension. newtype Tuple (dim :: Nat) a = Tuple { unTuple :: V.Vector a } deriving Show instance (V.Unbox a, Equality a) => Equality (Tuple dim a) where eq (Tuple u) (Tuple v) = V.foldl' mappend mempty $ V.zipWith eq u v -- | Equality checking is timing safe. instance (V.Unbox a, Equality a) => Eq (Tuple dim a) where (==) = (===) -- | Function to make the type checker happy getA :: Tuple dim a -> a getA _ = undefined -- | Function that returns the dimension of the tuple. The dimension -- is calculated without inspecting the tuple and hence the term -- @`dimension` (undefined :: Tuple 5 Int)@ will evaluate to 5. #if MIN_VERSION_base(4,7,0) -- | The constaint on the dimension of the tuple (since base 4.7.0) type Dimension (dim :: Nat) = KnownNat dim {-@ assume natValInt :: Dimension dim => proxy dim -> { v : Int | v == dim } @-} natValInt :: Dimension dim => proxy dim -> Int natValInt = fromEnum . natVal -- | This combinator returns the dimension of the tuple. {-@ dimension :: Dimension dim => Raaz.Core.Types.Tuple.Tuple dim a -> {n: Int | n == dim } @-} dimension :: Dimension dim => Tuple dim a -> Int dimensionP :: Dimension dim => Proxy dim -> Tuple dim a -> Int dimensionP sz _ = natValInt sz dimension = dimensionP Proxy #else -- | The constaint on the dimension of the tuple (pre base 4.7.0) type Dimension (dim :: Nat) = SingI dim -- | This combinator returns the dimension of the tuple. dimension :: Dimension dim => Tuple dim a -> Int dimensionP :: Dimension dim => Sing dim -> Tuple dim a -> Int dimension = withSing dimensionP dimensionP sz _ = fromEnum $ fromSing sz #endif -- | Get the dimension to parser getParseDimension :: (V.Unbox a, Dimension dim) => Parser (Tuple dim a) -> Int getTupFromP :: (V.Unbox a, Dimension dim) => Parser (Tuple dim a) -> Tuple dim a getParseDimension = dimension . getTupFromP getTupFromP _ = undefined instance (V.Unbox a, Storable a, Dimension dim) => Storable (Tuple dim a) where sizeOf tup = dimension tup * sizeOf (getA tup) alignment = alignment . getA peek = unsafeRunParser tupParser . castPtr where len = getParseDimension tupParser tupParser = Tuple <$> unsafeParseStorableVector len poke ptr tup = unsafeWrite writeTup cptr where writeTup = writeStorableVector $ unTuple tup cptr = castPtr ptr instance (V.Unbox a, EndianStore a, Dimension dim) => EndianStore (Tuple dim a) where load = unsafeRunParser tupParser . castPtr where tupParser = Tuple <$> unsafeParseVector len len = getParseDimension tupParser store ptr tup = unsafeWrite writeTup cptr where writeTup = writeVector $ unTuple tup cptr = castPtr ptr adjustEndian ptr n = adjustEndian (unTupPtr ptr) $ nos ptr undefined where nos :: Ptr (Tuple dim a) -> Tuple dim a -> Int nos _ w = dimension w * n unTupPtr :: Ptr (Tuple dim a) -> Ptr a unTupPtr = castPtr -- | Construct a tuple by repeating a monadic action. repeatM :: (Functor m, Monad m, V.Unbox a, Dimension dim) => m a -> m (Tuple dim a) repeatM = mkTupM undefined where mkTupM :: (Functor m, V.Unbox a, Monad m, Dimension dim) => Tuple dim a -> m a -> m (Tuple dim a) mkTupM uTup action = Tuple <$> V.replicateM (dimension uTup) action -- | Construct a tuple out of the list. This function is unsafe and -- will result in run time error if the list is not of the correct -- dimension. {-@ unsafeFromList :: (V.Unbox a, Dimension dim) => { xs:[a] | len xs = dim } -> Raaz.Core.Types.Tuple.Tuple dim a @-} unsafeFromList :: (V.Unbox a, Dimension dim) => [a] -> Tuple dim a unsafeFromList xs | dimension tup == L.length xs = tup | otherwise = wrongLengthMesg where tup = Tuple $ V.fromList xs wrongLengthMesg = error "tuple: unsafeFromList: wrong length" -- | Computes the initial fragment of a tuple. No length needs to be given -- as it is infered from the types. {-@ lazy initial @-} initial :: (V.Unbox a, Dimension dim0) => Tuple dim1 a -> Tuple dim0 a initial = mkTuple undefined where mkTuple :: (V.Unbox a, Dimension dim0) => Tuple dim0 a -> Tuple dim1 a -> Tuple dim0 a mkTuple uTup tup = Tuple $ V.take (dimension uTup) $ unTuple tup -- TODO: Put a constraint that dim0 <= dim1 -- | The @diagonal a@ gives a tuple, all of whose entries is @a@. diagonal :: (V.Unbox a, Dimension dim) => a -> Tuple dim a diagonal = mkTup undefined where mkTup :: (V.Unbox a, Dimension dim) => Tuple dim a -> a -> Tuple dim a mkTup uTup a = Tuple $ V.replicate (dimension uTup) a -- | A zipwith function for tuples zipWith :: (V.Unbox a, V.Unbox b, V.Unbox c) => (a -> b -> c) -> Tuple dim a -> Tuple dim b -> Tuple dim c zipWith f (Tuple at) (Tuple bt)= Tuple $ V.zipWith f at bt