Filename | /usr/share/perl5/Set/Infinite/_recurrence.pm |
Statements | Executed 16 statements in 1.53ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 12µs | 24µs | BEGIN@7 | Set::Infinite::_recurrence::
1 | 1 | 1 | 11µs | 32µs | BEGIN@15 | Set::Infinite::_recurrence::
1 | 1 | 1 | 8µs | 48µs | BEGIN@9 | Set::Infinite::_recurrence::
1 | 1 | 1 | 7µs | 7µs | BEGIN@17 | Set::Infinite::_recurrence::
1 | 1 | 1 | 7µs | 29µs | BEGIN@10 | Set::Infinite::_recurrence::
1 | 1 | 1 | 6µs | 39µs | BEGIN@12 | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:106] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:233] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:245] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:266] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:271] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:57] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | __ANON__[:76] | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | _is_recurrence | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | _recurrence | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | intersection | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | is_forever | Set::Infinite::_recurrence::
0 | 0 | 0 | 0s | 0s | union | Set::Infinite::_recurrence::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # Copyright (c) 2003 Flavio Soibelmann Glock. All rights reserved. | ||||
2 | # This program is free software; you can redistribute it and/or | ||||
3 | # modify it under the same terms as Perl itself. | ||||
4 | |||||
5 | package Set::Infinite::_recurrence; | ||||
6 | |||||
7 | 2 | 432µs | 2 | 37µs | # spent 24µs (12+13) within Set::Infinite::_recurrence::BEGIN@7 which was called:
# once (12µs+13µs) by DateTime::Set::BEGIN@10 at line 7 # spent 24µs making 1 call to Set::Infinite::_recurrence::BEGIN@7
# spent 13µs making 1 call to strict::import |
8 | |||||
9 | 2 | 31µs | 2 | 87µs | # spent 48µs (8+39) within Set::Infinite::_recurrence::BEGIN@9 which was called:
# once (8µs+39µs) by DateTime::Set::BEGIN@10 at line 9 # spent 48µs making 1 call to Set::Infinite::_recurrence::BEGIN@9
# spent 39µs making 1 call to constant::import |
10 | 2 | 25µs | 2 | 51µs | # spent 29µs (7+22) within Set::Infinite::_recurrence::BEGIN@10 which was called:
# once (7µs+22µs) by DateTime::Set::BEGIN@10 at line 10 # spent 29µs making 1 call to Set::Infinite::_recurrence::BEGIN@10
# spent 22µs making 1 call to constant::import |
11 | |||||
12 | 2 | 32µs | 2 | 71µs | # spent 39µs (6+33) within Set::Infinite::_recurrence::BEGIN@12 which was called:
# once (6µs+33µs) by DateTime::Set::BEGIN@10 at line 12 # spent 39µs making 1 call to Set::Infinite::_recurrence::BEGIN@12
# spent 32µs making 1 call to vars::import |
13 | |||||
14 | 1 | 9µs | @ISA = qw( Set::Infinite ); | ||
15 | 3 | 245µs | 3 | 53µs | # spent 32µs (11+21) within Set::Infinite::_recurrence::BEGIN@15 which was called:
# once (11µs+21µs) by DateTime::Set::BEGIN@10 at line 15 # spent 32µs making 1 call to Set::Infinite::_recurrence::BEGIN@15
# spent 13µs making 1 call to Exporter::import
# spent 9µs making 1 call to version::_VERSION |
16 | |||||
17 | # spent 7µs within Set::Infinite::_recurrence::BEGIN@17 which was called:
# once (7µs+0s) by DateTime::Set::BEGIN@10 at line 77 | ||||
18 | 1 | 300ns | $PRETTY_PRINT = 1; # enable Set::Infinite debug | ||
19 | 1 | 100ns | $max_iterate = 20; | ||
20 | |||||
21 | # TODO: inherit %Set::Infinite::_first / _last | ||||
22 | # in a more "object oriented" way | ||||
23 | |||||
24 | $Set::Infinite::_first{_recurrence} = | ||||
25 | sub { | ||||
26 | my $self = $_[0]; | ||||
27 | my ($callback_next, $callback_previous) = @{ $self->{param} }; | ||||
28 | my ($min, $min_open) = $self->{parent}->min_a; | ||||
29 | # my ($max, $max_open) = $self->{parent}->max_a; | ||||
30 | |||||
31 | my ( $min1, $min2 ); | ||||
32 | $min1 = $callback_next->( $min ); | ||||
33 | if ( ! $min_open ) | ||||
34 | { | ||||
35 | $min2 = $callback_previous->( $min1 ); | ||||
36 | $min1 = $min2 if defined $min2 && $min == $min2; | ||||
37 | } | ||||
38 | |||||
39 | my $start = $callback_next->( $min1 ); | ||||
40 | my $end = $self->{parent}->max; | ||||
41 | |||||
42 | #print STDERR "set "; | ||||
43 | #print STDERR $start->datetime | ||||
44 | # unless $start == INFINITY; | ||||
45 | #print STDERR " - " ; | ||||
46 | #print STDERR $end->datetime | ||||
47 | # unless $end == INFINITY; | ||||
48 | #print STDERR "\n"; | ||||
49 | |||||
50 | return ( $self->new( $min1 ), undef ) | ||||
51 | if $start > $end; | ||||
52 | |||||
53 | return ( $self->new( $min1 ), | ||||
54 | $self->new( $start, $end )-> | ||||
55 | # $self->new( {a => $start, b => $end, open_end => $max_open} )-> | ||||
56 | _function( '_recurrence', @{ $self->{param} } ) ); | ||||
57 | 1 | 2µs | }; | ||
58 | $Set::Infinite::_last{_recurrence} = | ||||
59 | sub { | ||||
60 | my $self = $_[0]; | ||||
61 | my ($callback_next, $callback_previous) = @{ $self->{param} }; | ||||
62 | my ($max, $max_open) = $self->{parent}->max_a; | ||||
63 | |||||
64 | my ( $max1, $max2 ); | ||||
65 | $max1 = $callback_previous->( $max ); | ||||
66 | if ( ! $max_open ) | ||||
67 | { | ||||
68 | $max2 = $callback_next->( $max1 ); | ||||
69 | $max1 = $max2 if $max == $max2; | ||||
70 | } | ||||
71 | |||||
72 | return ( $self->new( $max1 ), | ||||
73 | $self->new( $self->{parent}->min, | ||||
74 | $callback_previous->( $max1 ) )-> | ||||
75 | _function( '_recurrence', @{ $self->{param} } ) ); | ||||
76 | 1 | 5µs | }; | ||
77 | 1 | 751µs | 1 | 7µs | } # spent 7µs making 1 call to Set::Infinite::_recurrence::BEGIN@17 |
78 | |||||
79 | # $si->_recurrence( | ||||
80 | # \&callback_next, \&callback_previous ) | ||||
81 | # | ||||
82 | # Generates "recurrences" from a callback. | ||||
83 | # These recurrences are simple lists of dates. | ||||
84 | # | ||||
85 | # The recurrence generation is based on an idea from Dave Rolsky. | ||||
86 | # | ||||
87 | |||||
88 | # use Data::Dumper; | ||||
89 | # use Carp qw(cluck); | ||||
90 | |||||
91 | sub _recurrence { | ||||
92 | my $set = shift; | ||||
93 | my ( $callback_next, $callback_previous, $delta ) = @_; | ||||
94 | |||||
95 | $delta->{count} = 0 unless defined $delta->{delta}; | ||||
96 | |||||
97 | # warn "reusing delta: ". $delta->{count} if defined $delta->{delta}; | ||||
98 | # warn Dumper( $delta ); | ||||
99 | |||||
100 | if ( $#{ $set->{list} } != 0 || $set->is_too_complex ) | ||||
101 | { | ||||
102 | return $set->iterate( | ||||
103 | sub { | ||||
104 | $_[0]->_recurrence( | ||||
105 | $callback_next, $callback_previous, $delta ) | ||||
106 | } ); | ||||
107 | } | ||||
108 | # $set is a span | ||||
109 | my $result; | ||||
110 | if ($set->min != NEG_INFINITY && $set->max != INFINITY) | ||||
111 | { | ||||
112 | # print STDERR " finite set\n"; | ||||
113 | my ($min, $min_open) = $set->min_a; | ||||
114 | my ($max, $max_open) = $set->max_a; | ||||
115 | |||||
116 | my ( $min1, $min2 ); | ||||
117 | $min1 = $callback_next->( $min ); | ||||
118 | if ( ! $min_open ) | ||||
119 | { | ||||
120 | $min2 = $callback_previous->( $min1 ); | ||||
121 | $min1 = $min2 if defined $min2 && $min == $min2; | ||||
122 | } | ||||
123 | |||||
124 | $result = $set->new(); | ||||
125 | |||||
126 | # get "delta" - abort if this will take too much time. | ||||
127 | |||||
128 | unless ( defined $delta->{max_delta} ) | ||||
129 | { | ||||
130 | for ( $delta->{count} .. 10 ) | ||||
131 | { | ||||
132 | if ( $max_open ) | ||||
133 | { | ||||
134 | return $result if $min1 >= $max; | ||||
135 | } | ||||
136 | else | ||||
137 | { | ||||
138 | return $result if $min1 > $max; | ||||
139 | } | ||||
140 | push @{ $result->{list} }, | ||||
141 | { a => $min1, b => $min1, open_begin => 0, open_end => 0 }; | ||||
142 | $min2 = $callback_next->( $min1 ); | ||||
143 | |||||
144 | if ( $delta->{delta} ) | ||||
145 | { | ||||
146 | $delta->{delta} += $min2 - $min1; | ||||
147 | } | ||||
148 | else | ||||
149 | { | ||||
150 | $delta->{delta} = $min2 - $min1; | ||||
151 | } | ||||
152 | $delta->{count}++; | ||||
153 | $min1 = $min2; | ||||
154 | } | ||||
155 | |||||
156 | $delta->{max_delta} = $delta->{delta} * 40; | ||||
157 | } | ||||
158 | |||||
159 | if ( $max < $min + $delta->{max_delta} ) | ||||
160 | { | ||||
161 | for ( 1 .. 200 ) | ||||
162 | { | ||||
163 | if ( $max_open ) | ||||
164 | { | ||||
165 | return $result if $min1 >= $max; | ||||
166 | } | ||||
167 | else | ||||
168 | { | ||||
169 | return $result if $min1 > $max; | ||||
170 | } | ||||
171 | push @{ $result->{list} }, | ||||
172 | { a => $min1, b => $min1, open_begin => 0, open_end => 0 }; | ||||
173 | $min1 = $callback_next->( $min1 ); | ||||
174 | } | ||||
175 | } | ||||
176 | |||||
177 | # cluck "give up"; | ||||
178 | } | ||||
179 | |||||
180 | # return a "_function", such that we can backtrack later. | ||||
181 | my $func = $set->_function( '_recurrence', $callback_next, $callback_previous, $delta ); | ||||
182 | |||||
183 | # removed - returning $result doesn't help on speed | ||||
184 | ## return $func->_function2( 'union', $result ) if $result; | ||||
185 | |||||
186 | return $func; | ||||
187 | } | ||||
188 | |||||
189 | sub is_forever | ||||
190 | { | ||||
191 | $#{ $_[0]->{list} } == 0 && | ||||
192 | $_[0]->max == INFINITY && | ||||
193 | $_[0]->min == NEG_INFINITY | ||||
194 | } | ||||
195 | |||||
196 | sub _is_recurrence | ||||
197 | { | ||||
198 | exists $_[0]->{method} && | ||||
199 | $_[0]->{method} eq '_recurrence' && | ||||
200 | $_[0]->{parent}->is_forever | ||||
201 | } | ||||
202 | |||||
203 | sub intersection | ||||
204 | { | ||||
205 | my ($s1, $s2) = (shift,shift); | ||||
206 | |||||
207 | if ( exists $s1->{method} && $s1->{method} eq '_recurrence' ) | ||||
208 | { | ||||
209 | # optimize: recurrence && span | ||||
210 | return $s1->{parent}-> | ||||
211 | intersection( $s2, @_ )-> | ||||
212 | _recurrence( @{ $s1->{param} } ) | ||||
213 | unless ref($s2) && exists $s2->{method}; | ||||
214 | |||||
215 | # optimize: recurrence && recurrence | ||||
216 | if ( $s1->{parent}->is_forever && | ||||
217 | ref($s2) && _is_recurrence( $s2 ) ) | ||||
218 | { | ||||
219 | my ( $next1, $previous1 ) = @{ $s1->{param} }; | ||||
220 | my ( $next2, $previous2 ) = @{ $s2->{param} }; | ||||
221 | return $s1->{parent}->_function( '_recurrence', | ||||
222 | sub { | ||||
223 | # intersection of parent 'next' callbacks | ||||
224 | my ($n1, $n2); | ||||
225 | my $iterate = 0; | ||||
226 | $n2 = $next2->( $_[0] ); | ||||
227 | while(1) { | ||||
228 | $n1 = $next1->( $previous1->( $n2 ) ); | ||||
229 | return $n1 if $n1 == $n2; | ||||
230 | $n2 = $next2->( $previous2->( $n1 ) ); | ||||
231 | return if $iterate++ == $max_iterate; | ||||
232 | } | ||||
233 | }, | ||||
234 | sub { | ||||
235 | # intersection of parent 'previous' callbacks | ||||
236 | my ($p1, $p2); | ||||
237 | my $iterate = 0; | ||||
238 | $p2 = $previous2->( $_[0] ); | ||||
239 | while(1) { | ||||
240 | $p1 = $previous1->( $next1->( $p2 ) ); | ||||
241 | return $p1 if $p1 == $p2; | ||||
242 | $p2 = $previous2->( $next2->( $p1 ) ); | ||||
243 | return if $iterate++ == $max_iterate; | ||||
244 | } | ||||
245 | }, | ||||
246 | ); | ||||
247 | } | ||||
248 | } | ||||
249 | return $s1->SUPER::intersection( $s2, @_ ); | ||||
250 | } | ||||
251 | |||||
252 | sub union | ||||
253 | { | ||||
254 | my ($s1, $s2) = (shift,shift); | ||||
255 | if ( $s1->_is_recurrence && | ||||
256 | ref($s2) && _is_recurrence( $s2 ) ) | ||||
257 | { | ||||
258 | # optimize: recurrence || recurrence | ||||
259 | my ( $next1, $previous1 ) = @{ $s1->{param} }; | ||||
260 | my ( $next2, $previous2 ) = @{ $s2->{param} }; | ||||
261 | return $s1->{parent}->_function( '_recurrence', | ||||
262 | sub { # next | ||||
263 | my $n1 = $next1->( $_[0] ); | ||||
264 | my $n2 = $next2->( $_[0] ); | ||||
265 | return $n1 < $n2 ? $n1 : $n2; | ||||
266 | }, | ||||
267 | sub { # previous | ||||
268 | my $p1 = $previous1->( $_[0] ); | ||||
269 | my $p2 = $previous2->( $_[0] ); | ||||
270 | return $p1 > $p2 ? $p1 : $p2; | ||||
271 | }, | ||||
272 | ); | ||||
273 | } | ||||
274 | return $s1->SUPER::union( $s2, @_ ); | ||||
275 | } | ||||
276 | |||||
277 | =head1 NAME | ||||
278 | |||||
279 | Set::Infinite::_recurrence - Extends Set::Infinite with recurrence functions | ||||
280 | |||||
281 | =head1 SYNOPSIS | ||||
282 | |||||
283 | $recurrence = $base_set->_recurrence ( \&next, \&previous ); | ||||
284 | |||||
285 | =head1 DESCRIPTION | ||||
286 | |||||
287 | This is an internal class used by the DateTime::Set module. | ||||
288 | The API is subject to change. | ||||
289 | |||||
290 | It provides all functionality provided by Set::Infinite, plus the ability | ||||
291 | to define recurrences with arbitrary objects, such as dates. | ||||
292 | |||||
293 | =head1 METHODS | ||||
294 | |||||
295 | =over 4 | ||||
296 | |||||
297 | =item * _recurrence ( \&next, \&previous ) | ||||
298 | |||||
299 | Creates a recurrence set. The set is defined inside a 'base set'. | ||||
300 | |||||
301 | $recurrence = $base_set->_recurrence ( \&next, \&previous ); | ||||
302 | |||||
303 | The recurrence functions take one argument, and return the 'next' or | ||||
304 | the 'previous' occurrence. | ||||
305 | |||||
306 | Example: defines the set of all 'integer numbers': | ||||
307 | |||||
308 | use strict; | ||||
309 | |||||
310 | use Set::Infinite::_recurrence; | ||||
311 | use POSIX qw(floor); | ||||
312 | |||||
313 | # define the recurrence span | ||||
314 | my $forever = Set::Infinite::_recurrence->new( | ||||
315 | Set::Infinite::_recurrence::NEG_INFINITY, | ||||
316 | Set::Infinite::_recurrence::INFINITY | ||||
317 | ); | ||||
318 | |||||
319 | my $recurrence = $forever->_recurrence( | ||||
320 | sub { # next | ||||
321 | floor( $_[0] + 1 ) | ||||
322 | }, | ||||
323 | sub { # previous | ||||
324 | my $tmp = floor( $_[0] ); | ||||
325 | $tmp < $_[0] ? $tmp : $_[0] - 1 | ||||
326 | }, | ||||
327 | ); | ||||
328 | |||||
329 | print "sample recurrence ", | ||||
330 | $recurrence->intersection( -5, 5 ), "\n"; | ||||
331 | # sample recurrence -5,-4,-3,-2,-1,0,1,2,3,4,5 | ||||
332 | |||||
333 | { | ||||
334 | my $x = 234.567; | ||||
335 | print "next occurence after $x = ", | ||||
336 | $recurrence->{param}[0]->( $x ), "\n"; # 235 | ||||
337 | print "previous occurence before $x = ", | ||||
338 | $recurrence->{param}[2]->( $x ), "\n"; # 234 | ||||
339 | } | ||||
340 | |||||
341 | { | ||||
342 | my $x = 234; | ||||
343 | print "next occurence after $x = ", | ||||
344 | $recurrence->{param}[0]->( $x ), "\n"; # 235 | ||||
345 | print "previous occurence before $x = ", | ||||
346 | $recurrence->{param}[2]->( $x ), "\n"; # 233 | ||||
347 | } | ||||
348 | |||||
349 | =item * is_forever | ||||
350 | |||||
351 | Returns true if the set is a single span, | ||||
352 | ranging from -Infinity to Infinity. | ||||
353 | |||||
354 | =item * _is_recurrence | ||||
355 | |||||
356 | Returns true if the set is an unbounded recurrence, | ||||
357 | ranging from -Infinity to Infinity. | ||||
358 | |||||
359 | =back | ||||
360 | |||||
361 | =head1 CONSTANTS | ||||
362 | |||||
363 | =over 4 | ||||
364 | |||||
365 | =item * INFINITY | ||||
366 | |||||
367 | The C<Infinity> value. | ||||
368 | |||||
369 | =item * NEG_INFINITY | ||||
370 | |||||
371 | The C<-Infinity> value. | ||||
372 | |||||
373 | =back | ||||
374 | |||||
375 | =head1 SUPPORT | ||||
376 | |||||
377 | Support is offered through the C<datetime@perl.org> mailing list. | ||||
378 | |||||
379 | Please report bugs using rt.cpan.org | ||||
380 | |||||
381 | =head1 AUTHOR | ||||
382 | |||||
383 | Flavio Soibelmann Glock <fglock@gmail.com> | ||||
384 | |||||
385 | The recurrence generation algorithm is based on an idea from Dave Rolsky. | ||||
386 | |||||
387 | =head1 COPYRIGHT | ||||
388 | |||||
389 | Copyright (c) 2003 Flavio Soibelmann Glock. All rights reserved. | ||||
390 | This program is free software; you can distribute it and/or | ||||
391 | modify it under the same terms as Perl itself. | ||||
392 | |||||
393 | The full text of the license can be found in the LICENSE file | ||||
394 | included with this module. | ||||
395 | |||||
396 | =head1 SEE ALSO | ||||
397 | |||||
398 | Set::Infinite | ||||
399 | |||||
400 | DateTime::Set | ||||
401 | |||||
402 | For details on the Perl DateTime Suite project please see | ||||
403 | L<http://datetime.perl.org>. | ||||
404 | |||||
405 | =cut | ||||
406 |