File 2084-Implement-crypto_prng1-as-chipher-keystream.patch of Package erlang

From 7a33461f40d89867563f1dec53ba9ea9fe8271d1 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Thu, 4 Dec 2025 17:03:35 +0100
Subject: [PATCH 4/7] Implement crypto_prng1 as chipher keystream

---
 lib/crypto/src/crypto.erl      | 514 ++++++++++++++++++++++++++++-----
 lib/stdlib/src/rand.erl        |  11 +-
 lib/stdlib/test/rand_SUITE.erl | 368 ++++++++++++++++-------
 3 files changed, 709 insertions(+), 184 deletions(-)

diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index 324bee6b6d..038a227c3d 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -630,8 +630,9 @@ for every possible N for the range.
 %% of the generated bits.  The lowest bits from the range
 %% functions still have the generator's quality.
 %%
--type alg_handler() ::
-        #{type := alg(),
+-type alg_handler() :: alg_handler(alg()).
+-type alg_handler(Alg) ::
+        #{type := Alg,
           bits => non_neg_integer(),
           weak_low_bits => 0..3,
           max => non_neg_integer(), % Deprecated
@@ -677,7 +678,7 @@ for seeding to get some uniqueness.
 """.
 -type seed() :: [integer()] | integer() | {integer(), integer(), integer()}.
 -export_type(
-   [builtin_alg/0, alg/0, alg_handler/0, alg_state/0,
+   [builtin_alg/0, alg/0, alg_handler/0, alg_handler/1, alg_state/0,
     state/0, export_state/0, seed/0]).
 -export_type(
    [exsplus_state/0, exro928_state/0, exrop_state/0, exs1024_state/0,
@@ -1488,8 +1489,8 @@ as required to compose the `t:binary/0`.  Returns the generated
 > function cannot do.
 >
 > Alas, when this function is based on a PRNG that produces random integers,
-> such as all in this module's [algorithms](#algorithms) section,
-> bytes has to created from integers, which becomes rather slow.
+> such as any in this module's [algorithms](#algorithms) section,
+> bytes have to be created from integers, which becomes rather slow.
 >
 > A plug-in generator may implement a dedicated callback
 > for generating bytes, to mitigate this problem, which in that case
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index e5ca05b432..759f326319 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -40,7 +40,7 @@
     reference/1,
     uniform_real_conv/1,
     plugin/1, measure/1,
-    short_jump/1
+    short_jump/1, initial_jump/1
    ]).
 
 %% Manual test functions
@@ -65,7 +65,7 @@ all() ->
      uniform_real_conv,
      plugin, measure,
      {group, reference_jump},
-     short_jump,
+     short_jump, initial_jump,
      {group, shuffle},
      doctests
     ].
@@ -117,7 +117,7 @@ algs() ->
 all_algs() ->
     [default | algs()] ++
         case crypto_support() of
-            ok -> [crypto_aes];
+            ok -> [crypto_prng1, crypto_aes];
             _  -> []
         end.
 
@@ -132,13 +132,49 @@ crypto_support() ->
             no_crypto
     end.
 
-rand_crypto_seed(crypto_aes = Alg, Seed) ->
+rand_crypto_seed(ExportState = {Alg, _})
+  when Alg =:= crypto_aes;
+       Alg =:= crypto_prng1 ->
+    crypto:rand_seed_alg(ExportState);
+rand_crypto_seed(State = #{type := Alg})
+  when Alg =:= crypto_aes;
+       Alg =:= crypto_prng1 ->
+    crypto:rand_seed_alg(State);
+rand_crypto_seed(Alg)
+    when Alg =:= crypto_aes;
+         Alg =:= crypto_prng1 ->
+    {dummy,Uint} = rand:export_seed_s(rand:seed_s(dummy)),
+    crypto:rand_seed_alg(Alg, <<Uint:64>>);
+rand_crypto_seed(Alg_State) ->
+    rand:seed(Alg_State).
+
+rand_crypto_seed(Alg, Seed)
+when Alg =:= crypto_aes;
+     Alg =:= crypto_prng1 ->
     {dummy,Uint} = rand:export_seed_s(rand:seed_s(dummy, Seed)),
     crypto:rand_seed_alg(Alg, <<Uint:64>>);
 rand_crypto_seed(Alg, Seed) ->
     rand:seed(Alg, Seed).
 
-rand_crypto_seed_s(crypto_aes = Alg, Seed) ->
+rand_crypto_seed_s(ExportState = {Alg, _})
+  when Alg =:= crypto_aes;
+       Alg =:= crypto_prng1 ->
+    crypto:rand_seed_alg_s(ExportState);
+rand_crypto_seed_s(State = #{type := Alg})
+  when Alg =:= crypto_aes;
+       Alg =:= crypto_prng1 ->
+    crypto:rand_seed_alg_s(State);
+rand_crypto_seed_s(Alg)
+    when Alg =:= crypto_aes;
+         Alg =:= crypto_prng1 ->
+    {dummy,Uint} = rand:export_seed_s(rand:seed_s(dummy)),
+    crypto:rand_seed_alg_s(Alg, <<Uint:64>>);
+rand_crypto_seed_s(Alg_State) ->
+    rand:seed_s(Alg_State).
+
+rand_crypto_seed_s(Alg, Seed)
+    when Alg =:= crypto_aes;
+         Alg =:= crypto_prng1 ->
     {dummy,Uint} = rand:export_seed_s(rand:seed_s(dummy, Seed)),
     crypto:rand_seed_alg_s(Alg, <<Uint:64>>);
 rand_crypto_seed_s(Alg, Seed) ->
@@ -148,7 +184,24 @@ rand_crypto_seed_s(Alg, Seed) ->
 
 %% Test that seed and seed_s and export_seed/0 is working.
 seed(Config) when is_list(Config) ->
-    Algs = [default|algs()],
+    %% Check that uniform seeds automatically,
+    X1 = rand:uniform(),
+    S1 = get(rand_seed),
+    X2 = rand:uniform(),
+    erase(),
+    X3 = rand:uniform(),
+    true = X1 =/= X3, % hopefully
+    {X2, _} = rand:uniform_s(S1),
+    %%
+    %% Check that export_seed/1 returns 'undefined' if there is no seed
+    erase(rand_seed),
+    undefined = rand:export_seed(),
+    %%
+    %% Other seed terms shall not work
+    {'EXIT', _} = (catch rand_crypto_seed_s(foobar, os:timestamp())),
+    %%
+    %% Tests for specified algorithms
+    Algs = [default | all_algs()],
     Test = fun(Alg) ->
 		   try seed_1(Alg)
 		   catch _:Reason:Stacktrace ->
@@ -156,56 +209,95 @@ seed(Config) when is_list(Config) ->
 		   end
 	   end,
     [Test(Alg) || Alg <- Algs],
-    %%
-    %% Check that export_seed/1 returns 'undefined' if there is no seed
-    erase(rand_seed),
-    undefined = rand:export_seed(),
-    %%
-    %% Other seed terms shall not work
-    {'EXIT', _} = (catch rand:seed_s(foobar, os:timestamp())),
     ok.
 
 seed_1(Alg) ->
-    %% Check that uniform seeds automatically,
-    _ = rand:uniform(),
-    S00 = get(rand_seed),
-    erase(),
-    _ = rand:uniform_real(),
-    false = S00 =:= get(rand_seed), %% hopefully
-
+    %% For all repeatable PRNGS, the initial state should be
+    %% possible to clone, but not necessarily compare,
+    %% since we want to be able to optimize the implementatin
+    %%
     %% Choosing algo and seed
-    S0 = rand:seed(Alg, {0, 0, 0}),
+    S1 = rand_crypto_seed(Alg, {0, 0, 0}),
     %% Check that (documented?) process_dict variable is correct
-    S0 = get(rand_seed),
-    S0 = rand:seed_s(Alg, {0, 0, 0}),
-    %% Check that process_dict should not be used for seed_s functionality
-    _ = rand:seed_s(Alg, 4711),
-    S0 = get(rand_seed),
-    %% Test export
-    ES0 = rand:export_seed(),
-    ES0 = rand:export_seed_s(S0),
-    S0 = rand:seed(ES0),
-    S0 = rand:seed_s(ES0),
-    %% seed/1 calls should be unique
-    S1 = rand:seed(Alg),
-    false = (S1 =:= rand:seed_s(Alg)),
+    S1a = get(rand_seed),
+    S1b = rand_crypto_seed_s(Alg, {0, 0, 0}),
+    %% We can test that seeds are equivalent by testing
+    %% generated numbers for equality.
+    X2 = rand:uniform(),
+    {X2, S2a} = rand:uniform_s(S1),
+    {X2, _} = rand:uniform_s(S1a),
+    {X2, _} = rand:uniform_s(S1b),
+    {X3, _} = rand:uniform_s(S2a),
+    %% Check that seed_s does not touch the process dictionary
+    S4a = rand_crypto_seed_s(Alg, 4711),
+    X3 = rand:uniform(),
+    {X5, _} = rand:uniform_s(S4a),
+    X3 /= X5 orelse error({eq, X3, X5}), % hopefully
+
+    %% Test seed, export and import of initial state
+    S6a = rand_crypto_seed_s(Alg, {1, 2, 3}),
+    seed_2(S6a),
+
+    S7 = rand_crypto_seed(Alg, {4, 5, 6}),
+    {_, S8a} = rand:uniform_s(S7),
+    case Alg of
+        crypto_prng1 ->
+            %% Only the initial state can be cloned or imported
+            ES8a = rand:export_seed_s(S8a),
+            try rand_crypto_seed_s(ES8a) of
+                OK -> error({ok, OK})
+            catch
+                error : not_implemented -> ok
+            end;
+        _ ->
+            %% Test seed, export and import of state
+            seed_2(S8a)
+    end,
+
+    %% seed(Alg) calls should be unique
+    _ = rand_crypto_seed(Alg),
+    S11a = rand_crypto_seed_s(Alg),
+    X10 = rand:uniform(),
+    {X11, _} = rand:uniform_s(S11a),
+    X10 /= X11 orelse error({eq, X10, X11}), % hopefully
+
     %% Negative integers works
-    _ = rand:seed_s(Alg, {-1,-1,-1}),
+    _ = rand_crypto_seed_s(Alg, {-1,-1,-1}),
     %%
     %% Other seed terms shall not work
-    {'EXIT', _} = (catch rand:seed_s(Alg, {asd, 1, 1})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, {0, 234.1234, 1})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, {0, 234, [1, 123, 123]})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, asd)),
-    {'EXIT', _} = (catch rand:seed_s(Alg, make_ref())),
-    {'EXIT', _} = (catch rand:seed_s(Alg, fun () -> 0 end)),
-    {'EXIT', _} = (catch rand:seed_s(Alg, self())),
-    {'EXIT', _} = (catch rand:seed_s(Alg, {1,2})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, {1,2,3,4})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, #{})),
-    {'EXIT', _} = (catch rand:seed_s(Alg, [1|2])),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, {asd, 1, 1})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, {0, 234.1234, 1})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, {0, 234, [1, 123, 123]})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, asd)),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, make_ref())),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, fun () -> 0 end)),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, self())),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, {1,2})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, {1,2,3,4})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, #{})),
+    {'EXIT', _} = (catch rand_crypto_seed_s(Alg, [1|2])),
+    ok.
+
+seed_2(S1) ->
+    %% Test that state can be used as seed
+    {X2, _} = rand:uniform_s(S1),
+    S1a = rand_crypto_seed(S1),
+    X2 = rand:uniform(),
+    S1b = rand_crypto_seed_s(S1a),
+    {X2, _} = rand:uniform_s(S1b),
+
+    %% Test export of state and import (seed) after roundtrip
+    S1c = rand_crypto_seed(S1),
+    ES1 = rand:export_seed(),
+    ES1c = rand:export_seed_s(S1c),
+    _ = rand_crypto_seed(roundtrip(ES1c)),
+    X2 = rand:uniform(),
+    S1d = rand_crypto_seed_s(roundtrip(ES1)),
+    {X2, _} = rand:uniform_s(S1d),
     ok.
 
+roundtrip(Term) -> binary_to_term(term_to_binary(Term)).
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 %% Check that both APIs are consistent with each other.
@@ -375,36 +467,39 @@ interval_int(Config) when is_list(Config) ->
           Alg <- Algs,
           Range <- [R1, R2, R3]],
       #{
-        {default,R1}    => 11240267459155554,
-        {default,R2}    => 32601989265626580,
-        {default,R3}    => 17949116405932061,
-        {exsss,R1}      => 11240267459155554,
-        {exsss,R2}      => 32601989265626580,
-        {exsss,R3}      => 17949116405932061,
-        {exrop,R1}      => 29439025302668224,
-        {exrop,R2}      => 35757088269702251,
-        {exrop,R3}      => 18658039660916348,
-        {exsp,R1}       =>  3756226303137097,
-        {exsp,R2}       => 18978154034346741,
-        {exsp,R3}       => 31517684264452265,
-        {exs1024s,R1}   => 25663442531954265,
-        {exs1024s,R2}   => 19963226828780853,
-        {exs1024s,R3}   => 17293067974750216,
-        {exs64,R1}      => 31194709903027496,
-        {exs64,R2}      => 19805508609802443,
-        {exs64,R3}      => 26160839404403677,
-        {exsplus,R1}    =>  3756226303137097,
-        {exsplus,R2}    => 35795957558673381,
-        {exsplus,R3}    => 33355694743882377,
-        {exs1024,R1}    => 25663442531954265,
-        {exs1024,R2}    => 13597139056366660,
-        {exs1024,R3}    => 28403669731190641,
-        {exro928ss,R1}  => 15392329658099540,
-        {exro928ss,R2}  => 30958702749427846,
-        {exro928ss,R3}  =>  6454995828729814,
-        {crypto_aes,R1} => 34171729520417518,
-        {crypto_aes,R2} => 26079292509661060,
-        {crypto_aes,R3} => 35504948493323822}).
+        {default,R1}      => 11240267459155554,
+        {default,R2}      => 32601989265626580,
+        {default,R3}      => 17949116405932061,
+        {exsss,R1}        => 11240267459155554,
+        {exsss,R2}        => 32601989265626580,
+        {exsss,R3}        => 17949116405932061,
+        {exrop,R1}        => 29439025302668224,
+        {exrop,R2}        => 35757088269702251,
+        {exrop,R3}        => 18658039660916348,
+        {exsp,R1}         =>  3756226303137097,
+        {exsp,R2}         => 18978154034346741,
+        {exsp,R3}         => 31517684264452265,
+        {exs1024s,R1}     => 25663442531954265,
+        {exs1024s,R2}     => 19963226828780853,
+        {exs1024s,R3}     => 17293067974750216,
+        {exs64,R1}        => 31194709903027496,
+        {exs64,R2}        => 19805508609802443,
+        {exs64,R3}        => 26160839404403677,
+        {exsplus,R1}      =>  3756226303137097,
+        {exsplus,R2}      => 35795957558673381,
+        {exsplus,R3}      => 33355694743882377,
+        {exs1024,R1}      => 25663442531954265,
+        {exs1024,R2}      => 13597139056366660,
+        {exs1024,R3}      => 28403669731190641,
+        {exro928ss,R1}    => 15392329658099540,
+        {exro928ss,R2}    => 30958702749427846,
+        {exro928ss,R3}    =>  6454995828729814,
+        {crypto_aes,R1}   => 34171729520417518,
+        {crypto_aes,R2}   => 26079292509661060,
+        {crypto_aes,R3}   => 35504948493323822,
+        {crypto_prng1,R1} => 13762149051103742,
+        {crypto_prng1,R2} =>   336837978859784,
+        {crypto_prng1,R3} => 15530306782629424}).
 
 interval_int(M, Range, Alg, D) ->
     Seed = rand_crypto_seed(Alg, 16#c0ffee),
@@ -440,21 +535,19 @@ interval_float(Config) when is_list(Config) ->
            L = interval_float(S, 100_000),
            {Alg, hash_term(L)}
        end || Alg <- Algs],
-      #{ default    =>  5382017173793021,
-         exsss      =>  5382017173793021,
-         exrop      =>  5207813521787093,
-         exsp       => 28291248181663524,
-         exs1024s   => 22063655035448922,
-         exs64      =>  5902160523799262,
-         exsplus    =>  8865928157739066,
-         exs1024    => 25102382062514482,
-         exro928ss  =>  1472404561442754,
-         crypto_aes => 25675812191601515}).
-
-interval_float(S, 0) ->
-    E = rand:export_seed_s(S),
-    E = rand:export_seed(),
-    [];
+      #{ default      =>  5382017173793021,
+         exsss        =>  5382017173793021,
+         exrop        =>  5207813521787093,
+         exsp         => 28291248181663524,
+         exs1024s     => 22063655035448922,
+         exs64        =>  5902160523799262,
+         exsplus      =>  8865928157739066,
+         exs1024      => 25102382062514482,
+         exro928ss    =>  1472404561442754,
+         crypto_aes   => 25675812191601515,
+         crypto_prng1 => 29622255076745349}).
+
+interval_float(_S, 0) -> [];
 interval_float(S0, N) ->
     {X, S1} = rand:uniform_s(S0),
     X       = rand:uniform(),
@@ -501,19 +594,19 @@ bytes_count(Config) when is_list(Config) ->
         exro928ss =>
             <<65,25,82,241,64,57,88,83,156,185,226,152,76,85,5,124>>,
         crypto_aes =>
-            <<177,51,112,56,233,126,247,244,127,240,226,105,123,184,225,130>>}),
+            <<177,51,112,56,233,126,247,244,127,240,226,105,123,184,225,130>>,
+        crypto_prng1 =>
+            <<170,205,186,30,12,91,96,107,78,167,175,8,232,159,62,241>>}),
     ok.
 
 bytes_count([], _S) -> [];
 bytes_count([N | Counts], S0) ->
-    ExportState = rand:export_seed(),
-    ExportState = rand:export_seed_s(S0),
     {B, S1} = rand:bytes_s(N, S0),
     case rand:bytes(N) of
         B when byte_size(B) =:= N ->
             [B | bytes_count(Counts, S1)];
         Other ->
-            error({N,Other,B,ExportState})
+            error({N,Other,B,rand:export_seed_s(S0)})
     end.
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -526,8 +619,8 @@ shuffle_elements(Config) when is_list(Config) ->
     {ShuffledList, NewState} = rand:shuffle_s(SortedList, State),
     case rand:shuffle(SortedList) of
         ShuffledList ->
-            NewSeed = rand:export_seed_s(NewState),
-            NewSeed = rand:export_seed(),
+            {X, _} = rand:uniform_s(NewState),
+            X = rand:uniform(),
             case lists:sort(ShuffledList) of
                 SortedList -> ok;
                 _ ->
@@ -576,7 +669,9 @@ shuffle_reference(Config) when is_list(Config) ->
         exro928ss =>
             <<160,170,223,95,44,254,192,107,145,180,236,235,102,110,72,131>>,
         crypto_aes =>
-            <<93,108,161,203,65,139,111,30,50,188,3,103,165,204,166,10>>}),
+            <<93,108,161,203,65,139,111,30,50,188,3,103,165,204,166,10>>,
+        crypto_prng1 =>
+            <<125,207,120,140,2,146,248,72,20,217,198,98,197,175,48,147>>}),
     ok.
 
 mk_iolist([], _M) -> [];
@@ -1444,7 +1539,9 @@ do_measure(I) ->
     Algs =
         case crypto_support() of
             ok ->
-                algs() ++ [crypto64, crypto_cache, crypto_aes, crypto];
+                algs() ++
+                    [crypto64, crypto, crypto_cache,
+                     crypto_aes, crypto_prng1];
             _ ->
                 algs()
         end,
@@ -1966,7 +2063,7 @@ do_measure(I) ->
           end, {mwc59,bytes}, Iterations,
           TMarkBytes1, OverheadBytes1),
     %%
-    ByteSize2 = 1000, % At about 100 bytes crypto_bytes breaks even to exsss
+    ByteSize2 = 1_000, % At about 100 bytes crypto_bytes breaks even to exsss
     ct:log("~nRNG ~w bytes performance~n",[ByteSize2]),
     [TMarkBytes2,OverheadBytes2|_] =
         measure_1(
@@ -1993,6 +2090,33 @@ do_measure(I) ->
           end, {mwc59,bytes}, setelement(1, Iterations, I div 50),
           TMarkBytes2, OverheadBytes2),
     %%
+    ByteSize3 = 100_000, % At about 100 bytes crypto_bytes breaks even to exsss
+    ct:log("~nRNG ~w bytes performance~n",[ByteSize3]),
+    [TMarkBytes3,OverheadBytes3|_] =
+        measure_1(
+          fun (Mod, _State) ->
+                  Generator = fun Mod:bytes_s/2,
+                  fun (St0) ->
+                          ?CHECK_BYTE_SIZE(
+                             Generator(ByteSize3, St0), ByteSize3, Bin, St1)
+                  end
+          end,
+          case crypto_support() of
+              ok ->
+                  Algs ++ [crypto_bytes, crypto_bytes_cached];
+              _ ->
+                  Algs
+          end, {I div 5_000, us}),
+    _ =
+        measure_1(
+          fun (_Mod, _State) ->
+                  fun (St0) ->
+                          ?CHECK_BYTE_SIZE(
+                             mwc59_bytes(ByteSize3, St0), ByteSize3, Bin, St1)
+                  end
+          end, {mwc59,bytes}, {I div 5_000, us},
+          TMarkBytes3, OverheadBytes3),
+    %%
     ct:log("~nRNG uniform float performance~n",[]),
     [TMarkUniformFloat,OverheadUniformFloat|_] =
         measure_1(
@@ -2153,8 +2277,10 @@ measure_init(Alg) ->
             {rand, crypto:rand_seed_s()};
         crypto_aes ->
             {rand,
-             crypto:rand_seed_alg(
-               crypto_aes, crypto:strong_rand_bytes(256))};
+             crypto:rand_seed_alg(Alg, crypto:strong_rand_bytes(256))};
+        crypto_prng1 ->
+            {rand,
+             crypto:rand_seed_alg(Alg, crypto:strong_rand_bytes(256))};
         random ->
             {random, random:seed(os:timestamp()), get(random_seed)};
         crypto_bytes ->
@@ -2401,6 +2527,44 @@ check(N, Range, StateA, StateB) ->
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
+%% Verify initial jump - I am thinking of crypto_prng1
+
+initial_jump(Config) when is_list(Config) ->
+    Algs = all_algs() -- [exs64],
+    %%
+    keyverify(
+      [begin
+           io:format("Alg = ~p~n", [Alg]),
+           S = rand_crypto_seed(Alg, 666_666),
+           Generators =
+               [S | lists_generate(16, fun (S0) -> {rand:jump(S0)} end, S)],
+           Ls =
+               [lists_generate(1999, fun rand:uniform_s/1, S0)
+                || S0 <- Generators],
+           {Alg, hash_term(Ls)}
+       end || Alg <- Algs],
+      #{ default      => 15708185852798073,
+         exsss        => 15708185852798073,
+         exrop        => 31298989252134043,
+         exsp         => 32930764673205242,
+         exs1024s     =>  2597827732751246,
+         exsplus      => 15135345399219452,
+         exs1024      => 28060696513531577,
+         exro928ss    => 27807813292563979,
+         crypto_prng1 => 15069504648191534,
+         crypto_aes   => 13768035795224702}).
+
+lists_generate(0, _Fun, _State) -> [];
+lists_generate(N, Fun, State0) when is_integer(N), 0 < N ->
+    case Fun(State0) of
+        {X, State1} ->
+            [X | lists_generate(N - 1, Fun, State1)];
+        {State1} ->
+            [State1 | lists_generate(N - 1, Fun, State1)]
+    end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
 doctests(Config) when is_list(Config) ->
     shell_docs:test(rand, []).
 
-- 
2.51.0

openSUSE Build Service is sponsored by