summaryrefslogtreecommitdiff
path: root/tests/Data/Graph/Dynamic/Internal/Tree/Tests.hs
blob: c3f7b276a3adbaed3aacf11213d4ad53f4cc3dee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
module Data.Graph.Dynamic.Internal.Tree.Tests
    ( BuildTree
    , prop_build
    , prop_split
    ) where

import           Control.Monad.Primitive          (PrimMonad (..))
import           Control.Monad.ST                 (runST)
import           Data.Graph.Dynamic.Internal.Tree
import           Data.List.NonEmpty               (NonEmpty)
import qualified Data.List.NonEmpty               as NonEmpty
import           Data.Proxy                       (Proxy)
import           Data.Semigroup                   ((<>))
import qualified Test.QuickCheck                  as QC

data BuildTree a v
    = Singleton a v
    | Append (BuildTree a v) (BuildTree a v)
    | Snoc (BuildTree a v) a v
    | Cons a v (BuildTree a v)
    deriving (Show)

arbitraryBuildTree
    :: (QC.Arbitrary a, QC.Arbitrary v) => Int -> QC.Gen (BuildTree a v)
arbitraryBuildTree n
    | n <= 0    = Singleton <$> QC.arbitrary <*> QC.arbitrary
    | otherwise = QC.oneof
        [ Singleton <$> QC.arbitrary <*> QC.arbitrary
        , Append <$> arbitraryBuildTree (n - 1) <*> arbitraryBuildTree (n - 1)
        , Snoc <$> arbitraryBuildTree (n - 1) <*> QC.arbitrary <*> QC.arbitrary
        , Cons <$> QC.arbitrary <*> QC.arbitrary <*> arbitraryBuildTree (n - 1)
        ]

instance (QC.Arbitrary a, QC.Arbitrary v) => QC.Arbitrary (BuildTree a v) where
    arbitrary = QC.sized arbitraryBuildTree

    shrink (Singleton _ _) = []
    shrink (Snoc l a v)    = [l] ++ [Snoc l' a v | l' <- QC.shrink l]
    shrink (Cons a v r)    = [r] ++ [Cons a v r' | r' <- QC.shrink r]
    shrink (Append l r)    =
        [l, r] ++
        [Append l' r | l' <- QC.shrink l] ++
        [Append l r' | r' <- QC.shrink r]

-- | Returns pointers to all nodes.
appendsToTree
    :: (Tree t, PrimMonad m, Monoid v)
    => Proxy t
    -> TreeGen t (PrimState m)
    -> BuildTree a v
    -> m (t (PrimState m) a v, NonEmpty (t (PrimState m) a v))
appendsToTree _proxy gen = go
  where
    go (Singleton a v) = do
        s <- singleton gen a v
        return (s, s NonEmpty.:| [])
    go (Snoc b a v) = do
        (l, lps) <- go b
        s        <- singleton gen a v
        rt       <- l `snoc` s
        return (rt, lps <> (s NonEmpty.:| []))
    go (Cons a v b) = do
        s        <- singleton gen a v
        (r, rps) <- go b
        rt       <- s `cons` r
        return (rt, (s NonEmpty.:| []) <> rps)
    go (Append bl br)  = do
        (l, lps) <- go bl
        (r, rps) <- go br
        rt       <- append l r
        return (rt, lps <> rps)

appendsToList :: BuildTree a v -> [a]
appendsToList (Singleton a _) = [a]
appendsToList (Snoc l a _)    = appendsToList l ++ [a]
appendsToList (Cons a _ r)    = [a] ++ appendsToList r
appendsToList (Append l r)    = appendsToList l ++ appendsToList r

prop_build
    :: (TestTree t, Eq a, Show a, Eq v, Monoid v, Show v)
    => Proxy t -> BuildTree a v -> QC.Property
prop_build proxy appends = runST $ do
    gen    <- newTreeGen proxy
    (t, _) <- appendsToTree proxy gen appends
    assertInvariants t

    l <- toList t
    return $ l QC.=== appendsToList appends

prop_split
    :: (TestTree t, Eq a, Show a, Eq v, Monoid v, Show v)
    => Proxy t -> Int -> BuildTree a v -> QC.Property
prop_split proxy idx0 appends = runST $ do
    gen        <- newTreeGen proxy
    (_t, ptrs) <- appendsToTree proxy gen appends
    let idx = idx0 `mod` NonEmpty.length ptrs
        ptr = ptrs NonEmpty.!! idx

    (mbL, mbR) <- split ptr
    case mbL of
        Just l -> do
            assertInvariants l
            assertRoot l
        _ -> return ()

    case mbR of
        Just r -> do
            assertInvariants r
            assertRoot r
        _ -> return ()

    assertInvariants ptr
    assertSingleton ptr

    lList <- maybe (return []) toList mbL
    cList <- toList ptr
    rList <- maybe (return []) toList mbR

    return $ lList ++ cList ++ rList QC.=== appendsToList appends