├── .depend ├── .gitignore ├── META ├── test ├── reporting ├── benchmark.ml ├── consistency.ml ├── u01.ml └── generator.ml ├── Changes ├── docs ├── type_PRNG.Chacha.Pure.html ├── type_PRNG.Chacha.State.html ├── type_PRNG.Splitmix.Pure.html ├── type_PRNG.Splitmix.State.html ├── index_classes.html ├── index_class_types.html ├── index_exceptions.html ├── index_extensions.html ├── index_methods.html ├── index_attributes.html ├── type_PRNG.Chacha.html ├── type_PRNG.Splitmix.html ├── index_module_types.html ├── index_types.html ├── PRNG.Splitmix.html ├── PRNG.Chacha.html ├── index.html ├── style.css ├── index_modules.html ├── PRNG.PURE.html ├── PRNG.Chacha.Pure.html ├── PRNG.Splitmix.Pure.html ├── type_PRNG.STATE.html ├── PRNG.html ├── type_PRNG.PURE.html ├── index_values.html ├── PRNG.STATE.html ├── PRNG.Chacha.State.html ├── PRNG.Splitmix.State.html └── type_PRNG.html ├── pringo.opam ├── Makefile ├── README.md ├── PRNG.mli ├── stubs.c └── PRNG.ml /.depend: -------------------------------------------------------------------------------- 1 | PRNG.cmo : PRNG.cmi 2 | PRNG.cmx : PRNG.cmi 3 | PRNG.cmi : 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.cm[ioxa] 3 | *.cmxa 4 | *.o 5 | *.a 6 | *.so 7 | *.obj 8 | *.lib 9 | *.dll 10 | *.exe 11 | -------------------------------------------------------------------------------- /META: -------------------------------------------------------------------------------- 1 | description = "Splittable pseudo-random number generators" 2 | requires = "" 3 | version = "1.0" 4 | archive(byte) = "PRNG.cma" 5 | archive(native) = "PRNG.cmxa" 6 | -------------------------------------------------------------------------------- /test/reporting: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if grep '^ *[0-9].* eps' "$@"; then 4 | echo "FAILED!" 5 | exit 2 6 | fi 7 | printf "PASSED: " 8 | cat "$@" | grep -c 'All tests were passed' 9 | printf "WEAK: " 10 | cat "$@" | grep -c 'The following tests gave p-values outside' 11 | exit 0 12 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Release 1.4, 2024-03-28 2 | - Fix GC root registration in the pure LXM implementation (#6) 3 | 4 | Release 1.3, 2021-11-22 5 | - Add `uniform` function producing a random float in (0.0, 1.0) (#4) 6 | - Add the LXM generator (the L64X128 variant) 7 | - New test infrastructure based on TestU01 8 | - Add Schaathun's "split sequences" to the tests (#5) 9 | 10 | Release 1.2, 2021-09-13 11 | - #3: unbox the argument of the mix30 C stub 12 | - Install .cmti and .mli files for documentation 13 | 14 | Release 1.1, 2020-11-06 15 | - Fixed wrong buffering in function Chacha.State.bytes 16 | - Reduced number of Chacha rounds from 20 to 8, to improve performance 17 | - Various speed improvements for 32-bit hosts 18 | 19 | Release 1.0, 2020-10-06 20 | - First public release 21 | -------------------------------------------------------------------------------- /docs/type_PRNG.Chacha.Pure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Chacha.Pure 11 | 12 | 13 | PURE 14 | -------------------------------------------------------------------------------- /docs/type_PRNG.Chacha.State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Chacha.State 11 | 12 | 13 | STATE 14 | -------------------------------------------------------------------------------- /docs/type_PRNG.Splitmix.Pure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Splitmix.Pure 11 | 12 | 13 | PURE 14 | -------------------------------------------------------------------------------- /docs/type_PRNG.Splitmix.State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Splitmix.State 11 | 12 | 13 | STATE 14 | -------------------------------------------------------------------------------- /pringo.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "pringo" 3 | synopsis: "Pseudo-random, splittable number generators" 4 | description: 5 | "Pseudo-random number generators that support splitting and two interfaces: one stateful, one purely functional" 6 | maintainer: "Xavier Leroy " 7 | authors: "Xavier Leroy " 8 | license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" 9 | x-maintenance-intent: ["(latest)"] 10 | homepage: "https://github.com/xavierleroy/pringo" 11 | bug-reports: "https://github.com/xavierleroy/pringo/issues" 12 | depends: [ 13 | "ocaml" {>= "4.05.0"} 14 | "ocamlfind" 15 | "testu01" {with-test} 16 | ] 17 | build: make 18 | install: [make "install"] 19 | run-test: [make "smalltest"] {ocaml:version >= "4.08"} 20 | dev-repo: "git+https://https://github.com/xavierleroy/pringo" 21 | -------------------------------------------------------------------------------- /docs/index_classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of classes 12 | 13 | 14 | 16 |

Index of classes

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/index_class_types.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of class types 12 | 13 | 14 | 16 |

Index of class types

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/index_exceptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of exceptions 12 | 13 | 14 | 16 |

Index of exceptions

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/index_extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of extensions 12 | 13 | 14 | 16 |

Index of extensions

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/index_methods.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of class methods 12 | 13 | 14 | 16 |

Index of class methods

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/index_attributes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of class attributes 12 | 13 | 14 | 16 |

Index of class attributes

17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/type_PRNG.Chacha.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Chacha 11 | 12 | 13 | sig module State : STATE module Pure : PURE end 14 | -------------------------------------------------------------------------------- /docs/type_PRNG.Splitmix.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.Splitmix 11 | 12 | 13 | sig module State : STATE module Pure : PURE end 14 | -------------------------------------------------------------------------------- /docs/index_module_types.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of module types 12 | 13 | 14 | 16 |

Index of module types

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
P
PURE [PRNG]
S
STATE [PRNG]
25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/index_types.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of types 12 | 13 | 14 | 16 |

Index of types

17 | 18 | 19 | 20 | 21 | 22 | 27 |
T
t [PRNG.PURE]
t [PRNG.STATE]
23 |

The type of generators

24 | 25 |
26 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/PRNG.Splitmix.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | PRNG.Splitmix 15 | 16 | 17 | 20 |

Module PRNG.Splitmix

21 | 22 |
module Splitmix: sig .. end

23 | 24 |
module State: PRNG.STATE 
25 |
module Pure: PRNG.PURE 
26 | -------------------------------------------------------------------------------- /docs/PRNG.Chacha.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | PRNG.Chacha 16 | 17 | 18 | 22 |

Module PRNG.Chacha

23 | 24 |
module Chacha: sig .. end

25 | 26 |
module State: PRNG.STATE 
27 |
module Pure: PRNG.PURE 
28 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | 43 |
PRNG
26 |

This library provides pseudo-random number generators (PRNGs) comparable 27 | to that of the Random module from OCaml's standard library, but with 28 | two extensions: Generators are "splittable": they support a split operation that 29 | returns a new generator that is statistically independent from the 30 | current generator. Both generators can be used in parallel, and can 31 | be further splitted, without introducing statistical bias. This 32 | splitting operation is particularly useful to implement the lazy 33 | generation of pseudo-random infinite data structures such as 34 | functions or streams., In addition to a stateful, imperative interface resembling that of 35 | the Random.State standard library module, another, purely 36 | functional interface is provided. In the functional interface, the 37 | current state of the PRNG appears as parameter but also as result of 38 | the number generation functions. This interface can be used 39 | directly within a state monad.

40 | 41 |
42 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OCAMLFLAGS=-g -safe-string -bin-annot 2 | OCAMLC=ocamlc $(OCAMLFLAGS) 3 | OCAMLOPT=ocamlopt $(OCAMLFLAGS) 4 | OCAMLDEP=ocamldep 5 | OCAMLMKLIB=ocamlmklib 6 | OCAMLFIND=ocamlfind 7 | DIEHARDER=dieharder -g 200 -a 8 | ENT=head -c 1000000 | ent 9 | 10 | include $(shell $(OCAMLC) -where)/Makefile.config 11 | 12 | all: PRNG.cmxa PRNG.cma 13 | 14 | PRNG.cmxa PRNG.cma: PRNG.cmx PRNG.cmo stubs.$(O) 15 | $(OCAMLMKLIB) -o PRNG PRNG.cmo PRNG.cmx stubs.$(O) 16 | 17 | test/u01.exe: test/u01.ml PRNG.cmxa 18 | $(OCAMLFIND) ocamlopt -package testu01 -linkpkg $(OCAMLFLAGS) -I . \ 19 | -o test/u01.exe \ 20 | PRNG.cmxa test/u01.ml 21 | 22 | %.cmx: %.ml 23 | $(OCAMLOPT) -c $*.ml 24 | %.cmo: %.ml 25 | $(OCAMLC) -c $*.ml 26 | %.cmi: %.mli 27 | $(OCAMLOPT) -c $*.mli 28 | %.$(O): %.c 29 | $(OCAMLC) -c $*.c 30 | %.exe: %.ml PRNG.cmxa 31 | $(OCAMLOPT) -I . -o $@ PRNG.cmxa $*.ml 32 | 33 | clean:: 34 | rm -f *.cm[ioxa] *.cmxa *.$(O) *.$(A) *.$(SO) 35 | rm -f test/*.cm[iox] test/*.$(O) test/*.exe 36 | 37 | TOINSTALL=\ 38 | PRNG.mli PRNG.cmi PRNG.cmti \ 39 | PRNG.cma PRNG.cmxa PRNG.$(A) libPRNG.$(A) dllPRNG.$(SO) 40 | 41 | install: 42 | $(OCAMLFIND) install pringo META $(TOINSTALL) 43 | 44 | uninstall: 45 | $(OCAMLFIND) remove pringo 46 | 47 | testresults/us-%.log: test/u01.exe 48 | @mkdir -p testresults 49 | ./test/u01.exe -small $* > $@ 50 | 51 | testresults/um-%.log: test/u01.exe 52 | @mkdir -p testresults 53 | ./test/u01.exe -medium $* > $@ 54 | 55 | testresults/ub-%.log: test/u01.exe 56 | @mkdir -p testresults 57 | ./test/u01.exe -big $* > $@ 58 | 59 | testresults/ur-%.log: test/u01.exe 60 | @mkdir -p testresults 61 | ./test/u01.exe -rabbit $* > $@ 62 | 63 | testresults/ua-%.log: test/u01.exe 64 | @mkdir -p testresults 65 | ./test/u01.exe -alphabit $* > $@ 66 | 67 | testresults/dh-%.log: test/generator.exe 68 | @mkdir -p testresults 69 | ./test/generator.exe $* | $(DIEHARDER) > $@ 70 | 71 | testresults/ent-%.log: test/generator.exe 72 | @mkdir -p testresults 73 | ./test/generator.exe $* | $(ENT) > $@ 74 | 75 | clean:: 76 | rm -rf testresults 77 | 78 | TESTS=float seq8 seq32 seq64 block-13 \ 79 | treesplit-1 treesplit-4 laggedsplit-3 splita splits 80 | 81 | ALLTESTS=$(TESTS:%=chacha-%) $(TESTS:%=splitmix-%) $(TESTS:%=lxm-%) 82 | 83 | SMALLTESTS=$(ALLTESTS:%=testresults/us-%.log) 84 | 85 | smalltest: $(SMALLTESTS) 86 | @test/reporting $(SMALLTESTS) 87 | 88 | FULLTESTS=$(ALLTESTS:%=testresults/um-%.log) \ 89 | $(ALLTESTS:%=testresults/ur-%.log) \ 90 | $(ALLTESTS:%=testresults/ua-%.log) 91 | 92 | fulltest: $(FULLTESTS) 93 | @test/reporting $(FULLTESTS) 94 | 95 | HUGETESTS=$(ALLTESTS:%=testresults/ub-%.log) 96 | 97 | hugetest: $(HUGETESTS) 98 | @test/reporting $(HUGETESTS) 99 | 100 | consistencytest: test/consistency.exe 101 | ./test/consistency.exe 102 | 103 | benchmark: test/benchmark.exe 104 | ./test/benchmark.exe 105 | 106 | docs: *.mli 107 | mkdir -p docs 108 | ocamldoc -d docs/ -html *.mli 109 | 110 | depend: 111 | $(OCAMLDEP) *.mli *.ml > .depend 112 | 113 | include .depend 114 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | .keyword { font-weight : bold ; color : Red } 2 | .keywordsign { color : #C04600 } 3 | .comment { color : Green } 4 | .constructor { color : Blue } 5 | .type { color : #5C6585 } 6 | .string { color : Maroon } 7 | .warning { color : Red ; font-weight : bold } 8 | .info { margin-left : 3em; margin-right: 3em } 9 | .param_info { margin-top: 4px; margin-left : 3em; margin-right : 3em } 10 | .code { color : #465F91 ; } 11 | .typetable { border-style : hidden } 12 | .paramstable { border-style : hidden ; padding: 5pt 5pt} 13 | tr { background-color : White } 14 | td.typefieldcomment { background-color : #FFFFFF ; font-size: smaller ;} 15 | div.sig_block {margin-left: 2em} 16 | *:target { background: yellow; } 17 | body {font: 13px sans-serif; color: black; text-align: left; padding: 5px; margin: 0} 18 | h1 { font-size : 20pt ; text-align: center; } 19 | h2 { font-size : 20pt ; text-align: center; } 20 | h3 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #90BDFF ;padding: 2px; } 21 | h4 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #90DDFF ;padding: 2px; } 22 | h5 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #90EDFF ;padding: 2px; } 23 | h6 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #90FDFF ;padding: 2px; } 24 | div.h7 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #90BDFF ; padding: 2px; } 25 | div.h8 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #E0FFFF ; padding: 2px; } 26 | div.h9 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #F0FFFF ; padding: 2px; } 27 | div.h10 { font-size : 20pt ; border: 1px solid #000000; margin-top: 5px; margin-bottom: 2px;text-align: center; background-color: #FFFFFF ; padding: 2px; } 28 | a {color: #416DFF; text-decoration: none} 29 | a:hover {background-color: #ddd; text-decoration: underline} 30 | pre { margin-bottom: 4px; font-family: monospace; } 31 | pre.verbatim, pre.codepre { } 32 | .indextable {border: 1px #ddd solid; border-collapse: collapse} 33 | .indextable td, .indextable th {border: 1px #ddd solid; min-width: 80px} 34 | .indextable td.module {background-color: #eee ; padding-left: 2px; padding-right: 2px} 35 | .indextable td.module a {color: #4E6272; text-decoration: none; display: block; width: 100%} 36 | .indextable td.module a:hover {text-decoration: underline; background-color: transparent} 37 | .deprecated {color: #888; font-style: italic} 38 | .indextable tr td div.info { margin-left: 2px; margin-right: 2px } 39 | ul.indexlist { margin-left: 0; padding-left: 0;} 40 | ul.indexlist li { list-style-type: none ; margin-left: 0; padding-left: 0; } 41 | ul.info-attributes {list-style: none; margin: 0; padding: 0; } 42 | div.info > p:first-child { margin-top:0; } 43 | div.info-desc > p:first-child { margin-top:0; margin-bottom:0; } -------------------------------------------------------------------------------- /docs/index_modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of modules 12 | 13 | 14 | 16 |

Index of modules

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
C
Chacha [PRNG]
L
LXM [PRNG]
P
PRNG
27 |

This library provides pseudo-random number generators (PRNGs) comparable 28 | to that of the Random module from OCaml's standard library, but with 29 | two extensions: Generators are "splittable": they support a split operation that 30 | returns a new generator that is statistically independent from the 31 | current generator. Both generators can be used in parallel, and can 32 | be further splitted, without introducing statistical bias. This 33 | splitting operation is particularly useful to implement the lazy 34 | generation of pseudo-random infinite data structures such as 35 | functions or streams., In addition to a stateful, imperative interface resembling that of 36 | the Random.State standard library module, another, purely 37 | functional interface is provided. In the functional interface, the 38 | current state of the PRNG appears as parameter but also as result of 39 | the number generation functions. This interface can be used 40 | directly within a state monad.

41 | 42 |
43 |
Pure [PRNG.LXM]
Pure [PRNG.Chacha]
Pure [PRNG.Splitmix]
S
Splitmix [PRNG]
State [PRNG.LXM]
State [PRNG.Chacha]
State [PRNG.Splitmix]
60 | 61 | 62 | -------------------------------------------------------------------------------- /test/benchmark.ml: -------------------------------------------------------------------------------- 1 | (***********************************************************************) 2 | (* *) 3 | (* The PRINGO library *) 4 | (* *) 5 | (* Xavier Leroy, projet Gallium, INRIA Paris *) 6 | (* *) 7 | (* Copyright 2017 Institut National de Recherche en Informatique et *) 8 | (* en Automatique. All rights reserved. This file is distributed *) 9 | (* under the terms of the GNU Library General Public License v2, *) 10 | (* with the special exception on linking described in file LICENSE. *) 11 | (* *) 12 | (***********************************************************************) 13 | 14 | (* Speed test *) 15 | 16 | open Printf 17 | open PRNG 18 | 19 | let n = 50_000_000 20 | 21 | let time_fn msg fn = 22 | let start = Sys.time() in 23 | let res = fn() in 24 | let stop = Sys.time() in 25 | Printf.printf "%6.2f %s\n" (stop -. start) msg; 26 | flush stdout; 27 | res 28 | 29 | let repeat0 n fn () = 30 | for i = 1 to n do ignore (fn ()) done 31 | 32 | let repeat1 n fn arg () = 33 | for i = 1 to n do ignore (fn arg) done 34 | 35 | let repeat2 n fn arg1 arg2 () = 36 | for i = 1 to n do ignore (fn arg1 arg2) done 37 | 38 | module BState(X: STATE) = struct 39 | 40 | let repeat_state0 n fn () = 41 | let s = X.seed "xxx" in repeat1 n fn s () 42 | 43 | let repeat_state1 n fn arg () = 44 | let s = X.seed "xxx" in repeat2 n fn s arg () 45 | 46 | let run() = 47 | time_fn "bit" (repeat_state0 n X.bit); 48 | time_fn "bits" (repeat_state0 n X.bits); 49 | time_fn "int 0xFFEE" (repeat_state1 n X.int 0xFFEE); 50 | time_fn "int32 0xFFEEDD" (repeat_state1 n X.int32 0xFFEEDDl); 51 | time_fn "int64 0xFFEEDDCCAA" (repeat_state1 n X.int64 0xFFEEDDCCAAL); 52 | time_fn "float 1.0" (repeat_state1 n X.float 1.0); 53 | time_fn "split" (repeat_state0 n X.split); 54 | time_fn "seed 8" (repeat1 n X.seed "01234567"); 55 | time_fn "seed 16 (/10)" (repeat1 (n/10) X.seed "0123456789ABCDEF"); 56 | time_fn "make 3 (/10)" (repeat1 (n/10) X.make [|1234;5678;90909|]) 57 | 58 | end 59 | 60 | module BPure(X: PURE) = struct 61 | 62 | let repeat_pure0 n fn () = 63 | let rec rep n p = 64 | if n <= 0 then () else (let (_, p') = fn p in rep (n-1) p') 65 | in rep n (X.seed "xxx") 66 | 67 | let repeat_pure1 n fn arg () = 68 | let rec rep n p = 69 | if n <= 0 then () else (let (_, p') = fn arg p in rep (n-1) p') 70 | in rep n (X.seed "xxx") 71 | 72 | let run () = 73 | time_fn "bit" (repeat_pure0 n X.bit); 74 | time_fn "bits" (repeat_pure0 n X.bits); 75 | time_fn "int 0xFFEE" (repeat_pure1 n X.int 0xFFEE); 76 | time_fn "int32 0xFFEEDD" (repeat_pure1 n X.int32 0xFFEEDDl); 77 | time_fn "int64 0xFFEEDDCCAA" (repeat_pure1 n X.int64 0xFFEEDDCCAAL); 78 | time_fn "float 1.0" (repeat_pure1 n X.float 1.0); 79 | time_fn "split" (repeat_pure0 n X.split) 80 | 81 | end 82 | 83 | module BSS = BState(Splitmix.State) module BSP = BPure(Splitmix.Pure) 84 | module BCS = BState(Chacha.State) module BCP = BPure(Chacha.Pure) 85 | module BXS = BState(LXM.State) module BXP = BPure(LXM.Pure) 86 | 87 | let _ = 88 | printf "Times are in seconds for %d repetitions, unless indicated.\n" n; 89 | printf "---- Splitmix, state interface ----\n"; 90 | BSS.run(); 91 | printf "---- Splitmix, pure interface ----\n"; 92 | BSP.run(); 93 | printf "---- Chacha, state interface ----\n"; 94 | BCS.run(); 95 | printf "---- Chacha, pure interface ----\n"; 96 | BCP.run(); 97 | printf "---- LXM, state interface ----\n"; 98 | BXS.run(); 99 | printf "---- LXM, pure interface ----\n"; 100 | BXP.run(); 101 | printf "---- OCaml's Random module ----\n"; 102 | time_fn "bit" (repeat0 n Random.bool); 103 | time_fn "bits" (repeat0 n Random.bits); 104 | time_fn "int 0xFFEE" (repeat1 n Random.int 0xFFEE); 105 | time_fn "int32 0xFFEEDD" (repeat1 n Random.int32 0xFFEEDDl); 106 | time_fn "int64 0xFFEEDDCCAA" (repeat1 n Random.int64 0xFFEEDDCCAAL); 107 | time_fn "float 1.0" (repeat1 n Random.float 1.0); 108 | time_fn "make 3 (/1000)" (repeat1 (n/1000) Random.State.make [|1234;5678;90909|]); 109 | () 110 | 111 | 112 | -------------------------------------------------------------------------------- /test/consistency.ml: -------------------------------------------------------------------------------- 1 | (***********************************************************************) 2 | (* *) 3 | (* The PRINGO library *) 4 | (* *) 5 | (* Xavier Leroy, projet Gallium, INRIA Paris *) 6 | (* *) 7 | (* Copyright 2017 Institut National de Recherche en Informatique et *) 8 | (* en Automatique. All rights reserved. This file is distributed *) 9 | (* under the terms of the GNU Library General Public License v2, *) 10 | (* with the special exception on linking described in file LICENSE. *) 11 | (* *) 12 | (***********************************************************************) 13 | 14 | (* Check that the Pure and the State implementations agree *) 15 | 16 | open Printf 17 | open PRNG 18 | 19 | module Checker(X: sig module State: STATE module Pure: PURE end) = struct 20 | 21 | open X 22 | 23 | (* This is a variant of the state monad that carries both a Pure PRNG 24 | (monadically) and a State PRNG (as an environment). *) 25 | 26 | type 'a mon = State.t -> Pure.t -> 'a * Pure.t 27 | 28 | let ret (x: 'a) : 'a mon = fun s p -> (x, p) 29 | 30 | let (>>=) (m: 'a mon) (f: 'a -> 'b mon) : 'b mon = 31 | fun s p -> let (r1, p1) = m s p in f r1 s p1 32 | 33 | let rec check1 ?eq ?(n = 1) msg (sop: State.t -> 'a) 34 | (pop: Pure.t -> 'a * Pure.t) : 'a mon = 35 | fun s p -> 36 | let rs = sop s in 37 | let (rp, p') = pop p in 38 | if not (match eq with None -> rs = rp | Some f -> f rs rp) then begin 39 | printf "%s (%d): results differ\n" msg n; 40 | exit 2 41 | end; 42 | if n <= 1 then (rp, p') else check1 ?eq ~n:(n-1) msg sop pop s p' 43 | 44 | let rec check2 ?eq ?(n = 1) msg (sop: State.t -> 'a -> 'b) 45 | (pop: 'a -> Pure.t -> 'b * Pure.t) 46 | (arg: 'a) : 'b mon = 47 | fun s p -> 48 | let rs = sop s arg in 49 | let (rp, p') = pop arg p in 50 | if not (match eq with None -> rs = rp | Some f -> f rs rp) then begin 51 | printf "%s (%d): results differ\n" msg n; 52 | exit 2 53 | end; 54 | if n <= 1 then (rp, p') else check2 ?eq ~n:(n-1) msg sop pop arg s p' 55 | 56 | let lift (sop: State.t -> 'a) (pop: Pure.t -> 'b * Pure.t) : ('a * 'b) mon = 57 | fun s p -> 58 | let rs = sop s in 59 | let (rp, p') = pop p in 60 | ((rs, rp), p') 61 | 62 | (* Sequential checks *) 63 | 64 | let seq_checks = 65 | check1 ~n:10 "bit" State.bit Pure.bit >>= fun _ -> 66 | check1 ~n:10 "byte" State.byte Pure.byte >>= fun _ -> 67 | check1 ~n:10 "bits30" State.bits30 Pure.bits30 >>= fun _ -> 68 | check1 ~n:10 "bits32" State.bits32 Pure.bits32 >>= fun _ -> 69 | check1 ~n:10 "bits64" State.bits64 Pure.bits64 >>= fun _ -> 70 | check2 ~n:100 "int 42" State.int Pure.int 42 >>= fun _ -> 71 | check2 ~n:100 "int32 0xFFEE" State.int32 Pure.int32 0xFFEEl >>= fun _ -> 72 | check2 ~n:100 "int64 max_int" State.int64 Pure.int64 Int64.max_int >>= fun _ -> 73 | check2 ~n:100 "nativeint 0x123456" State.nativeint Pure.nativeint 0x123456n >>= fun _ -> 74 | check2 ~n:100 "float 1.0" State.float Pure.float 1.0 >>= fun _ -> 75 | lift State.split Pure.split 76 | 77 | (* Splitting *) 78 | 79 | let rec tree_checks n s p = 80 | if n > 0 then begin 81 | let ((new_s, new_p), final_p) = seq_checks s p in 82 | tree_checks (n-1) new_s new_p; 83 | tree_checks (n-1) s final_p 84 | end 85 | 86 | (* Seeding *) 87 | 88 | let full_checks_seed n seed = 89 | tree_checks n (State.seed seed) (Pure.seed seed) 90 | 91 | let full_checks_make n seed = 92 | tree_checks n (State.make seed) (Pure.make seed) 93 | 94 | end 95 | 96 | module ChkSplitmix = Checker(PRNG.Splitmix) 97 | module ChkChacha = Checker(PRNG.Chacha) 98 | module ChkLXM = Checker(PRNG.LXM) 99 | 100 | (* All together *) 101 | 102 | let _ = 103 | printf "Splitmix, with seed...\n"; 104 | ChkSplitmix.full_checks_seed 4 "Supercalifragiliciousexpialidolcius"; 105 | printf "Splitmix, with make...\n"; 106 | ChkSplitmix.full_checks_make 4 [|314159; 2718|]; 107 | printf "Chacha, with seed...\n"; 108 | ChkChacha.full_checks_seed 4 "Supercalifragiliciousexpialidolcius"; 109 | printf "Chacha, with make...\n"; 110 | ChkChacha.full_checks_make 4 [|314159; 2718|]; 111 | printf "LXM, with seed...\n"; 112 | ChkLXM.full_checks_seed 4 "Supercalifragiliciousexpialidolcius"; 113 | printf "LXM, with make...\n"; 114 | ChkLXM.full_checks_make 4 [|314159; 2718|]; 115 | printf "Test passed!\n" 116 | 117 | 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PRINGO: Pseudo-Random, splIttable Number Generators for Ocaml 2 | 3 | ## Overview 4 | 5 | This small library provides several pseudo-random number generators. From a hopefully truly random seed provided by the user or obtained from the system, those generators provide infinite streams of numbers whose statistical properties resemble the properties of streams of random numbers. 6 | 7 | Compared with the pseudo-random number generators provided by the `Random` module from the OCaml standard library, the PRINGO generators have two extra features: 8 | * They are **splittable**: they support a `split` operation that returns a new generator that is statistically independent from the current generator. Both generators can be used in parallel, and can be split further, without introducing statistical bias. This splitting operation is particularly useful to implement the lazy generation of pseudo-random infinite data structures such as functions or streams. 9 | * In addition to a stateful, imperative interface resembling that of the `Random.State` standard library module, another, **purely functional interface** is provided. In the functional interface, the current state of the PRNG appears as parameter but also as result of the number generation functions. This interface can be used directly within a state monad. 10 | 11 | Three PRNGS are provided: 12 | * `Splitmix`, as described in the paper [_Fast Splittable Pseudorandom Number Generators_](http://gee.cs.oswego.edu/dl/papers/oopsla14.pdf) by Guy L. Steele Jr., Doug Lea, and Christine H. Flood, published in the proceedings of OOPSLA 2014. 13 | * `Chacha`, which is based on the [Chacha stream cipher](https://cr.yp.to/chacha.html) by D. J. Bernstein. Splitting is implemented by pseudorandomly generating a 128-bit initial state for the new PRNG using the current PRNG. 14 | * `LXM`, as described in the paper [_LXM: Better Splittable Pseudorandom Number Generators (and Almost as Fast)_](https://doi.org/10.1145/3485525) by Guy L. Steele Jr. and Sebastiano Vigna, published in the proceedings of OOPSLA 2021. We use the L64X128 variant. 15 | 16 | All PRNGs pass the [TestU01](http://simul.iro.umontreal.ca/testu01/tu01.html) and [Dieharder](http://webhome.phy.duke.edu/~rgb/General/dieharder.php) statistical randomness tests. 17 | 18 | On 64-bit architectures, `Splitmix` is the fastest, closely followed by `LXM`. Both are slightly faster than OCaml's `Random` standard library implementation. 19 | 20 | On 32-bit architectures, `Chacha` is the faster of the three, a bit slower than OCaml's `Random` module. 21 | 22 | Splitmix has a 64-bit internal state, which is diversified by a 64-bit value called γ. Splitting is achieved by changing both γ and the state, while other operations change only the state. For a fixed γ, the period is 264, but it is recommended to reseed after 232 numbers were generated. From the initial seed, 64 bits worth of entropy are used. Splitmix is not cryptographically strong: the internal state can be reconstructed from any two consecutive calls to `bits64`. 23 | 24 | LXM, or more precisely the L64X128 variant used in PRINGO, has a 192-bit internal state plus a 64-bit value (the additive parameter) for diversification. Seeding and splitting change both the state and the additive parameter, while other operations change only the state. For a fixed additive parameter, the period is 2192-264. Reseeding should not be necessary in practice. From the initial seed, up to 32 bytes (256 bits) are used to initialize the state and the additive parameter. Like Splitmix, LXM is not cryptographically strong. 25 | 26 | Chacha is the 8-round Chacha stream cipher encrypting a sequence of zeros. The internal state is a 128-bit counter. Splitting is achieved by generating a pseudo-random initial value for the counter of the new PRNG. The period of the PRNG is unclear. It is recommended to generate no more than 264 bytes before reseeding. Up to 32 bytes (256 bits) of the seed are used as the Chacha key, although 16 bytes (128 bits) are probably enough. The PRNG is probably cryptographically strong, even though it uses the 8-round variant of Chacha, which has a lower security margin than the 20-round variant widely used as a stream cipher. 27 | 28 | ## Installation and usage 29 | 30 | The only dependencies are a recent enough version of OCaml (4.05.0 or up) and the findlib/ocamlfind library manager. 31 | 32 | To build and install, just do `make` then become superuser if necessary and do `make install`. 33 | 34 | To use the library, use `ocamlfind` and select the `pringo` package, e.g. 35 | ``` 36 | ocamlfind opt -linkpkg -package pringo 37 | ``` 38 | 39 | Documentation is available [online](https://xavierleroy.org/pringo/PRNG.html) 40 | and as comments in the interface `PRNG.mli`. 41 | 42 | ## Testing 43 | 44 | The `testu01` OPAM package must be installed. 45 | 46 | - `make -jN smalltest` runs the "small crush" battery of tests. It takes several minutes with N=8. 47 | - `make -jN fulltest` runs the "crush", "rabbit", and "alphabit" batteries of tests. It takes several hours with N=8. 48 | - `make -jN hugetest` runs the "big crush" battery. It takes several days with N=8. 49 | 50 | ## Copyright and license 51 | 52 | Copyright Inria. License LGPL v2 with special exception for static linking. 53 | -------------------------------------------------------------------------------- /docs/PRNG.PURE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PRNG.PURE 18 | 19 | 20 | 23 |

Module type PRNG.PURE

24 | 25 |
module type PURE = sig .. end

26 | 27 |
type t 
28 | 29 |

Creating and seeding PRNGs

30 |
val seed : string -> t
31 |
val make : int array -> t
32 |
val make_self_init : unit -> t

Generating pseudo-random data

33 |
val bool : t -> bool * t
34 |
val bit : t -> bool * t
35 |
val uniform : t -> float * t
36 |
val float : float -> t -> float * t
37 |
val byte : t -> int * t
38 |
val bits8 : t -> int * t
39 |
val int : int -> t -> int * t
40 |
val bits : t -> int * t
41 |
val bits30 : t -> int * t
42 |
val bits32 : t -> int32 * t
43 |
val int32 : int32 -> t -> int32 * t
44 |
val bits64 : t -> int64 * t
45 |
val int64 : int64 -> t -> int64 * t
46 |
val nativebits : t -> nativeint * t
47 |
val nativeint : nativeint -> t -> nativeint * t
48 |
val char : t -> char * t

Splitting

49 |
val split : t -> t * t
50 | -------------------------------------------------------------------------------- /docs/PRNG.Chacha.Pure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PRNG.Chacha.Pure 18 | 19 | 20 | 23 |

Module PRNG.Chacha.Pure

24 | 25 |
module Pure: PRNG.PURE 

26 | 27 |
type t 
28 | 29 |

Creating and seeding PRNGs

30 |
val seed : string -> t
31 |
val make : int array -> t
32 |
val make_self_init : unit -> t

Generating pseudo-random data

33 |
val bool : t -> bool * t
34 |
val bit : t -> bool * t
35 |
val uniform : t -> float * t
36 |
val float : float -> t -> float * t
37 |
val byte : t -> int * t
38 |
val bits8 : t -> int * t
39 |
val int : int -> t -> int * t
40 |
val bits : t -> int * t
41 |
val bits30 : t -> int * t
42 |
val bits32 : t -> int32 * t
43 |
val int32 : int32 -> t -> int32 * t
44 |
val bits64 : t -> int64 * t
45 |
val int64 : int64 -> t -> int64 * t
46 |
val nativebits : t -> nativeint * t
47 |
val nativeint : nativeint -> t -> nativeint * t
48 |
val char : t -> char * t

Splitting

49 |
val split : t -> t * t
50 | -------------------------------------------------------------------------------- /docs/PRNG.Splitmix.Pure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PRNG.Splitmix.Pure 18 | 19 | 20 | 23 |

Module PRNG.Splitmix.Pure

24 | 25 |
module Pure: PRNG.PURE 

26 | 27 |
type t 
28 | 29 |

Creating and seeding PRNGs

30 |
val seed : string -> t
31 |
val make : int array -> t
32 |
val make_self_init : unit -> t

Generating pseudo-random data

33 |
val bool : t -> bool * t
34 |
val bit : t -> bool * t
35 |
val uniform : t -> float * t
36 |
val float : float -> t -> float * t
37 |
val byte : t -> int * t
38 |
val bits8 : t -> int * t
39 |
val int : int -> t -> int * t
40 |
val bits : t -> int * t
41 |
val bits30 : t -> int * t
42 |
val bits32 : t -> int32 * t
43 |
val int32 : int32 -> t -> int32 * t
44 |
val bits64 : t -> int64 * t
45 |
val int64 : int64 -> t -> int64 * t
46 |
val nativebits : t -> nativeint * t
47 |
val nativeint : nativeint -> t -> nativeint * t
48 |
val char : t -> char * t

Splitting

49 |
val split : t -> t * t
50 | -------------------------------------------------------------------------------- /docs/type_PRNG.STATE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.STATE 11 | 12 | 13 | sig
14 |   type t
15 |   val seed : string -> PRNG.STATE.t
16 |   val make : int array -> PRNG.STATE.t
17 |   val make_self_init : unit -> PRNG.STATE.t
18 |   val bool : PRNG.STATE.t -> bool
19 |   val bit : PRNG.STATE.t -> bool
20 |   val uniform : PRNG.STATE.t -> float
21 |   val float : PRNG.STATE.t -> float -> float
22 |   val byte : PRNG.STATE.t -> int
23 |   val bits8 : PRNG.STATE.t -> int
24 |   val bits : PRNG.STATE.t -> int
25 |   val bits30 : PRNG.STATE.t -> int
26 |   val int : PRNG.STATE.t -> int -> int
27 |   val bits32 : PRNG.STATE.t -> int32
28 |   val int32 : PRNG.STATE.t -> int32 -> int32
29 |   val bits64 : PRNG.STATE.t -> int64
30 |   val int64 : PRNG.STATE.t -> int64 -> int64
31 |   val nativebits : PRNG.STATE.t -> nativeint
32 |   val nativeint : PRNG.STATE.t -> nativeint -> nativeint
33 |   val char : PRNG.STATE.t -> char
34 |   val bytes : PRNG.STATE.t -> bytes -> int -> int -> unit
35 |   val split : PRNG.STATE.t -> PRNG.STATE.t
36 |   val copy : PRNG.STATE.t -> PRNG.STATE.t
37 |   val reseed : PRNG.STATE.t -> string -> unit
38 |   val remake : PRNG.STATE.t -> int array -> unit
39 | end
40 | -------------------------------------------------------------------------------- /docs/PRNG.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PRNG 19 | 20 | 21 | 23 |

Module PRNG

24 | 25 |
module PRNG: sig .. end
26 |
27 |

This library provides pseudo-random number generators (PRNGs) comparable 28 | to that of the Random module from OCaml's standard library, but with 29 | two extensions:

30 |
    31 |
  • Generators are "splittable": they support a split operation that 32 | returns a new generator that is statistically independent from the 33 | current generator. Both generators can be used in parallel, and can 34 | be further splitted, without introducing statistical bias. This 35 | splitting operation is particularly useful to implement the lazy 36 | generation of pseudo-random infinite data structures such as 37 | functions or streams.
  • 38 |
  • In addition to a stateful, imperative interface resembling that of 39 | the Random.State standard library module, another, purely 40 | functional interface is provided. In the functional interface, the 41 | current state of the PRNG appears as parameter but also as result of 42 | the number generation functions. This interface can be used 43 | directly within a state monad.
  • 44 |
45 |
46 |
47 |
48 |

The stateful interface

49 |
module type STATE = sig .. end

The purely-functional, monadic interface

In this alternate interface, number-generating functions do not 50 | update the current state of the generator in-place. Instead, they 51 | return the updated generator as a second result. It is the 52 | programmer's responsibility to correctly thread the generators 53 | through the program, typically by using a state monad.

54 | 55 |

All operations of the STATE interface are provided except 56 | bytes (too imperative) and copy, reseed and remake (pointless).

57 | 58 |
module type PURE = sig .. end

The Splitmix implementation

59 |
module Splitmix: sig .. end

This is an implementation of the STATE and PURE interfaces 60 | based on the Splitmix design by Guy L. Steele Jr., Doug Lea, and 61 | Christine H. Flood.

62 | 63 |

For seeding, 64 bits of entropy is recommended. Seeds of 8 64 | characters or less are used as a 64-bit integer. Longer seeds 65 | are hashed using Digest.string before being used.

66 | 67 |

Reseeding is recommended after 232 numbers have been generated.

68 |

The Chacha-20 implementation

69 |
module Chacha: sig .. end

This is an implementation of the STATE and PURE interfaces 70 | based on the Chacha 20 stream cipher by D. J. Bernstein.

71 | 72 |

For seeding, 128 bits of entropy is recommended. Seeds of up 73 | to 32 characters are used as keys to the Chacha 20 cipher. 74 | Characters beyond the first 32 are ignored.

75 | 76 |

Reseeding is recommended after 264 numbers have been generated.

77 |

The LXM implementation

78 |
module LXM: sig .. end

This is an implementation of the STATE and PURE interfaces 79 | based on the LXM design by Guy L. Steele Jr, and Sebastiano Vigna. 80 | We use the L64X128 variant from Fig. 1 of their OOPSLA 2021 paper.

81 | 82 |

For seeding, 128 bits of entropy is recommended. The last 32 bytes 83 | of the seed are used to initialize the PRNG state.

84 | 85 |

This PRNG has a large internal state (192 bits) and a period of 86 | 2192 - 264. Therefore, reseeding should not be necessary 87 | in practice.

88 | 89 | -------------------------------------------------------------------------------- /docs/type_PRNG.PURE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG.PURE 11 | 12 | 13 | sig
14 |   type t
15 |   val seed : string -> PRNG.PURE.t
16 |   val make : int array -> PRNG.PURE.t
17 |   val make_self_init : unit -> PRNG.PURE.t
18 |   val bool : PRNG.PURE.t -> bool * PRNG.PURE.t
19 |   val bit : PRNG.PURE.t -> bool * PRNG.PURE.t
20 |   val uniform : PRNG.PURE.t -> float * PRNG.PURE.t
21 |   val float : float -> PRNG.PURE.t -> float * PRNG.PURE.t
22 |   val byte : PRNG.PURE.t -> int * PRNG.PURE.t
23 |   val bits8 : PRNG.PURE.t -> int * PRNG.PURE.t
24 |   val int : int -> PRNG.PURE.t -> int * PRNG.PURE.t
25 |   val bits : PRNG.PURE.t -> int * PRNG.PURE.t
26 |   val bits30 : PRNG.PURE.t -> int * PRNG.PURE.t
27 |   val bits32 : PRNG.PURE.t -> int32 * PRNG.PURE.t
28 |   val int32 : int32 -> PRNG.PURE.t -> int32 * PRNG.PURE.t
29 |   val bits64 : PRNG.PURE.t -> int64 * PRNG.PURE.t
30 |   val int64 : int64 -> PRNG.PURE.t -> int64 * PRNG.PURE.t
31 |   val nativebits : PRNG.PURE.t -> nativeint * PRNG.PURE.t
32 |   val nativeint : nativeint -> PRNG.PURE.t -> nativeint * PRNG.PURE.t
33 |   val char : PRNG.PURE.t -> char * PRNG.PURE.t
34 |   val split : PRNG.PURE.t -> PRNG.PURE.t * PRNG.PURE.t
35 | end
36 | -------------------------------------------------------------------------------- /test/u01.ml: -------------------------------------------------------------------------------- 1 | open TestU01 2 | 3 | (* Creating generators *) 4 | 5 | let gfloat name (f: unit -> float) = 6 | Unif01.create_extern_gen_01 name f 7 | 8 | let gint32 name (f: unit -> int32) = 9 | Unif01.create_extern_gen_int32 name f 10 | 11 | let gint8 name (f: unit -> int) = 12 | let g () = 13 | let b1 = f () in 14 | let b2 = f () in 15 | let b3 = f () in 16 | let b4 = f () in 17 | let b12 = b1 lsl 8 + b2 in 18 | let b34 = b3 lsl 8 + b4 in 19 | Int32.(add (shift_left (of_int b12) 16) (of_int b34)) 20 | in gint32 name g 21 | 22 | let gint32list name (f: unit -> int32 list) = 23 | let acc = ref [] in 24 | let rec g () = 25 | match !acc with 26 | | [] -> acc := f(); g() 27 | | h :: t -> acc := t; h 28 | in gint32 name g 29 | 30 | let gint32seq name (s: int32 Seq.t) = 31 | let rs = ref s in 32 | let rec f () = 33 | match !rs () with 34 | | Seq.Nil -> assert false 35 | | Seq.Cons(n, s') -> rs := s'; n 36 | in gint32 name f 37 | 38 | let gint64 name (f: unit -> int64) = 39 | let g () = 40 | let n = f() in 41 | [Int64.to_int32 n; Int64.(to_int32 (shift_right_logical n 32))] 42 | in gint32list name g 43 | 44 | (* Test harness *) 45 | 46 | let seed = ref "Jamais un coup de dés n'abolira le hasard. -Mallarmé" 47 | 48 | module type TEST = sig 49 | val gen_floats: unit -> Unif01.gen 50 | val gen_bytes: unit -> Unif01.gen 51 | val gen_int32: unit -> Unif01.gen 52 | val gen_int64: unit -> Unif01.gen 53 | val gen_blocks: int -> Unif01.gen 54 | val treesplits: int -> Unif01.gen 55 | val laggedsplit: int -> Unif01.gen 56 | val split_l: unit -> Unif01.gen 57 | val split_r: unit -> Unif01.gen 58 | val split_a: unit -> Unif01.gen 59 | val split_s: unit -> Unif01.gen 60 | end 61 | 62 | module Maketest (R: PRNG.STATE) : TEST = struct 63 | 64 | let init () = R.seed !seed 65 | 66 | let gen_floats () = 67 | let g = init() in gfloat "float" (fun () -> R.uniform g) 68 | 69 | let gen_bytes () = 70 | let g = init() in gint8 "byte" (fun () -> R.byte g) 71 | 72 | let gen_int32 () = 73 | let g = init() in gint32 "int32" (fun () -> R.bits32 g) 74 | 75 | let gen_int64 () = 76 | let g = init() in gint64 "int64" (fun () -> R.bits64 g) 77 | 78 | let gen_blocks n = 79 | let g = init() in 80 | let b = Bytes.create n in 81 | let pos = ref n in 82 | let f () = 83 | let p = !pos in 84 | if p < n 85 | then (pos := p + 1; Bytes.get_uint8 b p) 86 | else (R.bytes g b 0 n; pos := 1; Bytes.get_uint8 b 0) 87 | in gint8 "blocks" f 88 | 89 | let treesplits n = 90 | let rec mkgens n g = 91 | if n <= 0 then [g] else begin 92 | let g' = R.split g in 93 | mkgens (n-1) g @ mkgens (n-1) g' 94 | end in 95 | let gl = mkgens n (init()) in 96 | let f () = List.map R.bits32 gl 97 | in gint32list "treesplits" f 98 | 99 | let laggedsplit n = 100 | let g = ref (init()) 101 | and i = ref 0 in 102 | let rec f () = 103 | if !i < n 104 | then (incr i; R.bits32 !g) 105 | else (i := 0; g := R.split !g; f ()) 106 | in gint32 "laggedsplit" f 107 | 108 | (* Split sequence "S_L". Split, generate number with "left" generator, then 109 | recurse using "right" generator. 110 | 111 | This and the following "split sequences" are defined in sections 5.5 and 5.6 112 | of: 113 | 114 | Hans Georg Schaathun. 2015. Evaluation of splittable pseudo-random 115 | generators. Journal of Functional Programming, Vol. 25. 116 | https://doi.org/10.1017/S095679681500012X 117 | 118 | split 119 | / \ 120 | (1) … 121 | 122 | *) 123 | 124 | let split_l () = 125 | let rec spl g () = 126 | let gR = R.split g in (* now gL = g *) 127 | Seq.Cons(R.bits32 g (* 1 *), spl gR) 128 | in gint32seq "split_l" (spl (init())) 129 | 130 | (* Split sequence "S_R". Split, generate number with "right" generator, then 131 | recurse using "left" generator. 132 | 133 | split 134 | / \ 135 | … (1) 136 | 137 | *) 138 | 139 | let split_r () = 140 | let rec spl g () = 141 | let gR = R.split g in (* now gL = g *) 142 | Seq.Cons(R.bits32 gR (* 1 *), spl g) 143 | in gint32seq "split_r" (spl (init())) 144 | 145 | (* Split sequence "S_A". Split, generate number with "right" generator, then 146 | split again, generate number with "left" generator, and recurse with "right" 147 | generator. 148 | 149 | split 150 | / \ 151 | split (1) 152 | / \ 153 | (2) … 154 | 155 | *) 156 | 157 | let split_a () = 158 | let rec spl g () = 159 | let gR = R.split g in 160 | let n1 = R.bits32 gR in (* 1 *) 161 | let gLR = R.split g in 162 | let n2 = R.bits32 g in (* 2 *) 163 | Seq.Cons(n1, fun () -> Seq.Cons(n2, spl gLR)) 164 | in gint32seq "split_a" (spl (init())) 165 | 166 | (* Split sequence "S". 167 | 168 | split 169 | / \ 170 | … split 171 | / \ 172 | split split 173 | / \ / \ 174 | (1) (2) (3) (4) 175 | 176 | *) 177 | 178 | let split_s () = 179 | let rec spl g () = 180 | let gR = R.split g in (* now gL = g *) 181 | let gRR = R.split gR in (* now gRL = gR *) 182 | let gRRR = R.split gRR in (* now gRRL = gRR *) 183 | let gRLR = R.split gR in (* now gRLL = gR *) 184 | let n1 = R.bits32 gR (* 1 *) in 185 | let n2 = R.bits32 gRLR (* 2 *) in 186 | let n3 = R.bits32 gRR (* 3 *) in 187 | let n4 = R.bits32 gRRR (* 4 *) in 188 | Seq.Cons(n1, fun () -> Seq.Cons(n2, fun () -> Seq.Cons(n3, fun () -> Seq.Cons(n4, spl g)))) 189 | in gint32seq "split_s" (spl (init())) 190 | 191 | end 192 | 193 | module T1 = Maketest(PRNG.Splitmix.State) 194 | module T2 = Maketest(PRNG.Chacha.State) 195 | module T3 = Maketest(PRNG.LXM.State) 196 | 197 | let dut = ref (module T1 : TEST) 198 | 199 | let gen_floats () = 200 | let module T = (val !dut) in T.gen_floats() 201 | let gen_bytes () = 202 | let module T = (val !dut) in T.gen_bytes() 203 | let gen_int32 () = 204 | let module T = (val !dut) in T.gen_int32() 205 | let gen_int64 () = 206 | let module T = (val !dut) in T.gen_int64() 207 | let gen_blocks n = 208 | let module T = (val !dut) in T.gen_blocks n 209 | let treesplits n = 210 | let module T = (val !dut) in T.treesplits n 211 | let laggedsplit n = 212 | let module T = (val !dut) in T.laggedsplit n 213 | let split_l () = 214 | let module T = (val !dut) in T.split_l() 215 | let split_r () = 216 | let module T = (val !dut) in T.split_r() 217 | let split_a () = 218 | let module T = (val !dut) in T.split_a() 219 | let split_s () = 220 | let module T = (val !dut) in T.split_s() 221 | 222 | let gen_config s = 223 | let l = 224 | match String.split_on_char '-' s with 225 | | "splitmix" :: l -> dut := (module T1 : TEST); l 226 | | "chacha" :: l -> dut := (module T2 : TEST); l 227 | | "lxm" :: l -> dut := (module T3 : TEST); l 228 | | _ -> raise (Arg.Bad ("unknown configuration " ^ s)) in 229 | match l with 230 | | ["float"] -> gen_floats() 231 | | ["seq8"] -> gen_bytes() 232 | | ["seq32"] -> gen_int32() 233 | | ["seq64"] -> gen_int64() 234 | | ["block"; n] -> gen_blocks (int_of_string n) 235 | | ["treesplit"; n] -> treesplits (int_of_string n) 236 | | ["laggedsplit"; n] -> laggedsplit (int_of_string n) 237 | | ["splitl"] -> split_l() 238 | | ["splitr"] -> split_r() 239 | | ["splita"] -> split_a() 240 | | ["splits"] -> split_s() 241 | | _ -> raise (Arg.Bad ("unknown configuration " ^ s)) 242 | 243 | let size = ref 1 244 | 245 | let run_config config = 246 | match !size with 247 | | 1 -> Bbattery.small_crush (gen_config config) 248 | | 2 -> Bbattery.crush (gen_config config) 249 | | 3 -> Bbattery.big_crush (gen_config config) 250 | | 4 -> Bbattery.rabbit (gen_config config) (2.0 ** 26.) 251 | | 5 -> Bbattery.alphabit (gen_config config) (2.0 ** 30.) 0 32 252 | | _ -> assert false 253 | 254 | let _ = 255 | Arg.(parse [ 256 | "-small", Unit (fun () -> size := 1), 257 | "Run the small crush tests"; 258 | "-medium", Unit (fun () -> size := 2), 259 | "Run the normal crush tests"; 260 | "-big", Unit (fun () -> size := 3), 261 | "Run the big crush tests"; 262 | "-rabbit", Unit (fun () -> size := 4), 263 | "Run the Rabbit tests"; 264 | "-alphabit", Unit (fun () -> size := 5), 265 | "Run the Alphabit tests"; 266 | ] 267 | run_config 268 | "Usage: ./testU01 [config]\nOptions are:") 269 | -------------------------------------------------------------------------------- /test/generator.ml: -------------------------------------------------------------------------------- 1 | (***********************************************************************) 2 | (* *) 3 | (* The PRINGO library *) 4 | (* *) 5 | (* Xavier Leroy, projet Gallium, INRIA Paris *) 6 | (* *) 7 | (* Copyright 2017 Institut National de Recherche en Informatique et *) 8 | (* en Automatique. All rights reserved. This file is distributed *) 9 | (* under the terms of the GNU Library General Public License v2, *) 10 | (* with the special exception on linking described in file LICENSE. *) 11 | (* *) 12 | (***********************************************************************) 13 | 14 | (* Testing PRNGs using the Dieharder statistical tests, 15 | http://webhome.phy.duke.edu/~rgb/General/dieharder.php *) 16 | 17 | let seed = ref "Jamais un coup de dés n'abolira le hasard. -Mallarmé" 18 | 19 | module type TEST = sig 20 | val gen_bytes: unit -> unit 21 | val gen_int32: unit -> unit 22 | val gen_int64: unit -> unit 23 | val gen_blocks: int -> unit 24 | val treesplits: int -> unit 25 | val laggedsplit: int -> unit 26 | val split_l: unit -> unit 27 | val split_r: unit -> unit 28 | val split_a: unit -> unit 29 | val split_s: unit -> unit 30 | end 31 | 32 | module Maketest (R: PRNG.STATE) : TEST = struct 33 | 34 | let init () = R.seed !seed 35 | 36 | let out8 g = 37 | output_byte stdout (R.byte g) 38 | 39 | let out32 g = 40 | let n = R.bits32 g in 41 | output_byte stdout (Int32.to_int n); 42 | output_byte stdout (Int32.(to_int (shift_right_logical n 8))); 43 | output_byte stdout (Int32.(to_int (shift_right_logical n 16))); 44 | output_byte stdout (Int32.(to_int (shift_right_logical n 24))) 45 | 46 | let out64 g = 47 | let n = R.bits64 g in 48 | output_byte stdout (Int64.to_int n); 49 | output_byte stdout (Int64.(to_int (shift_right_logical n 8))); 50 | output_byte stdout (Int64.(to_int (shift_right_logical n 16))); 51 | output_byte stdout (Int64.(to_int (shift_right_logical n 24))); 52 | output_byte stdout (Int64.(to_int (shift_right_logical n 32))); 53 | output_byte stdout (Int64.(to_int (shift_right_logical n 40))); 54 | output_byte stdout (Int64.(to_int (shift_right_logical n 48))); 55 | output_byte stdout (Int64.(to_int (shift_right_logical n 56))) 56 | 57 | (* Generate byte per byte on stdout *) 58 | 59 | let gen_bytes () = 60 | let g = init() in while true do out8 g done 61 | 62 | (* Generate using 32-bit integers *) 63 | 64 | let gen_int32 () = 65 | let g = init() in while true do out32 g done 66 | 67 | (* Generate using 64-bit integers *) 68 | 69 | let gen_int64 () = 70 | let g = init() in while true do out64 g done 71 | 72 | (* Generate using N-byte blocks *) 73 | 74 | let gen_blocks n = 75 | let g = init() in 76 | let b = Bytes.create n in 77 | while true do 78 | R.bytes g b 0 n; 79 | output stdout b 0 n 80 | done 81 | 82 | (* Tree of splits. Produce 2^N generators then interleave their outputs *) 83 | 84 | let treesplits n = 85 | let rec mkgens n g = 86 | if n <= 0 then [g] else begin 87 | let g' = R.split g in 88 | mkgens (n-1) g @ mkgens (n-1) g' 89 | end in 90 | let gl = mkgens n (init()) in 91 | while true do List.iter out32 gl done 92 | 93 | (* Lagged split. Split, then produce N numbers with the original RNG, then 94 | switch to the splitted RNG and repeat. *) 95 | 96 | let laggedsplit n = 97 | let rec lag g = 98 | let g' = R.split g in 99 | for _i = 1 to n do out32 g done; 100 | lag g' 101 | in lag (init()) 102 | 103 | (* Split sequence "S_L". Split, generate number with "left" generator, then 104 | recurse using "right" generator. 105 | 106 | This and the following "split sequences" are defined in sections 5.5 and 5.6 107 | of: 108 | 109 | Hans Georg Schaathun. 2015. Evaluation of splittable pseudo-random 110 | generators. Journal of Functional Programming, Vol. 25. 111 | https://doi.org/10.1017/S095679681500012X 112 | 113 | split 114 | / \ 115 | (1) … 116 | 117 | *) 118 | 119 | let split_l () = 120 | let rec spl g = 121 | let gR = R.split g in (* now gL = g *) 122 | out32 g; (* 1 *) 123 | spl gR 124 | in spl (init()) 125 | 126 | (* Split sequence "S_R". Split, generate number with "right" generator, then 127 | recurse using "left" generator. 128 | 129 | split 130 | / \ 131 | … (1) 132 | 133 | *) 134 | 135 | let split_r () = 136 | let rec spl g = 137 | let gR = R.split g in (* now gL = g *) 138 | out32 gR; (* 1 *) 139 | spl g 140 | in spl (init()) 141 | 142 | (* Split sequence "S_A". Split, generate number with "right" generator, then 143 | split again, generate number with "left" generator, and recurse with "right" 144 | generator. 145 | 146 | split 147 | / \ 148 | split (1) 149 | / \ 150 | (2) … 151 | 152 | *) 153 | 154 | let split_a () = 155 | let rec spl g = 156 | let gR = R.split g in 157 | out32 gR; (* 1 *) 158 | let gLR = R.split g in 159 | out32 g; (* 2 *) 160 | spl gLR 161 | in spl (init()) 162 | 163 | (* Split sequence "S". 164 | 165 | split 166 | / \ 167 | … split 168 | / \ 169 | split split 170 | / \ / \ 171 | (1) (2) (3) (4) 172 | 173 | *) 174 | 175 | let split_s () = 176 | let rec spl g = 177 | let gR = R.split g in (* now gL = g *) 178 | let gRR = R.split gR in (* now gRL = gR *) 179 | let gRRR = R.split gRR in (* now gRRL = gRR *) 180 | let gRLR = R.split gR in (* now gRLL = gR *) 181 | out32 gR; (* 1 *) 182 | out32 gRLR; (* 2 *) 183 | out32 gRR; (* 3 *) 184 | out32 gRRR; (* 4 *) 185 | spl g 186 | in spl (init()) 187 | 188 | end 189 | 190 | module T1 = Maketest(PRNG.Splitmix.State) 191 | module T2 = Maketest(PRNG.Chacha.State) 192 | module T3 = Maketest(PRNG.LXM.State) 193 | 194 | let dut = ref (module T1 : TEST) 195 | 196 | let gen_bytes () = 197 | let module T = (val !dut) in T.gen_bytes() 198 | let gen_int32 () = 199 | let module T = (val !dut) in T.gen_int32() 200 | let gen_int64 () = 201 | let module T = (val !dut) in T.gen_int64() 202 | let gen_blocks n = 203 | let module T = (val !dut) in T.gen_blocks n 204 | let treesplits n = 205 | let module T = (val !dut) in T.treesplits n 206 | let laggedsplit n = 207 | let module T = (val !dut) in T.laggedsplit n 208 | let split_l () = 209 | let module T = (val !dut) in T.split_l() 210 | let split_r () = 211 | let module T = (val !dut) in T.split_r() 212 | let split_a () = 213 | let module T = (val !dut) in T.split_a() 214 | let split_s () = 215 | let module T = (val !dut) in T.split_s() 216 | 217 | let run_config s = 218 | let l = 219 | match String.split_on_char '-' s with 220 | | "splitmix" :: l -> dut := (module T1 : TEST); l 221 | | "chacha" :: l -> dut := (module T2 : TEST); l 222 | | "lxm" :: l -> dut := (module T3 : TEST); l 223 | | _ -> raise (Arg.Bad ("unknown configuration " ^ s)) in 224 | match l with 225 | | ["seq8"] -> gen_bytes() 226 | | ["seq32"] -> gen_int32() 227 | | ["seq64"] -> gen_int64() 228 | | ["block"; n] -> gen_blocks (int_of_string n) 229 | | ["treesplit"; n] -> treesplits (int_of_string n) 230 | | ["laggedsplit"; n] -> laggedsplit (int_of_string n) 231 | | ["splitl"] -> split_l() 232 | | ["splitr"] -> split_r() 233 | | ["splita"] -> split_a() 234 | | ["splits"] -> split_s() 235 | | _ -> raise (Arg.Bad ("unknown configuration " ^ s)) 236 | 237 | let _ = 238 | Arg.(parse [ 239 | "-splitmix", Unit (fun () -> dut := (module T1 : TEST)), 240 | " Test the Splitmix implementation"; 241 | "-chacha", Unit (fun () -> dut := (module T2 : TEST)), 242 | " Test the Chacha implementation"; 243 | "-lxm", Unit (fun () -> dut := (module T3 : TEST)), 244 | " Test the LXM implementation"; 245 | "-seed", Set_string seed, 246 | " Choose a seed"; 247 | "-seq8", Unit gen_bytes, 248 | " Produce 8-bit numbers sequentially"; 249 | "-seq32", Unit gen_int32, 250 | " Produce 32-bit numbers sequentially"; 251 | "-seq64", Unit gen_int64, 252 | " Produce 64-bit numbers sequentially"; 253 | "-block", Int gen_blocks, 254 | " Produce n-byte blocks sequentially"; 255 | "-treesplit", Int treesplits, 256 | " Perform 2^n splits then round robin between them"; 257 | "-laggedsplit", Int laggedsplit, 258 | " Split, produce n 32-bit numbers, then use the split"; 259 | "-splitl", Unit split_l, 260 | " Produce 'split sequence' S_L"; 261 | "-splitr", Unit split_r, 262 | " Produce 'split sequence' S_R"; 263 | "-splita", Unit split_a, 264 | " Produce 'split sequence' S_A"; 265 | "-splits", Unit split_s, 266 | " Produce 'split sequence' S" 267 | ] 268 | run_config 269 | "Usage: ./generator [config] | dieharder -a -g 200.\nOptions are:") 270 | 271 | -------------------------------------------------------------------------------- /docs/index_values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Index of values 12 | 13 | 14 | 16 |

Index of values

17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 61 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 81 | 82 | 83 | 84 | 85 | 90 | 91 | 96 | 97 | 98 | 99 | 100 | 106 | 107 | 108 | 109 | 110 | 116 | 117 | 118 | 119 | 125 | 126 | 127 | 128 | 134 | 135 | 136 | 137 | 138 | 143 | 144 | 145 | 146 | 152 | 153 | 154 | 155 | 156 | 163 | 164 | 165 | 166 | 172 | 173 | 174 | 180 | 181 | 187 | 188 | 189 | 190 | 191 | 196 | 197 | 198 | 199 | 205 | 206 | 207 | 208 | 209 | 214 |
B
bit [PRNG.PURE]
bit [PRNG.STATE]
23 |

Return a Boolean value in false,true with 0.5 probability each.

24 | 25 |
26 |
bits [PRNG.PURE]
bits [PRNG.STATE]
bits30 [PRNG.PURE]
bits30 [PRNG.STATE]
35 |

Return a 30-bit integer evenly distributed between 0 and 230-1 36 | (that is, 1073741823, or 0x3FFFFFFF).

37 | 38 |
39 |
bits32 [PRNG.PURE]
bits32 [PRNG.STATE]
44 |

Return a 32-bit integer evenly distributed between 45 | and .

46 | 47 |
48 |
bits64 [PRNG.PURE]
bits64 [PRNG.STATE]
53 |

Return a 64-bit integer evenly distributed between 54 | and .

55 | 56 |
57 |
bits8 [PRNG.PURE]
bits8 [PRNG.STATE]
62 |

Return an 8-bit integer evenly distributed between 0 and 255.

63 | 64 |
65 |
bool [PRNG.PURE]
bool [PRNG.STATE]
byte [PRNG.PURE]
byte [PRNG.STATE]
bytes [PRNG.STATE]
76 |

bytes g b ofs len produces len bytes of pseudo-random data 77 | and stores them in byte sequence b at offsets ofs to ofs+len-1.

78 | 79 |
80 |
C
char [PRNG.PURE]
char [PRNG.STATE]
86 |

Return a character evenly distributed among '\000' ... '\255'.

87 | 88 |
89 |
copy [PRNG.STATE]
92 |

copy g returns a generator g' that has the same state as g.

93 | 94 |
95 |
F
float [PRNG.PURE]
float [PRNG.STATE]
101 |

float g x returns a floating-point number evenly distributed 102 | between 0.0 and x.

103 | 104 |
105 |
I
int [PRNG.PURE]
int [PRNG.STATE]
111 |

int g n returns an integer evenly distributed between 0 included 112 | and n excluded.

113 | 114 |
115 |
int32 [PRNG.PURE]
int32 [PRNG.STATE]
120 |

int32 g n returns a 32-bit integer evenly distributed between 121 | 0 included and n excluded.

122 | 123 |
124 |
int64 [PRNG.PURE]
int64 [PRNG.STATE]
129 |

int64 g n returns a 64-bit integer evenly distributed between 130 | 0 included and n excluded.

131 | 132 |
133 |
M
make [PRNG.PURE]
make [PRNG.STATE]
139 |

Initialize a generator from the given seed.

140 | 141 |
142 |
make_self_init [PRNG.PURE]
make_self_init [PRNG.STATE]
147 |

Initialize a generator from a random seed obtained from the 148 | operating system.

149 | 150 |
151 |
N
nativebits [PRNG.PURE]
nativebits [PRNG.STATE]
157 |

nativebits g returns a platform-native integer (32 or 64 158 | bits) evenly distributed between and 159 | .

160 | 161 |
162 |
nativeint [PRNG.PURE]
nativeint [PRNG.STATE]
167 |

nativeint g n returns a platform-native integer between 168 | 0 included and n included.

169 | 170 |
171 |
R
remake [PRNG.STATE]
175 |

remake g a reinitializes the generator g with fresh seed data 176 | from array a.

177 | 178 |
179 |
reseed [PRNG.STATE]
182 |

reseed g s reinitializes the generator g with fresh seed data 183 | from string s.

184 | 185 |
186 |
S
seed [PRNG.PURE]
seed [PRNG.STATE]
192 |

Initialize a generator from the given seed.

193 | 194 |
195 |
split [PRNG.PURE]
split [PRNG.STATE]
200 |

split g returns a fresh generator g' that is statistically 201 | independent from the current generator g.

202 | 203 |
204 |
U
uniform [PRNG.PURE]
uniform [PRNG.STATE]
210 |

Return a floating-point number evenly distributed between 0.0 and 1.0.

211 | 212 |
213 |
215 | 216 | 217 | -------------------------------------------------------------------------------- /PRNG.mli: -------------------------------------------------------------------------------- 1 | (***********************************************************************) 2 | (* *) 3 | (* The PRINGO library *) 4 | (* *) 5 | (* Xavier Leroy, projet Gallium, INRIA Paris *) 6 | (* *) 7 | (* Copyright 2017 Institut National de Recherche en Informatique et *) 8 | (* en Automatique. All rights reserved. This file is distributed *) 9 | (* under the terms of the GNU Library General Public License v2, *) 10 | (* with the special exception on linking described in file LICENSE. *) 11 | (* *) 12 | (***********************************************************************) 13 | 14 | (** This library provides pseudo-random number generators (PRNGs) comparable 15 | to that of the Random module from OCaml's standard library, but with 16 | two extensions: 17 | - Generators are "splittable": they support a [split] operation that 18 | returns a new generator that is statistically independent from the 19 | current generator. Both generators can be used in parallel, and can 20 | be further splitted, without introducing statistical bias. This 21 | splitting operation is particularly useful to implement the lazy 22 | generation of pseudo-random infinite data structures such as 23 | functions or streams. 24 | - In addition to a stateful, imperative interface resembling that of 25 | the [Random.State] standard library module, another, purely 26 | functional interface is provided. In the functional interface, the 27 | current state of the PRNG appears as parameter but also as result of 28 | the number generation functions. This interface can be used 29 | directly within a state monad. 30 | *) 31 | 32 | (** {2 The stateful interface} *) 33 | 34 | module type STATE = sig 35 | type t 36 | (** The type of generators *) 37 | 38 | (** {3 Creating and seeding PRNGs} *) 39 | 40 | val seed: string -> t 41 | (** Initialize a generator from the given seed. The seed is given 42 | as a character string. The length and randomness of the seed 43 | limit the total entropy of the generator. For example, 64 44 | bits of entropy can be obtained by giving a seed consisting of 45 | 8 cryptographically-strong random characters, as obtained 46 | e.g. by reading [/dev/random]. *) 47 | val make: int array -> t 48 | (** Initialize a generator from the given seed. The seed is given 49 | as an array of integers. *) 50 | val make_self_init: unit -> t 51 | (** Initialize a generator from a random seed obtained from the 52 | operating system. Tries hard to provide at least 53 | 64 bits of entropy. With high probability, successive calls 54 | to [make_self_init] return different PRNGs with different seeds. *) 55 | 56 | (** {3 Generating pseudo-random data} *) 57 | 58 | val bool: t -> bool 59 | val bit: t -> bool 60 | (** Return a Boolean value in [false,true] with 0.5 probability each. *) 61 | 62 | val uniform: t -> float 63 | (** Return a floating-point number evenly distributed between 0.0 and 1.0. 64 | 0.0 and 1.0 are never returned. 65 | The result is of the form [n * 2{^-53}], where [n] is a random integer 66 | in [(0, 2{^53})]. *) 67 | 68 | val float: t -> float -> float 69 | (** [float g x] returns a floating-point number evenly distributed 70 | between 0.0 and [x]. If [x] is negative, negative numbers 71 | between [x] and 0.0 are returned. Implemented as [uniform g *. x]. 72 | Consequently, the values [0.0] and [x] can be returned 73 | (as a result of floating-point rounding), but not if [x] is 74 | [1.0], since [float g 1.0] behaves exactly like [uniform g]. *) 75 | 76 | val byte: t -> int 77 | val bits8: t -> int 78 | (** Return an 8-bit integer evenly distributed between 0 and 255. *) 79 | 80 | val bits: t -> int 81 | val bits30: t -> int 82 | (** Return a 30-bit integer evenly distributed between 0 and 2{^30}-1 83 | (that is, 1073741823, or 0x3FFFFFFF). *) 84 | 85 | val int: t -> int -> int 86 | (** [int g n] returns an integer evenly distributed between 0 included 87 | and [n] excluded. Hence there are [n] possible return values 88 | with probability [1/n] each. [n] must be greater than 0 and 89 | no greater than 2{^30}-1. *) 90 | 91 | val bits32: t -> int32 92 | (** Return a 32-bit integer evenly distributed between 93 | {Int32.min_int} and {Int32.max_int}. *) 94 | 95 | val int32: t -> int32 -> int32 96 | (** [int32 g n] returns a 32-bit integer evenly distributed between 97 | 0 included and [n] excluded. [n] must be strictly positive. 98 | 99 | Note that [int32 Int32.max_int] produces numbers between 0 and 100 | [Int32.max_int] excluded. To produce numbers between 0 and 101 | [Int32.max_int] included, use 102 | [Int32.logand (bits32 g) Int32.max_int]. *) 103 | 104 | val bits64: t -> int64 105 | (** Return a 64-bit integer evenly distributed between 106 | {Int64.min_int} and {Int64.max_int}. *) 107 | 108 | val int64: t -> int64 -> int64 109 | (** [int64 g n] returns a 64-bit integer evenly distributed between 110 | 0 included and [n] excluded. [n] must be strictly positive. 111 | 112 | Note that [int64 Int64.max_int] produces numbers between 0 and 113 | [Int64.max_int] excluded. To produce numbers between 0 and 114 | [Int64.max_int] included, use 115 | [Int64.logand (bits64 g) Int64.max_int]. *) 116 | 117 | val nativebits: t -> nativeint 118 | (** [nativebits g] returns a platform-native integer (32 or 64 119 | bits) evenly distributed between {Nativeint.min_int} and 120 | {Nativeint.max_int}. *) 121 | 122 | val nativeint: t -> nativeint -> nativeint 123 | (** [nativeint g n] returns a platform-native integer between 124 | 0 included and [n] included. [n] must be strictly positive. *) 125 | 126 | val char: t -> char 127 | (** Return a character evenly distributed among ['\000' ... '\255']. *) 128 | 129 | val bytes: t -> bytes -> int -> int -> unit 130 | (** [bytes g b ofs len] produces [len] bytes of pseudo-random data 131 | and stores them in byte sequence [b] at offsets [ofs] to [ofs+len-1]. 132 | 133 | Raise [Invalid_argument] if [len < 0] or [ofs] and [len] do not 134 | designate a valid range of [b]. *) 135 | 136 | (** {3 Splitting and copying} *) 137 | 138 | val split: t -> t 139 | (** [split g] returns a fresh generator [g'] that is statistically 140 | independent from the current generator [g]. The two generators 141 | [g] and [g'] can be used in parallel and will produce independent 142 | pseudo-random data. Each generator [g] and [g'] can be splitted 143 | again in the future. *) 144 | 145 | val copy: t -> t 146 | (** [copy g] returns a generator [g'] that has the same state as [g]. 147 | The two generators [g] and [g'] produce the same data. *) 148 | 149 | (** {3 Reseeding} *) 150 | 151 | val reseed: t -> string -> unit 152 | (** [reseed g s] reinitializes the generator [g] with fresh seed data 153 | from string [s]. This is like [seed s] except that the existing 154 | generator [g] is seeded, instead of a new generator being returned. 155 | It is good practice to reseed a PRNG after a certain quantity 156 | of pseudo-random data has been produced from it: typically 157 | 2{^32} numbers for the {!PRNG.Splitmix} generator and 158 | 2{^64} bytes for the {!PRNG.Chacha} generator. *) 159 | val remake: t -> int array -> unit 160 | (** [remake g a] reinitializes the generator [g] with fresh seed data 161 | from array [a]. This is like [reseed] except that the seed is 162 | given as an array of integers. *) 163 | 164 | end 165 | 166 | (** {2 The purely-functional, monadic interface} *) 167 | 168 | (** In this alternate interface, number-generating functions do not 169 | update the current state of the generator in-place. Instead, they 170 | return the updated generator as a second result. It is the 171 | programmer's responsibility to correctly thread the generators 172 | through the program, typically by using a state monad. 173 | 174 | All operations of the [STATE] interface are provided except 175 | [bytes] (too imperative) and [copy], [reseed] and [remake] (pointless). *) 176 | 177 | module type PURE = sig 178 | type t 179 | 180 | (** {3 Creating and seeding PRNGs} *) 181 | 182 | val seed: string -> t 183 | val make: int array -> t 184 | val make_self_init: unit -> t 185 | 186 | (** {3 Generating pseudo-random data} *) 187 | 188 | val bool: t -> bool * t 189 | val bit: t -> bool * t 190 | 191 | val uniform: t -> float * t 192 | val float: float -> t -> float * t 193 | 194 | val byte: t -> int * t 195 | val bits8: t -> int * t 196 | val int: int -> t -> int * t 197 | 198 | val bits: t -> int * t 199 | val bits30: t -> int * t 200 | 201 | val bits32: t -> int32 * t 202 | val int32: int32 -> t -> int32 * t 203 | 204 | val bits64: t -> int64 * t 205 | val int64: int64 -> t -> int64 * t 206 | 207 | val nativebits: t -> nativeint * t 208 | val nativeint: nativeint -> t -> nativeint * t 209 | 210 | val char: t -> char * t 211 | 212 | (** {3 Splitting} *) 213 | 214 | val split: t -> t * t 215 | end 216 | 217 | (** {2 The Splitmix implementation} *) 218 | 219 | module Splitmix: sig 220 | module State: STATE 221 | module Pure: PURE 222 | end 223 | 224 | (** This is an implementation of the [STATE] and [PURE] interfaces 225 | based on the Splitmix design by Guy L. Steele Jr., Doug Lea, and 226 | Christine H. Flood. 227 | 228 | For seeding, 64 bits of entropy is recommended. Seeds of 8 229 | characters or less are used as a 64-bit integer. Longer seeds 230 | are hashed using {!Digest.string} before being used. 231 | 232 | Reseeding is recommended after 2{^32} numbers have been generated. *) 233 | 234 | (** {2 The Chacha-20 implementation} *) 235 | 236 | module Chacha: sig 237 | module State: STATE 238 | module Pure: PURE 239 | end 240 | 241 | (** This is an implementation of the [STATE] and [PURE] interfaces 242 | based on the Chacha 20 stream cipher by D. J. Bernstein. 243 | 244 | For seeding, 128 bits of entropy is recommended. Seeds of up 245 | to 32 characters are used as keys to the Chacha 20 cipher. 246 | Characters beyond the first 32 are ignored. 247 | 248 | Reseeding is recommended after 2{^64} numbers have been generated. *) 249 | 250 | (** {2 The LXM implementation} *) 251 | 252 | module LXM: sig 253 | module State: STATE 254 | module Pure: PURE 255 | end 256 | 257 | (** This is an implementation of the [STATE] and [PURE] interfaces 258 | based on the LXM design by Guy L. Steele Jr, and Sebastiano Vigna. 259 | We use the L64X128 variant from Fig. 1 of their OOPSLA 2021 paper. 260 | 261 | For seeding, 128 bits of entropy is recommended. The last 32 bytes 262 | of the seed are used to initialize the PRNG state. 263 | 264 | This PRNG has a large internal state (192 bits) and a period of 265 | 2{^192} - 2{^64}. Therefore, reseeding should not be necessary 266 | in practice. *) 267 | -------------------------------------------------------------------------------- /stubs.c: -------------------------------------------------------------------------------- 1 | /***********************************************************************/ 2 | /* */ 3 | /* The PRINGO library */ 4 | /* */ 5 | /* Xavier Leroy, projet Gallium, INRIA Paris */ 6 | /* */ 7 | /* Copyright 2017 Institut National de Recherche en Informatique et */ 8 | /* en Automatique. All rights reserved. This file is distributed */ 9 | /* under the terms of the GNU Library General Public License v2, */ 10 | /* with the special exception on linking described in file LICENSE. */ 11 | /* */ 12 | /***********************************************************************/ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /* Mixing functions for Splitmix */ 22 | 23 | CAMLprim uint64_t pringo_mix64_unboxed(uint64_t z) 24 | { 25 | z = (z ^ (z >> 33)) * 0xff51afd7ed558ccdULL; 26 | z = (z ^ (z >> 33)) * 0xc4ceb9fe1a85ec53ULL; 27 | return z ^ (z >> 33); 28 | } 29 | 30 | CAMLprim value pringo_mix64(value vz) 31 | { 32 | return caml_copy_int64(pringo_mix64_unboxed(Int64_val(vz))); 33 | } 34 | 35 | CAMLprim uint32_t pringo_mix32_unboxed(uint64_t z) 36 | { 37 | z = (z ^ (z >> 33)) * 0xff51afd7ed558ccdULL; 38 | z = (z ^ (z >> 33)) * 0xc4ceb9fe1a85ec53ULL; 39 | return (uint32_t)(z >> 32); 40 | } 41 | 42 | CAMLprim value pringo_mix32(value vz) 43 | { 44 | return caml_copy_int32(pringo_mix32_unboxed(Int64_val(vz))); 45 | } 46 | 47 | CAMLprim value pringo_mix30_unboxed(uint64_t z) 48 | { 49 | z = (z ^ (z >> 33)) * 0xff51afd7ed558ccdULL; 50 | z = (z ^ (z >> 33)) * 0xc4ceb9fe1a85ec53ULL; 51 | return Val_long((intnat)(z >> 34)); 52 | } 53 | 54 | CAMLprim value pringo_mix30(value vz) 55 | { 56 | return pringo_mix30_unboxed(Int64_val(vz)); 57 | } 58 | 59 | static inline uint64_t mix64variant13(uint64_t z) 60 | { 61 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; 62 | z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; 63 | return z ^ (z >> 31); 64 | } 65 | 66 | static inline int popcount64(uint64_t x) 67 | { 68 | x = x - ((x >> 1) & 0x5555555555555555ULL); 69 | x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); 70 | x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL; 71 | return (x * 0x0101010101010101ULL) >> 56; 72 | } 73 | 74 | CAMLprim uint64_t pringo_mixGamma_unboxed(uint64_t z) 75 | { 76 | z = mix64variant13(z) | 1ULL; 77 | if (popcount64(z ^ (z >> 1)) < 24) z ^= 0xaaaaaaaaaaaaaaaaULL; 78 | return z; 79 | } 80 | 81 | CAMLprim value pringo_mixGamma(value vz) 82 | { 83 | return caml_copy_int64(pringo_mixGamma_unboxed(Int64_val(vz))); 84 | } 85 | 86 | /* Primitives for the Chacha20 cipher */ 87 | 88 | struct chacha20_key { 89 | uint32_t key[12]; 90 | }; 91 | 92 | struct chacha20_state { 93 | uint8_t output[64]; 94 | uint32_t ctr[2]; 95 | uint32_t nonce[2]; 96 | }; 97 | 98 | static void chacha20_init_key(struct chacha20_key * k, 99 | uint8_t * key, size_t key_len); 100 | static void chacha20_block(const struct chacha20_key * key, 101 | struct chacha20_state * st); 102 | 103 | #ifndef Data_abstract_val 104 | #define Data_abstract_val Op_val 105 | #endif 106 | 107 | #define Key_val(v) ((struct chacha20_key *) Data_abstract_val(v)) 108 | #define State_val(v) ((struct chacha20_state *) String_val(v)) 109 | #define Wsizeof(ty) ((sizeof(ty) + sizeof(value) - 1) / sizeof(value)) 110 | 111 | static inline void U32TO8_LITTLE(uint8_t * dst, uint32_t val) 112 | { 113 | #ifdef ARCH_BIG_ENDIAN 114 | dst[0] = val; 115 | dst[1] = val >> 8; 116 | dst[2] = val >> 16; 117 | dst[3] = val >> 24; 118 | #else 119 | *((uint32_t *) dst) = val; 120 | #endif 121 | } 122 | 123 | static inline uint32_t U8TO32_LITTLE(const uint8_t * src) 124 | { 125 | #ifdef ARCH_BIG_ENDIAN 126 | return (uint32_t) src[0] 127 | | ((uint32_t) src[1] << 8) 128 | | ((uint32_t) src[2] << 16) 129 | | ((uint32_t) src[3] << 24); 130 | #else 131 | return *((const uint32_t *) src); 132 | #endif 133 | } 134 | 135 | CAMLprim value pringo_chacha_make_key(value vkey) 136 | { 137 | mlsize_t keylen; 138 | uint8_t keybytes[32]; 139 | value res; 140 | 141 | keylen = caml_string_length(vkey); 142 | if (keylen > 32) keylen = 32; 143 | memcpy(keybytes, String_val(vkey), keylen); 144 | memset(keybytes + keylen, 0, 32 - keylen); 145 | res = caml_alloc_small(Wsizeof(struct chacha20_key), Abstract_tag); 146 | chacha20_init_key(Key_val(res), keybytes, keylen <= 16 ? 16 : 32); 147 | memset(keybytes, 0, 32); /* just in case key is sensitive */ 148 | return res; 149 | } 150 | 151 | CAMLprim value pringo_chacha_make_state(value vstate) 152 | { 153 | CAMLparam1(vstate); 154 | value res = caml_alloc_string(sizeof(struct chacha20_state)); 155 | State_val(res)->ctr[0] = U8TO32_LITTLE(&Byte_u(vstate, 0)); 156 | State_val(res)->ctr[1] = U8TO32_LITTLE(&Byte_u(vstate, 4)); 157 | State_val(res)->nonce[0] = U8TO32_LITTLE(&Byte_u(vstate, 8)); 158 | State_val(res)->nonce[1] = U8TO32_LITTLE(&Byte_u(vstate, 12)); 159 | CAMLreturn(res); 160 | } 161 | 162 | CAMLprim value pringo_chacha_transform(value vkey, value vstate) 163 | { 164 | chacha20_block(Key_val(vkey), State_val(vstate)); 165 | return Val_unit; 166 | } 167 | 168 | /* Based on D. J. Bernstein's chacha-regs.c version 200801118, 169 | https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/regs/chacha.c 170 | The initial code is in the public domain */ 171 | 172 | #define ROTATE(v,c) ((v) << (c) | (v) >> (32 - (c))) 173 | #define XOR(v,w) ((v) ^ (w)) 174 | #define PLUS(v,w) ((v) + (w)) 175 | #define PLUSONE(v) ((v) + 1) 176 | 177 | #define QUARTERROUND(a,b,c,d) \ 178 | a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ 179 | c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ 180 | a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ 181 | c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); 182 | 183 | static void chacha20_block(const struct chacha20_key * k, 184 | struct chacha20_state * s) 185 | { 186 | uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; 187 | int i; 188 | 189 | x0 = k->key[0]; 190 | x1 = k->key[1]; 191 | x2 = k->key[2]; 192 | x3 = k->key[3]; 193 | x4 = k->key[4]; 194 | x5 = k->key[5]; 195 | x6 = k->key[6]; 196 | x7 = k->key[7]; 197 | x8 = k->key[8]; 198 | x9 = k->key[9]; 199 | x10 = k->key[10]; 200 | x11 = k->key[11]; 201 | x12 = s->ctr[0]; 202 | x13 = s->ctr[1]; 203 | x14 = s->nonce[0]; 204 | x15 = s->nonce[1]; 205 | for (i = 8; i > 0; i -= 2) { 206 | QUARTERROUND( x0, x4, x8,x12) 207 | QUARTERROUND( x1, x5, x9,x13) 208 | QUARTERROUND( x2, x6,x10,x14) 209 | QUARTERROUND( x3, x7,x11,x15) 210 | QUARTERROUND( x0, x5,x10,x15) 211 | QUARTERROUND( x1, x6,x11,x12) 212 | QUARTERROUND( x2, x7, x8,x13) 213 | QUARTERROUND( x3, x4, x9,x14) 214 | } 215 | x0 = PLUS(x0,k->key[0]); 216 | x1 = PLUS(x1,k->key[1]); 217 | x2 = PLUS(x2,k->key[2]); 218 | x3 = PLUS(x3,k->key[3]); 219 | x4 = PLUS(x4,k->key[4]); 220 | x5 = PLUS(x5,k->key[5]); 221 | x6 = PLUS(x6,k->key[6]); 222 | x7 = PLUS(x7,k->key[7]); 223 | x8 = PLUS(x8,k->key[8]); 224 | x9 = PLUS(x9,k->key[9]); 225 | x10 = PLUS(x10,k->key[10]); 226 | x11 = PLUS(x11,k->key[11]); 227 | x12 = PLUS(x12,s->ctr[0]); 228 | x13 = PLUS(x13,s->ctr[1]); 229 | x14 = PLUS(x14,s->nonce[0]); 230 | x15 = PLUS(x15,s->nonce[1]); 231 | U32TO8_LITTLE(s->output + 0,x0); 232 | U32TO8_LITTLE(s->output + 4,x1); 233 | U32TO8_LITTLE(s->output + 8,x2); 234 | U32TO8_LITTLE(s->output + 12,x3); 235 | U32TO8_LITTLE(s->output + 16,x4); 236 | U32TO8_LITTLE(s->output + 20,x5); 237 | U32TO8_LITTLE(s->output + 24,x6); 238 | U32TO8_LITTLE(s->output + 28,x7); 239 | U32TO8_LITTLE(s->output + 32,x8); 240 | U32TO8_LITTLE(s->output + 36,x9); 241 | U32TO8_LITTLE(s->output + 40,x10); 242 | U32TO8_LITTLE(s->output + 44,x11); 243 | U32TO8_LITTLE(s->output + 48,x12); 244 | U32TO8_LITTLE(s->output + 52,x13); 245 | U32TO8_LITTLE(s->output + 56,x14); 246 | U32TO8_LITTLE(s->output + 60,x15); 247 | /* Increment the 64-bit counter and, on overflow, the 64-bit nonce */ 248 | /* (Incrementing the nonce is not standard but a reasonable default.) */ 249 | if (++ s->ctr[0] == 0) 250 | if (++ s->ctr[1] == 0) 251 | if (++ s->nonce[0] == 0) 252 | ++ s->nonce[1]; 253 | } 254 | 255 | static void chacha20_init_key(struct chacha20_key * k, 256 | uint8_t * key, size_t key_len) 257 | { 258 | const uint8_t *constants = 259 | (uint8_t *) (key_len == 32 ? "expand 32-byte k" : "expand 16-byte k"); 260 | CAMLassert (key_length == 16 || key_length == 32); 261 | k->key[0] = U8TO32_LITTLE(constants + 0); 262 | k->key[1] = U8TO32_LITTLE(constants + 4); 263 | k->key[2] = U8TO32_LITTLE(constants + 8); 264 | k->key[3] = U8TO32_LITTLE(constants + 12); 265 | k->key[4] = U8TO32_LITTLE(key + 0); 266 | k->key[5] = U8TO32_LITTLE(key + 4); 267 | k->key[6] = U8TO32_LITTLE(key + 8); 268 | k->key[7] = U8TO32_LITTLE(key + 12); 269 | if (key_len == 32) key += 16; 270 | k->key[8] = U8TO32_LITTLE(key + 0); 271 | k->key[9] = U8TO32_LITTLE(key + 4); 272 | k->key[10] = U8TO32_LITTLE(key + 8); 273 | k->key[11] = U8TO32_LITTLE(key + 12); 274 | } 275 | 276 | /* Primitives for LXM. We use the L64X128 variant */ 277 | 278 | static const uint64_t M = 0xd1342543de82ef95; 279 | 280 | struct LXM_state { 281 | uint64_t a; /* per-instance additive parameter (odd) */ 282 | uint64_t s; /* state of the LCG subgenerator */ 283 | uint64_t x[2]; /* state of the XBG subgenerator (not 0) */ 284 | }; 285 | 286 | #define LXM_val(v) ((struct LXM_state *) Data_abstract_val(v)) 287 | 288 | static inline uint64_t rotl(const uint64_t x, int k) { 289 | return (x << k) | (x >> (64 - k)); 290 | } 291 | 292 | CAMLprim uint64_t pringo_LXM_next_unboxed(value v) 293 | { 294 | uint64_t z, q0, q1; 295 | struct LXM_state * st = LXM_val(v); 296 | 297 | /* Combining operation */ 298 | z = st->s + st->x[0]; 299 | /* Mixing function */ 300 | z = (z ^ (z >> 32)) * 0xdaba0b6eb09322e3; 301 | z = (z ^ (z >> 32)) * 0xdaba0b6eb09322e3; 302 | z = (z ^ (z >> 32)); 303 | /* LCG update */ 304 | st->s = st->s * M + st->a; 305 | /* XBG update */ 306 | q0 = st->x[0]; q1 = st->x[1]; 307 | q1 ^= q0; 308 | q0 = rotl(q0, 24); 309 | q0 = q0 ^ q1 ^ (q1 << 16); 310 | q1 = rotl(q1, 37); 311 | st->x[0] = q0; st->x[1] = q1; 312 | /* Return result */ 313 | return z; 314 | } 315 | 316 | CAMLprim value pringo_LXM_next(value v) 317 | { 318 | return caml_copy_int64(pringo_LXM_next_unboxed(v)); 319 | } 320 | 321 | CAMLprim value pringo_LXM_copy(value v) 322 | { 323 | CAMLparam1(v); 324 | value res = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag); 325 | memcpy(LXM_val(res), LXM_val(v), sizeof(struct LXM_state)); 326 | CAMLreturn(res); 327 | } 328 | 329 | CAMLprim value pringo_LXM_assign(value vdst, value vsrc) 330 | { 331 | memcpy(LXM_val(vdst), LXM_val(vsrc), sizeof(struct LXM_state)); 332 | return Val_unit; 333 | } 334 | 335 | CAMLprim value pringo_LXM_init_unboxed(uint64_t i1, uint64_t i2, 336 | uint64_t i3, uint64_t i4) 337 | { 338 | value v = caml_alloc_small(Wsizeof(struct LXM_state), Abstract_tag); 339 | struct LXM_state * st = LXM_val(v); 340 | st->a = i1 | 1; /* must be odd */ 341 | st->x[0] = i2 != 0 ? i2 : 1; /* must be nonzero */ 342 | st->x[1] = i3 != 0 ? i3 : 2; /* must be nonzero */ 343 | st->s = i4; 344 | return v; 345 | } 346 | 347 | CAMLprim value pringo_LXM_init(value i1, value i2, value i3, value i4) 348 | { 349 | return pringo_LXM_init_unboxed(Int64_val(i1), Int64_val(i2), 350 | Int64_val(i3), Int64_val(i4)); 351 | } 352 | 353 | CAMLprim value pringo_LXM_seed(value s) 354 | { 355 | uint64_t d[4] = {0, 0, 0, 0}; 356 | mlsize_t i, len; 357 | for (i = 0, len = caml_string_length(s); i < len; i++) { 358 | d[i % 4] = (d[i % 4] << 8) | Byte_u(s, i); 359 | } 360 | return pringo_LXM_init_unboxed(d[0], d[1], d[2], d[3]); 361 | } 362 | 363 | CAMLprim value pringo_LXM_make(value a) 364 | { 365 | const uint64_t mix = 6364136223846793005; 366 | /* Multiplier taken from the MMIX LCG, Knoth TAOCP vol 2, 1998 edition */ 367 | uint64_t d[4] = {0, 0, 0, 0}; 368 | mlsize_t i, len; 369 | for (i = 0, len = Wosize_val(a); i < len; i++) { 370 | d[i % 4] = d[i % 4] * mix + Long_val(Field(a, i)); 371 | } 372 | return pringo_LXM_init_unboxed(d[0], d[1], d[2], d[3]); 373 | } 374 | 375 | -------------------------------------------------------------------------------- /docs/PRNG.STATE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PRNG.STATE 19 | 20 | 21 | 24 |

Module type PRNG.STATE

25 | 26 |
module type STATE = sig .. end

27 | 28 |
type t 
29 |
30 |
31 |

The type of generators

32 |
33 |
34 | 35 |

Creating and seeding PRNGs

36 |
val seed : string -> t
37 |
38 |

Initialize a generator from the given seed. The seed is given 39 | as a character string. The length and randomness of the seed 40 | limit the total entropy of the generator. For example, 64 41 | bits of entropy can be obtained by giving a seed consisting of 42 | 8 cryptographically-strong random characters (as obtained 43 | e.g. by reading /dev/random.

44 |
45 |
46 | 47 |
val make : int array -> t
48 |
49 |

Initialize a generator from the given seed. The seed is given 50 | as an array of integers.

51 |
52 |
53 | 54 |
val make_self_init : unit -> t
55 |
56 |

Initialize a generator from a random seed obtained from the 57 | operating system. Tries hard to provide at least 58 | 64 bits of entropy. With high probability, successive calls 59 | to make_self_init return different PRNGs with different seeds.

60 |
61 |
62 |

Generating pseudo-random data

63 |
val bool : t -> bool
64 |
val bit : t -> bool
65 |
66 |

Return a Boolean value in false,true with 0.5 probability each.

67 |
68 |
69 | 70 |
val uniform : t -> float
71 |
72 |

Return a floating-point number evenly distributed between 0.0 and 1.0. 73 | 0.0 and 1.0 are never returned. 74 | The result is of the form n * 2{^-53}, where n is a random integer 75 | in (0, 2{^53}).

76 |
77 |
78 | 79 |
val float : t -> float -> float
80 |
81 |

float g x returns a floating-point number evenly distributed 82 | between 0.0 and x. If x is negative, negative numbers 83 | between x and 0.0 are returned. Implemented as uniform g *. x. 84 | Consequently, the values 0.0 and x can be returned 85 | (as a result of floating-point rounding), but not if x is 86 | 1.0, since float g 1.0 behaves exactly like uniform g.

87 |
88 |
89 | 90 |
val byte : t -> int
91 |
val bits8 : t -> int
92 |
93 |

Return an 8-bit integer evenly distributed between 0 and 255.

94 |
95 |
96 | 97 |
val bits : t -> int
98 |
val bits30 : t -> int
99 |
100 |

Return a 30-bit integer evenly distributed between 0 and 230-1 101 | (that is, 1073741823, or 0x3FFFFFFF).

102 |
103 |
104 | 105 |
val int : t -> int -> int
106 |
107 |

int g n returns an integer evenly distributed between 0 included 108 | and n excluded. Hence there are n possible return values 109 | with probability 1/n each. n must be greater than 0 and 110 | no greater than 230-1.

111 |
112 |
113 | 114 |
val bits32 : t -> int32
115 |
116 |

Return a 32-bit integer evenly distributed between 117 | and .

118 |
119 |
120 | 121 |
val int32 : t -> int32 -> int32
122 |
123 |

int32 g n returns a 32-bit integer evenly distributed between 124 | 0 included and n excluded. n must be strictly positive.

125 | 126 |

Note that int32 Int32.max_int produces numbers between 0 and 127 | Int32.max_int excluded. To produce numbers between 0 and 128 | Int32.max_int included, use 129 | Int32.logand (bits32 g) Int32.max_int.

130 |
131 |
132 | 133 |
val bits64 : t -> int64
134 |
135 |

Return a 64-bit integer evenly distributed between 136 | and .

137 |
138 |
139 | 140 |
val int64 : t -> int64 -> int64
141 |
142 |

int64 g n returns a 64-bit integer evenly distributed between 143 | 0 included and n excluded. n must be strictly positive.

144 | 145 |

Note that int64 Int64.max_int produces numbers between 0 and 146 | Int64.max_int excluded. To produce numbers between 0 and 147 | Int64.max_int included, use 148 | Int64.logand (bits64 g) Int64.max_int.

149 |
150 |
151 | 152 |
val nativebits : t -> nativeint
153 |
154 |

nativebits g returns a platform-native integer (32 or 64 155 | bits) evenly distributed between and 156 | .

157 |
158 |
159 | 160 |
val nativeint : t -> nativeint -> nativeint
161 |
162 |

nativeint g n returns a platform-native integer between 163 | 0 included and n included. n must be strictly positive.

164 |
165 |
166 | 167 |
val char : t -> char
168 |
169 |

Return a character evenly distributed among '\000' ... '\255'.

170 |
171 |
172 | 173 |
val bytes : t -> bytes -> int -> int -> unit
174 |
175 |

bytes g b ofs len produces len bytes of pseudo-random data 176 | and stores them in byte sequence b at offsets ofs to ofs+len-1.

177 | 178 |

Raise Invalid_argument if len < 0 or ofs and len do not 179 | designate a valid range of b.

180 |
181 |
182 |

Splitting and copying

183 |
val split : t -> t
184 |
185 |

split g returns a fresh generator g' that is statistically 186 | independent from the current generator g. The two generators 187 | g and g' can be used in parallel and will produce independent 188 | pseudo-random data. Each generator g and g' can be splitted 189 | again in the future.

190 |
191 |
192 | 193 |
val copy : t -> t
194 |
195 |

copy g returns a generator g' that has the same state as g. 196 | The two generators g and g' produce the same data.

197 |
198 |
199 |

Reseeding

200 |
val reseed : t -> string -> unit
201 |
202 |

reseed g s reinitializes the generator g with fresh seed data 203 | from string s. This is like seed s except that the existing 204 | generator g is seeded, instead of a new generator being returned. 205 | It is good practice to reseed a PRNG after a certain quantity 206 | of pseudo-random data has been produced from it: typically 207 | 232 numbers for the PRNG.Splitmix generator and 208 | 264 bytes for then PRNG.Chacha generator.

209 |
210 |
211 | 212 |
val remake : t -> int array -> unit
213 |
214 |

remake g a reinitializes the generator g with fresh seed data 215 | from array a. This is like reseed except that the seed is 216 | given as an array of integers.

217 |
218 |
219 | 220 | -------------------------------------------------------------------------------- /docs/PRNG.Chacha.State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PRNG.Chacha.State 19 | 20 | 21 | 24 |

Module PRNG.Chacha.State

25 | 26 |
module State: PRNG.STATE 

27 | 28 |
type t 
29 |
30 |
31 |

The type of generators

32 |
33 |
34 | 35 |

Creating and seeding PRNGs

36 |
val seed : string -> t
37 |
38 |

Initialize a generator from the given seed. The seed is given 39 | as a character string. The length and randomness of the seed 40 | limit the total entropy of the generator. For example, 64 41 | bits of entropy can be obtained by giving a seed consisting of 42 | 8 cryptographically-strong random characters (as obtained 43 | e.g. by reading /dev/random.

44 |
45 |
46 | 47 |
val make : int array -> t
48 |
49 |

Initialize a generator from the given seed. The seed is given 50 | as an array of integers.

51 |
52 |
53 | 54 |
val make_self_init : unit -> t
55 |
56 |

Initialize a generator from a random seed obtained from the 57 | operating system. Tries hard to provide at least 58 | 64 bits of entropy. With high probability, successive calls 59 | to make_self_init return different PRNGs with different seeds.

60 |
61 |
62 |

Generating pseudo-random data

63 |
val bool : t -> bool
64 |
val bit : t -> bool
65 |
66 |

Return a Boolean value in false,true with 0.5 probability each.

67 |
68 |
69 | 70 |
val uniform : t -> float
71 |
72 |

Return a floating-point number evenly distributed between 0.0 and 1.0. 73 | 0.0 and 1.0 are never returned. 74 | The result is of the form n * 2{^-53}, where n is a random integer 75 | in (0, 2{^53}).

76 |
77 |
78 | 79 |
val float : t -> float -> float
80 |
81 |

float g x returns a floating-point number evenly distributed 82 | between 0.0 and x. If x is negative, negative numbers 83 | between x and 0.0 are returned. Implemented as uniform g *. x. 84 | Consequently, the values 0.0 and x can be returned 85 | (as a result of floating-point rounding), but not if x is 86 | 1.0, since float g 1.0 behaves exactly like uniform g.

87 |
88 |
89 | 90 |
val byte : t -> int
91 |
val bits8 : t -> int
92 |
93 |

Return an 8-bit integer evenly distributed between 0 and 255.

94 |
95 |
96 | 97 |
val bits : t -> int
98 |
val bits30 : t -> int
99 |
100 |

Return a 30-bit integer evenly distributed between 0 and 230-1 101 | (that is, 1073741823, or 0x3FFFFFFF).

102 |
103 |
104 | 105 |
val int : t -> int -> int
106 |
107 |

int g n returns an integer evenly distributed between 0 included 108 | and n excluded. Hence there are n possible return values 109 | with probability 1/n each. n must be greater than 0 and 110 | no greater than 230-1.

111 |
112 |
113 | 114 |
val bits32 : t -> int32
115 |
116 |

Return a 32-bit integer evenly distributed between 117 | and .

118 |
119 |
120 | 121 |
val int32 : t -> int32 -> int32
122 |
123 |

int32 g n returns a 32-bit integer evenly distributed between 124 | 0 included and n excluded. n must be strictly positive.

125 | 126 |

Note that int32 Int32.max_int produces numbers between 0 and 127 | Int32.max_int excluded. To produce numbers between 0 and 128 | Int32.max_int included, use 129 | Int32.logand (bits32 g) Int32.max_int.

130 |
131 |
132 | 133 |
val bits64 : t -> int64
134 |
135 |

Return a 64-bit integer evenly distributed between 136 | and .

137 |
138 |
139 | 140 |
val int64 : t -> int64 -> int64
141 |
142 |

int64 g n returns a 64-bit integer evenly distributed between 143 | 0 included and n excluded. n must be strictly positive.

144 | 145 |

Note that int64 Int64.max_int produces numbers between 0 and 146 | Int64.max_int excluded. To produce numbers between 0 and 147 | Int64.max_int included, use 148 | Int64.logand (bits64 g) Int64.max_int.

149 |
150 |
151 | 152 |
val nativebits : t -> nativeint
153 |
154 |

nativebits g returns a platform-native integer (32 or 64 155 | bits) evenly distributed between and 156 | .

157 |
158 |
159 | 160 |
val nativeint : t -> nativeint -> nativeint
161 |
162 |

nativeint g n returns a platform-native integer between 163 | 0 included and n included. n must be strictly positive.

164 |
165 |
166 | 167 |
val char : t -> char
168 |
169 |

Return a character evenly distributed among '\000' ... '\255'.

170 |
171 |
172 | 173 |
val bytes : t -> bytes -> int -> int -> unit
174 |
175 |

bytes g b ofs len produces len bytes of pseudo-random data 176 | and stores them in byte sequence b at offsets ofs to ofs+len-1.

177 | 178 |

Raise Invalid_argument if len < 0 or ofs and len do not 179 | designate a valid range of b.

180 |
181 |
182 |

Splitting and copying

183 |
val split : t -> t
184 |
185 |

split g returns a fresh generator g' that is statistically 186 | independent from the current generator g. The two generators 187 | g and g' can be used in parallel and will produce independent 188 | pseudo-random data. Each generator g and g' can be splitted 189 | again in the future.

190 |
191 |
192 | 193 |
val copy : t -> t
194 |
195 |

copy g returns a generator g' that has the same state as g. 196 | The two generators g and g' produce the same data.

197 |
198 |
199 |

Reseeding

200 |
val reseed : t -> string -> unit
201 |
202 |

reseed g s reinitializes the generator g with fresh seed data 203 | from string s. This is like seed s except that the existing 204 | generator g is seeded, instead of a new generator being returned. 205 | It is good practice to reseed a PRNG after a certain quantity 206 | of pseudo-random data has been produced from it: typically 207 | 232 numbers for the PRNG.Splitmix generator and 208 | 264 bytes for then PRNG.Chacha generator.

209 |
210 |
211 | 212 |
val remake : t -> int array -> unit
213 |
214 |

remake g a reinitializes the generator g with fresh seed data 215 | from array a. This is like reseed except that the seed is 216 | given as an array of integers.

217 |
218 |
219 | 220 | -------------------------------------------------------------------------------- /docs/PRNG.Splitmix.State.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | PRNG.Splitmix.State 19 | 20 | 21 | 24 |

Module PRNG.Splitmix.State

25 | 26 |
module State: PRNG.STATE 

27 | 28 |
type t 
29 |
30 |
31 |

The type of generators

32 |
33 |
34 | 35 |

Creating and seeding PRNGs

36 |
val seed : string -> t
37 |
38 |

Initialize a generator from the given seed. The seed is given 39 | as a character string. The length and randomness of the seed 40 | limit the total entropy of the generator. For example, 64 41 | bits of entropy can be obtained by giving a seed consisting of 42 | 8 cryptographically-strong random characters (as obtained 43 | e.g. by reading /dev/random.

44 |
45 |
46 | 47 |
val make : int array -> t
48 |
49 |

Initialize a generator from the given seed. The seed is given 50 | as an array of integers.

51 |
52 |
53 | 54 |
val make_self_init : unit -> t
55 |
56 |

Initialize a generator from a random seed obtained from the 57 | operating system. Tries hard to provide at least 58 | 64 bits of entropy. With high probability, successive calls 59 | to make_self_init return different PRNGs with different seeds.

60 |
61 |
62 |

Generating pseudo-random data

63 |
val bool : t -> bool
64 |
val bit : t -> bool
65 |
66 |

Return a Boolean value in false,true with 0.5 probability each.

67 |
68 |
69 | 70 |
val uniform : t -> float
71 |
72 |

Return a floating-point number evenly distributed between 0.0 and 1.0. 73 | 0.0 and 1.0 are never returned. 74 | The result is of the form n * 2{^-53}, where n is a random integer 75 | in (0, 2{^53}).

76 |
77 |
78 | 79 |
val float : t -> float -> float
80 |
81 |

float g x returns a floating-point number evenly distributed 82 | between 0.0 and x. If x is negative, negative numbers 83 | between x and 0.0 are returned. Implemented as uniform g *. x. 84 | Consequently, the values 0.0 and x can be returned 85 | (as a result of floating-point rounding), but not if x is 86 | 1.0, since float g 1.0 behaves exactly like uniform g.

87 |
88 |
89 | 90 |
val byte : t -> int
91 |
val bits8 : t -> int
92 |
93 |

Return an 8-bit integer evenly distributed between 0 and 255.

94 |
95 |
96 | 97 |
val bits : t -> int
98 |
val bits30 : t -> int
99 |
100 |

Return a 30-bit integer evenly distributed between 0 and 230-1 101 | (that is, 1073741823, or 0x3FFFFFFF).

102 |
103 |
104 | 105 |
val int : t -> int -> int
106 |
107 |

int g n returns an integer evenly distributed between 0 included 108 | and n excluded. Hence there are n possible return values 109 | with probability 1/n each. n must be greater than 0 and 110 | no greater than 230-1.

111 |
112 |
113 | 114 |
val bits32 : t -> int32
115 |
116 |

Return a 32-bit integer evenly distributed between 117 | and .

118 |
119 |
120 | 121 |
val int32 : t -> int32 -> int32
122 |
123 |

int32 g n returns a 32-bit integer evenly distributed between 124 | 0 included and n excluded. n must be strictly positive.

125 | 126 |

Note that int32 Int32.max_int produces numbers between 0 and 127 | Int32.max_int excluded. To produce numbers between 0 and 128 | Int32.max_int included, use 129 | Int32.logand (bits32 g) Int32.max_int.

130 |
131 |
132 | 133 |
val bits64 : t -> int64
134 |
135 |

Return a 64-bit integer evenly distributed between 136 | and .

137 |
138 |
139 | 140 |
val int64 : t -> int64 -> int64
141 |
142 |

int64 g n returns a 64-bit integer evenly distributed between 143 | 0 included and n excluded. n must be strictly positive.

144 | 145 |

Note that int64 Int64.max_int produces numbers between 0 and 146 | Int64.max_int excluded. To produce numbers between 0 and 147 | Int64.max_int included, use 148 | Int64.logand (bits64 g) Int64.max_int.

149 |
150 |
151 | 152 |
val nativebits : t -> nativeint
153 |
154 |

nativebits g returns a platform-native integer (32 or 64 155 | bits) evenly distributed between and 156 | .

157 |
158 |
159 | 160 |
val nativeint : t -> nativeint -> nativeint
161 |
162 |

nativeint g n returns a platform-native integer between 163 | 0 included and n included. n must be strictly positive.

164 |
165 |
166 | 167 |
val char : t -> char
168 |
169 |

Return a character evenly distributed among '\000' ... '\255'.

170 |
171 |
172 | 173 |
val bytes : t -> bytes -> int -> int -> unit
174 |
175 |

bytes g b ofs len produces len bytes of pseudo-random data 176 | and stores them in byte sequence b at offsets ofs to ofs+len-1.

177 | 178 |

Raise Invalid_argument if len < 0 or ofs and len do not 179 | designate a valid range of b.

180 |
181 |
182 |

Splitting and copying

183 |
val split : t -> t
184 |
185 |

split g returns a fresh generator g' that is statistically 186 | independent from the current generator g. The two generators 187 | g and g' can be used in parallel and will produce independent 188 | pseudo-random data. Each generator g and g' can be splitted 189 | again in the future.

190 |
191 |
192 | 193 |
val copy : t -> t
194 |
195 |

copy g returns a generator g' that has the same state as g. 196 | The two generators g and g' produce the same data.

197 |
198 |
199 |

Reseeding

200 |
val reseed : t -> string -> unit
201 |
202 |

reseed g s reinitializes the generator g with fresh seed data 203 | from string s. This is like seed s except that the existing 204 | generator g is seeded, instead of a new generator being returned. 205 | It is good practice to reseed a PRNG after a certain quantity 206 | of pseudo-random data has been produced from it: typically 207 | 232 numbers for the PRNG.Splitmix generator and 208 | 264 bytes for then PRNG.Chacha generator.

209 |
210 |
211 | 212 |
val remake : t -> int array -> unit
213 |
214 |

remake g a reinitializes the generator g with fresh seed data 215 | from array a. This is like reseed except that the seed is 216 | given as an array of integers.

217 |
218 |
219 | 220 | -------------------------------------------------------------------------------- /docs/type_PRNG.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PRNG 11 | 12 | 13 | sig
14 |   module type STATE =
15 |     sig
16 |       type t
17 |       val seed : string -> PRNG.STATE.t
18 |       val make : int array -> PRNG.STATE.t
19 |       val make_self_init : unit -> PRNG.STATE.t
20 |       val bool : PRNG.STATE.t -> bool
21 |       val bit : PRNG.STATE.t -> bool
22 |       val uniform : PRNG.STATE.t -> float
23 |       val float : PRNG.STATE.t -> float -> float
24 |       val byte : PRNG.STATE.t -> int
25 |       val bits8 : PRNG.STATE.t -> int
26 |       val bits : PRNG.STATE.t -> int
27 |       val bits30 : PRNG.STATE.t -> int
28 |       val int : PRNG.STATE.t -> int -> int
29 |       val bits32 : PRNG.STATE.t -> int32
30 |       val int32 : PRNG.STATE.t -> int32 -> int32
31 |       val bits64 : PRNG.STATE.t -> int64
32 |       val int64 : PRNG.STATE.t -> int64 -> int64
33 |       val nativebits : PRNG.STATE.t -> nativeint
34 |       val nativeint : PRNG.STATE.t -> nativeint -> nativeint
35 |       val char : PRNG.STATE.t -> char
36 |       val bytes : PRNG.STATE.t -> bytes -> int -> int -> unit
37 |       val split : PRNG.STATE.t -> PRNG.STATE.t
38 |       val copy : PRNG.STATE.t -> PRNG.STATE.t
39 |       val reseed : PRNG.STATE.t -> string -> unit
40 |       val remake : PRNG.STATE.t -> int array -> unit
41 |     end
42 |   module type PURE =
43 |     sig
44 |       type t
45 |       val seed : string -> PRNG.PURE.t
46 |       val make : int array -> PRNG.PURE.t
47 |       val make_self_init : unit -> PRNG.PURE.t
48 |       val bool : PRNG.PURE.t -> bool * PRNG.PURE.t
49 |       val bit : PRNG.PURE.t -> bool * PRNG.PURE.t
50 |       val uniform : PRNG.PURE.t -> float * PRNG.PURE.t
51 |       val float : float -> PRNG.PURE.t -> float * PRNG.PURE.t
52 |       val byte : PRNG.PURE.t -> int * PRNG.PURE.t
53 |       val bits8 : PRNG.PURE.t -> int * PRNG.PURE.t
54 |       val int : int -> PRNG.PURE.t -> int * PRNG.PURE.t
55 |       val bits : PRNG.PURE.t -> int * PRNG.PURE.t
56 |       val bits30 : PRNG.PURE.t -> int * PRNG.PURE.t
57 |       val bits32 : PRNG.PURE.t -> int32 * PRNG.PURE.t
58 |       val int32 : int32 -> PRNG.PURE.t -> int32 * PRNG.PURE.t
59 |       val bits64 : PRNG.PURE.t -> int64 * PRNG.PURE.t
60 |       val int64 : int64 -> PRNG.PURE.t -> int64 * PRNG.PURE.t
61 |       val nativebits : PRNG.PURE.t -> nativeint * PRNG.PURE.t
62 |       val nativeint : nativeint -> PRNG.PURE.t -> nativeint * PRNG.PURE.t
63 |       val char : PRNG.PURE.t -> char * PRNG.PURE.t
64 |       val split : PRNG.PURE.t -> PRNG.PURE.t * PRNG.PURE.t
65 |     end
66 |   module Splitmix : sig module State : STATE module Pure : PURE end
67 |   module Chacha : sig module State : STATE module Pure : PURE end
68 |   module LXM : sig module State : STATE module Pure : PURE end
69 | end
70 | -------------------------------------------------------------------------------- /PRNG.ml: -------------------------------------------------------------------------------- 1 | (***********************************************************************) 2 | (* *) 3 | (* The PRINGO library *) 4 | (* *) 5 | (* Xavier Leroy, projet Gallium, INRIA Paris *) 6 | (* *) 7 | (* Copyright 2017 Institut National de Recherche en Informatique et *) 8 | (* en Automatique. All rights reserved. This file is distributed *) 9 | (* under the terms of the GNU Library General Public License v2, *) 10 | (* with the special exception on linking described in file LICENSE. *) 11 | (* *) 12 | (***********************************************************************) 13 | 14 | (** The two interfaces *) 15 | 16 | module type STATE = sig 17 | type t 18 | val seed: string -> t 19 | val make: int array -> t 20 | val make_self_init: unit -> t 21 | val bool: t -> bool 22 | val bit: t -> bool 23 | val uniform: t -> float 24 | val float: t -> float -> float 25 | val byte: t -> int 26 | val bits8: t -> int 27 | val bits: t -> int 28 | val bits30: t -> int 29 | val int: t -> int -> int 30 | val bits32: t -> int32 31 | val int32: t -> int32 -> int32 32 | val bits64: t -> int64 33 | val int64: t -> int64 -> int64 34 | val nativebits: t -> nativeint 35 | val nativeint: t -> nativeint -> nativeint 36 | val char: t -> char 37 | val bytes: t -> bytes -> int -> int -> unit 38 | val split: t -> t 39 | val copy: t -> t 40 | val reseed: t -> string -> unit 41 | val remake: t -> int array -> unit 42 | end 43 | 44 | (** The purely-functional, monadic interface *) 45 | 46 | module type PURE = sig 47 | type t 48 | val seed: string -> t 49 | val make: int array -> t 50 | val make_self_init: unit -> t 51 | val bool: t -> bool * t 52 | val bit: t -> bool * t 53 | val uniform: t -> float * t 54 | val float: float -> t -> float * t 55 | val byte: t -> int * t 56 | val bits8: t -> int * t 57 | val int: int -> t -> int * t 58 | val bits: t -> int * t 59 | val bits30: t -> int * t 60 | val bits32: t -> int32 * t 61 | val int32: int32 -> t -> int32 * t 62 | val bits64: t -> int64 * t 63 | val int64: int64 -> t -> int64 * t 64 | val nativebits: t -> nativeint * t 65 | val nativeint: nativeint -> t -> nativeint * t 66 | val char: t -> char * t 67 | val split: t -> t * t 68 | end 69 | 70 | (** The seeders *) 71 | 72 | (* The seeder from OCaml's runtime system *) 73 | external sys_random_seed: unit -> int array = "caml_sys_random_seed" 74 | 75 | (* A better seeder for machines that support /dev/urandom *) 76 | let dev_urandom_seed len = 77 | match open_in_bin "/dev/urandom" with 78 | | exception Sys_error _ -> None 79 | | ic -> 80 | match really_input_string ic len with 81 | | exception (End_of_file | Sys_error _) -> close_in ic; None 82 | | s -> close_in ic; Some s 83 | 84 | (** Byte array manipulation *) 85 | 86 | external getbyte : bytes -> int -> int = "%bytes_safe_get" 87 | external setbyte : bytes -> int -> int -> unit = "%bytes_safe_set" 88 | (* If we feel adventurous: 89 | external getbyte : bytes -> int -> int = "%bytes_unsafe_get" 90 | external setbyte : bytes -> int -> int -> unit = "%bytes_unsafe_set" 91 | *) 92 | 93 | (** Derived operations for the STATE interface *) 94 | 95 | module StateDerived(X: sig 96 | type t 97 | val bits30: t -> int 98 | val bits32: t -> int32 99 | val bits64: t -> int64 100 | val errorprefix: string 101 | end) = 102 | struct 103 | 104 | let rec intaux g n = 105 | let r = X.bits30 g in 106 | let v = r mod n in 107 | if r - v > 0x3FFFFFFF - n + 1 then intaux g n else v 108 | 109 | let int g bound = 110 | if bound > 0x3FFFFFFF || bound <= 0 111 | then invalid_arg (X.errorprefix ^ "int") 112 | else intaux g bound 113 | 114 | let rec int32aux g n = 115 | let r = Int32.logand (X.bits32 g) 0x7FFF_FFFFl in 116 | let v = Int32.rem r n in 117 | if Int32.sub r v > Int32.(add (sub max_int n) 1l) 118 | then int32aux g n 119 | else v 120 | 121 | let int32 g bound = 122 | if bound <= 0l 123 | then invalid_arg (X.errorprefix ^ "int32") 124 | else int32aux g bound 125 | 126 | let rec int64aux g n = 127 | let r = Int64.logand (X.bits64 g) 0x7FFF_FFFF_FFFF_FFFFL in 128 | let v = Int64.rem r n in 129 | if Int64.sub r v > Int64.(add (sub max_int n) 1L) 130 | then int64aux g n 131 | else v 132 | 133 | let int64 g bound = 134 | if bound <= 0L 135 | then invalid_arg (X.errorprefix ^ "int64") 136 | else int64aux g bound 137 | 138 | let nativebits = 139 | if Nativeint.size = 32 140 | then fun g -> Nativeint.of_int32 (X.bits32 g) 141 | else fun g -> Int64.to_nativeint (X.bits64 g) 142 | 143 | let nativeint = 144 | if Nativeint.size = 32 145 | then fun g bound -> Nativeint.of_int32 (int32 g (Nativeint.to_int32 bound)) 146 | else fun g bound -> Int64.to_nativeint (int64 g (Int64.of_nativeint bound)) 147 | 148 | let rec uniform g = 149 | let b = X.bits64 g in 150 | let n = Int64.shift_right_logical b 11 in 151 | if n <> 0L then Int64.to_float n *. 0x1.p-53 else uniform g 152 | 153 | let float g bound = uniform g *. bound 154 | 155 | let bytes g dst ofs len = 156 | if ofs < 0 || len < 0 || ofs > Bytes.length dst - len then 157 | invalid_arg (X.errorprefix ^ "bytes") 158 | else begin 159 | let rec fill ofs len = 160 | let n = X.bits64 g in 161 | setbyte dst ofs (Int64.to_int n); 162 | if len > 1 then 163 | setbyte dst (ofs+1) (Int64.to_int (Int64.shift_right n 8)); 164 | if len > 2 then 165 | setbyte dst (ofs+2) (Int64.to_int (Int64.shift_right n 16)); 166 | if len > 3 then 167 | setbyte dst (ofs+3) (Int64.to_int (Int64.shift_right n 24)); 168 | if len > 4 then 169 | setbyte dst (ofs+4) (Int64.to_int (Int64.shift_right n 32)); 170 | if len > 5 then 171 | setbyte dst (ofs+5) (Int64.to_int (Int64.shift_right n 40)); 172 | if len > 6 then 173 | setbyte dst (ofs+6) (Int64.to_int (Int64.shift_right n 48)); 174 | if len > 7 then 175 | setbyte dst (ofs+7) (Int64.to_int (Int64.shift_right n 56)); 176 | if len > 8 then 177 | fill (ofs + 8) (len - 8) 178 | in if len > 0 then fill ofs len 179 | end 180 | 181 | end 182 | 183 | (** Derived operations for the PURE interface *) 184 | 185 | module PureDerived(X: sig 186 | type t 187 | val bits30: t -> int * t 188 | val bits32: t -> int32 * t 189 | val bits64: t -> int64 * t 190 | val errorprefix: string 191 | end) = 192 | struct 193 | 194 | let rec intaux n g = 195 | let (r, g') = X.bits30 g in 196 | let v = r mod n in 197 | if r - v > 0x3FFFFFFF - n + 1 then intaux n g' else (v, g') 198 | 199 | let int bound g = 200 | if bound > 0x3FFFFFFF || bound <= 0 201 | then invalid_arg (X.errorprefix ^ "int") 202 | else intaux bound g 203 | 204 | let rec int32aux n g = 205 | let (r, g') = X.bits32 g in 206 | let r = Int32.logand r 0x7FFF_FFFFl in 207 | let v = Int32.rem r n in 208 | if Int32.sub r v > Int32.(add (sub max_int n) 1l) 209 | then int32aux n g' 210 | else (v, g') 211 | 212 | let int32 bound g = 213 | if bound <= 0l 214 | then invalid_arg (X.errorprefix ^ "int32") 215 | else int32aux bound g 216 | 217 | let rec int64aux n g = 218 | let (r, g') = X.bits64 g in 219 | let r = Int64.logand r 0x7FFF_FFFF_FFFF_FFFFL in 220 | let v = Int64.rem r n in 221 | if Int64.sub r v > Int64.(add (sub max_int n) 1L) 222 | then int64aux n g' 223 | else (v, g') 224 | 225 | let int64 bound g = 226 | if bound <= 0L 227 | then invalid_arg (X.errorprefix ^ "int64") 228 | else int64aux bound g 229 | 230 | let nativebits = 231 | if Nativeint.size = 32 232 | then fun g -> let (r, g') = X.bits32 g in (Nativeint.of_int32 r, g') 233 | else fun g -> let (r, g') = X.bits64 g in (Int64.to_nativeint r, g') 234 | 235 | let nativeint = 236 | if Nativeint.size = 32 237 | then begin 238 | fun bound g -> 239 | let (r, g') = int32 (Nativeint.to_int32 bound) g in 240 | (Nativeint.of_int32 r, g') 241 | end else begin 242 | fun bound g -> 243 | let (r, g') = int64 (Int64.of_nativeint bound) g in 244 | (Int64.to_nativeint r, g') 245 | end 246 | 247 | let rec uniform g = 248 | let (b, g) = X.bits64 g in 249 | let n = Int64.shift_right_logical b 11 in 250 | if n <> 0L then (Int64.to_float n *. 0x1.p-53, g) else uniform g 251 | 252 | let float bound g = 253 | let (f, g) = uniform g in (f *. bound, g) 254 | 255 | end 256 | 257 | (** This is an implementation of the Splitmix PRNG, from: 258 | Guy L. Steele Jr., Doug Lea, Christine H. Flood 259 | "Fast Splittable Pseudorandom Number Generators" 260 | OOPSLA 2014. 261 | Two interfaces are provided: stateful and monadic. *) 262 | 263 | module Splitmix = struct 264 | 265 | (* Notations for int64 operations *) 266 | 267 | let (<<) = Int64.shift_left 268 | let (>>) = Int64.shift_right_logical 269 | let (^^) = Int64.logxor 270 | let (&&&) = Int64.logand 271 | let (|||) = Int64.logor 272 | let ( ** ) = Int64.mul 273 | let (++) = Int64.add 274 | let (--) = Int64.sub 275 | 276 | (* The core mixing functions. Could be defined in OCaml, and would run 277 | quite fast on 64-bit platforms, but are too slow on 32-bit platforms. *) 278 | 279 | external mix64: int64 -> int64 = "pringo_mix64" "pringo_mix64_unboxed" 280 | [@@unboxed] [@@noalloc] 281 | external mix32: int64 -> int32 = "pringo_mix32" "pringo_mix32_unboxed" 282 | [@@unboxed] [@@noalloc] 283 | external mix30: (int64[@unboxed]) -> int = "pringo_mix30" "pringo_mix30_unboxed" 284 | [@@noalloc] 285 | external mixGamma: int64 -> int64 = "pringo_mixGamma" "pringo_mixGamma_unboxed" 286 | [@@unboxed] [@@noalloc] 287 | 288 | (** Helpers for initialization *) 289 | 290 | let golden_gamma = 0x9e3779b97f4a7c15L 291 | 292 | let int64_of_seed s = 293 | let s = if String.length s <= 8 then s else Digest.string s in 294 | let rec extract i accu = 295 | if i < 0 then accu else 296 | extract (i-1) 297 | ((accu << 8) ++ Int64.of_int (Char.code (String.get s i))) in 298 | extract (min 7 (String.length s - 1)) 0L 299 | 300 | let mix_init accu n = 301 | mix64 (accu ++ Int64.of_int (n land 0x3FFFFFFF)) 302 | 303 | (** The stateful interface *) 304 | 305 | module State = struct 306 | 307 | type t = { mutable seed: int64; gamma: int64; } 308 | 309 | let seed s = 310 | { seed = int64_of_seed s; gamma = golden_gamma } 311 | 312 | let make s = 313 | { seed = Array.fold_left mix_init 0L s; gamma = golden_gamma } 314 | 315 | let make_self_init () = 316 | match dev_urandom_seed 8 with 317 | | Some s -> seed s 318 | | None -> make (sys_random_seed()) 319 | 320 | let [@inline] nextseed t = 321 | let z = t.seed ++ t.gamma in t.seed <- z; z 322 | 323 | let bit g = mix30 (nextseed g) land 0x1 = 1 324 | let bool = bit 325 | 326 | let bits8 g = mix30 (nextseed g) land 0xFF 327 | let byte = bits8 328 | let char g = Char.chr (bits8 g) 329 | 330 | let bits30 g = mix30 (nextseed g) 331 | let bits = bits30 332 | 333 | let bits32 g = mix32 (nextseed g) 334 | 335 | let bits64 g = mix64 (nextseed g) 336 | 337 | include StateDerived(struct 338 | type nonrec t = t 339 | let bits30 = bits30 340 | let bits32 = bits32 341 | let bits64 = bits64 342 | let errorprefix = "PRNG.Splitmix.State." 343 | end) 344 | 345 | let split g = 346 | let n1 = nextseed g in 347 | let n2 = nextseed g in 348 | { seed = mix64 n1; gamma = mixGamma n2 } 349 | 350 | let copy g = { seed = g.seed; gamma = g.gamma } 351 | 352 | let reseed g s = 353 | g.seed <- int64_of_seed s 354 | (* let's keep the original gamma, why not? *) 355 | 356 | let remake g s = 357 | g.seed <- Array.fold_left mix_init 0L s 358 | (* let's keep the original gamma, why not? *) 359 | 360 | end 361 | 362 | (** The pure interface *) 363 | 364 | module Pure = struct 365 | 366 | type t = { seed: int64; gamma: int64; } 367 | 368 | let seed s = 369 | { seed = int64_of_seed s; gamma = golden_gamma } 370 | 371 | let make s = 372 | { seed = Array.fold_left mix_init 0L s; gamma = golden_gamma } 373 | 374 | let make_self_init () = 375 | match dev_urandom_seed 8 with 376 | | Some s -> seed s 377 | | None -> make (sys_random_seed()) 378 | 379 | let [@inline] next g = { seed = g.seed ++ g.gamma; gamma = g.gamma } 380 | 381 | let bit g = 382 | let g = next g in ((mix30 g.seed land 0x1 = 1), g) 383 | let bool = bit 384 | 385 | let bits8 g = 386 | let g = next g in (mix30 g.seed land 0xFF, g) 387 | let byte = bits8 388 | let char g = 389 | let g = next g in (Char.chr (mix30 g.seed land 0xFF), g) 390 | 391 | let bits30 g = 392 | let g = next g in (mix30 g.seed, g) 393 | let bits = bits30 394 | 395 | let bits32 g = 396 | let g = next g in (mix32 g.seed, g) 397 | 398 | let bits64 g = 399 | let g = next g in (mix64 g.seed, g) 400 | 401 | include PureDerived(struct 402 | type nonrec t = t 403 | let bits30 = bits30 404 | let bits32 = bits32 405 | let bits64 = bits64 406 | let errorprefix = "PRNG.Splitmix.Pure." 407 | end) 408 | 409 | let split g = 410 | let g1 = next g in 411 | let g2 = next g1 in 412 | ({ seed = mix64 g1.seed; gamma = mixGamma g2.seed }, g2) 413 | 414 | end 415 | 416 | end 417 | 418 | (** The Chacha implementation *) 419 | 420 | module Chacha = struct 421 | 422 | type key 423 | type state = bytes 424 | 425 | external chacha_make_key: string -> key = "pringo_chacha_make_key" 426 | external chacha_make_state: bytes -> state = "pringo_chacha_make_state" 427 | external chacha_transform: key -> state -> unit = "pringo_chacha_transform" 428 | 429 | (** Helpers for initialization *) 430 | 431 | let empty_bytes = Bytes.make 16 '\000' 432 | 433 | let mix_init a = 434 | let buf = Bytes.create (4 * Array.length a) in 435 | let storeint n i = 436 | setbyte buf i n; 437 | setbyte buf (i+1) (n lsl 8); 438 | setbyte buf (i+2) (n lsl 16); 439 | setbyte buf (i+3) (n lsl 24) in 440 | Array.iteri (fun i n -> storeint n (i * 4)) a; 441 | Digest.bytes buf 442 | 443 | (** Helpers to build integers *) 444 | 445 | let [@inline] make30 b0 b1 b2 b3 = 446 | b0 + (b1 lsl 8) + (b2 lsl 16) + ((b3 land 0x3F) lsl 24) 447 | 448 | let [@inline] make32 b0 b1 b2 b3 = 449 | Int32.(add (add (of_int b0) 450 | (shift_left (of_int b1) 8)) 451 | (add (shift_left (of_int b2) 16) 452 | (shift_left (of_int b3) 24))) 453 | 454 | let make64 = 455 | if Sys.word_size = 64 456 | then (fun b0 b1 b2 b3 b4 b5 b6 b7 -> 457 | Int64.(add (add (add (of_int b0) 458 | (shift_left (of_int b1) 8)) 459 | (add (shift_left (of_int b2) 16) 460 | (shift_left (of_int b3) 24))) 461 | (add (add (shift_left (of_int b4) 32) 462 | (shift_left (of_int b5) 40)) 463 | (add (shift_left (of_int b6) 48) 464 | (shift_left (of_int b7) 56))))) 465 | [@inline] 466 | else (fun b0 b1 b2 b3 b4 b5 b6 b7 -> 467 | Int64.(add (of_int32 (make32 b0 b1 b2 b3)) 468 | (shift_left (of_int32 (make32 b4 b5 b6 b7)) 32))) 469 | [@inline] 470 | 471 | (** The stateful interface *) 472 | 473 | module State = struct 474 | 475 | type t = { mutable key: key; mutable st: state; mutable next: int } 476 | 477 | let seed s = 478 | { key = chacha_make_key s; st = chacha_make_state empty_bytes; next = 64 } 479 | 480 | let make s = 481 | { key = chacha_make_key (mix_init s); 482 | st = chacha_make_state empty_bytes; next = 64 } 483 | 484 | let make_self_init () = 485 | match dev_urandom_seed 16 with 486 | | Some s -> seed s 487 | | None -> make (sys_random_seed()) 488 | 489 | (* Layout of the state: 490 | 0...63 up to 64 bytes of already-generated pseudo-random data 491 | 64...79 16 bytes for the counter and the nonce *) 492 | 493 | let byte g = 494 | let i = g.next in 495 | if i <= 63 then begin 496 | g.next <- i + 1; 497 | getbyte g.st i 498 | end else begin 499 | chacha_transform g.key g.st; 500 | g.next <- 1; 501 | getbyte g.st 0 502 | end 503 | 504 | let bits8 = byte 505 | let char g = Char.chr (bits8 g) 506 | 507 | let bit g = byte g land 0x1 = 1 508 | let bool = bit 509 | 510 | let bits30 g = 511 | let i = g.next in 512 | if i <= 60 then begin 513 | g.next <- i + 4; 514 | make30 (getbyte g.st i) (getbyte g.st (i+1)) 515 | (getbyte g.st (i+2)) (getbyte g.st (i+3)) 516 | end else begin 517 | let b0 = byte g in let b1 = byte g in 518 | let b2 = byte g in let b3 = byte g in 519 | make30 b0 b1 b2 b3 520 | end 521 | 522 | let bits = bits30 523 | 524 | let bits32 g = 525 | let i = g.next in 526 | if i <= 60 then begin 527 | g.next <- i + 4; 528 | make32 (getbyte g.st i) (getbyte g.st (i+1)) 529 | (getbyte g.st (i+2)) (getbyte g.st (i+3)) 530 | end else begin 531 | let b0 = byte g in let b1 = byte g in 532 | let b2 = byte g in let b3 = byte g in 533 | make32 b0 b1 b2 b3 534 | end 535 | 536 | let bits64 g = 537 | let i = g.next in 538 | if i <= 56 then begin 539 | g.next <- i + 8; 540 | make64 (getbyte g.st i) (getbyte g.st (i+1)) 541 | (getbyte g.st (i+2)) (getbyte g.st (i+3)) 542 | (getbyte g.st (i+4)) (getbyte g.st (i+5)) 543 | (getbyte g.st (i+6)) (getbyte g.st (i+7)) 544 | end else begin 545 | let b0 = byte g in let b1 = byte g in 546 | let b2 = byte g in let b3 = byte g in 547 | let b4 = byte g in let b5 = byte g in 548 | let b6 = byte g in let b7 = byte g in 549 | make64 b0 b1 b2 b3 b4 b5 b6 b7 550 | end 551 | 552 | include StateDerived(struct 553 | type nonrec t = t 554 | let bits30 = bits30 555 | let bits32 = bits32 556 | let bits64 = bits64 557 | let errorprefix = "PRNG.Chacha.State." 558 | end) 559 | 560 | let bytes g dst ofs len = 561 | if ofs < 0 || len < 0 || Bytes.length dst - len > ofs then 562 | invalid_arg "PRNG.Chacha.State.bytes"; 563 | let rec fill ofs len = 564 | let next = g.next in 565 | let avail = 64 - next in 566 | if len <= avail then begin 567 | Bytes.blit g.st next dst ofs len; 568 | g.next <- next + len; 569 | end else begin 570 | Bytes.blit g.st next dst ofs avail; 571 | chacha_transform g.key g.st; 572 | g.next <- 0; 573 | fill (ofs + avail) (len - avail) 574 | end 575 | in fill ofs len 576 | 577 | let split g = 578 | let k = Bytes.create 16 in 579 | bytes g k 0 16; 580 | { key = g.key; st = chacha_make_state k; next = 64 } 581 | 582 | let copy g = { key = g.key; st = Bytes.copy g.st; next = g.next } 583 | 584 | let reseed g s = 585 | g.key <- chacha_make_key s; 586 | g.st <- chacha_make_state empty_bytes; 587 | g.next <- 64 588 | 589 | let remake g s = 590 | g.key <- chacha_make_key (mix_init s); 591 | g.st <- chacha_make_state empty_bytes; 592 | g.next <- 64 593 | 594 | end 595 | 596 | (** The pure interface *) 597 | 598 | module Pure = struct 599 | 600 | type t = { key: key; st: state; next: int } 601 | 602 | let seed s = 603 | { key = chacha_make_key s; st = chacha_make_state empty_bytes; next = 64 } 604 | 605 | let make s = 606 | { key = chacha_make_key (mix_init s); 607 | st = chacha_make_state empty_bytes; next = 64 } 608 | 609 | let make_self_init () = 610 | match dev_urandom_seed 16 with 611 | | Some s -> seed s 612 | | None -> make (sys_random_seed()) 613 | 614 | let byte g = 615 | let i = g.next in 616 | if i <= 63 then begin 617 | (getbyte g.st i, {g with next = i + 1}) 618 | end else begin 619 | let st' = Bytes.copy g.st in 620 | chacha_transform g.key st'; 621 | (getbyte st' 0, {g with st = st'; next = 1}) 622 | end 623 | 624 | let bits8 = byte 625 | let char g = let (n, g') = byte g in (Char.chr n, g') 626 | 627 | let bit g = let (n, g') = byte g in (n land 0x1 = 1, g') 628 | let bool = bit 629 | 630 | let bits30 g = 631 | let i = g.next in 632 | if i <= 60 then begin 633 | (make30 (getbyte g.st i) (getbyte g.st (i+1)) 634 | (getbyte g.st (i+2)) (getbyte g.st (i+3)), 635 | {g with next = i + 4}) 636 | end else begin 637 | let (b0, g) = byte g in let (b1, g) = byte g in 638 | let (b2, g) = byte g in let (b3, g) = byte g in 639 | (make30 b0 b1 b2 b3, g) 640 | end 641 | 642 | let bits = bits30 643 | 644 | let bits32 g = 645 | let i = g.next in 646 | if i <= 60 then begin 647 | (make32 (getbyte g.st i) (getbyte g.st (i+1)) 648 | (getbyte g.st (i+2)) (getbyte g.st (i+3)), 649 | {g with next = i + 4}) 650 | end else begin 651 | let (b0, g) = byte g in let (b1, g) = byte g in 652 | let (b2, g) = byte g in let (b3, g) = byte g in 653 | (make32 b0 b1 b2 b3, g) 654 | end 655 | 656 | let bits64 g = 657 | let i = g.next in 658 | if i <= 56 then begin 659 | (make64 (getbyte g.st i) (getbyte g.st (i+1)) 660 | (getbyte g.st (i+2)) (getbyte g.st (i+3)) 661 | (getbyte g.st (i+4)) (getbyte g.st (i+5)) 662 | (getbyte g.st (i+6)) (getbyte g.st (i+7)), 663 | {g with next = i + 8}) 664 | end else begin 665 | let (b0, g) = byte g in let (b1, g) = byte g in 666 | let (b2, g) = byte g in let (b3, g) = byte g in 667 | let (b4, g) = byte g in let (b5, g) = byte g in 668 | let (b6, g) = byte g in let (b7, g) = byte g in 669 | (make64 b0 b1 b2 b3 b4 b5 b6 b7, g) 670 | end 671 | 672 | include PureDerived(struct 673 | type nonrec t = t 674 | let bits30 = bits30 675 | let bits32 = bits32 676 | let bits64 = bits64 677 | let errorprefix = "PRNG.Chacha.Pure." 678 | end) 679 | 680 | let bytes g dst ofs len = 681 | if ofs < 0 || len < 0 || Bytes.length dst - len > ofs then 682 | invalid_arg "PRNG.Chacha.Pure.bytes"; 683 | let rec fill g ofs len = 684 | let next = g.next in 685 | let avail = 64 - next in 686 | if len <= avail then begin 687 | Bytes.blit g.st next dst ofs len; 688 | {g with next = next + len} 689 | end else begin 690 | Bytes.blit g.st next dst ofs avail; 691 | let st' = Bytes.copy g.st in 692 | chacha_transform g.key st'; 693 | fill {g with st = st'; next = 0} (ofs + avail) (len - avail) 694 | end 695 | in fill g ofs len 696 | 697 | let split g = 698 | let k = Bytes.create 16 in 699 | let g = bytes g k 0 16 in 700 | ( { key = g.key; st = chacha_make_state k; next = 64 }, g ) 701 | 702 | end 703 | 704 | end 705 | 706 | (* The LXM implementation *) 707 | 708 | module LXM = struct 709 | 710 | type state 711 | 712 | external next: state -> (int64[@unboxed]) 713 | = "pringo_LXM_next" "pringo_LXM_next_unboxed" 714 | external copy: state -> state = "pringo_LXM_copy" 715 | external assign: state -> state -> unit = "pringo_LXM_assign" 716 | external init: (int64[@unboxed]) -> (int64[@unboxed]) -> 717 | (int64[@unboxed]) -> (int64[@unboxed]) -> state 718 | = "pringo_LXM_init" "pringo_LXM_init_unboxed" 719 | external seed: string -> state = "pringo_LXM_seed" 720 | external make: int array -> state = "pringo_LXM_make" 721 | 722 | (** The stateful interface *) 723 | 724 | module State = struct 725 | 726 | type t = state 727 | 728 | let seed = seed 729 | let make = make 730 | let make_self_init () = 731 | match dev_urandom_seed 16 with 732 | | Some s -> seed s 733 | | None -> make (sys_random_seed()) 734 | 735 | let byte g = Int64.to_int (next g) land 0xFF 736 | let bits8 = byte 737 | let char g = Char.chr (bits8 g) 738 | let bit g = Int64.to_int (next g) land 0x1 = 1 739 | let bool = bit 740 | let bits30 g = Int64.to_int (next g) land 0x3FFFFFFF 741 | let bits = bits30 742 | let bits32 g = Int64.to_int32 (next g) 743 | let bits64 = next 744 | 745 | include StateDerived(struct 746 | type nonrec t = t 747 | let bits30 = bits30 748 | let bits32 = bits32 749 | let bits64 = bits64 750 | let errorprefix = "PRNG.LXM.State." 751 | end) 752 | 753 | let split g = 754 | let i1 = next g in let i2 = next g in let i3 = next g in let i4 = next g in 755 | init i1 i2 i3 i4 756 | 757 | let copy = copy 758 | 759 | let reseed g s = assign g (seed s) 760 | let remake g s = assign g (make s) 761 | 762 | end 763 | 764 | (** The pure interface *) 765 | 766 | module Pure = struct 767 | 768 | type t = state 769 | 770 | let seed = seed 771 | let make = make 772 | let make_self_init () = 773 | match dev_urandom_seed 16 with 774 | | Some s -> seed s 775 | | None -> make (sys_random_seed()) 776 | 777 | let byte g = 778 | let g = copy g in (Int64.to_int (next g) land 0xFF, g) 779 | let bits8 = byte 780 | let char g = 781 | let (n, g') = bits8 g in (Char.chr n, g') 782 | let bit g = 783 | let g = copy g in (Int64.to_int (next g) land 0x1 = 1, g) 784 | let bool = bit 785 | 786 | let bits30 g = 787 | let g = copy g in (Int64.to_int (next g) land 0x3FFFFFFF, g) 788 | let bits = bits30 789 | 790 | let bits32 g = 791 | let g = copy g in (Int64.to_int32 (next g), g) 792 | 793 | let bits64 g = 794 | let g = copy g in (next g, g) 795 | 796 | include PureDerived(struct 797 | type nonrec t = t 798 | let bits30 = bits30 799 | let bits32 = bits32 800 | let bits64 = bits64 801 | let errorprefix = "PRNG.Chacha.Pure." 802 | end) 803 | 804 | let split g = 805 | let g = copy g in 806 | let i1 = next g in let i2 = next g in let i3 = next g in let i4 = next g in 807 | (init i1 i2 i3 i4, g) 808 | 809 | end 810 | 811 | end 812 | --------------------------------------------------------------------------------