pool_of_entropy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 455d13c29630cf9a7255e9d4f76d1ee8ac7dd32b
4
- data.tar.gz: 4b61829c13593ed3aa534d6ca462b04ca7139b66
3
+ metadata.gz: 4bfa3a0e0d42e79d2af5b13539692da02db0d33c
4
+ data.tar.gz: 97f2e7d2a51605afeb88dbf2c1560e8829b91595
5
5
  SHA512:
6
- metadata.gz: f14bf6fdfc0466312c67608ca9bbb966ce5fbef1166d690d8baef622d4311fa9d8fa473bf5f5d292fdef38da9e8e08a5f2ef39ca20de677d59a9bb8ba9e6233f
7
- data.tar.gz: acc6a94d9bd4f2e436d783489863fcf0e9bd97c3be5f25033b676aefdde9f6136f7e3c58bfd7013e845f3530a4cfa6aa718b3c9261683f88d1f68c972af959d2
6
+ metadata.gz: 2a67971cfcae21b459bd249154c7b3ab74ec94f36c7b08c146928196084840a074a83c01faad59fd4e8eb23be888e02a74945c9b01b77a2264e44d908723d5e9
7
+ data.tar.gz: 73db8b9f6f8d8c8db1873a4d3be54eecf41793f0b7a6ab60a3c8225a2526de41d7508c07e7844b1adb60479dd155fa647511a08a9488d3695bb89a9499cfce99
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ lib/**/*.rb
2
+ --exclude ext
3
+ --no-private
4
+ -
5
+ README.md
6
+ LICENSE.txt
7
+ DIEHARDER_TEST.md
data/DIEHARDER_TEST.md CHANGED
@@ -8,15 +8,28 @@ more than the target use of PoolOfEntropy. It is a thorough test
8
8
  for hidden bias, short cycles and patterns that may occur as faults
9
9
  in "weak" PRNGs. No test of randomness can be 100% certain though.
10
10
 
11
- The test PASS received implies PoolOfEntropy is free from a variety
11
+ All modern cryptographic PRNGs should be expected to pass Dieharder. Although
12
+ passing the test does not imply a secure random number generator, failing
13
+ it consistently implies an insceure one, that could be predicted.
14
+ Ruby's built-in rand(), an implementation of the Mersenne Twister algorithm,
15
+ should also pass.
16
+
17
+ Note that there are over 100 tests, and a test that scores a p-value below
18
+ 0.01 would be marked "WEAK". For a perfect PRNG it is reasonable to expect
19
+ maybe one to five "WEAK" results in a single run. It was just chance -
20
+ there is roughly a 32% chance that a perfect PRNG gets all Dieharder tests to
21
+ cleanly pass. The usual way to deal with WEAK results is to repeat the offending
22
+ tests and show that other p-values are just as likely.
23
+
24
+ Generally, when a PRNG fails a Dieharder test systematically, you see a
25
+ very strong signal, p-value below 0.00001 for several related tests.
26
+
27
+ The test report below implies PoolOfEntropy is free from a variety
12
28
  of detectable faults that would prevent it being used as a source of
13
- statistically random numbers. Not all PRNGs pass these tests -
14
- for instance, C's standard library rand() fails most of them.
15
- All modern cryptographic PRNGs should be expected to pass,
16
- as should Ruby's built-in rand() - an implementation of the Mersenne
17
- Twister algorithm.
29
+ statistically random numbers. As it happens, there were no "WEAK" results
30
+ this time.
18
31
 
19
- ## Date run: 9 May 2014
32
+ ## Date run: 9-12 May 2014
20
33
 
21
34
  Completing all the tests took roughly 3 days.
22
35
 
data/RATIONALE.md ADDED
@@ -0,0 +1,74 @@
1
+ # Rationale for PoolOfEntropy
2
+
3
+ ## Properties of Software Random Numbers
4
+
5
+ Software PRNGs available are designed to produce data that cannot be statistically separated
6
+ from an ideal unbiased "truly random" source. There are quite a few algorithms that can do that,
7
+ with differing degrees of success. Current best-in-class generators are pretty good at creating
8
+ psuedo random data that has no discernable pattern.
9
+
10
+ Generators used for games also need another trait - they need to be unpredictable to the end users.
11
+ Often that is not strictly true in the academic sense, for example a well-informed user with enough
12
+ time and skill could predict the next output from Ruby's rand() method. However, when this really
13
+ needs to be true, you can use a Crytogaphically Secure PRNG (CSPRNG). Ruby's SecureRandom cannot
14
+ be predicted from outside the system. Part of how CSPRNGs achieve unpredicatbility is by collecting
15
+ entropy from sources within the computer - this might be timing of events from network cards and
16
+ keyboard presses, or by sampling from deliberately noisy circuits.
17
+
18
+ ## Properties of Dice
19
+
20
+ A humble 6-sided die achieves similar statistics and unpredictability, but in a different way.
21
+ Unbiased statistical randomness is achieved by making the shape and density as regular as possible.
22
+ It also assumes the user has "rolled well enough", which is quite tricky to define, but obviously just
23
+ placing the die with the number you want facing up does not count.
24
+
25
+ Unpredictability for the physical die comes from lack of precise control. Imperfections and
26
+ microscopic details of where the die is rolling have an impact. Quantum mechanics, which
27
+ as far as we know is inherently probability-based, may also have an impact if a die collides
28
+ and bounces enough. Also, a big influence, mechanically and philosophically, is how the
29
+ die is thrown - and that comes from the person throwing it. No-one can control their nerves
30
+ and muscles to a degree where they "roll well enough" but can consciously choose the
31
+ exact result on the die. However, the impulse you give to a die when you throw it is caused
32
+ by your nerves, bones and muscles. This gives many people a feeling of agency and relationship
33
+ to the end result. It may just be a random number, but in some sense it is *your random
34
+ number because you generated it*.
35
+
36
+ ## Normal PRNGs Used To Simulate Dice
37
+
38
+ When it comes to finding computer-based sources of randomness, you will find many systems
39
+ that excel at producing results that are statistically random. Ruby's rand() is already
40
+ very good at that. Computer-based PRNGs can apparently be made closer to ideal unbiased
41
+ randomness than physical dice (or at least beyond any realistic ability to measure it).
42
+
43
+ Truly unpredictable sources are also easy enough to find. They make themselves unpredictable
44
+ by collecting entropy from sources on the machines where they run, that no-one can predict.
45
+
46
+ However, there has been a cost to the user's agency. If I was playing a game
47
+ using one of these sources, even though it was fair in the sense that the outcomes could
48
+ well be the same, it gives me the same feeling as if another player was rolling all the dice.
49
+ In a role-playing game, it feels the same as if the DM was rolling all the dice. Now sometimes
50
+ and for some (many/most?) people that's OK. But other times, part of the fun is in rolling
51
+ the dice yourself. I would be happy rolling computer dice, but only if somehow it was
52
+ "me" rolling them.
53
+
54
+ ## What PoolOfEntropy Attempts To Do
55
+
56
+ That is the mission of this gem: To create a simple PRNG where the results are connected as much
57
+ as possible to the end user. This has to be achieved without compromising the
58
+ good features of fairness and unpredictability that PRNGs and CSPRNGs have in general.
59
+
60
+ Luckily this can be achieved using an approach that many CSPRNGs already use - using a
61
+ data source to seed number generation. The main difference between PoolOfEntropy and
62
+ regular CSPRNGs used to protect your computer on the internet is how this "entropy" is sourced.
63
+ In a secure system, entropy is sourced from multiple places - anywhere that data can be
64
+ gathered that an imagined attacker will have a hard time guessing the value. In PoolOfEntropy
65
+ this is subverted - the end user supplies any data they like, and the gem treats it
66
+ as "entropy". Technically, if you were an attacker, this would not be called entropy at
67
+ all (because you know it) - however, to the machinery of the PRNG, or to me as a fellow
68
+ player in a dice game, it counts just fine.
69
+
70
+ By default PoolOfEntropy objects start off with some machine-collected entropy from SecureRandom
71
+ to avoid trivial attacks (of always using the dice in the exact same way). You could view this
72
+ as representing the environment or the die itself (all the scratches and imperfections that
73
+ you cannot control, and have no influence over). Or, under an honour system of not repeating
74
+ yourself you can switch off that default.
data/README.md CHANGED
@@ -12,9 +12,10 @@ to each die in a game, and it can be influenced (similar to throwing a die diffe
12
12
  personalised by feeding in arbitrary data (e.g. a picture of the player, a favourite saying).
13
13
  It can handle these influences whilst remaining unbiased and fair on each roll.
14
14
 
15
- PoolOfEntropy is *probably* secure when used appropriately. However, cryptographic security is
16
- not its purpose. The core purpose is for playing with random number generation and non-standard
17
- sources of entropy. The choice of name is supposed to reflect this.
15
+ PoolOfEntropy is *probably* secure when used appropriately, and in a very limited sense.
16
+ However, cryptographic security is not its purpose. The core purpose is for playing with
17
+ random number generation and non-standard sources of entropy. The choice of name is
18
+ supposed to reflect this.
18
19
 
19
20
  If you are looking for a secure PRNG in Ruby, good for generating session codes or
20
21
  server-side secrets, use the standard library SecureRandom.
@@ -43,108 +44,86 @@ Create a new generator:
43
44
 
44
45
  pool = PoolOfEntropy.new
45
46
 
46
- Get a random number:
47
+ Get a random number. Not feeding the generator with any customisation
48
+ means it is completely deterministic based on current internal state. The
49
+ analogy here might be "trusting to Fate":
47
50
 
48
51
  pool.rand( 20 )
49
52
 
50
- Influence the next random number (but not any others):
53
+ Influence the next random number (but not any others). This is analogous to
54
+ shaking or throwing dice in a certain way. Only the next result from rand()
55
+ is affected:
51
56
 
52
- pool.modify_next( 'I hope this works! Whatever!' )
57
+ pool.modify_next( 'Shake the die.' )
53
58
  pool.rand( 20 )
54
59
 
55
60
  # Also
56
- pool.modify_next( 'I hope this works! Whatever!' ).rand( 20 )
61
+ pool.modify_next( 'Shake the die.' ).rand( 20 )
57
62
 
58
- Influence all random numbers until change mind (but do not alter internal state):
63
+ # This next result will not be influenced,
64
+ # we re-join the deterministic sequence of the PRNG:
65
+ pool.rand
66
+
67
+ Influence the next three random numbers. Data supplied to modify_next is
68
+ put in a queue, first in, first out:
69
+
70
+ pool.modify_next( 'Shake the die lots.' )
71
+ pool.modify_next( 'Roll the die cautiously.' )
72
+ pool.modify_next( 'Drop the die from a great height and watch it bounce.' )
73
+
74
+ pool.rand # influenced by 'Shake the die lots.'
75
+ pool.rand # etc
76
+ pool.rand
77
+
78
+ pool.rand # . . . and back to main sequence
79
+
80
+ Influence all random numbers from this point forward. This is analogous to
81
+ having a personal style of throwing dice, or perhaps a different environment
82
+ to throw them in.
59
83
 
60
84
  pool.modify_all( 'Gawds help me in my hour of need!' )
61
85
 
62
- # All these are modified in same way, the two modifier types "stack"
86
+ # All these are modified in same way
63
87
  pool.rand( 20 )
64
88
  pool.rand( 20 )
89
+
90
+ # The two modifier types "stack", and this is modified twice
65
91
  pool.modify_next( 'And I really mean it!' ).rand( 20 )
66
92
 
67
- Alter internal state of pool (aka customise or "collect entropy"):
68
-
69
- pool.update_state( 'Purple is my favourite colour.' )
70
-
71
- ## Rationale
72
-
73
- ### Properties of Software Random Numbers
74
-
75
- Software PRNGs available are designed to produce data that cannot be statistically separated
76
- from an ideal unbiased "truly random" source. There are quite a few algorithms that can do that,
77
- with differing degrees of success. Current best-in-class generators are pretty good at creating
78
- psuedo random data that has no discernable pattern.
79
-
80
- Generators used for games also need another trait - they need to be unpredictable to the end users.
81
- Often that is not strictly true in the academic sense, for example a well-informed user with enough
82
- time and skill could predict the next output from Ruby's rand() method. However, when this really
83
- needs to be true, you can use a Crytogaphically Secure PRNG (CSPRNG). Ruby's SecureRandom cannot
84
- be predicted from outside the system. Part of how CSPRNGs achieve unpredicatbility is by collecting
85
- entropy from sources within the computer - this might be timing of events from network cards and
86
- keyboard presses, or by sampling from deliberately noisy circuits.
87
-
88
- ### Properties of Dice
89
-
90
- A humble 6-sided die achieves similar statistics and unpredictability, but in a different way.
91
- Unbiased statistical randomness is achieved by making the shape and density as regular as possible.
92
- It also assumes the user has "rolled well enough", which is quite tricky to define, but obviously just
93
- placing the die with the number you want facing up does not count.
94
-
95
- Unpredictability for the physical die comes from lack of precise control. Imperfections and
96
- microscopic details of where the die is rolling have a large impact. Quantum mechanics may
97
- also have an impact if a die collides and bounces enough. Also, a huge influence is how the
98
- die is thrown, and that comes from the person throwing it. No-one can control their nerves
99
- and muscles to a degree where they "roll well enough" but can consciously choose the
100
- exact result on the die. However, the impulse you give to a die when you throw it caused
101
- by your nerves, bones and muscles. This gives many people a feeling of agency and relationship
102
- to the end result. It may just be a random number, but in some sense it is *your random
103
- number because you generated it*.
104
-
105
- ### Normal PRNGs Used To Simulate Dice
106
-
107
- When it comes to finding computer-based sources of randomness, you will find many systems
108
- that excel at producing results that are statistically random. Ruby's rand() is already
109
- very good at that. Computer-based PRNGs can apparently be made closer to ideal unbiased
110
- randomness than physical dice (or at least beyond any realistic ability to measure it).
111
-
112
- Truly unpredictable sources are also easy enough to find. They make themselves unpredictable
113
- by collecting entropy from sources on the machines where they run, that no-one can predict.
114
-
115
- However, there has been a cost to the user's agency. If I was playing a game
116
- using one of these sources, even though it was fair in the sense that the outcomes could
117
- well be the same, it gives me the same feeling as if another player was rolling all the dice.
118
- In a role-playing game, it feels the same as if the DM was rolling all the dice. Now sometimes
119
- and for some (many/most?) people that's OK. But other times, part of the fun is in rolling
120
- the dice yourself. I would be happy rolling computer dice, but only if somehow it was
121
- "me" rolling them.
122
-
123
- ### What PoolOfEntropy Attempts To Do
124
-
125
- That is the mission of this gem: To create a simple PRNG where the results are connected as much
126
- as possible to the end user. This has to be achieved without compromising the
127
- good features of fairness and unpredictability that PRNGs and CSPRNGs have in general.
128
-
129
- Luckily this can be achieved using an approach that many CSPRNGs already use - using a
130
- data source to seed number generation. The main difference between PoolOfEntropy and
131
- regular CSPRNGs used to protect your computer on the internet is how this "entropy" is sourced.
132
- In a secure system, entropy is sourced from multiple places - anywhere that data can be
133
- gathered that an imagined attacker will have a hard time guessing the value. In PoolOfEntropy
134
- this is subverted - the end user supplies any data they like, and the gem treats it
135
- as "entropy". Technically, if you were an attacker, this would not be called entropy at
136
- all (because you know it) - however, to the machinery of the PRNG, or to me as a fellow
137
- player in a dice game, it counts just fine.
138
-
139
- By default PoolOfEntropy objects start off with some machine-collected entropy from SecureRandom
140
- to avoid trivial attacks (of always using the dice in the exact same way). You could view this
141
- as representing the environment or the die itself (all the scratches and imperfections that
142
- you cannot control, and have no influence over). Or, under an honour system of not repeating
143
- yourself you can switch off that default.
93
+ Remove modfiers:
94
+
95
+ # Just the "all" modifier
96
+ pool.modify_all( nil )
97
+
98
+ # Insert a pause into the "next" queue
99
+ pool.modify_next( nil )
100
+
101
+ # Re-set "next" and "all" modifiers
102
+ pool.clear_all_modifiers
103
+
104
+ Alter internal state of pool. This mixes in any entropy in the supplied
105
+ data, and changes the deterministic sequence going forward. This is
106
+ analogous to long-term alterations to dice, the environment, or
107
+ person throwing the dice.
108
+
109
+ pool.add_to_pool( 'Purple is my favourite colour.' )
110
+
111
+ All the inputs can be any length String, from any source. If the data
112
+ contains *any* "true randomness" (however you want to define it, and however
113
+ the String is formatted), then PoolOfEntropy
114
+ will process that (using SHA-512) into unbiased results. If you care
115
+ about your own source of randomness being more "important" than
116
+ the initial state of the PRNG, or its continued deterministic ticking,
117
+ then make use of the modifiers and/or add data to the pool frequently.
118
+
119
+ ## More information
120
+
121
+ * [Rationale](RATIONALE.md)
122
+ * [Dieharder test of statistical randomness](DIEHARDER_TEST.md)
144
123
 
145
124
  ## Contributing
146
125
 
147
- 1. Fork it ( http://github.com/<my-github-username>/pool_of_entropy/fork )
126
+ 1. Fork it ( http://github.com/neilslater/pool_of_entropy/fork )
148
127
  2. Create your feature branch (`git checkout -b my-new-feature`)
149
128
  3. Commit your changes (`git commit -am 'Add some feature'`)
150
129
  4. Push to the branch (`git push origin my-new-feature`)
@@ -35,30 +35,13 @@ class PoolOfEntropy
35
35
  raise TypeError, "Expecting an options hash, got #{options.inspect}"
36
36
  end
37
37
 
38
- size = 1
39
- if options[:size]
40
- size = Integer( options[:size] )
41
- if size < 1 || size > 256
42
- raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
43
- end
44
- end
38
+ size = size_from_options( options )
45
39
 
46
- initial_state = if options[:blank]
47
- "\x0" * size * 64
48
- else
49
- SecureRandom.random_bytes( size * 64 )
50
- end
40
+ initial_state = state_from_options( options, size )
51
41
 
52
42
  @core_prng = CorePRNG.new( size, initial_state )
53
43
 
54
- if options[:seeds]
55
- unless options[:seeds].is_a? Array
56
- raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
57
- end
58
- options[:seeds].each do |seed|
59
- add_to_pool( seed )
60
- end
61
- end
44
+ seed_from_options( options )
62
45
 
63
46
  @next_modifier_queue = []
64
47
  @fixed_modifier = nil
@@ -157,4 +140,33 @@ class PoolOfEntropy
157
140
  @core_prng.generate_integer( max, *use_adjustments )
158
141
  end
159
142
 
143
+ def state_from_options( options, size )
144
+ if options[:blank]
145
+ "\x0" * size * 64
146
+ else
147
+ SecureRandom.random_bytes( size * 64 )
148
+ end
149
+ end
150
+
151
+ def size_from_options( options )
152
+ size = 1
153
+ if options[:size]
154
+ size = Integer( options[:size] )
155
+ if size < 1 || size > 256
156
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
157
+ end
158
+ end
159
+ size
160
+ end
161
+
162
+ def seed_from_options( options )
163
+ if options[:seeds]
164
+ unless options[:seeds].is_a? Array
165
+ raise TypeError, "Expected value for :seeds to be an Array, got #{options[:seeds].inspect}"
166
+ end
167
+ options[:seeds].each do |seed|
168
+ add_to_pool( seed )
169
+ end
170
+ end
171
+ end
160
172
  end
@@ -33,20 +33,8 @@ class PoolOfEntropy::CorePRNG
33
33
  # @param [Integer] mix_block_id
34
34
  # @return [PoolOfEntropy::CorePRNG]
35
35
  def initialize size = 1, initial_state = SecureRandom.random_bytes( 64 * Integer(size) ), mix_block_id = 0
36
- @size = Integer( size )
37
- if @size < 1 || @size > 256
38
- raise ArgumentError, "Size of pool must be in Range 1..256, got #{@size}"
39
- end
40
- unless initial_state.is_a? String
41
- raise TypeError, "Initial state must be a String, got #{initial_state.inspect}"
42
- end
43
- @state = initial_state.clone
44
- @state.force_encoding( 'BINARY' )
45
-
46
- if @state.size != size * 64
47
- raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{@state.size} bytes"
48
- end
49
-
36
+ @size = validate_size( size )
37
+ @state = validate_state( initial_state, size )
50
38
  @mix_block_id = Integer( mix_block_id ) % @size
51
39
  end
52
40
 
@@ -129,7 +117,6 @@ class PoolOfEntropy::CorePRNG
129
117
  def generate_integer top, *adjustments
130
118
  power = 1
131
119
  sum = 0
132
- lower_bound = 0
133
120
  words = []
134
121
 
135
122
  loop do
@@ -137,11 +124,8 @@ class PoolOfEntropy::CorePRNG
137
124
  sum = 2**32 * sum + words.shift
138
125
  power *= 2**32
139
126
  lower_bound = sum * top / power
140
- upper_bound = ( (sum + 1) * top ) / power
141
- break if lower_bound == upper_bound
127
+ break lower_bound if lower_bound == ( (sum + 1) * top ) / power
142
128
  end
143
-
144
- lower_bound
145
129
  end
146
130
 
147
131
  private
@@ -158,4 +142,24 @@ class PoolOfEntropy::CorePRNG
158
142
  folded_32bits.pack('L>*')
159
143
  end
160
144
 
145
+ def validate_size i
146
+ size = Integer( i )
147
+ if size < 1 || size > 256
148
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}"
149
+ end
150
+ size
151
+ end
152
+
153
+ def validate_state( initial_state, size )
154
+ unless initial_state.is_a? String
155
+ raise TypeError, "Initial state must be a String, got #{initial_state.inspect}"
156
+ end
157
+ state = initial_state.clone
158
+ state.force_encoding( 'BINARY' )
159
+
160
+ if state.size != size * 64
161
+ raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
162
+ end
163
+ state
164
+ end
161
165
  end
@@ -1,3 +1,3 @@
1
1
  class PoolOfEntropy
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -35,6 +35,8 @@ describe PoolOfEntropy::CorePRNG do
35
35
  end
36
36
 
37
37
  it "should fail with bad state data" do
38
+ expect { PoolOfEntropy::CorePRNG.new( 1, :boo ) }.to raise_error TypeError
39
+ expect { PoolOfEntropy::CorePRNG.new( 1, [] ) }.to raise_error TypeError
38
40
  expect { PoolOfEntropy::CorePRNG.new( 1, '' ) }.to raise_error ArgumentError
39
41
  expect { PoolOfEntropy::CorePRNG.new( 1, "\x0" * 63 ) }.to raise_error ArgumentError
40
42
  expect { PoolOfEntropy::CorePRNG.new( 1, "\x0" * 200 ) }.to raise_error ArgumentError
@@ -20,6 +20,12 @@ describe PoolOfEntropy do
20
20
  end
21
21
  end
22
22
 
23
+ it "should fail when param is not a hash" do
24
+ expect { PoolOfEntropy.new( [:size,12] ) }.to raise_error TypeError
25
+ expect { PoolOfEntropy.new( '' ) }.to raise_error TypeError
26
+ expect { PoolOfEntropy.new( :size ) }.to raise_error TypeError
27
+ end
28
+
23
29
  it "should fail with incorrect :size param" do
24
30
  expect { PoolOfEntropy.new( :size => -12 ) }.to raise_error ArgumentError
25
31
  expect { PoolOfEntropy.new( :size => -1 ) }.to raise_error ArgumentError
@@ -48,7 +54,7 @@ describe PoolOfEntropy do
48
54
  (10..20).map { |x| pool.rand(x) }.should == [8, 3, 0, 12, 11, 2, 4, 8, 1, 11, 18]
49
55
  end
50
56
 
51
- it "should accept and use :seed array" do
57
+ it "should accept and use :seeds array" do
52
58
  pool = PoolOfEntropy.new( :blank => true, :seeds => ['foo'] )
53
59
  pool.should be_a PoolOfEntropy
54
60
  (10..20).map { |x| pool.rand(x) }.should == [9, 1, 3, 7, 8, 12, 14, 5, 11, 5, 6]
@@ -57,10 +63,15 @@ describe PoolOfEntropy do
57
63
  pool.should be_a PoolOfEntropy
58
64
  (10..20).map { |x| pool.rand(x) }.should == [8, 1, 5, 8, 2, 7, 11, 8, 8, 13, 14]
59
65
  end
66
+
67
+ it "should fail if :seeds param is not an array" do
68
+ expect { PoolOfEntropy.new( :seeds => -12 ) }.to raise_error TypeError
69
+ expect { PoolOfEntropy.new( :seeds => { :seeds => [2] } ) }.to raise_error TypeError
70
+ expect { PoolOfEntropy.new( :seeds => 'more_seeds' ) }.to raise_error TypeError
71
+ end
60
72
  end
61
73
  end
62
74
 
63
-
64
75
  describe "instance methods" do
65
76
  pool_types = [
66
77
  [
@@ -239,6 +250,18 @@ describe PoolOfEntropy do
239
250
  pool_copy.rand.should == pool.rand
240
251
  end
241
252
  end
253
+
254
+ it "treats a nil modifier as 'do not modify'" do
255
+ pool_copy = pool.clone
256
+ 10.times do
257
+ pool_copy.modify_next( 'hello', nil, 'goodbye' )
258
+ pool_copy.rand.should_not == pool.rand
259
+ pool_copy.rand.should == pool.rand
260
+ pool_copy.rand.should_not == pool.rand
261
+ pool_copy.rand.should == pool.rand
262
+ pool_copy.rand.should == pool.rand
263
+ end
264
+ end
242
265
  end # modify_next
243
266
 
244
267
  describe "#modify_all" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pool_of_entropy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Slater
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-13 00:00:00.000000000 Z
11
+ date: 2014-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yard
@@ -91,9 +91,11 @@ extra_rdoc_files: []
91
91
  files:
92
92
  - ".gitignore"
93
93
  - ".travis.yml"
94
+ - ".yardopts"
94
95
  - DIEHARDER_TEST.md
95
96
  - Gemfile
96
97
  - LICENSE.txt
98
+ - RATIONALE.md
97
99
  - README.md
98
100
  - Rakefile
99
101
  - lib/pool_of_entropy.rb