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