module Foundation.Check.Arbitrary
( Arbitrary(..)
, frequency
, oneof
, elements
, between
) where
import Basement.Imports
import Foundation.Primitive
import Basement.Nat
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(..))
import Foundation.Check.Gen
import Foundation.Random
import Foundation.Bits
import Foundation.Collection
import Foundation.Numerical
import Control.Monad (replicateM)
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
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 (integralCast i) arbitrary)
instance Arbitrary AsciiString where
arbitrary = genWithParams $ \params ->
fromList <$> (genMax (genMaxSizeString params) >>= \i -> replicateM (integralCast 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 (integralCast i) 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 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 = integralCast <$> 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 (integralCast sz) arbitrary
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 _ _ = 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) = (+) x <$> genMax range
where range = y x
genMax :: Word -> Gen Word
genMax m = flip mod m <$> arbitrary