Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(examples): Add a useful set of high quality pseudo-random number generators #2868

Merged
merged 25 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
289f316
Add a useful set of high quality pseudo-random number generators.
wyhaines Sep 28, 2024
08315be
Added the ability to output float64 to the ufmt.Sprintf implementation.
wyhaines Sep 28, 2024
b67629d
Formatting fixes that were missed.
wyhaines Sep 28, 2024
e74b9da
WIP....is it logical that this change has to happen to avoid a compil…
wyhaines Oct 2, 2024
385d5a6
Fix the %f test.
wyhaines Oct 2, 2024
0a89297
Add a test for the strconv.FormatFloat() addition, and filled out spr…
wyhaines Oct 3, 2024
3d641ef
Tidy.
wyhaines Oct 3, 2024
030eb12
Fix ufmt tests, and tidy formatting of comments.
wyhaines Oct 6, 2024
0fff418
Small tweak.
wyhaines Oct 15, 2024
4231d76
Merge branch 'master' into kh.add-prngs
thehowl Oct 31, 2024
1889a46
Remove this, since the work that I did around floats has been undone …
wyhaines Oct 31, 2024
61c1391
Merge branch 'master' into kh.add-prngs
n2p5 Nov 12, 2024
9f8a9c2
Merge branch 'master' into kh.add-prngs
n2p5 Nov 13, 2024
25601e0
Merge branch 'master' into kh.add-prngs
wyhaines Nov 30, 2024
01acaa3
Update examples/gno.land/p/demo/math/rand/isaac/isaac.gno
wyhaines Nov 30, 2024
b6126da
Update examples/gno.land/p/demo/math/rand/isaac/isaac.gno
wyhaines Nov 30, 2024
90b3124
Move all of the PRNGs to wyhaines package path.
wyhaines Dec 4, 2024
668c9a1
Fix formatting.
wyhaines Dec 4, 2024
65a461c
Merge branch 'master' into kh.add-prngs
wyhaines Dec 4, 2024
0f3b19d
Tidy gno.mod files.
wyhaines Dec 4, 2024
2afca91
Merge branch 'master' into kh.add-prngs
wyhaines Dec 4, 2024
8b98f0b
Merge branch 'master' into kh.add-prngs
n2p5 Dec 4, 2024
e9dc37d
Revert this to it's previous shape.
wyhaines Dec 4, 2024
a01fd78
Merge branch 'master' into kh.add-prngs
wyhaines Dec 5, 2024
3ff9fe8
Looks like I made a mistake resolving conflicts and removed a test. R…
wyhaines Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/gno.land/p/demo/entropy/entropy.gno
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,11 @@ func (i *Instance) Value() uint32 {
i.addEntropy()
return i.value
}

func (i *Instance) Value64() uint64 {
i.addEntropy()
high := i.value
i.addEntropy()

return (uint64(high) << 32) | uint64(i.value)
}
32 changes: 32 additions & 0 deletions examples/gno.land/p/demo/entropy/entropy_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ func TestInstanceValue(t *testing.T) {
}
}

func TestInstanceValue64(t *testing.T) {
baseEntropy := New()
baseResult := computeValue64(t, baseEntropy)

sameHeightEntropy := New()
sameHeightResult := computeValue64(t, sameHeightEntropy)

if baseResult != sameHeightResult {
t.Errorf("should have the same result: new=%s, base=%s", sameHeightResult, baseResult)
}

std.TestSkipHeights(1)
differentHeightEntropy := New()
differentHeightResult := computeValue64(t, differentHeightEntropy)

if baseResult == differentHeightResult {
t.Errorf("should have different result: new=%s, base=%s", differentHeightResult, baseResult)
}
}

func computeValue(t *testing.T, r *Instance) string {
t.Helper()

Expand All @@ -44,3 +64,15 @@ func computeValue(t *testing.T, r *Instance) string {

return out
}

func computeValue64(t *testing.T, r *Instance) string {
t.Helper()

out := ""
for i := 0; i < 10; i++ {
val := int(r.Value64())
out += strconv.Itoa(val) + " "
}

return out
}
6 changes: 6 additions & 0 deletions examples/gno.land/p/demo/entropy/z_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func main() {
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value64())

// should be the same
println("---")
Expand All @@ -24,6 +25,7 @@ func main() {
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value64())

std.TestSkipHeights(1)
println("---")
Expand All @@ -33,6 +35,7 @@ func main() {
println(r.Value())
println(r.Value())
println(r.Value())
println(r.Value64())
}

// Output:
Expand All @@ -42,15 +45,18 @@ func main() {
// 1950222777
// 3348280598
// 438354259
// 6353385488959065197
// ---
// 4129293727
// 2141104956
// 1950222777
// 3348280598
// 438354259
// 6353385488959065197
// ---
// 49506731
// 1539580078
// 2695928529
// 1895482388
// 3462727799
// 16745038698684748445
6 changes: 6 additions & 0 deletions examples/gno.land/p/demo/ufmt/ufmt_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ func TestSprintf(t *testing.T) {
{"uint32 [%v]", []interface{}{uint32(32)}, "uint32 [32]"},
{"uint64 [%d]", []interface{}{uint64(64)}, "uint64 [64]"},
{"uint64 [%v]", []interface{}{uint64(64)}, "uint64 [64]"},
{"float64 [%e]", []interface{}{float64(64.1)}, "float64 [6.41e+01]"},
{"float64 [%E]", []interface{}{float64(64.1)}, "float64 [6.41E+01]"},
{"float64 [%f]", []interface{}{float64(64.1)}, "float64 [64.100000]"},
{"float64 [%F]", []interface{}{float64(64.1)}, "float64 [64.100000]"},
{"float64 [%g]", []interface{}{float64(64.1)}, "float64 [64.1]"},
{"float64 [%G]", []interface{}{float64(64.1)}, "float64 [64.1]"},
{"bool [%t]", []interface{}{true}, "bool [true]"},
{"bool [%v]", []interface{}{true}, "bool [true]"},
{"bool [%t]", []interface{}{false}, "bool [false]"},
Expand Down
86 changes: 86 additions & 0 deletions examples/gno.land/p/wyhaines/rand/isaac/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# package isaac // import "gno.land/p/demo/math/rand/isaac"

This is a port of the ISAAC cryptographically secure PRNG,
originally based on the reference implementation found at
https://burtleburtle.net/bob/rand/isaacafa.html

ISAAC has excellent statistical properties, with long cycle times, and
uniformly distributed, unbiased, and unpredictable number generation. It can
not be distinguished from real random data, and in three decades of scrutiny,
no practical attacks have been found.

The default random number algorithm in gno was ported from Go's v2 rand
implementatoon, which defaults to the PCG algorithm. This algorithm is
commonly used in language PRNG implementations because it has modest seeding
requirements, and generates statistically strong randomness.

This package provides an implementation of the 32-bit ISAAC PRNG algorithm. This
algorithm provides very strong statistical performance, and is cryptographically
secure, while still being substantially faster than the default PCG
implementation in `math/rand`. Note that this package does implement a `Uint64()`
function in order to generate a 64 bit number out of two 32 bit numbers. Doing this
makes the generator only slightly faster than PCG, however,

Note that the approach to seeing with ISAAC is very important for best results,
and seeding with ISAAC is not as simple as seeding with a single uint64 value.
The ISAAC algorithm requires a 256-element seed. If used for cryptographic
purposes, this will likely require entropy generated off-chain for actual
cryptographically secure seeding. For other purposes, however, one can utilize
the built-in seeding mechanism, which will leverage the xorshiftr128plus PRNG to
generate any missing seeds if fewer than 256 are provided.


```
Benchmark
---------
PCG: 1000000 Uint64 generated in 15.58s
ISAAC: 1000000 Uint64 generated in 13.23s (uint64)
ISAAC: 1000000 Uint32 generated in 6.43s (uint32)
Ratio: x1.18 times faster than PCG (uint64)
Ratio: x2.42 times faster than PCG (uint32)
```

Use it directly:

```
prng = isaac.New() // pass 0 to 256 uint32 seeds; if fewer than 256 are provided, the rest
// will be generated using the xorshiftr128plus PRNG.
```

Or use it as a drop-in replacement for the default PRNT in Rand:

```
source = isaac.New()
prng := rand.New(source)
```

# TYPES

`
type ISAAC struct {
// Has unexported fields.
}
`

`func New(seeds ...uint32) *ISAAC`
ISAAC requires a large, 256-element seed. This implementation will leverage
the entropy package combined with the the xorshiftr128plus PRNG to generate
any missing seeds of fewer than the required number of arguments are
provided.

`func (isaac *ISAAC) MarshalBinary() ([]byte, error)`
MarshalBinary() returns a byte array that encodes the state of the PRNG.
This can later be used with UnmarshalBinary() to restore the state of the
PRNG. MarshalBinary implements the encoding.BinaryMarshaler interface.

`func (isaac *ISAAC) Seed(seed [256]uint32)`

`func (isaac *ISAAC) Uint32() uint32`

`func (isaac *ISAAC) Uint64() uint64`

`func (isaac *ISAAC) UnmarshalBinary(data []byte) error`
UnmarshalBinary() restores the state of the PRNG from a byte array
that was created with MarshalBinary(). UnmarshalBinary implements the
encoding.BinaryUnmarshaler interface.

7 changes: 7 additions & 0 deletions examples/gno.land/p/wyhaines/rand/isaac/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/p/wyhaines/rand/isaac

require (
gno.land/p/demo/entropy v0.0.0-latest
gno.land/p/demo/ufmt v0.0.0-latest
gno.land/p/wyhaines/rand/xorshiftr128plus v0.0.0-latest
)
Loading
Loading