Current section
Files
Jump to
Current section
Files
src/prng@random.erl
-module(prng@random).
-compile([no_auto_import, nowarn_unused_vars]).
-export([step/2, to_iterator/2, int/2, sample/1, sample_once/1, float/2, constant/1, list/2, then/2, map/2, weighted/2, try_weighted/1, map2/3, pair/2, uniform/2, try_uniform/1, choose/2, dictionary/3, map3/4, map4/5, map5/6]).
-export_type([generator/1]).
-opaque generator(FDZ) :: {generator,
fun((prng@seed:seed()) -> {FDZ, prng@seed:seed()})}.
-spec step(generator(FEA), prng@seed:seed()) -> {FEA, prng@seed:seed()}.
step(Generator, Seed) ->
(erlang:element(2, Generator))(Seed).
-spec to_iterator(generator(FEH), prng@seed:seed()) -> gleam@iterator:iterator(FEH).
to_iterator(Generator, Seed) ->
gleam@iterator:unfold(
Seed,
fun(Seed@1) ->
{Value, New_seed} = step(Generator, Seed@1),
{next, Value, New_seed}
end
).
-spec sort_ascending(FEL, FEL, fun((FEL, FEL) -> gleam@order:order())) -> {FEL,
FEL}.
sort_ascending(One, Other, Compare) ->
case Compare(One, Other) of
lt ->
{One, Other};
eq ->
{One, Other};
gt ->
{Other, One}
end.
-spec int(integer(), integer()) -> generator(integer()).
int(From, To) ->
{generator,
fun(Seed) ->
{Low, High} = sort_ascending(From, To, fun gleam@int:compare/2),
ffi:random_int(Seed, Low, High)
end}.
-spec sample(generator(FEE)) -> gleam@iterator:iterator(FEE).
sample(Generator) ->
to_iterator(Generator, ffi:new_seed(gleam@int:random(0, 4294967296))).
-spec sample_once(generator(FEC)) -> FEC.
sample_once(Generator) ->
_assert_subject = gleam@iterator:first(sample(Generator)),
{ok, Result} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"prng/random"/utf8>>,
function => <<"sample_once"/utf8>>,
line => 122})
end,
Result.
-spec float(float(), float()) -> generator(float()).
float(From, To) ->
{generator,
fun(Seed) ->
{Low, High} = sort_ascending(From, To, fun gleam@float:compare/2),
ffi:random_float(Seed, Low, High)
end}.
-spec constant(FEN) -> generator(FEN).
constant(Value) ->
{generator, fun(Seed) -> {Value, Seed} end}.
-spec get_by_weight({float(), FFF}, list({float(), FFF}), float()) -> FFF.
get_by_weight(First, Others, Countdown) ->
{Weight, Value} = First,
case Others of
[] ->
Value;
[Second | Rest] ->
Positive_weight = gleam@float:absolute_value(Weight),
case gleam@float:compare(Countdown, Positive_weight) of
lt ->
Value;
eq ->
Value;
gt ->
get_by_weight(Second, Rest, Countdown - Positive_weight)
end
end.
-spec do_list(list(FFS), prng@seed:seed(), generator(FFS), integer()) -> {list(FFS),
prng@seed:seed()}.
do_list(Acc, Seed, Generator, Length) ->
case Length =< 0 of
true ->
{Acc, Seed};
false ->
{Value, Seed@1} = step(Generator, Seed),
do_list([Value | Acc], Seed@1, Generator, Length - 1)
end.
-spec list(generator(FFO), integer()) -> generator(list(FFO)).
list(Generator, Length) ->
{generator, fun(Seed) -> do_list([], Seed, Generator, Length) end}.
-spec then(generator(FGD), fun((FGD) -> generator(FGF))) -> generator(FGF).
then(Generator, Generator_from) ->
{generator,
fun(Seed) ->
{Value, Seed@1} = step(Generator, Seed),
_pipe = Generator_from(Value),
step(_pipe, Seed@1)
end}.
-spec map(generator(FGI), fun((FGI) -> FGK)) -> generator(FGK).
map(Generator, Fun) ->
{generator,
fun(Seed) ->
{Value, Seed@1} = step(Generator, Seed),
{Fun(Value), Seed@1}
end}.
-spec weighted({float(), FEX}, list({float(), FEX})) -> generator(FEX).
weighted(First, Others) ->
Normalise = fun(Pair) ->
gleam@float:absolute_value(gleam@pair:first(Pair))
end,
Total = Normalise(First) + gleam@float:sum(
gleam@list:map(Others, Normalise)
),
map(
float(0.0, Total),
fun(_capture) -> get_by_weight(First, Others, _capture) end
).
-spec try_weighted(list({float(), FFA})) -> {ok, generator(FFA)} | {error, nil}.
try_weighted(Options) ->
case Options of
[First | Rest] ->
{ok, weighted(First, Rest)};
[] ->
{error, nil}
end.
-spec map2(generator(FGM), generator(FGO), fun((FGM, FGO) -> FGQ)) -> generator(FGQ).
map2(One, Other, Fun) ->
{generator,
fun(Seed) ->
{A, Seed@1} = step(One, Seed),
{B, Seed@2} = step(Other, Seed@1),
{Fun(A, B), Seed@2}
end}.
-spec pair(generator(FFJ), generator(FFL)) -> generator({FFJ, FFL}).
pair(One, Other) ->
map2(One, Other, fun gleam@pair:new/2).
-spec uniform(FEP, list(FEP)) -> generator(FEP).
uniform(First, Others) ->
weighted(
{1.0, First},
gleam@list:map(
Others,
fun(_capture) -> gleam@pair:new(1.0, _capture) end
)
).
-spec try_uniform(list(FES)) -> {ok, generator(FES)} | {error, nil}.
try_uniform(Options) ->
case Options of
[First | Rest] ->
{ok, uniform(First, Rest)};
[] ->
{error, nil}
end.
-spec choose(FFH, FFH) -> generator(FFH).
choose(One, Other) ->
uniform(One, [Other]).
-spec dictionary(integer(), generator(FFW), generator(FFY)) -> generator(gleam@map:map_(FFW, FFY)).
dictionary(Size, Keys, Values) ->
_pipe = pair(Keys, Values),
_pipe@1 = list(_pipe, Size),
map(_pipe@1, fun gleam@map:from_list/1).
-spec map3(
generator(FGS),
generator(FGU),
generator(FGW),
fun((FGS, FGU, FGW) -> FGY)
) -> generator(FGY).
map3(One, Two, Three, Fun) ->
{generator,
fun(Seed) ->
{A, Seed@1} = step(One, Seed),
{B, Seed@2} = step(Two, Seed@1),
{C, Seed@3} = step(Three, Seed@2),
{Fun(A, B, C), Seed@3}
end}.
-spec map4(
generator(FHA),
generator(FHC),
generator(FHE),
generator(FHG),
fun((FHA, FHC, FHE, FHG) -> FHI)
) -> generator(FHI).
map4(One, Two, Three, Four, Fun) ->
{generator,
fun(Seed) ->
{A, Seed@1} = step(One, Seed),
{B, Seed@2} = step(Two, Seed@1),
{C, Seed@3} = step(Three, Seed@2),
{D, Seed@4} = step(Four, Seed@3),
{Fun(A, B, C, D), Seed@4}
end}.
-spec map5(
generator(FHK),
generator(FHM),
generator(FHO),
generator(FHQ),
generator(FHS),
fun((FHK, FHM, FHO, FHQ, FHS) -> FHU)
) -> generator(FHU).
map5(One, Two, Three, Four, Five, Fun) ->
{generator,
fun(Seed) ->
{A, Seed@1} = step(One, Seed),
{B, Seed@2} = step(Two, Seed@1),
{C, Seed@3} = step(Three, Seed@2),
{D, Seed@4} = step(Four, Seed@3),
{E, Seed@5} = step(Five, Seed@4),
{Fun(A, B, C, D, E), Seed@5}
end}.