File Coverage

blib/lib/DataStructure/BTree.pm
Criterion Covered Total %
statement 27 124 21.7
branch 0 34 0.0
condition 0 18 0.0
subroutine 9 18 50.0
pod 0 7 0.0
total 36 201 17.9


line stmt bran cond sub pod time code
1             # A binary tree data-structure.
2             #
3             # Mostly from the Cormen-Leiserson-Rivest book.
4              
5             package DataStructure::BTree;
6              
7 1     1   216771 use strict;
  1         4  
  1         28  
8 1     1   6 use warnings;
  1         3  
  1         22  
9 1     1   5 use utf8;
  1         3  
  1         8  
10 1     1   24 use feature ':5.24';
  1         3  
  1         193  
11 1     1   7 use feature 'signatures';
  1         3  
  1         32  
12 1     1   6 no warnings 'experimental::signatures';
  1         2  
  1         44  
13              
14 1     1   437 use DataStructure::BTree::Node;
  1         2  
  1         44  
15              
16 1     1   523 use parent qw(DataStructure::OrderedSet);
  1         387  
  1         6  
17              
18             =pod
19              
20             =head1 NAME
21              
22             DataStructure::BTree
23              
24             =head1 SYNOPSIS
25              
26             A binary tree data-structure, written in pure Perl.
27              
28             =head1 DESCRIPTION
29              
30             =head2 CONSTRUCTOR
31              
32             C<< DataStructure::BTree->new(%options) >>
33              
34             Creates an empty binary tree.
35              
36             Available options are:
37              
38             =over 4
39              
40             =item cmp
41              
42             The comparator used for the elements of the tree. Should be a reference to a
43             sub using C<$a> and C<$b>. Defaults to using C if not specified.
44              
45             =item multiset
46              
47             Whether several identical values can be stored.
48              
49             =back
50              
51             =cut
52              
53 0     0 0   sub new ($class, %options) {
  0            
  0            
  0            
54 0   0       my $calling_pkg = caller($options{_context_skip} // 0);
55 1     1   92 no strict 'refs';
  1         2  
  1         1146  
56             return bless {
57             size => 0,
58             root => undef,
59 0     0     compare => $options{cmp} // sub { $a cmp $b},
60             multi => $options{multiset} // 0,
61 0           a => \*{ $calling_pkg . '::a' },
62 0   0       b => \*{ $calling_pkg . '::b' },
  0   0        
63             }, $class;
64             }
65              
66             # Returns a node with the given value or undef.
67 0     0 0   sub find ($self, $value) {
  0            
  0            
  0            
68 0           my $current = $self->{root};
69 0           local *{$self->{a}} = $value;
  0            
70 0   0       while (defined $current && $current->{value} != $value) {
71 0           local *{$self->{b}} = $current->{value};
  0            
72 0 0         if ($self->{compare}->() < 0) {
73 0           $current = $current->{left};
74             } else {
75 0           $current = $current->{right};
76             }
77             }
78 0           return $current;
79             }
80              
81             # Returns the newly inserted node or undef if the value is already present and
82             # multiset was not passed in the options.
83 0     0 0   sub insert ($self, $value, $hint = undef) {
  0            
  0            
  0            
  0            
84 0           my $new_node = DataStructure::BTree::Node->new($self, $value);
85 0           my $current = $self->{root};
86 0           my $parent = undef;
87 0           local *{$self->{a}} = $value;
  0            
88 0           my $c;
89 0           while (defined $current) {
90 0           $parent = $current;
91 0           local *{$self->{b}} = $current->{value};
  0            
92 0           $c = $self->{compare}->();
93 0 0 0       if ($c < 0) {
    0          
94 0           $current = $current->{left};
95             } elsif ($c >0 || $self->{multi}) {
96 0           $current = $current->{right};
97             } else {
98 0           return;
99             }
100             }
101 0           $self->{size}++;
102 0           $new_node->{parent} = $parent;
103 0 0         if (!defined $parent) {
    0          
104 0           $self->{root} = $new_node;
105             } elsif ($c < 0) {
106 0           $parent->{left} = $new_node;
107             } else {
108 0           $parent->{right} = $new_node;
109             }
110 0           return $new_node;
111             }
112              
113             # Deletes the given node or one node with that value.
114             # Returns a true value on success and undef if the value is not found.
115             # Invalidates all node of the tree.
116 0     0 0   sub delete ($self, $value_or_node) {
  0            
  0            
  0            
117 0           my $node;
118 0 0 0       if (ref($value_or_node) && $value_or_node->isa('DataStructure::BTree::Node')) {
119 0           $node = $value_or_node;
120 0 0         return unless $self == $node->{tree};
121             } else {
122 0           $node = $self->find($value_or_node);
123 0 0         return unless defined $node;
124             }
125 0           my $replacement;
126 0 0 0       if (defined $node->{left} && defined $node->{right}) {
127 0           $replacement = $node->_succ(); # cannot be null because we have a right child.
128             } else {
129 0           $replacement = $node;
130             }
131 0           my $new_child;
132 0 0         if (defined $replacement->{left}) {
133 0           $new_child = $replacement->{left};
134             } else {
135 0           $new_child = $replacement->{right};
136             }
137 0 0         $new_child->{parent} = $replacement->{parent} if defined $new_child;
138 0           my $parent = $replacement->{parent};
139 0 0         if ($parent) {
140 0 0         if ($replacement == $parent->{left}) {
141 0           $parent->{left} = $new_child;
142             } else {
143 0           $parent->{right} = $new_child;
144             }
145             } else {
146 0           $self->{root} = $new_child;
147             }
148 0 0         $node->{value} = $replacement->{value} if $node != $replacement;
149 0           $self->{size}--;
150 0           return 1;
151             }
152              
153             # Returns the smallest node in the tree
154 0     0 0   sub min ($self) {
  0            
  0            
155 0 0         return unless defined $self->{root};
156 0           return $self->{root}->_min_child();
157             }
158              
159             # Returns the biggest node in the tree
160 0     0 0   sub max ($self) {
  0            
  0            
161 0 0         return unless defined $self->{root};
162 0           return $self->{root}->_max_child();
163             }
164              
165 0     0     sub _values ($self) {
  0            
  0            
166 0     0 0   sub node_value ($node) {
  0            
  0            
167 0 0         return (undef) unless defined $node;
168 0           return [$node->value(), $node->left(), $node->right()];
169             }
170 0           return node_value($self->{root});
171             }
172              
173             1;