packages feed

hjugement-1.0.0.20170804: test/QuickCheck.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module QuickCheck where

import Test.QuickCheck
import Test.Tasty
import Test.Tasty.QuickCheck

import Control.Monad (replicateM)
import qualified Data.Set as Set

import Hjugement
import Types

quickchecks :: TestTree
quickchecks =
	testGroup "QuickCheck"
	 [ testProperty "arbitraryJudgments" $ \(SameLength (x::[(G6,Count)],y)) ->
		let (gx, cx) = unzip x in
		let (gy, cy) = unzip y in
		gx == gy && sum cx == sum cy
	 , testGroup "Value"
		 [ testProperty "compare" $ \(SameLength (x::Value G6,y)) ->
			expandValue x`compare` expandValue y == x`compare`y
		 ]
	 {-
	 , testProperty "majorityGauge and majorityValue consistency" $
		 \(SameLength (x@(Merit xs)::Merit G6,y@(Merit ys))) ->
			not (all (==0) xs || all (==0) ys) ==>
			case majorityGauge x`compare`majorityGauge y of
			 LT -> majorityValue x < majorityValue y
			 GT -> majorityValue x > majorityValue y
			 EQ -> True
	 -}
	 ]

-- | Decompress a 'Value'.
expandValue :: Value a -> [a]
expandValue (Value []) = []
expandValue (Value ((x,c):xs)) = replicate c x ++ expandValue (Value xs)

-- | @arbitraryJudgments n@ arbitrarily generates 'n' lists of pairs of grade and 'Count'
-- for the same arbitrary grades,
-- and with the same total 'Count' of individual judgments.
arbitraryJudgments :: forall g. (Bounded g, Enum g) => Int -> Gen [[(g, Count)]]
arbitraryJudgments n = sized $ \s -> do
	minG <- choose (fromEnum(minBound::g), fromEnum(maxBound::g))
	maxG <- choose (minG, fromEnum(maxBound::g))
	let gs::[g] = toEnum minG`enumFromTo`toEnum maxG
	let lg = maxG - minG + 1
	replicateM n $ do
		cs  <- resize s $ arbitrarySizedNaturalSum lg
		cs' <- arbitraryPad (lg - length cs) (return 0) cs
		return $ zip gs cs'

-- | @arbitrarySizedNaturalSum maxLen@
-- arbitrarily chooses a list of 'length' at most 'maxLen',
-- containing 'Int's summing up to 'sized'.
arbitrarySizedNaturalSum :: Int -> Gen [Int]
arbitrarySizedNaturalSum maxLen = sized (go maxLen)
	where
	go :: Int -> Int -> Gen [Int]
	go len tot | len <= 0 = return []
	           | len == 1 = return [tot]
	           | tot <= 0 = return [tot]
	go len tot = do
		d <- choose (0, tot)
		(d:) <$> go (len-1) (tot - d)

-- | @arbitraryPad n pad xs@
-- arbitrarily grows list 'xs' with 'pad' elements
-- up to length 'n'.
arbitraryPad :: Int -> Gen a -> [a] -> Gen [a]
arbitraryPad n pad [] = replicateM n pad
arbitraryPad n pad xs = do
	(r, xs') <- go n xs
	if r > 0
	 then arbitraryPad r pad xs'
	 else return xs'
	where
	go r xs' | r <= 0 = return (0,xs')
	go r [] =  arbitrary >>= \b ->
		if b then pad >>= \p -> ((p:)<$>) <$> go (r-1) []
		     else return (r,[])
	go r (x:xs') = arbitrary >>= \b ->
		if b then pad >>= \p -> (([p,x]++)<$>) <$> go (r-1) xs'
		     else ((x:)<$>) <$> go r xs'

-- | Like 'nub', but O(n * log n).
nubList :: Ord a => [a] -> [a]
nubList = go Set.empty where
	go _ [] = []
	go s (x:xs) | x`Set.member`s = go s xs
	            | otherwise      = x:go (Set.insert x s) xs

instance Arbitrary G6 where
	arbitrary = arbitraryBoundedEnum
instance (Arbitrary g, Bounded g, Enum g, Ord g, Show g) => Arbitrary (Merit g) where
	arbitrary = fromList . head <$> arbitraryJudgments 1
	shrink (Merit m) = Merit <$> shrink m
instance
 ( Arbitrary p, Bounded p, Enum p, Ord p, Show p
 , Arbitrary g, Bounded g, Enum g, Ord g, Show g
 ) => Arbitrary (Merits p g) where
	arbitrary = do
		minP <- choose (fromEnum(minBound::p), fromEnum(maxBound::p))
		maxP <- choose (minP, fromEnum(maxBound::p))
		let ps = toEnum minP`enumFromTo`toEnum maxP
		let ms = (fromList <$>) <$> arbitraryJudgments (maxP - minP + 1)
		fromList . zip ps <$> ms
instance (Bounded g, Eq g, Integral g, Arbitrary g) => Arbitrary (Value g) where
	arbitrary = head . (Value <$>) <$> arbitraryJudgments 1
	shrink (Value vs) = Value <$> shrink vs

-- * Type 'SameLength'
newtype SameLength a = SameLength a
 deriving (Eq, Show)
instance Functor SameLength where
	fmap f (SameLength x) = SameLength (f x)
instance (Arbitrary g, Bounded g, Enum g) => Arbitrary (SameLength (Value g, Value g)) where
	arbitrary = do
		SameLength (x,y) <- arbitrary
		return $ SameLength (Value x, Value y)
instance (Arbitrary g, Bounded g, Enum g, Ord g, Show g) => Arbitrary (SameLength (Merit g, Merit g)) where
	arbitrary = do
		SameLength (x,y) <- arbitrary
		return $ SameLength (fromList x, fromList y)
instance (Arbitrary g, Bounded g, Enum g) => Arbitrary (SameLength ([(g,Count)], [(g,Count)])) where
	arbitrary = do
		vs <- arbitraryJudgments 2
		case vs of
		 [x,y] -> return $ SameLength (x,y)
		 _ -> undefined