rainbox-0.2.0.0: test/Rainbox/Array2dTests.hs
module Rainbox.Array2dTests where
import Test.Tasty
import Test.QuickCheck
import Test.Tasty.QuickCheck (testProperty)
import Data.Array
import Rainbox.Array2d
-- | Generates a two-dimensional array of Int. The size of the
-- array depends on the size parameter.
genArray :: Gen (Array (Int, Int) Int)
genArray = do
bnds <- genBounds
let nElems = rangeSize bnds
es <- vectorOf nElems arbitraryBoundedIntegral
return $ listArray bnds es
-- | Generates array bounds. The size of the bounds depends on the
-- size parameter.
genBounds :: Gen ((Int, Int), (Int, Int))
genBounds = do
w:x:y:z:[] <- vectorOf 4 arbitrarySizedIntegral
let (minC, maxC) | w < x = (w, x)
| otherwise = (x, w)
(minR, maxR) | y < z = (y, z)
| otherwise = (z, y)
return ((minC, minR), (maxC, maxR))
genTable :: Gen (Table (Int, [(Int, Int)]) (Int, [(Int, Int)]) Int Int Int)
genTable = do
ay <- genArray
return $ table (,) (,) ay
type LabelF
= (Int, [(Int, Int)])
-> (Int, [(Int, Int)])
-> Int -> Int -> Int -> Int
type ChangeLabelF
= (Int, [(Int, Int)])
-> Int
-> [((Int, [(Int, Int)]), Int, Int)]
-> Int
genLabelF :: Gen LabelF
genLabelF = arbitrary
genChangeLabelF :: Gen ChangeLabelF
genChangeLabelF = arbitrary
-- # Properties
-- | Bounds of columns in a Table matches those of the cells
propTableColsBounds
:: (Ix col, Ix row)
=> Table lCol lRow col row a
-> Bool
propTableColsBounds tbl = bounds cls == tgtBounds
where
cls = lCols tbl
((minC, _), (maxC, _)) = bounds . cells $ tbl
tgtBounds = (minC, maxC)
-- | Bounds of rows in a Table matches those of the cells
propTableRowsBounds
:: (Ix col, Ix row)
=> Table lCol lRow col row a
-> Bool
propTableRowsBounds tbl = bounds rws == tgtBounds
where
rws = lRows tbl
((_, minR), (_, maxR)) = bounds . cells $ tbl
tgtBounds = (minR, maxR)
-- | Generating a table using the contents of the rows as labels
-- allows reconstruction of the original array
propGenRebuildByRow
:: (Ix col, Ix row, Eq a)
=> Array (col, row) a
-> Bool
propGenRebuildByRow ay = ay == ay'
where
ay' = array (bounds ay) . concat . elems . lRows
. table (\_ _ -> ()) fRow $ ay
fRow rw ls = map g ls
where
g (col, a) = ((col, rw), a)
-- | Generating a table using the contents of the columns as labels
-- allows reconstruction of the original array
propGenRebuildByCol
:: (Ix col, Ix row, Eq a)
=> Array (col, row) a
-> Bool
propGenRebuildByCol ay = ay == ay'
where
ay' = array (bounds ay) . concat . elems . lCols
. table fCol (\_ _ -> ()) $ ay
fCol cl ls = map g ls
where
g (rw, a) = ((cl, rw), a)
-- | Round-tripping through rows and arrayByRows
propRoundTripRows
:: Eq a
=> Array (Int, Int) a
-> Bool
propRoundTripRows ay = sameShape ay ay'
where
ay' = arrayByRows . rows $ ay
-- | Round-tripping through columns and arrayByCols
propRoundTripCols
:: (Eq a, Show a)
=> Array (Int, Int) a
-> Bool
propRoundTripCols ay = sameShape ay ay'
where
ay' = arrayByCols . cols $ ay
-- | True if both arrays have the same shape; that is, the same
-- number of rows and the same number of columns and the same
-- elements.
sameShape
:: (Ix col, Ix row, Eq a)
=> Array (col, row) a
-> Array (col, row) a
-> Bool
sameShape x y = rx == ry && cx == cy && ex == ey
where
((minCx, minRx), (maxCx, maxRx)) = bounds x
((minCy, minRy), (maxCy, maxRy)) = bounds y
rx = rangeSize (minRx, maxRx)
ry = rangeSize (minRy, maxRy)
cx = rangeSize (minCx, maxCx)
cy = rangeSize (minCy, maxCy)
ex = elems x
ey = elems y
-- # mapTable properties
-- | mapTable does not change lCols
mapTableNoChangeCols
:: (Ix col, Ix row, Eq lCol)
=> (lCol -> lRow -> col -> row -> a -> b)
-> Table lCol lRow col row a
-> Bool
mapTableNoChangeCols f t = lCols t == lCols t'
where
t' = mapTable f t
-- | mapTable does not change lRows
mapTableNoChangeRows
:: (Ix col, Ix row, Eq lRow)
=> (lCol -> lRow -> col -> row -> a -> b)
-> Table lCol lRow col row a
-> Bool
mapTableNoChangeRows f t = lRows t == lRows t'
where
t' = mapTable f t
-- | mapTable allows rebuild of original array
mapTableRebuildNoIndices
:: (Ix col, Ix row, Eq a)
=> Table lCol lRow col row a
-> Bool
mapTableRebuildNoIndices tbl = cells tbl == ay'
where
ay' = listArray (bounds . cells $ tbl) . elems . cells
. mapTable f $ tbl
f _ _ _ _ a = a
mapTableRebuildWithIndices
:: (Ix col, Ix row, Eq a)
=> Table lCol lRow col row a
-> Bool
mapTableRebuildWithIndices tbl = cells tbl == ay'
where
ay' = array (bounds . cells $ tbl) . elems . cells
. mapTable f $ tbl
f _ _ cl rw a = ((cl, rw), a)
-- # labelRows and labelCols properties
-- | labelCols allows rebuild of original array
propLabelColsRebuild
:: (Ix col, Ix row, Eq a)
=> Array (col, row) a
-> Bool
propLabelColsRebuild ay = ay == ay'
where
ay' = array (bounds ay) . concat . elems
. labelCols f $ ay
f cl ls = map g ls
where
g (rw, a) = ((cl, rw), a)
-- | labelRows allows rebuild of original array
propLabelRowsRebuild
:: (Ix col, Ix row, Eq a)
=> Array (col, row) a
-> Bool
propLabelRowsRebuild ay = ay == ay'
where
ay' = array (bounds ay) . concat . elems
. labelRows f $ ay
f rw ls = map g ls
where
g (cl, a) = ((cl, rw), a)
-- # mapRowLabels properties
-- | mapRowLabels does not change column labels
propMapRowLabelsCols
:: (Ix col, Ix row, Eq lCol)
=> (lRow -> row -> [(lCol, col, a)] -> lRow')
-> Table lCol lRow col row a
-> Bool
propMapRowLabelsCols f tb = lbls == lbls'
where
lbls = lCols tb
tb' = mapRowLabels f tb
lbls' = lCols tb'
-- | mapRowLabels does not change cells
propMapRowLabelsCells
:: (Ix col, Ix row, Eq a, Eq lCol)
=> (lRow -> row -> [(lCol, col, a)] -> lRow')
-> Table lCol lRow col row a
-> Bool
propMapRowLabelsCells f tb = ay == ay'
where
ay = cells tb
tb' = mapRowLabels f tb
ay' = cells tb'
-- | mapRowLabels permits reconstruction of original array
propMapRowLabelsRebuild
:: (Ix col, Ix row, Eq a)
=> Table lCol lRow col row a
-> Bool
propMapRowLabelsRebuild t = ay == ay'
where
ay = cells t
ay' = array (bounds ay) . concat . elems
. lRows . mapRowLabels f $ t
f _ rw ls = map g ls
where
g (_, cl, a) = ((cl, rw), a)
-- | mapRowLabels gives the original row labels
propMapRowLabelsRelabel
:: (Ix col, Ix row, Eq a, Eq lRow, Eq lCol)
=> Table lCol lRow col row a
-> Bool
propMapRowLabelsRelabel t = t == t'
where
t' = mapRowLabels (\r _ _ -> r) t
-- # mapColLabels properties
-- | mapColLabels does not change row labels
propMapColLabelsCols
:: (Ix col, Ix row, Eq lRow)
=> (lCol -> col -> [(lRow, row, a)] -> lCol')
-> Table lCol lRow col row a
-> Bool
propMapColLabelsCols f tb = lbls == lbls'
where
lbls = lRows tb
tb' = mapColLabels f tb
lbls' = lRows tb'
-- | mapColLabels does not change cells
propMapColLabelsCells
:: (Ix col, Ix row, Eq a, Eq lCol)
=> (lCol -> col -> [(lRow, row, a)] -> lCol')
-> Table lCol lRow col row a
-> Bool
propMapColLabelsCells f tb = ay == ay'
where
ay = cells tb
tb' = mapColLabels f tb
ay' = cells tb'
-- | mapColLabels permits reconstruction of original array
propMapColLabelsRebuild
:: (Ix col, Ix row, Eq a)
=> Table lCol lRow col row a
-> Bool
propMapColLabelsRebuild t = ay == ay'
where
ay = cells t
ay' = array (bounds ay) . concat . elems
. lCols . mapColLabels f $ t
f _ cl ls = map g ls
where
g (_, rw, a) = ((cl, rw), a)
-- | mapColLabels gives the original row labels
propMapColLabelsRelabel
:: (Ix col, Ix row, Eq a, Eq lRow, Eq lCol)
=> Table lCol lRow col row a
-> Bool
propMapColLabelsRelabel t = t == t'
where
t' = mapColLabels (\r _ _ -> r) t
tests :: TestTree
tests = testGroup "Array2d"
[ testProperty "bounds of columns in Table matches those of cells" $
forAll genTable $
propTableColsBounds
, testProperty "bounds of rows in Table matches those of cells" $
forAll genTable $
propTableRowsBounds
, testProperty "propGenRebuildByRow" $
forAll genArray propGenRebuildByRow
, testProperty "propGenRebuildByCol" $
forAll genArray propGenRebuildByCol
, testProperty "propRoundTripRows" $
forAll genArray propRoundTripRows
, testProperty "propRoundTripCols" $
forAll genArray propRoundTripCols
, testProperty "mapTableNoChangeCols" $
forAll genLabelF $ \f ->
forAll genTable $ \t ->
mapTableNoChangeCols f t
, testProperty "mapTableNoChangeRows" $
forAll genLabelF $ \f ->
forAll genTable $ \t ->
mapTableNoChangeRows f t
, testProperty "mapTableRebuildNoIndices" $
forAll genTable mapTableRebuildNoIndices
, testProperty "mapTableRebuildWithIndices" $
forAll genTable mapTableRebuildWithIndices
, testProperty "propLabelColsRebuild" $
forAll genArray propLabelColsRebuild
, testProperty "propLabelRowsRebuild" $
forAll genArray propLabelRowsRebuild
, testProperty "propMapRowLabelsCols" $
forAll genChangeLabelF $ \f ->
forAll genTable $ \t ->
propMapRowLabelsCols f t
, testProperty "propMapRowLabelsCells" $
forAll genChangeLabelF $ \f ->
forAll genTable $ \t ->
propMapRowLabelsCells f t
, testProperty "propMapRowLabelsRebuild" $
forAll genTable propMapRowLabelsRebuild
, testProperty "propMapRowLabelsRelabel" $
forAll genTable propMapRowLabelsRelabel
, testProperty "propMapColLabelsCols" $
forAll genChangeLabelF $ \f ->
forAll genTable $ \t ->
propMapColLabelsCols f t
, testProperty "propMapColLabelsCells" $
forAll genChangeLabelF $ \f ->
forAll genTable $ \t ->
propMapColLabelsCells f t
, testProperty "propMapColLabelsRebuild" $
forAll genTable propMapColLabelsRebuild
, testProperty "propMapColLabelsRelabel" $
forAll genTable propMapColLabelsRelabel
]