-- | Extraction d’aléa.
--
-- NOTE: Afin de ne produire que des bits qui ont chacun
-- une probabilité d’un sur deux d’être à 'True' ou à 'False',
-- les fonctions de ce module n’extraient que les bits
-- des combinaisons de rang lexicographique strictement inférieur
-- à la plus grande puissance de 2 inférieure ou égale
-- au nombre de combinaisons possibles.
-- Car il n’y a que @2^n@ combinaisons de @n@ bits.
-- Et que parmi ces combinaisons un bit a une probabilité
-- de @2^(n-1)@ sur @2^n@ soit de @1/2@ d’être à 'True', et autant d’être à 'False'.
module Reloto.Random where

import Data.Bool
import Data.Eq (Eq(..))
import Data.Foldable (all)
import Data.Int (Int)
import Data.List ((++), length, nub, sort)
import Data.Ord (Ord(..))
import Prelude (Integer, Num(..), undefined, (^))

import Reloto.Bits
import Reloto.Combin
import Reloto.Sequence

-- | @equiprobableBits n@ retourne le nombre maximal de bits de 'i'
-- équiprobables quand @i@ parcourt @[0..n-1]@.
--
-- Ce nombre est le plus grand 'b' dans @[0..]@ tel que @2^b-1 <= n@.
--
-- @
-- 'equiprobableBits' '<$>' [0..17] == [0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4]
-- @
equiprobableBits :: Integer -> Int
equiprobableBits :: Integer -> Int
equiprobableBits Integer
n | Integer
n Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
2Integer -> Int -> Integer
forall a b. (Num a, Integral b) => a -> b -> a
^Int
bInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-Integer
1 = Int
b
                   | Bool
otherwise  = Int
bInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1
                   where b :: Int
b = Integer -> Int
bitSize Integer
n

-- | @randomOfCombin n k c@ retourne des bits équiprobables donnés
-- par la combinaison 'c' obtenue par tirage équiprobable
-- d’une combinaison de 'k' entiers parmi @[1..n]@.
--
-- WARNING: aucun bit n’est extrait du tirage 'c'
-- dans le cas où 'c' a un rang lexicographique encodé par
-- un nombre de bits strictement supérieur à @'equiprobableBits' (n`nCk`k)@.
randomOfCombin :: Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin :: Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
n Integer
k [Integer]
xs
 | Integer
0Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n, Integer
0Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
k, Integer
kInteger -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n
 , (Integer -> Bool) -> [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\Integer
x -> Integer
1Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
xBool -> Bool -> Bool
&&Integer
xInteger -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n) [Integer]
xs
 , [Integer] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Integer] -> [Integer]
forall a. Eq a => [a] -> [a]
nub [Integer]
xs) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [Integer] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Integer]
xs =
  if Integer -> Int
bitSize Integer
rank Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
epBits
  then Int
epBits Int -> Integer -> [Bool]
`bitsOfInteger` Integer
rank
  else []
  where rank :: Integer
rank   = Integer -> [Integer] -> Integer
forall i. Integral i => i -> [i] -> i
rankOfCombin Integer
n ([Integer] -> [Integer]
forall a. Ord a => [a] -> [a]
sort [Integer]
xs)
        epBits :: Int
epBits = Integer -> Int
equiprobableBits (Integer
nInteger -> Integer -> Integer
forall i. Integral i => i -> i -> i
`nCk`Integer
k)
randomOfCombin Integer
_ Integer
_ [Integer]
_ = [Bool]
forall a. HasCallStack => a
undefined

-- | @randomOfSequence n k a@ retourne des bits équiprobables donnés
-- par l’arrangement 'a' obtenue par tirage équiprobable
-- d’une combinaison de 'k' entiers parmi @[1..n]@.
--
-- WARNING: aucun bit n’est extrait du tirage 'a'
-- dans le cas où 'a' a un rang lexicographique encodé par
-- un nombre de bits strictement supérieur à @'equiprobableBits' (n`nAk`k)@.
randomOfSequence :: Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence :: Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence Integer
n Integer
k [Integer]
xs
 | Integer
0Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n, Integer
0Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
k, Integer
kInteger -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n
 , (Integer -> Bool) -> [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\Integer
x -> Integer
1Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
xBool -> Bool -> Bool
&&Integer
xInteger -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<=Integer
n) [Integer]
xs
 , [Integer] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Integer] -> [Integer]
forall a. Eq a => [a] -> [a]
nub [Integer]
xs) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== [Integer] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Integer]
xs =
  if Integer -> Int
bitSize Integer
rank Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
epBits
  then Int
epBits Int -> Integer -> [Bool]
`bitsOfInteger` Integer
rank
  else []
  where rank :: Integer
rank   = Integer -> [Integer] -> Integer
forall i. Integral i => i -> [i] -> i
rankOfSequence Integer
n ([Integer] -> [Integer]
forall a. Ord a => [a] -> [a]
sort [Integer]
xs)
        epBits :: Int
epBits = Integer -> Int
equiprobableBits (Integer
nInteger -> Integer -> Integer
forall i. Integral i => i -> i -> i
`nAk`Integer
k)
randomOfSequence Integer
_ Integer
_ [Integer]
_ = [Bool]
forall a. HasCallStack => a
undefined

-- * Aléas publics

-- | @randomOf6aus49 nums numComplementaire@ retourne les bits équiprobables donnés
-- par un tirage du <https://www.lotto.de/de/ergebnisse/lotto-6aus49/archiv.html 6aus49>.
--
-- Il peut produire @26@ bits équiprobables :
-- @'sum' $ 'equiprobableBits' '<$>' [49\`nCk\`6, 10\`nCk\`1]@
--
-- @
-- 'randomOf6aus49' (1,2,3,4,5,6)      1 == 'replicate' (23+3) False
-- 'randomOf6aus49' (7,14,20,30,37,45) 8 == 'replicate' (23+3) True
-- 
-- 'combinOfRank' 49 6 (2 ^ 'equiprobableBits' (49`nCk`6) - 1) == [7,14,20,30,37,45]
-- 'combinOfRank' 49 6 (2 ^ 'equiprobableBits' (49`nCk`6))     == [7,14,20,30,37,46]
-- 'randomOf6aus49' (7,14,20,30,37,45) 1 == 'replicate' 23 True ++ 'replicate' 3 False
-- 'randomOf6aus49' (7,14,20,30,37,46) 1 == [False,False,False]
-- 
-- 'combinOfRank' 10 1 (2 ^ 'equiprobableBits' (10`nCk`1) - 1) == [8]
-- 'combinOfRank' 10 1 (2 ^ 'equiprobableBits' (10`nCk`1))     == [9]
-- 'randomOf6aus49' (7,14,20,30,37,46) 8 == [True,True,True]
-- 'randomOf6aus49' (7,14,20,30,37,46) 9 == []
-- @
randomOf6aus49 :: (Integer,Integer,Integer,Integer,Integer,Integer) -> Integer -> [Bool]
randomOf6aus49 :: (Integer, Integer, Integer, Integer, Integer, Integer)
-> Integer -> [Bool]
randomOf6aus49 (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5,Integer
n6) Integer
nc = Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
49 Integer
6 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5,Integer
n6] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                        Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
10 Integer
1 [Integer
nc]

-- | @randomOfEuroMillions nums numComplementaires@ retourne les bits équiprobables donnés
-- par un tirage de l’<https://www.fdj.fr/jeux/jeux-de-tirage/euromillions/resultats EuroMillions>.
--
-- Il peut produire @27@ bits équiprobables :
-- @'sum' $ 'equiprobableBits' '<$>' [50\`nCk\`5, 12\`nCk\`2]@
--
-- @
-- 'randomOfEuroMillions' (1,2,3,4,5)      (1,2) == 'replicate' (21+5) False
-- 'randomOfEuroMillions' (29,36,38,41,48) (1,9) == 'replicate' (21+5) True
-- 
-- 'combinOfRank' 50 5 (2 ^ 'equiprobableBits' (50`nCk`5) - 1) == [29,36,38,41,48]
-- 'combinOfRank' 50 5 (2 ^ 'equiprobableBits' (50`nCk`5))     == [29,36,38,41,49]
-- 'randomOfEuroMillions' (29,36,38,41,48) (1,2) == 'replicate' 21 True ++ 'replicate' 6 False
-- 'randomOfEuroMillions' (29,36,38,41,49) (1,2) == [False,False,False,False,False,False]
-- 
-- 'combinOfRank' 12 2 (2 ^ 'equiprobableBits' (12`nCk`2) - 1) == [10,11]
-- 'combinOfRank' 12 2 (2 ^ 'equiprobableBits' (12`nCk`2))     == [10,12]
-- 'randomOfEuroMillions' (29,36,38,41,49) (10,11)  == [True,True,True,True,True,True]
-- 'randomOfEuroMillions' (29,36,38,41,49) (10,12) == []
-- @
randomOfEuroMillions :: (Integer,Integer,Integer,Integer,Integer) -> (Integer,Integer) -> [Bool]
randomOfEuroMillions :: (Integer, Integer, Integer, Integer, Integer)
-> (Integer, Integer) -> [Bool]
randomOfEuroMillions (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5) (Integer
nc1,Integer
nc2) = Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
50 Integer
5 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                                  Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
12 Integer
2 [Integer
nc1,Integer
nc2]
-- | @34@ bits équiprobables en allant voir l’ordre du tirage :
-- @'sum' $ 'equiprobableBits' '<$>' [50\`nAk\`5, 12\`nAk\`2]@
randomOfOrderedEuroMillions :: (Integer,Integer,Integer,Integer,Integer) -> (Integer,Integer) -> [Bool]
randomOfOrderedEuroMillions :: (Integer, Integer, Integer, Integer, Integer)
-> (Integer, Integer) -> [Bool]
randomOfOrderedEuroMillions (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5) (Integer
nc1,Integer
nc2) = Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence Integer
50 Integer
5 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                                         Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence Integer
12 Integer
2 [Integer
nc1,Integer
nc2]

-- | @randomOfFrenchLoto nums numComplementaire@ retourne les bits équiprobables donnés
-- par un tirage du <https://www.fdj.fr/jeux/jeux-de-tirage/loto/resultats/ Loto Français>.
--
-- Il peut produire @23@ bits équiprobables :
-- @'sum' $ 'equiprobableBits' '<$>' [49\`nCk\`5, 10\`nCk\`1]@
--
-- @
-- 'randomOfFrenchLoto' (1,2,3,4,5)     1 == 'replicate' (20+3) False
-- 'randomOfFrenchLoto' (7,27,36,40,46) 8 == 'replicate' (20+3) True
-- 
-- 'combinOfRank' 49 5 (2 ^ 'equiprobableBits' (49`nCk`5) - 1) == [7,27,36,40,46]
-- 'combinOfRank' 49 5 (2 ^ 'equiprobableBits' (49`nCk`5))     == [7,27,36,40,47]
-- 'randomOfFrenchLoto' (7,27,36,40,46) 1 == 'replicate' 20 True ++ 'replicate' 3 False
-- 'randomOfFrenchLoto' (7,27,36,40,47) 1 == [False,False,False]
-- 
-- 'combinOfRank' 10 1 (2 ^ 'equiprobableBits' (10`nCk`1) - 1) == [8]
-- 'combinOfRank' 10 1 (2 ^ 'equiprobableBits' (10`nCk`1))     == [9]
-- 'randomOfFrenchLoto' (7,27,36,40,47) 8 == [True,True,True]
-- 'randomOfFrenchLoto' (7,27,36,40,47) 9 == []
-- @
randomOfFrenchLoto :: (Integer,Integer,Integer,Integer,Integer) -> Integer -> [Bool]
randomOfFrenchLoto :: (Integer, Integer, Integer, Integer, Integer) -> Integer -> [Bool]
randomOfFrenchLoto (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5) Integer
nc = Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
49 Integer
5 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                         Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
10 Integer
1 [Integer
nc]
-- | @30@ bits équiprobables en allant voir l’ordre du tirage :
-- @'sum' $ 'equiprobableBits' '<$>' [49\`nAk\`5, 10\`nAk\`1]@
randomOfOrderedFrenchLoto :: (Integer,Integer,Integer,Integer,Integer) -> Integer -> [Bool]
randomOfOrderedFrenchLoto :: (Integer, Integer, Integer, Integer, Integer) -> Integer -> [Bool]
randomOfOrderedFrenchLoto (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5) Integer
nc = Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence Integer
49 Integer
5 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                                Integer -> Integer -> [Integer] -> [Bool]
randomOfSequence Integer
10 Integer
1 [Integer
nc]

-- | @randomOfSwissLoto nums numComplementaire@ retourne les bits équiprobables donnés
-- par un tirage du <https://jeux.loro.ch/FR/1/SwissLoto#action=game-history SwissLoto>.
--
-- Il peut produire @24@ bits équiprobables :
-- @'sum' $ 'equiprobableBits' '<$>' [42\`nCk\`6, 6\`nCk\`1]@
--
-- @
-- 'randomOfSwissLoto' (1,2,3,4,5,6)       1 == 'replicate' (22+2) False
-- 'randomOfSwissLoto' (10,12,25,28,33,38) 4 == 'replicate' (22+2) True
-- 
-- 'combinOfRank' 42 6 (2 ^ 'equiprobableBits' (42`nCk`6) - 1) == [10,12,25,28,33,38]
-- 'combinOfRank' 42 6 (2 ^ 'equiprobableBits' (42`nCk`6))     == [10,12,25,28,33,39]
-- 'randomOfSwissLoto' (10,12,25,28,33,38) 1 == 'replicate' 22 True ++ 'replicate' 2 False
-- 'randomOfSwissLoto' (10,12,25,28,33,39) 1 == [False,False]
-- 
-- 'combinOfRank' 6 1 (2 ^ 'equiprobableBits' (6`nCk`1) - 1) == [4]
-- 'combinOfRank' 6 1 (2 ^ 'equiprobableBits' (6`nCk`1))     == [5]
-- 'randomOfSwissLoto' (10,12,25,28,33,39) 4 == [True,True]
-- 'randomOfSwissLoto' (10,12,25,28,33,39) 5 == []
-- @
randomOfSwissLoto :: (Integer,Integer,Integer,Integer,Integer,Integer) -> Integer -> [Bool]
randomOfSwissLoto :: (Integer, Integer, Integer, Integer, Integer, Integer)
-> Integer -> [Bool]
randomOfSwissLoto (Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5,Integer
n6) Integer
nc = Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin Integer
42 Integer
6 [Integer
n1,Integer
n2,Integer
n3,Integer
n4,Integer
n5,Integer
n6] [Bool] -> [Bool] -> [Bool]
forall a. [a] -> [a] -> [a]
++
                                           Integer -> Integer -> [Integer] -> [Bool]
randomOfCombin  Integer
6 Integer
1 [Integer
nc]