File Coverage

blib/lib/Math/Rand48/Cursor.pm
Criterion Covered Total %
statement 75 75 100.0
branch 9 10 90.0
condition 6 6 100.0
subroutine 15 15 100.0
pod 8 8 100.0
total 113 114 99.1


line stmt bran cond sub pod time code
1             package Math::Rand48::Cursor;
2              
3 7     7   893261 use v5.34;
  7         20  
4 7     7   28 use warnings;
  7         11  
  7         357  
5 7     7   2816 use experimental qw(signatures);
  7         19488  
  7         35  
6 7     7   5621 use Carp qw(croak);
  7         15  
  7         325  
7 7     7   6229 use Math::BigInt;
  7         144200  
  7         32  
8 7     7   93669 use Math::BigFloat;
  7         214568  
  7         49  
9              
10             our $VERSION = '0.001';
11              
12             my $M = Math::BigInt->bone->blsft(48);
13             my $A = Math::BigInt->from_hex('5DEECE66D'); # drand48 multiplier
14             my $C = Math::BigInt->new(11); # drand48 increment
15             my $AI = $A->copy->bmodinv($M);
16             my $CI = ( -$AI * $C ) % $M;
17              
18             # Compose the step x -> a*x + c with itself n times, by squaring.
19 172     172   8682 sub _affine_pow ( $a, $c, $n ) {
  172         217  
  172         192  
  172         161  
  172         181  
20 172         454 my ( $ra, $rc ) = ( Math::BigInt->bone, Math::BigInt->bzero );
21 172         11635 my ( $ba, $bc ) = ( $a->copy, $c->copy );
22 172         5036 until ( $n->is_zero ) {
23 880 100       354498 ( $ra, $rc ) = ( ( $ba * $ra ) % $M, ( $ba * $rc + $bc ) % $M )
24             if $n->is_odd;
25 880         232599 ( $ba, $bc ) = ( ( $ba * $ba ) % $M, ( $ba * $bc + $bc ) % $M );
26 880         602645 $n->brsft(1);
27             }
28 172         82249 return ( $ra, $rc );
29             }
30              
31 124     124 1 320192 sub new ( $class, %arg ) {
  124         179  
  124         227  
  124         151  
32             my $state =
33             defined $arg{state}
34 124 50       576 ? Math::BigInt->new("$arg{state}")
35             : Math::BigInt->bzero;
36 124         10468 return bless { state => $state % $M }, $class;
37             }
38              
39 27     27 1 136989 sub from_rand ( $class, $obs ) {
  27         36  
  27         33  
  27         32  
40 27         121 return $class->new( state => sprintf '%.0f', $obs * 2**48 );
41             }
42              
43 45     45 1 451973 sub from_seed48 ( $class, $seed ) {
  45         66  
  45         55  
  45         44  
44 45         195 my $n = Math::BigFloat->new("$seed");
45 45 100 100     5015 croak "from_seed48() seed must be a finite number, got '$seed'"
46             if $n->is_nan || $n->is_inf;
47 41         606 my $int = $n->babs->bfloor->as_int;
48 41         17178 my $x = $int->blsft(16)->bior( Math::BigInt->from_hex('330e') );
49 41         40135 return $class->new( state => $x );
50             }
51              
52 152     152 1 6066 sub state ($self) { $self->{state}->copy }
  152         188  
  152         174  
  152         347  
53 12     12 1 1418 sub rand ($self) { $self->{state}->numify / 2**48 }
  12         17  
  12         12  
  12         52  
54              
55 177     177 1 6184 sub seek ( $self, $n ) {
  177         216  
  177         213  
  177         153  
56 177         531 my $steps = Math::BigInt->new("$n");
57 177 100 100     11067 croak "seek() count must be a finite integer, got '$n'"
58             if $steps->is_nan || $steps->is_inf;
59 172 100       1922 my ( $a, $c ) = $steps->is_negative ? ( $AI, $CI ) : ( $A, $C );
60 172         1104 my ( $pa, $pc ) = _affine_pow( $a, $c, $steps->babs );
61 172         597 $self->{state} = ( $pa * $self->{state} + $pc ) % $M;
62 172         64593 return $self;
63             }
64              
65 109     109 1 38579 sub forward ($self) { $self->seek(1) }
  109         185  
  109         103  
  109         224  
66 3     3 1 251 sub backward ($self) { $self->seek(-1) }
  3         5  
  3         5  
  3         8  
67              
68             1;
69              
70             __END__