{-# LANGUAGE ExplicitForAll #-}
module Faker.Combinators where

import Control.Monad
import Data.Foldable
import Data.List (sort)
import Faker
import System.Random

-- | Generates a random element in the given inclusive range.
--
-- @
-- λ> item \<- 'generate' $ 'fromRange' (1,10)
-- λ> item
-- 2
-- @
--
fromRange :: forall a m. (Monad m, Random a) => (a, a) -> FakeT m a
fromRange :: (a, a) -> FakeT m a
fromRange (a, a)
rng =
  (FakerSettings -> m a) -> FakeT m a
forall (m :: * -> *) a. (FakerSettings -> m a) -> FakeT m a
FakeT
    (\FakerSettings
r ->
       let (a
x, StdGen
_) = (a, a) -> StdGen -> (a, StdGen)
forall a g. (Random a, RandomGen g) => (a, a) -> g -> (a, g)
randomR (a, a)
rng (FakerSettings -> StdGen
getRandomGen FakerSettings
r)
        in a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x)

-- | Generates a random element over the natural range of `a`.
--
-- @
-- λ> import Data.Word
-- λ> item :: forall a m. Word8 \<- 'generate' 'pickAny'
-- λ> item
-- 57
-- @
--
pickAny :: forall a m. (Monad m, Random a) => FakeT m a
pickAny :: FakeT m a
pickAny =
  (FakerSettings -> m a) -> FakeT m a
forall (m :: * -> *) a. (FakerSettings -> m a) -> FakeT m a
FakeT
    (\FakerSettings
settings ->
       let (a
x, StdGen
_) = StdGen -> (a, StdGen)
forall a g. (Random a, RandomGen g) => g -> (a, g)
random (FakerSettings -> StdGen
getRandomGen FakerSettings
settings)
        in a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x)

-- | Tries to generate a value that satisfies a predicate.
--
-- @
-- λ> import qualified Faker.Address as AD
-- λ> item :: forall a m. Text \<- 'generate' $ 'suchThatMaybe' AD.country (\x -> (T.length x > 5))
-- λ> item
-- Just Ecuador
-- @
--
suchThatMaybe :: forall a m. Monad m => FakeT m a -> (a -> Bool) -> FakeT m (Maybe a)
FakeT m a
gen suchThatMaybe :: FakeT m a -> (a -> Bool) -> FakeT m (Maybe a)
`suchThatMaybe` a -> Bool
p = do
  a
x <- FakeT m a
gen
  Maybe a -> FakeT m (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a -> FakeT m (Maybe a)) -> Maybe a -> FakeT m (Maybe a)
forall a b. (a -> b) -> a -> b
$
    if a -> Bool
p a
x
      then a -> Maybe a
forall a. a -> Maybe a
Just a
x
      else Maybe a
forall a. Maybe a
Nothing

-- | Generates a value that satisfies a predicate.
--
-- @
-- λ> import qualified Faker.Address as AD
-- λ> item :: forall a m. Text \<- 'generate' $ 'suchThat' AD.country (\\x -> (T.length x > 5))
-- λ> item
-- Ecuador
-- @
--
suchThat :: forall a m. Monad m => FakeT m a -> (a -> Bool) -> FakeT m a
FakeT m a
gen suchThat :: FakeT m a -> (a -> Bool) -> FakeT m a
`suchThat` a -> Bool
p = do
  Maybe a
mx <- FakeT m a
gen FakeT m a -> (a -> Bool) -> FakeT m (Maybe a)
forall a (m :: * -> *).
Monad m =>
FakeT m a -> (a -> Bool) -> FakeT m (Maybe a)
`suchThatMaybe` a -> Bool
p
  case Maybe a
mx of
    Just a
x -> a -> FakeT m a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x
    Maybe a
Nothing -> FakeT m a
gen FakeT m a -> (a -> Bool) -> FakeT m a
forall a (m :: * -> *).
Monad m =>
FakeT m a -> (a -> Bool) -> FakeT m a
`suchThat` a -> Bool
p

-- | Randomly uses one of the given generators. The input structure
-- must be non-empty.
--
-- @
-- λ> import qualified Faker.Address as FA
-- λ> let fakes = [FA.country, FA.postcode, FA.state]
-- λ> generate (oneof fakes)
-- Montana
-- @
--
oneof :: forall t a m. (Monad m, Foldable t) => t (FakeT m a) -> FakeT m a
oneof :: t (FakeT m a) -> FakeT m a
oneof t (FakeT m a)
xs = FakeT m a
helper
  where
    items :: [FakeT m a]
items = t (FakeT m a) -> [FakeT m a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList t (FakeT m a)
xs
    helper :: FakeT m a
helper =
      case [FakeT m a]
items of
        [] -> [Char] -> FakeT m a
forall a. HasCallStack => [Char] -> a
error [Char]
"Faker.Combinators.oneof should be non-empty"
        [FakeT m a]
xs' -> (Int, Int) -> FakeT m Int
forall a (m :: * -> *). (Monad m, Random a) => (a, a) -> FakeT m a
fromRange (Int
0, [FakeT m a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FakeT m a]
xs' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) FakeT m Int -> (Int -> FakeT m a) -> FakeT m a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ([FakeT m a]
items [FakeT m a] -> Int -> FakeT m a
forall a. [a] -> Int -> a
!!)

-- | Generates one of the given values. The input list must be non-empty.
--
-- @
-- λ> let fakeInt = elements [1..100]
-- λ> generate fakeInt
-- 22
-- @
--
elements :: forall t a m. (Monad m, Foldable t) => t a -> FakeT m a
elements :: t a -> FakeT m a
elements t a
xs =
  case [a]
items of
    [] -> [Char] -> FakeT m a
forall a. HasCallStack => [Char] -> a
error [Char]
"Faker.Combinators.element used with empty list"
    [a]
ys -> ([a]
ys [a] -> Int -> a
forall a. [a] -> Int -> a
!!) (Int -> a) -> FakeT m Int -> FakeT m a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` (Int, Int) -> FakeT m Int
forall a (m :: * -> *). (Monad m, Random a) => (a, a) -> FakeT m a
fromRange (Int
0, t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
xs Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
  where
    items :: [a]
items = t a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList t a
xs

-- | Generates a list of the given length.
listOf :: forall a m. Monad m => Int -> FakeT m a -> FakeT m [a]
listOf :: Int -> FakeT m a -> FakeT m [a]
listOf = Int -> FakeT m a -> FakeT m [a]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM

-- | Generates an ordered list.
orderedList :: forall a m. (Monad m, Ord a) => Int -> FakeT m a -> FakeT m [a]
orderedList :: Int -> FakeT m a -> FakeT m [a]
orderedList Int
n FakeT m a
gen = [a] -> [a]
forall a. Ord a => [a] -> [a]
sort ([a] -> [a]) -> FakeT m [a] -> FakeT m [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> FakeT m a -> FakeT m [a]
forall a (m :: * -> *). Monad m => Int -> FakeT m a -> FakeT m [a]
listOf Int
n FakeT m a
gen

-- | Chooses one of the given generators, with a weighted random distribution.
-- The input list must be non-empty.
frequency :: forall a m. Monad m => [(Int, FakeT m a)] -> FakeT m a
frequency :: [(Int, FakeT m a)] -> FakeT m a
frequency [] = [Char] -> FakeT m a
forall a. HasCallStack => [Char] -> a
error [Char]
"Faker.Combinators.frequency used with empty list"
frequency [(Int, FakeT m a)]
xs0 = (Int, Int) -> FakeT m Int
forall a (m :: * -> *). (Monad m, Random a) => (a, a) -> FakeT m a
fromRange (Int
1, Int
tot) FakeT m Int -> (Int -> FakeT m a) -> FakeT m a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (Int -> [(Int, FakeT m a)] -> FakeT m a
forall t p. (Ord t, Num t) => t -> [(t, p)] -> p
`pick` [(Int, FakeT m a)]
xs0)
  where
    tot :: Int
tot = [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (((Int, FakeT m a) -> Int) -> [(Int, FakeT m a)] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int, FakeT m a) -> Int
forall a b. (a, b) -> a
fst [(Int, FakeT m a)]
xs0)
    pick :: t -> [(t, p)] -> p
pick t
n ((t
k, p
x):[(t, p)]
xs)
      | t
n t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
k = p
x
      | Bool
otherwise = t -> [(t, p)] -> p
pick (t
n t -> t -> t
forall a. Num a => a -> a -> a
- t
k) [(t, p)]
xs
    pick t
_ [(t, p)]
_ = [Char] -> p
forall a. HasCallStack => [Char] -> a
error [Char]
"FakeT m.pick used with empty list"

-- | Generate a value of an enumeration in the range [from, to].
--
-- @since 0.2.0
--
-- @
-- λ> data Animal = Cat | Dog | Zebra | Elephant | Giarfee deriving (Eq,Ord,Enum, Show)
-- λ> generate (fakeEnumFromTo Cat Zebra)
-- Zebra
-- @
--
fakeEnumFromTo :: forall a m. (Monad m, Enum a) => a -> a -> FakeT m a
fakeEnumFromTo :: a -> a -> FakeT m a
fakeEnumFromTo a
from a
to = Int -> a
forall a. Enum a => Int -> a
toEnum (Int -> a) -> FakeT m Int -> FakeT m a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Int, Int) -> FakeT m Int
forall a (m :: * -> *). (Monad m, Random a) => (a, a) -> FakeT m a
fromRange (a -> Int
forall a. Enum a => a -> Int
fromEnum a
from, a -> Int
forall a. Enum a => a -> Int
fromEnum a
to)

-- | A sumtype can just use this function directly. Defined as
-- fakeBoundedEnum = `fakeEnumFromTo` `minBound` `maxBound`
--
-- @since 0.7.1
--
fakeBoundedEnum :: forall a m. (Monad m, Enum a, Bounded a) => FakeT m a
fakeBoundedEnum :: FakeT m a
fakeBoundedEnum = a -> a -> FakeT m a
forall a (m :: * -> *). (Monad m, Enum a) => a -> a -> FakeT m a
fakeEnumFromTo a
forall a. Bounded a => a
minBound a
forall a. Bounded a => a
maxBound