{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main (main) where
import Control.Applicative
import Data.Coerce
import Data.Foldable
import Data.Functor.Compose
import Data.Monoid (All (..))
import Data.Traversable
import Test.Framework
(Test, defaultMain, testGroup)
import Test.Framework.Providers.HUnit (testCase)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.HUnit (Assertion, (@?=))
import Test.QuickCheck
(Arbitrary (..), Gen, choose, listOf, shrinkIntegral,
vectorOf)
import Control.Monad
import Control.Monad.ST
import Data.Function (on)
import Data.Functor.Identity
import Data.List (groupBy)
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Word
import Debug.Trace
import Data.Primitive.Array.Maybe
import qualified Data.ArrayList.Generic as ArrayList
import qualified Data.Graph.Immutable as Graph
import qualified Data.Graph.Mutable as MGraph
import qualified Data.Heap.Mutable.ModelD as HeapD
import Data.Maybe.Unsafe hiding (maybe)
import qualified Data.Maybe.Unsafe as UMaybe
import qualified Data.Trie.Mutable.Bits as BitTrie
import qualified Data.Vector as V
main :: IO ()
main = defaultMain tests
tests :: [Test]
tests =
[ testGroup "Heaps"
[ testProperty "Model D Push No Crash" multipush
, testProperty "Model D Push Pop" heapPushPop
, testProperty "Model D List" heapMatchesList
]
, testGroup "ArrayList"
[ testProperty "Insertion followed by freezing" arrayListWorks
]
, testGroup "Graph"
[ testProperty "Building only from vertices" graphBuildingVertices
, testProperty "Trivial case for Dijkstras Algorithm" dijkstraEasyDistance
]
, testGroup "MaybeArray"
[ testCase "Values are as expected" maybeArrayWorks
]
, testGroup "Bit Trie"
[ testProperty "Basic Insert and Lookup" bitTrieBasic
, testProperty "Prefixes" bitTriePrefix
]
, testGroup "Unsafe Maybe"
[ testCase "Unsafe Maybe functions" unsafeMaybeWorks
-- , testProperty "Unsafe Maybe Quickcheck props" unsafeMaybeProp
]
]
testElements :: Int
testElements = 15
newtype Min = Min { getMin :: Word32 }
deriving (Show,Read,Eq,Ord)
instance Arbitrary Min where
arbitrary = fmap Min (choose (0,20))
shrink (Min a) = fmap Min $ filter (>= 0) $ shrinkIntegral a
instance Monoid Min where
mempty = Min maxBound
mappend (Min a) (Min b) = Min (min a b)
newtype MyElement = MyElement { getMyElement :: Int }
deriving (Show,Read,Eq,Ord)
instance Arbitrary MyElement where
arbitrary = fmap MyElement (choose (0,fromIntegral testElements - 1))
shrink (MyElement a) = fmap MyElement $ filter (>= 0) $ shrinkIntegral a -- fmap MyElement (enumFromTo 0 (a - 1))
newtype TenElemsOrLessList a = TenElemsOrLessList [a]
deriving (Read,Show,Eq,Ord)
instance Arbitrary a => Arbitrary (TenElemsOrLessList a) where
arbitrary = fmap TenElemsOrLessList $ flip vectorOf arbitrary =<< choose (0 :: Int,10)
shrink (TenElemsOrLessList a) = fmap TenElemsOrLessList $ shrink a
multipush :: [(Min,MyElement)] -> Bool
multipush xs = runST $ do
h <- trace "Running Test" (HeapD.new testElements)
HeapD.pushList (coerce xs :: [(Min,Int)]) h
return True
heapPushPop :: [(Min,MyElement)] -> Bool
heapPushPop xs =
let res = runST $ do
h <- HeapD.new testElements
forM xs $ \(c,MyElement i) -> do
HeapD.push c i h
HeapD.pop h
in sequence res == coerce (Just xs)
heapMatchesList :: [(Min,MyElement)] -> Bool
heapMatchesList xs' =
let xs = coerce xs' :: [(Min,Int)]
xsSet = fmap (\(p,e) -> (e,p)) xs
ys = Map.fromListWith mappend xsSet
listRes = Map.toList $ Map.fromListWith Set.union $ map (\(e,p) -> (p,Set.singleton e)) (Map.toList ys)
heapRes = runST $ do
h <- HeapD.new testElements
HeapD.pushList xs h
HeapD.popAll h
heapResSet = map (\pairs@((p,_) : _) -> (p,Set.fromList $ map snd pairs))
$ groupBy (on (==) fst) heapRes
in heapResSet == listRes
arrayListWorks :: [Int] -> Bool
arrayListWorks xs =
let ys = runST $ do
a <- ArrayList.new 1
forM_ xs $ \x -> ArrayList.push a x
ArrayList.freeze a
in xs == V.toList ys
-- This makes sure that when you insert a bunch of vertices into
-- a graph, you get the same vertices back out. This does not
-- do anything to check for edges.
graphBuildingVertices :: [Int] -> Bool
graphBuildingVertices xs =
let sg = runST $ Graph.create $ \mg -> do
forM_ xs (MGraph.insertVertex mg)
ys = Graph.with sg (Graph.verticesToVector . Graph.vertices)
in List.nub (List.sort xs) == List.sort (V.toList ys)
graphBuildingEdgesEverywhere :: TenElemsOrLessList Int -> Bool
graphBuildingEdgesEverywhere (TenElemsOrLessList xs) =
let onlyEdge = 77 :: Int
sg = runST $ Graph.create $ \mg -> do
vertices <- forM xs $ \x -> do
MGraph.insertVertex mg x
forM_ vertices $ \source -> do
forM_ vertices $ \dest -> do
MGraph.insertEdge mg source dest onlyEdge
in getAll $ getConst $ Graph.with sg $ \g ->
let v = Graph.vertices g -- Graph.vertices g Graph.verticesToVector . Graph.vertices)
vlist = Graph.verticesToVertexList v
in for vlist $ \va ->
for vlist $ \vb ->
Const (All $ Graph.lookupEdge va vb g == Just onlyEdge)
-- Every node is connected to at most two other nodes. The end
-- nodes only have one neighbor. Go from one end node to the other.
dijkstraEasyDistance :: [Word32] -> Bool
dijkstraEasyDistance xs =
let sg = runST $ Graph.create $ \mg -> do
start <- MGraph.insertVertex mg (0 :: Int)
let insertNext prevVertex zs i = case zs of
[] -> return ()
y : ys -> do
vertex <- MGraph.insertVertex mg i
MGraph.insertEdge mg prevVertex vertex y
insertNext vertex ys (i + 1)
insertNext start xs 1
in Graph.with sg $ \g -> case (Graph.lookupVertex 0 g, Graph.lookupVertex (List.length xs) g) of
(Nothing,Nothing) -> False
(Nothing,Just _) -> False
(Just _,Nothing) -> False
(Just start, Just end) ->
let expected = Min (sum xs)
in expected == Graph.atVertex end
(Graph.dijkstra
(\_ _ (Min x) distance -> Min (x + distance))
(Min 0) (Identity start) g
)
data Thing = Foo | Bar Int | Baz Bool
deriving (Eq,Show)
maybeArrayWorks :: IO ()
maybeArrayWorks = do
arr <- newMaybeArray 17 Nothing
writeMaybeArray arr 0 (Just Foo)
writeMaybeArray arr 9 (Just (Bar 62))
writeMaybeArray arr 16 (Just (Baz True))
a <- readMaybeArray arr 0
b <- readMaybeArray arr 9
c <- readMaybeArray arr 16
d <- readMaybeArray arr 12
arr2 <- newMaybeArray 17 (Just (Baz True))
writeMaybeArray arr 2 Nothing
writeMaybeArray arr 7 (Just (Bar 15))
e <- readMaybeArray arr 2
f <- readMaybeArray arr 7
(a,b,c,d,e,f) @?=
( Just Foo, Just $ Bar 62
, Just $ Baz True, Nothing
, Nothing, Just (Bar 15)
)
unsafeMaybeWorks :: IO ()
unsafeMaybeWorks = (a,b,c,d,e,f,g,h) @?= (Nothing,Just 0,1,Nothing,Just 3,Nothing,Just 4,Nothing)
where a = toMaybe $ nothing :: Maybe Int
b = toMaybe $ just (0 :: Int)
c = UMaybe.maybe (0 :: Int) (+1) (just 0)
d = toMaybe $ nothing :: Maybe Int
e = toMaybe $ fmap (+1) (just 2)
f = toMaybe $ fmap (+1) nothing
g = toMaybe $ just (just 4) >>= id
h = toMaybe $ UMaybe.maybe nothing (const nothing) nothing :: Maybe Int
bitTrieBasic :: [Word8] -> Bool
bitTrieBasic xs =
let res = runST $ do
trie <- BitTrie.new
for_ xs $ \x -> BitTrie.insert trie x x
Const (All res) <- getCompose $ for_ xs $ \x -> Compose $ do
m <- BitTrie.lookup trie x
return $ Const $ case m of
Nothing -> All False
Just y -> All (x == y)
return res
in res == True
bitTriePrefix :: Word8 -> Bool
bitTriePrefix x = do
let res = runST $ do
trie <- BitTrie.new
BitTrie.insertPrefix trie 4 0xF0 'B'
BitTrie.insertPrefix trie 1 0x80 'A'
BitTrie.insertPrefix trie 4 0x00 'C'
m <- BitTrie.lookup trie x
return $ case () of
() | x < 0x10 -> m == Just 'C'
| x >= 0xF0 -> m == Just 'B'
| x >= 0x80 -> m == Just 'A'
| otherwise -> m == Nothing
in res == True