🎨 elm-format

This commit is contained in:
Richard Feldman
2018-02-22 17:00:38 -05:00
parent 40c66a8a10
commit c8c89adc63
60 changed files with 4253 additions and 124 deletions

View File

@@ -0,0 +1,47 @@
# Random.Pcg for Elm
> "The generation of random numbers is too important to be left to chance." Robert R. Coveyou
An alternate random number generator built around four principles:
* **Statistical Quality.** If you use any seed less than 53,668 and generate one bool, it will be `True` if you're
using core's `Random` module. More sophisticated statistical tests spot patterns in the "random" numbers almost
immediately. Would you want to trust the accuracy of your [fuzz
tests](http://package.elm-lang.org/packages/elm-community/elm-test/latest/) to such a flawed algorithm? This library
produces far less predictable and biased output, especially if you use thousands of random numbers. See
`test/dieharder` for more details.
* **Useful features.** This library exports `constant` and `andMap`, which are conspicuously absent from core, along
with other helpful functions for composing generators. Particularly interesting is `independentSeed`, which allows for
lazy lists and isolated components to generate as much randomness as they need, when they need it.
* **Performace.** This library will generate floats about 3.5 times faster than core, and ints do not regress. These
figures stand to improve pending some optimizations to the compiler. You can see the [full
benchmark results](https://github.com/mgold/elm-random-pcg/issues/5#issuecomment-236398261).
* **Compatibility.** This library is a drop-in replacement for core's Random module. Specifically, you
can replace `import Random` with `import Random.Pcg as Random` and everything will continue to work. (The one exception is third party
libraries like [elm-random-extra](http://package.elm-lang.org/packages/NoRedInk/elm-random-extra/latest/Random-Extra).)
This is an implementation of [PCG](http://www.pcg-random.org/) by M. E. O'Neil. The generator is **not cryptographically
secure**.
Please report bugs, feature requests, and other issues [on GitHub](https://github.com/mgold/elm-random-pcg/issues/new).
## Changelog (major versions only)
### 4.0.0
* Upgraded for 0.18.
* Argument order of `andThen` flipped.
### 3.0.0
* Change implementation to use the RXS-M-SH variant of PCG. Now much faster and not much worse statistically.
* Remove `initialSeed2`, since there are now only 32 bits of state.
* `Random.Pcg.Interop.fission` has been changed to a (core) generator of (PCG) seeds.
* Add `generate` to match core 4.x API. Implemented by Richard Feldman.
### 2.0.0
* Upgraded for 0.17.
* `generate` renamed `step` to match core 4.x API.
* Module renamed `Random.Pcg` from `Random.PCG`.
* `split` has been removed; use `independentSeed`.
* `minInt` and `maxInt` values changed to match core.

View File

@@ -0,0 +1,4 @@
# Random.Pcg Tests
The tests in this directory are one-off simple programs to confirm that certain functions aren't obviously wrong.
Heavyweight statistical tests are under `dieharder`. Benchmarks (using a 0.16 benchmarking lib) are in `benchmark`.

View File

@@ -0,0 +1,14 @@
# Benchmarks for Random.Pcg
These benchmarks are possible thanks to Robin Heggelund Hansen's work benchmarking [collections-ng](https://github.com/Skinney/collections-ng).
To run them yourself, first download the vendor files (you only need to do this once):
```
mkdir vendor
cd vendor
wget https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js
wget https://cdn.jsdelivr.net/benchmarkjs/2.1.0/benchmark.js
```
Then adjust the import of `Random` in `Bencher.elm` to control which library is being benchmarked. Finally run `sh prep-bench.sh` then
`elm-reactor` in this directory and open `run-benchmarks.html`.

View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -e
elm-make --yes --output bench.js Bencher.elm

View File

@@ -0,0 +1,27 @@
# Dieharder tests for Random.Pcg
**These tests rely on a library that has not been update for 0.17.**
The purpose of testing the random number generator is twofold: one, show the deficiencies in the core implementation;
two, show the correctness of the Pcg implementation *in Elm*. Because we are testing the Elm implementation, not the Pcg
algorithm, we must feed dieharder a file of already-generated random numbers. I've seen sources recommending 10 million
random numbers; these tests use 24 million, but even so the files are "rewound", as many as 2500 times on later tests.
For the original 19 diehard tests, the core fails 9 tests while Pcg fails none. (One test resulted in "weak" but further
investigation resulted in passing; we expect this on one test in 100). On the entire battery, core passes 29, fails 75,
and is weak in 10. Pcg passes 82, fails 24, and is weak in 8. Some of Pcg's strength may be attributed to its 64 bits of
state, compared to core's 32, but this does not excuse failing the less-comprehensive diehard tests. Conversely, many of
the failures can be attributed to reusing pre-generated random numbers.
The source Elm is `Dieharder.elm`. Because the tests require more random numbers than can be stored in memory, they
output chunks of 2 million at a time. In order to reuse the code, `compile.sh` rewrites the file to change the Random
implementation. It also writes out the `.txt` data files, and then runs dieharder, logging the results.
The result log files have been committed to version control; I would have liked to commit the data files but they're
huge, and only take a few minutes to generate. You are free to alter the random seed in the Elm code, and rerun the
tests with `sh compile` (which takes several hours to complete). That said, I encourage you to run it long enough to see
core fail the birthday test, after only five or six seconds of scrutiny. (The `dieharder` tool is available through most
package managers. Once the data files are generated, try `time dieharder -g 202 -f elm-core-random.txt -d 0`.)
The Pcg paper uses the TestU01 (e.g. BigCrush) suite; I'm using dieharder since it was easier to get to work
reading from a file.

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -e
if [ ! -f elm-core-random.txt ]; then
sed -i.bak 's/import Random.Pcg as Random/import Random/g' Dieharder.elm
sed -i.bak 's/elm-random-pcg/elm-core-random/g' Dieharder.elm
elm make Dieharder.elm --output=raw_out.js
sh elm-io.sh raw_out.js generate_files.js
echo "Generating elm-core-random.txt..."
node generate_files.js > elm-core-random.txt
dieharder -g 202 -f elm-core-random.txt -a | tee dieharder-core.log
fi
if [ ! -f elm-random-pcg.txt ]; then
sed -i.bak 's/import Random$/import Random.Pcg as Random/g' Dieharder.elm
sed -i.bak 's/elm-core-random/elm-random-pcg/g' Dieharder.elm
elm make Dieharder.elm --output=raw_out.js
sh elm-io.sh raw_out.js generate_files.js
echo "Generating elm-random-pcg.txt..."
node generate_files.js > elm-random-pcg.txt
dieharder -g 202 -f elm-random-pcg.txt -a | tee dieharder-pcg.log
fi

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
# Script for use with the Console library to allow Elm to run on Node.
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <generated-js-file> <output-file>"
exit 1
fi
read -d '' handler <<- EOF
(function(){
if (typeof Elm === "undefined") { throw "elm-io config error: Elm is not defined. Make sure you call elm-io with a real Elm output file"}
if (typeof Elm.Main === "undefined" ) { throw "Elm.Main is not defined, make sure your module is named Main." };
var worker = Elm.worker(Elm.Main);
})();
EOF
cat $1 > $2
echo "$handler" >> $2

View File

@@ -0,0 +1,7 @@
# Port test
```
elm make Test.elm --output=elm.js
```
This shows how to hook up ports to the random number generator to produce different values each time.