{-# language RankNTypes, MagicHash, UnboxedTuples, BangPatterns, ScopedTypeVariables #-} import GHC.Prim import GHC.Types import GHC.Prim.Array import GHC.Prim.SmallArray import Test.Tasty import Test.Tasty.QuickCheck import Test.QuickCheck runSTRep :: (forall s. State# s -> (# State# s, a #)) -> a runSTRep f = case f realWorld# of (# _ , a #) -> a {-# INLINE [0] runSTRep #-} insList :: Int -> a -> [a] -> [a] insList n a as = let (as1, as2) = splitAt n as in as1 ++ a:as2 delList :: Int -> [a] -> [a] delList n as = case splitAt n as of (as1, _:as2) -> as1 ++ as2 _ -> as data A a = A (Array# a) consA a (A arr) = A (consArray# a arr) snocA (A arr) a = A (snocArray# arr a) snocAPad (I# pad) padElem (A arr) a = A (snocArrayWithPadding# pad padElem arr a) insertA (I# i) a (A arr) = A (insertArray# i a arr) deleteA (I# i) (A arr) = A (deleteArray# i arr) toListA :: forall a. A a -> [a] toListA (A arr) = go arr 0# where go arr i | isTrue# (i ==# sizeofArray# arr) = [] go arr i = case indexArray# arr i of (# a #) -> a : go arr (i +# 1#) fromListA :: [a] -> A a fromListA xs = runSTRep $ \s -> let !(I# size) = length xs in case newArray# size undefined s of (# s, marr #) -> go xs 0# s where go (x:xs) i s = case writeArray# marr i x s of s -> go xs (i +# 1#) s go _ _ s = case unsafeFreezeArray# marr s of (# s , arr #) -> (# s, A arr #) data SA a = SA (SmallArray# a) consSA a (SA arr) = SA (consSmallArray# a arr) snocSA (SA arr) a = SA (snocSmallArray# arr a) snocSAPad (I# pad) padElem (SA arr) a = SA (snocSmallArrayWithPadding# pad padElem arr a) insertSA (I# i) a (SA arr) = SA (insertSmallArray# i a arr) deleteSA (I# i) (SA arr) = SA (deleteSmallArray# i arr) toListSA :: forall a. SA a -> [a] toListSA (SA arr) = go arr 0# where go arr i | isTrue# (i ==# sizeofSmallArray# arr) = [] go arr i = case indexSmallArray# arr i of (# a #) -> a : go arr (i +# 1#) fromListSA :: [a] -> SA a fromListSA xs = runSTRep $ \s -> let !(I# size) = length xs in case newSmallArray# size undefined s of (# s, marr #) -> go xs 0# s where go (x:xs) i s = case writeSmallArray# marr i x s of s -> go xs (i +# 1#) s go _ _ s = case unsafeFreezeSmallArray# marr s of (# s , arr #) -> (# s, SA arr #) main :: IO () main = defaultMain $ testGroup "tests" [ testGroup "smallArray" [ testProperty "cons" $ \(xs :: [Int]) x -> (x:xs) == toListSA (consSA x (fromListSA xs)) , testProperty "snoc" $ \(xs :: [Int]) x -> (xs ++ [x]) == toListSA (snocSA (fromListSA xs) x) , testProperty "snocWithPadding" $ \(xs :: [Int]) x -> forAll (choose (0, 10)) $ \pad -> (xs ++ [x] ++ replicate pad 0) == toListSA (snocSAPad pad 0 (fromListSA xs) x) , testProperty "insert" $ \(xs :: [Int]) x -> forAll (choose (0, length xs)) $ \n -> insList n x xs == toListSA (insertSA n x (fromListSA xs)) , testProperty "delete" $ \(NonEmpty (xs :: [Int])) -> forAll (choose (0, length xs - 1)) $ \n -> delList n xs == toListSA (deleteSA n (fromListSA xs)) ], testGroup "array" [ testProperty "cons" $ \(xs :: [Int]) x -> (x:xs) == toListA (consA x (fromListA xs)) , testProperty "snoc" $ \(xs :: [Int]) x -> (xs ++ [x]) == toListA (snocA (fromListA xs) x) , testProperty "snocWithPadding" $ \(xs :: [Int]) x -> forAll (choose (0, 10)) $ \pad -> (xs ++ [x] ++ replicate pad 0) == toListA (snocAPad pad 0 (fromListA xs) x) , testProperty "insert" $ \(xs :: [Int]) x -> forAll (choose (0, length xs)) $ \n -> insList n x xs == toListA (insertA n x (fromListA xs)) , testProperty "delete" $ \(NonEmpty (xs :: [Int])) -> forAll (choose (0, length xs - 1)) $ \n -> delList n xs == toListA (deleteA n (fromListA xs)) ] ]