File Coverage

blib/lib/Math/BigInt/Random.pm
Criterion Covered Total %
statement 72 85 84.7
branch 22 34 64.7
condition 26 31 83.8
subroutine 8 9 88.8
pod 5 5 100.0
total 133 164 81.1


line stmt bran cond sub pod time code
1             package Math::BigInt::Random;
2              
3             our $VERSION = 0.04;
4              
5 1     1   108797 use strict;
  1         3  
  1         38  
6 1     1   7 use warnings;
  1         1  
  1         76  
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT = qw(random_bigint);
10 1     1   6 use Carp qw(carp croak);
  1         6  
  1         82  
11 1     1   4 use Math::BigInt;
  1         2  
  1         5  
12              
13             sub random_bigint {
14 6     6 1 3405 my (%args) = @_;
15 6   100     28 my $max = $args{max} || 0;
16 6         69 $max = Math::BigInt->new($max);
17 6         378 my ($length_hex) = $args{length_hex};
18 6         12 my ($length_bin) = $args{length_bin};
19 6         10 my ($use_internet) = $args{use_internet};
20 6 50 66     20 carp("hmm...two incompatible specs for length, will use hex")
21             if $length_hex and $length_bin;
22 6         7 my $required_length = 0;
23 6 50       22 croak "max must be > 0, but $max was specified" if $max < 0;
24 6 100       833 if ( $max == 0 ) {
25 3 50       320 $required_length = $args{length}
26             or croak "Need a maximum or a length for the random number";
27 3         7 my $digit = '9';
28 3         4 my $prefix = '';
29 3 100       52 if ($length_hex) {
    100          
30 1         2 $digit = 'f';
31 1         3 $prefix = '0x';
32             }
33             elsif ($length_bin) {
34 1         2 $digit = '1';
35 1         1 $prefix = '0b';
36             }
37 3         11 my $max_num_string = $prefix . ( $digit x $required_length );
38 3         9 $max = Math::BigInt->new($max_num_string);
39             }
40 6   100     1062 my $min = $args{min} || 0;
41 6         52 $min = Math::BigInt->new($min);
42 6         292 my $interval = $max - $min;
43 6 50       528 croak "too narrow a range" if $interval <= 0;
44 6         887 my $tries = 1000;
45 6 100       16 $tries *= 16 if $length_bin;
46 6         25 for ( my $i = 0 ; $i < $tries ; ++$i ) {
47 6 100 66     15 my $rand_num =
48             ( $interval < 0xfffff and !$use_internet )
49             ? Math::BigInt->new( int rand($interval) )
50             : bigint_rand($interval, $use_internet);
51 6         1205 $rand_num += $min;
52 6 50       306 next if $rand_num > $max;
53 6         174 my $num_length_10 = length $rand_num;
54 6         158 my $num_length_16 = int length( $rand_num->as_hex() ) - 2;
55 6         10098 my $num_length_2 = int length( $rand_num->as_bin() ) - 2;
56             next
57 6 50 100     805 if $required_length
      66        
58             and $length_hex
59             and $num_length_16 != $required_length;
60             next
61 6 50 100     25 if $required_length
      66        
62             and $length_bin
63             and $num_length_2 != $required_length;
64             next
65 6 50 100     46 if $required_length
      100        
      66        
66             and !$length_hex
67             and !$length_bin
68             and $num_length_10 != $required_length;
69 6         49 return $rand_num;
70             }
71 0         0 carp "Could not make random number $required_length size in $tries tries";
72 0         0 return;
73             }
74              
75             sub bigint_rand {
76 4     4 1 373 my( $max, $use_internet ) = @_;
77 4         13 my $as_hex = $max->as_hex();
78 4         765 my $len = length($as_hex); # include '0x' prefix
79 4         10 my $bottom_quads = int( ( $len - 3 ) / 4 );
80 4         12 my $top_quad_chunk = substr($as_hex, 0, $len - 4 * $bottom_quads);
81 4         4 my $num = '0x';
82 4 50       9 if($use_internet) {
83 0         0 $num .= get_random_org_digits(hex $top_quad_chunk, 1);
84 0         0 $num .= get_random_org_digits(65535, $bottom_quads);
85             }
86             else {
87 4         14 $num .= get_random_hex_digits(hex $top_quad_chunk, 1);
88 4         10 $num .= get_random_hex_digits(65535, $bottom_quads);
89             }
90 4         24 return Math::BigInt->new($num);
91             }
92              
93             sub get_random_hex_digits {
94 8     8 1 12 my($max, $count) = @_;
95 8 50       14 return '' if $count < 1;
96 8         8 my @digits;
97 8         14 for(1 .. $count) { push @digits, int rand($max) }
  24         91  
98 8         18 return assemble_digits(\@digits);
99             }
100              
101             sub get_random_org_digits {
102 0     0 1 0 my($max, $count) = @_;
103 0 0       0 return if $count < 1;
104 0         0 require LWP::Simple;
105 0         0 my $request = "http://www.random.org/cgi-bin/randnum?num=$count&min=0&max=$max&col=1";
106 0         0 my $page = LWP::Simple::get($request);
107 0         0 my @digits = split /\s+/, $page;
108 0         0 foreach (@digits) { $_ ^= int rand($max) }
  0         0  
109 0         0 return assemble_digits(\@digits);
110             }
111              
112             sub assemble_digits {
113 8     8 1 11 my $array = shift;
114 8         9 my $retval;
115 8 100       13 if(scalar(@$array) > 1) {
116 3         6 foreach (@$array) { $retval .= sprintf( "%04x", $_ ) }
  19         31  
117             }
118             else {
119 5         21 $retval = sprintf( "%x", $array->[0] )
120             }
121 8         22 return $retval;
122             }
123              
124              
125             =head1 NAME
126              
127             Math::BigInt::Random -- arbitrary sized random integers
128              
129             =head1 DESCRIPTION
130              
131             Random number generator for arbitrarily large integers.
132             Uses the Math::BigInt module to handle the generated values.
133              
134             This module exports a single function called random_bigint, which returns
135             a single random Math::BigInt number of the specified range or size.
136              
137              
138             =head1 SYNOPSIS
139              
140             use Math::BigInt;
141             use Math::BigInt::Random qw/ random_bigint /;
142            
143             print "random by max : ", random_bigint( max => '10000000000000000000000000'), "\n",
144             "random by max and min : ",
145             random_bigint( min => '7000000000000000000000000', max => '10000000000000000000000000'), "\n",
146             "random by length (base 10): ",
147             random_bigint( length => 20 ), "\n",
148             "random by length (base 16) :",
149             random_bigint( length_hex => 1, length => 20)->as_hex, "\n";
150             "random by length (base 2) :",
151             random_bigint( length_bin => 1, length => 319)->as_bin, "\n";
152             "random from random.org" :",
153             random_bigint( max => '3333333333333333000000000000000000000000',
154             min => '3333333333333300000000000000000000000000', use_internet => 1);
155            
156            
157              
158             =head1 FUNCTION ARGUMENTS
159              
160             =over 4
161              
162             This module exports a single function called random_bigint, which returns
163             a single random Math::BigInt of arbitrary size.
164              
165              
166             Parameters to the function are given in paired hash style:
167              
168             max => $max,
169             the maximum integer that can be returned. Either the 'max' or the 'length'
170             parameter is mandatory. If both max and length are given, only the 'max'
171             parameter will be used. Note that the max must be >= 1.
172            
173             min => $min,
174             which specifies the minimum integer that can be returned.
175              
176             length => $required_length,
177             which specifies the number of digits (with most significant digit not 0).
178             Note that if max is specified, length will be ignored. However, if max is
179             not specified, length is a required argument.
180            
181             length_hex => 1,
182             which specifies that, if length is used, the length is that of the base 16
183             number, not the base 10 number which is the default for the length.
184              
185             length_bin => 1,
186             which specifies that, if length is used, the length is that of the base 2
187             number, not the base 10 number which is the default for the length. Note
188             that, due to discarding half the possible random numbers due to random
189             generation of a most significant place digit of 0, this method is about
190             half as efficient as when an exact maximum and minimum are given instead.
191            
192             use_internet => 1,
193             which specifies that the random.org website will be used for random
194             number generation. Note this is NOT secure, since anyone monitoring
195             the connnection might be able to read the numbers that are received.
196             It is quite random, however.
197              
198             =back
199              
200             =head2 Class Internal Methods and Functions
201              
202             =over 4
203              
204             =item assemble_digits
205              
206             =item bigint_rand
207              
208             =item get_random_hex_digits
209              
210             =item get_random_org_digits
211              
212             =item random_bigint
213            
214             =back
215              
216             =head1 AUTHOR
217              
218             William Herrera (wherrera@skylightview.com)
219              
220             =head1 COPYRIGHT
221              
222             Copyright (C) 2007 William Hererra. All Rights Reserved.
223              
224             This module is free software; you can redistribute it and/or modify it
225             under the same terms as Perl itself.
226              
227            
228             =cut
229              
230             1;