From e1a74e3077ca870520a748f29dd7c4b9115ce090 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 3 Apr 2017 12:29:23 +0200 Subject: Clean up documentation and test cases --- lib/crypto/doc/src/crypto.xml | 35 +++++++++++++--------- lib/stdlib/doc/src/rand.xml | 43 +++++++++++++++++++------- lib/stdlib/src/rand.erl | 68 ++++++++++++++++++++++++++---------------- lib/stdlib/test/rand_SUITE.erl | 33 ++++++++++++++------ 4 files changed, 120 insertions(+), 59 deletions(-) (limited to 'lib') diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 30835a8447..552d95d7dc 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -732,14 +732,17 @@ rand_seed() -> rand:state() - Strong random number generation plugin state> - - Creates state object for random number generation, - in order to generate cryptographically strong random numbers - (based on OpenSSL's BN_rand_range), - and saves it on process dictionary before returning it as well. - See also rand:seed/1 - + Strong random number generation plugin state + +

+ Creates state object for + random number generation, + in order to generate cryptographically strong random numbers + (based on OpenSSL's BN_rand_range), + and saves it on process dictionary before returning it as well. + See also + rand:seed/1. +

Example

 _ = crypto:rand_seed(),
@@ -750,12 +753,16 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[
rand_seed_s() -> rand:state() - Strong random number generation plugin state> - - Creates state object for random number generation, - in order to generate cryptographically strongly random numbers - (based on OpenSSL's BN_rand_range). - See also rand:seed_s/1 + Strong random number generation plugin state + +

+ Creates state object for + random number generation, + in order to generate cryptographically strongly random numbers + (based on OpenSSL's BN_rand_range). + See also + rand:seed_s/1. +

diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml index e7a5fb7fab..2ddf3021ac 100644 --- a/lib/stdlib/doc/src/rand.xml +++ b/lib/stdlib/doc/src/rand.xml @@ -139,7 +139,19 @@ S0 = rand:seed_s(exsplus), - + + + + +

Algorithm specific internal state

+
+ + +

Algorithm specific internal state

+
+ + +

Algorithm specific internal state

@@ -147,8 +159,11 @@ S0 = rand:seed_s(exsplus), -

Algorithm-dependent state that can be printed or saved to - file.

+ +

+ Algorithm-dependent state that can be printed or saved to file. +

+
@@ -223,8 +238,11 @@ S0 = rand:seed_s(exsplus), Seed random number generator. -

Seeds random number generation with the specifed algorithm and - time-dependent data if AlgOrStateOrExpState is an algorithm.

+

+ Seeds random number generation with the specifed algorithm and + time-dependent data if AlgOrStateOrExpState + is an algorithm. +

Otherwise recreates the exported seed in the process dictionary, and returns the state. See also export_seed/0.

@@ -244,8 +262,11 @@ S0 = rand:seed_s(exsplus), Seed random number generator. -

Seeds random number generation with the specifed algorithm and - time-dependent data if AlgOrStateOrExpState is an algorithm.

+

+ Seeds random number generation with the specifed algorithm and + time-dependent data if AlgOrStateOrExpState + is an algorithm. +

Otherwise recreates the exported seed and returns the state. See also export_seed/0.

@@ -266,7 +287,7 @@ S0 = rand:seed_s(exsplus), Return a random float.

Returns a random float uniformly distributed in the value - range 0.0 < X < 1.0 and + range 0.0 =< X < 1.0 and updates the state in the process dictionary.

@@ -277,7 +298,7 @@ S0 = rand:seed_s(exsplus),

Returns, for a specified integer N >= 1, a random integer uniformly distributed in the value range - 1 <= X <= N and + 1 =< X =< N and updates the state in the process dictionary.

@@ -287,7 +308,7 @@ S0 = rand:seed_s(exsplus), Return a random float.

Returns, for a specified state, random float - uniformly distributed in the value range 0.0 < + uniformly distributed in the value range 0.0 =< X < 1.0 and a new state.

@@ -298,7 +319,7 @@ S0 = rand:seed_s(exsplus),

Returns, for a specified integer N >= 1 and a state, a random integer uniformly distributed in the value - range 1 <= X <= N and a + range 1 =< X =< N and a new state.

diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl index 60da53cd2b..dfd102f9ef 100644 --- a/lib/stdlib/src/rand.erl +++ b/lib/stdlib/src/rand.erl @@ -45,22 +45,31 @@ %% ===================================================================== %% This depends on the algorithm handler function --type alg_seed() :: exs64_state() | exsplus_state() | exs1024_state() | term(). +-type alg_state() :: + exs64_state() | exsplus_state() | exs1024_state() | term(). %% This is the algorithm handler function within this module --type alg_handler() :: #{type := alg(), - max := integer() | infinity, - next := fun((alg_seed()) -> {uint64(), alg_seed()}), - uniform := fun((state()) -> {float(), state()}), - uniform_n := fun((pos_integer(), state()) -> {pos_integer(), state()}), - jump := fun((state()) -> state())}. +-type alg_handler() :: + #{type := alg(), + max := integer() | infinity, + next := + fun((alg_state()) -> {non_neg_integer(), alg_state()}), + uniform := + fun((state()) -> {float(), state()}), + uniform_n := + fun((pos_integer(), state()) -> {pos_integer(), state()}), + jump := + fun((state()) -> state())}. %% Algorithm state --type state() :: {alg_handler(), alg_seed()}. +-type state() :: {alg_handler(), alg_state()}. -type builtin_alg() :: exs64 | exsplus | exs1024. -type alg() :: builtin_alg() | atom(). --type export_state() :: {alg(), alg_seed()}. --export_type([builtin_alg/0, alg/0, alg_handler/0, alg_seed/0, state/0, export_state/0]). +-type export_state() :: {alg(), alg_state()}. +-export_type( + [builtin_alg/0, alg/0, alg_handler/0, alg_state/0, + state/0, export_state/0]). +-export_type([exs64_state/0, exsplus_state/0, exs1024_state/0]). %% ===================================================================== %% API @@ -74,7 +83,7 @@ export_seed() -> _ -> undefined end. --spec export_seed_s(state()) -> export_state(). +-spec export_seed_s(State :: state()) -> export_state(). export_seed_s({#{type:=Alg}, Seed}) -> {Alg, Seed}. %% seed(Alg) seeds RNG with runtime dependent values @@ -83,11 +92,15 @@ export_seed_s({#{type:=Alg}, Seed}) -> {Alg, Seed}. %% seed({Alg,Seed}) setup RNG with a previously exported seed %% and return the NEW state --spec seed(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state(). +-spec seed( + AlgOrStateOrExpState :: builtin_alg() | state() | export_state()) -> + state(). seed(Alg) -> seed_put(seed_s(Alg)). --spec seed_s(AlgOrStateOrExpState::builtin_alg() | state() | export_state()) -> state(). +-spec seed_s( + AlgOrStateOrExpState :: builtin_alg() | state() | export_state()) -> + state(). seed_s({AlgHandler, _Seed} = State) when is_map(AlgHandler) -> State; seed_s({Alg0, Seed}) -> @@ -101,11 +114,15 @@ seed_s(Alg) -> %% seed/2: seeds RNG with the algorithm and given values %% and returns the NEW state. --spec seed(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state(). +-spec seed( + Alg :: builtin_alg(), Seed :: {integer(), integer(), integer()}) -> + state(). seed(Alg0, S0) -> seed_put(seed_s(Alg0, S0)). --spec seed_s(Alg :: builtin_alg(), {integer(), integer(), integer()}) -> state(). +-spec seed_s( + Alg :: builtin_alg(), Seed :: {integer(), integer(), integer()}) -> + state(). seed_s(Alg0, S0 = {_, _, _}) -> {Alg, Seed} = mk_alg(Alg0), AS = Seed(S0), @@ -117,7 +134,7 @@ seed_s(Alg0, S0 = {_, _, _}) -> %% uniform/0: returns a random float X where 0.0 < X < 1.0, %% updating the state in the process dictionary. --spec uniform() -> X::float(). +-spec uniform() -> X :: float(). uniform() -> {X, Seed} = uniform_s(seed_get()), _ = seed_put(Seed), @@ -127,7 +144,7 @@ uniform() -> %% uniform/1 returns a random integer X where 1 =< X =< N, %% updating the state in the process dictionary. --spec uniform(N :: pos_integer()) -> X::pos_integer(). +-spec uniform(N :: pos_integer()) -> X :: pos_integer(). uniform(N) -> {X, Seed} = uniform_s(N, seed_get()), _ = seed_put(Seed), @@ -137,7 +154,7 @@ uniform(N) -> %% returns a random float X where 0.0 < X < 1.0, %% and a new state. --spec uniform_s(state()) -> {X::float(), NewS :: state()}. +-spec uniform_s(State :: state()) -> {X :: float(), NewState :: state()}. uniform_s(State = {#{uniform:=Uniform}, _}) -> Uniform(State). @@ -145,7 +162,8 @@ uniform_s(State = {#{uniform:=Uniform}, _}) -> %% uniform_s/2 returns a random integer X where 1 =< X =< N, %% and a new state. --spec uniform_s(N::pos_integer(), state()) -> {X::pos_integer(), NewS::state()}. +-spec uniform_s(N :: pos_integer(), State :: state()) -> + {X :: pos_integer(), NewState :: state()}. uniform_s(N, State = {#{uniform_n:=Uniform, max:=Max}, _}) when 0 < N, N =< Max -> Uniform(N, State); @@ -159,7 +177,7 @@ uniform_s(N, State0 = {#{uniform:=Uniform}, _}) %% after a large number of call defined for each algorithm. %% The large number is algorithm dependent. --spec jump(state()) -> NewS :: state(). +-spec jump(state()) -> NewState :: state(). jump(State = {#{jump:=Jump}, _}) -> Jump(State). @@ -168,7 +186,7 @@ jump(State = {#{jump:=Jump}, _}) -> %% and write back the new value to the internal state, %% then returns the new value. --spec jump() -> NewS :: state(). +-spec jump() -> NewState :: state(). jump() -> seed_put(jump(seed_get())). @@ -186,7 +204,7 @@ normal() -> %% The Ziggurat Method for generating random variables - Marsaglia and Tsang %% Paper and reference code: http://www.jstatsoft.org/v05/i08/ --spec normal_s(state()) -> {float(), NewS :: state()}. +-spec normal_s(State :: state()) -> {float(), NewState :: state()}. normal_s(State0) -> {Sign, R, State} = get_52(State0), Idx = R band 16#FF, @@ -249,7 +267,7 @@ mk_alg(exs1024) -> %% Reference URL: http://xorshift.di.unimi.it/ %% ===================================================================== --type exs64_state() :: uint64(). +-opaque exs64_state() :: uint64(). exs64_seed({A1, A2, A3}) -> {V1, _} = exs64_next(((A1 band ?UINT32MASK) * 4294967197 + 1)), @@ -284,7 +302,7 @@ exs64_jump(_) -> %% Modification of the original Xorshift128+ algorithm to 116 %% by Sebastiano Vigna, a lot of thanks for his help and work. %% ===================================================================== --type exsplus_state() :: nonempty_improper_list(uint58(), uint58()). +-opaque exsplus_state() :: nonempty_improper_list(uint58(), uint58()). -dialyzer({no_improper_lists, exsplus_seed/1}). @@ -353,7 +371,7 @@ exsplus_jump(S, [AS0|AS1], J, N) -> %% Reference URL: http://xorshift.di.unimi.it/ %% ===================================================================== --type exs1024_state() :: {list(uint64()), list(uint64())}. +-opaque exs1024_state() :: {list(uint64()), list(uint64())}. exs1024_seed({A1, A2, A3}) -> B1 = (((A1 band ?UINT21MASK) + 1) * 2097131) band ?UINT21MASK, diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl index fe5eaccda5..098eefeb61 100644 --- a/lib/stdlib/test/rand_SUITE.erl +++ b/lib/stdlib/test/rand_SUITE.erl @@ -356,14 +356,23 @@ basic_normal_1(0, {#{type:=Alg}, _}, Sum, SumSq) -> %% Test that the user can write algorithms. plugin(Config) when is_list(Config) -> - _ = lists:foldl(fun(_, S0) -> - {V1, S1} = rand:uniform_s(10000, S0), - true = is_integer(V1), - {V2, S2} = rand:uniform_s(S1), - true = is_float(V2), - S2 - end, crypto_seed(), lists:seq(1, 200)), - ok. + try crypto:strong_rand_bytes(1) of + <<_>> -> + _ = lists:foldl( + fun(_, S0) -> + {V1, S1} = rand:uniform_s(10000, S0), + true = is_integer(V1), + {V2, S2} = rand:uniform_s(S1), + true = is_float(V2), + S2 + end, crypto_seed(), lists:seq(1, 200)), + ok + catch + error:low_entropy -> + {skip,low_entropy}; + error:undef -> + {skip,no_crypto} + end. %% Test implementation crypto_seed() -> @@ -397,7 +406,13 @@ crypto_uniform_n(N, State0) -> measure(Suite) when is_atom(Suite) -> []; measure(_Config) -> ct:timetrap({minutes,15}), %% valgrind needs a lot of time - Algos = [crypto64|algs()], + Algos = + try crypto:strong_rand_bytes(1) of + <<_>> -> [crypto64] + catch + error:low_entropy -> []; + error:undef -> [] + end ++ algs(), io:format("RNG uniform integer performance~n",[]), _ = measure_1(random, fun(State) -> {int, random:uniform_s(10000, State)} end), _ = [measure_1(Algo, fun(State) -> {int, rand:uniform_s(10000, State)} end) || Algo <- Algos], -- cgit v1.2.3