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   246678 use strict;
  10         24  
  10         329  
4 10     10   83 use warnings;
  10         23  
  10         306  
5              
6 10     10   51 use vars qw($VERSION);
  10         23  
  10         5715  
7             $VERSION = 1.14;
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 77 my ($class, @seeds) = @_;
20 16         30 my $self = {};
21 16         34 bless $self, $class;
22             # Seed the random number generator
23 16         53 $self->set_seed(@seeds);
24 16         66 return $self;
25             }
26              
27              
28             sub rand {
29             # Generate a random number in requested range
30 36     36 1 4285 my ($self, $range) = @_;
31 36 100       98 if (ref $self) {
32 16   100     82 return ($range || 1) * $self->_mt_genrand();
33             }
34             else {
35 20         26 $range = $self;
36 20 100       54 Math::Random::MT::Perl::srand() unless defined $gen;
37 20   100     110 return ($range || 1) * $gen->_mt_genrand();
38             }
39             }
40              
41              
42             sub irand {
43             # Generate a random integer
44 24     24 1 4758 my ($self) = @_;
45 24 100       64 if (ref $self) {
46 16         38 return $self->_mt_genirand();
47             }
48             else {
49 8 50       24 Math::Random::MT::Perl::srand() unless defined $gen;
50 8         22 return $gen->_mt_genirand();
51             }
52             }
53              
54              
55             sub get_seed {
56             # Get the seed
57 10     10 1 18 my ($self) = @_;
58 10         23 return $self->{seed};
59             }
60              
61              
62             sub set_seed {
63             # Set the seed
64 17     17 1 37 my ($self, @seeds) = @_;
65 17         78 $self->{mt} = undef;
66 17         41 $self->{mti} = undef;
67 17         31 $self->{seed} = undef;
68 17 100 66     103 @seeds > 1 ? $self->_mt_setup_array(@seeds) :
69             $self->_mt_init_seed($seeds[0]||_rand_seed());
70 17         57 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 40 my (@seeds) = @_;
78 9 100       33 if (not @seeds) {
79 7         19 $seeds[0] = _rand_seed();
80             }
81 9         53 $gen = Math::Random::MT::Perl->new(@seeds);
82 9         88 my $seed = $gen->get_seed;
83 9         45 return $seed;
84             }
85              
86              
87             sub _rand_seed {
88 9     9   15 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         203 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   31 my ($self, $seed) = @_;
107 17         28 my @mt;
108 17         37 $mt[0] = $seed & 0xffffffff;
109 17         58 for ( my $i = 1; $i < $N; $i++ ) {
110 10591         14490 my $xor = $mt[$i-1]^($mt[$i-1]>>30);
111 10     10   8493 { use integer; $mt[$i] = (1812433253 * $xor + $i) & 0xffffffff }
  10         109  
  10         50  
  10591         10676  
  10591         24986  
112             }
113 17         43 $self->{mt} = \@mt;
114 17         86 $self->{mti} = $N;
115 17         27 $self->{seed} = ${$self->{mt}}[0];
  17         61  
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         40 my @mt = @{$self->{mt}};
  3         137  
124 3         7 my $i = 1;
125 3         4 my $j = 0;
126 3         32 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         2574 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
131 10     10   2048 { use integer; $uint32 = ($xor * 1664525) & 0xffffffff }
  10         20  
  10         36  
  1872         1879  
  1872         2477  
132 1872         2311 $mt[$i] = ($mt[$i] ^ $uint32);
133 10     10   563 { use integer; $mt[$i] = ($mt[$i] + $seeds[$j] + $j) & 0xffffffff }
  10         25  
  10         36  
  1872         1836  
  1872         2665  
134 1872         1947 $i++; $j++;
  1872         1881  
135 1872 100       3363 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         11  
  3         7  
136 1872 100       4952 if ($j>=$n) { $j=0; }
  468         1018  
137             }
138 3         12 for ($k=$N-1; $k; $k--) {
139 1869         2559 $xor = $mt[$i-1] ^ ($mt[$i-1] >> 30);
140 10     10   915 { use integer; $uint32 = ($xor * 1566083941) & 0xffffffff }
  10         27  
  10         37  
  1869         1873  
  1869         2354  
141 1869         2380 $mt[$i] = ($mt[$i] ^ $uint32) - $i;
142 1869         1905 $i++;
143 1869 100       5292 if ($i>=$N) { $mt[0] = $mt[$N-1]; $i=1; }
  3         5  
  3         7  
144             }
145 3         4 $mt[0] = 0x80000000;
146 3         7 $self->{mt} = \@mt;
147 3         33 $self->{seed} = ${$self->{mt}}[0];
  3         13  
148             }
149              
150              
151             sub _mt_genrand {
152 36     36   54 my ($self) = @_;
153 36         86 return $self->_mt_genirand*(1.0/4294967296.0);
154             }
155              
156              
157             sub _mt_genirand {
158 60     60   79 my ($self) = @_;
159 60         75 my ($kk, $y);
160 60         122 my @mag01 = (0x0, 0x9908b0df);
161 60 100       159 if ($self->{mti} >= $N) {
162 12         71 for ($kk = 0; $kk < $N-$M; $kk++) {
163 2724         4555 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
164 2724         7563 $self->{mt}->[$kk] = $self->{mt}->[$kk+$M] ^ ($y >> 1) ^ $mag01[$y & 1];
165             }
166 12         81 for (; $kk < $N-1; $kk++) {
167 4752         7759 $y = ($self->{mt}->[$kk] & $UP_MASK) | ($self->{mt}->[$kk+1] & $LOW_MASK);
168 4752         13248 $self->{mt}->[$kk] = $self->{mt}->[$kk+($M-$N)] ^ ($y >> 1) ^ $mag01[$y & 1];
169             }
170 12         37 $y = ($self->{mt}->[$N-1] & $UP_MASK) | ($self->{mt}->[0] & $LOW_MASK);
171 12         42 $self->{mt}->[$N-1] = $self->{mt}->[$M-1] ^ ($y >> 1) ^ $mag01[$y & 1];
172 12         25 $self->{mti} = 0;
173             }
174 60         124 $y = $self->{mt}->[$self->{mti}++];
175 60         86 $y ^= $y >> 11;
176 60         85 $y ^= ($y << 7) & 0x9d2c5680;
177 60         79 $y ^= ($y << 15) & 0xefc60000;
178 60         73 $y ^= $y >> 18;
179 60         345 return $y;
180             }
181              
182              
183             sub import {
184 10     10   4947 no strict 'refs';
  10         20  
  10         1163  
185 10     10   123 my $pkg = caller;
186 10         29 for my $sym (@_) {
187 22 100 100     266 *{"${pkg}::$sym"} = \&$sym if $sym eq "srand" or $sym eq "rand" or $sym eq "irand";
  12   100     113  
188             }
189             }
190              
191              
192             1;
193              
194             __END__