1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE CPP #-}
module Foundation.Check.Arbitrary
( Arbitrary(..)
, frequency
, oneof
, elements
, between
) where
import Basement.Imports
import Foundation.Primitive
import Basement.Nat
import Basement.Cast (cast)
import Basement.IntegralConv
import Basement.Bounded
import Basement.Types.OffsetSize
import qualified Basement.Types.Char7 as Char7
import Basement.Types.Word128 (Word128(..))
import Basement.Types.Word256 (Word256(..))
#if __GLASGOW_HASKELL__ >= 710
import qualified Basement.Sized.List as ListN
#endif
import Foundation.Check.Gen
import Foundation.Random
import Foundation.Bits
import Foundation.Collection
import Foundation.Numerical
import Control.Monad (replicateM)
-- | How to generate an arbitrary value for 'a'
class Arbitrary a where
arbitrary :: Gen a
instance Arbitrary Integer where
arbitrary = arbitraryInteger
instance Arbitrary Natural where
arbitrary = arbitraryNatural
instance (NatWithinBound Word64 n, KnownNat n) => Arbitrary (Zn64 n) where
arbitrary = zn64 <$> arbitrary
instance KnownNat n => Arbitrary (Zn n) where
arbitrary = zn <$> arbitraryNatural
-- prim types
instance Arbitrary Int where
arbitrary = int64ToInt <$> arbitraryInt64
instance Arbitrary Word where
arbitrary = word64ToWord <$> arbitraryWord64
instance Arbitrary Word256 where
arbitrary = Word256 <$> arbitraryWord64 <*> arbitraryWord64 <*> arbitraryWord64 <*> arbitraryWord64
instance Arbitrary Word128 where
arbitrary = Word128 <$> arbitraryWord64 <*> arbitraryWord64
instance Arbitrary Word64 where
arbitrary = arbitraryWord64
instance Arbitrary Word32 where
arbitrary = integralDownsize <$> arbitraryWord64
instance Arbitrary Word16 where
arbitrary = integralDownsize <$> arbitraryWord64
instance Arbitrary Word8 where
arbitrary = integralDownsize <$> arbitraryWord64
instance Arbitrary Int64 where
arbitrary = arbitraryInt64
instance Arbitrary Int32 where
arbitrary = integralDownsize <$> arbitraryInt64
instance Arbitrary Int16 where
arbitrary = integralDownsize <$> arbitraryInt64
instance Arbitrary Int8 where
arbitrary = integralDownsize <$> arbitraryInt64
instance Arbitrary Char where
arbitrary = arbitraryChar
instance Arbitrary Char7 where
arbitrary = Char7.fromByteMask . integralDownsize <$> arbitraryWord64
instance Arbitrary (CountOf ty) where
arbitrary = CountOf <$> arbitrary
instance Arbitrary Bool where
arbitrary = flip testBit 0 <$> arbitraryWord64
instance Arbitrary String where
arbitrary = genWithParams $ \params ->
fromList <$> (genMax (genMaxSizeString params) >>= \i -> replicateM (cast i) arbitrary)
instance Arbitrary AsciiString where
arbitrary = genWithParams $ \params ->
fromList <$> (genMax (genMaxSizeString params) >>= \i -> replicateM (cast i) arbitrary)
instance Arbitrary Float where
arbitrary = arbitraryF32
instance Arbitrary Double where
arbitrary = arbitraryF64
instance Arbitrary a => Arbitrary (Maybe a) where
arbitrary = frequency $ nonEmpty_ [ (1, pure Nothing), (4, Just <$> arbitrary) ]
instance (Arbitrary l, Arbitrary r) => Arbitrary (Either l r) where
arbitrary = oneof $ nonEmpty_ [ Left <$> arbitrary, Right <$> arbitrary ]
instance (Arbitrary a, Arbitrary b)
=> Arbitrary (a,b) where
arbitrary = (,) <$> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b, Arbitrary c)
=> Arbitrary (a,b,c) where
arbitrary = (,,) <$> arbitrary <*> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)
=> Arbitrary (a,b,c,d) where
arbitrary = (,,,) <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e)
=> Arbitrary (a,b,c,d,e) where
arbitrary = (,,,,) <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e, Arbitrary f)
=> Arbitrary (a,b,c,d,e,f) where
arbitrary = (,,,,,) <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
instance Arbitrary a => Arbitrary [a] where
arbitrary = genWithParams $ \params ->
fromList <$> (genMax (genMaxSizeArray params) >>= \i -> replicateM (cast i) arbitrary)
#if __GLASGOW_HASKELL__ >= 710
instance (Arbitrary a, KnownNat n, NatWithinBound Int n) => Arbitrary (ListN.ListN n a) where
arbitrary = ListN.replicateM arbitrary
#endif
arbitraryInteger :: Gen Integer
arbitraryInteger =
-- TODO use the sized parameter
frequency $ nonEmpty_
[ (4, integerOfSize True 2)
, (4, integerOfSize False 2)
, (4, integerOfSize True 4)
, (4, integerOfSize False 4)
, (2, integerOfSize True 8)
, (2, integerOfSize False 8)
, (1, integerOfSize True 16)
, (1, integerOfSize False 16)
]
where
integerOfSize :: Bool -> Word -> Gen Integer
integerOfSize toSign n = ((if toSign then negate else id) . foldl' (\x y -> x + integralUpsize y) 0 . toList)
<$> (arbitraryUArrayOf n :: Gen (UArray Word8))
arbitraryNatural :: Gen Natural
arbitraryNatural = integralDownsize . abs <$> arbitraryInteger
arbitraryChar :: Gen Char
arbitraryChar = frequency $ nonEmpty_
[ (6, wordToChar <$> genMax 128)
, (1, wordToChar <$> genMax 0x10ffff)
]
arbitraryWord64 :: Gen Word64
arbitraryWord64 = genWithRng getRandomWord64
arbitraryInt64 :: Gen Int64
arbitraryInt64 = cast <$> arbitraryWord64
arbitraryF64 :: Gen Double
arbitraryF64 = genWithRng getRandomF64
arbitraryF32 :: Gen Float
arbitraryF32 = genWithRng getRandomF32
arbitraryUArrayOf :: (PrimType ty, Arbitrary ty) => Word -> Gen (UArray ty)
arbitraryUArrayOf size = between (0, size) >>=
\sz -> fromList <$> replicateM (cast sz) arbitrary
-- | Call one of the generator weighted
frequency :: NonEmpty [(Word, Gen a)] -> Gen a
frequency (getNonEmpty -> l) = between (0, sum) >>= pickOne l
where
sum :: Word
!sum = foldl' (+) 0 $ fmap fst l
pickOne ((k,x):xs) n
| n <= k = x
| otherwise = pickOne xs (n-k)
pickOne _ _ = error "frequency"
oneof :: NonEmpty [Gen a] -> Gen a
oneof ne = frequency (nonEmptyFmap (\x -> (1, x)) ne)
elements :: NonEmpty [a] -> Gen a
elements l = frequency (nonEmptyFmap (\x -> (1, pure x)) l)
between :: (Word, Word) -> Gen Word
between (x,y)
| range == 0 = pure x
| otherwise = (+) x <$> genMax range
where range = y - x
genMax :: Word -> Gen Word
genMax m = flip mod m <$> arbitrary
|