module Foundation.Check.Arbitrary
( Arbitrary(..)
, frequency
, oneof
, elements
, between
) where
import Foundation.Internal.Base
import Foundation.Internal.Natural
import Foundation.Primitive
import Foundation.Primitive.IntegralConv (wordToChar)
import Foundation.Primitive.Floating
import Foundation.Check.Gen
import Foundation.Random
import Foundation.Bits
import Foundation.Collection
import Foundation.Array
import Foundation.Numerical
import Foundation.String
import Control.Monad (replicateM)
class Arbitrary a where
arbitrary :: Gen a
instance Arbitrary Integer where
arbitrary = arbitraryInteger
instance Arbitrary Natural where
arbitrary = arbitraryNatural
instance Arbitrary Int where
arbitrary = arbitraryPrimtype
instance Arbitrary Word where
arbitrary = arbitraryPrimtype
instance Arbitrary Word64 where
arbitrary = arbitraryPrimtype
instance Arbitrary Word32 where
arbitrary = arbitraryPrimtype
instance Arbitrary Word16 where
arbitrary = arbitraryPrimtype
instance Arbitrary Word8 where
arbitrary = arbitraryPrimtype
instance Arbitrary Int64 where
arbitrary = arbitraryPrimtype
instance Arbitrary Int32 where
arbitrary = arbitraryPrimtype
instance Arbitrary Int16 where
arbitrary = arbitraryPrimtype
instance Arbitrary Int8 where
arbitrary = arbitraryPrimtype
instance Arbitrary Char where
arbitrary = arbitraryChar
instance Arbitrary Bool where
arbitrary = flip testBit 0 <$> (arbitraryPrimtype :: Gen Word)
instance Arbitrary String where
arbitrary = genWithParams $ \params ->
fromList <$> (genMax (genMaxSizeString params) >>= \i -> replicateM (integralCast i) arbitrary)
instance Arbitrary Float where
arbitrary = toFloat <$> arbitrary <*> arbitrary <*> arbitrary
where toFloat i n Nothing = integerToFloat i + (naturalToFloat n / 100000)
toFloat i n (Just e) = (integerToFloat i + (naturalToFloat n / 1000000)) * (integerToFloat e)
instance Arbitrary Double where
arbitrary = toDouble <$> arbitrary <*> arbitrary <*> arbitrary
where toDouble i n Nothing = integerToDouble i + (naturalToDouble n / 100000)
toDouble i n (Just e) = (integerToDouble i + (naturalToDouble n / 1000000)) * (integerToDouble e)
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
arbitraryInteger :: Gen Integer
arbitraryInteger =
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 (\x -> 0 x) 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)
]
arbitraryPrimtype :: PrimType ty => Gen ty
arbitraryPrimtype = genWithRng getRandomPrimType
arbitraryUArrayOf :: PrimType ty => Word -> Gen (UArray ty)
arbitraryUArrayOf size =
between (0, size) >>= \sz -> (fromList <$> replicateM (integralCast sz) arbitraryPrimtype)
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 (nk)
pickOne _ _ = internalError "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) = (+) x <$> genMax range
where range = y x
genMax :: Word -> Gen Word
genMax m = (flip mod m) <$> arbitraryPrimtype