You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.4 KiB
162 lines
4.4 KiB
package NGCP::Panel::Utils::IntervalTree::Node;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use POSIX ();
|
|
use List::Util qw(min max);
|
|
|
|
my $EMPTY_NODE = __PACKAGE__->new(0, 0, undef);
|
|
|
|
sub _nlog {
|
|
return -1.0 / log(0.5);
|
|
}
|
|
|
|
sub left_node {
|
|
my ($self) = @_;
|
|
return $self->{cleft} != $EMPTY_NODE ? $self->{cleft} : undef;
|
|
}
|
|
|
|
sub right_node {
|
|
my ($self) = @_;
|
|
return $self->{cright} != $EMPTY_NODE ? $self->{cright} : undef;
|
|
}
|
|
|
|
sub root_node {
|
|
my ($self) = @_;
|
|
return $self->{croot} != $EMPTY_NODE ? $self->{croot} : undef;
|
|
}
|
|
|
|
#sub str {
|
|
# my ($self) = @_;
|
|
# return "Node($self->{start}, $self->{end})";
|
|
#}
|
|
|
|
sub new {
|
|
my ($class, $start, $end, $interval) = @_;
|
|
# Perl lacks the binomial distribution, so we convert a
|
|
# uniform into a binomial because it naturally scales with
|
|
# tree size. Also, perl's uniform is perfect since the
|
|
# upper limit is not inclusive, which gives us undefined here.
|
|
my $self = {};
|
|
$self->{priority} = POSIX::ceil(_nlog() * log(-1.0/(1.0 * rand() - 1)));
|
|
$self->{start} = $start;
|
|
$self->{end} = $end;
|
|
$self->{interval} = $interval;
|
|
$self->{maxend} = $end;
|
|
$self->{minstart} = $start;
|
|
$self->{minend} = $end;
|
|
$self->{cleft} = $EMPTY_NODE;
|
|
$self->{cright} = $EMPTY_NODE;
|
|
$self->{croot} = $EMPTY_NODE;
|
|
return bless $self, $class;
|
|
}
|
|
|
|
sub insert {
|
|
my ($self, $start, $end, $interval) = @_;
|
|
my $croot = $self;
|
|
# If starts are the same, decide which to add interval to based on
|
|
# end, thus maintaining sortedness relative to start/end
|
|
my $decision_endpoint = $start;
|
|
if ($start == $self->{start}) {
|
|
$decision_endpoint = $end;
|
|
}
|
|
|
|
if ($decision_endpoint > $self->{start}) {
|
|
# insert to cright tree
|
|
if ($self->{cright} != $EMPTY_NODE) {
|
|
$self->{cright} = $self->{cright}->insert( $start, $end, $interval );
|
|
}
|
|
else {
|
|
$self->{cright} = __PACKAGE__->new( $start, $end, $interval );
|
|
}
|
|
# rebalance tree
|
|
if ($self->{priority} < $self->{cright}{priority}) {
|
|
$croot = $self->rotate_left();
|
|
}
|
|
}
|
|
else {
|
|
# insert to cleft tree
|
|
if ($self->{cleft} != $EMPTY_NODE) {
|
|
$self->{cleft} = $self->{cleft}->insert( $start, $end, $interval);
|
|
}
|
|
else {
|
|
$self->{cleft} = __PACKAGE__->new( $start, $end, $interval);
|
|
}
|
|
# rebalance tree
|
|
if ($self->{priority} < $self->{cleft}{priority}) {
|
|
$croot = $self->rotate_right();
|
|
}
|
|
}
|
|
|
|
$croot->set_ends();
|
|
$self->{cleft}{croot} = $croot;
|
|
$self->{cright}{croot} = $croot;
|
|
return $croot;
|
|
}
|
|
|
|
sub rotate_right {
|
|
my ($self) = @_;
|
|
my $croot = $self->{cleft};
|
|
$self->{cleft} = $self->{cleft}{cright};
|
|
$croot->{cright} = $self;
|
|
$self->set_ends();
|
|
return $croot;
|
|
}
|
|
|
|
sub rotate_left {
|
|
my ($self) = @_;
|
|
my $croot = $self->{cright};
|
|
$self->{cright} = $self->{cright}{cleft};
|
|
$croot->{cleft} = $self;
|
|
$self->set_ends();
|
|
return $croot;
|
|
}
|
|
|
|
sub set_ends {
|
|
my ($self) = @_;
|
|
if ($self->{cright} != $EMPTY_NODE && $self->{cleft} != $EMPTY_NODE) {
|
|
$self->{maxend} = max($self->{end}, $self->{cright}{maxend}, $self->{cleft}{maxend});
|
|
$self->{minend} = min($self->{end}, $self->{cright}{minend}, $self->{cleft}{minend});
|
|
$self->{minstart} = min($self->{start}, $self->{cright}{minstart}, $self->{cleft}{minstart});
|
|
}
|
|
elsif ( $self->{cright} != $EMPTY_NODE) {
|
|
$self->{maxend} = max($self->{end}, $self->{cright}{maxend});
|
|
$self->{minend} = min($self->{end}, $self->{cright}{minend});
|
|
$self->{minstart} = min($self->{start}, $self->{cright}{minstart});
|
|
}
|
|
elsif ( $self->{cleft} != $EMPTY_NODE) {
|
|
$self->{maxend} = max($self->{end}, $self->{cleft}{maxend});
|
|
$self->{minend} = min($self->{end}, $self->{cleft}{minend});
|
|
$self->{minstart} = min($self->{start}, $self->{cleft}{minstart});
|
|
}
|
|
}
|
|
|
|
sub intersect {
|
|
my ( $self, $start, $end, $sort ) = @_;
|
|
$sort = 1 if !defined $sort;
|
|
my $results = [];
|
|
$self->_intersect( $start, $end, $results );
|
|
return $results;
|
|
}
|
|
|
|
#*find = \&intersect;
|
|
|
|
sub _intersect {
|
|
my ( $self, $start, $end, $results) = @_;
|
|
# Left subtree
|
|
if ($self->{cleft} != $EMPTY_NODE && $self->{cleft}{maxend} > $start) {
|
|
$self->{cleft}->_intersect( $start, $end, $results );
|
|
}
|
|
# This interval
|
|
if (( $self->{end} > $start ) && ( $self->{start} < $end )) {
|
|
push @$results, $self->{interval};
|
|
}
|
|
# Right subtree
|
|
if ($self->{cright} != $EMPTY_NODE && $self->{start} < $end) {
|
|
$self->{cright}->_intersect( $start, $end, $results );
|
|
}
|
|
}
|
|
|
|
1;
|