random 0.2.0
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.
- data/LICENSE +32 -0
- data/README +83 -0
- data/Rakefile +247 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/doc/simple_example.rb +17 -0
- data/ext/random/extconf.rb +9 -0
- data/ext/random/mersenne_twister_ext.c +465 -0
- data/lib/math/statistics/chi_square.rb +76 -0
- data/lib/math/statistics/table_chi_square_probabilities.html +472 -0
- data/lib/random.rb +7 -0
- data/lib/random/array_random_element.rb +6 -0
- data/lib/random/random_number_generator.rb +196 -0
- data/lib/random/testing.rb +35 -0
- data/test/acceptance/wagner_distribution_1_0/testWagner.out +625 -0
- data/test/acceptance/wagner_distribution_1_0/test_compare_to_wagners_test_output.rb +91 -0
- data/test/performance/test_mersenne_twister.rb +20 -0
- data/test/unit/#mt19937ar.c# +190 -0
- data/test/unit/expected_mersenne_4357.txt +200 -0
- data/test/unit/test_chi_square.rb +25 -0
- data/test/unit/test_mersenne_twister.rb +131 -0
- data/test/unit/test_random_number_generator.rb +168 -0
- data/test/unit/test_random_testing.rb +22 -0
- data/test/unit/test_rng.rb +9 -0
- metadata +71 -0
data/LICENSE
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Random - a Ruby extension library for random number generation
|
2
|
+
|
3
|
+
Copyright (c) 2006, Robert Feldt, robert.feldt@gmail.com
|
4
|
+
|
5
|
+
All rights reserved.
|
6
|
+
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
8
|
+
modification, are permitted provided that the following conditions
|
9
|
+
are met:
|
10
|
+
|
11
|
+
1. Redistributions of source code must retain the above copyright
|
12
|
+
notice, this list of conditions and the following disclaimer.
|
13
|
+
|
14
|
+
2. Redistributions in binary form must reproduce the above copyright
|
15
|
+
notice, this list of conditions and the following disclaimer in the
|
16
|
+
documentation and/or other materials provided with the distribution.
|
17
|
+
|
18
|
+
3. The names of its contributors may not be used to endorse or promote
|
19
|
+
products derived from this software without specific prior written
|
20
|
+
permission.
|
21
|
+
|
22
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
23
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
24
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
25
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
26
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
27
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
28
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
29
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
30
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
31
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
32
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= Random - Ruby classes for pseudorandom number generation
|
2
|
+
|
3
|
+
This package contains Random, a ruby extension adding classes for
|
4
|
+
generating pseudorandom number generation (PRNG).
|
5
|
+
|
6
|
+
Random has the following features:
|
7
|
+
|
8
|
+
* A fast PRNG implemented in C and wrapped in a Ruby class. The algorithm is the well-known and widely used MersenneTwister.
|
9
|
+
|
10
|
+
* Multiple independent random streams can be active at the same time (since the PRNG is wrapped in a class and objects of that class are independent).
|
11
|
+
|
12
|
+
* PRNG objects supports marshaling.
|
13
|
+
|
14
|
+
This is in contrast to Ruby's standard srand/rand methods. Even though the standard methods use the same PRNG algorithm (the MersenneTwister) only a single (global) PRNG exists for each ruby invocation and its state cannot be saved to disc. Random alleviates these problems. This is especially important for scientific and simulation applications.
|
15
|
+
|
16
|
+
== Download
|
17
|
+
|
18
|
+
The latest version of Random can be found at
|
19
|
+
|
20
|
+
* http://rubyforge.org/projects/random/
|
21
|
+
|
22
|
+
== Installation
|
23
|
+
|
24
|
+
=== GEM Installation
|
25
|
+
|
26
|
+
Download and install Random with the following.
|
27
|
+
|
28
|
+
gem install --remote random
|
29
|
+
|
30
|
+
=== Running the Random Test Suite
|
31
|
+
|
32
|
+
Random comes with an extensive test suite. If you wish to run it:
|
33
|
+
|
34
|
+
* CD into the top project directory of random.
|
35
|
+
* Type:
|
36
|
+
|
37
|
+
rake # If you have a version of rake installed
|
38
|
+
|
39
|
+
== Online Resources and References
|
40
|
+
|
41
|
+
* Documentation Home: coming!
|
42
|
+
* Project Page: http://rubyforge.org/projects/random
|
43
|
+
* API Documents: coming!
|
44
|
+
|
45
|
+
== Simple Example
|
46
|
+
|
47
|
+
Once installed, you can use random as follows:
|
48
|
+
|
49
|
+
:include: doc/simple_example.rb
|
50
|
+
|
51
|
+
== Credits
|
52
|
+
|
53
|
+
[<b>Richard Wagner</b>] For the C++ implementation of the MersenneTwister which is the basis for Random::MersenneTwister.
|
54
|
+
|
55
|
+
== License
|
56
|
+
|
57
|
+
Random is available under a BSD-style license.
|
58
|
+
|
59
|
+
:include: LICENSE
|
60
|
+
|
61
|
+
== Support
|
62
|
+
|
63
|
+
The Random homepage is http://random.rubyforge.org. You can find the Random
|
64
|
+
RubyForge page at http://rubyforge.org/projects/random.
|
65
|
+
|
66
|
+
Feel free to submit commits or feature requests. If you send a patch,
|
67
|
+
please also update and send the corresponding unit tests.
|
68
|
+
|
69
|
+
It would be great to get help with creating pre-compiled gems of this to simplify for users that do not have a build environment. Please email if you can help with this (especially for non-linux targets).
|
70
|
+
|
71
|
+
For other information, feel free to ask on the ruby-talk mailing list
|
72
|
+
(which is mirrored to comp.lang.ruby) or contact
|
73
|
+
mailto:robert.feldt@gmail.com.
|
74
|
+
|
75
|
+
---
|
76
|
+
|
77
|
+
= Other stuff
|
78
|
+
|
79
|
+
Author:: Robert Feldt <robert.feldt@gmail.com>
|
80
|
+
Requires:: Ruby 1.8.2 or later (but only tested with 1.8.4)
|
81
|
+
License:: Copyright 2006 by Robert Feldt
|
82
|
+
Released under a BSD-style license. See the LICENSE file
|
83
|
+
included in the distribution.
|
data/Rakefile
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/packagetask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
|
7
|
+
PROJECT = "random"
|
8
|
+
|
9
|
+
VERSION_FILE = "VERSION"
|
10
|
+
#MANIFEST_FILE = "Manifest"
|
11
|
+
README_FILE = "README"
|
12
|
+
TODO_FILE = "TODO"
|
13
|
+
RAKE_FILE = "Rakefile"
|
14
|
+
|
15
|
+
CLEAN.include("ext/**/*.so")
|
16
|
+
CLEAN.include("ext/**/*.o")
|
17
|
+
CLEAN.include("ext/**/Makefile")
|
18
|
+
CLEAN.include("CHANGES")
|
19
|
+
CLEAN.include("CODE_STATS")
|
20
|
+
|
21
|
+
task :default => [:test]
|
22
|
+
|
23
|
+
|
24
|
+
#############################################################################
|
25
|
+
# Version related
|
26
|
+
#############################################################################
|
27
|
+
|
28
|
+
# Read the version from the version file
|
29
|
+
def Version()
|
30
|
+
File.open(VERSION_FILE) {|fh| fh.read.strip}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Update the version num on disc
|
34
|
+
def update_version
|
35
|
+
v = Version().split(".").map {|vp| vp.to_i}
|
36
|
+
new_v = yield(v)
|
37
|
+
File.open(VERSION_FILE, "w") {|fh| fh.write(new_v)}
|
38
|
+
puts Version()
|
39
|
+
end
|
40
|
+
|
41
|
+
def inc_tiny; update_version {|v| "#{v[0]}.#{v[1]}.#{v[2]+1}\n"}; end
|
42
|
+
def inc_minor; update_version {|v| "#{v[0]}.#{v[1]+1}.0\n"}; end
|
43
|
+
def inc_major; update_version {|v| "#{v[0]+1}.0.0\n"}; end
|
44
|
+
|
45
|
+
task :version do
|
46
|
+
puts Version()
|
47
|
+
end
|
48
|
+
task :inc_tiny do
|
49
|
+
inc_tiny()
|
50
|
+
end
|
51
|
+
task :inc_minor do
|
52
|
+
inc_minor()
|
53
|
+
end
|
54
|
+
task :inc_major do
|
55
|
+
inc_major()
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
#############################################################################
|
60
|
+
# Test related
|
61
|
+
#############################################################################
|
62
|
+
|
63
|
+
UTestFiles = FileList["test/unit/**/test*.rb"]
|
64
|
+
ATestFiles = FileList["test/acceptance/**/*test*.rb"]
|
65
|
+
PTestFiles = FileList["test/performance/**/test*.rb"]
|
66
|
+
|
67
|
+
task :setup_for_test => [:pack_and_geminstall] do
|
68
|
+
require 'rubygems'
|
69
|
+
require 'test/common'
|
70
|
+
end
|
71
|
+
|
72
|
+
def load_files(list)
|
73
|
+
list.each {|f| puts "Loading #{f}"; require f}
|
74
|
+
end
|
75
|
+
|
76
|
+
task :utest => [:setup_for_test] do
|
77
|
+
load_files UTestFiles
|
78
|
+
end
|
79
|
+
|
80
|
+
task :atest => [:setup_for_test] do
|
81
|
+
load_files ATestFiles
|
82
|
+
end
|
83
|
+
|
84
|
+
task :ptest => [:setup_for_test] do
|
85
|
+
load_files PTestFiles
|
86
|
+
end
|
87
|
+
|
88
|
+
task :test => [:setup_for_test] do
|
89
|
+
load_files UTestFiles
|
90
|
+
load_files ATestFiles
|
91
|
+
end
|
92
|
+
|
93
|
+
task :ut => :utest
|
94
|
+
task :at => :atest
|
95
|
+
task :pt => :ptest
|
96
|
+
|
97
|
+
|
98
|
+
############################################################################
|
99
|
+
# RDoc related
|
100
|
+
############################################################################
|
101
|
+
|
102
|
+
file "CHANGES" do
|
103
|
+
system "darcs changes > CHANGES"
|
104
|
+
end
|
105
|
+
|
106
|
+
file "CODE_STATS" do
|
107
|
+
system "rake stats > CODE_STATS"
|
108
|
+
end
|
109
|
+
|
110
|
+
rd = Rake::RDocTask.new("rdoc") do |rdoc|
|
111
|
+
rdoc.rdoc_dir = 'html'
|
112
|
+
rdoc.title = "Random -- Pseudorandom Number Generators for Ruby"
|
113
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
114
|
+
rdoc.rdoc_files.include('README', 'LICENSE', 'TODO', 'CHANGES', 'CODE_STATS')
|
115
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
116
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
#############################################################################
|
121
|
+
# Gem and package related
|
122
|
+
#############################################################################
|
123
|
+
|
124
|
+
BaseFileToIncludeGlobs = [
|
125
|
+
"README",
|
126
|
+
"TODO",
|
127
|
+
"LICENSE",
|
128
|
+
"VERSION",
|
129
|
+
"Rakefile",
|
130
|
+
"ext/random/mersenne_twister_ext.c",
|
131
|
+
"ext/random/extconf.rb",
|
132
|
+
"lib/random.rb",
|
133
|
+
"lib/random/array_random_element.rb",
|
134
|
+
"lib/random/random_number_generator.rb",
|
135
|
+
"lib/random/testing.rb",
|
136
|
+
"lib/math/statistics/chi_square.rb",
|
137
|
+
"lib/math/statistics/table_chi_square_probabilities.html",
|
138
|
+
"doc/**/*",
|
139
|
+
]
|
140
|
+
|
141
|
+
TestFilesToIncludeGlobs = [
|
142
|
+
"test/acceptance/**/*",
|
143
|
+
"test/performance/**/*",
|
144
|
+
"test/unit/**/*",
|
145
|
+
]
|
146
|
+
|
147
|
+
PkgFileGlobs = BaseFileToIncludeGlobs + TestFilesToIncludeGlobs
|
148
|
+
|
149
|
+
def gem_spec
|
150
|
+
Gem::Specification.new do |s|
|
151
|
+
s.name = PROJECT
|
152
|
+
s.version = Version()
|
153
|
+
s.platform = Gem::Platform::RUBY
|
154
|
+
s.summary = "Stand-alone random number generator (RNG) class allowing multiple, active RNG's at the same time (which is not possible with Ruby's rand/srand)."
|
155
|
+
s.author = "Robert Feldt"
|
156
|
+
s.email = "Robert.Feldt@gmail.com"
|
157
|
+
s.homepage = "http://www.rubyforge.org/projects/random"
|
158
|
+
|
159
|
+
s.description = <<EOS
|
160
|
+
A Mersenne-Twister random number generator (RNG) packed up as a class. This allows multiple RNG streams to be active at the same time (which Ruby's normal rand/srand does not allow). The Mersenne-Twister is implemented with fast C code for speed.
|
161
|
+
EOS
|
162
|
+
|
163
|
+
s.require_paths << "ext"
|
164
|
+
s.require_paths << "lib"
|
165
|
+
|
166
|
+
s.has_rdoc = false
|
167
|
+
|
168
|
+
s.files = []
|
169
|
+
PkgFileGlobs.each do |globpat|
|
170
|
+
fs = Dir.glob(globpat).delete_if {|i| i.include?(".svn")}
|
171
|
+
s.files += fs
|
172
|
+
end
|
173
|
+
|
174
|
+
s.extensions = ["ext/random/extconf.rb"]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
Rake::GemPackageTask.new(gem_spec()) do |pkg|
|
179
|
+
pkg.need_zip = true
|
180
|
+
pkg.need_tar = true
|
181
|
+
end
|
182
|
+
|
183
|
+
def gem_name
|
184
|
+
Dir["pkg/*.gem"].first
|
185
|
+
end
|
186
|
+
|
187
|
+
task :gem_uninstall do
|
188
|
+
system "sudo gem uninstall random"
|
189
|
+
end
|
190
|
+
|
191
|
+
task :pack_and_geminstall => [:gem_uninstall, :package] do
|
192
|
+
system "sudo gem install #{gem_name()}"
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
#############################################################################
|
197
|
+
# Rubyforge related
|
198
|
+
#############################################################################
|
199
|
+
|
200
|
+
# Create a package (a name for a top-level "item" which can then have multiple
|
201
|
+
# releases where each release can have multiple files)
|
202
|
+
def create_package(packageName = "random")
|
203
|
+
system "rubyforge create_package #{PROJECT} #{packageName}"
|
204
|
+
end
|
205
|
+
|
206
|
+
# Release a <file> with a given <release_name> in the given <package>.
|
207
|
+
def release_file(file, release_name, package = PROJECT)
|
208
|
+
group_id = PROJECT
|
209
|
+
system "rubyforge add_release #{group_id} #{package} #{release_name} #{file}"
|
210
|
+
end
|
211
|
+
|
212
|
+
# Delete a package (and all its files)
|
213
|
+
def delete_package(packageName)
|
214
|
+
system "rubyforge create_package random #{ARGV.last}"
|
215
|
+
end
|
216
|
+
|
217
|
+
# We use a temporary package name for now, should be PROJECT later...
|
218
|
+
#PACKAGE_NAME = "1801" # if of the test package in random group
|
219
|
+
PACKAGE_NAME = "368" # Name of random package
|
220
|
+
|
221
|
+
task :pack => [:gen_paper, :package]
|
222
|
+
|
223
|
+
desc "Create package and release to rubyforge"
|
224
|
+
task :rubyforge_release => [:pack] do
|
225
|
+
puts "Logging in to rubyforge"
|
226
|
+
system "rubyforge login"
|
227
|
+
|
228
|
+
release_name = "#{PROJECT}-#{Version()}"
|
229
|
+
files_to_release = FileList["pkg/#{release_name}.gem"]
|
230
|
+
files_to_release.each do |file|
|
231
|
+
puts "Releasing #{file} to Rubyforge"
|
232
|
+
release_file(file, release_name, PACKAGE_NAME)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
############################################################################
|
237
|
+
# Code stats
|
238
|
+
############################################################################
|
239
|
+
begin
|
240
|
+
require 'feldt/rake/codestatstask'
|
241
|
+
|
242
|
+
Rake::CodeStatsTask.new do |t|
|
243
|
+
t.code_directories = ['lib'] # not needed since it is the default
|
244
|
+
t.test_directories = ['test'] # not needed since it is the default
|
245
|
+
end
|
246
|
+
rescue Exception => e
|
247
|
+
end
|
data/TODO
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'random'
|
3
|
+
|
4
|
+
mt = Random::RNG.new()
|
5
|
+
mt.rand # like Ruby's normal rand
|
6
|
+
mt.rand_num(1, 6) # gives a random integer in 1..6
|
7
|
+
mt.rand_float_exclusive # gives random float >= 0.0 and < 1.0
|
8
|
+
mt.rand_float # gives random float >= 0.0 and <= 1.0
|
9
|
+
mt.rand_int_with_bits(1000) # gives random integer with 1000 random bits
|
10
|
+
mt.rand_bytes(10) # gives a random string of length 10
|
11
|
+
mt.next # next random number in RNG stream (typically an
|
12
|
+
# integer >= 0 and < 2**32)
|
13
|
+
str = Marshal.dump(mt) # Marshal the full state of mt to a string
|
14
|
+
mt2 = Marshal.load(str) # and read it back in.
|
15
|
+
mt2.next == mt.next # => true
|
16
|
+
a = (1..100).to_a
|
17
|
+
a.random_element # gives a random element from the array
|
@@ -0,0 +1,465 @@
|
|
1
|
+
/* MersenneTwister class for Ruby
|
2
|
+
* Robert Feldt robert.feldt@gmail.com
|
3
|
+
*
|
4
|
+
* based on code by Makoto Matsumoto, Takuji Nishimura, Shawn Cokus, and
|
5
|
+
* Richard Wagner. Main basis has been Richard Wagner's v1.0 15 May 2003
|
6
|
+
* version MersenneTwister.h.
|
7
|
+
*
|
8
|
+
* The Mersenne Twister is an algorithm for generating random numbers. It
|
9
|
+
* was designed with consideration of the flaws in various other generators.
|
10
|
+
* The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
|
11
|
+
* are far greater. The generator is also fast; it avoids multiplication and
|
12
|
+
* division, and it benefits from caches and pipelines. For more information
|
13
|
+
* see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
|
14
|
+
*
|
15
|
+
* Reference
|
16
|
+
* M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
|
17
|
+
* Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
|
18
|
+
* Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
|
19
|
+
*
|
20
|
+
* Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
21
|
+
* Copyright (C) 2000 - 2003, Richard J. Wagner
|
22
|
+
* Copyright (C) 2006, Robert Feldt
|
23
|
+
*
|
24
|
+
* All rights reserved.
|
25
|
+
*
|
26
|
+
* Redistribution and use in source and binary forms, with or without
|
27
|
+
* modification, are permitted provided that the following conditions
|
28
|
+
* are met:
|
29
|
+
*
|
30
|
+
* 1. Redistributions of source code must retain the above copyright
|
31
|
+
* notice, this list of conditions and the following disclaimer.
|
32
|
+
*
|
33
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
34
|
+
* notice, this list of conditions and the following disclaimer in the
|
35
|
+
* documentation and/or other materials provided with the distribution.
|
36
|
+
*
|
37
|
+
* 3. The names of its contributors may not be used to endorse or promote
|
38
|
+
* products derived from this software without specific prior written
|
39
|
+
* permission.
|
40
|
+
*
|
41
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
42
|
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
43
|
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
44
|
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
45
|
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
46
|
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
47
|
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
48
|
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
49
|
+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
50
|
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
51
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
52
|
+
|
53
|
+
* The original code included the following notice:
|
54
|
+
*
|
55
|
+
* When you use this, send an email to: matumoto@math.keio.ac.jp
|
56
|
+
* with an appropriate reference to your work.
|
57
|
+
*
|
58
|
+
* It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu
|
59
|
+
* and robert.feldt@gmail.com when you write.
|
60
|
+
*/
|
61
|
+
#include "ruby.h"
|
62
|
+
#include <time.h>
|
63
|
+
|
64
|
+
static VALUE mRandom;
|
65
|
+
static VALUE cRandomNumberGenerator;
|
66
|
+
static VALUE cMersenneTwister;
|
67
|
+
|
68
|
+
typedef unsigned long uint32; // unsigned integer type, at least 32 bits
|
69
|
+
|
70
|
+
#define N (624) // length of state vector
|
71
|
+
#define SAVE (N + 1) // length of array for save()
|
72
|
+
#define M (397) // period parameter
|
73
|
+
|
74
|
+
typedef struct {
|
75
|
+
uint32 state[N]; // internal state
|
76
|
+
uint32 *pNext; // next value to get from state
|
77
|
+
int left; // number of values left before reload needed
|
78
|
+
} MersenneTwister;
|
79
|
+
|
80
|
+
#define hiBit(u) (u & 0x80000000UL)
|
81
|
+
#define loBit(u) (u & 0x00000001UL)
|
82
|
+
#define loBits(u) (u & 0x7fffffffUL)
|
83
|
+
#define mixBits(u,v) (hiBit(u) | loBits(v))
|
84
|
+
#define twist(m, s0, s1) ((m) ^ (mixBits((s0),(s1))>>1) ^(-loBit(s1) & 0x9908b0dfUL))
|
85
|
+
|
86
|
+
inline void reload(MersenneTwister* mt) {
|
87
|
+
// Generate N new values in state
|
88
|
+
// Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
|
89
|
+
register uint32 *p = mt->state;
|
90
|
+
register int i;
|
91
|
+
|
92
|
+
for( i = N - M; i--; ++p )
|
93
|
+
*p = twist( p[M], p[0], p[1] );
|
94
|
+
for( i = M; --i; ++p )
|
95
|
+
*p = twist( p[M-N], p[0], p[1] );
|
96
|
+
*p = twist( p[M-N], p[0], mt->state[0] );
|
97
|
+
|
98
|
+
mt->left = N, mt->pNext = mt->state;
|
99
|
+
}
|
100
|
+
|
101
|
+
// integer in [0,2^32-1]
|
102
|
+
inline uint32 rand_int(MersenneTwister* mt) {
|
103
|
+
// Pull a 32-bit integer from the generator state
|
104
|
+
// Every other access function simply transforms the numbers extracted here
|
105
|
+
register uint32 s1;
|
106
|
+
|
107
|
+
if( mt->left == 0 ) {
|
108
|
+
reload(mt);
|
109
|
+
}
|
110
|
+
|
111
|
+
(mt->left)--;
|
112
|
+
|
113
|
+
s1 = *(mt->pNext);
|
114
|
+
mt->pNext++;
|
115
|
+
s1 ^= (s1 >> 11);
|
116
|
+
s1 ^= (s1 << 7) & 0x9d2c5680UL;
|
117
|
+
s1 ^= (s1 << 15) & 0xefc60000UL;
|
118
|
+
return ( s1 ^ (s1 >> 18) );
|
119
|
+
}
|
120
|
+
|
121
|
+
// real number in [0,1] (inclusive)
|
122
|
+
inline double rand_double(MersenneTwister* mt) {
|
123
|
+
return ((double)rand_int(mt)) * (1.0/4294967295.0);
|
124
|
+
}
|
125
|
+
|
126
|
+
// real number in [0,n] (inclusive)
|
127
|
+
inline double rand_double_scaled(MersenneTwister* mt, const double n ) {
|
128
|
+
return rand_double(mt) * n;
|
129
|
+
}
|
130
|
+
|
131
|
+
// real number in [0,1) (exclusive)
|
132
|
+
inline double rand_double_excl(MersenneTwister* mt) {
|
133
|
+
return ((double)rand_int(mt)) * (1.0/4294967296.0);
|
134
|
+
}
|
135
|
+
|
136
|
+
// real number in [0,n) (exclusive)
|
137
|
+
inline double rand_double_scaled_excl(MersenneTwister* mt, const double n ) {
|
138
|
+
return rand_double_excl(mt) * n;
|
139
|
+
}
|
140
|
+
|
141
|
+
// integer in [0,n] for n < 2^32
|
142
|
+
inline uint32 rand_int_small_limited(MersenneTwister* mt, const uint32 n ) {
|
143
|
+
register uint32 i;
|
144
|
+
register uint32 used = n;
|
145
|
+
|
146
|
+
// Find which bits are used in n
|
147
|
+
// Optimized by Magnus Jonsson (magnus@smartelectronix.com)
|
148
|
+
used |= used >> 1;
|
149
|
+
used |= used >> 2;
|
150
|
+
used |= used >> 4;
|
151
|
+
used |= used >> 8;
|
152
|
+
used |= used >> 16;
|
153
|
+
|
154
|
+
// Draw numbers until one is found in [0,n]
|
155
|
+
i = rand_int(mt) & used;
|
156
|
+
while( i > n ) {
|
157
|
+
i = rand_int(mt) & used; // toss unused bits to shorten search
|
158
|
+
}
|
159
|
+
|
160
|
+
return i;
|
161
|
+
}
|
162
|
+
|
163
|
+
inline void initialize_state( MersenneTwister* mt, const uint32 seed ) {
|
164
|
+
// Initialize generator state with seed
|
165
|
+
// See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
|
166
|
+
// In previous versions, most significant bits (MSBs) of the seed affect
|
167
|
+
// only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
|
168
|
+
register uint32 *s = mt->state;
|
169
|
+
register uint32 *r = mt->state;
|
170
|
+
register int i = 1;
|
171
|
+
*s++ = seed & 0xffffffffUL;
|
172
|
+
for( ; i < N; ++i ) {
|
173
|
+
*s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
|
174
|
+
r++;
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
inline void seed( MersenneTwister* mt, const uint32 oneSeed ) {
|
179
|
+
// Seed the generator with a simple uint32
|
180
|
+
initialize_state(mt, oneSeed);
|
181
|
+
reload(mt);
|
182
|
+
}
|
183
|
+
|
184
|
+
inline void seed_from_array( MersenneTwister* mt,
|
185
|
+
uint32 *const bigSeed,
|
186
|
+
const uint32 seedLength ) {
|
187
|
+
// Seed the generator with an array of uint32's
|
188
|
+
// There are 2^19937-1 possible initial states. This function allows
|
189
|
+
// all of those to be accessed by providing at least 19937 bits (with a
|
190
|
+
// default seed length of N = 624 uint32's). Any bits above the lower 32
|
191
|
+
// in each element are discarded.
|
192
|
+
// Just call seed() if you want to get array from /dev/urandom
|
193
|
+
initialize_state(mt, 19650218UL);
|
194
|
+
register int i = 1;
|
195
|
+
register uint32 j = 0;
|
196
|
+
register int k = ( N > seedLength ? N : seedLength );
|
197
|
+
for( ; k; --k ) {
|
198
|
+
mt->state[i] =
|
199
|
+
mt->state[i] ^ ( (mt->state[i-1] ^ (mt->state[i-1] >> 30)) * 1664525UL );
|
200
|
+
mt->state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
|
201
|
+
mt->state[i] &= 0xffffffffUL;
|
202
|
+
++i; ++j;
|
203
|
+
if( i >= N ) { mt->state[0] = mt->state[N-1]; i = 1; }
|
204
|
+
if( j >= seedLength ) j = 0;
|
205
|
+
}
|
206
|
+
for( k = N - 1; k; --k ) {
|
207
|
+
mt->state[i] =
|
208
|
+
mt->state[i] ^ ( (mt->state[i-1] ^ (mt->state[i-1] >> 30)) * 1566083941UL );
|
209
|
+
mt->state[i] -= i;
|
210
|
+
mt->state[i] &= 0xffffffffUL;
|
211
|
+
++i;
|
212
|
+
if( i >= N ) { mt->state[0] = mt->state[N-1]; i = 1; }
|
213
|
+
}
|
214
|
+
mt->state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
|
215
|
+
reload(mt);
|
216
|
+
}
|
217
|
+
|
218
|
+
|
219
|
+
/////////////////////////////////////////////////////////////////////////////
|
220
|
+
// Ruby wrapper functions
|
221
|
+
/////////////////////////////////////////////////////////////////////////////
|
222
|
+
|
223
|
+
void mt_free(MersenneTwister* mt) {
|
224
|
+
if(mt) {
|
225
|
+
xfree(mt);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
static VALUE
|
230
|
+
mt_alloc(VALUE klass) {
|
231
|
+
MersenneTwister *mt;
|
232
|
+
VALUE obj;
|
233
|
+
|
234
|
+
mt = (MersenneTwister*)xcalloc(sizeof(MersenneTwister), 1);
|
235
|
+
obj = Data_Wrap_Struct(klass, 0, mt_free, mt);
|
236
|
+
|
237
|
+
return obj;
|
238
|
+
}
|
239
|
+
|
240
|
+
// Get a unique seed
|
241
|
+
inline uint32 hash( time_t t, clock_t c ) {
|
242
|
+
// Get a uint32 from t and c
|
243
|
+
// Better than uint32(x) in case x is floating point in [0,1]
|
244
|
+
// Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
|
245
|
+
|
246
|
+
static uint32 differ = 0; // guarantee time-based seeds will change
|
247
|
+
|
248
|
+
uint32 h1 = 0;
|
249
|
+
unsigned char *p = (unsigned char *) &t;
|
250
|
+
size_t i, j;
|
251
|
+
|
252
|
+
for( i = 0; i < sizeof(t); ++i ) {
|
253
|
+
h1 *= UCHAR_MAX + 2U;
|
254
|
+
h1 += p[i];
|
255
|
+
}
|
256
|
+
uint32 h2 = 0;
|
257
|
+
p = (unsigned char *) &c;
|
258
|
+
for( j = 0; j < sizeof(c); ++j ) {
|
259
|
+
h2 *= UCHAR_MAX + 2U;
|
260
|
+
h2 += p[j];
|
261
|
+
}
|
262
|
+
return ( h1 + differ++ ) ^ h2;
|
263
|
+
}
|
264
|
+
|
265
|
+
/* Initialize an instance of the Mersenne Twister RNG. The seed can either
|
266
|
+
* be a 32 bit unsigned integer (ie a Ruby Integer in the range
|
267
|
+
* 0..(-1+2**32)) or an array of 624 32 bit unsigned integers (the state
|
268
|
+
* used by the Mersenne Twister algorithm). If an array larger than 624
|
269
|
+
* is given only the first 624 integers are used. If no seed is used
|
270
|
+
* we use a (fixed) default value.
|
271
|
+
*/
|
272
|
+
static VALUE
|
273
|
+
mt_initialize(int argc, VALUE* argv, VALUE self) {
|
274
|
+
MersenneTwister* mt;
|
275
|
+
VALUE arg_seed;
|
276
|
+
register int i;
|
277
|
+
uint32 array_seed[N];
|
278
|
+
uint32 oneSeed;
|
279
|
+
uint32 max;
|
280
|
+
|
281
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
282
|
+
|
283
|
+
rb_scan_args(argc, argv, "01", &arg_seed);
|
284
|
+
|
285
|
+
// Get a new seed if none were given
|
286
|
+
if(TYPE(arg_seed) == T_NIL) {
|
287
|
+
arg_seed = UINT2NUM(hash(time(NULL), clock()));
|
288
|
+
}
|
289
|
+
|
290
|
+
switch (TYPE(arg_seed)) {
|
291
|
+
|
292
|
+
case T_ARRAY:
|
293
|
+
// Find max number of numbers in seed array
|
294
|
+
if (RARRAY(arg_seed)->len < N)
|
295
|
+
max = RARRAY(arg_seed)->len;
|
296
|
+
else
|
297
|
+
max = N;
|
298
|
+
|
299
|
+
// Translate Ruby Integers to uint32s
|
300
|
+
for( i = 0; i<max; i++) {
|
301
|
+
array_seed[i] = NUM2UINT(rb_ary_entry(arg_seed, i));
|
302
|
+
}
|
303
|
+
for(; i<N; i++) {
|
304
|
+
array_seed[i] = (uint32)0;
|
305
|
+
}
|
306
|
+
|
307
|
+
// Then seed from that
|
308
|
+
seed_from_array(mt, array_seed, max);
|
309
|
+
break;
|
310
|
+
|
311
|
+
case T_BIGNUM:
|
312
|
+
case T_FIXNUM:
|
313
|
+
seed(mt, (uint32)NUM2UINT(arg_seed));
|
314
|
+
break;
|
315
|
+
|
316
|
+
default:
|
317
|
+
rb_raise(rb_eException, "Invalid argument");
|
318
|
+
break;
|
319
|
+
}
|
320
|
+
|
321
|
+
return self;
|
322
|
+
}
|
323
|
+
|
324
|
+
static VALUE
|
325
|
+
mt_small_seed(VALUE self, VALUE arg1) {
|
326
|
+
register uint32 *s;
|
327
|
+
register int i;
|
328
|
+
uint32 oneSeed = (uint32)NUM2UINT(arg1);
|
329
|
+
MersenneTwister *mt;
|
330
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
331
|
+
}
|
332
|
+
|
333
|
+
static VALUE
|
334
|
+
mt_gen_uint32(VALUE self)
|
335
|
+
{
|
336
|
+
MersenneTwister *mt;
|
337
|
+
|
338
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
339
|
+
|
340
|
+
return UINT2NUM(rand_int(mt));
|
341
|
+
}
|
342
|
+
|
343
|
+
static VALUE
|
344
|
+
mt_rand_int_small_limited(VALUE self, VALUE limit)
|
345
|
+
{
|
346
|
+
uint32 lim = NUM2UINT(limit);
|
347
|
+
MersenneTwister *mt;
|
348
|
+
|
349
|
+
if(lim == 0) {
|
350
|
+
return UINT2NUM(0);
|
351
|
+
}
|
352
|
+
|
353
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
354
|
+
|
355
|
+
return UINT2NUM(rand_int_small_limited(mt, lim));
|
356
|
+
}
|
357
|
+
|
358
|
+
static VALUE
|
359
|
+
mt_rand_float(VALUE self)
|
360
|
+
{
|
361
|
+
MersenneTwister *mt;
|
362
|
+
|
363
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
364
|
+
return rb_float_new(rand_double(mt));
|
365
|
+
}
|
366
|
+
|
367
|
+
static VALUE
|
368
|
+
mt_rand_float_excl(VALUE self)
|
369
|
+
{
|
370
|
+
MersenneTwister *mt;
|
371
|
+
|
372
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
373
|
+
return rb_float_new(rand_double_excl(mt));
|
374
|
+
}
|
375
|
+
|
376
|
+
static VALUE
|
377
|
+
mt_state(VALUE self)
|
378
|
+
{
|
379
|
+
register VALUE ary;
|
380
|
+
register const uint32 *s;
|
381
|
+
register int i = N;
|
382
|
+
MersenneTwister *mt;
|
383
|
+
|
384
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
385
|
+
|
386
|
+
s = mt->state;
|
387
|
+
ary = rb_ary_new();
|
388
|
+
|
389
|
+
for( ; i--; s++) {
|
390
|
+
rb_ary_push(ary, UINT2NUM(*s));
|
391
|
+
}
|
392
|
+
|
393
|
+
rb_ary_push(ary, UINT2NUM(mt->left));
|
394
|
+
|
395
|
+
//printf("get: left=%u, s-n=%u, s[0]=%u, s[623]=%u\n",
|
396
|
+
// mt->left, mt->pNext - mt->state, mt->state[0], mt->state[623]);
|
397
|
+
|
398
|
+
return ary;
|
399
|
+
}
|
400
|
+
|
401
|
+
static VALUE
|
402
|
+
mt_state_set(VALUE self, VALUE ary)
|
403
|
+
{
|
404
|
+
register uint32 *s;
|
405
|
+
register int i = N;
|
406
|
+
MersenneTwister *mt;
|
407
|
+
|
408
|
+
Data_Get_Struct(self, MersenneTwister, mt);
|
409
|
+
|
410
|
+
s = mt->state;
|
411
|
+
|
412
|
+
for(i = 0; i < N; s++) {
|
413
|
+
*s = NUM2UINT(rb_ary_entry(ary, i++));
|
414
|
+
}
|
415
|
+
|
416
|
+
mt->left = NUM2UINT(rb_ary_entry(ary, i));
|
417
|
+
mt->pNext = &(mt->state[N-mt->left]);
|
418
|
+
|
419
|
+
// printf("set: left=%u, s-n=%u, s[0]=%u, s[623]=%u\n",
|
420
|
+
// mt->left, mt->pNext - mt->state, mt->state[0], mt->state[623]);
|
421
|
+
|
422
|
+
return ary;
|
423
|
+
}
|
424
|
+
|
425
|
+
static VALUE
|
426
|
+
mt_load(VALUE klass, VALUE str)
|
427
|
+
{
|
428
|
+
VALUE ary;
|
429
|
+
VALUE obj = rb_funcall(klass, rb_intern("new"), 1, INT2FIX(4357));
|
430
|
+
|
431
|
+
ary = rb_funcall(rb_eval_string("Marshal"), rb_intern("load"), 1, str);
|
432
|
+
mt_state_set(obj, ary);
|
433
|
+
|
434
|
+
return obj;
|
435
|
+
}
|
436
|
+
|
437
|
+
void Init_mersenne_twister_ext()
|
438
|
+
{
|
439
|
+
// Require the base module and get references to the module and super class.
|
440
|
+
rb_require("random");
|
441
|
+
mRandom = rb_eval_string("Random");
|
442
|
+
cRandomNumberGenerator = rb_eval_string("Random::RandomNumberGenerator");
|
443
|
+
|
444
|
+
// Define the MT class itself and its allocator
|
445
|
+
cMersenneTwister =
|
446
|
+
rb_define_class_under(mRandom, "MersenneTwister", cRandomNumberGenerator);
|
447
|
+
rb_define_alloc_func(cMersenneTwister, mt_alloc);
|
448
|
+
|
449
|
+
// Instance methods
|
450
|
+
rb_define_method(cMersenneTwister, "initialize", mt_initialize, -1);
|
451
|
+
|
452
|
+
rb_define_method(cMersenneTwister, "next", mt_gen_uint32, 0);
|
453
|
+
rb_define_method(cMersenneTwister, "gen_uint32", mt_gen_uint32, 0);
|
454
|
+
|
455
|
+
rb_define_method(cMersenneTwister, "rand_int_small_limited",
|
456
|
+
mt_rand_int_small_limited, 1);
|
457
|
+
|
458
|
+
rb_define_method(cMersenneTwister, "rand_float", mt_rand_float, 0);
|
459
|
+
rb_define_method(cMersenneTwister, "rand_float_excl", mt_rand_float_excl, 0);
|
460
|
+
|
461
|
+
rb_define_method(cMersenneTwister, "state", mt_state, 0);
|
462
|
+
rb_define_method(cMersenneTwister, "state=", mt_state_set, 1);
|
463
|
+
rb_define_method(cMersenneTwister, "marshal_dump", mt_state, 0);
|
464
|
+
rb_define_method(cMersenneTwister, "marshal_load", mt_state_set, 1);
|
465
|
+
}
|