#ifdef GENERICS
#endif
module Test.SmallCheck.Series (
Depth, Series, Serial(..),
cons0, cons1, cons2, cons3, cons4,
alts0, alts1, alts2, alts3, alts4,
(\/), (><),
N(..), Nat, Natural,
depth
) where
import Data.List (intersperse)
#ifdef GENERICS
import GHC.Generics
import Data.DList (DList, toList, fromList)
import Data.Monoid (mempty, mappend)
#endif
type Depth = Int
type Series a = Depth -> [a]
infixr 7 \/
(\/) :: Series a -> Series a -> Series a
s1 \/ s2 = \d -> s1 d ++ s2 d
infixr 8 ><
(><) :: Series a -> Series b -> Series (a,b)
s1 >< s2 = \d -> [(x,y) | x <- s1 d, y <- s2 d]
class Serial a where
series :: Series a
coseries :: Series b -> Series (a->b)
#ifdef GENERICS
default series :: (Generic a, GSerial (Rep a)) => Series a
series = map to . gSeries
default coseries :: (Generic a, GSerial (Rep a)) => Series b -> Series (a->b)
coseries rs = map (. from) . gCoseries rs
class GSerial f where
gSeries :: Series (f a)
gCoseries :: Series b -> Series (f a -> b)
instance GSerial f => GSerial (M1 i c f) where
gSeries = map M1 . gSeries
gCoseries rs = map (. unM1) . gCoseries rs
instance Serial c => GSerial (K1 i c) where
gSeries = map K1 . series
gCoseries rs = map (. unK1) . coseries rs
instance GSerial U1 where
gSeries = cons0 U1
gCoseries rs d = [\U1 -> b | b <- rs d]
instance (GSerial a, GSerial b) => GSerial (a :*: b) where
gSeries d = [x :*: y | x <- gSeries d, y <- gSeries d]
gCoseries rs = map uncur . gCoseries (gCoseries rs)
where
uncur f (x :*: y) = f x y
instance (GSerialSum a, GSerialSum b) => GSerial (a :+: b) where
gSeries = toList . gSeriesSum
gCoseries = gCoseriesSum
class GSerialSum f where
gSeriesSum :: DSeries (f a)
gCoseriesSum :: Series b -> Series (f a -> b)
type DSeries a = Depth -> DList a
instance (GSerialSum a, GSerialSum b) => GSerialSum (a :+: b) where
gSeriesSum d = fmap L1 (gSeriesSum d) `mappend` fmap R1 (gSeriesSum d)
gCoseriesSum rs d = [ \e -> case e of
L1 x -> f x
R1 y -> g y
| f <- gCoseriesSum rs d
, g <- gCoseriesSum rs d
]
instance GSerial f => GSerialSum (C1 c f) where
gSeriesSum d | d > 0 = fromList $ gSeries (d1)
| otherwise = mempty
gCoseriesSum rs d | d > 0 = gCoseries rs (d1)
| otherwise = [\_ -> x | x <- rs d]
#endif
instance Serial () where
series _ = [()]
coseries rs d = [ \() -> b
| b <- rs d ]
instance Serial Int where
series d = [(d)..d]
coseries rs d = [ \i -> if i > 0 then f (N (i 1))
else if i < 0 then g (N (abs i 1))
else z
| z <- alts0 rs d, f <- alts1 rs d, g <- alts1 rs d ]
instance Serial Integer where
series d = [ toInteger (i :: Int)
| i <- series d ]
coseries rs d = [ f . (fromInteger :: Integer->Int)
| f <- coseries rs d ]
newtype N a = N a
deriving (Eq, Ord)
instance Show a => Show (N a) where
show (N i) = show i
instance (Integral a, Serial a) => Serial (N a) where
series d = map N [0..d']
where
d' = fromInteger (toInteger d)
coseries rs d = [ \(N i) -> if i > 0 then f (N (i 1))
else z
| z <- alts0 rs d, f <- alts1 rs d ]
type Nat = N Int
type Natural = N Integer
instance Serial Float where
series d = [ encodeFloat sig exp
| (sig,exp) <- series d,
odd sig || sig==0 && exp==0 ]
coseries rs d = [ f . decodeFloat
| f <- coseries rs d ]
instance Serial Double where
series d = [ frac (x :: Float)
| x <- series d ]
coseries rs d = [ f . (frac :: Double->Float)
| f <- coseries rs d ]
frac :: (Real a, Fractional a, Real b, Fractional b) => a -> b
frac = fromRational . toRational
instance Serial Char where
series d = take (d+1) ['a'..'z']
coseries rs d = [ \c -> f (N (fromEnum c fromEnum 'a'))
| f <- coseries rs d ]
instance (Serial a, Serial b) =>
Serial (a,b) where
series = series >< series
coseries rs = map uncurry . (coseries $ coseries rs)
instance (Serial a, Serial b, Serial c) =>
Serial (a,b,c) where
series = \d -> [(a,b,c) | (a,(b,c)) <- series d]
coseries rs = map uncurry3 . (coseries $ coseries $ coseries rs)
instance (Serial a, Serial b, Serial c, Serial d) =>
Serial (a,b,c,d) where
series = \d -> [(a,b,c,d) | (a,(b,(c,d))) <- series d]
coseries rs = map uncurry4 . (coseries $ coseries $ coseries $ coseries rs)
uncurry3 :: (a->b->c->d) -> ((a,b,c)->d)
uncurry3 f (x,y,z) = f x y z
uncurry4 :: (a->b->c->d->e) -> ((a,b,c,d)->e)
uncurry4 f (w,x,y,z) = f w x y z
cons0 ::
a -> Series a
cons0 c _ = [c]
cons1 :: Serial a =>
(a->b) -> Series b
cons1 c d = [c z | d > 0, z <- series (d1)]
cons2 :: (Serial a, Serial b) =>
(a->b->c) -> Series c
cons2 c d = [c y z | d > 0, (y,z) <- series (d1)]
cons3 :: (Serial a, Serial b, Serial c) =>
(a->b->c->d) -> Series d
cons3 c d = [c x y z | d > 0, (x,y,z) <- series (d1)]
cons4 :: (Serial a, Serial b, Serial c, Serial d) =>
(a->b->c->d->e) -> Series e
cons4 c d = [c w x y z | d > 0, (w,x,y,z) <- series (d1)]
alts0 :: Series a ->
Series a
alts0 as d = as d
alts1 :: Serial a =>
Series b -> Series (a->b)
alts1 bs d = if d > 0 then coseries bs (dec d)
else [\_ -> x | x <- bs d]
alts2 :: (Serial a, Serial b) =>
Series c -> Series (a->b->c)
alts2 cs d = if d > 0 then coseries (coseries cs) (dec d)
else [\_ _ -> x | x <- cs d]
alts3 :: (Serial a, Serial b, Serial c) =>
Series d -> Series (a->b->c->d)
alts3 ds d = if d > 0 then coseries (coseries (coseries ds)) (dec d)
else [\_ _ _ -> x | x <- ds d]
alts4 :: (Serial a, Serial b, Serial c, Serial d) =>
Series e -> Series (a->b->c->d->e)
alts4 es d = if d > 0 then coseries (coseries (coseries (coseries es))) (dec d)
else [\_ _ _ _ -> x | x <- es d]
instance Serial Bool where
series = cons0 True \/ cons0 False
coseries rs d = [ \x -> if x then r1 else r2
| r1 <- rs d, r2 <- rs d ]
instance Serial a => Serial (Maybe a) where
series = cons0 Nothing \/ cons1 Just
coseries rs d = [ \m -> case m of
Nothing -> z
Just x -> f x
| z <- alts0 rs d ,
f <- alts1 rs d ]
instance (Serial a, Serial b) => Serial (Either a b) where
series = cons1 Left \/ cons1 Right
coseries rs d = [ \e -> case e of
Left x -> f x
Right y -> g y
| f <- alts1 rs d ,
g <- alts1 rs d ]
instance Serial a => Serial [a] where
series = cons0 [] \/ cons2 (:)
coseries rs d = [ \xs -> case xs of
[] -> y
(x:xs') -> f x xs'
| y <- alts0 rs d ,
f <- alts2 rs d ]
instance (Serial a, Serial b) => Serial (a->b) where
series = coseries series
coseries rs d =
[ \ f -> g [ f a | a <- args ]
| g <- nest args d ]
where
args = series d
nest [] _ = [ \[] -> c
| c <- rs d ]
nest (a:as) _ = [ \(b:bs) -> f b bs
| f <- coseries (nest as) d ]
depth :: Depth -> Depth -> Depth
depth d d' | d >= 0 = d'+1d
| otherwise = error "SmallCheck.depth: argument < 0"
dec :: Depth -> Depth
dec d | d > 0 = d1
| otherwise = error "SmallCheck.dec: argument <= 0"
inc :: Depth -> Depth
inc d = d+1
instance (Serial a, Show a, Show b) => Show (a->b) where
show f =
if maxarheight == 1
&& sumarwidth + length ars * length "->;" < widthLimit then
"{"++(
concat $ intersperse ";" $ [a++"->"++r | (a,r) <- ars]
)++"}"
else
concat $ [a++"->\n"++indent r | (a,r) <- ars]
where
ars = take lengthLimit [ (show x, show (f x))
| x <- series depthLimit ]
maxarheight = maximum [ max (height a) (height r)
| (a,r) <- ars ]
sumarwidth = sum [ length a + length r
| (a,r) <- ars]
indent = unlines . map (" "++) . lines
height = length . lines
(widthLimit,lengthLimit,depthLimit) = (80,20,3)::(Int,Int,Depth)