11module Test.QuickCheck.LCG
22 ( Seed ()
3+ , mkSeed
4+ , runSeed
35 , lcgM
46 , lcgC
57 , lcgN
@@ -16,8 +18,6 @@ import Data.Int (fromNumber, toNumber)
1618import Data.Int.Bits (shl )
1719import qualified Data.Maybe.Unsafe as U
1820
19- type Seed = Int
20-
2121-- | The *multiplier*: a magic constant for the linear congruential generator
2222lcgM :: Int
2323lcgM = 48271
@@ -34,26 +34,41 @@ lcgN :: Int
3434lcgN = 2147483647
3535
3636-- | Step the linear congruential generator
37- lcgNext :: Int -> Int
38- lcgNext n = U .fromJust $ fromNumber $ (toNumber lcgM * n' + toNumber lcgC) % toNumber lcgN
37+ lcgNext :: Seed -> Seed
38+ lcgNext = Seed <<< go <<< runSeed
3939 where
40- -- Ensure that the input is between seedMin and seedMax; the LCG will not
41- -- work well for other inputs.
42- n' = ensureBetween (toNumber seedMin) (toNumber seedMax) (toNumber n)
43-
44- ensureBetween :: Number -> Number -> Number -> Number
45- ensureBetween min max n =
46- let rangeSize = max - min
47- in (((n % rangeSize) + rangeSize) % rangeSize) + min
40+ go n = U .fromJust $ fromNumber $ (toNumber lcgM * toNumber n + toNumber lcgC) % toNumber lcgN
4841
4942-- | Create a random seed
5043randomSeed :: forall e . Eff (random :: RANDOM | e ) Seed
51- randomSeed = randomInt seedMin seedMax
44+ randomSeed = mkSeed <$> randomInt seedMin seedMax
5245
5346-- | The minimum permissible Seed value.
54- seedMin :: Seed
47+ seedMin :: Int
5548seedMin = 1
5649
5750-- | The maximum permissible Seed value.
58- seedMax :: Seed
51+ seedMax :: Int
5952seedMax = lcgM - 1
53+
54+ -- | A seed for the linear congruential generator. We omit a `Semiring`
55+ -- | instance because there is no `zero` value, as 0 is not an acceptable
56+ -- | seed for the generator.
57+ newtype Seed = Seed Int
58+
59+ mkSeed :: Int -> Seed
60+ mkSeed x = Seed (ensureBetween seedMin seedMax x)
61+
62+ runSeed :: Seed -> Int
63+ runSeed (Seed x) = x
64+
65+ ensureBetween :: Int -> Int -> Int -> Int
66+ ensureBetween min max n =
67+ let rangeSize = max - min
68+ in (((n `mod` rangeSize) + rangeSize) `mod` rangeSize) + min
69+
70+ instance showSeed :: Show Seed where
71+ show (Seed x) = " Seed " <> show x
72+
73+ instance eqSeed :: Eq Seed where
74+ eq (Seed x) (Seed y) = eq x y
0 commit comments