comfort-array-0.5: src/Data/Array/Comfort/Shape/Test.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
module Data.Array.Comfort.Shape.Test (tests) where
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Shape.Utility (isRight)
import Control.Applicative (pure)
import Data.Tuple.HT (mapSnd)
import qualified Test.QuickCheck as QC
inBounds :: (Shape.Indexed sh) => sh -> Bool
inBounds sh = all (Shape.inBounds sh) $ Shape.indices sh
forAllIndices ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix, QC.Testable prop) =>
sh -> (ix -> prop) -> QC.Property
forAllIndices sh f =
let ixs = Shape.indices sh
in not (null ixs) QC.==> QC.forAll (QC.elements ixs) f
-- ToDo: we need to check for indices outside of bounds, too
inBoundsOffset ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) => sh -> QC.Property
inBoundsOffset sh =
forAllIndices sh $ \ix ->
Shape.inBounds sh ix ==
isRight (Shape.getChecked (Shape.unifiedOffset sh ix))
inBoundsSizeOffset ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) => sh -> QC.Property
inBoundsSizeOffset sh =
forAllIndices sh $ \ix ->
Shape.inBounds sh ix ==
isRight (Shape.getChecked (snd (Shape.unifiedSizeOffset sh) ix))
sizeOffset ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) => sh -> QC.Property
sizeOffset sh =
forAllIndices sh $ \ix ->
mapSnd ($ix) (Shape.sizeOffset sh)
==
(Shape.size sh, Shape.offset sh ix)
uncheckedSizeOffset ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) => sh -> QC.Property
uncheckedSizeOffset sh =
forAllIndices sh $ \ix ->
mapSnd ($ix) (Shape.uncheckedSizeOffset sh) ==
(Shape.size sh, Shape.uncheckedOffset sh ix)
unifiedSizeOffsetA ::
(Shape.Checking check, Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) =>
Shape.CheckSingleton check -> sh -> QC.Property
unifiedSizeOffsetA check sh =
forAllIndices sh $ \ix ->
mapSnd ($ix) (Shape.unifiedSizeOffset sh) ==
(Shape.size sh, Shape.requireCheck check $ Shape.unifiedOffset sh ix)
unifiedSizeOffsetB ::
(Shape.Checking check, Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) =>
Shape.CheckSingleton check -> sh -> QC.Property
unifiedSizeOffsetB check sh =
forAllIndices sh $ \ix ->
(mapSnd (Shape.requireCheck check . ($ix)) $ Shape.unifiedSizeOffset sh)
==
case check of
Shape.Checked ->
mapSnd (pure . ($ix)) (Shape.sizeOffset sh)
Shape.Unchecked ->
mapSnd (pure . ($ix)) (Shape.uncheckedSizeOffset sh)
uncheckedOffset ::
(Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) => sh -> QC.Property
uncheckedOffset sh =
forAllIndices sh $ \ix ->
Shape.offset sh ix == Shape.uncheckedOffset sh ix
unifiedOffset ::
(Shape.Checking check, Shape.Indexed sh, Shape.Index sh ~ ix, Show ix) =>
Shape.CheckSingleton check -> sh -> QC.Property
unifiedOffset check sh =
forAllIndices sh $ \ix ->
Shape.requireCheck check (Shape.unifiedOffset sh ix) ==
case check of
Shape.Checked -> pure $ Shape.offset sh ix
Shape.Unchecked -> pure $ Shape.uncheckedOffset sh ix
lengthIndices :: (Shape.Indexed sh) => sh -> Bool
lengthIndices sh = length (Shape.indices sh) == Shape.size sh
indexOffsets :: (Shape.Indexed sh) => sh -> Bool
indexOffsets sh =
map (Shape.offset sh) (Shape.indices sh) == take (Shape.size sh) [0..]
invIndices :: (Shape.InvIndexed sh, Shape.Index sh ~ ix, Eq ix) => sh -> Bool
invIndices sh =
Shape.indices sh ==
map (Shape.indexFromOffset sh) (take (Shape.size sh) [0..])
uncheckedInvIndices ::
(Shape.InvIndexed sh, Shape.Index sh ~ ix, Eq ix) => sh -> Bool
uncheckedInvIndices sh =
Shape.indices sh ==
map (Shape.uncheckedIndexFromOffset sh) (take (Shape.size sh) [0..])
unifiedInvIndicesA ::
(Shape.Checking check, Shape.InvIndexed sh, Shape.Index sh ~ ix, Eq ix) =>
Shape.CheckSingleton check -> sh -> Bool
unifiedInvIndicesA check sh =
map pure (Shape.indices sh) ==
map (Shape.requireCheck check . Shape.unifiedIndexFromOffset sh)
(take (Shape.size sh) [0..])
unifiedInvIndicesB ::
(Shape.Checking check, Shape.InvIndexed sh, Shape.Index sh ~ ix, Eq ix) =>
Shape.CheckSingleton check -> sh -> QC.Property
unifiedInvIndicesB check sh =
let n = Shape.size sh in n>0 QC.==>
QC.forAll (QC.choose (0, n-1)) $ \k ->
Shape.requireCheck check (Shape.unifiedIndexFromOffset sh k) ==
case check of
Shape.Checked -> pure $ Shape.indexFromOffset sh k
Shape.Unchecked -> pure $ Shape.uncheckedIndexFromOffset sh k
unifiedTests ::
(Shape.Checking check,
Shape.InvIndexed sh, Show sh, Shape.Index sh ~ ix, Eq ix, Show ix) =>
Shape.CheckSingleton check -> QC.Gen sh -> [(String, QC.Property)]
unifiedTests check gen =
("unifiedSizeOffsetA", QC.forAll gen (unifiedSizeOffsetA check)) :
("unifiedSizeOffsetB", QC.forAll gen (unifiedSizeOffsetB check)) :
("unifiedOffset", QC.forAll gen (unifiedOffset check)) :
("unifiedInvIndicesA", QC.forAll gen (unifiedInvIndicesA check)) :
("unifiedInvIndicesB", QC.forAll gen (unifiedInvIndicesB check)) :
[]
-- cf. Test.Utility
prefix :: String -> [(String, test)] -> [(String, test)]
prefix msg =
map (\(str,test) -> (msg ++ "." ++ str, test))
tests ::
(Shape.InvIndexed sh, Show sh, Shape.Index sh ~ ix, Eq ix, Show ix) =>
QC.Gen sh -> [(String, QC.Property)]
tests gen =
("inBounds", QC.forAll gen inBounds) :
("inBoundsOffset", QC.forAll gen inBoundsOffset) :
("inBoundsSizeOffset", QC.forAll gen inBoundsSizeOffset) :
("sizeOffset", QC.forAll gen sizeOffset) :
("uncheckedSizeOffset", QC.forAll gen uncheckedSizeOffset) :
("uncheckedOffset", QC.forAll gen uncheckedOffset) :
("lengthIndices", QC.forAll gen lengthIndices) :
("indexOffsets", QC.forAll gen indexOffsets) :
("invIndices", QC.forAll gen invIndices) :
("uncheckedInvIndices", QC.forAll gen uncheckedInvIndices) :
prefix "Checked" (unifiedTests Shape.Checked gen) ++
prefix "Unchecked" (unifiedTests Shape.Unchecked gen) ++
[]