pool_of_entropy 0.0.1 → 0.0.2

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 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