File Coverage

blib/lib/MCE/Shared/Sequence.pm
Criterion Covered Total %
statement 23 105 21.9
branch 0 74 0.0
condition 0 19 0.0
subroutine 8 14 57.1
pod 3 3 100.0
total 34 215 15.8


line stmt bran cond sub pod time code
1             ###############################################################################
2             ## ----------------------------------------------------------------------------
3             ## Sequence helper class.
4             ##
5             ###############################################################################
6              
7             package MCE::Shared::Sequence;
8              
9 1     1   1153 use strict;
  1         2  
  1         27  
10 1     1   5 use warnings;
  1         2  
  1         23  
11              
12 1     1   20 use 5.010001;
  1         3  
13              
14 1     1   6 no warnings qw( threads recursion uninitialized numeric );
  1         2  
  1         57  
15              
16             our $VERSION = '1.881';
17              
18 1     1   6 use Scalar::Util qw( looks_like_number );
  1         2  
  1         42  
19 1     1   5 use MCE::Shared::Base ();
  1         2  
  1         37  
20              
21             use constant {
22 1         143 _BEGV => 0, # sequence begin value
23             _ENDV => 1, # sequence end value
24             _STEP => 2, # sequence step size
25             _FMT => 3, # sequence format
26             _CKSZ => 4, # chunk_size option, default 1
27             _ONLY => 5, # bounds_only option, default 0
28             _ITER => 6, # iterator count
29 1     1   6 };
  1         1  
30              
31             use overload (
32 1         5 q("") => \&MCE::Shared::Base::_stringify,
33             q(0+) => \&MCE::Shared::Base::_numify,
34             fallback => 1
35 1     1   7 );
  1         2  
36              
37             sub _croak {
38 0     0     goto &MCE::Shared::Base::_croak;
39             }
40              
41             sub _reset {
42 0     0     my $self = shift;
43 0 0         my $opts = ref($_[0]) eq 'HASH' ? shift() : {};
44              
45 0           @{ $self } = @_;
  0            
46              
47 0 0         _croak('invalid begin') unless looks_like_number( $self->[_BEGV] );
48 0 0         _croak('invalid end' ) unless looks_like_number( $self->[_ENDV] );
49              
50 0 0         $self->[_STEP] = ( $self->[_BEGV] <= $self->[_ENDV] ) ? 1 : -1
    0          
51             unless ( defined $self->[_STEP] );
52              
53 0 0         $self->[_FMT] =~ s/%// if ( defined $self->[_FMT] );
54              
55 0 0         _croak('invalid step' ) unless looks_like_number( $self->[_STEP] );
56              
57 0           for my $_k (_BEGV, _ENDV, _STEP) {
58 0 0         $self->[$_k] = int($self->[$_k]) unless ( $self->[$_k] =~ /\./ );
59             }
60              
61 0   0       $self->[_CKSZ] = $opts->{'chunk_size' } || 1;
62 0   0       $self->[_ONLY] = $opts->{'bounds_only'} // 0;
63              
64 0 0         _croak('invalid chunk_size' ) unless ( $self->[_CKSZ] =~ /^[0-9e\+]+$/ );
65 0 0         _croak('invalid bounds_only' ) unless ( $self->[_ONLY] =~ /^[01]$/ );
66              
67 0           $self->[_CKSZ] = int($self->[_CKSZ]);
68 0           $self->[_ITER] = undef;
69              
70 0           return;
71             }
72              
73             sub _sprintf {
74 0     0     my ( $fmt, $arg ) = @_;
75             # remove tainted'ness
76 0           ($fmt) = $fmt =~ /(.*)/;
77              
78 0           return sprintf("$fmt", $arg);
79             }
80              
81             ###############################################################################
82             ## ----------------------------------------------------------------------------
83             ## Public methods.
84             ##
85             ###############################################################################
86              
87             # new ( begin, end [, step, format ] )
88             # new ( )
89              
90             sub new {
91 0     0 1   my ( $class, $self ) = ( shift, [] );
92              
93 0 0         if ( !@_ ) {
94 0           @{ $self } = ( 0, 0, 1, '__NOOP__' );
  0            
95             } else {
96 0           _reset( $self, @_ );
97             }
98              
99 0           bless $self, $class;
100             }
101              
102             # next ( )
103              
104             sub next {
105 0     0 1   my ( $self ) = @_;
106 0           my $iter = $self->[_ITER];
107              
108 0 0         if ( defined $iter ) {
109 0           my ( $begv, $endv, $step, $fmt, $chunk_size, $bounds_only ) = @{ $self };
  0            
110 0           my ( $begn, $seqn );
111              
112             # computes from *begv* value to not lose precision during iteration
113              
114 0 0         if ( $begv <= $endv ) {
115 0           $begn = $seqn = $begv + ( $iter++ * $chunk_size * $step );
116 0 0 0       return if ( $begv == $endv && $begn != $begv );
117 0 0         return if ( $seqn > $endv );
118             }
119             else {
120 0           $begn = $seqn = $begv - -( $iter++ * $chunk_size * $step );
121 0 0         return if ( $seqn < $endv );
122             }
123              
124 0           $self->[_ITER] = $iter;
125              
126 0 0 0       if ( $chunk_size == 1 || $begv == $endv ) {
127 0 0         $seqn = _sprintf( "%$fmt", $seqn ) if ( defined $fmt );
128 0 0         return ( $bounds_only ) ? ( $seqn, $seqn, $iter ) : $seqn;
129             }
130              
131 0 0         if ( $bounds_only ) {
132 0           my ( $seqb, $seqe ) = ( $seqn );
133              
134 0 0         if ( $begv <= $endv ) {
135 0 0         if ( $step * ( $chunk_size - 1 ) + $seqn <= $endv ) {
    0          
136 0           $seqe = $step * ( $chunk_size - 1 ) + $seqn;
137             }
138             elsif ( $step == 1 ) {
139 0           $seqe = $endv;
140             }
141             else {
142 0           for my $i ( 1 .. $chunk_size ) {
143 0 0         last if ( $seqn > $endv );
144 0           $seqe = $seqn;
145 0           $seqn = $step * $i + $begn;
146             }
147             }
148             }
149             else {
150 0 0         if ( $step * ( $chunk_size - 1 ) + $seqn >= $endv ) {
    0          
151 0           $seqe = $step * ( $chunk_size - 1 ) + $seqn;
152             }
153             elsif ( $step == -1 ) {
154 0           $seqe = $endv;
155             }
156             else {
157 0           for my $i ( 1 .. $chunk_size ) {
158 0 0         last if ( $seqn < $endv );
159 0           $seqe = $seqn;
160 0           $seqn = $step * $i + $begn;
161             }
162             }
163             }
164              
165 0 0         return ( defined $fmt )
166             ? ( _sprintf("%$fmt",$seqb), _sprintf("%$fmt",$seqe) )
167             : ( $seqb, $seqe, $iter );
168             }
169              
170 0           my @n;
171              
172 0 0         if ( $begv <= $endv ) {
173 0 0 0       if ( !defined $fmt && $step == 1 && abs($endv) < ~1 && abs($begv) < ~1 ) {
      0        
      0        
174 0 0         return ( $seqn + $chunk_size <= $endv )
175             ? ( $seqn .. $seqn + $chunk_size - 1 )
176             : ( $seqn .. $endv );
177             }
178 0           for my $i ( 1 .. $chunk_size ) {
179 0 0         last if ( $seqn > $endv );
180 0 0         push @n, defined $fmt ? _sprintf( "%$fmt", $seqn ) : $seqn;
181 0           $seqn = $step * $i + $begn;
182             }
183             }
184             else {
185 0           for my $i ( 1 .. $chunk_size ) {
186 0 0         last if ( $seqn < $endv );
187 0 0         push @n, defined $fmt ? _sprintf( "%$fmt", $seqn ) : $seqn;
188 0           $seqn = $step * $i + $begn;
189             }
190             }
191              
192 0           return @n;
193             }
194              
195             else {
196 0           $self->[_ITER] = 0;
197 0           $self->next();
198             }
199             }
200              
201             # rewind ( begin, end [, step, format ] )
202             # rewind ( )
203              
204             sub rewind {
205 0     0 1   my $self = shift;
206              
207 0 0         if ( !@_ ) {
208 0 0         $self->[_ITER] = undef unless ( $self->[_FMT] eq '__NOOP__' );
209             } else {
210 0           _reset( $self, @_ );
211             }
212              
213 0           return;
214             }
215              
216             1;
217              
218             __END__