Tree-BPTree
view release on metacpan or search on metacpan
lib/Tree/BPTree.pm view on Meta::CPAN
# my ($self, $new) = @_;
# $$self[-1] = $new if defined $new;
# return $$self[-1];
# }
#
sub first_leaf {
my ($self) = @_;
my $current = $self;
until ($current->isa('Tree::BPTree::Leaf')) {
$current = $$current[0];
}
return $current;
}
sub last_leaf {
my ($self) = @_;
my $current = $self;
until ($current->isa('Tree::BPTree::Leaf')) {
$current = $$current[-1];
}
return $current;
}
#
# sub nkeys {
# my ($self) = @_;
# return (scalar(@$self) - 1) / 2;
# }
#
# sub nvalues {
# my ($self) = @_;
# return (scalar(@$self) + 1) / 2;
# }
# The find operation differs slightly between branch and leaf. See the comment
# near Tree::BPTree::Leaf::find for details.
sub find {
my ($self, $cmp, $key) = @_;
my $nkeys = (@$self - 1) / 2;
for (my $k = 0; $k < $nkeys; $k++) {
if (&$cmp($key, $self->[($k) * 2 + 1]) < 0) {
return $k;
}
}
return (@$self + 1) / 2 - 1;
}
sub insert {
my ($self, $v, $key, $value) = @_;
splice @$self, $v * 2, 0, $value, $key;
}
sub split {
my ($self, $n, $cmp, $key) = @_;
# find the node we're going to insert to; split that node; if it splits
# either incorporate the split in ourselves or split ourselves if we are
# full
my $v = $self->find($cmp, $key);
my $result = $self->[($v) * 2]->split($n, $cmp, $key);
if ((@$self + 1) / 2 == $n && defined $result) {
# We're full and they split, we must split too. The way the split must
# be handled will depend upon whether this is a Left, Center, or Right
# split. That is, is the sub-split node pointer on the left side, the
# middle, or the right. But first, let's go ahead and split the node in
# half.
#
# The way a node can be split depends on the oddness of n. If n is odd
# (normal looking node split), then we split at index n-1 and give the
# new node n elements. If n is even, we split at index n and give the
# new node n-1 elements. The combinatorics of this solution are kind of
# interesting. In any case, we create the new node complete while
# leaving the current node with a missing end-pointer.
my $new_node = Tree::BPTree::Node->new(
splice @$self,
$n - ($n % 2), # n - 1 for odd or n - 0 for even
$n - (($n + 1) % 2), # n - 0 for odd or n - 1 for even
);
my $root_key;
if ($v < $n / 2) {
# This is a left split. We need to clip off the last key, insert the
# child's new root key and set the pointers on either side to the
# new root nodes. Finally, return a new root with clipped key
# pointing to us and the new node.
$root_key = pop @$self;
my $i = $self->find($cmp, $result->[1]);
$self->insert($i, $result->[1], $result->[0]);
$self->[($i+1) * 2] = $result->[2];
} elsif ($v > $n / 2) {
# This is a right split. Same as left in reverse, basically. We do
# need to first shear of the first pointer to the new node and
# append it back onto as the last pointer of the first node first.
push @$self, shift @$new_node;
$root_key = shift @$new_node;
my $i = $new_node->find($cmp, $result->[1]);
$new_node->[($i) * 2] = $result->[2];
$new_node->insert($i, $result->[1], $result->[0]);
} else {
# This is a center split. Here, we append to ourself a new pointer
# pointing to the new left node. We set the new node's first pointer
# to the new right node. And we set the new root key to the child's
# new root key.
push @$self, $result->[0];
$new_node->[0] = $result->[2];
$root_key = $result->[1];
}
return Tree::BPTree::Node->new($self, $root_key, $new_node);
} elsif (defined $result) {
# We have room to accomodate their split, add the new nodes here.
# Regular insert will do this in the wrong order.
# $self->insert($v, $$result[-1]->first_leaf->[1], $$result[-1]);
# The new node will always be the last node, so we need to insert the
# key/pointer in reverse order from normal such that the key happens at
# $i and the value is at $i + 1
my $i = $self->find($cmp, $key);
splice @$self, $i * 2 + 1, 0, $$result[-1]->first_leaf->[1], $$result[-1];
return undef;
} else {
( run in 2.203 seconds using v1.01-cache-2.11-cpan-71847e10f99 )