use
constant
INFINITY
=> 100 ** 100 ** 100 ;
use
constant
NEG_INFINITY
=> -1 * (100 ** 100 ** 100);
use
vars
qw( @ISA $PRETTY_PRINT $max_iterate )
;
@ISA
=
qw( Set::Infinite )
;
BEGIN {
$PRETTY_PRINT
= 1;
$max_iterate
= 20;
$Set::Infinite::_first
{_recurrence} =
sub
{
my
$self
=
$_
[0];
my
(
$callback_next
,
$callback_previous
) = @{
$self
->{param} };
my
(
$min
,
$min_open
) =
$self
->{parent}->min_a;
my
(
$min1
,
$min2
);
$min1
=
$callback_next
->(
$min
);
if
( !
$min_open
)
{
$min2
=
$callback_previous
->(
$min1
);
$min1
=
$min2
if
defined
$min2
&&
$min
==
$min2
;
}
my
$start
=
$callback_next
->(
$min1
);
my
$end
=
$self
->{parent}->max;
return
(
$self
->new(
$min1
),
undef
)
if
$start
>
$end
;
return
(
$self
->new(
$min1
),
$self
->new(
$start
,
$end
)->
_function(
'_recurrence'
, @{
$self
->{param} } ) );
};
$Set::Infinite::_last
{_recurrence} =
sub
{
my
$self
=
$_
[0];
my
(
$callback_next
,
$callback_previous
) = @{
$self
->{param} };
my
(
$max
,
$max_open
) =
$self
->{parent}->max_a;
my
(
$max1
,
$max2
);
$max1
=
$callback_previous
->(
$max
);
if
( !
$max_open
)
{
$max2
=
$callback_next
->(
$max1
);
$max1
=
$max2
if
$max
==
$max2
;
}
return
(
$self
->new(
$max1
),
$self
->new(
$self
->{parent}->min,
$callback_previous
->(
$max1
) )->
_function(
'_recurrence'
, @{
$self
->{param} } ) );
};
}
sub
_recurrence {
my
$set
=
shift
;
my
(
$callback_next
,
$callback_previous
,
$delta
) =
@_
;
$delta
->{count} = 0
unless
defined
$delta
->{delta};
if
( $
{
return
$set
->iterate(
sub
{
$_
[0]->_recurrence(
$callback_next
,
$callback_previous
,
$delta
)
} );
}
my
$result
;
if
(
$set
->min != NEG_INFINITY &&
$set
->max != INFINITY)
{
my
(
$min
,
$min_open
) =
$set
->min_a;
my
(
$max
,
$max_open
) =
$set
->max_a;
my
(
$min1
,
$min2
);
$min1
=
$callback_next
->(
$min
);
if
( !
$min_open
)
{
$min2
=
$callback_previous
->(
$min1
);
$min1
=
$min2
if
defined
$min2
&&
$min
==
$min2
;
}
$result
=
$set
->new();
unless
(
defined
$delta
->{max_delta} )
{
for
(
$delta
->{count} .. 10 )
{
if
(
$max_open
)
{
return
$result
if
$min1
>=
$max
;
}
else
{
return
$result
if
$min1
>
$max
;
}
push
@{
$result
->{list} },
{
a
=>
$min1
,
b
=>
$min1
,
open_begin
=> 0,
open_end
=> 0 };
$min2
=
$callback_next
->(
$min1
);
if
(
$delta
->{delta} )
{
$delta
->{delta} +=
$min2
-
$min1
;
}
else
{
$delta
->{delta} =
$min2
-
$min1
;
}
$delta
->{count}++;
$min1
=
$min2
;
}
$delta
->{max_delta} =
$delta
->{delta} * 40;
}
if
(
$max
<
$min
+
$delta
->{max_delta} )
{
for
( 1 .. 200 )
{
if
(
$max_open
)
{
return
$result
if
$min1
>=
$max
;
}
else
{
return
$result
if
$min1
>
$max
;
}
push
@{
$result
->{list} },
{
a
=>
$min1
,
b
=>
$min1
,
open_begin
=> 0,
open_end
=> 0 };
$min1
=
$callback_next
->(
$min1
);
}
}
}
my
$func
=
$set
->_function(
'_recurrence'
,
$callback_next
,
$callback_previous
,
$delta
);
return
$func
;
}
sub
is_forever
{
$
$_
[0]->max == INFINITY &&
$_
[0]->min == NEG_INFINITY
}
sub
_is_recurrence
{
exists
$_
[0]->{method} &&
$_
[0]->{method} eq
'_recurrence'
&&
$_
[0]->{parent}->is_forever
}
sub
intersects
{
my
(
$s1
,
$s2
) = (
shift
,
shift
);
if
(
exists
$s1
->{method} &&
$s1
->{method} eq
'_recurrence'
)
{
unless
(
ref
(
$s2
) &&
exists
$s2
->{method} ) {
my
$intersection
=
$s1
->intersection(
$s2
,
@_
);
my
$min
=
$intersection
->min;
return
1
if
defined
$min
&&
$min
!= NEG_INFINITY &&
$min
!= INFINITY;
my
$max
=
$intersection
->max;
return
1
if
defined
$max
&&
$max
!= NEG_INFINITY &&
$max
!= INFINITY;
}
if
(
$s1
->{parent}->is_forever &&
ref
(
$s2
) && _is_recurrence(
$s2
) )
{
my
$intersection
=
$s1
->intersection(
$s2
,
@_
);
my
$min
=
$intersection
->min;
return
1
if
defined
$min
&&
$min
!= NEG_INFINITY &&
$min
!= INFINITY;
my
$max
=
$intersection
->max;
return
1
if
defined
$max
&&
$max
!= NEG_INFINITY &&
$max
!= INFINITY;
}
}
return
$s1
->SUPER::intersects(
$s2
,
@_
);
}
sub
intersection
{
my
(
$s1
,
$s2
) = (
shift
,
shift
);
if
(
exists
$s1
->{method} &&
$s1
->{method} eq
'_recurrence'
)
{
return
$s1
->{parent}->
intersection(
$s2
,
@_
)->
_recurrence( @{
$s1
->{param} } )
unless
ref
(
$s2
) &&
exists
$s2
->{method};
if
(
$s1
->{parent}->is_forever &&
ref
(
$s2
) && _is_recurrence(
$s2
) )
{
my
(
$next1
,
$previous1
) = @{
$s1
->{param} };
my
(
$next2
,
$previous2
) = @{
$s2
->{param} };
return
$s1
->{parent}->_function(
'_recurrence'
,
sub
{
my
(
$n1
,
$n2
);
my
$iterate
= 0;
$n2
=
$next2
->(
$_
[0] );
while
(1) {
$n1
=
$next1
->(
$previous1
->(
$n2
) );
return
$n1
if
$n1
==
$n2
;
$n2
=
$next2
->(
$previous2
->(
$n1
) );
return
if
$iterate
++ ==
$max_iterate
;
}
},
sub
{
my
(
$p1
,
$p2
);
my
$iterate
= 0;
$p2
=
$previous2
->(
$_
[0] );
while
(1) {
$p1
=
$previous1
->(
$next1
->(
$p2
) );
return
$p1
if
$p1
==
$p2
;
$p2
=
$previous2
->(
$next2
->(
$p1
) );
return
if
$iterate
++ ==
$max_iterate
;
}
},
);
}
}
return
$s1
->SUPER::intersection(
$s2
,
@_
);
}
sub
union
{
my
(
$s1
,
$s2
) = (
shift
,
shift
);
if
(
$s1
->_is_recurrence &&
ref
(
$s2
) && _is_recurrence(
$s2
) )
{
my
(
$next1
,
$previous1
) = @{
$s1
->{param} };
my
(
$next2
,
$previous2
) = @{
$s2
->{param} };
return
$s1
->{parent}->_function(
'_recurrence'
,
sub
{
my
$n1
=
$next1
->(
$_
[0] );
my
$n2
=
$next2
->(
$_
[0] );
return
$n1
<
$n2
?
$n1
:
$n2
;
},
sub
{
my
$p1
=
$previous1
->(
$_
[0] );
my
$p2
=
$previous2
->(
$_
[0] );
return
$p1
>
$p2
?
$p1
:
$p2
;
},
);
}
return
$s1
->SUPER::union(
$s2
,
@_
);
}