pool_of_entropy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +7 -0
- data/DIEHARDER_TEST.md +156 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +151 -0
- data/Rakefile +10 -0
- data/lib/pool_of_entropy.rb +160 -0
- data/lib/pool_of_entropy/core_prng.rb +161 -0
- data/lib/pool_of_entropy/version.rb +3 -0
- data/pool_of_entropy.gemspec +26 -0
- data/spec/core_prng_spec.rb +541 -0
- data/spec/pool_of_entropy_spec.rb +362 -0
- data/spec/spec_helper.rb +7 -0
- metadata +134 -0
@@ -0,0 +1,362 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PoolOfEntropy do
|
4
|
+
describe "class methods" do
|
5
|
+
|
6
|
+
describe "#new" do
|
7
|
+
it "should instantiate a default object" do
|
8
|
+
pool = PoolOfEntropy.new
|
9
|
+
pool.should be_a PoolOfEntropy
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should allow setting number of blocks in pool" do
|
13
|
+
[1,3,5,8,13,21,34,55,89,144,233].each do |s|
|
14
|
+
pool = PoolOfEntropy.new( :size => 12 )
|
15
|
+
pool.should be_a PoolOfEntropy
|
16
|
+
num = pool.rand()
|
17
|
+
num.should be_a Float
|
18
|
+
num.should >= 0.0
|
19
|
+
num.should < 1.0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should fail with incorrect :size param" do
|
24
|
+
expect { PoolOfEntropy.new( :size => -12 ) }.to raise_error ArgumentError
|
25
|
+
expect { PoolOfEntropy.new( :size => -1 ) }.to raise_error ArgumentError
|
26
|
+
expect { PoolOfEntropy.new( :size => 0 ) }.to raise_error ArgumentError
|
27
|
+
expect { PoolOfEntropy.new( :size => 257 ) }.to raise_error ArgumentError
|
28
|
+
expect { PoolOfEntropy.new( :size => 1000 ) }.to raise_error ArgumentError
|
29
|
+
expect { PoolOfEntropy.new( :size => '' ) }.to raise_error ArgumentError
|
30
|
+
expect { PoolOfEntropy.new( :size => { :foo => 'bar' } ) }.to raise_error TypeError
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should default to unpredicatble internal state" do
|
34
|
+
pool = PoolOfEntropy.new()
|
35
|
+
pool.should be_a PoolOfEntropy
|
36
|
+
(10..20).map { |x| pool.rand(x) }.should_not == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should accept { :blank => false } as explicit statement of default" do
|
40
|
+
pool = PoolOfEntropy.new( :blank => false )
|
41
|
+
pool.should be_a PoolOfEntropy
|
42
|
+
(10..20).map { |x| pool.rand(x) }.should_not == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow an initial blank internal state" do
|
46
|
+
pool = PoolOfEntropy.new( :blank => true )
|
47
|
+
pool.should be_a PoolOfEntropy
|
48
|
+
(10..20).map { |x| pool.rand(x) }.should == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should accept and use :seed array" do
|
52
|
+
pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo'] )
|
53
|
+
pool.should be_a PoolOfEntropy
|
54
|
+
(10..20).map { |x| pool.rand(x) }.should == [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
|
55
|
+
|
56
|
+
pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo', 'bar'] )
|
57
|
+
pool.should be_a PoolOfEntropy
|
58
|
+
(10..20).map { |x| pool.rand(x) }.should == [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
describe "instance methods" do
|
65
|
+
pool_types = [
|
66
|
+
[
|
67
|
+
'default instance',
|
68
|
+
PoolOfEntropy.new
|
69
|
+
],
|
70
|
+
[
|
71
|
+
'instance with 2KB pool size',
|
72
|
+
PoolOfEntropy.new( :size => 32 )
|
73
|
+
],
|
74
|
+
[
|
75
|
+
'instance with 1KB pool size, blank start',
|
76
|
+
PoolOfEntropy.new( :size => 16, :blank => true )
|
77
|
+
],
|
78
|
+
[
|
79
|
+
'instance with default size, seeded',
|
80
|
+
PoolOfEntropy.new( :blank => true, :seeds => ['of change'] )
|
81
|
+
],
|
82
|
+
[
|
83
|
+
'instance cloned from another instance',
|
84
|
+
PoolOfEntropy.new( :size => 3 ).clone
|
85
|
+
],
|
86
|
+
[
|
87
|
+
'instance with maximum size, 16KB pool',
|
88
|
+
PoolOfEntropy.new( :size => 256, :blank => true, :seeds => ['dgeq','dsfsf','dsafsaf'] )
|
89
|
+
],
|
90
|
+
]
|
91
|
+
|
92
|
+
# NB "probability" and "randomness" tests in the following block are very light, just
|
93
|
+
# intended to capture high-level failures in logic. See DIEHARDER_TEST.md for thorough
|
94
|
+
# checks on statistical randomness of PoolOfEntropy::CorePRNG
|
95
|
+
pool_types.each do |pool_name, pool|
|
96
|
+
|
97
|
+
context "using #{pool_name}" do
|
98
|
+
before do
|
99
|
+
pool.clear_all_modifiers
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#clone" do
|
103
|
+
it "should return a deep copy with same state and modifiers" do
|
104
|
+
pool.modify_next( *((0..4).map {|i| "foo" + i.to_s} ) )
|
105
|
+
pool.modify_all( 'bar' )
|
106
|
+
|
107
|
+
pool_copy = pool.clone
|
108
|
+
100.times do
|
109
|
+
pool_copy.rand().should == pool.rand()
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#rand" do
|
115
|
+
|
116
|
+
context "with no param" do
|
117
|
+
it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
|
118
|
+
allow_any_instance_of( PoolOfEntropy::CorePRNG).
|
119
|
+
to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
|
120
|
+
20.times { pool.rand.should == 0.12106507972838931 }
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should return a Float between 0.0 and 1.0" do
|
124
|
+
100.times do
|
125
|
+
num = pool.rand
|
126
|
+
num.should be_a Float
|
127
|
+
num.should >= 0.0
|
128
|
+
num.should < 1.0
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should return a different Float each time (with high probability) " do
|
133
|
+
Set[ *(1..100).map{ pool.rand } ].size.should == 100
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "with an Integer param" do
|
138
|
+
it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
|
139
|
+
allow_any_instance_of( PoolOfEntropy::CorePRNG).
|
140
|
+
to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
|
141
|
+
20.times { pool.rand(20).should == 2 }
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should return an Integer between 0 and x (excluding x)" do
|
145
|
+
[ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ].each do |x|
|
146
|
+
100.times do
|
147
|
+
num = pool.rand( x )
|
148
|
+
num.should be_a Fixnum
|
149
|
+
num.should >= 0
|
150
|
+
num.should < x
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# This is a very weak test of randomness, see DIEHARDER_TEST.md
|
156
|
+
it "should select values without obvious bias" do
|
157
|
+
Set[ *(1..200).map{ pool.rand( 20 ) } ].size.should == 20
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should return a different Integer each time (with high probability for large x) " do
|
161
|
+
Set[ *(1..100).map{ pool.rand( 2**64 ) } ].size.should == 100
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context "with a Range param" do
|
166
|
+
it "should call PoolOfEntropy::CorePRNG::read_bytes internally" do
|
167
|
+
allow_any_instance_of( PoolOfEntropy::CorePRNG).
|
168
|
+
to receive( :read_bytes ).and_return( "\x1e\xfe" * 8 )
|
169
|
+
20.times { pool.rand( 7..28 ).should == 9 }
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should return an Integer that is a member of the range" do
|
173
|
+
[ 1..2, 2..5, 3..8, 5..13, 21..34, 55..89 ].each do |r|
|
174
|
+
100.times do
|
175
|
+
num = pool.rand( r )
|
176
|
+
num.should be_a Fixnum
|
177
|
+
num.should >= r.min
|
178
|
+
num.should <= r.max
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should return a different Integer each time (with high probability for large range) " do
|
184
|
+
Set[ *(1..100).map{ pool.rand( 100000000000..200000000000 ) } ].size.should == 100
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
describe "#modify_next" do
|
193
|
+
it "changes output value from next call to #rand" do
|
194
|
+
pool_copy = pool.clone
|
195
|
+
100.times do
|
196
|
+
modifier = SecureRandom.hex
|
197
|
+
pool_copy.modify_next( modifier ).rand.should_not == pool.rand
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it "changes output value consistently" do
|
202
|
+
pool_copy = pool.clone
|
203
|
+
100.times do
|
204
|
+
modifier = SecureRandom.hex
|
205
|
+
pool_copy.modify_next( modifier ).rand.should == pool.modify_next( modifier ).rand
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "changes next output value but not future ones" do
|
210
|
+
pool_copy = pool.clone
|
211
|
+
100.times do
|
212
|
+
modifier = SecureRandom.hex
|
213
|
+
pool_copy.modify_next( modifier ).rand.should_not == pool.rand
|
214
|
+
pool_copy.rand.should == pool.rand
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it "can create a 'queue' of modifiers, used in turn" do
|
219
|
+
pool_copy = pool.clone
|
220
|
+
10.times do
|
221
|
+
modifiers = (0..5).map { SecureRandom.hex }
|
222
|
+
|
223
|
+
# Syntax for all-at-once
|
224
|
+
pool_copy.modify_next( *modifiers )
|
225
|
+
modifiers.each do |modifier|
|
226
|
+
pool_copy.rand.should == pool.modify_next( modifier ).rand
|
227
|
+
end
|
228
|
+
# Assert we're back in sync without modifiers
|
229
|
+
pool_copy.rand.should == pool.rand
|
230
|
+
|
231
|
+
# Adding to queue one-at-a-time
|
232
|
+
modifiers.each do |modifier|
|
233
|
+
pool_copy.modify_next( modifier )
|
234
|
+
end
|
235
|
+
modifiers.each do |modifier|
|
236
|
+
pool_copy.rand.should == pool.modify_next( modifier ).rand
|
237
|
+
end
|
238
|
+
# Assert we're back in sync without modifiers
|
239
|
+
pool_copy.rand.should == pool.rand
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end # modify_next
|
243
|
+
|
244
|
+
describe "#modify_all" do
|
245
|
+
it "changes output value from future calls to #rand" do
|
246
|
+
pool_copy = pool.clone
|
247
|
+
10.times do
|
248
|
+
modifier = SecureRandom.hex
|
249
|
+
pool_copy.modify_all( modifier ).rand.should_not == pool.rand
|
250
|
+
10.times do
|
251
|
+
pool_copy.rand.should_not == pool.rand
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
it "changes output value consistently" do
|
257
|
+
pool_copy = pool.clone
|
258
|
+
10.times do
|
259
|
+
modifier = SecureRandom.hex
|
260
|
+
pool_copy.modify_all( modifier ).rand.should == pool.modify_all( modifier ).rand
|
261
|
+
10.times do
|
262
|
+
pool_copy.rand.should == pool.rand
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
it "changes output value consistently with #modify_next" do
|
268
|
+
pool_copy = pool.clone
|
269
|
+
100.times do
|
270
|
+
modifier = SecureRandom.hex
|
271
|
+
pool_copy.modify_all( modifier ).rand.should == pool.modify_next( modifier ).rand
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "can be reset wih nil modifier" do
|
276
|
+
pool_copy = pool.clone
|
277
|
+
10.times do
|
278
|
+
modifier = SecureRandom.hex
|
279
|
+
pool_copy.modify_all( modifier ).rand.should_not == pool.rand
|
280
|
+
pool_copy.rand.should_not == pool.rand
|
281
|
+
pool_copy.modify_all( nil )
|
282
|
+
10.times do
|
283
|
+
pool_copy.rand.should == pool.rand
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "#clear_all_modifiers" do
|
291
|
+
|
292
|
+
it "removes a queue of 'next' modifiers" do
|
293
|
+
pool_copy = pool.clone
|
294
|
+
10.times do
|
295
|
+
modifiers = (0..5).map { SecureRandom.hex }
|
296
|
+
pool_copy.modify_next( *modifiers )
|
297
|
+
pool_copy.rand.should_not == pool.rand
|
298
|
+
pool_copy.clear_all_modifiers
|
299
|
+
10.times do
|
300
|
+
pool_copy.rand.should == pool.rand
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
it "removes a current 'all' modifier" do
|
306
|
+
pool_copy = pool.clone
|
307
|
+
10.times do
|
308
|
+
modifier = SecureRandom.hex
|
309
|
+
pool_copy.modify_all( modifier ).rand.should_not == pool.rand
|
310
|
+
pool_copy.rand.should_not == pool.rand
|
311
|
+
pool_copy.clear_all_modifiers
|
312
|
+
10.times do
|
313
|
+
pool_copy.rand.should == pool.rand
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "removes both 'all' and 'next' modifiers in one go" do
|
319
|
+
pool_copy = pool.clone
|
320
|
+
10.times do
|
321
|
+
modifier = SecureRandom.hex
|
322
|
+
pool_copy.modify_next( *(0..5).map { SecureRandom.hex } )
|
323
|
+
pool_copy.modify_all( modifier ).rand.should_not == pool.rand
|
324
|
+
pool_copy.rand.should_not == pool.rand
|
325
|
+
pool_copy.clear_all_modifiers
|
326
|
+
10.times do
|
327
|
+
pool_copy.rand.should == pool.rand
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "#add_to_pool" do
|
334
|
+
|
335
|
+
it "alters all future output values" do
|
336
|
+
pool_copy = pool.clone
|
337
|
+
pool_copy.add_to_pool( 'Some user data!' )
|
338
|
+
100.times do
|
339
|
+
pool_copy.rand.should_not == pool.rand
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
it "alters all future output values consistently" do
|
344
|
+
pool_copy = pool.clone
|
345
|
+
|
346
|
+
10.times do
|
347
|
+
user_data = SecureRandom.hex
|
348
|
+
pool_copy.add_to_pool( user_data )
|
349
|
+
pool.add_to_pool( user_data )
|
350
|
+
|
351
|
+
10.times do
|
352
|
+
pool_copy.rand.should == pool.rand
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pool_of_entropy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Neil Slater
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yard
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.7.2
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.7.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.13.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.13.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.9.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.9.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: coveralls
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.7
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.7
|
83
|
+
description: PoolOfEntropy is a PRNG based on cryptographic secure PRNGs, intended
|
84
|
+
to bring back the feeling of 'personal luck' that some gamers feel when rolling
|
85
|
+
their own dice.
|
86
|
+
email:
|
87
|
+
- slobo777@gmail.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".travis.yml"
|
94
|
+
- DIEHARDER_TEST.md
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- lib/pool_of_entropy.rb
|
100
|
+
- lib/pool_of_entropy/core_prng.rb
|
101
|
+
- lib/pool_of_entropy/version.rb
|
102
|
+
- pool_of_entropy.gemspec
|
103
|
+
- spec/core_prng_spec.rb
|
104
|
+
- spec/pool_of_entropy_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
homepage: https://github.com/neilslater/pool_of_entropy
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 2.2.2
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Random number generator with extra features for gamers.
|
130
|
+
test_files:
|
131
|
+
- spec/core_prng_spec.rb
|
132
|
+
- spec/pool_of_entropy_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
has_rdoc:
|