├── Makefile ├── doc ├── erlang.png ├── edoc-info ├── index.html ├── stylesheet.css ├── overview-summary.html ├── modules-frame.html ├── quickrand_normal.html ├── quickrand_cache_normal.html ├── random_wh82.html ├── random_wh06_int.html ├── quickrand_hash.html ├── quickrand_cache.html └── quickrand.html ├── .travis.yml ├── rebar.config ├── src ├── quickrand.app.src ├── quickrand_math.hrl ├── quickrand_internal.hrl ├── quickrand_test.hrl ├── quickrand_constants.hrl ├── quickrand_normal.erl ├── quickrand_cache_normal.erl ├── random_wh82.erl ├── random_wh06_int.erl ├── quickrand_cache.erl ├── quickrand.erl └── quickrand_hash.erl ├── README.markdown ├── LICENSE ├── mix.exs └── erlang.mk /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = quickrand 2 | 3 | 4 | include erlang.mk 5 | -------------------------------------------------------------------------------- /doc/erlang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okeuday/quickrand/HEAD/doc/erlang.png -------------------------------------------------------------------------------- /doc/edoc-info: -------------------------------------------------------------------------------- 1 | %% encoding: UTF-8 2 | {application,quickrand}. 3 | {modules,[quickrand,quickrand_cache,quickrand_cache_normal,quickrand_hash, 4 | quickrand_normal,random_wh06_int,random_wh82]}. 5 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The quickrand application 5 | 6 | 7 | 8 | 9 | 10 | 11 | <h2>This page uses frames</h2> 12 | <p>Your browser does not accept frames. 13 | <br>You should go to the <a href="overview-summary.html">non-frame version</a> instead. 14 | </p> 15 | 16 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 26.1.1 4 | - 25.3.2.6 5 | - 24.3.1 6 | - 23.3.1 7 | - 22.3.4.25 8 | script: 9 | - rebar3 compile 10 | - rebar3 eunit 11 | after_script: 12 | - rebar3 clean 13 | branches: 14 | only: 15 | - master 16 | notifications: 17 | email: 18 | recipients: 19 | - mjtruog@gmail.com 20 | irc: 21 | channels: 22 | - "irc.oftc.net#cloudi" 23 | template: 24 | - "%{repository_slug} (%{branch} - %{commit}) %{author}: %{commit_message}" 25 | - "View Changes %{compare_url}" 26 | - "Build #%{build_number}: %{message} (%{build_url})" 27 | on_success: change 28 | on_failure: always 29 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | 4 | {erl_opts, 5 | [{platform_define, "^R16", 'ERLANG_OTP_VERSION_16'}, 6 | {platform_define, "^17\.", 'ERLANG_OTP_VERSION_17'}, 7 | {platform_define, "^18\.", 'ERLANG_OTP_VERSION_18'}, 8 | {platform_define, "^19\.", 'ERLANG_OTP_VERSION_19'}, 9 | {platform_define, "^20\.", 'ERLANG_OTP_VERSION_20'}, 10 | warn_export_vars, 11 | warn_unused_import, 12 | %warn_missing_spec, 13 | warnings_as_errors]}. 14 | {edoc_opts, [{preprocess, true}]}. 15 | {cover_enabled, true}. 16 | {cover_print_enabled, true}. 17 | {cover_export_enabled, true}. 18 | -------------------------------------------------------------------------------- /src/quickrand.app.src: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | 4 | {application, quickrand, 5 | [{description, "Quick Random Number Generation"}, 6 | {vsn, "2.0.7"}, 7 | {modules, [ 8 | quickrand, 9 | quickrand_cache, 10 | quickrand_cache_normal, 11 | quickrand_hash, 12 | quickrand_normal, 13 | random_wh82, 14 | random_wh06_int]}, 15 | {registered, []}, 16 | {applications, [ 17 | crypto, 18 | stdlib, 19 | kernel]}, 20 | {env, [ 21 | % default cache_size == 64 KB 22 | {cache_size, 65536}]}]}. 23 | 24 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Quick Random Number Generation 2 | ============================== 3 | 4 | [![Build Status](https://app.travis-ci.com/okeuday/quickrand.svg?branch=master)](https://app.travis-ci.com/okeuday/quickrand) 5 | 6 | Provides a simple interface to call efficient random number generation 7 | functions based on the context. Proper random number seeding is enforced. 8 | 9 | `random_wh82` is provided as an alternative to the `random` module 10 | which is scheduled to be removed in Erlang/OTP 20. 11 | 12 | Build 13 | ----- 14 | 15 | rebar compile 16 | 17 | Author 18 | ------ 19 | 20 | Michael Truog (mjtruog at protonmail dot com) 21 | 22 | Thanks 23 | ------ 24 | 25 | * Raimo Niskanen (Box-Muller transformation floating-point period) 26 | * Richard O'Keefe (floating-point random number period insights) 27 | 28 | License 29 | ------- 30 | 31 | MIT License 32 | 33 | -------------------------------------------------------------------------------- /doc/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* standard EDoc style sheet */ 2 | body { 3 | font-family: Verdana, Arial, Helvetica, sans-serif; 4 | margin-left: .25in; 5 | margin-right: .2in; 6 | margin-top: 0.2in; 7 | margin-bottom: 0.2in; 8 | color: #000000; 9 | background-color: #ffffff; 10 | } 11 | h1,h2 { 12 | margin-left: -0.2in; 13 | } 14 | div.navbar { 15 | background-color: #add8e6; 16 | padding: 0.2em; 17 | } 18 | h2.indextitle { 19 | padding: 0.4em; 20 | background-color: #add8e6; 21 | } 22 | h3.function,h3.typedecl { 23 | background-color: #add8e6; 24 | padding-left: 1em; 25 | } 26 | div.spec { 27 | margin-left: 2em; 28 | background-color: #eeeeee; 29 | } 30 | a.module { 31 | text-decoration:none 32 | } 33 | a.module:hover { 34 | background-color: #eeeeee; 35 | } 36 | ul.definitions { 37 | list-style-type: none; 38 | } 39 | ul.index { 40 | list-style-type: none; 41 | background-color: #eeeeee; 42 | } 43 | 44 | /* 45 | * Minor style tweaks 46 | */ 47 | ul { 48 | list-style-type: square; 49 | } 50 | table { 51 | border-collapse: collapse; 52 | } 53 | td { 54 | padding: 3 55 | } 56 | -------------------------------------------------------------------------------- /doc/overview-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The quickrand application 6 | 7 | 8 | 9 | 10 |

The quickrand application

11 | 12 |
13 | 14 |

Generated by EDoc

15 | 16 | 17 | -------------------------------------------------------------------------------- /doc/modules-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The quickrand application 5 | 6 | 7 | 8 |

Modules

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
quickrand
quickrand_cache
quickrand_cache_normal
quickrand_hash
quickrand_normal
random_wh06_int
random_wh82
17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2023 Michael Truog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | #-*-Mode:elixir;coding:utf-8;tab-width:2;c-basic-offset:2;indent-tabs-mode:()-*- 2 | # ex: set ft=elixir fenc=utf-8 sts=2 ts=2 sw=2 et nomod: 3 | 4 | defmodule Quickrand.Mixfile do 5 | use Mix.Project 6 | 7 | def project do 8 | [app: :quickrand, 9 | version: "2.0.7", 10 | language: :erlang, 11 | erlc_options: [ 12 | {:d, :erlang.list_to_atom(~c"ERLANG_OTP_VERSION_" ++ :erlang.system_info(:otp_release))}, 13 | :deterministic, 14 | :debug_info, 15 | :warn_export_vars, 16 | :warn_unused_import, 17 | #:warn_missing_spec, 18 | :warnings_as_errors], 19 | description: description(), 20 | package: package(), 21 | deps: deps()] 22 | end 23 | 24 | def application do 25 | [applications: [ 26 | :crypto]] 27 | end 28 | 29 | defp deps do 30 | [] 31 | end 32 | 33 | defp description do 34 | "Quick Random Number Generation: " <> 35 | "Provides a simple interface to call efficient random number generation " <> 36 | "functions based on the context. Proper random number seeding is enforced." 37 | end 38 | 39 | defp package do 40 | [files: ~w(src doc rebar.config README.markdown LICENSE), 41 | maintainers: ["Michael Truog"], 42 | licenses: ["MIT"], 43 | links: %{"GitHub" => "https://github.com/okeuday/quickrand"}] 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /src/quickrand_math.hrl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% 6 | %%% MIT License 7 | %%% 8 | %%% Copyright (c) 2017 Michael Truog 9 | %%% 10 | %%% Permission is hereby granted, free of charge, to any person obtaining a 11 | %%% copy of this software and associated documentation files (the "Software"), 12 | %%% to deal in the Software without restriction, including without limitation 13 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | %%% and/or sell copies of the Software, and to permit persons to whom the 15 | %%% Software is furnished to do so, subject to the following conditions: 16 | %%% 17 | %%% The above copyright notice and this permission notice shall be included in 18 | %%% all copies or substantial portions of the Software. 19 | %%% 20 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | %%% DEALINGS IN THE SOFTWARE. 27 | %%% 28 | %%%------------------------------------------------------------------------ 29 | 30 | -define(PI2, math:pi() * 2.0). 31 | 32 | -------------------------------------------------------------------------------- /src/quickrand_internal.hrl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% 6 | %%% MIT License 7 | %%% 8 | %%% Copyright (c) 2017-2022 Michael Truog 9 | %%% 10 | %%% Permission is hereby granted, free of charge, to any person obtaining a 11 | %%% copy of this software and associated documentation files (the "Software"), 12 | %%% to deal in the Software without restriction, including without limitation 13 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | %%% and/or sell copies of the Software, and to permit persons to whom the 15 | %%% Software is furnished to do so, subject to the following conditions: 16 | %%% 17 | %%% The above copyright notice and this permission notice shall be included in 18 | %%% all copies or substantial portions of the Software. 19 | %%% 20 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | %%% DEALINGS IN THE SOFTWARE. 27 | %%% 28 | %%%------------------------------------------------------------------------ 29 | 30 | -define(BYTES_RESOLUTION, 4). % bytes 31 | 32 | bytes(I) when is_integer(I), I > 0 -> 33 | bytes(I, 0). 34 | 35 | bytes(0, Bytes) -> 36 | Bytes; 37 | bytes(I, Bytes) -> 38 | bytes(I bsr (?BYTES_RESOLUTION * 8), Bytes + ?BYTES_RESOLUTION). 39 | 40 | -------------------------------------------------------------------------------- /src/quickrand_test.hrl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% quickrand eunit common functionality 6 | %%% 7 | %%% MIT License 8 | %%% 9 | %%% Copyright (c) 2020 Michael Truog 10 | %%% 11 | %%% Permission is hereby granted, free of charge, to any person obtaining a 12 | %%% copy of this software and associated documentation files (the "Software"), 13 | %%% to deal in the Software without restriction, including without limitation 14 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 | %%% and/or sell copies of the Software, and to permit persons to whom the 16 | %%% Software is furnished to do so, subject to the following conditions: 17 | %%% 18 | %%% The above copyright notice and this permission notice shall be included in 19 | %%% all copies or substantial portions of the Software. 20 | %%% 21 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | %%% DEALINGS IN THE SOFTWARE. 28 | %%% 29 | %%%------------------------------------------------------------------------ 30 | 31 | -ifndef(_assertOk). 32 | -define(_assertOk(Expr), ?_assertEqual(ok, Expr)). 33 | -endif. 34 | 35 | -ifdef(CLOUDI_TEST_TIMEOUT). 36 | -define(TEST_TIMEOUT, ?CLOUDI_TEST_TIMEOUT). % seconds 37 | -else. 38 | -define(TEST_TIMEOUT, 10). % seconds 39 | -endif. 40 | 41 | -------------------------------------------------------------------------------- /src/quickrand_constants.hrl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% 6 | %%% MIT License 7 | %%% 8 | %%% Copyright (c) 2022 Michael Truog 9 | %%% 10 | %%% Permission is hereby granted, free of charge, to any person obtaining a 11 | %%% copy of this software and associated documentation files (the "Software"), 12 | %%% to deal in the Software without restriction, including without limitation 13 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 | %%% and/or sell copies of the Software, and to permit persons to whom the 15 | %%% Software is furnished to do so, subject to the following conditions: 16 | %%% 17 | %%% The above copyright notice and this permission notice shall be included in 18 | %%% all copies or substantial portions of the Software. 19 | %%% 20 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | %%% DEALINGS IN THE SOFTWARE. 27 | %%% 28 | %%%------------------------------------------------------------------------ 29 | 30 | -define(APPLICATION, quickrand). 31 | 32 | % 1 / (1 + 16#1fffffffffffff) =:= math:pow(2, -53) to provide [0.0 .. 1.0] 33 | -define(DBL_EPSILON_DIV2, 1.1102230246251565e-16). 34 | 35 | -define(BITMASK_16, 16#ffff). 36 | -define(BITMASK_32, 16#ffffffff). 37 | -define(BITMASK_35, 16#7ffffffff). 38 | -define(BITMASK_59, 16#7ffffffffffffff). 39 | -define(BITMASK_64, 16#ffffffffffffffff). 40 | -define(BITMASK_128, 16#ffffffffffffffffffffffffffffffff). 41 | -define(BITMASK_1024, 16#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff). 42 | 43 | -------------------------------------------------------------------------------- /doc/quickrand_normal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module quickrand_normal 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module quickrand_normal

13 | 14 |

Quick Normal Distribution Random Number Generation

. 15 |

Copyright © 2017-2022 Michael Truog

16 | 17 |

Version: 2.0.5 Oct 26 2023 11:37:41 18 | ------------------------------------------------------------------------

19 |

Authors: Michael Truog (mjtruog at protonmail dot com).

20 | 21 |

Description

22 |

Quick Normal Distribution Random Number Generation

23 | 24 |

Function Index

25 | 28 |
box_muller/2 26 |

Box-Muller transformation for generating Gaussian noise.

27 | Has numerical stability problems when X1 is very close to zero.
29 | 30 |

Function Details

31 | 32 |

box_muller/2

33 |
34 |

box_muller(Mean::number(), StdDev::number()) -> {Result1::float(), Result2::float()}

35 |

36 |

37 |

Box-Muller transformation for generating Gaussian noise.

38 | Has numerical stability problems when X1 is very close to zero. 39 | This is a serious problem for stochastic modeling that is generating 40 | millions of numbers. If the result is used with added noise 41 | (e.g., creating a process sleep value) then it isn't a problem.

42 |
43 | 44 | 45 |

Generated by EDoc

46 | 47 | 48 | -------------------------------------------------------------------------------- /doc/quickrand_cache_normal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module quickrand_cache_normal 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module quickrand_cache_normal

13 | 14 |

Quick Normal Distribution Random Number Generation With Cached Data

. 15 |

Copyright © 2017 Michael Truog

16 | 17 |

Version: 1.7.2 Oct 26 2023 11:37:41 18 | ------------------------------------------------------------------------

19 |

Authors: Michael Truog (mjtruog at protonmail dot com).

20 | 21 |

Description

22 |

Quick Normal Distribution Random Number Generation With Cached Data

23 | 24 |

Function Index

25 | 27 | 29 |
box_muller/2 26 |

Process dictionary cache version of quickrand_normal:box_muller/2.

.
box_muller/3 28 |

State cache version of quickrand_normal:box_muller/2.

.
30 | 31 |

Function Details

32 | 33 |

box_muller/2

34 |
35 |

box_muller(Mean::number(), StdDev::number()) -> {Result1::float(), Result2::float()}

36 |

37 |

38 |

Process dictionary cache version of quickrand_normal:box_muller/2.

39 |

40 | 41 |

box_muller/3

42 |
43 |

box_muller(Mean::number(), StdDev::number(), State0::quickrand_cache:state()) -> {Result1::float(), Result2::float(), StateN::quickrand_cache:state()}

44 |

45 |

46 |

State cache version of quickrand_normal:box_muller/2.

47 |

48 |
49 | 50 | 51 |

Generated by EDoc

52 | 53 | 54 | -------------------------------------------------------------------------------- /src/quickrand_normal.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% @doc 6 | %%% ==Quick Normal Distribution Random Number Generation== 7 | %%% @end 8 | %%% 9 | %%% MIT License 10 | %%% 11 | %%% Copyright (c) 2017-2022 Michael Truog 12 | %%% 13 | %%% Permission is hereby granted, free of charge, to any person obtaining a 14 | %%% copy of this software and associated documentation files (the "Software"), 15 | %%% to deal in the Software without restriction, including without limitation 16 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 17 | %%% and/or sell copies of the Software, and to permit persons to whom the 18 | %%% Software is furnished to do so, subject to the following conditions: 19 | %%% 20 | %%% The above copyright notice and this permission notice shall be included in 21 | %%% all copies or substantial portions of the Software. 22 | %%% 23 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | %%% DEALINGS IN THE SOFTWARE. 30 | %%% 31 | %%% @author Michael Truog 32 | %%% @copyright 2017-2022 Michael Truog 33 | %%% @version 2.0.5 {@date} {@time} 34 | %%%------------------------------------------------------------------------ 35 | 36 | -module(quickrand_normal). 37 | -author('mjtruog at protonmail dot com'). 38 | 39 | %% external interface 40 | -export([box_muller/2]). 41 | 42 | -include("quickrand_math.hrl"). 43 | 44 | %%%------------------------------------------------------------------------ 45 | %%% External interface functions 46 | %%%------------------------------------------------------------------------ 47 | 48 | %%------------------------------------------------------------------------- 49 | %% @doc 50 | %% ===Box-Muller transformation for generating Gaussian noise.=== 51 | %% Has numerical stability problems when X1 is very close to zero. 52 | %% This is a serious problem for stochastic modeling that is generating 53 | %% millions of numbers. If the result is used with added noise 54 | %% (e.g., creating a process sleep value) then it isn't a problem. 55 | %% @end 56 | %%------------------------------------------------------------------------- 57 | 58 | -spec box_muller(Mean :: number(), 59 | StdDev :: number()) -> 60 | {Result1 :: float(), Result2 :: float()}. 61 | 62 | box_muller(Mean, StdDev) -> 63 | % use Box-Muller transformation to generate Gaussian noise 64 | % 65 | % George Edward Pelham Box, Mervin Edgar Muller. 66 | % A Note on the Generation of Random Normal Deviates. 67 | % The Annals of Mathematical Statistics, 68 | % vol. 29, no. 2, pp. 610–611, 1958. 69 | X1 = quickrand:strong_floatR(), 70 | X2 = ?PI2 * quickrand:strong_floatR(), 71 | K = StdDev * math:sqrt(-2.0 * math:log(X1)), 72 | Result1 = Mean + K * math:cos(X2), 73 | Result2 = Mean + K * math:sin(X2), 74 | {Result1, Result2}. 75 | 76 | %%%------------------------------------------------------------------------ 77 | %%% Private functions 78 | %%%------------------------------------------------------------------------ 79 | 80 | -------------------------------------------------------------------------------- /src/quickrand_cache_normal.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% @doc 6 | %%% ==Quick Normal Distribution Random Number Generation With Cached Data== 7 | %%% @end 8 | %%% 9 | %%% MIT License 10 | %%% 11 | %%% Copyright (c) 2017 Michael Truog 12 | %%% 13 | %%% Permission is hereby granted, free of charge, to any person obtaining a 14 | %%% copy of this software and associated documentation files (the "Software"), 15 | %%% to deal in the Software without restriction, including without limitation 16 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 17 | %%% and/or sell copies of the Software, and to permit persons to whom the 18 | %%% Software is furnished to do so, subject to the following conditions: 19 | %%% 20 | %%% The above copyright notice and this permission notice shall be included in 21 | %%% all copies or substantial portions of the Software. 22 | %%% 23 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | %%% DEALINGS IN THE SOFTWARE. 30 | %%% 31 | %%% @author Michael Truog 32 | %%% @copyright 2017 Michael Truog 33 | %%% @version 1.7.2 {@date} {@time} 34 | %%%------------------------------------------------------------------------ 35 | 36 | -module(quickrand_cache_normal). 37 | -author('mjtruog at protonmail dot com'). 38 | 39 | %% external interface 40 | -export([box_muller/2, 41 | box_muller/3]). 42 | 43 | -include("quickrand_math.hrl"). 44 | 45 | %%%------------------------------------------------------------------------ 46 | %%% External interface functions 47 | %%%------------------------------------------------------------------------ 48 | 49 | %%------------------------------------------------------------------------- 50 | %% @doc 51 | %% ===Process dictionary cache version of quickrand_normal:box_muller/2.=== 52 | %% @end 53 | %%------------------------------------------------------------------------- 54 | 55 | -spec box_muller(Mean :: number(), 56 | StdDev :: number()) -> 57 | {Result1 :: float(), Result2 :: float()}. 58 | 59 | box_muller(Mean, StdDev) -> 60 | X1 = quickrand_cache:floatR(), 61 | X2 = ?PI2 * quickrand_cache:floatR(), 62 | K = StdDev * math:sqrt(-2.0 * math:log(X1)), 63 | Result1 = Mean + K * math:cos(X2), 64 | Result2 = Mean + K * math:sin(X2), 65 | {Result1, Result2}. 66 | 67 | %%------------------------------------------------------------------------- 68 | %% @doc 69 | %% ===State cache version of quickrand_normal:box_muller/2.=== 70 | %% @end 71 | %%------------------------------------------------------------------------- 72 | 73 | -spec box_muller(Mean :: number(), 74 | StdDev :: number(), 75 | State0 :: quickrand_cache:state()) -> 76 | {Result1 :: float(), Result2 :: float(), StateN :: quickrand_cache:state()}. 77 | 78 | box_muller(Mean, StdDev, State0) -> 79 | {X1, State1} = quickrand_cache:floatR(State0), 80 | {R2, StateN} = quickrand_cache:floatR(State1), 81 | X2 = ?PI2 * R2, 82 | K = StdDev * math:sqrt(-2.0 * math:log(X1)), 83 | Result1 = Mean + K * math:cos(X2), 84 | Result2 = Mean + K * math:sin(X2), 85 | {Result1, Result2, StateN}. 86 | 87 | %%%------------------------------------------------------------------------ 88 | %%% Private functions 89 | %%%------------------------------------------------------------------------ 90 | 91 | -------------------------------------------------------------------------------- /doc/random_wh82.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module random_wh82 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module random_wh82

13 | 14 | 15 | 16 |

Data Types

17 | 18 |

seed()

19 |

seed() = {pos_integer(), pos_integer(), pos_integer()}

20 | 21 | 22 |

Function Index

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
next_sequence/1
seed/0
seed/1
seed/3
seed0/0
uniform/0
uniform/1
uniform_s/1
uniform_s/2
33 | 34 |

Function Details

35 | 36 |

next_sequence/1

37 |
38 |

next_sequence(X1::seed()) -> seed()

39 |

40 |
41 | 42 |

seed/0

43 |
44 |

seed() -> seed()

45 |

46 |
47 | 48 |

seed/1

49 |
50 |

seed(X1::seed()) -> undefined | seed()

51 |

52 |
53 | 54 |

seed/3

55 |
56 |

seed(A1::pos_integer(), A2::pos_integer(), A3::pos_integer()) -> undefined | seed()

57 |

58 |
59 | 60 |

seed0/0

61 |
62 |

seed0() -> {3172, 9814, 20125}

63 |

64 |
65 | 66 |

uniform/0

67 |
68 |

uniform() -> float()

69 |

70 |
71 | 72 |

uniform/1

73 |
74 |

uniform(N::pos_integer()) -> pos_integer()

75 |

76 |
77 | 78 |

uniform_s/1

79 |
80 |

uniform_s(X1::seed()) -> {float(), seed()}

81 |

82 |
83 | 84 |

uniform_s/2

85 |
86 |

uniform_s(N::pos_integer(), State0::seed()) -> {pos_integer(), seed()}

87 |

88 |
89 |
90 | 91 | 92 |

Generated by EDoc

93 | 94 | 95 | -------------------------------------------------------------------------------- /doc/random_wh06_int.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module random_wh06_int 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module random_wh06_int

13 | 14 | 15 | 16 |

Data Types

17 | 18 |

seed()

19 |

seed() = {pos_integer(), pos_integer(), pos_integer(), pos_integer()}

20 | 21 | 22 |

Function Index

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
next_sequence/1
seed/0
seed/1
seed/4
seed0/0
uniform/0
uniform/1
uniform_s/1
uniform_s/2
33 | 34 |

Function Details

35 | 36 |

next_sequence/1

37 |
38 |

next_sequence(X1::seed()) -> seed()

39 |

40 |
41 | 42 |

seed/0

43 |
44 |

seed() -> seed()

45 |

46 |
47 | 48 |

seed/1

49 |
50 |

seed(X1::seed()) -> undefined | seed()

51 |

52 |
53 | 54 |

seed/4

55 |
56 |

seed(A1::pos_integer(), A2::pos_integer(), A3::pos_integer(), A4::pos_integer()) -> undefined | seed()

57 |

58 |
59 | 60 |

seed0/0

61 |
62 |

seed0() -> {123456789, 345678901, 567890123, 789012345}

63 |

64 |
65 | 66 |

uniform/0

67 |
68 |

uniform() -> non_neg_integer()

69 |

70 |
71 | 72 |

uniform/1

73 |
74 |

uniform(N::pos_integer()) -> pos_integer()

75 |

76 |
77 | 78 |

uniform_s/1

79 |
80 |

uniform_s(X1::seed()) -> {non_neg_integer(), seed()}

81 |

82 |
83 | 84 |

uniform_s/2

85 |
86 |

uniform_s(N::pos_integer(), State0::seed()) -> {pos_integer(), seed()}

87 |

88 |
89 |
90 | 91 | 92 |

Generated by EDoc

93 | 94 | 95 | -------------------------------------------------------------------------------- /src/random_wh82.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | 4 | %% Modified version of random module (in Erlang/OTP before version 20.0) 5 | 6 | %% Copyright (c) 2016-2020 Michael Truog All rights reserved. 7 | 8 | %% 9 | %% %CopyrightBegin% 10 | %% 11 | %% Copyright Ericsson AB 1996-2011. All Rights Reserved. 12 | %% 13 | %% Licensed under the Apache License, Version 2.0 (the "License"); 14 | %% you may not use this file except in compliance with the License. 15 | %% You may obtain a copy of the License at 16 | %% 17 | %% http://www.apache.org/licenses/LICENSE-2.0 18 | %% 19 | %% Unless required by applicable law or agreed to in writing, software 20 | %% distributed under the License is distributed on an "AS IS" BASIS, 21 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 | %% See the License for the specific language governing permissions and 23 | %% limitations under the License. 24 | %% 25 | %% %CopyrightEnd% 26 | %% 27 | -module(random_wh82). 28 | 29 | %% Reasonable random number generator (period is 2.78e13): 30 | %% The method is attributed to B. A. Wichmann and I. D. Hill 31 | %% See "An efficient and portable pseudo-random number generator", 32 | %% Journal of Applied Statistics. AS183. 1982. Also Byte March 1987. 33 | 34 | -export([seed0/0, seed/0, seed/1, seed/3, 35 | uniform/0, uniform/1, 36 | uniform_s/1, uniform_s/2, 37 | next_sequence/1]). 38 | 39 | -define(PRIME1, 30269). 40 | -define(PRIME2, 30307). 41 | -define(PRIME3, 30323). 42 | 43 | -define(SEED_DICT, random_seed). 44 | 45 | %%----------------------------------------------------------------------- 46 | %% The type of the state 47 | 48 | -type seed() :: {pos_integer(), pos_integer(), pos_integer()}. 49 | 50 | %%----------------------------------------------------------------------- 51 | 52 | -spec seed0() -> {3172, 9814, 20125}. 53 | 54 | seed0() -> 55 | {3172, 9814, 20125}. 56 | 57 | %% seed() 58 | %% Seed random number generation with default values 59 | 60 | -spec seed() -> seed(). 61 | 62 | seed() -> 63 | reseed(seed0()). 64 | 65 | %% seed({A1, A2, A3}) 66 | %% Seed random number generation 67 | 68 | -spec seed(seed()) -> 69 | 'undefined' | seed(). 70 | 71 | seed({A1, A2, A3}) -> 72 | seed(A1, A2, A3). 73 | 74 | %% seed(A1, A2, A3) 75 | %% Seed random number generation 76 | 77 | -spec seed(pos_integer(), pos_integer(), pos_integer()) -> 78 | 'undefined' | seed(). 79 | 80 | seed(A1, A2, A3) 81 | when is_integer(A1), A1 > 0, 82 | is_integer(A2), A2 > 0, 83 | is_integer(A3), A3 > 0 -> 84 | put(?SEED_DICT, 85 | {(A1 rem (?PRIME1 - 1)) + 1, 86 | (A2 rem (?PRIME2 - 1)) + 1, 87 | (A3 rem (?PRIME3 - 1)) + 1}). 88 | 89 | reseed({A1, A2, A3}) -> 90 | case seed(A1, A2, A3) of 91 | undefined -> seed0(); 92 | {_,_,_} = Tuple -> Tuple 93 | end. 94 | 95 | %% uniform() 96 | %% Returns a random float in the range [0.0 .. 1.0) 97 | 98 | -spec uniform() -> float(). 99 | 100 | uniform() -> 101 | {A1, A2, A3} = case get(?SEED_DICT) of 102 | undefined -> seed0(); 103 | Tuple -> Tuple 104 | end, 105 | 106 | B1 = (171 * A1) rem ?PRIME1, 107 | B2 = (172 * A2) rem ?PRIME2, 108 | B3 = (170 * A3) rem ?PRIME3, 109 | 110 | put(?SEED_DICT, {B1, B2, B3}), 111 | 112 | R = B1/?PRIME1 + B2/?PRIME2 + B3/?PRIME3, 113 | R - trunc(R). 114 | 115 | %% uniform(N) -> I 116 | %% Given an integer N > 1, N =< 27817185604309, 117 | %% uniform(N) returns a random integer 118 | %% between 1 and N. 119 | 120 | -spec uniform(pos_integer()) -> pos_integer(). 121 | 122 | uniform(N) 123 | when is_integer(N), N > 1, N =< 27817185604309 -> 124 | trunc(uniform() * N) + 1. 125 | 126 | %%% Functional versions 127 | 128 | %% uniform_s(State) -> {F, NewState} 129 | %% Returns a random float in the range [0.0 .. 1.0) 130 | 131 | -spec uniform_s(seed()) -> {float(), seed()}. 132 | 133 | uniform_s({A1, A2, A3}) 134 | when is_integer(A1), A1 > 0, 135 | is_integer(A2), A2 > 0, 136 | is_integer(A3), A3 > 0 -> 137 | B1 = (171 * A1) rem ?PRIME1, 138 | B2 = (172 * A2) rem ?PRIME2, 139 | B3 = (170 * A3) rem ?PRIME3, 140 | 141 | R = B1/?PRIME1 + B2/?PRIME2 + B3/?PRIME3, 142 | 143 | {R - trunc(R), {B1, B2, B3}}. 144 | 145 | %% uniform_s(N, State) -> {I, NewState} 146 | %% Given an integer N > 1, N =< 27817185604309, 147 | %% uniform(N) returns a random integer 148 | %% between 1 and N. 149 | 150 | -spec uniform_s(pos_integer(), seed()) -> {pos_integer(), seed()}. 151 | 152 | uniform_s(N, State0) 153 | when is_integer(N), N > 1, N =< 27817185604309 -> 154 | {F, State1} = uniform_s(State0), 155 | {trunc(F * N) + 1, State1}. 156 | 157 | %% generating another seed for multiple sequences 158 | 159 | -spec next_sequence(seed()) -> seed(). 160 | 161 | next_sequence({A1, A2, A3}) 162 | when is_integer(A1), A1 > 0, 163 | is_integer(A2), A2 > 0, 164 | is_integer(A3), A3 > 0 -> 165 | B1 = (171 * A1) rem ?PRIME1, 166 | B2 = (172 * A2) rem ?PRIME2, 167 | B3 = (170 * A3) rem ?PRIME3, 168 | {B1, B2, B3}. 169 | 170 | -------------------------------------------------------------------------------- /erlang.mk: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013, Loïc Hoguin 2 | # 3 | # Permission to use, copy, modify, and/or distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | # Project. 16 | 17 | PROJECT ?= $(notdir $(CURDIR)) 18 | 19 | # Packages database file. 20 | 21 | PKG_FILE ?= $(CURDIR)/.erlang.mk.packages.v1 22 | export PKG_FILE 23 | 24 | PKG_FILE_URL ?= https://raw.github.com/extend/erlang.mk/master/packages.v1.tsv 25 | 26 | define get_pkg_file 27 | wget -O $(PKG_FILE) $(PKG_FILE_URL) 28 | endef 29 | 30 | # Verbosity and tweaks. 31 | 32 | V ?= 0 33 | 34 | appsrc_verbose_0 = @echo " APP " $(PROJECT).app.src; 35 | appsrc_verbose = $(appsrc_verbose_$(V)) 36 | 37 | erlc_verbose_0 = @echo " ERLC " $(filter %.erl %.core,$(?F)); 38 | erlc_verbose = $(erlc_verbose_$(V)) 39 | 40 | xyrl_verbose_0 = @echo " XYRL " $(filter %.xrl %.yrl,$(?F)); 41 | xyrl_verbose = $(xyrl_verbose_$(V)) 42 | 43 | dtl_verbose_0 = @echo " DTL " $(filter %.dtl,$(?F)); 44 | dtl_verbose = $(dtl_verbose_$(V)) 45 | 46 | gen_verbose_0 = @echo " GEN " $@; 47 | gen_verbose = $(gen_verbose_$(V)) 48 | 49 | .PHONY: all clean-all app clean deps clean-deps docs clean-docs \ 50 | build-tests tests build-plt dialyze 51 | 52 | # Deps directory. 53 | 54 | DEPS_DIR ?= $(CURDIR)/deps 55 | export DEPS_DIR 56 | 57 | REBAR_DEPS_DIR = $(DEPS_DIR) 58 | export REBAR_DEPS_DIR 59 | 60 | ALL_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(DEPS)) 61 | ALL_TEST_DEPS_DIRS = $(addprefix $(DEPS_DIR)/,$(TEST_DEPS)) 62 | 63 | # Application. 64 | 65 | ERL_LIBS ?= $(DEPS_DIR) 66 | export ERL_LIBS 67 | 68 | ERLC_OPTS ?= -Werror +debug_info +warn_export_all +warn_export_vars \ 69 | +warn_shadow_vars +warn_obsolete_guard # +bin_opt_info +warn_missing_spec 70 | COMPILE_FIRST ?= 71 | COMPILE_FIRST_PATHS = $(addprefix src/,$(addsuffix .erl,$(COMPILE_FIRST))) 72 | 73 | all: deps app 74 | 75 | clean-all: clean clean-deps clean-docs 76 | $(gen_verbose) rm -rf .$(PROJECT).plt $(DEPS_DIR) logs 77 | 78 | app: ebin/$(PROJECT).app 79 | $(eval MODULES := $(shell find ebin -type f -name \*.beam \ 80 | | sed 's/ebin\///;s/\.beam/,/' | sed '$$s/.$$//')) 81 | $(appsrc_verbose) cat src/$(PROJECT).app.src \ 82 | | sed 's/{modules, \[\]}/{modules, \[$(MODULES)\]}/' \ 83 | > ebin/$(PROJECT).app 84 | 85 | define compile_erl 86 | $(erlc_verbose) erlc -v $(ERLC_OPTS) -o ebin/ \ 87 | -pa ebin/ -I include/ $(COMPILE_FIRST_PATHS) $(1) 88 | endef 89 | 90 | define compile_xyrl 91 | $(xyrl_verbose) erlc -v -o ebin/ $(1) 92 | $(xyrl_verbose) erlc $(ERLC_OPTS) -o ebin/ ebin/*.erl 93 | @rm ebin/*.erl 94 | endef 95 | 96 | define compile_dtl 97 | $(dtl_verbose) erl -noshell -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/ -eval ' \ 98 | Compile = fun(F) -> \ 99 | Module = list_to_atom( \ 100 | string:to_lower(filename:basename(F, ".dtl")) ++ "_dtl"), \ 101 | erlydtl_compiler:compile(F, Module, [{out_dir, "ebin/"}]) \ 102 | end, \ 103 | _ = [Compile(F) || F <- string:tokens("$(1)", " ")], \ 104 | init:stop()' 105 | endef 106 | 107 | ebin/$(PROJECT).app: $(shell find src -type f -name \*.erl) \ 108 | $(shell find src -type f -name \*.core) \ 109 | $(shell find src -type f -name \*.xrl) \ 110 | $(shell find src -type f -name \*.yrl) \ 111 | $(shell find templates -type f -name \*.dtl 2>/dev/null) 112 | @mkdir -p ebin/ 113 | $(if $(strip $(filter %.erl %.core,$?)), \ 114 | $(call compile_erl,$(filter %.erl %.core,$?))) 115 | $(if $(strip $(filter %.xrl %.yrl,$?)), \ 116 | $(call compile_xyrl,$(filter %.xrl %.yrl,$?))) 117 | $(if $(strip $(filter %.dtl,$?)), \ 118 | $(call compile_dtl,$(filter %.dtl,$?))) 119 | 120 | clean: 121 | $(gen_verbose) rm -rf ebin/ test/*.beam erl_crash.dump 122 | 123 | # Dependencies. 124 | 125 | define get_dep 126 | @mkdir -p $(DEPS_DIR) 127 | ifeq (,$(findstring pkg://,$(word 1,$(dep_$(1))))) 128 | git clone -n -- $(word 1,$(dep_$(1))) $(DEPS_DIR)/$(1) 129 | else 130 | @if [ ! -f $(PKG_FILE) ]; then $(call get_pkg_file); fi 131 | git clone -n -- `awk 'BEGIN { FS = "\t" }; \ 132 | $$$$1 == "$(subst pkg://,,$(word 1,$(dep_$(1))))" { print $$$$2 }' \ 133 | $(PKG_FILE)` $(DEPS_DIR)/$(1) 134 | endif 135 | cd $(DEPS_DIR)/$(1) ; git checkout -q $(word 2,$(dep_$(1))) 136 | endef 137 | 138 | define dep_target 139 | $(DEPS_DIR)/$(1): 140 | $(call get_dep,$(1)) 141 | endef 142 | 143 | $(foreach dep,$(DEPS),$(eval $(call dep_target,$(dep)))) 144 | 145 | deps: $(ALL_DEPS_DIRS) 146 | @for dep in $(ALL_DEPS_DIRS) ; do \ 147 | if [ -f $$dep/Makefile ] ; then \ 148 | $(MAKE) -C $$dep ; \ 149 | else \ 150 | echo "include $(CURDIR)/erlang.mk" | $(MAKE) -f - -C $$dep ; \ 151 | fi ; \ 152 | done 153 | 154 | clean-deps: 155 | @for dep in $(ALL_DEPS_DIRS) ; do $(MAKE) -C $$dep clean; done 156 | 157 | # Documentation. 158 | 159 | docs: clean-docs 160 | $(gen_verbose) erl -noshell \ 161 | -eval 'edoc:application($(PROJECT), ".", []), init:stop().' 162 | 163 | clean-docs: 164 | $(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info 165 | 166 | # Tests. 167 | 168 | $(foreach dep,$(TEST_DEPS),$(eval $(call dep_target,$(dep)))) 169 | 170 | build-test-deps: $(ALL_TEST_DEPS_DIRS) 171 | @for dep in $(ALL_TEST_DEPS_DIRS) ; do $(MAKE) -C $$dep; done 172 | 173 | build-tests: build-test-deps 174 | $(gen_verbose) erlc -v $(ERLC_OPTS) -o test/ \ 175 | $(wildcard test/*.erl test/*/*.erl) -pa ebin/ 176 | 177 | CT_RUN = ct_run \ 178 | -no_auto_compile \ 179 | -noshell \ 180 | -pa $(realpath ebin) $(DEPS_DIR)/*/ebin \ 181 | -dir test \ 182 | -logdir logs 183 | # -cover test/cover.spec 184 | 185 | CT_SUITES ?= 186 | 187 | define test_target 188 | test_$(1): ERLC_OPTS += -DTEST=1 +'{parse_transform, eunit_autoexport}' 189 | test_$(1): clean deps app build-tests 190 | @if [ -d "test" ] ; \ 191 | then \ 192 | mkdir -p logs/ ; \ 193 | $(CT_RUN) -suite $(addsuffix _SUITE,$(1)) ; \ 194 | fi 195 | $(gen_verbose) rm -f test/*.beam 196 | endef 197 | 198 | $(foreach test,$(CT_SUITES),$(eval $(call test_target,$(test)))) 199 | 200 | tests: ERLC_OPTS += -DTEST=1 +'{parse_transform, eunit_autoexport}' 201 | tests: clean deps app build-tests 202 | @if [ -d "test" ] ; \ 203 | then \ 204 | mkdir -p logs/ ; \ 205 | $(CT_RUN) -suite $(addsuffix _SUITE,$(CT_SUITES)) ; \ 206 | fi 207 | $(gen_verbose) rm -f test/*.beam 208 | 209 | # Dialyzer. 210 | 211 | PLT_APPS ?= 212 | DIALYZER_OPTS ?= -Werror_handling -Wrace_conditions \ 213 | -Wunmatched_returns # -Wunderspecs 214 | 215 | build-plt: deps app 216 | @dialyzer --build_plt --output_plt .$(PROJECT).plt \ 217 | --apps erts kernel stdlib $(PLT_APPS) $(ALL_DEPS_DIRS) 218 | 219 | dialyze: 220 | @dialyzer --src src --plt .$(PROJECT).plt --no_native $(DIALYZER_OPTS) 221 | 222 | # Packages. 223 | 224 | $(PKG_FILE): 225 | @$(call get_pkg_file) 226 | 227 | pkg-list: $(PKG_FILE) 228 | @cat $(PKG_FILE) | awk 'BEGIN { FS = "\t" }; { print \ 229 | "Name:\t\t" $$1 "\n" \ 230 | "Repository:\t" $$2 "\n" \ 231 | "Website:\t" $$3 "\n" \ 232 | "Description:\t" $$4 "\n" }' 233 | 234 | ifdef q 235 | pkg-search: $(PKG_FILE) 236 | @cat $(PKG_FILE) | grep -i ${q} | awk 'BEGIN { FS = "\t" }; { print \ 237 | "Name:\t\t" $$1 "\n" \ 238 | "Repository:\t" $$2 "\n" \ 239 | "Website:\t" $$3 "\n" \ 240 | "Description:\t" $$4 "\n" }' 241 | else 242 | pkg-search: 243 | @echo "Usage: make pkg-search q=STRING" 244 | endif 245 | -------------------------------------------------------------------------------- /src/random_wh06_int.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | 4 | %% Modified version of random module 5 | %% to use Wichmann-Hill algorithm published on 2006 6 | %% which succeeds the old AS183 algorithm in 1982. 7 | 8 | %% Copyright (c) 2010 Kenji Rikitake All rights reserved. 9 | %% Copyright (c) 2012-2020 Michael Truog All rights reserved. 10 | 11 | %% 12 | %% %CopyrightBegin% 13 | %% 14 | %% Copyright Ericsson AB 1996-2011. All Rights Reserved. 15 | %% 16 | %% Licensed under the Apache License, Version 2.0 (the "License"); 17 | %% you may not use this file except in compliance with the License. 18 | %% You may obtain a copy of the License at 19 | %% 20 | %% http://www.apache.org/licenses/LICENSE-2.0 21 | %% 22 | %% Unless required by applicable law or agreed to in writing, software 23 | %% distributed under the License is distributed on an "AS IS" BASIS, 24 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 25 | %% See the License for the specific language governing permissions and 26 | %% limitations under the License. 27 | %% 28 | %% %CopyrightEnd% 29 | %% 30 | -module(random_wh06_int). 31 | 32 | %% Reasonable random number generator (period is 2.66e36): 33 | %% The method is attributed to B. A. Wichmann and I. D. Hill 34 | %% See "Generating good pseudo-random numbers", 35 | %% Computational Statistics & Data Analysis 51 (2006) 1614-1622. 36 | 37 | %% Explanation: 38 | %% Example C code, using 64-bit integer arithmetic 39 | %% (from Richard O'Keefe) 40 | %% 41 | %% a = (a * 11600LL) % 2147483579; 42 | %% b = (b * 47003LL) % 2147483543; 43 | %% c = (c * 23000LL) % 2147483423; 44 | %% d = (d * 33000LL) % 2147483123; 45 | %% w = a/2147483579.0 + b/2147483543.0 46 | %% + c/2147483423.0 + d/2147483123.0; 47 | %% if (w >= 2.0) w -= 2.0; 48 | %% if (w >= 1.0) w -= 1.0; 49 | %% return w; 50 | %% 51 | %% To avoid floating-point precision problems, 52 | %% it is best to use Erlang's native bigint support: 53 | %% 54 | %% B1 = (11600 * A1) rem 2147483579, 55 | %% B2 = (47003 * A2) rem 2147483543, 56 | %% B3 = (23000 * A3) rem 2147483423, 57 | %% B4 = (33000 * A4) rem 2147483123, 58 | %% put(random_wh06_seed, {B1, B2, B3, B4}), 59 | %% I = ((B1 * 9903516371291919229607132747) + 60 | %% (B2 * 9903516537312557910938853791) + 61 | %% (B3 * 9903517090714727049595319831) + 62 | %% (B4 * 9903518474220420479167438931)) 63 | %% rem 21267638781707063560975648195455661513, 64 | %% 65 | %% (21267638781707063560975648195455661513 == 66 | %% 2147483579 * 2147483543 * 2147483423 * 2147483123, 67 | %% so w * 21267638781707063560975648195455661513.0 == I, 68 | %% based on modular arithmetic) 69 | %% 70 | %% The algorithm provides 123 bits of randomness: 71 | %% 1> math:log(21267638781707063560975648195455661513) / math:log(2). 72 | %% 73 | 74 | -export([seed0/0, seed/0, seed/1, seed/4, 75 | uniform/0, uniform/1, 76 | uniform_s/1, uniform_s/2, 77 | next_sequence/1]). 78 | 79 | -define(PRIME1, 2147483579). 80 | -define(PRIME2, 2147483543). 81 | -define(PRIME3, 2147483423). 82 | -define(PRIME4, 2147483123). 83 | 84 | -define(SEED_DICT, random_wh06_seed). 85 | 86 | %%----------------------------------------------------------------------- 87 | %% The type of the state 88 | 89 | -type seed() :: {pos_integer(), pos_integer(), pos_integer(), pos_integer()}. 90 | 91 | %%----------------------------------------------------------------------- 92 | 93 | -spec seed0() -> {123456789, 345678901, 567890123, 789012345}. 94 | 95 | seed0() -> 96 | {123456789, 345678901, 567890123, 789012345}. 97 | 98 | %% seed() 99 | %% Seed random number generation with default values 100 | 101 | -spec seed() -> seed(). 102 | 103 | seed() -> 104 | reseed(seed0()). 105 | 106 | %% seed({A1, A2, A3, A4}) 107 | %% Seed random number generation 108 | 109 | -spec seed(seed()) -> 110 | 'undefined' | seed(). 111 | 112 | seed({A1, A2, A3, A4}) -> 113 | seed(A1, A2, A3, A4). 114 | 115 | %% seed(A1, A2, A3, A4) 116 | %% Seed random number generation 117 | 118 | -spec seed(pos_integer(), pos_integer(), pos_integer(), pos_integer()) -> 119 | 'undefined' | seed(). 120 | 121 | seed(A1, A2, A3, A4) 122 | when is_integer(A1), A1 > 0, 123 | is_integer(A2), A2 > 0, 124 | is_integer(A3), A3 > 0, 125 | is_integer(A4), A4 > 0 -> 126 | put(?SEED_DICT, 127 | {(A1 rem (?PRIME1 - 1)) + 1, 128 | (A2 rem (?PRIME2 - 1)) + 1, 129 | (A3 rem (?PRIME3 - 1)) + 1, 130 | (A4 rem (?PRIME4 - 1)) + 1}). 131 | 132 | reseed({A1, A2, A3, A4}) -> 133 | case seed(A1, A2, A3, A4) of 134 | undefined -> seed0(); 135 | {_,_,_,_} = Tuple -> Tuple 136 | end. 137 | 138 | %% uniform() 139 | %% Returns a random integer between 140 | %% 0 and 21267638781707063560975648195455661512. 141 | 142 | -spec uniform() -> non_neg_integer(). 143 | 144 | uniform() -> 145 | {A1, A2, A3, A4} = case get(?SEED_DICT) of 146 | undefined -> seed0(); 147 | Tuple -> Tuple 148 | end, 149 | 150 | B1 = (11600 * A1) rem ?PRIME1, 151 | B2 = (47003 * A2) rem ?PRIME2, 152 | B3 = (23000 * A3) rem ?PRIME3, 153 | B4 = (33000 * A4) rem ?PRIME4, 154 | 155 | put(?SEED_DICT, {B1, B2, B3, B4}), 156 | 157 | I = ((B1 * 9903516371291919229607132747) + 158 | (B2 * 9903516537312557910938853791) + 159 | (B3 * 9903517090714727049595319831) + 160 | (B4 * 9903518474220420479167438931)) 161 | rem 21267638781707063560975648195455661513, 162 | I. 163 | 164 | %% uniform(N) -> I 165 | %% Given an integer N > 1, N =< 21267638781707063560975648195455661513, 166 | %% uniform(N) returns a random integer 167 | %% between 1 and N. 168 | 169 | -spec uniform(pos_integer()) -> pos_integer(). 170 | 171 | uniform(N) 172 | when is_integer(N), N > 1, N =< 21267638781707063560975648195455661513 -> 173 | (uniform() rem N) + 1. 174 | 175 | %%% Functional versions 176 | 177 | %% uniform_s(State) -> {I, NewState} 178 | %% Returns a random integer I, between 179 | %% 0 and 21267638781707063560975648195455661512 (inclusive). 180 | 181 | -spec uniform_s(seed()) -> {non_neg_integer(), seed()}. 182 | 183 | uniform_s({A1, A2, A3, A4}) 184 | when is_integer(A1), A1 > 0, 185 | is_integer(A2), A2 > 0, 186 | is_integer(A3), A3 > 0, 187 | is_integer(A4), A4 > 0 -> 188 | B1 = (11600 * A1) rem ?PRIME1, 189 | B2 = (47003 * A2) rem ?PRIME2, 190 | B3 = (23000 * A3) rem ?PRIME3, 191 | B4 = (33000 * A4) rem ?PRIME4, 192 | 193 | I = ((B1 * 9903516371291919229607132747) + 194 | (B2 * 9903516537312557910938853791) + 195 | (B3 * 9903517090714727049595319831) + 196 | (B4 * 9903518474220420479167438931)) 197 | rem 21267638781707063560975648195455661513, 198 | 199 | {I, {B1, B2, B3, B4}}. 200 | 201 | %% uniform_s(N, State) -> {I, NewState} 202 | %% Given an integer N > 1, N =< 21267638781707063560975648195455661513, 203 | %% uniform(N) returns a random integer 204 | %% between 1 and N. 205 | 206 | -spec uniform_s(pos_integer(), seed()) -> {pos_integer(), seed()}. 207 | 208 | uniform_s(N, State0) 209 | when is_integer(N), N > 1, N =< 21267638781707063560975648195455661513 -> 210 | {I, State1} = uniform_s(State0), 211 | {(I rem N) + 1, State1}. 212 | 213 | %% generating another seed for multiple sequences 214 | 215 | -spec next_sequence(seed()) -> seed(). 216 | 217 | next_sequence({A1, A2, A3, A4}) 218 | when is_integer(A1), A1 > 0, 219 | is_integer(A2), A2 > 0, 220 | is_integer(A3), A3 > 0, 221 | is_integer(A4), A4 > 0 -> 222 | B1 = (11600 * A1) rem ?PRIME1, 223 | B2 = (47003 * A2) rem ?PRIME2, 224 | B3 = (23000 * A3) rem ?PRIME3, 225 | B4 = (33000 * A4) rem ?PRIME4, 226 | {B1, B2, B3, B4}. 227 | 228 | -------------------------------------------------------------------------------- /doc/quickrand_hash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module quickrand_hash 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module quickrand_hash

13 | 14 |

Random Number Generation With Hash Functions

15 | The random numbers created by the functions in this module are 16 | not meant for cryptographic purposes.

. 17 |

Copyright © 2017-2022 Michael Truog

18 | 19 |

Version: 2.0.5 Oct 26 2023 11:37:41 20 | ------------------------------------------------------------------------

21 |

Authors: Michael Truog (mjtruog at protonmail dot com).

22 | 23 |

Description

24 |

Random Number Generation With Hash Functions

25 | The random numbers created by the functions in this module are 26 | not meant for cryptographic purposes.

27 | 28 |

Any functions that have a jenkins prefix use Bob Jenkins' lookup3 hashing 29 | (lookup3, May 2006). In my testing, the function jenkins_32 is 4 times 30 | slower than erlang:phash2/1 because jenkins_32 is implemented in Erlang. 31 | Both the jenkins_32 and jenkins_64 functions execute at a speed similar to 32 | crypto:hash(ripemd160,_) with crypto:hash(sha256,_) slightly faster and 33 | crypto:hash(sha512,_) slightly slower.

34 | 35 |

Any functions that have a jenkins64 prefix use Bob Jenkins' SpookyHash 36 | (SpookyV2, August 5 2012). In my testing, the function jenkins64_128 is 37 | 4.5 times slower than crypto:hash(md5,_) which provides the same number 38 | of bits, because the jenkins64 functions are implemented in Erlang.

39 | 40 |

The jenkins prefix functions are faster than the jenkins64 prefix functions 41 | due to avoiding Erlang bignums and both provide the same quality.

42 | 43 | All the functions have been checked with the C++ implementations to ensure 44 | the same hash value is obtained, though this implementation forces numbers 45 | to be interpreted as big-endian. 46 |

Function Index

47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 |
jenkins64_128/1 48 |

Bob Jenkins SpookyHashV2 Hash128.

.
jenkins64_128/2 50 |

Bob Jenkins SpookyHashV2 Hash128.

.
jenkins64_32/1 52 |

Bob Jenkins SpookyHashV2 Hash32.

.
jenkins64_32/2 54 |

Bob Jenkins SpookyHashV2 Hash32.

.
jenkins64_64/1 56 |

Bob Jenkins SpookyHashV2 Hash64.

.
jenkins64_64/2 58 |

Bob Jenkins SpookyHashV2 Hash64.

.
jenkins_32/1 60 |

Bob Jenkins lookup3 hashword for iodata.

.
jenkins_32/2 62 |

Bob Jenkins lookup3 hashword for iodata.

.
jenkins_64/1 64 |

Bob Jenkins lookup3 hashword2 for iodata.

.
jenkins_64/2 66 |

Bob Jenkins lookup3 hashword2 for iodata.

.
68 | 69 |

Function Details

70 | 71 |

jenkins64_128/1

72 |
73 |

jenkins64_128(MessageRaw::iodata()) -> non_neg_integer()

74 |

75 |

76 |

Bob Jenkins SpookyHashV2 Hash128.

77 |

78 | 79 |

jenkins64_128/2

80 |
81 |

jenkins64_128(MessageRaw::iodata(), Seed::non_neg_integer()) -> non_neg_integer()

82 |

83 |

84 |

Bob Jenkins SpookyHashV2 Hash128.

85 |

86 | 87 |

jenkins64_32/1

88 |
89 |

jenkins64_32(MessageRaw::iodata()) -> non_neg_integer()

90 |

91 |

92 |

Bob Jenkins SpookyHashV2 Hash32.

93 |

94 | 95 |

jenkins64_32/2

96 |
97 |

jenkins64_32(MessageRaw::iodata(), Seed::non_neg_integer()) -> non_neg_integer()

98 |

99 |

100 |

Bob Jenkins SpookyHashV2 Hash32.

101 |

102 | 103 |

jenkins64_64/1

104 |
105 |

jenkins64_64(MessageRaw::iodata()) -> non_neg_integer()

106 |

107 |

108 |

Bob Jenkins SpookyHashV2 Hash64.

109 |

110 | 111 |

jenkins64_64/2

112 |
113 |

jenkins64_64(MessageRaw::iodata(), Seed::non_neg_integer()) -> non_neg_integer()

114 |

115 |

116 |

Bob Jenkins SpookyHashV2 Hash64.

117 |

118 | 119 |

jenkins_32/1

120 |
121 |

jenkins_32(MessageRaw::iodata()) -> non_neg_integer()

122 |

123 |

124 |

Bob Jenkins lookup3 hashword for iodata.

125 |

126 | 127 |

jenkins_32/2

128 |
129 |

jenkins_32(MessageRaw::iodata(), Seed::non_neg_integer()) -> non_neg_integer()

130 |

131 |

132 |

Bob Jenkins lookup3 hashword for iodata.

133 |

134 | 135 |

jenkins_64/1

136 |
137 |

jenkins_64(MessageRaw::iodata()) -> non_neg_integer()

138 |

139 |

140 |

Bob Jenkins lookup3 hashword2 for iodata.

141 |

142 | 143 |

jenkins_64/2

144 |
145 |

jenkins_64(MessageRaw::iodata(), Seed::non_neg_integer()) -> non_neg_integer()

146 |

147 |

148 |

Bob Jenkins lookup3 hashword2 for iodata.

149 |

150 |
151 | 152 | 153 |

Generated by EDoc

154 | 155 | 156 | -------------------------------------------------------------------------------- /doc/quickrand_cache.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module quickrand_cache 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module quickrand_cache

13 | 14 |

Quick Random Number Generation With Cached Data

15 | Use this module for data from crypto:strong_rand_bytes/1 while avoiding 16 | the high latency normally associated with crypto:strong_rand_bytes/1 17 | usage. Data is cached to minimize the latency from each 18 | crypto:strong_rand_bytes/1 function call.

19 | 20 | The cache_size option may be provided to either the init/1 function or 21 | the new/1 function to adjust the amount of data cached. 22 |

Copyright © 2017-2023 Michael Truog

23 | 24 |

Version: 2.0.6 Oct 26 2023 11:37:41 25 | ------------------------------------------------------------------------

26 |

Authors: Michael Truog (mjtruog at protonmail dot com).

27 | 28 |

Description

29 |

Quick Random Number Generation With Cached Data

30 | Use this module for data from crypto:strong_rand_bytes/1 while avoiding 31 | the high latency normally associated with crypto:strong_rand_bytes/1 32 | usage. Data is cached to minimize the latency from each 33 | crypto:strong_rand_bytes/1 function call.

34 | 35 | The cache_size option may be provided to either the init/1 function or 36 | the new/1 function to adjust the amount of data cached. The cache_size 37 | value should vary based on the amount of random data consumed for a 38 | single function call and can be set based on higher-level system testing. 39 | If the cache_size option is not provided, the default set in the 40 | quickrand Erlang/OTP application env cache_size configuration parameter 41 | is used (64 KB is the default setting). 42 |

Data Types

43 | 44 |

options()

45 |

options() = [{cache_size, Bytes::pos_integer()}]

46 | 47 | 48 |

state()

49 |

state() = #quickrand_cache{i = non_neg_integer(), cache_size = pos_integer(), cache = binary()}

50 | 51 | 52 |

Function Index

53 | 55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 | 75 | 77 | 79 | 81 | 83 | 85 | 87 | 89 | 91 |
destroy/0 54 |

Destroy cached process dictionary data if it exists.

.
float/0 56 |

Process dictionary cache version of quickrand:strong_float/0.

.
float/1 58 |

State cache version of quickrand:strong_float/0.

.
floatL/0 60 |

Process dictionary cache version of quickrand:strong_floatL/0.

.
floatL/1 62 |

State cache version of quickrand:strong_floatL/0.

.
floatM/0 64 |

Process dictionary cache version of quickrand:strong_floatM/0.

.
floatM/1 66 |

State cache version of quickrand:strong_floatM/0.

.
floatR/0 68 |

Process dictionary cache version of quickrand:strong_floatR/0.

.
floatR/1 70 |

State cache version of quickrand:strong_floatR/0.

.
init/0 72 |

Initialize cached process dictionary data.

.
init/1 74 |

Initialize cached process dictionary data with options.

.
new/0 76 |

Initialize cached state data.

.
new/1 78 |

Initialize cached state data with options.

.
rand_bytes/1 80 |

Get random bytes using cached process dictionary data.

.
rand_bytes/2 82 |

Get random bytes using cached state data.

.
uniform/1 84 |

Process dictionary cache version of quickrand:strong_uniform/1.

.
uniform/2 86 |

State cache version of quickrand:strong_uniform/1.

.
uniform_range/2 88 |

Process dictionary cache version of quickrand:strong_uniform_range/2.

.
uniform_range/3 90 |

State cache version of quickrand:strong_uniform_range/2.

.
92 | 93 |

Function Details

94 | 95 |

destroy/0

96 |
97 |

destroy() -> ok

98 |

99 |

100 |

Destroy cached process dictionary data if it exists.

101 |

102 | 103 |

float/0

104 |
105 |

float() -> float()

106 |

107 |

108 |

Process dictionary cache version of quickrand:strong_float/0.

109 |

110 | 111 |

float/1

112 |
113 |

float(State::state()) -> {float(), state()}

114 |

115 |

116 |

State cache version of quickrand:strong_float/0.

117 |

118 | 119 |

floatL/0

120 |
121 |

floatL() -> float()

122 |

123 |

124 |

Process dictionary cache version of quickrand:strong_floatL/0.

125 |

126 | 127 |

floatL/1

128 |
129 |

floatL(State::state()) -> {float(), state()}

130 |

131 |

132 |

State cache version of quickrand:strong_floatL/0.

133 |

134 | 135 |

floatM/0

136 |
137 |

floatM() -> float()

138 |

139 |

140 |

Process dictionary cache version of quickrand:strong_floatM/0.

141 |

142 | 143 |

floatM/1

144 |
145 |

floatM(State::state()) -> {float(), state()}

146 |

147 |

148 |

State cache version of quickrand:strong_floatM/0.

149 |

150 | 151 |

floatR/0

152 |
153 |

floatR() -> float()

154 |

155 |

156 |

Process dictionary cache version of quickrand:strong_floatR/0.

157 |

158 | 159 |

floatR/1

160 |
161 |

floatR(State::state()) -> {float(), state()}

162 |

163 |

164 |

State cache version of quickrand:strong_floatR/0.

165 |

166 | 167 |

init/0

168 |
169 |

init() -> ok

170 |

171 |

172 |

Initialize cached process dictionary data.

173 |

174 | 175 |

init/1

176 |
177 |

init(Options::options()) -> ok

178 |

179 |

180 |

Initialize cached process dictionary data with options.

181 |

182 | 183 |

new/0

184 |
185 |

new() -> state()

186 |

187 |

188 |

Initialize cached state data.

189 |

190 | 191 |

new/1

192 |
193 |

new(Options::options()) -> state()

194 |

195 |

196 |

Initialize cached state data with options.

197 |

198 | 199 |

rand_bytes/1

200 |
201 |

rand_bytes(N::pos_integer()) -> binary()

202 |

203 |

204 |

Get random bytes using cached process dictionary data.

205 |

206 | 207 |

rand_bytes/2

208 |
209 |

rand_bytes(N::pos_integer(), State::state()) -> {binary(), state()}

210 |

211 |

212 |

Get random bytes using cached state data.

213 |

214 | 215 |

uniform/1

216 |
217 |

uniform(N::pos_integer()) -> pos_integer()

218 |

219 |

220 |

Process dictionary cache version of quickrand:strong_uniform/1.

221 |

222 | 223 |

uniform/2

224 |
225 |

uniform(N::pos_integer(), State::state()) -> {pos_integer(), state()}

226 |

227 |

228 |

State cache version of quickrand:strong_uniform/1.

229 |

230 | 231 |

uniform_range/2

232 |
233 |

uniform_range(Min::integer(), Max::non_neg_integer()) -> integer()

234 |

235 |

236 |

Process dictionary cache version of quickrand:strong_uniform_range/2.

237 |

238 | 239 |

uniform_range/3

240 |
241 |

uniform_range(Min::integer(), Max::non_neg_integer(), State::state()) -> {integer(), state()}

242 |

243 |

244 |

State cache version of quickrand:strong_uniform_range/2.

245 |

246 |
247 | 248 | 249 |

Generated by EDoc

250 | 251 | 252 | -------------------------------------------------------------------------------- /src/quickrand_cache.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% @doc 6 | %%% ==Quick Random Number Generation With Cached Data== 7 | %%% Use this module for data from crypto:strong_rand_bytes/1 while avoiding 8 | %%% the high latency normally associated with crypto:strong_rand_bytes/1 9 | %%% usage. Data is cached to minimize the latency from each 10 | %%% crypto:strong_rand_bytes/1 function call. 11 | %%% 12 | %%% The cache_size option may be provided to either the init/1 function or 13 | %%% the new/1 function to adjust the amount of data cached. The cache_size 14 | %%% value should vary based on the amount of random data consumed for a 15 | %%% single function call and can be set based on higher-level system testing. 16 | %%% If the cache_size option is not provided, the default set in the 17 | %%% quickrand Erlang/OTP application env cache_size configuration parameter 18 | %%% is used (64 KB is the default setting). 19 | %%% @end 20 | %%% 21 | %%% MIT License 22 | %%% 23 | %%% Copyright (c) 2017-2023 Michael Truog 24 | %%% 25 | %%% Permission is hereby granted, free of charge, to any person obtaining a 26 | %%% copy of this software and associated documentation files (the "Software"), 27 | %%% to deal in the Software without restriction, including without limitation 28 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 29 | %%% and/or sell copies of the Software, and to permit persons to whom the 30 | %%% Software is furnished to do so, subject to the following conditions: 31 | %%% 32 | %%% The above copyright notice and this permission notice shall be included in 33 | %%% all copies or substantial portions of the Software. 34 | %%% 35 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 38 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 | %%% DEALINGS IN THE SOFTWARE. 42 | %%% 43 | %%% @author Michael Truog 44 | %%% @copyright 2017-2023 Michael Truog 45 | %%% @version 2.0.6 {@date} {@time} 46 | %%%------------------------------------------------------------------------ 47 | 48 | -module(quickrand_cache). 49 | -author('mjtruog at protonmail dot com'). 50 | 51 | %% external interface 52 | -export([destroy/0, 53 | float/0, 54 | float/1, 55 | floatL/0, 56 | floatL/1, 57 | floatM/0, 58 | floatM/1, 59 | floatR/0, 60 | floatR/1, 61 | init/0, 62 | init/1, 63 | new/0, 64 | new/1, 65 | rand_bytes/1, 66 | rand_bytes/2, 67 | uniform/1, 68 | uniform/2, 69 | uniform_range/2, 70 | uniform_range/3]). 71 | 72 | -record(quickrand_cache, 73 | { 74 | i :: non_neg_integer(), 75 | cache_size :: pos_integer(), % bytes 76 | cache :: binary() 77 | }). 78 | -define(TUPLE_PDICT_KEY, quickrand_cache). 79 | 80 | -type options() :: list({cache_size, Bytes :: pos_integer()}). 81 | -type state() :: #quickrand_cache{}. 82 | -export_type([options/0, 83 | state/0]). 84 | 85 | -include("quickrand_constants.hrl"). 86 | -include("quickrand_internal.hrl"). 87 | 88 | %%------------------------------------------------------------------------- 89 | %% @doc 90 | %% ===Destroy cached process dictionary data if it exists.=== 91 | %% @end 92 | %%------------------------------------------------------------------------- 93 | 94 | -spec destroy() -> 95 | ok. 96 | 97 | destroy() -> 98 | _ = erlang:put(?TUPLE_PDICT_KEY, undefined), 99 | ok. 100 | 101 | %%------------------------------------------------------------------------- 102 | %% @doc 103 | %% ===Process dictionary cache version of quickrand:strong_float/0.=== 104 | %% @end 105 | %%------------------------------------------------------------------------- 106 | 107 | -spec float() -> 108 | float(). 109 | 110 | float() -> 111 | <> = rand_bytes(7), 112 | if 113 | Bit == 1, I == 0 -> 114 | 1.0; 115 | true -> 116 | I * ?DBL_EPSILON_DIV2 117 | end. 118 | 119 | %%------------------------------------------------------------------------- 120 | %% @doc 121 | %% ===State cache version of quickrand:strong_float/0.=== 122 | %% @end 123 | %%------------------------------------------------------------------------- 124 | 125 | -spec float(State :: state()) -> 126 | {float(), state()}. 127 | 128 | float(State) -> 129 | {<>, NewState} = rand_bytes(7, State), 130 | Value = if 131 | Bit == 1, I == 0 -> 132 | 1.0; 133 | true -> 134 | I * ?DBL_EPSILON_DIV2 135 | end, 136 | {Value, NewState}. 137 | 138 | %%------------------------------------------------------------------------- 139 | %% @doc 140 | %% ===Process dictionary cache version of quickrand:strong_floatL/0.=== 141 | %% @end 142 | %%------------------------------------------------------------------------- 143 | 144 | -spec floatL() -> 145 | float(). 146 | 147 | floatL() -> 148 | <> = rand_bytes(7), 149 | I * ?DBL_EPSILON_DIV2. 150 | 151 | %%------------------------------------------------------------------------- 152 | %% @doc 153 | %% ===State cache version of quickrand:strong_floatL/0.=== 154 | %% @end 155 | %%------------------------------------------------------------------------- 156 | 157 | -spec floatL(State :: state()) -> 158 | {float(), state()}. 159 | 160 | floatL(State) -> 161 | {<>, NewState} = rand_bytes(7, State), 162 | {I * ?DBL_EPSILON_DIV2, NewState}. 163 | 164 | %%------------------------------------------------------------------------- 165 | %% @doc 166 | %% ===Process dictionary cache version of quickrand:strong_floatM/0.=== 167 | %% @end 168 | %%------------------------------------------------------------------------- 169 | 170 | -spec floatM() -> 171 | float(). 172 | 173 | floatM() -> 174 | <> = rand_bytes(7), 175 | if 176 | I == 0 -> 177 | floatM(); 178 | true -> 179 | I * ?DBL_EPSILON_DIV2 180 | end. 181 | 182 | %%------------------------------------------------------------------------- 183 | %% @doc 184 | %% ===State cache version of quickrand:strong_floatM/0.=== 185 | %% @end 186 | %%------------------------------------------------------------------------- 187 | 188 | -spec floatM(State :: state()) -> 189 | {float(), state()}. 190 | 191 | floatM(State) -> 192 | {<>, NewState} = rand_bytes(7, State), 193 | if 194 | I == 0 -> 195 | floatM(NewState); 196 | true -> 197 | {I * ?DBL_EPSILON_DIV2, NewState} 198 | end. 199 | 200 | %%------------------------------------------------------------------------- 201 | %% @doc 202 | %% ===Process dictionary cache version of quickrand:strong_floatR/0.=== 203 | %% @end 204 | %%------------------------------------------------------------------------- 205 | 206 | -spec floatR() -> 207 | float(). 208 | 209 | floatR() -> 210 | <> = rand_bytes(7), 211 | (I + 1) * ?DBL_EPSILON_DIV2. 212 | 213 | %%------------------------------------------------------------------------- 214 | %% @doc 215 | %% ===State cache version of quickrand:strong_floatR/0.=== 216 | %% @end 217 | %%------------------------------------------------------------------------- 218 | 219 | -spec floatR(State :: state()) -> 220 | {float(), state()}. 221 | 222 | floatR(State) -> 223 | {<>, NewState} = rand_bytes(7, State), 224 | {(I + 1) * ?DBL_EPSILON_DIV2, NewState}. 225 | 226 | %%------------------------------------------------------------------------- 227 | %% @doc 228 | %% ===Initialize cached process dictionary data.=== 229 | %% @end 230 | %%------------------------------------------------------------------------- 231 | 232 | -spec init() -> 233 | ok. 234 | 235 | init() -> 236 | init([]). 237 | 238 | %%------------------------------------------------------------------------- 239 | %% @doc 240 | %% ===Initialize cached process dictionary data with options.=== 241 | %% @end 242 | %%------------------------------------------------------------------------- 243 | 244 | -spec init(Options :: options()) -> 245 | ok. 246 | 247 | init(Options) -> 248 | _ = erlang:put(?TUPLE_PDICT_KEY, state_to_tuple(new(Options))), 249 | ok. 250 | 251 | %%------------------------------------------------------------------------- 252 | %% @doc 253 | %% ===Initialize cached state data.=== 254 | %% @end 255 | %%------------------------------------------------------------------------- 256 | 257 | -spec new() -> 258 | state(). 259 | 260 | new() -> 261 | new([]). 262 | 263 | %%------------------------------------------------------------------------- 264 | %% @doc 265 | %% ===Initialize cached state data with options.=== 266 | %% @end 267 | %%------------------------------------------------------------------------- 268 | 269 | -spec new(Options :: options()) -> 270 | state(). 271 | 272 | new(Options) -> 273 | {CacheSize, []} = option(cache_size, Options), 274 | true = is_integer(CacheSize) andalso (CacheSize > 0), 275 | #quickrand_cache{i = 0, 276 | cache_size = CacheSize, 277 | cache = crypto:strong_rand_bytes(CacheSize)}. 278 | 279 | %%------------------------------------------------------------------------- 280 | %% @doc 281 | %% ===Get random bytes using cached process dictionary data.=== 282 | %% @end 283 | %%------------------------------------------------------------------------- 284 | 285 | -spec rand_bytes(N :: pos_integer()) -> 286 | binary(). 287 | 288 | rand_bytes(N) 289 | when is_integer(N), N > 0 -> 290 | {I, CacheSize, Cache} = erlang:get(?TUPLE_PDICT_KEY), 291 | {Bytes, NewI, NewCache} = bytes_get(N, I, CacheSize, Cache), 292 | _ = erlang:put(?TUPLE_PDICT_KEY, {NewI, CacheSize, NewCache}), 293 | Bytes. 294 | 295 | %%------------------------------------------------------------------------- 296 | %% @doc 297 | %% ===Get random bytes using cached state data.=== 298 | %% @end 299 | %%------------------------------------------------------------------------- 300 | 301 | -spec rand_bytes(N :: pos_integer(), 302 | State :: state()) -> 303 | {binary(), state()}. 304 | 305 | rand_bytes(N, #quickrand_cache{i = I, 306 | cache_size = CacheSize, 307 | cache = Cache} = State) 308 | when is_integer(N), N > 0 -> 309 | {Bytes, NewI, NewCache} = bytes_get(N, I, CacheSize, Cache), 310 | {Bytes, State#quickrand_cache{i = NewI, 311 | cache = NewCache}}. 312 | 313 | %%------------------------------------------------------------------------- 314 | %% @doc 315 | %% ===Process dictionary cache version of quickrand:strong_uniform/1.=== 316 | %% @end 317 | %%------------------------------------------------------------------------- 318 | 319 | -spec uniform(N :: pos_integer()) -> 320 | pos_integer(). 321 | 322 | uniform(N) when is_integer(N), N < 1 -> 323 | erlang:exit(badarg); 324 | 325 | uniform(1) -> 326 | 1; 327 | 328 | uniform(N) when is_integer(N), N > 1 -> 329 | Bytes = bytes(N), 330 | Bits = Bytes * 8, 331 | <> = rand_bytes(Bytes), 332 | (I rem N) + 1. 333 | 334 | %%------------------------------------------------------------------------- 335 | %% @doc 336 | %% ===State cache version of quickrand:strong_uniform/1.=== 337 | %% @end 338 | %%------------------------------------------------------------------------- 339 | 340 | -spec uniform(N :: pos_integer(), 341 | State :: state()) -> 342 | {pos_integer(), state()}. 343 | 344 | uniform(N, _) when is_integer(N), N < 1 -> 345 | erlang:exit(badarg); 346 | 347 | uniform(1, State) -> 348 | {1, State}; 349 | 350 | uniform(N, State) when is_integer(N), N > 1 -> 351 | Bytes = bytes(N), 352 | Bits = Bytes * 8, 353 | {<>, NewState} = rand_bytes(Bytes, State), 354 | {(I rem N) + 1, NewState}. 355 | 356 | %%------------------------------------------------------------------------- 357 | %% @doc 358 | %% ===Process dictionary cache version of quickrand:strong_uniform_range/2.=== 359 | %% @end 360 | %%------------------------------------------------------------------------- 361 | 362 | -spec uniform_range(Min :: integer(), 363 | Max :: non_neg_integer()) -> 364 | integer(). 365 | 366 | uniform_range(Min, Max) 367 | when is_integer(Min), is_integer(Max), Max >= 0 -> 368 | if 369 | Min == Max -> 370 | Min; 371 | Min < Max -> 372 | uniform(1 + Max - Min) - 1 + Min 373 | end. 374 | 375 | %%------------------------------------------------------------------------- 376 | %% @doc 377 | %% ===State cache version of quickrand:strong_uniform_range/2.=== 378 | %% @end 379 | %%------------------------------------------------------------------------- 380 | 381 | -spec uniform_range(Min :: integer(), 382 | Max :: non_neg_integer(), 383 | State :: state()) -> 384 | {integer(), state()}. 385 | 386 | uniform_range(Min, Max, State) 387 | when is_integer(Min), is_integer(Max), Max >= 0 -> 388 | if 389 | Min == Max -> 390 | {Min, State}; 391 | Min < Max -> 392 | {Value, NewState} = uniform(1 + Max - Min, State), 393 | {Value - 1 + Min, NewState} 394 | end. 395 | 396 | %%%------------------------------------------------------------------------ 397 | %%% Private functions 398 | %%%------------------------------------------------------------------------ 399 | 400 | bytes_get(N, I, CacheSize, Cache) -> 401 | BytesExist = CacheSize - I, 402 | BytesExistUsed = erlang:min(N, BytesExist), 403 | BytesOld = if 404 | BytesExistUsed == 0 -> 405 | <<>>; 406 | true -> 407 | binary:part(Cache, I, BytesExistUsed) 408 | end, 409 | if 410 | BytesExist < N -> 411 | BytesExtra = N - BytesExist, 412 | <> = 414 | crypto:strong_rand_bytes(BytesExtra + CacheSize), 415 | {<>, 0, NewCache}; 416 | true -> 417 | {BytesOld, I + BytesExistUsed, Cache} 418 | end. 419 | 420 | state_to_tuple(#quickrand_cache{i = I, 421 | cache_size = CacheSize, 422 | cache = Cache}) -> 423 | {I, CacheSize, Cache}. 424 | 425 | option(Key, Options) -> 426 | case lists:keytake(Key, 1, Options) of 427 | false -> 428 | {ok, Value} = application:get_env(?APPLICATION, Key), 429 | {Value, Options}; 430 | {value, {Key, Value}, NewOptions} -> 431 | {Value, NewOptions} 432 | end. 433 | 434 | -ifdef(TEST). 435 | -include_lib("eunit/include/eunit.hrl"). 436 | 437 | -include("quickrand_test.hrl"). 438 | 439 | module_test_() -> 440 | {timeout, ?TEST_TIMEOUT, [ 441 | {"process dictionary tests", ?_assertOk(t_process_dictionary())}, 442 | {"state tests", ?_assertOk(t_state())} 443 | ]}. 444 | 445 | t_process_dictionary() -> 446 | ok = init([{cache_size, 8}]), 447 | Random0 = rand_bytes(2), 448 | 2 = byte_size(Random0), 449 | Random1 = rand_bytes(6), 450 | 6 = byte_size(Random1), 451 | Random2 = rand_bytes(8), 452 | 8 = byte_size(Random2), 453 | Random3 = rand_bytes(1024), 454 | 1024 = byte_size(Random3), 455 | Random4 = rand_bytes(1023), 456 | 1023 = byte_size(Random4), 457 | ok. 458 | 459 | t_state() -> 460 | State0 = new([{cache_size, 8}]), 461 | {Random0, State1} = rand_bytes(2, State0), 462 | 2 = byte_size(Random0), 463 | {Random1, State2} = rand_bytes(6, State1), 464 | 6 = byte_size(Random1), 465 | {Random2, State3} = rand_bytes(8, State2), 466 | 8 = byte_size(Random2), 467 | {Random3, State4} = rand_bytes(1024, State3), 468 | 1024 = byte_size(Random3), 469 | {Random4, _} = rand_bytes(1023, State4), 470 | 1023 = byte_size(Random4), 471 | ok. 472 | 473 | -endif. 474 | -------------------------------------------------------------------------------- /doc/quickrand.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Module quickrand 6 | 7 | 8 | 9 | 10 |
11 | 12 |

Module quickrand

13 | 14 |

Quick Random Number Generation

. 15 |

Copyright © 2012-2023 Michael Truog

16 | 17 |

Version: 2.0.7 Oct 26 2023 11:37:41 18 | ------------------------------------------------------------------------

19 |

Authors: Michael Truog (mjtruog at protonmail dot com).

20 | 21 |

Description

22 |

Quick Random Number Generation

23 | 24 |

Data Types

25 | 26 |

algorithms()

27 |

algorithms() = lcg35x | mwc59x | mwc256 | rand | random_wh06_int | random_wh82

28 | 29 | 30 |

Function Index

31 | 36 | 55 | 58 | 61 | 78 | 89 | 95 | 97 | 100 | 103 | 106 | 108 | 110 | 113 | 116 | 119 |
lcg35x_32/1 32 |

35-bit state 32-bit value Linear Congruential Generators xor.

33 | Both algorithms used for the variables LCG and MCG provide 34 | fast low-quality pseudo-random number generation without using 35 | Erlang bignums.
mwc256/1 37 |

256-bit state Marsaglia multiply-with-carry generator.

38 |
  T = A * X0 + C0
 39 |   X1 = Y0
 40 |   Y1 = Z0
 41 |   C1 = T bsr 64
 42 |   Z1 = T band 16#ffffffffffffffff
 43 |   A = 16#ff377e26f82da74a, 0 < X0, 0 < Y0, 0 < Z0, 0 < C0 < A - 1
 44 |  
 45 |   Simulates a multiplicative LCG with prime modulus
 46 |   M = 16#ff377e26f82da749ffffffffffffffffffffffffffffffffffffffffffffffff .
 47 |   The period is approximately 2^255.

48 | Vigna, Sebastiano. 49 | https://prng.di.unimi.it/MWC256.c 50 | https://prng.di.unimi.it/#quality 51 | TestU01 BigCrush passed (p-value statistics are in [0.001..0.999]) 52 | when starting from 100 equispaced points of the state space.

53 | 54 | Marsaglia, George.
mwc256_128/1 56 |

256-bit state 128-bit value Marsaglia multiply-with-carry generator.

57 | mwc256/1 limited to a 128-bit return value for less latency.
mwc256_64/1 59 |

256-bit state 64-bit value Marsaglia multiply-with-carry generator.

60 | mwc256/1 limited to a 64-bit return value for less latency.
mwc59x_32/1 62 |

59-bit state 32-bit value Marsaglia multiply-with-carry generator xor.

63 |
  T = A * X0 + C0
 64 |   C1 = T bsr 32
 65 |   X1 = T band 16#ffffffff
 66 |   A = 16#7fa6502, 0 < X0, 0 < C0 < A - 1
 67 |  
 68 |   Simulates a multiplicative LCG with prime modulus
 69 |   M = 16#7fa6501ffffffff (M = A * 2^32 - 1).
 70 |   The period is approximately 2^58.

71 | X1 and C1 are combined with xor to produce a 32-bit random number. 72 | TestU01 SmallCrush/Crush/BigCrush have been used to test the 32-bit result 73 | (both with the bits forward and reversed) 74 | and the p-value statistics are in [0.0000001..0.9999999] 75 | (when starting from 100 equispaced points of the state space). 76 | The wider bounds (i.e., wider than [0.001..0.999]) are due to the 77 | shorter period.

.
seed/0 79 |

Randomized seeding of random number generators.

80 | Backwards-compatible seeding of random number generators for this 81 | module's uniform prefix functions and the external modules used 82 | (rand, random_wh06_int and random_wh82). 83 | Use seed/1 to seed specific random number generators.

84 | 85 | Instead of using this function, it is better to use a jump function 86 | for obtaining non-overlapping sequences, if a jump function is available 87 | and the number of Erlang processes used is limited 88 | (to ensure concurrent usage of the same algorithm has no collisions).
seed/1 90 |

Randomized seeding of specific random number generators.

91 | Instead of using this function, it is better to use a jump function 92 | for obtaining non-overlapping sequences, if a jump function is available 93 | and the number of Erlang processes used is limited 94 | (to ensure concurrent usage of the same algorithm has no collisions).
strong_float/0 96 |

Return an Erlang double-precision random number with the range [0.0 .. 1.0].

.
strong_floatL/0 98 |

Return an Erlang double-precision random number with the range [0.0 .. 1.0).

99 | Left portion of the 0.0 to 1.0 range.
strong_floatM/0 101 |

Return an Erlang double-precision random number with the range (0.0 .. 1.0).

102 | Middle portion of the 0.0 to 1.0 range.
strong_floatR/0 104 |

Return an Erlang double-precision random number with the range (0.0 .. 1.0].

105 | Right portion of the 0.0 to 1.0 range.
strong_uniform/1 107 |

Strong uniform random number generation.

.
strong_uniform_range/2 109 |

Strong uniform random number generation in a range.

.
uniform/1 111 |

Quick uniform random number generation.

112 | Not meant for cryptographic purposes.
uniform_cache/1 114 |

Quick uniform random number generation with cached data.

115 | Not meant for cryptographic purposes.
uniform_cache/2 117 |

Quick uniform random number generation with cached data.

118 | Not meant for cryptographic purposes.
120 | 121 |

Function Details

122 | 123 |

lcg35x_32/1

124 |
125 |

lcg35x_32(N::1..4294967296) -> 1..4294967296

126 |

127 |

128 |

35-bit state 32-bit value Linear Congruential Generators xor.

129 | Both algorithms used for the variables LCG and MCG provide 130 | fast low-quality pseudo-random number generation without using 131 | Erlang bignums. 132 |
  LCG:
133 |     35-bit classical Linear Congruential Generator
134 |     based on Erlang/OTP 25.0-rc3 rand:lcg35/1.
135 |  
136 |     X1 = (A * X0 + C) rem M
137 |     A = 15319397, C = 15366142135, M = 2^35
138 |  
139 |     C is an odd value close to M / sqrt(5).
140 |     The period is M (i.e., 2^35).
141 |  
142 |   MCG:
143 |     35-bit Multiplicative Congruential Generator
144 |     (i.e., Lehmer random number generator,
145 |      Park-Miller random number generator)
146 |     based on Erlang/OTP 25.0-rc3 rand:mcg35/1.
147 |  
148 |     X1 = (A * X0) rem M
149 |     A = 185852, B = 35, D = 31, M = 2^B - D
150 |  
151 |     D makes M prime (M == 34359738337) so X0 is always coprime.
152 |     The period is M (i.e., 2^35 - 31).

153 | The LCG and MCG are combined with xor to produce a 32-bit random number. 154 | TestU01 SmallCrush/Crush/BigCrush have been used to test the 32-bit result 155 | (both with the bits forward and reversed) 156 | and the p-value statistics are in [0.0000001..0.9999999] 157 | (when starting from 100 equispaced points of the state space). 158 | The wider bounds (i.e., wider than [0.001..0.999]) are due to the 159 | shorter period.

160 | 161 |

mwc59x_32/1 is slighly more efficient but provides slightly less randomness 162 | (same p-value statistics bounds but the separate sums of 163 | (1e-8 .. 1e-4] and [1 - 1e-4 .. 1 - 1e-8) are less extreme 164 | for lcg35x_32/1, i.e., the mwc59x_32/1 (1e-8 .. 1e-4] sum is 25.5% smaller 165 | and the mwc59x_32/1 [1 - 1e-4 .. 1 - 1e-8) sum is 16.1% larger while 166 | mwc59x_32/1 provides roughly a 1.08x speedup with Erlang/OTP 25.0).

167 | 168 |

Pierre L'Ecuyer, Richard Simard. 169 | TestU01: A C Library for Empirical Testing of Random Number Generators. 170 | ACM Transactions on Mathematical Software, vol. 33, iss. 4, article 22, 2007. 171 | http://portal.acm.org/citation.cfm?doid=1268776.1268777 172 | http://simul.iro.umontreal.ca/testu01/tu01.html

173 | 174 | (A is selected from) 175 | L'Ecuyer, Pierre. Tables of linear congruential generators of 176 | different sizes and good lattice structure. 177 | Mathematics of Computation, vol. 68, no. 225, pp. 249–260, 1999. 178 | https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/ 179 | https://www.iro.umontreal.ca/~lecuyer/myftp/papers/latrules99Errata.pdf

180 | 181 |

mwc256/1

182 |
183 |

mwc256(N::pos_integer()) -> pos_integer()

184 |

185 |

186 |

256-bit state Marsaglia multiply-with-carry generator.

187 |
  T = A * X0 + C0
188 |   X1 = Y0
189 |   Y1 = Z0
190 |   C1 = T bsr 64
191 |   Z1 = T band 16#ffffffffffffffff
192 |   A = 16#ff377e26f82da74a, 0 < X0, 0 < Y0, 0 < Z0, 0 < C0 < A - 1
193 |  
194 |   Simulates a multiplicative LCG with prime modulus
195 |   M = 16#ff377e26f82da749ffffffffffffffffffffffffffffffffffffffffffffffff .
196 |   The period is approximately 2^255.

197 | Vigna, Sebastiano. 198 | https://prng.di.unimi.it/MWC256.c 199 | https://prng.di.unimi.it/#quality 200 | TestU01 BigCrush passed (p-value statistics are in [0.001..0.999]) 201 | when starting from 100 equispaced points of the state space.

202 | 203 | Marsaglia, George. Xorshift RNGs. 204 | Journal of Statistical Software, vol. 8, no. 14, pp. 1–6, 2003-07. 205 | https://doi.org/10.18637/jss.v008.i14

206 | 207 |

mwc256_128/1

208 |
209 |

mwc256_128(N::1..340282366920938463463374607431768211456) -> 1..340282366920938463463374607431768211456

210 |

211 |

212 |

256-bit state 128-bit value Marsaglia multiply-with-carry generator.

213 | mwc256/1 limited to a 128-bit return value for less latency.

214 | 215 |

mwc256_64/1

216 |
217 |

mwc256_64(N::1..18446744073709551616) -> 1..18446744073709551616

218 |

219 |

220 |

256-bit state 64-bit value Marsaglia multiply-with-carry generator.

221 | mwc256/1 limited to a 64-bit return value for less latency.

222 | 223 |

mwc59x_32/1

224 |
225 |

mwc59x_32(N::1..4294967296) -> 1..4294967296

226 |

227 |

228 |

59-bit state 32-bit value Marsaglia multiply-with-carry generator xor.

229 |
  T = A * X0 + C0
230 |   C1 = T bsr 32
231 |   X1 = T band 16#ffffffff
232 |   A = 16#7fa6502, 0 < X0, 0 < C0 < A - 1
233 |  
234 |   Simulates a multiplicative LCG with prime modulus
235 |   M = 16#7fa6501ffffffff (M = A * 2^32 - 1).
236 |   The period is approximately 2^58.

237 | X1 and C1 are combined with xor to produce a 32-bit random number. 238 | TestU01 SmallCrush/Crush/BigCrush have been used to test the 32-bit result 239 | (both with the bits forward and reversed) 240 | and the p-value statistics are in [0.0000001..0.9999999] 241 | (when starting from 100 equispaced points of the state space). 242 | The wider bounds (i.e., wider than [0.001..0.999]) are due to the 243 | shorter period.

244 | 245 |

rand:mwc59/1 in Erlang/OTP 25.0 is similar. However, usage of rand:mwc59/1 246 | with rand:mwc59_value32/1 clearly fails the TestU01 Crush and BigCrush tests 247 | (e.g., with X0 and C0 initially set to 1). mwc59x_32/1 was created 248 | to provide more statistically significant randomness than is possible when 249 | using rand:mwc59/1 .

250 | 251 |

Pierre L'Ecuyer, Richard Simard. 252 | TestU01: A C Library for Empirical Testing of Random Number Generators. 253 | ACM Transactions on Mathematical Software, vol. 33, iss. 4, article 22, 2007. 254 | http://portal.acm.org/citation.cfm?doid=1268776.1268777 255 | http://simul.iro.umontreal.ca/testu01/tu01.html

256 | 257 | Marsaglia, George. Xorshift RNGs. 258 | Journal of Statistical Software, vol. 8, no. 14, pp. 1–6, 2003-07. 259 | https://doi.org/10.18637/jss.v008.i14

260 | 261 |

seed/0

262 |
263 |

seed() -> ok

264 |

265 |

266 |

Randomized seeding of random number generators.

267 | Backwards-compatible seeding of random number generators for this 268 | module's uniform prefix functions and the external modules used 269 | (rand, random_wh06_int and random_wh82). 270 | Use seed/1 to seed specific random number generators.

271 | 272 | Instead of using this function, it is better to use a jump function 273 | for obtaining non-overlapping sequences, if a jump function is available 274 | and the number of Erlang processes used is limited 275 | (to ensure concurrent usage of the same algorithm has no collisions).

276 | 277 |

seed/1

278 |
279 |

seed(L::[all | quickrand | algorithms(), ...]) -> ok

280 |

281 |

282 |

Randomized seeding of specific random number generators.

283 | Instead of using this function, it is better to use a jump function 284 | for obtaining non-overlapping sequences, if a jump function is available 285 | and the number of Erlang processes used is limited 286 | (to ensure concurrent usage of the same algorithm has no collisions).

287 | 288 |

strong_float/0

289 |
290 |

strong_float() -> float()

291 |

292 |

293 |

Return an Erlang double-precision random number with the range [0.0 .. 1.0].

294 |

295 | 296 |

strong_floatL/0

297 |
298 |

strong_floatL() -> float()

299 |

300 |

301 |

Return an Erlang double-precision random number with the range [0.0 .. 1.0).

302 | Left portion of the 0.0 to 1.0 range.

303 | 304 |

strong_floatM/0

305 |
306 |

strong_floatM() -> float()

307 |

308 |

309 |

Return an Erlang double-precision random number with the range (0.0 .. 1.0).

310 | Middle portion of the 0.0 to 1.0 range.

311 | 312 |

strong_floatR/0

313 |
314 |

strong_floatR() -> float()

315 |

316 |

317 |

Return an Erlang double-precision random number with the range (0.0 .. 1.0].

318 | Right portion of the 0.0 to 1.0 range.

319 | 320 |

strong_uniform/1

321 |
322 |

strong_uniform(N::pos_integer()) -> pos_integer()

323 |

324 |

325 |

Strong uniform random number generation.

326 |

327 | 328 |

strong_uniform_range/2

329 |
330 |

strong_uniform_range(Min::integer(), Max::non_neg_integer()) -> integer()

331 |

332 |

333 |

Strong uniform random number generation in a range.

334 |

335 | 336 |

uniform/1

337 |
338 |

uniform(N::pos_integer()) -> pos_integer()

339 |

340 |

341 |

Quick uniform random number generation.

342 | Not meant for cryptographic purposes.

343 | 344 |

uniform_cache/1

345 |
346 |

uniform_cache(N::pos_integer()) -> pos_integer()

347 |

348 |

349 |

Quick uniform random number generation with cached data.

350 | Not meant for cryptographic purposes.

351 | 352 |

uniform_cache/2

353 |
354 |

uniform_cache(N::pos_integer(), State::quickrand_cache:state()) -> {pos_integer(), quickrand_cache:state()}

355 |

356 |

357 |

Quick uniform random number generation with cached data.

358 | Not meant for cryptographic purposes.

359 |
360 | 361 | 362 |

Generated by EDoc

363 | 364 | 365 | -------------------------------------------------------------------------------- /src/quickrand.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% @doc 6 | %%% ==Quick Random Number Generation== 7 | %%% @end 8 | %%% 9 | %%% MIT License 10 | %%% 11 | %%% Copyright (c) 2012-2023 Michael Truog 12 | %%% 13 | %%% Permission is hereby granted, free of charge, to any person obtaining a 14 | %%% copy of this software and associated documentation files (the "Software"), 15 | %%% to deal in the Software without restriction, including without limitation 16 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 17 | %%% and/or sell copies of the Software, and to permit persons to whom the 18 | %%% Software is furnished to do so, subject to the following conditions: 19 | %%% 20 | %%% The above copyright notice and this permission notice shall be included in 21 | %%% all copies or substantial portions of the Software. 22 | %%% 23 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | %%% DEALINGS IN THE SOFTWARE. 30 | %%% 31 | %%% @author Michael Truog 32 | %%% @copyright 2012-2023 Michael Truog 33 | %%% @version 2.0.7 {@date} {@time} 34 | %%%------------------------------------------------------------------------ 35 | 36 | -module(quickrand). 37 | -author('mjtruog at protonmail dot com'). 38 | 39 | %% external interface 40 | -export([lcg35x_32/1, 41 | mwc59x_32/1, 42 | mwc256/1, 43 | mwc256_64/1, 44 | mwc256_128/1, 45 | seed/0, 46 | seed/1, 47 | strong_float/0, 48 | strong_floatL/0, 49 | strong_floatM/0, 50 | strong_floatR/0, 51 | strong_uniform/1, 52 | strong_uniform_range/2, 53 | uniform/1, 54 | uniform_cache/1, 55 | uniform_cache/2]). 56 | 57 | -ifdef(ERLANG_OTP_VERSION_16). 58 | -else. 59 | -ifdef(ERLANG_OTP_VERSION_17). 60 | -else. 61 | -define(ERLANG_OTP_VERSION_18_FEATURES, true). 62 | -ifdef(ERLANG_OTP_VERSION_18). 63 | -else. 64 | -ifdef(ERLANG_OTP_VERSION_19). 65 | -else. 66 | -define(ERLANG_OTP_VERSION_20_FEATURES, true). 67 | -ifdef(OTP_RELEASE). % Erlang/OTP >= 21.0 68 | % able to use -if/-elif here 69 | -if(?OTP_RELEASE >= 25). 70 | -endif. 71 | -endif. 72 | -endif. 73 | -endif. 74 | -endif. 75 | -endif. 76 | 77 | -ifdef(ERLANG_OTP_VERSION_20_FEATURES). 78 | -define(TIME_UNIT_MICROSECOND, microsecond). 79 | -else. 80 | -define(TIME_UNIT_MICROSECOND, micro_seconds). 81 | -endif. 82 | 83 | -define(ALGORITHMS, 84 | [lcg35x, 85 | mwc59x, mwc256, 86 | rand, random_wh06_int, random_wh82]). 87 | -ifdef(ERLANG_OTP_VERSION_18_FEATURES). 88 | -define(UNIFORM_FUNCTION_ALGORITHMS, [lcg35x, mwc59x, rand, mwc256]). 89 | -else. 90 | -define(UNIFORM_FUNCTION_ALGORITHMS, [lcg35x, mwc59x, mwc256]). 91 | -endif. 92 | 93 | -define(LCG35X_PDICT_KEY, quickrand_lcg35x_seed). 94 | -define(MWC59X_PDICT_KEY, quickrand_mwc59x_seed). 95 | -define(MWC256_PDICT_KEY, quickrand_mwc256_seed). 96 | 97 | -type algorithms() :: 98 | lcg35x | 99 | mwc59x | mwc256 | 100 | rand | random_wh06_int | random_wh82. 101 | -export_type([algorithms/0]). 102 | 103 | -include("quickrand_constants.hrl"). 104 | -include("quickrand_internal.hrl"). 105 | 106 | %%%------------------------------------------------------------------------ 107 | %%% External interface functions 108 | %%%------------------------------------------------------------------------ 109 | 110 | %%------------------------------------------------------------------------- 111 | %% @doc 112 | %% ===35-bit state 32-bit value Linear Congruential Generators xor.=== 113 | %% Both algorithms used for the variables LCG and MCG provide 114 | %% fast low-quality pseudo-random number generation without using 115 | %% Erlang bignums. 116 | %% ``` 117 | %% LCG: 118 | %% 35-bit classical Linear Congruential Generator 119 | %% based on Erlang/OTP 25.0-rc3 rand:lcg35/1. 120 | %% 121 | %% X1 = (A * X0 + C) rem M 122 | %% A = 15319397, C = 15366142135, M = 2^35 123 | %% 124 | %% C is an odd value close to M / sqrt(5). 125 | %% The period is M (i.e., 2^35). 126 | %% 127 | %% MCG: 128 | %% 35-bit Multiplicative Congruential Generator 129 | %% (i.e., Lehmer random number generator, 130 | %% Park-Miller random number generator) 131 | %% based on Erlang/OTP 25.0-rc3 rand:mcg35/1. 132 | %% 133 | %% X1 = (A * X0) rem M 134 | %% A = 185852, B = 35, D = 31, M = 2^B - D 135 | %% 136 | %% D makes M prime (M == 34359738337) so X0 is always coprime. 137 | %% The period is M (i.e., 2^35 - 31). 138 | %% ''' 139 | %% The LCG and MCG are combined with xor to produce a 32-bit random number. 140 | %% TestU01 SmallCrush/Crush/BigCrush have been used to test the 32-bit result 141 | %% (both with the bits forward and reversed) 142 | %% and the p-value statistics are in [0.0000001..0.9999999] 143 | %% (when starting from 100 equispaced points of the state space). 144 | %% The wider bounds (i.e., wider than [0.001..0.999]) are due to the 145 | %% shorter period. 146 | %% 147 | %% mwc59x_32/1 is slighly more efficient but provides slightly less randomness 148 | %% (same p-value statistics bounds but the separate sums of 149 | %% (1e-8 .. 1e-4] and [1 - 1e-4 .. 1 - 1e-8) are less extreme 150 | %% for lcg35x_32/1, i.e., the mwc59x_32/1 (1e-8 .. 1e-4] sum is 25.5% smaller 151 | %% and the mwc59x_32/1 [1 - 1e-4 .. 1 - 1e-8) sum is 16.1% larger while 152 | %% mwc59x_32/1 provides roughly a 1.08x speedup with Erlang/OTP 25.0). 153 | %% 154 | %% Pierre L'Ecuyer, Richard Simard. 155 | %% TestU01: A C Library for Empirical Testing of Random Number Generators. 156 | %% ACM Transactions on Mathematical Software, vol. 33, iss. 4, article 22, 2007. 157 | %% http://portal.acm.org/citation.cfm?doid=1268776.1268777 158 | %% http://simul.iro.umontreal.ca/testu01/tu01.html 159 | %% 160 | %% (A is selected from) 161 | %% L'Ecuyer, Pierre. Tables of linear congruential generators of 162 | %% different sizes and good lattice structure. 163 | %% Mathematics of Computation, vol. 68, no. 225, pp. 249–260, 1999. 164 | %% https://www.ams.org/journals/mcom/1999-68-225/S0025-5718-99-00996-5/ 165 | %% https://www.iro.umontreal.ca/~lecuyer/myftp/papers/latrules99Errata.pdf 166 | %% @end 167 | %%------------------------------------------------------------------------- 168 | 169 | -spec lcg35x_32(N :: 1..(1 + ?BITMASK_32)) -> 170 | 1..(1 + ?BITMASK_32). 171 | 172 | lcg35x_32(N) -> 173 | {LCG1, MCG1} = case erlang:get(?LCG35X_PDICT_KEY) of 174 | undefined -> 175 | <> = crypto:strong_rand_bytes(9), 178 | {LCG0 + 1, MCG0 + 1}; 179 | {LCG0, MCG0} = Seed when is_integer(LCG0), is_integer(MCG0) -> 180 | Seed 181 | end, 182 | LCGN = (15319397 * (LCG1 band ?BITMASK_35) + 183 | 15366142135) band ?BITMASK_35, 184 | MCG2 = 185852 * (MCG1 band ?BITMASK_35), 185 | MCG3 = (MCG2 band ?BITMASK_35) + 31 * (MCG2 bsr 35), 186 | MCGN = if 187 | MCG3 =< ?BITMASK_35 - 31 -> 188 | MCG3; 189 | true -> 190 | % an optimization to avoid rem 191 | MCG3 - (1 + ?BITMASK_35 - 31) 192 | end, 193 | _ = erlang:put(?LCG35X_PDICT_KEY, {LCGN, MCGN}), 194 | (((LCG1 bsr 4) bxor (MCG1 bsr 2)) rem N) + 1. 195 | 196 | %%------------------------------------------------------------------------- 197 | %% @doc 198 | %% ===59-bit state 32-bit value Marsaglia multiply-with-carry generator xor.=== 199 | %% ``` 200 | %% T = A * X0 + C0 201 | %% C1 = T bsr 32 202 | %% X1 = T band 16#ffffffff 203 | %% A = 16#7fa6502, 0 < X0, 0 < C0 < A - 1 204 | %% 205 | %% Simulates a multiplicative LCG with prime modulus 206 | %% M = 16#7fa6501ffffffff (M = A * 2^32 - 1). 207 | %% The period is approximately 2^58. 208 | %% ''' 209 | %% X1 and C1 are combined with xor to produce a 32-bit random number. 210 | %% TestU01 SmallCrush/Crush/BigCrush have been used to test the 32-bit result 211 | %% (both with the bits forward and reversed) 212 | %% and the p-value statistics are in [0.0000001..0.9999999] 213 | %% (when starting from 100 equispaced points of the state space). 214 | %% The wider bounds (i.e., wider than [0.001..0.999]) are due to the 215 | %% shorter period. 216 | %% 217 | %% rand:mwc59/1 in Erlang/OTP 25.0 is similar. However, usage of rand:mwc59/1 218 | %% with rand:mwc59_value32/1 clearly fails the TestU01 Crush and BigCrush tests 219 | %% (e.g., with X0 and C0 initially set to 1). mwc59x_32/1 was created 220 | %% to provide more statistically significant randomness than is possible when 221 | %% using rand:mwc59/1 . 222 | %% 223 | %% Pierre L'Ecuyer, Richard Simard. 224 | %% TestU01: A C Library for Empirical Testing of Random Number Generators. 225 | %% ACM Transactions on Mathematical Software, vol. 33, iss. 4, article 22, 2007. 226 | %% http://portal.acm.org/citation.cfm?doid=1268776.1268777 227 | %% http://simul.iro.umontreal.ca/testu01/tu01.html 228 | %% 229 | %% Marsaglia, George. Xorshift RNGs. 230 | %% Journal of Statistical Software, vol. 8, no. 14, pp. 1–6, 2003-07. 231 | %% https://doi.org/10.18637/jss.v008.i14 232 | %% @end 233 | %%------------------------------------------------------------------------- 234 | 235 | -spec mwc59x_32(N :: 1..(1 + ?BITMASK_32)) -> 236 | 1..(1 + ?BITMASK_32). 237 | 238 | mwc59x_32(N) -> 239 | {X1, C1} = case erlang:get(?MWC59X_PDICT_KEY) of 240 | undefined -> 241 | <> = crypto:strong_rand_bytes(8), 244 | {X0 + 1, C0 + 1}; 245 | {X0, C0} = Seed 246 | when is_integer(X0), is_integer(C0) -> 247 | Seed 248 | end, 249 | T = (16#7fa6502 * X1 + C1) band ?BITMASK_59, 250 | CN = T bsr 32, 251 | XN = T band ?BITMASK_32, 252 | _ = erlang:put(?MWC59X_PDICT_KEY, {XN, CN}), 253 | ((XN bxor (CN bsl 3)) rem N) + 1. 254 | 255 | %%------------------------------------------------------------------------- 256 | %% @doc 257 | %% ===256-bit state Marsaglia multiply-with-carry generator.=== 258 | %% ``` 259 | %% T = A * X0 + C0 260 | %% X1 = Y0 261 | %% Y1 = Z0 262 | %% C1 = T bsr 64 263 | %% Z1 = T band 16#ffffffffffffffff 264 | %% A = 16#ff377e26f82da74a, 0 < X0, 0 < Y0, 0 < Z0, 0 < C0 < A - 1 265 | %% 266 | %% Simulates a multiplicative LCG with prime modulus 267 | %% M = 16#ff377e26f82da749ffffffffffffffffffffffffffffffffffffffffffffffff . 268 | %% The period is approximately 2^255. 269 | %% ''' 270 | %% Vigna, Sebastiano. 271 | %% https://prng.di.unimi.it/MWC256.c 272 | %% https://prng.di.unimi.it/#quality 273 | %% TestU01 BigCrush passed (p-value statistics are in [0.001..0.999]) 274 | %% when starting from 100 equispaced points of the state space. 275 | %% 276 | %% Marsaglia, George. Xorshift RNGs. 277 | %% Journal of Statistical Software, vol. 8, no. 14, pp. 1–6, 2003-07. 278 | %% https://doi.org/10.18637/jss.v008.i14 279 | %% @end 280 | %%------------------------------------------------------------------------- 281 | 282 | -spec mwc256(N :: pos_integer()) -> 283 | pos_integer(). 284 | 285 | mwc256(N) when is_integer(N), N > 0 -> 286 | {X1, Y1, Z1, C1} = case erlang:get(?MWC256_PDICT_KEY) of 287 | undefined -> 288 | <> = crypto:strong_rand_bytes(32), 293 | {X0 + 1, Y0 + 1, Z0 + 1, C0 + 1}; 294 | {X0, Y0, Z0, C0} = Seed 295 | when is_integer(X0), is_integer(Y0), 296 | is_integer(Z0), is_integer(C0) -> 297 | Seed 298 | end, 299 | mwc256_next(N, 0, X1, Y1, Z1, C1, N). 300 | 301 | %%------------------------------------------------------------------------- 302 | %% @doc 303 | %% ===256-bit state 64-bit value Marsaglia multiply-with-carry generator.=== 304 | %% mwc256/1 limited to a 64-bit return value for less latency. 305 | %% @end 306 | %%------------------------------------------------------------------------- 307 | 308 | -spec mwc256_64(N :: 1..(1 + ?BITMASK_64)) -> 309 | 1..(1 + ?BITMASK_64). 310 | 311 | mwc256_64(N) -> 312 | {X1, Y1, Z1, C1} = case erlang:get(?MWC256_PDICT_KEY) of 313 | undefined -> 314 | <> = crypto:strong_rand_bytes(32), 319 | {X0 + 1, Y0 + 1, Z0 + 1, C0 + 1}; 320 | {X0, Y0, Z0, C0} = Seed 321 | when is_integer(X0), is_integer(Y0), 322 | is_integer(Z0), is_integer(C0) -> 323 | Seed 324 | end, 325 | T = 16#ff377e26f82da74a * X1 + C1, 326 | XN = Y1, 327 | YN = Z1, 328 | CN = T bsr 64, 329 | ZN = T band ?BITMASK_64, 330 | _ = erlang:put(?MWC256_PDICT_KEY, {XN, YN, ZN, CN}), 331 | (ZN rem N) + 1. 332 | 333 | %%------------------------------------------------------------------------- 334 | %% @doc 335 | %% ===256-bit state 128-bit value Marsaglia multiply-with-carry generator.=== 336 | %% mwc256/1 limited to a 128-bit return value for less latency. 337 | %% @end 338 | %%------------------------------------------------------------------------- 339 | 340 | -spec mwc256_128(N :: 1..(1 + ?BITMASK_128)) -> 341 | 1..(1 + ?BITMASK_128). 342 | 343 | mwc256_128(N) -> 344 | {X1, Y1, Z1, C1} = case erlang:get(?MWC256_PDICT_KEY) of 345 | undefined -> 346 | <> = crypto:strong_rand_bytes(32), 351 | {X0 + 1, Y0 + 1, Z0 + 1, C0 + 1}; 352 | {X0, Y0, Z0, C0} = Seed 353 | when is_integer(X0), is_integer(Y0), 354 | is_integer(Z0), is_integer(C0) -> 355 | Seed 356 | end, 357 | A = 16#ff377e26f82da74a, 358 | T0 = A * X1 + C1, 359 | X2 = Y1, 360 | Y2 = Z1, 361 | C2 = T0 bsr 64, 362 | Z2 = T0 band ?BITMASK_64, 363 | T1 = A * X2 + C2, 364 | XN = Y2, 365 | YN = Z2, 366 | CN = T1 bsr 64, 367 | ZN = T1 band ?BITMASK_64, 368 | _ = erlang:put(?MWC256_PDICT_KEY, {XN, YN, ZN, CN}), 369 | (((Z2 bsl 64) bor ZN) rem N) + 1. 370 | 371 | %%------------------------------------------------------------------------- 372 | %% @doc 373 | %% ===Randomized seeding of random number generators.=== 374 | %% Backwards-compatible seeding of random number generators for this 375 | %% module's uniform prefix functions and the external modules used 376 | %% (rand, random_wh06_int and random_wh82). 377 | %% Use seed/1 to seed specific random number generators. 378 | %% 379 | %% Instead of using this function, it is better to use a jump function 380 | %% for obtaining non-overlapping sequences, if a jump function is available 381 | %% and the number of Erlang processes used is limited 382 | %% (to ensure concurrent usage of the same algorithm has no collisions). 383 | %% @end 384 | %%------------------------------------------------------------------------- 385 | 386 | -spec seed() -> 387 | ok. 388 | 389 | seed() -> 390 | seed([quickrand, rand, random_wh06_int, random_wh82]). 391 | 392 | %%------------------------------------------------------------------------- 393 | %% @doc 394 | %% ===Randomized seeding of specific random number generators.=== 395 | %% Instead of using this function, it is better to use a jump function 396 | %% for obtaining non-overlapping sequences, if a jump function is available 397 | %% and the number of Erlang processes used is limited 398 | %% (to ensure concurrent usage of the same algorithm has no collisions). 399 | %% @end 400 | %%------------------------------------------------------------------------- 401 | 402 | -spec seed(nonempty_list(all | quickrand | algorithms())) -> 403 | ok. 404 | 405 | seed([_ | _] = L) -> 406 | seed_algorithms(L). 407 | 408 | %%------------------------------------------------------------------------- 409 | %% @doc 410 | %% ===Return an Erlang double-precision random number with the range [0.0 .. 1.0].=== 411 | %% @end 412 | %%------------------------------------------------------------------------- 413 | 414 | -spec strong_float() -> 415 | float(). 416 | 417 | strong_float() -> 418 | % 53 bits maximum for double precision floating point representation 419 | % (need to use a maximum value of math:pow(2, 53) with extra bit, 420 | % i.e. 1 + 16#1fffffffffffff) 421 | <> = crypto:strong_rand_bytes(7), 422 | if 423 | Bit == 1, I == 0 -> 424 | 1.0; 425 | true -> 426 | I * ?DBL_EPSILON_DIV2 427 | end. 428 | 429 | %%------------------------------------------------------------------------- 430 | %% @doc 431 | %% ===Return an Erlang double-precision random number with the range [0.0 .. 1.0).=== 432 | %% Left portion of the 0.0 to 1.0 range. 433 | %% @end 434 | %%------------------------------------------------------------------------- 435 | 436 | -spec strong_floatL() -> 437 | float(). 438 | 439 | strong_floatL() -> 440 | <> = crypto:strong_rand_bytes(7), 441 | I * ?DBL_EPSILON_DIV2. 442 | 443 | %%------------------------------------------------------------------------- 444 | %% @doc 445 | %% ===Return an Erlang double-precision random number with the range (0.0 .. 1.0).=== 446 | %% Middle portion of the 0.0 to 1.0 range. 447 | %% @end 448 | %%------------------------------------------------------------------------- 449 | 450 | -spec strong_floatM() -> 451 | float(). 452 | 453 | strong_floatM() -> 454 | <> = crypto:strong_rand_bytes(7), 455 | if 456 | I == 0 -> 457 | % almost never executes this case, an additional function call 458 | % is necessary to have a uniform distribution 459 | strong_floatM(); 460 | true -> 461 | I * ?DBL_EPSILON_DIV2 462 | end. 463 | 464 | %%------------------------------------------------------------------------- 465 | %% @doc 466 | %% ===Return an Erlang double-precision random number with the range (0.0 .. 1.0].=== 467 | %% Right portion of the 0.0 to 1.0 range. 468 | %% @end 469 | %%------------------------------------------------------------------------- 470 | 471 | -spec strong_floatR() -> 472 | float(). 473 | 474 | strong_floatR() -> 475 | <> = crypto:strong_rand_bytes(7), 476 | (I + 1) * ?DBL_EPSILON_DIV2. 477 | 478 | %%------------------------------------------------------------------------- 479 | %% @doc 480 | %% ===Strong uniform random number generation.=== 481 | %% @end 482 | %%------------------------------------------------------------------------- 483 | 484 | -spec strong_uniform(N :: pos_integer()) -> 485 | pos_integer(). 486 | 487 | strong_uniform(N) when is_integer(N) -> 488 | if 489 | N < 1 -> 490 | erlang:exit(badarg); 491 | N == 1 -> 492 | 1; 493 | N > 1 -> 494 | Bytes = bytes(N), 495 | Bits = Bytes * 8, 496 | <> = crypto:strong_rand_bytes(Bytes), 497 | (I rem N) + 1 498 | end. 499 | 500 | %%------------------------------------------------------------------------- 501 | %% @doc 502 | %% ===Strong uniform random number generation in a range.=== 503 | %% @end 504 | %%------------------------------------------------------------------------- 505 | 506 | -spec strong_uniform_range(Min :: integer(), 507 | Max :: non_neg_integer()) -> 508 | integer(). 509 | 510 | strong_uniform_range(Min, Max) 511 | when is_integer(Min), is_integer(Max), Max >= 0 -> 512 | if 513 | Min == Max -> 514 | Min; 515 | Min < Max -> 516 | strong_uniform(1 + Max - Min) - 1 + Min 517 | end. 518 | 519 | %%------------------------------------------------------------------------- 520 | %% @doc 521 | %% ===Quick uniform random number generation.=== 522 | %% Not meant for cryptographic purposes. 523 | %% @end 524 | %%------------------------------------------------------------------------- 525 | 526 | -spec uniform(N :: pos_integer()) -> 527 | pos_integer(). 528 | 529 | -ifdef(ERLANG_OTP_VERSION_18_FEATURES). 530 | 531 | uniform(N) when is_integer(N) -> 532 | if 533 | N < 1 -> 534 | erlang:exit(badarg); 535 | N == 1 -> 536 | 1; 537 | N =< ?BITMASK_32 -> 538 | % 32 bits, period 3.44e10 539 | lcg35x_32(N); 540 | N =< 16#3ffffffffffffff -> 541 | % assuming exsplus/exsp for 58 bits, period 8.31e34 542 | rand:uniform(N); 543 | N =< ?BITMASK_64 -> 544 | % 64 bits, period 5.79e76 545 | mwc256_64(N); 546 | N =< ?BITMASK_128 -> 547 | % 128 bits, period 5.79e76 548 | mwc256_128(N); 549 | N =< ?BITMASK_1024 -> 550 | % mwc256 for up to 1024 bits, period 5.79e76 551 | mwc256(N); 552 | true -> 553 | strong_uniform(N) 554 | end. 555 | 556 | -else. 557 | 558 | uniform(N) when is_integer(N) -> 559 | if 560 | N < 1 -> 561 | erlang:exit(badarg); 562 | N == 1 -> 563 | 1; 564 | N =< ?BITMASK_32 -> 565 | % 32 bits, period 3.44e10 566 | lcg35x_32(N); 567 | N =< ?BITMASK_64 -> 568 | % 64 bits, period 5.79e76 569 | mwc256_64(N); 570 | N =< ?BITMASK_128 -> 571 | % 128 bits, period 5.79e76 572 | mwc256_128(N); 573 | N =< ?BITMASK_1024 -> 574 | % mwc256 for up to 1024 bits, period 5.79e76 575 | mwc256(N); 576 | true -> 577 | strong_uniform(N) 578 | end. 579 | 580 | -endif. 581 | 582 | %%------------------------------------------------------------------------- 583 | %% @doc 584 | %% ===Quick uniform random number generation with cached data.=== 585 | %% Not meant for cryptographic purposes. 586 | %% @end 587 | %%------------------------------------------------------------------------- 588 | 589 | -spec uniform_cache(N :: pos_integer()) -> 590 | pos_integer(). 591 | 592 | uniform_cache(N) when is_integer(N) -> 593 | if 594 | N < 1 -> 595 | erlang:exit(badarg); 596 | N == 1 -> 597 | 1; 598 | N =< ?BITMASK_32 -> 599 | % 32 bits, period 3.44e10 600 | lcg35x_32(N); 601 | N > ?BITMASK_32 -> 602 | quickrand_cache:uniform(N) 603 | end. 604 | 605 | %%------------------------------------------------------------------------- 606 | %% @doc 607 | %% ===Quick uniform random number generation with cached data.=== 608 | %% Not meant for cryptographic purposes. 609 | %% @end 610 | %%------------------------------------------------------------------------- 611 | 612 | -spec uniform_cache(N :: pos_integer(), 613 | State :: quickrand_cache:state()) -> 614 | {pos_integer(), quickrand_cache:state()}. 615 | 616 | uniform_cache(N, State) when is_integer(N) -> 617 | if 618 | N < 1 -> 619 | erlang:exit(badarg); 620 | N == 1 -> 621 | {1, State}; 622 | N =< ?BITMASK_32 -> 623 | % 32 bits, period 3.44e10 624 | {lcg35x_32(N), State}; 625 | N > ?BITMASK_32 -> 626 | quickrand_cache:uniform(N, State) 627 | end. 628 | 629 | %%%------------------------------------------------------------------------ 630 | %%% Private functions 631 | %%%------------------------------------------------------------------------ 632 | 633 | mwc256_next(0, I, X0, Y0, Z0, C0, N) -> 634 | _ = erlang:put(?MWC256_PDICT_KEY, {X0, Y0, Z0, C0}), 635 | (I rem N) + 1; 636 | mwc256_next(B, I, X0, Y0, Z0, C0, N) -> 637 | T = 16#ff377e26f82da74a * X0 + C0, 638 | XN = Y0, 639 | YN = Z0, 640 | CN = T bsr 64, 641 | ZN = T band ?BITMASK_64, 642 | mwc256_next(B bsr 64, (I bsl 64) bor ZN, XN, YN, ZN, CN, N). 643 | 644 | seed_algorithms([]) -> 645 | ok; 646 | seed_algorithms([all]) -> 647 | seed_algorithms(?ALGORITHMS); 648 | seed_algorithms([quickrand | L]) -> 649 | seed_algorithms(lists:usort(?UNIFORM_FUNCTION_ALGORITHMS ++ L)); 650 | seed_algorithms([lcg35x | L]) -> 651 | 1 = lcg35x_32(1), 652 | seed_algorithms(L); 653 | seed_algorithms([mwc59x | L]) -> 654 | 1 = mwc59x_32(1), 655 | seed_algorithms(L); 656 | seed_algorithms([mwc256 | L]) -> 657 | 1 = mwc256_64(1), 658 | seed_algorithms(L); 659 | seed_algorithms([rand | L]) -> 660 | <> = crypto:strong_rand_bytes(22), 664 | IP1 = I1 + 1, 665 | IP2 = I2 + 1, 666 | IP3 = I3 + 1, 667 | ok = seed_algorithms_rand(IP1, IP2, IP3), 668 | seed_algorithms(L); 669 | seed_algorithms([random_wh06_int | L]) -> 670 | <> = crypto:strong_rand_bytes(16), 674 | IP1 = I1 + 1, 675 | IP2 = I2 + 1, 676 | IP3 = I3 + 1, 677 | IP4 = I4 + 1, 678 | _ = random_wh06_int:seed(IP1, IP2, IP3, IP4), 679 | seed_algorithms(L); 680 | seed_algorithms([random_wh82 | L]) -> 681 | <> = crypto:strong_rand_bytes(12), 684 | IP1 = I1 + 1, 685 | IP2 = I2 + 1, 686 | IP3 = I3 + 1, 687 | _ = random_wh82:seed(IP1, IP2, IP3), 688 | seed_algorithms(L). 689 | 690 | -ifdef(ERLANG_OTP_VERSION_20_FEATURES). 691 | seed_algorithms_rand(IP1, IP2, IP3) -> 692 | _ = rand:seed(exsp, {IP1, IP2, IP3}), 693 | ok. 694 | -else. 695 | -ifdef(ERLANG_OTP_VERSION_18_FEATURES). 696 | seed_algorithms_rand(IP1, IP2, IP3) -> 697 | _ = rand:seed(exsplus, {IP1, IP2, IP3}), 698 | ok. 699 | -else. 700 | seed_algorithms_rand(_, _, _) -> 701 | ok. 702 | -endif. 703 | -endif. 704 | 705 | -------------------------------------------------------------------------------- /src/quickrand_hash.erl: -------------------------------------------------------------------------------- 1 | %-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- 2 | % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: 3 | %%% 4 | %%%------------------------------------------------------------------------ 5 | %%% @doc 6 | %%% ==Random Number Generation With Hash Functions== 7 | %%% The random numbers created by the functions in this module are 8 | %%% not meant for cryptographic purposes. 9 | %%% 10 | %%% Any functions that have a jenkins prefix use Bob Jenkins' lookup3 hashing 11 | %%% (lookup3, May 2006). In my testing, the function jenkins_32 is 4 times 12 | %%% slower than erlang:phash2/1 because jenkins_32 is implemented in Erlang. 13 | %%% Both the jenkins_32 and jenkins_64 functions execute at a speed similar to 14 | %%% crypto:hash(ripemd160,_) with crypto:hash(sha256,_) slightly faster and 15 | %%% crypto:hash(sha512,_) slightly slower. 16 | %%% 17 | %%% Any functions that have a jenkins64 prefix use Bob Jenkins' SpookyHash 18 | %%% (SpookyV2, August 5 2012). In my testing, the function jenkins64_128 is 19 | %%% 4.5 times slower than crypto:hash(md5,_) which provides the same number 20 | %%% of bits, because the jenkins64 functions are implemented in Erlang. 21 | %%% 22 | %%% The jenkins prefix functions are faster than the jenkins64 prefix functions 23 | %%% due to avoiding Erlang bignums and both provide the same quality. 24 | %%% 25 | %%% All the functions have been checked with the C++ implementations to ensure 26 | %%% the same hash value is obtained, though this implementation forces numbers 27 | %%% to be interpreted as big-endian. 28 | %%% @end 29 | %%% 30 | %%% MIT License 31 | %%% 32 | %%% Copyright (c) 2017-2022 Michael Truog 33 | %%% 34 | %%% Permission is hereby granted, free of charge, to any person obtaining a 35 | %%% copy of this software and associated documentation files (the "Software"), 36 | %%% to deal in the Software without restriction, including without limitation 37 | %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | %%% and/or sell copies of the Software, and to permit persons to whom the 39 | %%% Software is furnished to do so, subject to the following conditions: 40 | %%% 41 | %%% The above copyright notice and this permission notice shall be included in 42 | %%% all copies or substantial portions of the Software. 43 | %%% 44 | %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 49 | %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 50 | %%% DEALINGS IN THE SOFTWARE. 51 | %%% 52 | %%% @author Michael Truog 53 | %%% @copyright 2017-2022 Michael Truog 54 | %%% @version 2.0.5 {@date} {@time} 55 | %%%------------------------------------------------------------------------ 56 | 57 | -module(quickrand_hash). 58 | -author('mjtruog at protonmail dot com'). 59 | 60 | %% external interface 61 | -export([jenkins_32/1, 62 | jenkins_32/2, 63 | jenkins_64/1, 64 | jenkins_64/2, 65 | jenkins64_128/1, 66 | jenkins64_128/2, 67 | jenkins64_64/1, 68 | jenkins64_64/2, 69 | jenkins64_32/1, 70 | jenkins64_32/2]). 71 | 72 | -ifdef(OTP_RELEASE). 73 | % able to use -if/-elif here 74 | -if(?OTP_RELEASE >= 24). 75 | -define(ERLANG_OTP_VERSION_24_FEATURES, true). 76 | -endif. 77 | -endif. 78 | 79 | -ifdef(ERLANG_OTP_VERSION_24_FEATURES). 80 | % iodata() recursive type is not infinitely recursive 81 | % like within the function iodata_to_list/1 82 | % (unable to only use no_underspecs on iodata_to_list/1) 83 | -dialyzer({no_underspecs, 84 | [jenkins_32/1, 85 | jenkins_32/2, 86 | jenkins_64/1, 87 | jenkins_64/2, 88 | jenkins64_128/1, 89 | jenkins64_128/2, 90 | jenkins64_64/1, 91 | jenkins64_64/2, 92 | jenkins64_32/1, 93 | jenkins64_32/2]}). 94 | -endif. 95 | 96 | % a constant which: 97 | % * is not zero 98 | % * is odd 99 | % * is a not-very-regular mix of 1's and 0's 100 | % * does not need any other special mathematical properties 101 | -define(JENKINS64_CONST, 16#DEADBEEFDEADBEEF). 102 | -define(JENKINS_CONST, 16#DEADBEEF). 103 | 104 | -include("quickrand_constants.hrl"). 105 | 106 | %%%------------------------------------------------------------------------ 107 | %%% External interface functions 108 | %%%------------------------------------------------------------------------ 109 | 110 | %%------------------------------------------------------------------------- 111 | %% @doc 112 | %% ===Bob Jenkins lookup3 hashword for iodata.=== 113 | %% @end 114 | %%------------------------------------------------------------------------- 115 | 116 | -spec jenkins_32(MessageRaw :: iodata()) -> 117 | non_neg_integer(). 118 | 119 | jenkins_32(MessageRaw) -> 120 | jenkins_32(MessageRaw, 0). 121 | 122 | %%------------------------------------------------------------------------- 123 | %% @doc 124 | %% ===Bob Jenkins lookup3 hashword for iodata.=== 125 | %% @end 126 | %%------------------------------------------------------------------------- 127 | 128 | -spec jenkins_32(MessageRaw :: iodata(), 129 | Seed :: non_neg_integer()) -> 130 | non_neg_integer(). 131 | 132 | jenkins_32(MessageRaw, Seed) 133 | when is_integer(Seed), Seed >= 0 -> 134 | {Message, Size} = iodata_to_list(MessageRaw), 135 | A = B = C = add_32(?JENKINS_CONST, ((Size + 3) div 4) bsl 2, Seed), 136 | {HashA, _} = jenkins_32(Message, Size, A, B, C), 137 | HashA. 138 | 139 | %%------------------------------------------------------------------------- 140 | %% @doc 141 | %% ===Bob Jenkins lookup3 hashword2 for iodata.=== 142 | %% @end 143 | %%------------------------------------------------------------------------- 144 | 145 | -spec jenkins_64(MessageRaw :: iodata()) -> 146 | non_neg_integer(). 147 | 148 | jenkins_64(MessageRaw) -> 149 | jenkins_64(MessageRaw, 0). 150 | 151 | %%------------------------------------------------------------------------- 152 | %% @doc 153 | %% ===Bob Jenkins lookup3 hashword2 for iodata.=== 154 | %% @end 155 | %%------------------------------------------------------------------------- 156 | 157 | -spec jenkins_64(MessageRaw :: iodata(), 158 | Seed :: non_neg_integer()) -> 159 | non_neg_integer(). 160 | 161 | jenkins_64(MessageRaw, Seed) 162 | when is_integer(Seed), Seed >= 0 -> 163 | {Message, Size} = iodata_to_list(MessageRaw), 164 | A = B = C0 = add_32(?JENKINS_CONST, ((Size + 3) div 4) bsl 2, Seed), 165 | CN = add_32(C0, Seed bsr 32), 166 | {HashA, HashB} = jenkins_32(Message, Size, A, B, CN), 167 | (HashB bsl 32) + HashA. 168 | 169 | %%------------------------------------------------------------------------- 170 | %% @doc 171 | %% ===Bob Jenkins SpookyHashV2 Hash128.=== 172 | %% @end 173 | %%------------------------------------------------------------------------- 174 | 175 | -spec jenkins64_128(MessageRaw :: iodata()) -> 176 | non_neg_integer(). 177 | 178 | jenkins64_128(MessageRaw) -> 179 | jenkins64_128(MessageRaw, 0). 180 | 181 | %%------------------------------------------------------------------------- 182 | %% @doc 183 | %% ===Bob Jenkins SpookyHashV2 Hash128.=== 184 | %% @end 185 | %%------------------------------------------------------------------------- 186 | 187 | -spec jenkins64_128(MessageRaw :: iodata(), 188 | Seed :: non_neg_integer()) -> 189 | non_neg_integer(). 190 | 191 | jenkins64_128(MessageRaw, Seed) 192 | when is_integer(Seed), Seed >= 0 -> 193 | {Message, Size} = iodata_to_list(MessageRaw), 194 | SeedA = Seed band ?BITMASK_64, 195 | SeedB = (Seed bsr 64) band ?BITMASK_64, 196 | {HashA, HashB} = jenkins64_128(Message, Size, SeedA, SeedB), 197 | (HashB bsl 64) + HashA. 198 | 199 | %%------------------------------------------------------------------------- 200 | %% @doc 201 | %% ===Bob Jenkins SpookyHashV2 Hash64.=== 202 | %% @end 203 | %%------------------------------------------------------------------------- 204 | 205 | -spec jenkins64_64(MessageRaw :: iodata()) -> 206 | non_neg_integer(). 207 | 208 | jenkins64_64(MessageRaw) -> 209 | jenkins64_64(MessageRaw, 0). 210 | 211 | %%------------------------------------------------------------------------- 212 | %% @doc 213 | %% ===Bob Jenkins SpookyHashV2 Hash64.=== 214 | %% @end 215 | %%------------------------------------------------------------------------- 216 | 217 | -spec jenkins64_64(MessageRaw :: iodata(), 218 | Seed :: non_neg_integer()) -> 219 | non_neg_integer(). 220 | 221 | jenkins64_64(MessageRaw, Seed) 222 | when is_integer(Seed), Seed >= 0 -> 223 | {Message, Size} = iodata_to_list(MessageRaw), 224 | SeedA = Seed band ?BITMASK_64, 225 | {HashA, _} = jenkins64_128(Message, Size, SeedA, SeedA), 226 | HashA. 227 | 228 | %%------------------------------------------------------------------------- 229 | %% @doc 230 | %% ===Bob Jenkins SpookyHashV2 Hash32.=== 231 | %% @end 232 | %%------------------------------------------------------------------------- 233 | 234 | -spec jenkins64_32(MessageRaw :: iodata()) -> 235 | non_neg_integer(). 236 | 237 | jenkins64_32(MessageRaw) -> 238 | jenkins64_32(MessageRaw, 0). 239 | 240 | %%------------------------------------------------------------------------- 241 | %% @doc 242 | %% ===Bob Jenkins SpookyHashV2 Hash32.=== 243 | %% @end 244 | %%------------------------------------------------------------------------- 245 | 246 | -spec jenkins64_32(MessageRaw :: iodata(), 247 | Seed :: non_neg_integer()) -> 248 | non_neg_integer(). 249 | 250 | jenkins64_32(MessageRaw, Seed) 251 | when is_integer(Seed), Seed >= 0 -> 252 | {Message, Size} = iodata_to_list(MessageRaw), 253 | SeedA = Seed band ?BITMASK_32, 254 | {HashA, _} = jenkins64_128(Message, Size, SeedA, SeedA), 255 | HashA band ?BITMASK_32. 256 | 257 | %%%------------------------------------------------------------------------ 258 | %%% Private functions 259 | %%%------------------------------------------------------------------------ 260 | 261 | jenkins_32(Message0, Size0, A0, B0, C0) when Size0 >= 12 -> 262 | {IncrA, Message1, Size1} = consume_32(Message0, Size0), 263 | {IncrB, Message2, Size2} = consume_32(Message1, Size1), 264 | {IncrC, MessageN, SizeN} = consume_32(Message2, Size2), 265 | {AN, 266 | BN, 267 | CN} = jenkins_mix(add_32(A0, IncrA), add_32(B0, IncrB), add_32(C0, IncrC)), 268 | jenkins_32(MessageN, SizeN, AN, BN, CN); 269 | jenkins_32(Message0, Size0, A, B, C) when Size0 >= 1 -> 270 | {IncrA, Message1, Size1} = if 271 | Size0 >= 4 -> 272 | consume_32(Message0, Size0); 273 | true -> 274 | consume_32_part(Message0, Size0) 275 | end, 276 | {IncrB, MessageN, SizeN} = if 277 | Size1 >= 4 -> 278 | consume_32(Message1, Size1); 279 | true -> 280 | consume_32_part(Message1, Size1) 281 | end, 282 | {IncrC, [], 0} = consume_32_part(MessageN, SizeN), 283 | jenkins_final(add_32(A, IncrA), add_32(B, IncrB), add_32(C, IncrC)); 284 | jenkins_32([], 0, _, B, C) -> 285 | {C, B}. 286 | 287 | % mix -- mix 3 32-bit values reversibly. 288 | % 289 | % This is reversible, so any information in (a,b,c) before mix() is 290 | % still in (a,b,c) after mix(). 291 | % 292 | % If four pairs of (a,b,c) inputs are run through mix(), or through 293 | % mix() in reverse, there are at least 32 bits of the output that 294 | % are sometimes the same for one pair and different for another pair. 295 | % This was tested for: 296 | % * pairs that differed by one bit, by two bits, in any combination 297 | % of top bits of (a,b,c), or in any combination of bottom bits of 298 | % (a,b,c). 299 | % * "differ" is defined as +, -, ^, or ~^. For + and -, the output 300 | % delta transformed to a Gray code (a^(a>>1)) so a string of 1's (as 301 | % is commonly produced by subtraction) look like a single 1-bit 302 | % difference. 303 | % * the base values were pseudorandom, all zero but one bit set, or 304 | % all zero plus a counter that starts at zero. 305 | % 306 | % Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that 307 | % satisfy this are 308 | % 4 6 8 16 19 4 309 | % 9 15 3 18 27 15 310 | % 14 9 3 7 17 3 311 | % Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing 312 | % for "differ" defined as + with a one-bit base and a two-bit delta. 313 | % Data at http://burtleburtle.net/bob/hash/avalanche.html was used to 314 | % choose the operations, constants, and arrangements of the variables. 315 | % 316 | % This does not achieve avalanche. There are input bits of (a,b,c) 317 | % that fail to affect some output bits of (a,b,c), especially of a. The 318 | % most thoroughly mixed value is c, but it doesn't really even achieve 319 | % avalanche in c. 320 | % 321 | jenkins_mix(A0, B0, C0) -> 322 | A1 = subtract_32(A0, C0),A2 = A1 bxor rotate_32(C0, 4),C1 = add_32(C0, B0), 323 | B1 = subtract_32(B0, A2),B2 = B1 bxor rotate_32(A2, 6),A3 = add_32(A2, C1), 324 | C2 = subtract_32(C1, B2),C3 = C2 bxor rotate_32(B2, 8),B3 = add_32(B2, A3), 325 | A4 = subtract_32(A3, C3),A5 = A4 bxor rotate_32(C3, 16),C4 = add_32(C3, B3), 326 | B4 = subtract_32(B3, A5),B5 = B4 bxor rotate_32(A5, 19),AN = add_32(A5, C4), 327 | C5 = subtract_32(C4, B5),CN = C5 bxor rotate_32(B5, 4),BN = add_32(B5, AN), 328 | {AN, BN, CN}. 329 | 330 | % final -- final mixing of 3 32-bit values (a,b,c) into c 331 | % 332 | % Pairs of (a,b,c) values differing in only a few bits will usually 333 | % produce values of c that look totally different. This was tested for 334 | % * pairs that differed by one bit, by two bits, in any combination 335 | % of top bits of (a,b,c), or in any combination of bottom bits of 336 | % (a,b,c). 337 | % * "differ" is defined as +, -, ^, or ~^. For + and -, the output 338 | % delta transformed to a Gray code (a^(a>>1)) so a string of 1's (as 339 | % is commonly produced by subtraction) look like a single 1-bit 340 | % difference. 341 | % * the base values were pseudorandom, all zero but one bit set, or 342 | % all zero plus a counter that starts at zero. 343 | % 344 | % These constants passed: 345 | % 14 11 25 16 4 14 24 346 | % 12 14 25 16 4 14 24 347 | % and these came close: 348 | % 4 8 15 26 3 22 24 349 | % 10 8 15 26 3 22 24 350 | % 11 8 15 26 3 22 24 351 | % 352 | jenkins_final(A0, B0, C0) -> 353 | C1 = C0 bxor B0, C2 = subtract_32(C1, rotate_32(B0, 14)), 354 | A1 = A0 bxor C2, A2 = subtract_32(A1, rotate_32(C2, 11)), 355 | B1 = B0 bxor A2, B2 = subtract_32(B1, rotate_32(A2, 25)), 356 | C3 = C2 bxor B2, C4 = subtract_32(C3, rotate_32(B2, 16)), 357 | A3 = A2 bxor C4, A4 = subtract_32(A3, rotate_32(C4, 4)), 358 | B3 = B2 bxor A4, BN = subtract_32(B3, rotate_32(A4, 14)), 359 | C5 = C4 bxor BN, CN = subtract_32(C5, rotate_32(BN, 24)), 360 | {CN, BN}. 361 | 362 | jenkins64_128(Message, Size, SeedA, SeedB) -> 363 | HashC = ?JENKINS64_CONST, 364 | HashD = ?JENKINS64_CONST, 365 | jenkins64_128(Message, Size, SeedA, SeedB, HashC, HashD, Size). 366 | 367 | jenkins64_128(Message0, Size0, A0, B0, C0, D0, TotalSize) when Size0 >= 32 -> 368 | {IncrC, Message1, Size1} = consume_64(Message0, Size0), 369 | {IncrD, Message2, Size2} = consume_64(Message1, Size1), 370 | {AN, 371 | BN, 372 | CN, 373 | DN} = jenkins64_short_mix(A0, B0, add_64(C0, IncrC), add_64(D0, IncrD)), 374 | {IncrA, Message3, Size3} = consume_64(Message2, Size2), 375 | {IncrB, MessageN, SizeN} = consume_64(Message3, Size3), 376 | jenkins64_128(MessageN, SizeN, 377 | add_64(AN, IncrA), add_64(BN, IncrB), CN, DN, TotalSize); 378 | jenkins64_128(Message0, Size0, A0, B0, C0, D0, TotalSize) when Size0 >= 16 -> 379 | {IncrC, Message1, Size1} = consume_64(Message0, Size0), 380 | {IncrD, MessageN, SizeN} = consume_64(Message1, Size1), 381 | {AN, 382 | BN, 383 | CN, 384 | DN} = jenkins64_short_mix(A0, B0, add_64(C0, IncrC), add_64(D0, IncrD)), 385 | jenkins64_128(MessageN, SizeN, AN, BN, CN, DN, TotalSize); 386 | jenkins64_128(Message0, Size, AN, BN, C0, D0, TotalSize) when Size >= 12 -> 387 | [Byte00, Byte01, Byte02, Byte03, Byte04, Byte05, Byte06, Byte07, 388 | Byte08, Byte09, Byte10, Byte11 | Message1] = Message0, 389 | <> = 391 | <>, 393 | {ValueByte12, Message3} = if 394 | Size >= 13 -> 395 | [Byte12 | Message2] = Message1, 396 | {Byte12 bsl 32, Message2}; 397 | true -> 398 | {0, Message1} 399 | end, 400 | {ValueByte13, Message5} = if 401 | Size >= 14 -> 402 | [Byte13 | Message4] = Message3, 403 | {Byte13 bsl 40, Message4}; 404 | true -> 405 | {0, Message3} 406 | end, 407 | ValueByte14 = if 408 | Size == 15 -> 409 | [Byte14] = Message5, 410 | Byte14 bsl 48; 411 | true -> 412 | [] = Message5, 413 | 0 414 | end, 415 | CN = add_64(C0, Value64), 416 | DN = add_64(D0, TotalSize bsl 56, 417 | ValueByte14, ValueByte13, ValueByte12, Value32), 418 | jenkins64_short_end(AN, BN, CN, DN); 419 | jenkins64_128(Message0, Size, AN, BN, C0, D0, TotalSize) when Size >= 8 -> 420 | [Byte00, Byte01, Byte02, Byte03, Byte04, Byte05, Byte06, Byte07 | 421 | Message1] = Message0, 422 | <> = 423 | <>, 424 | {ValueByte08, Message3} = if 425 | Size >= 9 -> 426 | [Byte08 | Message2] = Message1, 427 | {Byte08, Message2}; 428 | true -> 429 | {0, Message1} 430 | end, 431 | {ValueByte09, Message5} = if 432 | Size >= 10 -> 433 | [Byte09 | Message4] = Message3, 434 | {Byte09 bsl 8, Message4}; 435 | true -> 436 | {0, Message3} 437 | end, 438 | ValueByte10 = if 439 | Size == 11 -> 440 | [Byte10] = Message5, 441 | Byte10 bsl 16; 442 | true -> 443 | [] = Message5, 444 | 0 445 | end, 446 | CN = add_64(C0, Value64), 447 | DN = add_64(D0, TotalSize bsl 56, ValueByte10, ValueByte09, ValueByte08), 448 | jenkins64_short_end(AN, BN, CN, DN); 449 | jenkins64_128(Message0, Size, AN, BN, C0, D0, TotalSize) when Size >= 4 -> 450 | [Byte00, Byte01, Byte02, Byte03 | Message1] = Message0, 451 | <> = <>, 452 | {ValueByte04, Message3} = if 453 | Size >= 5 -> 454 | [Byte04 | Message2] = Message1, 455 | {Byte04 bsl 32, Message2}; 456 | true -> 457 | {0, Message1} 458 | end, 459 | {ValueByte05, Message5} = if 460 | Size >= 6 -> 461 | [Byte05 | Message4] = Message3, 462 | {Byte05 bsl 40, Message4}; 463 | true -> 464 | {0, Message3} 465 | end, 466 | ValueByte06 = if 467 | Size == 7 -> 468 | [Byte06] = Message5, 469 | Byte06 bsl 48; 470 | true -> 471 | [] = Message5, 472 | 0 473 | end, 474 | CN = add_64(C0, ValueByte06, ValueByte05, ValueByte04, Value32), 475 | DN = add_64(D0, TotalSize bsl 56), 476 | jenkins64_short_end(AN, BN, CN, DN); 477 | jenkins64_128(Message0, Size, AN, BN, C0, D0, TotalSize) when Size >= 1 -> 478 | [Byte00 | Message1] = Message0, 479 | {ValueByte01, Message3} = if 480 | Size >= 2 -> 481 | [Byte01 | Message2] = Message1, 482 | {Byte01 bsl 8, Message2}; 483 | true -> 484 | {0, Message1} 485 | end, 486 | ValueByte02 = if 487 | Size == 3 -> 488 | [Byte02] = Message3, 489 | Byte02 bsl 16; 490 | true -> 491 | [] = Message3, 492 | 0 493 | end, 494 | CN = add_64(C0, ValueByte02, ValueByte01, Byte00), 495 | DN = add_64(D0, TotalSize bsl 56), 496 | jenkins64_short_end(AN, BN, CN, DN); 497 | jenkins64_128([], 0, AN, BN, C0, D0, TotalSize) -> 498 | CN = add_64(C0, ?JENKINS64_CONST), 499 | DN = add_64(D0, TotalSize bsl 56, ?JENKINS64_CONST), 500 | jenkins64_short_end(AN, BN, CN, DN). 501 | 502 | % 503 | % The goal is for each bit of the input to expand into 128 bits of 504 | % apparent entropy before it is fully overwritten. 505 | % n trials both set and cleared at least m bits of h0 h1 h2 h3 506 | % n: 2 m: 29 507 | % n: 3 m: 46 508 | % n: 4 m: 57 509 | % n: 5 m: 107 510 | % n: 6 m: 146 511 | % n: 7 m: 152 512 | % when run forwards or backwards 513 | % for all 1-bit and 2-bit diffs 514 | % with diffs defined by either xor or subtraction 515 | % with a base of all zeros plus a counter, or plus another bit, or random 516 | % 517 | jenkins64_short_mix(H0_0, H1_0, H2_0, H3_0) 518 | when is_integer(H0_0), is_integer(H1_0), 519 | is_integer(H2_0), is_integer(H3_0) -> 520 | H2_1 = add_64(rotate_64(H2_0, 50), H3_0),H0_1 = H0_0 bxor H2_1, 521 | H3_1 = add_64(rotate_64(H3_0, 52), H0_1),H1_1 = H1_0 bxor H3_1, 522 | H0_2 = add_64(rotate_64(H0_1, 30), H1_1),H2_2 = H2_1 bxor H0_2, 523 | H1_2 = add_64(rotate_64(H1_1, 41), H2_2),H3_2 = H3_1 bxor H1_2, 524 | H2_3 = add_64(rotate_64(H2_2, 54), H3_2),H0_3 = H0_2 bxor H2_3, 525 | H3_3 = add_64(rotate_64(H3_2, 48), H0_3),H1_3 = H1_2 bxor H3_3, 526 | H0_4 = add_64(rotate_64(H0_3, 38), H1_3),H2_4 = H2_3 bxor H0_4, 527 | H1_4 = add_64(rotate_64(H1_3, 37), H2_4),H3_4 = H3_3 bxor H1_4, 528 | H2_5 = add_64(rotate_64(H2_4, 62), H3_4),H0_5 = H0_4 bxor H2_5, 529 | H3_5 = add_64(rotate_64(H3_4, 34), H0_5),H1_5 = H1_4 bxor H3_5, 530 | H0_N = add_64(rotate_64(H0_5, 5), H1_5),H2_N = H2_5 bxor H0_N, 531 | H1_N = add_64(rotate_64(H1_5, 36), H2_N),H3_N = H3_5 bxor H1_N, 532 | {H0_N, H1_N, H2_N, H3_N}. 533 | 534 | % 535 | % Mix all 4 inputs together so that h0, h1 are a hash of them all. 536 | % 537 | % For two inputs differing in just the input bits 538 | % Where "differ" means xor or subtraction 539 | % And the base value is random, or a counting value starting at that bit 540 | % The final result will have each bit of h0, h1 flip 541 | % For every input bit, 542 | % with probability 50 +- .3% (it is probably better than that) 543 | % For every pair of input bits, 544 | % with probability 50 +- .75% (the worst case is approximately that) 545 | % 546 | jenkins64_short_end(H0_0, H1_0, H2_0, H3_0) 547 | when is_integer(H0_0), is_integer(H1_0), 548 | is_integer(H2_0), is_integer(H3_0) -> 549 | H3_1 = H3_0 bxor H2_0,H2_1 = rotate_64(H2_0, 15),H3_2 = add_64(H3_1, H2_1), 550 | H0_1 = H0_0 bxor H3_2,H3_3 = rotate_64(H3_2, 52),H0_2 = add_64(H0_1, H3_3), 551 | H1_1 = H1_0 bxor H0_2,H0_3 = rotate_64(H0_2, 26),H1_2 = add_64(H1_1, H0_3), 552 | H2_2 = H2_1 bxor H1_2,H1_3 = rotate_64(H1_2, 51),H2_3 = add_64(H2_2, H1_3), 553 | H3_4 = H3_3 bxor H2_3,H2_4 = rotate_64(H2_3, 28),H3_5 = add_64(H3_4, H2_4), 554 | H0_4 = H0_3 bxor H3_5,H3_6 = rotate_64(H3_5, 9),H0_5 = add_64(H0_4, H3_6), 555 | H1_4 = H1_3 bxor H0_5,H0_6 = rotate_64(H0_5, 47),H1_5 = add_64(H1_4, H0_6), 556 | H2_5 = H2_4 bxor H1_5,H1_6 = rotate_64(H1_5, 54),H2_6 = add_64(H2_5, H1_6), 557 | H3_7 = H3_6 bxor H2_6,H2_N = rotate_64(H2_6, 32),H3_8 = add_64(H3_7, H2_N), 558 | H0_7 = H0_6 bxor H3_8,H3_N = rotate_64(H3_8, 25),H0_8 = add_64(H0_7, H3_N), 559 | H1_7 = H1_6 bxor H0_8,H0_N = rotate_64(H0_8, 63),H1_N = add_64(H1_7, H0_N), 560 | {H0_N, H1_N}. 561 | 562 | -compile({inline, 563 | [{rotate_32,2}, 564 | {rotate_64,2}, 565 | {consume_32,2}, 566 | {consume_32_part,2}, 567 | {consume_64,2}, 568 | {add_32,2}, 569 | {add_32,3}, 570 | {add_64,2}, 571 | {add_64,3}, 572 | {add_64,4}, 573 | {add_64,5}, 574 | {add_64,6}, 575 | {subtract_32,2}]}). 576 | 577 | % left rotate a 32-bit value by k bits 578 | rotate_32(X, Bits) 579 | when is_integer(X), is_integer(Bits), Bits >= 0, Bits =< 32 -> 580 | ((X bsl Bits) band ?BITMASK_32) bor (X bsr (32 - Bits)). 581 | 582 | % left rotate a 64-bit value by k bits 583 | rotate_64(X, Bits) 584 | when is_integer(X), is_integer(Bits), Bits >= 0, Bits =< 64 -> 585 | ((X bsl Bits) band ?BITMASK_64) bor (X bsr (64 - Bits)). 586 | 587 | consume_32([Byte00, Byte01, Byte02, Byte03 | Message], Size) -> 588 | <> = <>, 589 | {Value, Message, Size - 4}. 590 | 591 | consume_32_part([], 0) -> 592 | {0, [], 0}; 593 | consume_32_part([Byte00], 1) -> 594 | <> = <>, 595 | {Value, [], 0}; 596 | consume_32_part([Byte00, Byte01], 2) -> 597 | <> = <>, 598 | {Value, [], 0}; 599 | consume_32_part([Byte00, Byte01, Byte02], 3) -> 600 | <> = <>, 601 | {Value, [], 0}. 602 | 603 | consume_64([Byte00, Byte01, Byte02, Byte03, Byte04, Byte05, Byte06, Byte07 | 604 | Message], Size) -> 605 | <> = 606 | <>, 607 | {Value, Message, Size - 8}. 608 | 609 | add_32(X0, X1) 610 | when is_integer(X0), is_integer(X1) -> 611 | (X0 + X1) band ?BITMASK_32. 612 | 613 | add_32(X0, X1, X2) 614 | when is_integer(X0), is_integer(X1), is_integer(X2) -> 615 | (X0 + X1 + X2) band ?BITMASK_32. 616 | 617 | add_64(X0, X1) 618 | when is_integer(X0), is_integer(X1) -> 619 | (X0 + X1) band ?BITMASK_64. 620 | 621 | add_64(X0, X1, X2) 622 | when is_integer(X0), is_integer(X1), is_integer(X2) -> 623 | (X0 + X1 + X2) band ?BITMASK_64. 624 | 625 | add_64(X0, X1, X2, X3) 626 | when is_integer(X0), is_integer(X1), is_integer(X2), is_integer(X3) -> 627 | (X0 + X1 + X2 + X3) band ?BITMASK_64. 628 | 629 | add_64(X0, X1, X2, X3, X4) 630 | when is_integer(X0), is_integer(X1), is_integer(X2), 631 | is_integer(X3), is_integer(X4) -> 632 | (X0 + X1 + X2 + X3 + X4) band ?BITMASK_64. 633 | 634 | add_64(X0, X1, X2, X3, X4, X5) 635 | when is_integer(X0), is_integer(X1), is_integer(X2), 636 | is_integer(X3), is_integer(X4), is_integer(X5) -> 637 | (X0 + X1 + X2 + X3 + X4 + X5) band ?BITMASK_64. 638 | 639 | subtract_32(X0, X1) 640 | when is_integer(X0), is_integer(X1) -> 641 | (X0 - X1) band ?BITMASK_32. 642 | 643 | -spec iodata_to_list(IOData :: iodata()) -> 644 | {list(byte()), non_neg_integer()}. 645 | 646 | iodata_to_list(IOData) 647 | when is_binary(IOData) -> 648 | {erlang:binary_to_list(IOData), byte_size(IOData)}; 649 | iodata_to_list(IOData) 650 | when is_list(IOData) -> 651 | iodata_to_list([], IOData, 0). 652 | 653 | iodata_to_list(ListOut, [], Size) -> 654 | {lists:reverse(ListOut), Size}; 655 | iodata_to_list(ListOut, Binary, Size) 656 | when is_binary(Binary) -> 657 | iodata_to_list(lists:reverse(erlang:binary_to_list(Binary), ListOut), 658 | [], Size + byte_size(Binary)); 659 | iodata_to_list(ListOut, [Binary | IODataIn], Size) 660 | when is_binary(Binary) -> 661 | iodata_to_list(lists:reverse(erlang:binary_to_list(Binary), ListOut), 662 | IODataIn, Size + byte_size(Binary)); 663 | iodata_to_list(ListOut0, [List | IODataIn], Size0) 664 | when is_list(List) -> 665 | {ListOutN, SizeN} = iodata_to_list(ListOut0, List, Size0), 666 | iodata_to_list(lists:reverse(ListOutN), IODataIn, SizeN); 667 | iodata_to_list(ListOut, [Byte | IOData], Size) 668 | when is_integer(Byte), Byte >= 0, Byte =< 255 -> 669 | iodata_to_list([Byte | ListOut], IOData, Size + 1). 670 | 671 | -ifdef(TEST). 672 | -include_lib("eunit/include/eunit.hrl"). 673 | 674 | -include("quickrand_test.hrl"). 675 | 676 | module_test_() -> 677 | {timeout, ?TEST_TIMEOUT, [ 678 | {"jenkins tests", ?_assertOk(t_jenkins())}, 679 | {"jenkins64 tests", ?_assertOk(t_jenkins64())} 680 | ]}. 681 | 682 | t_jenkins() -> 683 | Message1List = "The quick brown fox jumps over the lazy dog", 684 | Message1Binary = erlang:list_to_binary(Message1List), 685 | Hash1_32 = 1995770187, 686 | Hash1_64 = 10406847816247085387, 687 | Hash1_32 = quickrand_hash:jenkins_32(Message1List), 688 | Hash1_32 = quickrand_hash:jenkins_32(Message1Binary), 689 | Hash1_64 = quickrand_hash:jenkins_64(Message1List), 690 | Hash1_64 = quickrand_hash:jenkins_64(Message1Binary), 691 | ok. 692 | 693 | t_jenkins64() -> 694 | Message1List = "The quick brown fox jumps over the lazy dog", 695 | Message1Binary = erlang:list_to_binary(Message1List), 696 | Hash1_32 = 564871382, 697 | Hash1_64 = 15135784821221703894, 698 | Hash1_128 = 93844563773912622153168398518001025238, 699 | Hash1_32 = quickrand_hash:jenkins64_32(Message1List), 700 | Hash1_32 = quickrand_hash:jenkins64_32(Message1Binary), 701 | Hash1_64 = quickrand_hash:jenkins64_64(Message1List), 702 | Hash1_64 = quickrand_hash:jenkins64_64(Message1Binary), 703 | Hash1_128 = quickrand_hash:jenkins64_128(Message1List), 704 | Hash1_128 = quickrand_hash:jenkins64_128(Message1Binary), 705 | ok. 706 | 707 | -endif. 708 | --------------------------------------------------------------------------------