File Coverage

blib/lib/Math/Random/MT/Perl.pm
Criterion Covered Total %
statement 130 130 100.0
branch 24 26 92.3
condition 10 10 100.0
subroutine 20 20 100.0
pod 6 6 100.0
total 190 192 98.9


line stmt bran cond sub pod time code
1             package Math::Random::MT::Perl;
2              
3 10     10   238001 use strict;
  10         25  
  10         248  
4 10     10   49 use warnings;
  10         18  
  10         273  
5              
6 10     10   47 use vars qw($VERSION);
  10         20  
  10         5542  
7             $VERSION = 1.15;
8              
9             my $N = 624;
10             my $M = 397;
11             my $UP_MASK = 0x80000000;
12             my $LOW_MASK = 0x7fffffff;
13              
14             my $gen = undef;
15              
16              
17             sub new {
18             # Create a Math::Random::MT::Perl object
19 18     18 1 83 my ($class, @seeds) = @_;
20 18         35 my $self = {};
21 18         40 bless $self, $class;
22             # Seed the random number generator
23 18         54 $self->set_seed(@seeds);
24 18         76 return $self;
25             }
26              
27              
28             sub rand {
29             # Generate a random number in requested range
30 36     36 1 2860 my ($self, $range) = @_;
31 36 100       103 if (ref $self) {
32 16   100     86 return ($range || 1) * $self->_mt_genrand();
33             }
34             else {
35 20         26 $range = $self;
36 20 100       51 Math::Random::MT::Perl::srand() unless defined $gen;
37 20   100     111 return ($range || 1) * $gen->_mt_genrand();
38             }
39             }
40              
41              
42             sub irand {
43             # Generate a random integer
44 24     24 1 4518 my ($self) = @_;
45 24 100       72 if (ref $self) {
46 16         41 return $self->_mt_genirand();
47             }
48             else {
49 8 50       24 Math::Random::MT::Perl::srand() unless defined $gen;
50 8         25 return $gen->_mt_genirand();
51             }
52             }
53              
54              
55             sub get_seed {
56             # Get the seed
57 12     12 1 23 my ($self) = @_;
58 12         30 return $self->{seed};
59             }
60              
61              
62             sub set_seed {
63             # Set the seed
64 19     19 1 42 my ($self, @seeds) = @_;
65 19         82 $self->{mt} = undef;
66 19         47 $self->{mti} = undef;
67 19         33 $self->{seed} = undef;
68 19 100       112 @seeds > 1 ? $self->_mt_setup_array(@seeds) :
    100          
69             $self->_mt_init_seed(defined $seeds[0] ? $seeds[0] : _rand_seed());
70 19         65 return $self->{seed};
71             }
72              
73              
74             sub srand {
75             # Seed the random number generator, automatically generating a seed if none
76             # is provided
77 10     10 1 45 my (@seeds) = @_;
78 10 100       36 if (not @seeds) {
79 7         19 $seeds[0] = _rand_seed();
80             }
81 10         64 $gen = Math::Random::MT::Perl->new(@seeds);
82 10         109 my $seed = $gen->get_seed;
83 10         51 return $seed;
84             }
85              
86              
87             sub _rand_seed {
88 9     9   16 my ($self) = @_;
89              
90             # Get a seed at random through Perl's CORE::rand(). We do not call
91             # CORE::srand() to avoid altering the random numbers that other parts of
92             # the running script might be using. The seeds obtained by rapid calls to
93             # the _rand_seed() function are all different.
94            
95 9         197 return int(CORE::rand(2**32));
96             }
97              
98              
99             # Note that we need to use integer some of the time to force integer overflow
100             # rollover ie 2**32+1 => 0. Unfortunately we really want uint but integer
101             # casts to signed ints, thus we can't do everything within an integer block,
102             # specifically the bitshift xor functions below. The & 0xffffffff is required
103             # to constrain the integer to 32 bits on 64 bit systems.
104              
105             sub _mt_init_seed {
106 19     19   36 my ($self, $seed) = @_;
107 19         26 my @mt;
108 19         46 $mt[0] = $seed & 0xffffffff;
109 19         71 for ( my $i = 1; $i < $N; $i++ ) {
110 11837         16184 my $xor = $mt[$i-1]^($mt[$i-1]>>30);
111 10     10   7901 { use integer; $mt[$i] = (1812433253 * $xor + $i) & 0xffffffff }
  10         106  
  10         52  
  11837         11755  
  11837         27857  
112             }
113 19         47 $self->{mt} = \@mt;
114 19         127 $self->{mti} = $N;
115 19         32 $self->{seed} = ${$self->{mt}}[0];
  19         72  
116             }
117              
118              
119             sub _mt_setup_array {
120 3     3   8 my ($self, @seeds) = @_;
121 3         8 @seeds = map{ $_ & 0xffffffff }@seeds; # limit seeds to 32 bits
  12         23  
122 3         11 $self->_mt_init_seed( 19650218 );
123 3         38 my @mt = @{$self->{mt}};
  3         145  
124 3         7 my $i = 1;
125 3         4 my $j = 0;
126 3         5 my $n = @seeds;
127 3 50       9 my $k = $N > $n ? $N : $n;
128 3         5 my ($uint32, $xor);
129 3         10 for (; $k; $k--) {
130 1872         2521 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
131 10     10   1916 { use integer; $uint32 = ($xor * 1664525) & 0xffffffff }
  10         22  
  10         33  
  1872         1938  
  1872         2286  
132 1872         2110 $mt[$i] = ($mt[$i] ^ $uint32);
133 10     10   503 { use integer; $mt[$i] = ($mt[$i] + $seeds[$j] + $j) & 0xffffffff }
  10         21  
  10         39  
  1872         1778  
  1872         2570  
134 1872         1916 $i++; $j++;
  1872         1772  
135 1872 100       3103 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         13  
  3         5  
136 1872 100       4654 if ($j>=$n) { $j=0; }
  468         922  
137             }
138 3         11 for ($k=$N-1; $k; $k--) {
139 1869         2429 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
140 10     10   804 { use integer; $uint32 = ($xor * 1566083941) & 0xffffffff }
  10         20  
  10         38  
  1869         1709  
  1869         2236  
141 1869         2190 $mt[$i] = ($mt[$i] ^ $uint32) - $i;
142 1869         1803 $i++;
143 1869 100       4854 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         5  
  3         8  
144             }
145 3         5 $mt[0] = 0x80000000;
146 3         8 $self->{mt} = \@mt;
147 3         34 $self->{seed} = ${$self->{mt}}[0];
  3         13  
148             }
149              
150              
151             sub _mt_genrand {
152 36     36   57 my ($self) = @_;
153 36         90 return $self->_mt_genirand*(1.0/4294967296.0);
154             }
155              
156              
157             sub _mt_genirand {
158 60     60   81 my ($self) = @_;
159 60         71 my ($kk, $y);
160 60         114 my @mag01 = (0x0, 0x9908b0df);
161 60 100       169 if ($self->{mti} >= $N) {
162 12         59 for ($kk = 0; $kk < $N-$M; $kk++) {
163 2724         4324 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
164 2724         7517 $self->{mt}->[$kk] = $self->{mt}->[$kk+$M] ^ ($y >> 1) ^ $mag01[$y & 1];
165             }
166 12         72 for (; $kk < $N-1; $kk++) {
167 4752         7680 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
168 4752         13448 $self->{mt}->[$kk] = $self->{mt}->[$kk+($M-$N)] ^ ($y >> 1) ^ $mag01[$y & 1];
169             }
170 12         38 $y = ($self->{mt}->[$N-1] & $UP_MASK) | ($self->{mt}->[0] & $LOW_MASK);
171 12         40 $self->{mt}->[$N-1] = $self->{mt}->[$M-1] ^ ($y >> 1) ^ $mag01[$y & 1];
172 12         21 $self->{mti} = 0;
173             }
174 60         126 $y = $self->{mt}->[$self->{mti}++];
175 60         87 $y ^= $y >> 11;
176 60         123 $y ^= ($y << 7) & 0x9d2c5680;
177 60         77 $y ^= ($y << 15) & 0xefc60000;
178 60         73 $y ^= $y >> 18;
179 60         348 return $y;
180             }
181              
182              
183             sub import {
184 10     10   4698 no strict 'refs';
  10         20  
  10         1127  
185 10     10   108 my $pkg = caller;
186 10         25 for my $sym (@_) {
187 22 100 100     248 *{"${pkg}::$sym"} = \&$sym if $sym eq "srand" or $sym eq "rand" or $sym eq "irand";
  12   100     132  
188             }
189             }
190              
191              
192             1;
193              
194             __END__