-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,195 @@ | ||
-- SPDX-FileCopyrightText: Copyright Preetham Gujjula | ||
-- | ||
-- SPDX-License-Identifier: BSD-3-Clause | ||
{-# LANGUAGE CPP #-} | ||
{-# LANGUAGE ImpredicativeTypes #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
{-# LANGUAGE UndecidableInstances #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
|
||
module Test.Data.List.ApplyMerge.New (tests) where | ||
|
||
#if !MIN_VERSION_base(4,18,0) | ||
import Control.Applicative (liftA2) | ||
#endif | ||
import Data.Bifunctor (bimap) | ||
import Data.Function (on) | ||
import Data.Kind (Type) | ||
import Data.List (sort) | ||
import Data.List qualified as List | ||
import Data.List.ApplyMerge (applyMerge, applyMergeBy, applyMergeOn) | ||
import Data.List.NonEmpty (NonEmpty ((:|))) | ||
import Data.List.NonEmpty qualified as NE | ||
import Data.List.NonEmpty.ApplyMerge qualified as NE | ||
import GHC.Generics (Generic) | ||
Check failure on line 24 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.2.8
Check failure on line 24 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.4.8
Check failure on line 24 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.6.6
Check failure on line 24 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.8.4
|
||
import Numeric.Natural (Natural) | ||
import Test.QuickCheck.Instances.Natural () | ||
import Test.QuickCheck.Instances.Text () | ||
import Test.Tasty (TestTree, testGroup) | ||
import Test.Tasty.HUnit (testCase, (@?=)) | ||
import Test.Tasty.QuickCheck | ||
Check failure on line 29 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.2.8
Check failure on line 29 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.4.8
Check failure on line 29 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.6.6
Check failure on line 29 in test/Test/Data/List/ApplyMerge/New.hs GitHub Actions / cabal test - ghc-9.8.4
|
||
( Arbitrary, | ||
InfiniteList, | ||
arbitrary, | ||
genericShrink, | ||
getInfiniteList, | ||
getNonNegative, | ||
(===), | ||
) | ||
import Test.Tasty.QuickCheck qualified as QC | ||
|
||
tests :: TestTree | ||
tests = | ||
testGroup | ||
"Data.List.ApplyMerge.New" | ||
[ testCase "placeholder" ((1 :: Int) @?= 1) | ||
[ genericTestApplyMerge | ||
"List" | ||
testListApplyMerge | ||
(applyMerge, applyMergeOn, applyMergeBy), | ||
genericTestApplyMerge | ||
"NonEmpty" | ||
testNonEmptyApplyMerge | ||
(NE.applyMerge, NE.applyMergeOn, NE.applyMergeBy) | ||
] | ||
|
||
genericTestApplyMerge :: | ||
forall f. | ||
(Functor f) => | ||
String -> | ||
(ApplyMerge f -> String -> String -> TestTree) -> | ||
(ApplyMerge f, ApplyMergeOn f, ApplyMergeBy f) -> | ||
TestTree | ||
genericTestApplyMerge label testAm (am, amOn, amBy) = | ||
testGroup | ||
label | ||
[ testAm am "applyMerge f xs ys" "f", | ||
testGroup "applyMergeOn proj f xs ys" . List.singleton $ | ||
let applyMergeViaOn :: ApplyMerge f | ||
applyMergeViaOn f xs ys = | ||
fmap (uncurry f) (amOn (uncurry f) (,) xs ys) | ||
in testAm applyMergeViaOn "f = (,)" "proj", | ||
testGroup "applyMergeBy cmp f xs ys" . List.singleton $ | ||
testAm (amBy compare) "cmp = compare" "f" | ||
] | ||
|
||
type ApplyMerge f = | ||
forall a b c. (Ord c) => (a -> b -> c) -> f a -> f b -> f c | ||
|
||
type ApplyMergeOn f = | ||
forall a b c d. (Ord d) => (c -> d) -> (a -> b -> c) -> f a -> f b -> f c | ||
|
||
type ApplyMergeBy f = | ||
forall a b c. (c -> c -> Ordering) -> (a -> b -> c) -> f a -> f b -> f c | ||
|
||
testNonEmptyApplyMerge :: ApplyMerge NonEmpty -> String -> String -> TestTree | ||
testNonEmptyApplyMerge = testGenericApplyMerge testNonEmptyFunctions | ||
|
||
testListApplyMerge :: ApplyMerge [] -> String -> String -> TestTree | ||
testListApplyMerge = testGenericApplyMerge testListFunctions | ||
|
||
type TestFunctions f = | ||
forall a. | ||
(Show a, Integral a, QC.Arbitrary a) => | ||
String -> | ||
ApplyMerge f -> | ||
[(String, a -> a -> a)] -> | ||
(a -> a -> a) -> | ||
TestTree | ||
|
||
testGenericApplyMerge :: | ||
TestFunctions f -> ApplyMerge f -> String -> String -> TestTree | ||
testGenericApplyMerge testGenericFunctions am label funcLabel = | ||
testGroup | ||
label | ||
[ testGenericFunctions | ||
("increasing " <> funcLabel <> ", increasing xs and ys") | ||
am | ||
increasingNaturalFuncs | ||
(+), | ||
testGenericFunctions | ||
("decreasing " <> funcLabel <> ", decreasing xs and ys") | ||
am | ||
decreasingIntegerFuncs | ||
(-) | ||
] | ||
|
||
increasingNaturalFuncs :: [(String, Natural -> Natural -> Natural)] | ||
increasingNaturalFuncs = | ||
let xs = | ||
[ ("const", const), | ||
("min", min), | ||
("max", max), | ||
("(+)", (+)), | ||
("(\\x y -> 4 * x + y)", \x y -> 4 * x + y), | ||
("(*)", (*)), | ||
("(\\x y -> x ^ (3 :: Int) * y)", \x y -> x ^ (3 :: Int) * y), | ||
("(\\x y -> x * x + y * y)", \x y -> x * x + y * y), | ||
("(\\x y -> 4 * x * x + y * y)", \x y -> 4 * x * x + y * y), | ||
("(\\x _ -> x `quot` 5)", \x _ -> x `quot` 5), | ||
("(\\x y -> (x `quot` 5) + y)", \x y -> (x `quot` 5) + y), | ||
( "(\\x y -> (x `quot` 5) + (y `quot` 5))", | ||
\x y -> (x `quot` 5) + (y `quot` 5) | ||
) | ||
] | ||
in xs ++ map (bimap ("flip " <>) flip) xs | ||
|
||
decreasingIntegerFuncs :: [(String, Integer -> Integer -> Integer)] | ||
decreasingIntegerFuncs = | ||
let xs = | ||
[ ("(\\x _ -> -x)", \x _ -> -x), | ||
("(\\x y -> -min x y)", \x y -> -min x y), | ||
("(\\x y -> -max x y)", \x y -> -max x y), | ||
("(\\x y -> -x - y)", \x y -> -x - y), | ||
("(\\x y -> -(4 * x) - y)", \x y -> -(4 * x) - y), | ||
("(\\x _ -> -(x `quot` 5))", \x _ -> -(x `quot` 5)), | ||
("(\\x y -> -(x `quot` 5) - y)", \x y -> -(x `quot` 5) - y), | ||
( "(\\x y -> -(x `quot` 5) - (y `quot` 5))", | ||
\x y -> -(x `quot` 5) - (y `quot` 5) | ||
) | ||
] | ||
in xs ++ map (bimap ("flip " <>) flip) xs | ||
|
||
getOrderedList :: (a -> a -> a) -> PossiblyInfinite [QC.NonNegative a] -> [a] | ||
getOrderedList op = scanl1 op . map getNonNegative . getPossiblyInfinite | ||
|
||
getOrderedNonEmpty :: | ||
(a -> a -> a) -> PossiblyInfinite (NonEmpty (QC.NonNegative a)) -> NonEmpty a | ||
getOrderedNonEmpty op = | ||
NE.scanl1 op . NE.map QC.getNonNegative . getPossiblyInfinite | ||
|
||
testListFunctions :: TestFunctions [] | ||
testListFunctions label am funcs op = | ||
QC.testProperty label $ do | ||
(fName, f) <- QC.elements funcs | ||
let limit = 100 | ||
pure . QC.counterexample fName $ | ||
\(getOrderedList op -> xs) (getOrderedList op -> ys) -> | ||
let actual = am f xs ys | ||
expected = sort $ on (liftA2 f) (take limit) xs ys | ||
in on (===) (take limit) actual expected | ||
|
||
testNonEmptyFunctions :: TestFunctions NonEmpty | ||
testNonEmptyFunctions label am funcs op = | ||
QC.testProperty label $ do | ||
(fName, f) <- QC.elements funcs | ||
let limit = 100 | ||
pure . QC.counterexample fName $ | ||
\(getOrderedNonEmpty op -> xs) (getOrderedNonEmpty op -> ys) -> | ||
let actual = am f xs ys | ||
expected = NE.sort $ on (liftA2 f) (take1 limit) xs ys | ||
in on (===) (NE.take limit) actual expected | ||
|
||
class HasPossiblyInfinite (a :: Type) where | ||
type PossiblyInfinite a :: Type | ||
getPossiblyInfinite :: PossiblyInfinite a -> a | ||
|
||
instance HasPossiblyInfinite [a] where | ||
type PossiblyInfinite [a] = Either [a] (InfiniteList a) | ||
getPossiblyInfinite = either id getInfiniteList | ||
|
||
instance HasPossiblyInfinite (NonEmpty a) where | ||
type PossiblyInfinite (NonEmpty a) = (a, PossiblyInfinite [a]) | ||
getPossiblyInfinite (x, xs) = x :| getPossiblyInfinite xs | ||
|
||
-- Utilities | ||
take1 :: Int -> NonEmpty a -> NonEmpty a | ||
take1 n (x :| xs) = x :| take n xs |