File Coverage

blib/lib/Math/BigInt/Random/OO.pm
Criterion Covered Total %
statement 118 122 96.7
branch 50 76 65.7
condition 7 9 77.7
subroutine 9 9 100.0
pod 2 2 100.0
total 186 218 85.3


line stmt bran cond sub pod time code
1             # -*- mode: perl; coding: utf-8-unix; -*-
2              
3             package Math::BigInt::Random::OO;
4              
5             ###############################################################################
6             ## Modules and general package variables.
7             ###############################################################################
8              
9 4     4   404194 use 5.008; # required version of Perl
  4         12  
10 4     4   22 use strict; # restrict unsafe constructs
  4         8  
  4         130  
11 4     4   16 use warnings; # control optional warnings
  4         20  
  4         294  
12 4     4   1908 use utf8; # enable UTF-8 in source code
  4         1196  
  4         20  
13              
14             # Modules from the Standard Perl Library.
15              
16 4     4   128 use Config;
  4         6  
  4         144  
17 4     4   15 use Carp;
  4         7  
  4         243  
18 4     4   5998 use Math::BigInt;
  4         188617  
  4         28  
19             #use Data::Dumper;
20              
21             ###############################################################################
22             # Package variables.
23             ###############################################################################
24              
25             our $VERSION = '0.05';
26              
27             =pod
28              
29             =encoding utf8
30              
31             =head1 NAME
32              
33             Math::BigInt::Random::OO - generate uniformly distributed Math::BigInt objects
34              
35             =head1 SYNOPSIS
36              
37             =for test_synopsis
38             no strict 'vars'
39              
40             use Math::BigInt::Random::OO;
41              
42             # Random numbers between 1e20 and 2e30:
43              
44             $gen = Math::BigInt::Random::OO -> new(min => "1e20",
45             min => "2e30");
46             $x = $gen -> generate(); # one number
47             $x = $gen -> generate(1); # ditto
48             @x = $gen -> generate(100); # 100 numbers
49              
50             # Random numbers with size fitting 20 hexadecimal digits:
51              
52             $gen = Math::BigInt::Random::OO -> new(length => 20,
53             base => 16);
54             @x = $gen -> generate(100);
55              
56             =head1 ABSTRACT
57              
58             Math::BigInt::Random::OO is a module for generating arbitrarily large random
59             integers from a discrete, uniform distribution. The numbers are returned as
60             Math::BigInt objects.
61              
62             =head1 DESCRIPTION
63              
64             Math::BigInt::Random::OO is a module for generating arbitrarily large random
65             integers from a discrete, uniform distribution. The numbers are returned as
66             Math::BigInt objects.
67              
68             =head1 CONSTRUCTORS
69              
70             =over 4
71              
72             =item CLASS -E new ( ... )
73              
74             Returns a new C random number generator object. The
75             arguments are given in the "hash style", as shown in the following example
76             which constructs a generator for random numbers in the range from -2 to 3,
77             inclusive.
78              
79             my $gen = Math::BigInt::Random::OO -> new(min => -2,
80             max => 3);
81              
82             The following parameters are recognized.
83              
84             =over 4
85              
86             =item min =E NUM
87              
88             Specifies the minimum possible output value, i.e., the lower bound. If `max' is
89             given, but `min' is not, then `min' is set to zero.
90              
91             =item max =E NUM
92              
93             Specifies the maximum possible output value, i.e., the upper bound. If `max' is
94             given, but `min' is not, then `max' must be non-negative.
95              
96             =item length =E NUM
97              
98             Specifies the length of the output value, i.e., the number of digits. This
99             parameter, possibly used together with `base', is more convenient than `min'
100             and `max' when you want all random numbers have the same number of digits. If
101             the base is not given explicitly with the `base' option, then a base of 10 is
102             used. The following two constructors are equivalent
103              
104             $gen1 = Math::BigInt::Random::OO -> new(length => $n, base => $b);
105              
106             $min = Math::BigInt -> new($b) -> bpow($n - 1);
107             $max = Math::BigInt -> new($b) -> bpow($n) -> bsub(1));
108             $gen2 = Math::BigInt::Random::OO -> new(min => $min, max => $max);
109              
110             For instance, if the length is 4 and the base is 10, the random numbers will be
111             in the range from 1000 to 9999, inclusive. If the length is 3 and the base is
112             16, the random numbers will be in the range from 256 to 4095, which is 100 to
113             fff hexadecimal.
114              
115             This option is ignored if the `max' option is present.
116              
117             =item base =E NUM
118              
119             Sets the base to be used with the `length' option. See also the description for
120             the `length' option.
121              
122             =item length_bin =E NUM
123              
124             This option is only for compatibility with Math::BigInt::Random. The following
125             two cases are equivalent
126              
127             $class -> new(length_bin => $n);
128             $class -> new(length => $n, base => 2);
129              
130             =item length_hex =E NUM
131              
132             This option is only for compatibility with Math::BigInt::Random. The following
133             two cases are equivalent
134              
135             $class -> new(length_hex => $n);
136             $class -> new(length => $n, base => 16);
137              
138             =back
139              
140             =cut
141              
142             sub new {
143 338     338 1 10298103 my $proto = shift;
144 338         883 my $protoref = ref $proto;
145 338   33     2067 my $class = $protoref || $proto;
146 338         835 my $name = 'new';
147              
148             # Check how the method is called.
149              
150 338 50       1199 croak "$name() is a class method, not an instance/object method"
151             if $protoref;
152              
153             # Check the number of input arguments.
154              
155 338 50       1190 croak "$name(): not enough input arguments" if @_ < 1;
156 338 50       1362 croak "$name(): odd number of input arguments" if @_ % 2;
157              
158             # Check the context.
159              
160 338 50       1267 carp "$name(): useless use of method in void context"
161             unless defined wantarray;
162              
163             # Initialize the new object.
164              
165 338         2887 my $self = {
166             min => undef,
167             max => undef,
168             length => undef,
169             base => 10,
170             };
171              
172             # Get the input arguments.
173              
174 338         1140 while (@_) {
175 674         1350 my $key = shift;
176 674         1149 my $val = shift;
177              
178 674 50       1627 croak "$name(): parameter can't be undefined"
179             unless defined $key;
180              
181             # The minimum value, i.e., lower bound.
182              
183 674 100       1940 if ($key eq 'min') {
184 265 50       656 croak "$name(): minimum value can't be undefined"
185             unless defined $val;
186 265 100 100     1854 $val = Math::BigInt -> new($val)
187             unless ref($val) && $val -> isa('Math::BigInt');
188 265 50       24532 croak "$name(): minimum is not a valid number"
189             if $val -> is_nan();
190 265         2703 $self -> {min} = $val -> as_int();
191 265         45082 next;
192             }
193              
194             # The maximum value, i.e., upper bound.
195              
196 409 100       1231 if ($key eq 'max') {
197 265 50       729 croak "$name(): maximum value can't be undefined"
198             unless defined $val;
199 265 100 100     1632 $val = Math::BigInt -> new($val)
200             unless ref($val) && $val -> isa('Math::BigInt');
201 265 50       21479 croak "$name(): maximum is not a valid number"
202             if $val -> is_nan();
203 265         2121 $self -> {max} = $val -> as_int();
204 265         37982 next;
205             }
206              
207             # The length for the given base.
208              
209 144 100       405 if ($key eq 'length') {
210 71 50       181 croak "$name(): length value can't be undefined"
211             unless defined $val;
212 71 50       250 croak "$name(): length value must be positive"
213             unless $val > 0;
214 71         230 $self -> {length} = $val;
215 71         200 $self -> {base} = 10;
216 71         208 next;
217             }
218              
219             # The base used when computing the length.
220              
221 73 100       207 if ($key eq 'base') {
222 71 50       197 croak "$name(): base value can't be undefined"
223             unless defined $val;
224 71 50       286 croak "$name(): base value must be positive"
225             unless $val > 0;
226 71         168 $self -> {base} = $val;
227 71         213 next;
228             }
229              
230             # The length with an implicit base 16.
231              
232 2 100       6 if ($key eq 'length_hex') {
233 1 50       3 croak "$name(): length_hex value can't be undefined"
234             unless defined $val;
235 1 50       4 croak "$name(): length_hex value must be positive"
236             unless $val > 0;
237 1         2 $self -> {length} = $val;
238 1         3 $self -> {base} = 16;
239 1         3 next;
240             }
241              
242             # The length with an implicit base 2.
243              
244 1 50       5 if ($key eq 'length_bin') {
245 1 50       2 croak "$name(): length_bin value can't be undefined"
246             unless defined $val;
247 1 50       3 croak "$name(): length_bin value must be positive"
248             unless $val > 0;
249 1         22 $self -> {length} = $val;
250 1         2 $self -> {base} = 2;
251 1         3 next;
252             }
253              
254 0         0 croak "$name(): unknown parameter -- $key\n";
255             }
256              
257             # If the maximum value is given, use that. If the length is given, compute
258             # the minimum and maximum values for the given length and base. For
259             # instance, if the base is 10 and the length is 3, the minimum value is
260             # 100, and the maximum is 999. If the base is 2 and the length is 5, the
261             # minimum value is 10000 binary (= 16 decimal) and the maximum is 11111
262             # binary (= 31 decimal).
263              
264 338 100       1098 if (defined $self->{max}) {
265              
266 265 50       830 if (defined $self->{length}) {
267 0         0 carp "$name(): 'max' is given, so 'length' is ignored";
268             }
269 265 50       772 if (! defined $self->{min}) {
270 0         0 $self->{min} = 0,
271             }
272              
273             croak "$name(): maximum can't be smaller than minimum"
274 265 50       1186 if $self->{max} < $self->{min};
275              
276             } else {
277              
278 73 50       263 if (defined $self->{length}) {
279 73         175 my $base = $self -> {base};
280 73         151 my $len = $self -> {length};
281 73         414 $self->{min} = Math::BigInt -> new($base) -> bpow($len - 1);
282 73         61293 $self->{max} = $self->{min} -> copy() -> bmul($base) -> bsub(1);
283             } else {
284 0         0 croak "$name(): either 'max' or 'length' must be given\n";
285             }
286              
287             }
288              
289 338         58319 $self -> {_range} = $self->{max} - $self->{min} + 1;
290              
291             # The task is to generate a uniformly distributed random integer X,
292             # satisfying 0 <= X < N. We do this by generating uniformly distributed
293             # random numbers X, where 0 <= X <= 2^E for some integer E, until X < N.
294              
295             # Now find the power of 2 that is no larger than $max, i.e.,
296             #
297             # $exp2 = ceil(log($max) / log(2));
298             # $pow2 = 2 ** $exp2;
299              
300             # We subtract one from the length to avoid overshooting too much. If we
301             # undershoot, it will be corrected below.
302              
303 338         164203 my ($mlen, $elen) = $self -> {_range} -> length();
304 338         7045 my $len = $mlen + $elen - 1;
305              
306 338         1020 my $exp2 = int($len * log(10) / log(2));
307 338         1112 my $pow2 = Math::BigInt -> new("1") -> blsft($exp2);
308              
309 338         216382 if (0) {
310             printf "\n";
311             printf " range = %s\n", $self->{_range};
312             printf "\n";
313             printf " len = %s\n", $len;
314             printf " exp2 = %s\n", $exp2;
315             printf " pow2 = %s\n", $pow2;
316             }
317              
318             # Final adjustment of the estimate above. If we overshoot, like if $max is
319             # 15 and we use $exp2 = 5 and $pow2 = 32 rather than $exp2 = 4 and $pow2 =
320             # 16, it only makes the algorithm slower. If we undershoot, however, the
321             # algorithm fails, so this must be avoided.
322              
323 338         1140 my $two = Math::BigInt -> new("2");
324 338         29176 while ($pow2 < $self -> {_range}) {
325 573         27148 $pow2 -> bmul($two);
326 573         56747 $exp2++;
327             }
328              
329 338         13799 if (0) {
330             printf "\n";
331             printf " exp2 : %s\n", $exp2;
332             printf " pow2 : %s\n", $pow2;
333             }
334              
335 338         1063 my $whole_bytes = int($exp2 / 8);
336 338         792 my $extra_bits = $exp2 % 8;
337              
338             # Save these, since they are needed to generate the random numbers.
339              
340 338         963 $self->{_whole_bytes} = $whole_bytes;
341 338         1338 $self->{_extra_bits} = $extra_bits;
342              
343             # Bless the reference into an object and return it.
344              
345 338         3290 bless $self, $class;
346             }
347              
348             =pod
349              
350             =item OBJECT -E generate ( COUNT )
351              
352             =item OBJECT -E generate ( )
353              
354             Generates the given number of random numbers, or one number, if no input
355             argument is given.
356              
357             # Generate ten random numbers:
358              
359             my @num = $gen -> generate(10);
360              
361             =cut
362              
363             sub generate {
364 334     334 1 732 my $self = shift;
365 334         699 my $selfref = ref $self;
366 334         656 my $name = 'generate';
367              
368             # Check how the method is called.
369              
370 334 50       990 croak "$name() is an object instance method, not a class method"
371             unless $selfref;
372              
373             # Check number of input arguments.
374              
375             #croak "$name(): not enough input arguments" if @_ < 1;
376 334 50       946 croak "$name(): too many input arguments" if @_ > 1;
377              
378             # Get the count.
379              
380 334         579 my $count = 1;
381 334 100       827 if (@_) {
382 268         474 $count = shift;
383 268 50       799 croak "$name(): input argument must be defined"
384             unless defined $count;
385 268 50       797 croak "$name(): input argument must be an integer"
386             unless $count = int $count;
387             }
388              
389             # Generate the random numbers.
390              
391 334         627 my @num;
392              
393 334 100       1212 if ($self->{_range} -> is_one()) {
394              
395 56         1134 for (1 .. $count) {
396 98         1251 push @num, $self->{min} -> copy();
397             }
398              
399             } else {
400              
401 278         5589 for (1 .. $count) {
402 2149         3537 my $num;
403             do {
404 2677         363449 my $str = "";
405             $str .= sprintf "%02x", int(rand(1 << $self->{_extra_bits}))
406 2677 100       17760 if $self->{_extra_bits};
407             $str .= sprintf "%02x", int(rand(256))
408 2677         25870 for 1 .. $self->{_whole_bytes};
409 2677         8329 $num = Math::BigInt -> from_hex($str);
410 2149         3402 } until $num < $self->{_range};
411 2149         1362314 $num += $self->{min};
412 2149         262220 push @num, $num;
413             }
414             }
415              
416 334 100       8065 return @num if wantarray;
417 132         3590 return $num[0];
418             }
419              
420             =pod
421              
422             =back
423              
424             =head1 TODO
425              
426             =over 4
427              
428             =item *
429              
430             Add a way to change the core uniform random number generator. Currently,
431             CORE::rand() is used, but it would be nice to be able to switch to, e.g.,
432             Math::Random::random_uniform_integer().
433              
434             =item *
435              
436             Add functionality similar to the C parameter argument in
437             Math::BigInt::Random::random_bigint(). This could be implemented using, e.g.,
438             Net::Random.
439              
440             =item *
441              
442             Add more tests.
443              
444             =back
445              
446             =head1 NOTES
447              
448             The task is to generate a random integer X satisfying X_min E= X E=
449             X_max. This is equivalent to generating a random integer U satisfying 0 E=
450             U E U_max, where U_max = X_max - X_min + 1, and then returning X, where X =
451             U + X_min.
452              
453             =over
454              
455             =item *
456              
457             Find the smallest integer N so that U_max E= 2**N.
458              
459             =item *
460              
461             Generate uniformly distributed random integers U in the range 0 E= U E
462             2**N until we have the first U E U_max. Then return X, where X = U + X_min.
463              
464             =back
465              
466             The random integer U, where 0 E= U E 2**N is generated as a sequence of
467             random bytes, except for the N % 8 most significant bits, if any. For example,
468             if N = 21 = 5 + 8 + 8, then the 5 most significand bits are generated first,
469             followed by two 8 bit bytes.
470              
471             | top bits | first whole byte | second whole byte |
472             0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2
473             int(rand(1 << 5)) int(rand(1 << 8)) int(rand(1 << 8))
474              
475             =head2 Problems with Math::BigInt::Random
476              
477             I wrote this module partly since Math::BigInt::Random v0.04 is buggy, and in
478             many cases slower, and partly because I prefer an object-oriented interface.
479             The bugs in Math::BigInt::Random v0.04 are
480              
481             =over 4
482              
483             =item *
484              
485             When the range (the maximum value minus the minimum value) is smaller than
486             1048575 (fffff hexadecimal), the maximum value will never be returned.
487              
488             =item *
489              
490             When the range is not a power of two, certain values are more likely to occur
491             than others.
492              
493             =back
494              
495             The core of this last problem is the use of int(rand(X)), which only returns
496             uniformly distributed numbers when X is a power of two no larger than
497             I.
498              
499             In addition, the function Math::BigInt::Random::random_bigint() generates only
500             one random integer at a time, and in doing so, there is some overhead. In
501             Math::BigInt::Random::OO, this overhead is placed in the new() constructor, so
502             it is done only once, independently of how many random numbers are generated by
503             the generator() method.
504              
505             =head1 CAVEATS
506              
507             =over 4
508              
509             =item *
510              
511             Some versions of CORE::rand() behave poorly, so the quality of the random
512             numbers generated depend on the quality of the random number returned
513             by int(rand(256)).
514              
515             =back
516              
517             =head1 BUGS
518              
519             Please report any bugs or feature requests to C
520             rt.cpan.org>, or through the web interface at
521             L I will
522             be notified, and then you'll automatically be notified of progress on your bug
523             as I make changes.
524              
525             =head1 SUPPORT
526              
527             You can find documentation for this module with the perldoc command.
528              
529             perldoc Math::BigInt::Random::OO
530              
531             You can also look for information at:
532              
533             =over 4
534              
535             =item * GitHub Source Repository
536              
537             L
538              
539             =item * RT: CPAN's request tracker
540              
541             L
542              
543             =item * MetaCPAN
544              
545             L
546              
547             =item * CPAN Testers Matrix
548              
549             L
550              
551             =back
552              
553             =head1 SEE ALSO
554              
555             Math::BigInt::Random(3), Math::Random(3), Net::Random(3).
556              
557             =head1 AUTHOR
558              
559             Peter John Acklam Epjacklam (at) gmail.comE.
560              
561             =head1 COPYRIGHT & LICENSE
562              
563             Copyright 2010,2020,2023 Peter John Acklam.
564              
565             This program is free software; you may redistribute it and/or modify it under
566             the same terms as Perl itself.
567              
568             =cut
569              
570             1; # modules must return true