pool_of_entropy 0.0.3 → 0.0.5

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
- SHA1:
3
- metadata.gz: 9282759abb1c13fd70a45b27da5f58030107bc23
4
- data.tar.gz: 88da59e58548d584b72df4640be40be61aef9b37
2
+ SHA256:
3
+ metadata.gz: 4cf2426e234e3234f458defe0d59aadda7b95e08b3161a3edc5c79fed8f829dc
4
+ data.tar.gz: 5573c75b6a5a565296c4271c1e32acc0e14989996bc3b85f660f94072f0e7d38
5
5
  SHA512:
6
- metadata.gz: b158c97ffc614e6f40c5f769812a5f1441877e96c429e525875639c18f3c1a6f8699341bf81c062981e7eac2a2e986120718029facdb8bfe135e5ad8fc140d7f
7
- data.tar.gz: c68191705fce736800291804d24d30c46e74be13906cde6cae5e46983ae1afaeaeb6d72aa5b4e4cb72110b06d2d9751b32034c3e2424381378f13d8fdf08518a
6
+ metadata.gz: 6bb69625454353f670b3fe6e12a8cf8d70b2bf11aeeb145e6d3fea93a3a967f8bccb0a985bc3060b6c856a7d794bcc83a9c206f87a2b10f99ccc2af989b95c37
7
+ data.tar.gz: e03b6919f0ca301e74c3c7255a4e8302381f763e04f199dcc144d5e0a3601b560b9bdd70769e62dd74625e2d3e481b7eb9185c913814c7c29d4508284ea65cd0
@@ -0,0 +1,23 @@
1
+ name: RSpec
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ test:
8
+ runs-on: ubuntu-latest
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby-version:
13
+ - '3.3'
14
+ - '3.4'
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - run: bundle exec rubocop
23
+ - run: bundle exec rspec
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ plugins:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 3.3
7
+ NewCops: enable
8
+
9
+ Layout/LineLength:
10
+ Max: 120
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - spec/**/*
15
+ AllowedMethods:
16
+ - context
17
+ - describe
18
+ - shared_examples
data/Gemfile CHANGED
@@ -1,4 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- # Dependencies are in pool_of_entropy.gemspec
4
5
  gemspec
6
+
7
+ gem 'bundler', '>= 2.5', '< 5'
8
+ gem 'rake', '~> 13.2'
9
+ gem 'rspec', '~> 3.13'
10
+ gem 'rubocop', '~> 1.88'
11
+ gem 'rubocop-rake', '~> 0.7'
12
+ gem 'rubocop-rspec', '~> 3.10'
13
+ gem 'simplecov', '~> 1.0'
14
+ gem 'yard', '~> 0.9.37'
data/README.md CHANGED
@@ -1,9 +1,5 @@
1
1
  # PoolOfEntropy
2
2
  [![Gem Version](https://badge.fury.io/rb/pool_of_entropy.png)](http://badge.fury.io/rb/pool_of_entropy)
3
- [![Build Status](https://travis-ci.org/neilslater/pool_of_entropy.png?branch=master)](http://travis-ci.org/neilslater/pool_of_entropy)
4
- [![Coverage Status](https://coveralls.io/repos/neilslater/pool_of_entropy/badge.png?branch=master)](https://coveralls.io/r/neilslater/pool_of_entropy?branch=master)
5
- [![Code Climate](https://codeclimate.com/github/neilslater/pool_of_entropy.png)](https://codeclimate.com/github/neilslater/pool_of_entropy)
6
- [![Dependency Status](https://gemnasium.com/neilslater/pool_of_entropy.png)](https://gemnasium.com/neilslater/pool_of_entropy)
7
3
 
8
4
  PoolOfEntropy is a pseudo random number generator (PRNG) based on secure hashes,
9
5
  intended to bring back the feeling of 'personal agency' that some gamers may feel when rolling
data/Rakefile CHANGED
@@ -1,10 +1,12 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
3
2
 
4
- desc "PoolOfEntropy unit tests"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ desc 'PoolOfEntropy unit tests'
5
7
  RSpec::Core::RakeTask.new(:test) do |t|
6
- t.pattern = "spec/*_spec.rb"
8
+ t.pattern = 'spec/**/*_spec.rb'
7
9
  t.verbose = false
8
10
  end
9
11
 
10
- task :default => [:test]
12
+ task default: [:test]
@@ -1,165 +1,171 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest/sha2'
2
4
  require 'securerandom'
3
5
 
4
- # This class implements a random number generator based on SHA-512
5
- #
6
- # An object of the class has internal state that is modified on each call to
7
- # #update or any #read_... method (internally the #read_... methods call #update).
8
- # The #read_... methods generate a pseudo-random number from the current
9
- # state pool using SHA-512 and use it in the return value. It is not feasible to
10
- # determine the internal state from the pseudo-random data received, and not
11
- # possible to manipulate results from the PRNG in a predictable manner without
12
- # knowing the internal state.
13
- #
14
- # @example Using default internal state, initialised using SecureRandom.random_bytes
15
- # prng = PoolOfEntropy::CorePRNG.new
16
- # prng.read_bytes
17
- # # E.g. => "]\x12\x9E\xF5\x17\xF3\xC2\x1A\x15\xDFu]\x95\nd\x12"
18
- # prng.read_hex
19
- # # E.g. => "a0e00d2848242ec49e0a15ef411ba647"
20
- # prng.read_bignum
21
- # # E.g. => 33857278877368906880463811096418580004
22
- # prng.read_float
23
- # # E.g. => 0.6619838265836278
24
- # prng.generate_integer( 20 )
25
- # # E.g. => 7
26
- #
27
-
28
- class PoolOfEntropy::CorePRNG
29
-
30
- # Creates a new random number source. All parameters are optional.
31
- # @param [Integer] size Number of 64-byte blocks, between 1 and 256 (16KB)
32
- # @param [String] initial_state Sets contents of state pool (default uses SecureRandom)
33
- # @param [Integer] mix_block_id
34
- # @return [PoolOfEntropy::CorePRNG]
35
- def initialize size = 1, initial_state = SecureRandom.random_bytes( 64 * Integer(size) ), mix_block_id = 0
36
- @size = validate_size( size )
37
- @state = validate_state( initial_state, size )
38
- @mix_block_id = Integer( mix_block_id ) % @size
39
- end
40
-
41
- # The number of 64-byte blocks used in the internal state pool.
42
- # @return [Integer]
43
- attr_reader :size
44
-
45
- # Identifies the next 64-byte block in the pool that will be altered
46
- # by a read or update process.
47
- # @return [Integer]
48
- attr_reader :mix_block_id
49
-
50
- # A clone of the internal state pool. In combination with #mix_block_id, describes
51
- # the whole PRNG. If this value is supplied to an end user, then they can easily
52
- # predict future values of the PRNG.
53
- # @return [PoolOfEntropy::CorePRNG]
54
- def state
55
- @state.clone
56
- end
57
-
58
- # The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
59
- # @return [PoolOfEntropy::CorePRNG]
60
- def clone
61
- PoolOfEntropy::CorePRNG.new( self.size, self.state, self.mix_block_id )
62
- end
6
+ class PoolOfEntropy
7
+ # This class implements a random number generator based on SHA-512
8
+ #
9
+ # An object of the class has internal state that is modified on each call to
10
+ # #update or any #read_... method (internally the #read_... methods call #update).
11
+ # The #read_... methods generate a pseudo-random number from the current
12
+ # state pool using SHA-512 and use it in the return value. It is not feasible to
13
+ # determine the internal state from the pseudo-random data received, and not
14
+ # possible to manipulate results from the PRNG in a predictable manner without
15
+ # knowing the internal state.
16
+ #
17
+ # @example Using default internal state, initialised using SecureRandom.random_bytes
18
+ # prng = PoolOfEntropy::CorePRNG.new
19
+ # prng.read_bytes
20
+ # # E.g. => "]\x12\x9E\xF5\x17\xF3\xC2\x1A\x15\xDFu]\x95\nd\x12"
21
+ # prng.read_hex
22
+ # # E.g. => "a0e00d2848242ec49e0a15ef411ba647"
23
+ # prng.read_bignum
24
+ # # E.g. => 33857278877368906880463811096418580004
25
+ # prng.read_float
26
+ # # E.g. => 0.6619838265836278
27
+ # prng.generate_integer( 20 )
28
+ # # E.g. => 7
29
+ #
30
+ class CorePRNG
31
+ # Creates a new random number source. All parameters are optional.
32
+ # @param [Integer] size Number of 64-byte blocks, between 1 and 256 (16KB)
33
+ # @param [String] initial_state Sets contents of state pool (default uses SecureRandom)
34
+ # @param [Integer] mix_block_id
35
+ # @return [PoolOfEntropy::CorePRNG]
36
+ def initialize(size = 1, initial_state = SecureRandom.random_bytes(64 * Integer(size)), mix_block_id = 0)
37
+ @size = validate_size(size)
38
+ @state = validate_state(initial_state, size)
39
+ @mix_block_id = Integer(mix_block_id) % @size
40
+ end
63
41
 
64
- # Mixes supplied data into the curent state. This is called
65
- # internally by #read_... methods as well.
66
- # @param [String] data Data to be mixed. Note empty string '' and nil are equivalent and *do* change the state.
67
- # @return [nil]
68
- def update data
69
- new_block = Digest::SHA512.digest( @state + data.to_s )
70
- @state[64*@mix_block_id,64] = new_block
71
- @mix_block_id = (@mix_block_id + 1) % @size
72
- nil
73
- end
42
+ # The number of 64-byte blocks used in the internal state pool.
43
+ # @return [Integer]
44
+ attr_reader :size
45
+
46
+ # Identifies the next 64-byte block in the pool that will be altered
47
+ # by a read or update process.
48
+ # @return [Integer]
49
+ attr_reader :mix_block_id
50
+
51
+ # A clone of the internal state pool. In combination with #mix_block_id, describes
52
+ # the whole PRNG. If this value is supplied to an end user, then they can easily
53
+ # predict future values of the PRNG.
54
+ # @return [PoolOfEntropy::CorePRNG]
55
+ def state
56
+ @state.clone
57
+ end
74
58
 
75
- # Statistically flat distribution of 128 bits (16 bytes)
76
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
77
- # @return [String] 16 characters in ASCII-8BIT encoding
78
- def read_bytes *adjustments
79
- raw_digest = Digest::SHA512.digest( @state )
80
- self.update( raw_digest )
81
- adjustments.compact.each do |adjust|
82
- raw_digest = Digest::SHA512.digest( raw_digest + adjust )
59
+ # The clone of a PoolOfEntropy::CorePRNG object includes separate copy of internal state
60
+ # @return [PoolOfEntropy::CorePRNG]
61
+ def clone
62
+ PoolOfEntropy::CorePRNG.new(size, state, mix_block_id)
83
63
  end
84
- fold_bits( fold_bits( raw_digest ) )
85
- end
86
64
 
87
- # Statistically flat distribution of 32 hex digits
88
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
89
- # @return [String] 32 hex digits
90
- def read_hex *adjustments
91
- read_bytes( *adjustments ).unpack('H*').first
92
- end
65
+ # Mixes supplied data into the curent state. This is called
66
+ # internally by #read_... methods as well.
67
+ # @param [String] data Data to be mixed. Note empty string '' and nil are equivalent and *do* change the state.
68
+ # @return [nil]
69
+ def update(data)
70
+ new_block = Digest::SHA512.digest(@state + data.to_s)
71
+ @state[64 * @mix_block_id, 64] = new_block
72
+ @mix_block_id = (@mix_block_id + 1) % @size
73
+ nil
74
+ end
93
75
 
94
- # Statistically flat distribution from range 0...2**128
95
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
96
- # @return [Bignum,Fixnum] between 0 and 0xffffffffffffffffffffffffffffffff
97
- def read_bignum *adjustments
98
- nums = read_bytes( *adjustments ).unpack('Q>*')
99
- nums.inject(0) { |sum,v| (sum << 64) + v }
100
- end
76
+ # Statistically flat distribution of 128 bits (16 bytes)
77
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
78
+ # the return value, but not internal state
79
+ # @return [String] 16 characters in ASCII-8BIT encoding
80
+ def read_bytes(*adjustments)
81
+ raw_digest = Digest::SHA512.digest(@state)
82
+ update(raw_digest)
83
+ adjustments.compact.each do |adjust|
84
+ raw_digest = Digest::SHA512.digest(raw_digest + adjust)
85
+ end
86
+ fold_bits(fold_bits(raw_digest))
87
+ end
101
88
 
102
- # Statistically flat distribution from interval 0.0...1.0, with 53-bit precision
103
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
104
- # @return [Float] between 0.0 and 0.9999999999999999
105
- def read_float *adjustments
106
- num = read_bytes( *adjustments ).unpack('Q>*').first >> 11
107
- num.to_f / 2 ** 53
108
- end
89
+ # Statistically flat distribution of 32 hex digits
90
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
91
+ # the return value, but not internal state
92
+ # @return [String] 32 hex digits
93
+ def read_hex(*adjustments)
94
+ read_bytes(*adjustments).unpack1('H*')
95
+ end
109
96
 
110
- # Statistically flat distribution from range (0...top).
111
- # If necessary, it will read more data to ensure absolute fairness. This method
112
- # can generate an unbiased distribution of Bignums up to roughly half the maximum bit size
113
- # allowed by Ruby (i.e. much larger than 2**128 generated in a single read)
114
- # @param [Fixnum,Bignum] top upper bound of distribution, not inclusive
115
- # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect return value, but not internal state
116
- # @return [Fixnum,Bignum] between 0 and top-1 inclusive
117
- def generate_integer top, *adjustments
118
- power = 1
119
- sum = 0
120
- words = []
121
-
122
- loop do
123
- words = read_bytes( *adjustments ).unpack('L>*') if words.empty?
124
- sum = 2**32 * sum + words.shift
125
- power *= 2**32
126
- lower_bound = sum * top / power
127
- break lower_bound if lower_bound == ( (sum + 1) * top ) / power
97
+ # Statistically flat distribution from range 0...2**128
98
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
99
+ # the return value, but not internal state
100
+ # @return [Bignum,Fixnum] between 0 and 0xffffffffffffffffffffffffffffffff
101
+ def read_bignum(*adjustments)
102
+ nums = read_bytes(*adjustments).unpack('Q>*')
103
+ nums.inject(0) { |sum, v| (sum << 64) + v }
128
104
  end
129
- end
130
105
 
131
- private
106
+ # Statistically flat distribution from interval 0.0...1.0, with 53-bit precision
107
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
108
+ # the return value, but not internal state
109
+ # @return [Float] between 0.0 and 0.9999999999999999
110
+ def read_float(*adjustments)
111
+ num = read_bytes(*adjustments).unpack1('Q>*') >> 11
112
+ num.to_f / (2**53)
113
+ end
132
114
 
133
- # Xors first half of a message with second half. Only works for messages
134
- # which are multiples of 8 bytes long.
135
- # @param [String] msg bytes to fold
136
- # @return [String] folded message, half the length of original
137
- def fold_bits msg
138
- l = msg.length/2
139
- folded_32bits = msg[0,l].unpack('L>*').zip( msg[l,l].unpack('L>*') ).map do |x,y|
140
- x ^ y
115
+ # Statistically flat distribution from range (0...top).
116
+ # If necessary, it will read more data to ensure absolute fairness. This method
117
+ # can generate an unbiased distribution of Bignums up to roughly half the maximum bit size
118
+ # allowed by Ruby (i.e. much larger than 2**128 generated in a single read)
119
+ # @param [Fixnum,Bignum] top upper bound of distribution, not inclusive
120
+ # @param [Array<String>] adjustments mixed in using SHA-512, so that they affect
121
+ # the return value, but not internal state
122
+ # @return [Fixnum,Bignum] between 0 and top-1 inclusive
123
+ def generate_integer(top, *adjustments)
124
+ power = 1
125
+ sum = 0
126
+ words = []
127
+
128
+ loop do
129
+ words = read_bytes(*adjustments).unpack('L>*') if words.empty?
130
+ sum = ((2**32) * sum) + words.shift
131
+ power *= 2**32
132
+ lower_bound = sum * top / power
133
+ break lower_bound if lower_bound == ((sum + 1) * top) / power
134
+ end
141
135
  end
142
- folded_32bits.pack('L>*')
143
- end
144
136
 
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}"
137
+ private
138
+
139
+ # Xors first half of a message with second half. Only works for messages
140
+ # which are multiples of 8 bytes long.
141
+ # @param [String] msg bytes to fold
142
+ # @return [String] folded message, half the length of original
143
+ def fold_bits(msg)
144
+ l = msg.length / 2
145
+ folded_32bits = msg[0, l].unpack('L>*').zip(msg[l, l].unpack('L>*')).map do |x, y|
146
+ x ^ y
147
+ end
148
+ folded_32bits.pack('L>*')
149
149
  end
150
- size
151
- end
152
150
 
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}"
151
+ def validate_size(value)
152
+ size = Integer(value)
153
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}" if size < 1 || size > 256
154
+
155
+ size
156
156
  end
157
- state = initial_state.clone
158
- state.force_encoding( 'BINARY' )
159
157
 
160
- if state.size != size * 64
161
- raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
158
+ def validate_state(initial_state, size)
159
+ raise TypeError, "Initial state must be a String, got #{initial_state.inspect}" unless initial_state.is_a? String
160
+
161
+ state = initial_state.clone
162
+ state.force_encoding('BINARY')
163
+
164
+ if state.size != size * 64
165
+ raise ArgumentError, "Initial state bad size - expected #{size * 64} bytes, got #{state.size} bytes"
166
+ end
167
+
168
+ state
162
169
  end
163
- state
164
170
  end
165
171
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PoolOfEntropy
2
- VERSION = "0.0.3"
4
+ VERSION = '0.0.5'
3
5
  end
@@ -1,5 +1,7 @@
1
- require "pool_of_entropy/version"
2
- require "pool_of_entropy/core_prng"
1
+ # frozen_string_literal: true
2
+
3
+ require 'pool_of_entropy/version'
4
+ require 'pool_of_entropy/core_prng'
3
5
 
4
6
  # This class models a random number generator that can mix user input into
5
7
  # the generation mechanism in a few different ways.
@@ -20,28 +22,23 @@ require "pool_of_entropy/core_prng"
20
22
  # # E.g. => 12
21
23
  # sleep 5
22
24
  # end
23
- #
24
-
25
25
  class PoolOfEntropy
26
-
27
26
  # Creates a new random number source. All parameters are optional.
28
27
  # @param [Hash] options
29
28
  # @option options [Integer] :size, number of 512-bit (64 byte) blocks to use as internal state, defaults to 1
30
29
  # @option options [Boolean] :blank, if true then initial state is all zeroes, otherwise use SecureRandom
31
30
  # @option options [Array<String>] :seeds, if provided these are sent to #add_to_pool during initialize
32
31
  # @return [PoolOfEntropy]
33
- def initialize options = {}
34
- unless options.is_a? Hash
35
- raise TypeError, "Expecting an options hash, got #{options.inspect}"
36
- end
32
+ def initialize(options = {})
33
+ raise TypeError, "Expecting an options hash, got #{options.inspect}" unless options.is_a? Hash
37
34
 
38
- size = size_from_options( options )
35
+ size = size_from_options(options)
39
36
 
40
- initial_state = state_from_options( options, size )
37
+ initial_state = state_from_options(options, size)
41
38
 
42
- @core_prng = CorePRNG.new( size, initial_state )
39
+ @core_prng = CorePRNG.new(size, initial_state)
43
40
 
44
- seed_from_options( options )
41
+ seed_from_options(options)
45
42
 
46
43
  @next_modifier_queue = []
47
44
  @fixed_modifier = nil
@@ -50,7 +47,11 @@ class PoolOfEntropy
50
47
  # Cloning creates a deep copy with identical PRNG state and modifiers
51
48
  # @return [PoolOfEntropy]
52
49
  def clone
53
- Marshal.load( Marshal.dump( self ) )
50
+ copy = super
51
+ copy.instance_variable_set(:@core_prng, @core_prng.clone)
52
+ copy.instance_variable_set(:@fixed_modifier, @fixed_modifier.clone) if @fixed_modifier
53
+ copy.instance_variable_set(:@next_modifier_queue, @next_modifier_queue.map(&:clone))
54
+ copy
54
55
  end
55
56
 
56
57
  # Same functionality as Kernel#rand or Random#rand, but using
@@ -58,19 +59,14 @@ class PoolOfEntropy
58
59
  # two modifiers that are in effect.
59
60
  # @param [Integer,Range] max if 0 then will return a Float
60
61
  # @return [Float,Fixnum,Bignum] type depends on value of max
61
- def rand max = 0
62
- if max.is_a? Range
63
- bottom = max.first
64
- top = max.last
65
- return( nil ) if top < bottom
66
- return bottom + generate_integer( ( top - bottom + 1 ) )
62
+ def rand(max = 0)
63
+ return rand_from_range(max) if max.is_a? Range
64
+
65
+ effective_max = max.to_i.abs
66
+ if effective_max.zero?
67
+ generate_float
67
68
  else
68
- effective_max = max.to_i.abs
69
- if effective_max == 0
70
- return generate_float
71
- else
72
- return generate_integer( effective_max )
73
- end
69
+ generate_integer(effective_max)
74
70
  end
75
71
  end
76
72
 
@@ -81,13 +77,13 @@ class PoolOfEntropy
81
77
  # affect the internal state of the data pool used by the generator.
82
78
  # @param [Array<String>] modifiers
83
79
  # @return [PoolOfEntropy] self
84
- def modify_next *modifiers
80
+ def modify_next(*modifiers)
85
81
  modifiers.each do |modifier|
86
- if modifier.nil?
87
- @next_modifier_queue << nil
88
- else
89
- @next_modifier_queue << Digest::SHA512.digest( modifier.to_s )
90
- end
82
+ @next_modifier_queue << if modifier.nil?
83
+ nil
84
+ else
85
+ Digest::SHA512.digest(modifier.to_s)
86
+ end
91
87
  end
92
88
  self
93
89
  end
@@ -100,11 +96,9 @@ class PoolOfEntropy
100
96
  # affect the internal state of the data pool used by the generator.
101
97
  # @param [String,nil] modifier
102
98
  # @return [PoolOfEntropy] self
103
- def modify_all modifier
99
+ def modify_all(modifier)
104
100
  @fixed_modifier = modifier
105
- unless @fixed_modifier.nil?
106
- @fixed_modifier = Digest::SHA512.digest( @fixed_modifier.to_s )
107
- end
101
+ @fixed_modifier = Digest::SHA512.digest(@fixed_modifier.to_s) unless @fixed_modifier.nil?
108
102
  self
109
103
  end
110
104
 
@@ -113,8 +107,8 @@ class PoolOfEntropy
113
107
  # from #rand() and cannot be undone.
114
108
  # @param [String] data
115
109
  # @return [PoolOfEntropy] self
116
- def add_to_pool data
117
- @core_prng.update( data )
110
+ def add_to_pool(data)
111
+ @core_prng.update(data)
118
112
  self
119
113
  end
120
114
 
@@ -128,45 +122,51 @@ class PoolOfEntropy
128
122
 
129
123
  private
130
124
 
125
+ def rand_from_range(range)
126
+ bottom = range.first
127
+ top = range.last
128
+ return nil if top < bottom
129
+
130
+ bottom + generate_integer(top - bottom + 1)
131
+ end
132
+
131
133
  def use_adjustments
132
- [ @fixed_modifier, @next_modifier_queue.shift ].compact
134
+ [@fixed_modifier, @next_modifier_queue.shift].compact
133
135
  end
134
136
 
135
137
  def generate_float
136
- @core_prng.read_float( *use_adjustments )
138
+ @core_prng.read_float(*use_adjustments)
137
139
  end
138
140
 
139
- def generate_integer max
140
- @core_prng.generate_integer( max, *use_adjustments )
141
+ def generate_integer(max)
142
+ @core_prng.generate_integer(max, *use_adjustments)
141
143
  end
142
144
 
143
- def state_from_options( options, size )
145
+ def state_from_options(options, size)
144
146
  if options[:blank]
145
147
  "\x0" * size * 64
146
148
  else
147
- SecureRandom.random_bytes( size * 64 )
149
+ SecureRandom.random_bytes(size * 64)
148
150
  end
149
151
  end
150
152
 
151
- def size_from_options( options )
153
+ def size_from_options(options)
152
154
  size = 1
153
155
  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
156
+ size = Integer(options[:size])
157
+ raise ArgumentError, "Size of pool must be in Range 1..256, got #{size}" if size < 1 || size > 256
158
158
  end
159
159
  size
160
160
  end
161
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
162
+ def seed_from_options(options)
163
+ return unless 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
+
168
+ options[:seeds].each do |seed|
169
+ add_to_pool(seed)
170
170
  end
171
171
  end
172
172
  end