File Coverage

blib/lib/Math/Random.pm
Criterion Covered Total %
statement 155 162 95.6
branch 76 128 59.3
condition 19 68 27.9
subroutine 25 27 92.5
pod 20 22 90.9
total 295 407 72.4


line stmt bran cond sub pod time code
1             package Math::Random;
2              
3 3     3   540311 use v5.10;
  3         10  
4 3     3   11 use strict;
  3         18  
  3         61  
5 3     3   12 use warnings;
  3         5  
  3         97  
6              
7 3     3   10 use Carp;
  3         4  
  3         220  
8              
9 3     3   987 use parent qw( Exporter DynaLoader);
  3         719  
  3         14  
10              
11             our $VERSION = '0.75';
12              
13             ## no critic ( Wantarray DollarAB ComplexMappings )
14              
15             our @EXPORT = qw(random_normal
16             random_permutation
17             random_permuted_index
18             random_uniform
19             random_uniform_integer
20             random_seed_from_phrase
21             random_get_seed
22             random_set_seed_from_phrase
23             random_set_seed
24             );
25              
26             our @EXPORT_OK = qw(random_beta
27             random_chi_square
28             random_exponential
29             random_f
30             random_gamma
31             random_multivariate_normal
32             random_multinomial
33             random_noncentral_chi_square
34             random_noncentral_f
35             random_normal
36             random_permutation
37             random_permuted_index
38             random_uniform
39             random_poisson
40             random_uniform_integer
41             random_negative_binomial
42             random_binomial
43             random_seed_from_phrase
44             random_get_seed
45             random_set_seed_from_phrase
46             random_set_seed
47             random_integer
48             random_init_generator
49             random_set_antithetic
50             random_advance_state
51             random_get_generator_num
52             random_set_generator_num
53             );
54              
55             our %EXPORT_TAGS = ( all => [@EXPORT_OK] );
56              
57             bootstrap Math::Random $VERSION;
58              
59              
60             ### set seeds by default
61             salfph( get_seed() || scalar( localtime ) );
62              
63             #####################################################################
64             # RANDOM DEVIATE GENERATORS #
65             #####################################################################
66              
67             sub random_beta { # Arguments: ($n,$aa,$bb)
68              
69 4 50   4 1 3054 croak 'Usage: random_beta($n,$aa,$bb)' if @_ < 3;
70              
71 4         12 my ( $n, $aa, $bb ) = @_;
72 4 50 33     25 croak( "random_beta: ($aa = \$aa < 1.0E-37) or ($bb = \$bb < 1.0E-37)" )
73             if ( $aa < 1.0E-37 ) || ( $bb < 1.0E-37 );
74              
75             return wantarray
76 4 100       50 ? map { genbet( $aa, $bb ) } 1 .. $n
  3         21  
77             : genbet( $aa, $bb );
78             }
79              
80             sub random_chi_square { # Arguments: ($n,$df)
81 4 50   4 1 2950 croak 'Usage: random_chi_square(\$n,\$df)'
82             if @_ < 2;
83              
84 4         11 my ( $n, $df ) = @_;
85 4 50       16 croak "random_chi_square: $df = \$df <= 0"
86             if $df <= 0;
87              
88             return wantarray
89 4 100       32 ? map { genchi( $df ) } 1 .. $n
  3         37  
90             : genchi( $df );
91             }
92              
93             sub random_exponential { # Arguments: ($n,$av), defaults (1,1)
94 4 0   4 1 1743 return wantarray() ? ( genexp( 1 ) ) : genexp( 1 )
    50          
95             if @_ == 0; # default behavior if no arguments
96              
97 4         13 my ( $n, $av ) = @_;
98 4   50     10 $av //= 1; # default $av is 1
99 4 50       12 croak "random_exponential: $av = \$av < 0"
100             if $av < 0;
101              
102             return wantarray
103 4 100       30 ? map { genexp( $av ) } 1 .. $n
  3         38  
104             : genexp( $av );
105             }
106              
107             sub random_f { # Arguments: ($n,$dfn,$dfd)
108 4 50   4 1 3089 croak 'Usage: random_f($n,$dfn,$dfd)' if @_ < 3;
109 4         13 my ( $n, $dfn, $dfd ) = @_;
110 4 50 33     25 croak( "random_f: ($dfn = \$dfn <= 0) or ($dfd = \$dfd <= 0" )
111             if ( $dfn <= 0 ) || ( $dfd <= 0 );
112              
113             return wantarray
114 4 100       37 ? map { genf( $dfn, $dfd ) } 1 .. $n
  3         25  
115             : genf( $dfn, $dfd );
116             }
117              
118             sub random_gamma { # Arguments: ($n,$a,$r)
119 4 50   4 1 2978 croak 'Usage: random_gamma($n,$a,$r)' if @_ < 3;
120 4         13 my ( $n, $a, $r ) = @_;
121 4 50 33     23 croak "random_gamma: ($a = \$a <= 0) or ($r = \$r <= 0)"
122             if ( $a <= 0 ) || ( $r <= 0 );
123              
124             return wantarray
125 4 100       32 ? map { gengam( $a, $r ) } 1 .. $n
  3         17  
126             : gengam( $a, $r );
127             }
128              
129             sub random_multivariate_normal { # Arguments: ($n, @mean, @covar(2-dimensional))
130              
131 3 50   3 1 1581 croak 'Usage: random_multivariate_normal($n,@mean,@covar(2-dimensional))'
132             if @_ < 3;
133 3         8 my $n = shift( @_ ); # first element is number of obs. desired
134 3         9 my $p = @_ / 2; # best guess at dimension of deviate
135              
136             # check outline of arguments
137 3 50 33     34 croak( q{random_multivariate_normal: Sizes of @mean and @covar don't match} )
      33        
138             if $p != int( $p )
139             || ref $_[ $p - 1 ] eq 'ARRAY'
140             || ref $_[$p] ne 'ARRAY';
141              
142             # linearize input - it seems faster to push
143 3         12 my @linear = splice( @_, 0, $p ); # fill first $p slots w/ mean
144              
145             # expand array references
146 3         8 foreach my $ref ( @_ ) { # for the rest of the input
147              
148             # check length of row of @covariance
149             croak( 'random_multivariate_normal: @covar is not a $p x $p array ($p is size of @mean)' )
150 6 50       11 unless @{$ref} == $p;
  6         16  
151              
152 6         10 push @linear, @{$ref};
  6         17  
153             }
154              
155             # load float working array with linearized input
156 3 50       13 putflt( @linear )
157             or croak 'random_multivariate_normal: unable to allocate memory';
158              
159             # initialize parameter array for multivariate normal generator
160 3 50       17 psetmn( $p )
161             or croak 'random_multivariate_normal: unable to allocate memory';
162              
163 3 100       10 unless ( wantarray() ) {
164             ### if called in a scalar context, returns single reference to obs
165 2         12 pgenmn();
166 2         7 return [ getflt( $p ) ];
167             }
168              
169             # otherwise return an $n by $p array of obs.
170             return map {
171 1         4 pgenmn();
  2         9  
172 2         6 [ getflt( $p ) ]
173             } 1 .. $n;
174             }
175              
176             sub random_multinomial { # Arguments: ($n,@p)
177 2     2 1 1345 my ( $n, @p ) = @_;
178 2         6 my $ncat = @p; # number of categories
179 2         5 $n = int( $n );
180              
181 2 50       10 croak "random_multinomial: $n = \$n < 0"
182             if $n < 0;
183              
184 2 50       7 croak "random_multinomial: $ncat = (length of \@p) < 2"
185             if $ncat < 2;
186              
187 2 50       16 rspriw( $ncat )
188             or croak 'random_multinomial: Unable to allocate memory';
189              
190 2         4 pop @p;
191 2 50       10 rsprfw( scalar( @p ) )
192             or croak 'random_multinomial: Unable to allocate memory';
193              
194 2         6 my ( $i, $sum ) = ( 0, 0 );
195 2         6 foreach my $val ( @p ) {
196 4 50 33     25 croak "random_multinomial: $val = (some \$p[i]) < 0 or > 1"
197             if $val < 0 || $val > 1;
198 4         16 svprfw( $i++, $val );
199 4         10 $sum += $val;
200             }
201 2 50       7 croak 'random_multinomial: sum of \@p > 1'
202             if $sum > 0.99999;
203              
204 2         14 pgnmul( $n, $ncat );
205             ### get the results
206 2         4 $i = 0;
207 2         11 $_ = gvpriw( $i++ ) for @p;
208 2         7 push @p, gvpriw( $i );
209 2         15 return @p;
210             }
211              
212             sub random_noncentral_chi_square { # Arguments: ($n,$df,$nonc)
213 4 50   4 1 3012 croak 'Usage: random_noncentral_chi_square($n,$df,$nonc)'
214             if @_ < 3;
215 4         13 my ( $n, $df, $nonc ) = @_;
216 4 50 33     46 croak( "random_noncentral_chi_square: ($df = \$df < 1) or ($nonc = \$nonc) < 0" )
217             if $df < 1 || $nonc < 0;
218              
219             return wantarray
220 4 100       44 ? map { gennch( $df, $nonc ) } 1 .. $n
  3         26  
221             : gennch( $df, $nonc );
222             }
223              
224             sub random_noncentral_f { # Arguments: ($n,$dfn,$dfd,$nonc)
225 4 50   4 1 3036 croak 'Usage: random_noncentral_f($n,$dfn,$dfd,$nonc)'
226             if @_ < 4;
227 4         14 my ( $n, $dfn, $dfd, $nonc ) = @_;
228              
229 4 50 33     35 croak( "random_noncentral_f: ($dfn = \$dfn < 1) or ($dfd = \$dfd <= 0) or ($nonc = \$nonc < 0)" )
      33        
230             if $dfn < 1 || $dfd <= 0 || $nonc < 0;
231              
232             return wantarray
233 4 100       38 ? map { gennf( $dfn, $dfd, $nonc ) } 1 .. $n
  3         20  
234             : gennf( $dfn, $dfd, $nonc );
235             }
236              
237             sub random_normal { # Arguments: ($n,$av,$sd), defaults (1,0,1)
238 4 0   4 1 1684 return wantarray() ? ( gennor( 0, 1 ) ) : gennor( 0, 1 )
    50          
239             if @_ == 0; # default behavior if no arguments
240 4         11 my ( $n, $av, $sd ) = @_;
241              
242 4   50     13 $av //= 0; # $av defaults to 0
243 4   50     12 $sd //= 1; # $sd defaults to 1, even if $av specified
244 4 50       11 croak "random_normal: $sd = \$sd < 0)"
245             if $sd < 0;
246              
247             return wantarray
248 4 100       31 ? map { gennor( $av, $sd ) } 1 .. $n
  3         34  
249             : gennor( $av, $sd );
250             }
251              
252             sub random_permutation { # Argument: (@array) - array to be permuted.
253 2     2 1 1954 my $n = @_; # number of elements to be permuted
254 2 50       10 return () if $n == 0;
255 2 50       20 rspriw( $n )
256             or croak 'random_permutation: unable to allocate memory';
257              
258 2         12 pgnprm( $n );
259 2         8 my @ans = map { gvpriw( $_ ) } 0 .. $n - 1;
  16         40  
260 2         25 return @_[@ans];
261             }
262              
263             sub random_permuted_index { # Argument: $n = scalar(@array) (for permutation)
264              
265 2 50   2 1 12 croak 'Usage: random_permuted_index($n)'
266             if @_ < 1;
267              
268 2         6 my $n = int( shift( @_ ) ); # number of elements to be permuted
269 2 50       8 croak "random_permuted_index: $n = \$n < 0"
270             if $n < 0;
271 2 50       20 return () if $n == 0;
272 2 50       12 rspriw( $n )
273             or croak 'random_permuted_index: unable to allocate memory';
274              
275 2         11 pgnprm( $n );
276 2         8 return map { gvpriw( $_ ) } 0 .. $n - 1;
  18         53  
277             }
278              
279             sub random_uniform { # Arguments: ($n,$low,$high), defaults (1,0,1)
280 10 50   10 1 5507 return wantarray() ? ( genunf( 0, 1 ) ) : genunf( 0, 1 )
    100          
281             if @_ == 0;
282              
283 8 50       23 croak 'Usage: random_uniform([$n,[$low,$high]])'
284             if @_ == 2; # only default is (0,1) for ($low,$high) both undef
285              
286 8         23 my ( $n, $low, $high ) = @_;
287 8   50     25 $low //= 0; # default for $low is 0
288 8   50     18 $high //= 1; # default for $high is 1
289              
290 8 50       28 croak( "random_uniform: $low = \$low > \$high = $high" )
291             if $low > $high;
292              
293             return wantarray
294 8 100       57 ? map { genunf( $low, $high ) } 1 .. $n
  6         33  
295             : genunf( $low, $high );
296             }
297              
298             sub random_poisson { # Arguments: ($n, $mu)
299 4 50   4 1 1501 croak 'Usage: random_poisson($n,$mu)' if @_ < 2;
300              
301 4         11 my ( $n, $mu ) = @_;
302 4 50       13 croak "random_poisson: $mu = \$mu < 0"
303             if $mu < 0;
304              
305             return wantarray
306 4 100       31 ? map { ignpoi( $mu ) } 1 .. $n
  3         20  
307             : ignpoi( $mu );
308             }
309              
310             sub random_uniform_integer { # Arguments: ($n,$low,$high)
311              
312 4 50   4 1 5252 croak 'Usage: random_uniform_integer($n,$low,$high)' if @_ < 3;
313              
314 4         13 my ( $n, $low, $high ) = @_;
315 4         8 $low = int( $low );
316 4         6 $high = int( $high );
317 4 50       11 croak( "random_uniform_integer: $low = \$low > \$high = $high" )
318             if $low > $high;
319 4         29 my $range = $high - $low;
320              
321 4 50       11 croak( "random_uniform_integer: $range = (\$high - \$low) > 2147483561" )
322             if $range > 2_147_483_561;
323              
324             return wantarray
325 4 100       31 ? map { $low + ignuin( 0, $range ) } 1 .. $n
  3         19  
326             : $low + ignuin( 0, $range );
327             }
328              
329             sub random_negative_binomial { # Arguments: ($n,$ne,$p)
330              
331 4 50   4 1 1472 croak 'Usage: random_negative_binomial($n,$ne,$p)'
332             if @_ < 3;
333              
334 4         10 my ( $n, $ne, $p ) = @_;
335 4         9 $ne = int( $ne );
336 4 50 33     33 croak( "random_negative_binomial: ($ne = \$ne <= 0) or ($p = \$p <= 0 or >= 1)" )
      33        
337             if $ne <= 0 || $p <= 0 || $p >= 1;
338              
339             return wantarray
340 4 100       66 ? map { ignnbn( $ne, $p ) } 1 .. $n
  3         22  
341             : ignnbn( $ne, $p );
342             }
343              
344             sub random_binomial { # Arguments: ($n,$nt,$p)
345 4 50   4 1 2841 croak 'Usage: random_binomial($n,$nt,$p)'
346             if @_ < 3;
347              
348 4         12 my ( $n, $nt, $p ) = @_;
349 4         8 $nt = int( $nt );
350 4 50 33     55 croak( "random_binomial: ($nt = \$nt < 0) or ($p = \$p < 0 or > 1)" )
      33        
351             if $nt < 0 || $p < 0 || $p > 1;
352              
353             return wantarray
354 4 100       55 ? map { ignbin( $nt, $p ) } 1 .. $n
  3         19  
355             : ignbin( $nt, $p );
356             }
357              
358              
359             #####################################################################
360             # SEED HANDLER FUNCTIONS #
361             #####################################################################
362              
363             sub random_seed_from_phrase { # Argument $phrase
364 0     0 1 0 my $phrase = shift( @_ );
365 0   0     0 $phrase ||= q{};
366 0         0 return phrtsd( $phrase );
367             }
368              
369             sub random_set_seed_from_phrase { # Argument $phrase
370 13     13 1 585002 my $phrase = shift( @_ );
371 13   50     48 $phrase ||= q{};
372 13         276 salfph( $phrase );
373 13         34 return 1;
374             }
375              
376             sub random_set_seed { # Argument @seed
377 0     0 1 0 my ( $seed1, $seed2 ) = @_;
378 0 0 0     0 croak( 'Usage: random_set_seed(\@seed)\n\@seed[0,1] must be two integers '
      0        
      0        
      0        
      0        
379             . "in the range (1,1) to (2147483562,2147483398)\nand usually comes "
380             . 'from a call to random_get_seed() '
381             . "or\nrandom_seed_from_phrase(\$phrase)." )
382             unless ( $seed1 == int( $seed1 ) && $seed1 > 0 && $seed1 < 2_147_483_563 )
383             && ( $seed2 == int( $seed2 ) && $seed2 > 0 && $seed2 < 2_147_483_399 );
384              
385 0         0 setall( $seed1, $seed2 );
386 0         0 return 1;
387             }
388              
389             #####################################################################
390             # HELPER ROUTINES #
391             # These use the C work arrays and are not intended for export #
392             # (Currently only used in random_multivariate_normal) #
393             #####################################################################
394              
395             sub getflt {
396 4     4 0 8 my $n = shift;
397 4         13 return map { gvprfw( $_ ) } 0 .. $n - 1;
  8         38  
398             }
399              
400             sub putflt {
401 3     3 0 7 my $n = @_;
402 3 50       31 rsprfw( $n )
403             or return 0;
404 3         6 my $i = 0;
405 3         23 svprfw( $i++, $_ ) for @_;
406 3         9 return 1;
407             }
408              
409             1;
410              
411             __END__