This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

Random

This library helps you generate pseudo-random values.

This library is all about building generators for whatever type of values you need. There are a bunch of primitive generators like bool and int that you can build up into fancier generators with functions like list and map.

It may be helpful to read about JSON decoders because they work very similarly.

Note: This is an implementation of the Portable Combined Generator of L'Ecuyer for 32-bit computers. It is almost a direct translation from the System.Random module. It has a period of roughly 2.30584e18.

Generators

type Generator a = Generator (Seed -> (a, Seed))

A Generator is like a recipe for generating certain random values. So a Generator Int describes how to generate integers and a Generator String describes how to generate strings.

To actually run a generator and produce the random values, you need to use functions like generate and initialSeed.

Primitive Generators

bool : Generator Bool

Create a generator that produces boolean values. The following example simulates a coin flip that may land heads or tails.

type Flip = Heads | Tails

coinFlip : Generator Flip
coinFlip =
    map (\b -> if b then Heads else Tails) bool
int : Int -> Int -> Generator Int

Generate 32-bit integers in a given range.

int 0 10   -- an integer between zero and ten
int -5 5   -- an integer between -5 and 5

int minInt maxInt  -- an integer in the widest range feasible

This function can produce values outside of the range [minInt, maxInt] but sufficient randomness is not guaranteed.

float : Float -> Float -> Generator Float

Generate floats in a given range. The following example is a generator that produces decimals between 0 and 1.

probability : Generator Float
probability =
    float 0 1

Data Structure Generators

pair : Generator a -> Generator b -> Generator (a,b)

Create a pair of random values. A common use of this might be to generate a point in a certain 2D space. Imagine we have a collage that is 400 pixels wide and 200 pixels tall.

randomPoint : Generator (Int,Int)
randomPoint =
    pair (int -200 200) (int -100 100)
list : Int -> Generator a -> Generator (List a)

Create a list of random values.

floatList : Generator (List Float)
floatList =
    list 10 (float 0 1)

intList : Generator (List Int)
intList =
    list 5 (int 0 100)

intPairs : Generator (List (Int, Int))
intPairs =
    list 10 <| pair (int 0 100) (int 0 100)

Custom Generators

map : (a -> b) -> Generator a -> Generator b

Transform the values produced by a generator. The following examples show how to generate booleans and letters based on a basic integer generator.

bool : Generator Bool
bool =
  map ((==) 1) (int 0 1)

lowercaseLetter : Generator Char
lowercaseLetter =
  map (\n -> Char.fromCode (n + 97)) (int 0 25)

uppercaseLetter : Generator Char
uppercaseLetter =
  map (\n -> Char.fromCode (n + 65)) (int 0 25)
map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c

Combine two generators.

This function is used to define things like pair where you want to put two generators together.

pair : Generator a -> Generator b -> Generator (a,b)
pair genA genB =
  map2 (,) genA genB
map3 : (a -> b -> c -> d) -> Generator a -> Generator b -> Generator c -> Generator d

Combine three generators. This could be used to produce random colors.

import Color

rgb : Generator Color.Color
rgb =
  map3 Color.rgb (int 0 255) (int 0 255) (int 0 255)

hsl : Generator Color.Color
hsl =
  map3 Color.hsl (map degrees (int 0 360)) (float 0 1) (float 0 1)
map4 : (a -> b -> c -> d -> e) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e

Combine four generators.

map5 : (a -> b -> c -> d -> e -> f) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e -> Generator f

Combine five generators.

andThen : Generator a -> (a -> Generator b) -> Generator b

Chain random operations, threading through the seed. In the following example, we will generate a random letter by putting together uppercase and lowercase letters.

letter : Generator Char
letter =
  bool `andThen` \b ->
    if b then uppercaseLetter else lowercaseLetter

-- bool : Generator Bool
-- uppercaseLetter : Generator Char
-- lowercaseLetter : Generator Char

Generate Values

generate : (a -> msg) -> Generator a -> Cmd msg

Create a command that will generate random values.

Read more about how to use this in your programs in The Elm Architecture tutorial which has a section specifically about random values.

Generate Values Manually

step : Generator a -> Seed -> (a, Seed)

Generate a random value as specified by a given Generator.

In the following example, we are trying to generate a number between 0 and 100 with the int 0 100 generator. Each time we call step we need to provide a seed. This will produce a random number and a new seed to use if we want to run other generators later.

So here it is done right, where we get a new seed from each step call and thread that through.

seed0 = initialSeed 31415

-- step (int 0 100) seed0 ==> (42, seed1)
-- step (int 0 100) seed1 ==> (31, seed2)
-- step (int 0 100) seed2 ==> (99, seed3)

Notice that we use different seeds on each line. This is important! If you use the same seed, you get the same results.

-- step (int 0 100) seed0 ==> (42, seed1)
-- step (int 0 100) seed0 ==> (42, seed1)
-- step (int 0 100) seed0 ==> (42, seed1)
type Seed = Seed { state : State , next : State -> (Int, State) , split : State -> (State, State) , range : State -> (Int,Int) }

A Seed is the source of randomness in this whole system. Whenever you want to use a generator, you need to pair it with a seed.

initialSeed : Int -> Seed

Create a “seed” of randomness which makes it possible to generate random values. If you use the same seed many times, it will result in the same thing every time! A good way to get an unexpected seed is to use the current time.

Constants

maxInt : Int

The maximum value for randomly generated 32-bit ints: 2147483647

minInt : Int

The minimum value for randomly generated 32-bit ints: -2147483648

effect module Random where { command = MyCmd } exposing
  ( Generator, Seed
  , bool, int, float
  , list, pair
  , map, map2, map3, map4, map5
  , andThen
  , minInt, maxInt
  , generate
  , step, initialSeed
  )

{-| This library helps you generate pseudo-random values.

This library is all about building [`generators`](#Generator) for whatever
type of values you need. There are a bunch of primitive generators like
[`bool`](#bool) and [`int`](#int) that you can build up into fancier
generators with functions like [`list`](#list) and [`map`](#map).

It may be helpful to [read about JSON decoders][json] because they work very
similarly.

[json]: https://evancz.gitbooks.io/an-introduction-to-elm/content/interop/json.html

> *Note:* This is an implementation of the Portable Combined Generator of
L'Ecuyer for 32-bit computers. It is almost a direct translation from the
[System.Random](http://hackage.haskell.org/package/random-1.0.1.1/docs/System-Random.html)
module. It has a period of roughly 2.30584e18.

# Generators
@docs Generator

# Primitive Generators
@docs bool, int, float

# Data Structure Generators
@docs pair, list

# Custom Generators
@docs map, map2, map3, map4, map5, andThen

# Generate Values
@docs generate

# Generate Values Manually
@docs step, Seed, initialSeed

# Constants
@docs maxInt, minInt

-}

import Basics exposing (..)
import List exposing ((::))
import Platform
import Platform.Cmd exposing (Cmd)
import Task exposing (Task)
import Time



-- PRIMITIVE GENERATORS


{-| Create a generator that produces boolean values. The following example
simulates a coin flip that may land heads or tails.

    type Flip = Heads | Tails

    coinFlip : Generator Flip
    coinFlip =
        map (\b -> if b then Heads else Tails) bool
-}
bool : Generator Bool
bool =
  map ((==) 1) (int 0 1)


{-| Generate 32-bit integers in a given range.

    int 0 10   -- an integer between zero and ten
    int -5 5   -- an integer between -5 and 5

    int minInt maxInt  -- an integer in the widest range feasible

This function *can* produce values outside of the range [[`minInt`](#minInt),
[`maxInt`](#maxInt)] but sufficient randomness is not guaranteed.
-}
int : Int -> Int -> Generator Int
int a b =
  Generator <| \(Seed seed) ->
    let
      (lo,hi) =
        if a < b then (a,b) else (b,a)

      k = hi - lo + 1
      -- 2^31 - 87
      base = 2147483561
      n = iLogBase base k

      f n acc state =
        case n of
          0 -> (acc, state)
          _ ->
            let
              (x, state') = seed.next state
            in
              f (n - 1) (x + acc * base) state'

      (v, state') =
        f n 1 seed.state
    in
      ( lo + v % k
      , Seed { seed | state = state' }
      )


iLogBase : Int -> Int -> Int
iLogBase b i =
  if i < b then
    1
  else
    1 + iLogBase b (i // b)


{-| The maximum value for randomly generated 32-bit ints: 2147483647 -}
maxInt : Int
maxInt =
  2147483647


{-| The minimum value for randomly generated 32-bit ints: -2147483648 -}
minInt : Int
minInt =
  -2147483648


{-| Generate floats in a given range. The following example is a generator
that produces decimals between 0 and 1.

    probability : Generator Float
    probability =
        float 0 1
-}
float : Float -> Float -> Generator Float
float a b =
  Generator <| \seed ->
    let
      (lo, hi) =
        if a < b then (a,b) else (b,a)

      (number, newSeed) =
        step (int minInt maxInt) seed

      negativeOneToOne =
        toFloat number / toFloat (maxInt - minInt)

      scaled =
        (lo+hi)/2 + ((hi-lo) * negativeOneToOne)
    in
      (scaled, newSeed)



-- DATA STRUCTURES


{-| Create a pair of random values. A common use of this might be to generate
a point in a certain 2D space. Imagine we have a collage that is 400 pixels
wide and 200 pixels tall.

    randomPoint : Generator (Int,Int)
    randomPoint =
        pair (int -200 200) (int -100 100)

-}
pair : Generator a -> Generator b -> Generator (a,b)
pair genA genB =
  map2 (,) genA genB


{-| Create a list of random values.

    floatList : Generator (List Float)
    floatList =
        list 10 (float 0 1)

    intList : Generator (List Int)
    intList =
        list 5 (int 0 100)

    intPairs : Generator (List (Int, Int))
    intPairs =
        list 10 <| pair (int 0 100) (int 0 100)
-}
list : Int -> Generator a -> Generator (List a)
list n (Generator generate) =
  Generator <| \seed ->
    listHelp [] n generate seed


listHelp : List a -> Int -> (Seed -> (a,Seed)) -> Seed -> (List a, Seed)
listHelp list n generate seed =
  if n < 1 then
    (List.reverse list, seed)

  else
    let
      (value, newSeed) =
        generate seed
    in
      listHelp (value :: list) (n-1) generate newSeed



-- CUSTOM GENERATORS


{-| Transform the values produced by a generator. The following examples show
how to generate booleans and letters based on a basic integer generator.

    bool : Generator Bool
    bool =
      map ((==) 1) (int 0 1)

    lowercaseLetter : Generator Char
    lowercaseLetter =
      map (\n -> Char.fromCode (n + 97)) (int 0 25)

    uppercaseLetter : Generator Char
    uppercaseLetter =
      map (\n -> Char.fromCode (n + 65)) (int 0 25)

-}
map : (a -> b) -> Generator a -> Generator b
map func (Generator genA) =
  Generator <| \seed0 ->
    let
      (a, seed1) = genA seed0
    in
      (func a, seed1)


{-| Combine two generators.

This function is used to define things like [`pair`](#pair) where you want to
put two generators together.

    pair : Generator a -> Generator b -> Generator (a,b)
    pair genA genB =
      map2 (,) genA genB

-}
map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c
map2 func (Generator genA) (Generator genB) =
  Generator <| \seed0 ->
    let
      (a, seed1) = genA seed0
      (b, seed2) = genB seed1
    in
      (func a b, seed2)


{-| Combine three generators. This could be used to produce random colors.

    import Color

    rgb : Generator Color.Color
    rgb =
      map3 Color.rgb (int 0 255) (int 0 255) (int 0 255)

    hsl : Generator Color.Color
    hsl =
      map3 Color.hsl (map degrees (int 0 360)) (float 0 1) (float 0 1)
-}
map3 : (a -> b -> c -> d) -> Generator a -> Generator b -> Generator c -> Generator d
map3 func (Generator genA) (Generator genB) (Generator genC) =
  Generator <| \seed0 ->
    let
      (a, seed1) = genA seed0
      (b, seed2) = genB seed1
      (c, seed3) = genC seed2
    in
      (func a b c, seed3)


{-| Combine four generators.
-}
map4 : (a -> b -> c -> d -> e) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e
map4 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) =
  Generator <| \seed0 ->
    let
      (a, seed1) = genA seed0
      (b, seed2) = genB seed1
      (c, seed3) = genC seed2
      (d, seed4) = genD seed3
    in
      (func a b c d, seed4)


{-| Combine five generators.
-}
map5 : (a -> b -> c -> d -> e -> f) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e -> Generator f
map5 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) (Generator genE) =
  Generator <| \seed0 ->
    let
      (a, seed1) = genA seed0
      (b, seed2) = genB seed1
      (c, seed3) = genC seed2
      (d, seed4) = genD seed3
      (e, seed5) = genE seed4
    in
      (func a b c d e, seed5)


{-| Chain random operations, threading through the seed. In the following
example, we will generate a random letter by putting together uppercase and
lowercase letters.

    letter : Generator Char
    letter =
      bool `andThen` \b ->
        if b then uppercaseLetter else lowercaseLetter

    -- bool : Generator Bool
    -- uppercaseLetter : Generator Char
    -- lowercaseLetter : Generator Char
-}
andThen : Generator a -> (a -> Generator b) -> Generator b
andThen (Generator generate) callback =
  Generator <| \seed ->
    let
      (result, newSeed) =
        generate seed

      (Generator genB) =
        callback result
    in
      genB newSeed



-- IMPLEMENTATION


{-| A `Generator` is like a recipe for generating certain random values. So a
`Generator Int` describes how to generate integers and a `Generator String`
describes how to generate strings.

To actually *run* a generator and produce the random values, you need to use
functions like [`generate`](#generate) and [`initialSeed`](#initialSeed).
-}
type Generator a =
    Generator (Seed -> (a, Seed))


type State = State Int Int


{-| A `Seed` is the source of randomness in this whole system. Whenever
you want to use a generator, you need to pair it with a seed.
-}
type Seed =
  Seed
    { state : State
    , next  : State -> (Int, State)
    , split : State -> (State, State)
    , range : State -> (Int,Int)
    }


{-| Generate a random value as specified by a given `Generator`.

In the following example, we are trying to generate a number between 0 and 100
with the `int 0 100` generator. Each time we call `step` we need to provide a
seed. This will produce a random number and a *new* seed to use if we want to
run other generators later.

So here it is done right, where we get a new seed from each `step` call and
thread that through.

    seed0 = initialSeed 31415

    -- step (int 0 100) seed0 ==> (42, seed1)
    -- step (int 0 100) seed1 ==> (31, seed2)
    -- step (int 0 100) seed2 ==> (99, seed3)

Notice that we use different seeds on each line. This is important! If you use
the same seed, you get the same results.

    -- step (int 0 100) seed0 ==> (42, seed1)
    -- step (int 0 100) seed0 ==> (42, seed1)
    -- step (int 0 100) seed0 ==> (42, seed1)
-}
step : Generator a -> Seed -> (a, Seed)
step (Generator generator) seed =
  generator seed


{-| Create a &ldquo;seed&rdquo; of randomness which makes it possible to
generate random values. If you use the same seed many times, it will result
in the same thing every time! A good way to get an unexpected seed is to use
the current time.
-}
initialSeed : Int -> Seed
initialSeed n =
  Seed
    { state = initState n
    , next = next
    , split = split
    , range = range
    }


{-| Produce the initial generator state. Distinct arguments should be likely
to produce distinct generator states.
-}
initState : Int -> State
initState s' =
  let
    s = max s' -s'
    q  = s // (magicNum6-1)
    s1 = s %  (magicNum6-1)
    s2 = q %  (magicNum7-1)
  in
    State (s1+1) (s2+1)


magicNum0 = 40014
magicNum1 = 53668
magicNum2 = 12211
magicNum3 = 52774
magicNum4 = 40692
magicNum5 = 3791
magicNum6 = 2147483563
magicNum7 = 2147483399
magicNum8 = 2147483562


next : State -> (Int, State)
next (State s1 s2) =
  -- Div always rounds down and so random numbers are biased
  -- ideally we would use division that rounds towards zero so
  -- that in the negative case it rounds up and in the positive case
  -- it rounds down. Thus half the time it rounds up and half the time it
  -- rounds down
  let
    k = s1 // magicNum1
    s1' = magicNum0 * (s1 - k * magicNum1) - k * magicNum2
    s1'' = if s1' < 0 then s1' + magicNum6 else s1'
    k' = s2 // magicNum3
    s2' = magicNum4 * (s2 - k' * magicNum3) - k' * magicNum5
    s2'' = if s2' < 0 then s2' + magicNum7 else s2'
    z = s1'' - s2''
    z' = if z < 1 then z + magicNum8 else z
  in
    (z', State s1'' s2'')


split : State -> (State, State)
split (State s1 s2 as std) =
  let
    new_s1 =
      if s1 == magicNum6-1 then 1 else s1 + 1

    new_s2 =
      if s2 == 1 then magicNum7-1 else s2 - 1

    (State t1 t2) =
      snd (next std)
  in
    (State new_s1 t2, State t1 new_s2)


range : State -> (Int,Int)
range _ =
    (0, magicNum8)



-- MANAGER


{-| Create a command that will generate random values.

Read more about how to use this in your programs in [The Elm Architecture
tutorial][arch] which has a section specifically [about random values][rand].

[arch]: https://evancz.gitbooks.io/an-introduction-to-elm/content/architecture/index.html
[rand]: https://evancz.gitbooks.io/an-introduction-to-elm/content/architecture/effects/random.html
-}
generate : (a -> msg) -> Generator a -> Cmd msg
generate tagger generator =
  command (Generate (map tagger generator))


type MyCmd msg = Generate (Generator msg)


cmdMap : (a -> b) -> MyCmd a -> MyCmd b
cmdMap func (Generate generator) =
  Generate (map func generator)


init : Task Never Seed
init =
  Time.now `Task.andThen` \t ->
    Task.succeed (initialSeed (round t))


onEffects : Platform.Router msg Never -> List (MyCmd msg) -> Seed -> Task Never Seed
onEffects router commands seed =
  case commands of
    [] ->
      Task.succeed seed

    Generate generator :: rest ->
      let
        (value, newSeed) =
          step generator seed
      in
        Platform.sendToApp router value
          `Task.andThen` \_ ->

        onEffects router rest newSeed


onSelfMsg : Platform.Router msg Never -> Never -> Seed -> Task Never Seed
onSelfMsg _ _ seed =
  Task.succeed seed