File Coverage

blib/lib/Math/Random/MT/Perl.pm
Criterion Covered Total %
statement 130 130 100.0
branch 22 24 91.6
condition 12 13 92.3
subroutine 20 20 100.0
pod 6 6 100.0
total 190 193 98.4


line stmt bran cond sub pod time code
1             package Math::Random::MT::Perl;
2              
3 10     10   358834 use strict;
  10         26  
  10         374  
4 10     10   56 use warnings;
  10         21  
  10         298  
5              
6 10     10   53 use vars qw($VERSION);
  10         158  
  10         6613  
7             $VERSION = 1.13;
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 16     16 1 89 my ($class, @seeds) = @_;
20 16         36 my $self = {};
21 16         61 bless $self, $class;
22             # Seed the random number generator
23 16         62 $self->set_seed(@seeds);
24 16         106 return $self;
25             }
26              
27              
28             sub rand {
29             # Generate a random number in requested range
30 36     36 1 5370 my ($self, $range) = @_;
31 36 100       179 if (ref $self) {
32 16   100     90 return ($range || 1) * $self->_mt_genrand();
33             }
34             else {
35 20         29 $range = $self;
36 20 100       58 Math::Random::MT::Perl::srand() unless defined $gen;
37 20   100     1074 return ($range || 1) * $gen->_mt_genrand();
38             }
39             }
40              
41              
42             sub irand {
43             # Generate a random integer
44 24     24 1 4516 my ($self) = @_;
45 24 100       73 if (ref $self) {
46 16         34 return $self->_mt_genirand();
47             }
48             else {
49 8 50       21 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 10     10 1 21 my ($self) = @_;
58 10         29 return $self->{seed};
59             }
60              
61              
62             sub set_seed {
63             # Set the seed
64 17     17 1 40 my ($self, @seeds) = @_;
65 17         91 $self->{mt} = undef;
66 17         56 $self->{mti} = undef;
67 17         35 $self->{seed} = undef;
68 17 100 66     188 @seeds > 1 ? $self->_mt_setup_array(@seeds) :
69             $self->_mt_init_seed($seeds[0]||_rand_seed());
70 17         76 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 9     9 1 50 my (@seeds) = @_;
78 9 100       35 if (not @seeds) {
79 7         27 $seeds[0] = _rand_seed();
80             }
81 9         70 $gen = Math::Random::MT::Perl->new(@seeds);
82 9         123 my $seed = $gen->get_seed;
83 9         72 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         301 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 17     17   40 my ($self, $seed) = @_;
107 17         27 my @mt;
108 17         47 $mt[0] = $seed & 0xffffffff;
109 17         159 for ( my $i = 1; $i < $N; $i++ ) {
110 10591         14086 my $xor = $mt[$i-1]^($mt[$i-1]>>30);
111 10     10   10481 { use integer; $mt[$i] = (1812433253 * $xor + $i) & 0xffffffff }
  10         104  
  10         50  
  10591         9568  
  10591         24037  
112             }
113 17         69 $self->{mt} = \@mt;
114 17         196 $self->{mti} = $N;
115 17         37 $self->{seed} = ${$self->{mt}}[0];
  17         72  
116             }
117              
118              
119             sub _mt_setup_array {
120 3     3   10 my ($self, @seeds) = @_;
121 3         11 @seeds = map{ $_ & 0xffffffff }@seeds; # limit seeds to 32 bits
  12         27  
122 3         16 $self->_mt_init_seed( 19650218 );
123 3         48 my @mt = @{$self->{mt}};
  3         201  
124 3         8 my $i = 1;
125 3         6 my $j = 0;
126 3         6 my $n = @seeds;
127 3 50       11 my $k = $N > $n ? $N : $n;
128 3         7 my ($uint32, $xor);
129 3         12 for (; $k; $k--) {
130 1872         2632 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
131 10     10   2215 { use integer; $uint32 = ($xor * 1664525) & 0xffffffff }
  10         30  
  10         38  
  1872         1734  
  1872         2184  
132 1872         2306 $mt[$i] = ($mt[$i] ^ $uint32);
133 10     10   516 { use integer; $mt[$i] = ($mt[$i] + $seeds[$j] + $j) & 0xffffffff }
  10         20  
  10         38  
  1872         1900  
  1872         2511  
134 1872         2558 $i++; $j++;
  1872         1845  
135 1872 100       3275 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         762  
  3         664  
136 1872 100       4714 if ($j>=$n) { $j=0; }
  468         896  
137             }
138 3         16 for ($k=$N-1; $k; $k--) {
139 1869         2572 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
140 10     10   915 { use integer; $uint32 = ($xor * 1566083941) & 0xffffffff }
  10         15  
  10         41  
  1869         1751  
  1869         2010  
141 1869         2394 $mt[$i] = ($mt[$i] ^ $uint32) - $i;
142 1869         1689 $i++;
143 1869 100       4565 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         10  
  3         8  
144             }
145 3         9 $mt[0] = 0x80000000;
146 3         101 $self->{mt} = \@mt;
147 3         48 $self->{seed} = ${$self->{mt}}[0];
  3         27  
148             }
149              
150              
151             sub _mt_genrand {
152 36     36   64 my ($self) = @_;
153 36         91 return $self->_mt_genirand*(1.0/4294967296.0);
154             }
155              
156              
157             sub _mt_genirand {
158 60     60   79 my ($self) = @_;
159 60         72 my ($kk, $y);
160 60         121 my @mag01 = (0x0, 0x9908b0df);
161 60 100       249 if ($self->{mti} >= $N) {
162 12         627 for ($kk = 0; $kk < $N-$M; $kk++) {
163 2724         4880 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
164 2724         8491 $self->{mt}->[$kk] = $self->{mt}->[$kk+$M] ^ ($y >> 1) ^ $mag01[$y & 1];
165             }
166 12         1223 for (; $kk < $N-1; $kk++) {
167 4752         7648 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
168 4752         12746 $self->{mt}->[$kk] = $self->{mt}->[$kk+($M-$N)] ^ ($y >> 1) ^ $mag01[$y & 1];
169             }
170 12         42 $y = ($self->{mt}->[$N-1] & $UP_MASK) | ($self->{mt}->[0] & $LOW_MASK);
171 12         45 $self->{mt}->[$N-1] = $self->{mt}->[$M-1] ^ ($y >> 1) ^ $mag01[$y & 1];
172 12         32 $self->{mti} = 0;
173             }
174 60         124 $y = $self->{mt}->[$self->{mti}++];
175 60         93 $y ^= $y >> 11;
176 60         90 $y ^= ($y << 7) & 0x9d2c5680;
177 60         71 $y ^= ($y << 15) & 0xefc60000;
178 60         67 $y ^= $y >> 18;
179 60         519 return $y;
180             }
181              
182              
183             sub import {
184 10     10   5408 no strict 'refs';
  10         20  
  10         1206  
185 10     10   105 my $pkg = caller;
186 10         30 for my $sym (@_) {
187 22 100 100     249 *{"${pkg}::$sym"} = \&$sym if $sym eq "srand" or $sym eq "rand" or $sym eq "irand";
  12   100     111  
188             }
189             }
190              
191              
192             1;
193              
194             __END__