random 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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
@@ -0,0 +1,4 @@
1
+ === Primary
2
+
3
+ === Secondary
4
+ * Add more randomness testing algorithms (now only chi-square test)
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,9 @@
1
+ require 'mkmf'
2
+
3
+ srcs = %w{
4
+ mersenne_twister_ext
5
+ }
6
+
7
+ $objs = srcs.collect {|src| src + ".o"}
8
+
9
+ create_makefile 'mersenne_twister_ext'
@@ -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
+ }