File Coverage

blib/lib/Math/Random/MT/Auto.pm
Criterion Covered Total %
statement 243 281 86.4
branch 63 96 65.6
condition 11 20 55.0
subroutine 33 33 100.0
pod 6 8 75.0
total 356 438 81.2


line stmt bran cond sub pod time code
1             package Math::Random::MT::Auto; {
2              
3             require 5.006;
4              
5 13     13   139711 use strict;
  13         31  
  13         1056  
6 13     13   73 use warnings;
  13         29  
  13         1073  
7              
8             our $VERSION = '6.22';
9             my $XS_VERSION = $VERSION;
10             $VERSION = eval $VERSION;
11              
12             require Carp;
13 13     13   90 use Scalar::Util 1.18;
  13         502  
  13         1141  
14              
15             require XSLoader;
16             XSLoader::load('Math::Random::MT::Auto', $XS_VERSION);
17              
18 13     13   18258 use Object::InsideOut 2.06 ':hash_only';
  13         860088  
  13         96  
19 13     13   1443 use Object::InsideOut::Util 'shared_copy';
  13         23  
  13         116  
20              
21             # Exceptions thrown by this package
22             use Exception::Class (
23 13         144 'MRMA::Args' => {
24             'isa' => 'OIO::Args',
25             'description' =>
26             'Math::Random::MT::Auto exception that indicates an argument error',
27             },
28 13     13   1045 );
  13         26  
29              
30              
31             ### Inside-out Object Attributes ###
32              
33             # Object data is stored in these attribute hashes, and is keyed to the object
34             # by a unique ID that is stored in the object's scalar reference. For this
35             # class, that ID is the address of the PRNG's internal memory.
36             #
37             # These hashes are declared using the 'Field' attribute.
38              
39             my %sources_for :Field; # Sources from which to obtain random seed data
40             my %seed_for :Field; # Last seed sent to the PRNG
41              
42              
43             # Seed source subroutine dispatch table
44             my %DISPATCH = (
45             'device' => \&_acq_device,
46             'random_org' => \&_acq_www,
47             'hotbits' => \&_acq_www,
48             'rn_info' => \&_acq_www,
49             );
50              
51              
52             ### Module Initialization ###
53              
54             # Handle exportation of subroutine names, user-specified and default
55             # seeding sources. Also, auto-seeding of the standalone PRNG.
56             sub import
57             {
58 19     19   565 my $class = shift; # Not used
59              
60             # Exportable subroutines
61 19         39 my %EXPORT_OK;
62 19         214 @EXPORT_OK{qw(rand irand shuffle gaussian
63             exponential erlang poisson binomial
64             srand get_seed set_seed get_state set_state)} = undef;
65              
66 19         41 my $auto_seed = 1; # Flag to auto-seed the standalone PRNG
67              
68             # Handle entries in the import list
69 19         54 my $caller = caller();
70 19         149 my @sources;
71 19         118 while (my $sym = shift) {
72 38 100       171 if (exists($EXPORT_OK{lc($sym)})) {
    100          
73             # Export subroutine names
74 13     13   11701 no strict 'refs';
  13         26  
  13         11034  
75 26         34 *{$caller.'::'.$sym} = \&{lc($sym)};
  26         250  
  26         70  
76              
77             } elsif ($sym =~ /^:(no|!)?auto$/i) {
78             # To auto-seed (:auto is default) or not (:!auto or :noauto)
79 7         32 $auto_seed = not defined($1);
80              
81             } else {
82             # User-specified seed acquisition sources
83             # or user-defined seed acquisition subroutines
84 5         14 push(@sources, $sym);
85             # Add max. source count, if specified
86 5 50 33     36 if (@_ && Scalar::Util::looks_like_number($_[0])) {
87 0         0 push(@sources, shift);
88             }
89             }
90             }
91              
92             # Setup default sources, if needed
93 19 100       69 if (! @sources) {
94 14 50       303 if (exists($DISPATCH{'win32'})) {
    50          
    0          
95 0         0 push(@sources, 'win32');
96             } elsif (-e '/dev/urandom') {
97 14         38 push(@sources, '/dev/urandom');
98             } elsif (-e '/dev/random') {
99 0         0 push(@sources, '/dev/random');
100             }
101 14         25 push(@sources, 'random_org');
102             }
103              
104             # Create standalone PRNG
105 19 100       278 $MRMA::PRNG = Math::Random::MT::Auto->new(
106             'SOURCE' => \@sources,
107             ($auto_seed) ? () : ( 'SEED' => [ $$, time(), Scalar::Util::refaddr(\$VERSION) ] )
108             );
109             }
110              
111              
112             ### Dual-Interface (Functional and OO) Subroutines ###
113             #
114             # The subroutines below work both as regular 'functions' for the functional
115             # interface to the standalone PRNG, as well as methods for the OO interface
116             # to PRNG objects.
117              
118             # Starts PRNG with random seed using specified sources (if any)
119             sub srand
120             {
121             # Generalize for both OO and standalone PRNGs
122 1 50   1 1 1568 my $obj = (Scalar::Util::blessed($_[0])) ? shift : $MRMA::PRNG;
123              
124 1 50       6 if (@_) {
125             # If sent seed by mistake, then send it to set_seed()
126 1 50 33     13 if (Scalar::Util::looks_like_number($_[0]) || ref($_[0]) eq 'ARRAY') {
127 0         0 $obj->set_seed(@_);
128 0         0 return;
129             }
130              
131             # Save specified sources
132 1         9 $sources_for{$$obj} = shared_copy(\@_);
133             }
134              
135             # Acquire seed from sources
136 1         150 _acquire_seed($obj);
137              
138             # Seed the PRNG
139 1         41 _seed_prng($obj);
140             }
141              
142              
143             # Return ref to PRNG's saved seed (if any)
144             sub get_seed
145             {
146             # Generalize for both OO and standalone PRNGs
147 2 100   2 1 1920 my $obj = (Scalar::Util::blessed($_[0])) ? shift : $MRMA::PRNG;
148              
149 2 100       17 if (wantarray()) {
150 1         2 return (@{$seed_for{$$obj}});
  1         7  
151             }
152 1         6 return ($seed_for{$$obj});
153             }
154              
155              
156             # Apply supplied seed, if given, to the PRNG,
157             sub set_seed
158             {
159             # Generalize for both OO and standalone PRNGs
160 2 100   2 1 569 my $obj = (Scalar::Util::blessed($_[0])) ? shift : $MRMA::PRNG;
161              
162             # Check argument
163 2 50       8 if (! @_) {
164 0         0 MRMA::Args->throw('message' => q/Missing argument to '->set_seed()'/);
165             }
166              
167             # Save a copy of the seed
168 2 50       8 if (ref($_[0]) eq 'ARRAY') {
169 2         11 $seed_for{$$obj} = shared_copy($_[0]);
170             } else {
171 0         0 $seed_for{$$obj} = shared_copy(\@_);
172             }
173              
174             # Seed the PRNG
175 2         91 _seed_prng($obj);
176             }
177              
178              
179             # Return copy of PRNG's current state
180             sub get_state
181             {
182             # Generalize for both OO and standalone PRNGs
183 7 100   7 1 949080 my $obj = (Scalar::Util::blessed($_[0])) ? shift : $MRMA::PRNG;
184              
185 7 50       31 if (wantarray()) {
186 7         11 return (@{Math::Random::MT::Auto::_::get_state($obj)});
  7         672  
187             }
188 0         0 return (Math::Random::MT::Auto::_::get_state($obj));
189             }
190              
191              
192             # Set PRNG to supplied state
193             sub set_state
194             {
195             # Generalize for both OO and standalone PRNGs
196 6 100   6 1 22350 my $obj = (Scalar::Util::blessed($_[0])) ? shift : $MRMA::PRNG;
197              
198             # Input can be array ref or array
199 6 50       41 if (ref($_[0]) eq 'ARRAY') {
200 6         233 Math::Random::MT::Auto::_::set_state($obj, $_[0]);
201             } else {
202 0           Math::Random::MT::Auto::_::set_state($obj, \@_);
203             }
204             }
205              
206              
207             ### Inside-out Object Internal Subroutines ###
208              
209             # Object Constructor
210             sub _new_prng :ID
211             {
212 34         3652253 return (Math::Random::MT::Auto::_::new_prng());
213 13     13   81 }
  13         27  
  13         94  
214              
215             sub _clone_state :Replicate
216             {
217 2         164 my ($from_obj, $to_obj) = @_;
218              
219 2         67 my $state = Math::Random::MT::Auto::_::get_state($from_obj);
220 2         46 Math::Random::MT::Auto::_::set_state($to_obj, $state);
221 13     13   4370 }
  13         31  
  13         64  
222              
223             sub _free_prng :Destroy
224             {
225 21         2793222 Math::Random::MT::Auto::_::free_prng(shift);
226 13     13   2975 }
  13         32  
  13         115  
227              
228             my %init_args :InitArgs = (
229             'SOURCE' => {
230             'REGEX' => qr/^(?:source|src)s?$/i,
231             'FIELD' => \%sources_for,
232             'TYPE' => 'LIST',
233             },
234             'SEED' => {
235             'REGEX' => qr/^seed$/i,
236             'DEFAULT' => [],
237             'FIELD' => \%seed_for,
238             'TYPE' => 'LIST',
239             },
240             'STATE' => {
241             'REGEX' => qr/^state$/i,
242             'TYPE' => 'ARRAY',
243             },
244             );
245              
246             # Object initializer - for internal use only
247             sub _init :Init
248             {
249 30         13692 my $self = $_[0];
250 30         58 my $args = $_[1]; # Hash ref containing arguments from object
251             # constructor as specified by %init_args above
252              
253             # If no sources specified, then use default sources from standalone PRNG
254 30 100       144 if (! exists($sources_for{$$self})) {
255 10         21 my @srcs = @{$sources_for{$$MRMA::PRNG}};
  10         163  
256 10         44 $self->set(\%sources_for, \@srcs);
257             }
258              
259             # If state is specified, then use it
260 30 100       354 if (exists($args->{'STATE'})) {
261 1         6 $self->set_state($args->{'STATE'});
262              
263             } else {
264             # Acquire seed, if none provided
265 29 100       46 if (! @{$seed_for{$$self}}) {
  29         117  
266 18         79 _acquire_seed($self);
267             }
268              
269             # Seed the PRNG
270 29         192 _seed_prng($self);
271             }
272 13     13   6155 }
  13         27  
  13         62  
273              
274              
275             ### Overloading ###
276              
277             sub as_string :Stringify :Numerify
278             {
279 2     2 0 505 return ($_[0]->irand());
280 13     13   2933 }
  13         31  
  13         60  
281              
282             sub bool :Boolify
283             {
284 1     1 0 462 return ($_[0]->irand() & 1);
285 13     13   3517 }
  13         27  
  13         55  
286              
287             sub array :Arrayify
288             {
289 2     2 1 423 my $self = $_[0];
290 2   100     10 my $count = $_[1] || 1;
291              
292 2         1 my @ary;
293 2         3 do {
294 4         15 push(@ary, $self->irand());
295             } while (--$count > 0);
296              
297 2         7 return (\@ary);
298 13     13   9307 }
  13         23  
  13         72  
299              
300             sub _code :Codify
301             {
302 1     1   437 my $self = $_[0];
303 1     1   5 return (sub { $self->irand(); });
  1         9  
304 13     13   3275 }
  13         23  
  13         58  
305              
306              
307             ### Serialization ###
308              
309             # Support for ->dump() method
310             sub _dump :DUMPER
311             {
312 2         3336 my $obj = shift;
313              
314 2         4 my @seed = @{$seed_for{$$obj}};
  2         65  
315             # Must filter out code refs from sources
316 2         5 my @sources = grep { ref($_) ne 'CODE' } @{$sources_for{$$obj}};
  4         10  
  2         5  
317 2         13 my @state = $obj->get_state();
318              
319             return ({
320 2         40 'SOURCES' => \@sources,
321             'SEED' => \@seed,
322             'STATE' => \@state,
323             });
324 13     13   3761 }
  13         31  
  13         59  
325              
326             # Support for Object::InsideOut::pump()
327             sub _pump :PUMPER
328             {
329 2         47 my ($obj, $data) = @_;
330              
331 2         11 $obj->set(\%sources_for, $$data{'SOURCES'});
332 2         66 $obj->set(\%seed_for, $$data{'SEED'});
333 2         51 $obj->set_state($$data{'STATE'});
334 13     13   3655 }
  13         25  
  13         354  
335              
336              
337             ### Internal Subroutines ###
338              
339             # Constants #
340              
341             # Size of Perl's integers (32- or 64-bit) and corresponding unpack code
342             require Config;
343             my $INT_SIZE = $Config::Config{'uvsize'};
344             my $UNPACK_CODE = ($INT_SIZE == 8) ? 'Q' : 'L';
345             # Number of ints for a full 19968-bit seed
346             my $FULL_SEED = 2496 / $INT_SIZE;
347              
348              
349             # If Windows XP and Win32::API, then make 'win32' a valid source
350             if (($^O eq 'MSWin32') || ($^O eq 'cygwin')) {
351             eval { require Win32; };
352             if (! $@) {
353             my ($id, $major, $minor) = (Win32::GetOSVersion())[4,1,2];
354             if (defined($minor) &&
355             (($id > 2) ||
356             ($id == 2 && $major > 5) ||
357             ($id == 2 && $major == 5 && $minor >= 1)))
358             {
359             eval {
360             # Suppress (harmless) warning about Win32::API::Type's INIT block
361             local $SIG{__WARN__} = sub {
362             if ($_[0] !~ /^Too late to run INIT block/) {
363             print(STDERR "$_[0]\n"); # Output other warnings
364             }
365             };
366              
367             require Win32::API;
368             };
369             if (! $@) {
370             $DISPATCH{'win32'} = \&_acq_win32;
371             }
372             }
373             }
374             }
375              
376              
377             # Acquire seed data from specific sources
378             sub _acquire_seed :PRIVATE
379             {
380 19         189 my $prng = $_[0];
381              
382 19         49 my $sources = $sources_for{$$prng};
383 19         46 my $seed = $seed_for{$$prng};
384              
385             # Acquire seed data until we have a full seed,
386             # or until we run out of sources
387 19         50 @{$seed} = ();
  19         67  
388 19   100     74 for (my $ii=0;
  38         212  
389 24         155 (@{$seed} < $FULL_SEED) && ($ii < @{$sources});
390             $ii++)
391             {
392 19         43 my $src = $sources->[$ii];
393 19         61 my $src_key = lc($src); # Suitable as hash key
394              
395             # Determine amount of data needed
396 19         29 my $need = $FULL_SEED - @{$seed};
  19         40  
397 19 50 66     41 if (($ii+1 < @{$sources}) &&
  19         143  
398             Scalar::Util::looks_like_number($sources->[$ii+1]))
399             {
400 0 0       0 if ($sources->[++$ii] < $need) {
401 0         0 $need = $sources->[$ii];
402             }
403             }
404              
405 19 100       505 if (ref($src) eq 'CODE') {
    100          
    50          
406             # User-supplied seeding subroutine
407 1         6 $src->($seed, $need);
408              
409             } elsif (defined($DISPATCH{$src_key})) {
410             # Module defined seeding source
411             # Execute subroutine ref from dispatch table
412 3         15 $DISPATCH{$src_key}->($src_key, $prng, $need);
413              
414             } elsif (-e $src) {
415             # Random device or file
416 15         66 $DISPATCH{'device'}->($src, $prng, $need);
417              
418             } else {
419 0         0 Carp::carp("Unknown seeding source: $src");
420             }
421             }
422              
423 19 100       38 if (! @{$seed}) {
  19 100       81  
  18         103  
424             # Complain about not getting any seed data, and provide a minimal seed
425 1         165 Carp::carp('No seed data obtained from sources - Setting minimal seed using PID and time');
426 1         7 push(@{$seed}, $$, time());
  1         17  
427              
428             } elsif (@{$seed} < $FULL_SEED) {
429             # Complain about not getting a full seed
430 4         10 Carp::carp('Partial seed - only ' . scalar(@{$seed}) . ' of ' . $FULL_SEED);
  4         1146  
431             }
432 13     13   11142 }
  13         24  
  13         63  
433              
434              
435             # Acquire seed data from a device/file
436             sub _acq_device :PRIVATE
437             {
438 15         32 my $device = $_[0];
439 15         28 my $prng = $_[1];
440 15         38 my $need = $_[2];
441              
442             # Try opening device/file
443 15         42 my $FH;
444 15 50       612 if (! open($FH, '<', $device)) {
445 0         0 Carp::carp("Failure opening random device '$device': $!");
446 0         0 return;
447             }
448 15         48 binmode($FH);
449              
450             # Try to set non-blocking mode (but not on Windows and Haiku)
451 15 50 33     154 if ($^O ne 'MSWin32' && $^O ne 'Haiku') {
452 15         32 eval {
453 15         124 require Fcntl;
454              
455 15         24 my $flags;
456 15 50       134 $flags = fcntl($FH, &Fcntl::F_GETFL, 0)
457             or die("Failed getting filehandle flags: $!\n");
458 15 50       150 fcntl($FH, &Fcntl::F_SETFL, $flags | &Fcntl::O_NONBLOCK)
459             or die("Failed setting filehandle flags: $!\n");
460             };
461 15 50       59 if ($@) {
462 0         0 Carp::carp("Failure setting non-blocking mode on random device '$device': $@");
463             }
464             }
465              
466             # Read data
467 15         119 for (1..$need) {
468 4680         6345 my $data;
469 4680         20331 my $cnt = read($FH, $data, $INT_SIZE);
470              
471 4680 100       7064 if (defined($cnt)) {
472             # Complain if we didn't get all the data we asked for
473 4368 100       7213 if ($cnt < $INT_SIZE) {
474 308         38437 Carp::carp("Random device '$device' exhausted");
475             }
476             # Add data to seed array
477 4368 100       15949 if ($cnt = int($cnt / $INT_SIZE)) {
478 4060         3797 push(@{$seed_for{$$prng}}, unpack("$UNPACK_CODE$cnt", $data));
  4060         12032  
479             }
480             } else {
481 312         57738 Carp::carp("Failure reading from random device '$device': $!");
482             }
483             }
484 15         483 close($FH);
485 13     13   7733 }
  13         26  
  13         72  
486              
487              
488             # Cached LWP::UserAgent object
489             my $LWP_UA;
490              
491             # Subroutine to acquire seed data from Internet sources
492             sub _acq_www :PRIVATE
493             {
494 3         8 my $src = $_[0];
495 3         5 my $prng = $_[1];
496 3         7 my $need = $_[2];
497              
498             # First, create user-agent object, if needed
499 3 50       12 if (! $LWP_UA) {
500 3         6 eval {
501 3         4655 require LWP::UserAgent;
502 3         182419 $LWP_UA = LWP::UserAgent->new('timeout' => 5, 'env_proxy' => 1);
503             };
504 3 50       71584 if ($@) {
505 0         0 Carp::carp("Failure creating user-agent: $@");
506 0         0 return;
507             }
508             }
509              
510             ### Internal subroutines for processing Internet data
511              
512             # Process data from random.org
513             my $random_org = sub {
514 1         21 my $prng = $_[0];
515 1         3 my $content = $_[1];
516              
517             # Add data to seed array
518 1         3 push(@{$seed_for{$$prng}}, unpack("$UNPACK_CODE*", $content));
  1         278  
519 3         21 };
520              
521             # Process data from HotBits
522             my $hotbits = sub {
523 1         18 my $prng = $_[0];
524 1         2 my $content = $_[1];
525              
526 1 50       8 if ($content =~ /exceeded your 24-hour quota/) {
527             # Complain about exceeding Hotbits quota
528 0         0 Carp::carp('You have exceeded your 24-hour quota for HotBits.');
529             } else {
530             # Add data to seed array
531 1         2 push(@{$seed_for{$$prng}}, unpack("$UNPACK_CODE*", $content));
  1         198  
532             }
533 3         17 };
534              
535             # Process data from RandomNumbers.info
536             my $rn_info = sub {
537 1         54 my $prng = $_[0];
538 1         3 my $content = $_[1];
539              
540             # Extract digits from web page
541 1         847 my (@bytes) = $content =~ / ([\d]+)/g;
542             # Make sure we have correct number of bytes for complete integers.
543             # Also gets rid of copyright year that gets picked up from end of web page.
544 1         24 do {
545 1         79 pop(@bytes);
546             } while (@bytes % $INT_SIZE);
547 1         4 while (@bytes) {
548             # Construct integers from bytes
549 125         116 my $num = 0;
550 125         148 for (1 .. $INT_SIZE) {
551 1000         1142 $num = ($num << 8) + pop(@bytes);
552             }
553             # Add integer data to seed array
554 125         110 push(@{$seed_for{$$prng}}, $num);
  125         450  
555             }
556 3         28 };
557              
558             ### Internet seed source information table
559 3         49 my %www = (
560             'random_org' => {
561             'sitename' => 'random.org',
562             'URL' => 'http://www.random.org/cgi-bin/randbyte?nbytes=',
563             'max_bytes' => $FULL_SEED * $INT_SIZE,
564             'processor' => $random_org
565             },
566             'hotbits' => {
567             'sitename' => 'HotBits',
568             'URL' => 'http://www.fourmilab.ch/cgi-bin/uncgi/Hotbits?fmt=bin&nbytes=',
569             'max_bytes' => 2048,
570             'processor' => $hotbits
571             },
572             'rn_info' => {
573             'sitename' => 'RandomNumbers.info',
574             'URL' => 'http://www.randomnumbers.info/cgibin/wqrng.cgi?limit=255&amount=',
575             'max_bytes' => 1000,
576             'processor' => $rn_info
577             }
578             );
579              
580             # Number of bytes to request (observing maximum data limit)
581 3         10 my $bytes = $need * $INT_SIZE;
582 3 100       16 if ($bytes > $www{$src}{'max_bytes'}) {
583 2         6 $bytes = $www{$src}{'max_bytes'};
584             }
585              
586             # Request the data
587 3         4 my $res;
588 3         7 eval {
589             # Create request
590 3         36 my $req = HTTP::Request->new('GET' => $www{$src}{'URL'} . $bytes);
591             # Send the request
592 3         30413 $res = $LWP_UA->request($req);
593             };
594              
595             # Handle the response
596 3 50       2304606 if ($@) {
    50          
597 0         0 Carp::carp("Failure contacting $www{$src}{'sitename'}: $@");
598             } elsif ($res->is_success) {
599             # Process the data
600 3         70 $www{$src}{'processor'}->($prng, $res->content);
601             } else {
602 0         0 Carp::carp("Failure getting data from $www{$src}{'sitename'}: "
603             . $res->status_line);
604             }
605 13     13   11695 }
  13         196  
  13         75  
606              
607              
608             # Acquire seed data from Win XP random source
609             sub _acq_win32 :PRIVATE
610             {
611 0         0 my $src = $_[0]; # Not used
612 0         0 my $prng = $_[1];
613 0         0 my $need = $_[2];
614 0         0 my $bytes = $need * $INT_SIZE;
615              
616 0         0 eval {
617             # Import the random source function
618 0         0 my $func = Win32::API->new('ADVAPI32.DLL',
619             'SystemFunction036',
620             'PN', 'I');
621 0 0       0 if (! defined($func)) {
622 0         0 die("Failure importing 'SystemFunction036': $!\n");
623             }
624              
625             # Acquire the random data
626 0         0 my $buffer = chr(0) x $bytes;
627 0 0       0 if (! $func->Call($buffer, $bytes)) {
628 0         0 die("'SystemFunction036' failed: $^E\n");
629             }
630              
631             # Add data to seed array
632 0         0 push(@{$seed_for{$$prng}}, unpack("$UNPACK_CODE*", $buffer));
  0         0  
633             };
634 0 0       0 if ($@) {
635 0         0 Carp::carp("Failure acquiring Win XP random data: $@");
636             }
637 13     13   5743 }
  13         23  
  13         144  
638              
639              
640             # Seeds a PRNG
641             sub _seed_prng :PRIVATE
642             {
643 32         347 my $prng = $_[0];
644              
645 32         83 my $seed = $seed_for{$$prng}; # Get the seed for the PRNG
646              
647 32 50 33     157 if ($threads::shared::threads_shared && threads::shared::_id($seed)) {
648             # If the seed is thread-shared, then must make a non-shared copy to
649             # send to the PRNG
650 0         0 my @seed = @{$seed};
  0         0  
651 0         0 Math::Random::MT::Auto::_::seed_prng($prng, \@seed);
652              
653             } else {
654             # If no thread object sharing, then just send the seed
655 32         924 Math::Random::MT::Auto::_::seed_prng($prng, $seed);
656             }
657 13     13   3660 }
  13         29  
  13         80  
658              
659             } # End of package's lexical scope
660              
661             1;
662              
663             __END__