File Coverage

blib/lib/Tree/Range/base.pm
Criterion Covered Total %
statement 92 92 100.0
branch 59 74 79.7
condition 9 15 60.0
subroutine 13 13 100.0
pod 4 8 50.0
total 177 202 87.6


line stmt bran cond sub pod time code
1             ### base.pm --- Tree::Range::base: base class for the range trees -*- Perl -*-
2              
3             ### Copyright (C) 2013 Ivan Shmakov
4              
5             ## Permission to copy this software, to modify it, to redistribute it,
6             ## to distribute modified versions, and to use it for any purpose is
7             ## granted, subject to the following restrictions and understandings.
8              
9             ## 1. Any copy made of this software must include this copyright notice
10             ## in full.
11              
12             ## 2. I have made no warranty or representation that the operation of
13             ## this software will be error-free, and I am under no obligation to
14             ## provide any services, by way of maintenance, update, or otherwise.
15              
16             ## 3. In conjunction with products arising from the use of this
17             ## material, there shall be no use of my name in any advertising,
18             ## promotional, or sales literature without prior written consent in
19             ## each case.
20              
21             ### Code:
22              
23             package Tree::Range::base;
24              
25 7     7   45 use strict;
  7         13  
  7         464  
26              
27             our $VERSION = 0.22;
28              
29             require Carp;
30              
31 7     7   45 use Scalar::Util qw (refaddr);
  7         16  
  7         12876  
32              
33             sub safe_eq {
34             ## return true if either both are undef, or the same ref
35 109     109 0 179 my ($a, $b) = @_;
36             ## .
37 109 50 66     734 return (defined ($a)
38             ? (ref ($a) && ref ($b)
39             && refaddr ($a) == refaddr ($b))
40             : ! defined ($b));
41             }
42              
43             sub del_range {
44 53     53 0 100 my ($obj, $left, $cmp, $high) = @_;
45 53         70 my ($last_ref, @delk);
46 53         171 for (my $e = $left;
47             defined ($e);
48             $e = $e->successor ()) {
49 122         1148 my $c
50             = &$cmp ($e->key (), $high);
51             last
52 122 100       919 if ($c > 0);
53             # print STDERR ("-g: ", scalar (Data::Dump::dump ({ $e->key () => $e->val () })), "\n");
54 88         209 $last_ref
55             = [ $e->key, $e->val () ];
56             last
57 88 100       865 if ($c >= 0);
58             ## FIXME: shouldn't there be a better way?
59 72         172 push (@delk, $e->key ());
60             }
61             # print STDERR ("-g: ", scalar (Data::Dump::dump (\@delk)), "\n");
62 53         144 foreach my $k (@delk) {
63 72 50       6097 $obj->delete ($k)
64             or Carp::croak ($k, ": failed to delete key");
65             }
66             ## .
67 53         3466 return $last_ref;
68             }
69              
70             sub get_range {
71 544     544 1 15583 my ($self, $key) = @_;
72 544         1423 my $left
73             = $self->lookup_leq ($key);
74 544 100       1766 my $v
75             = (defined ($left)
76             ? $left->val ()
77             : $self->leftmost_value ());
78             ## .
79 544 100       4325 return $v
80             unless (wantarray ());
81 15 100       38 unless (defined ($left)) {
82 5         51 my $right
83             = $self->lookup_geq ($key);
84             ## .
85 5 100       50 return (defined ($right)
86             ? ($v, undef, $right->key ())
87             : ($v));
88             }
89 10         29 my ($l_k, $right)
90             = ($left->key (), $left->successor ());
91             ## .
92 10 100       154 return (defined ($right)
93             ? ($v, $l_k, $right->key ())
94             : ($v, $l_k));
95             }
96              
97             sub range_free_p {
98 8     8 1 5568 my ($self, $lower, $upper) = @_;
99 8         22 my $cmp
100             = $self->cmp_fn ();
101 8 50       29 Carp::croak ("Upper bound (", $upper,
102             ") must be greater than the lower (", $lower,
103             ") one")
104             unless (&$cmp ($upper, $lower) > 0);
105              
106 8         49 my $right
107             = $self->lookup_leq ($upper);
108             ## .
109 8 100       35 return 1
110             unless (defined ($right));
111             ## FIXME: a crude ->lookup_lt ()
112 4 100       13 $right
113             = $right->predecessor ()
114             if ($cmp->($upper, $right->key ()) == 0);
115             ## .
116 4 100       70 return 1
117             unless (defined ($right));
118              
119 3         10 my ($r, $lm, $eq_u)
120             = ($right->val (),
121             $self->leftmost_value (),
122             $self->value_equal_p_fn ());
123             ## .
124 3 100 66     9 return (! 1)
125             unless (safe_eq ($r, $lm)
126             || $eq_u->($r, $lm));
127              
128             ## by now, we know that $upper is mapped to $lm
129             ## check if $lower is covered by the same range
130             ## .
131 1         4 return ($cmp->($right->key (), $lower) <= 0);
132             }
133              
134             sub prepare_range_iter_asc {
135 2     2 0 4 my ($self, $fn_ref, $may_be_key) = @_;
136              
137 2         4 my $cur;
138             my $fn = sub {
139             ## .
140             return
141 10 100   10   30 unless (defined ($cur));
142 8         26 my $next
143             = $cur->successor ();
144             my @r
145 8 100       91 = (wantarray ()
    50          
146             ? ($cur->val (), $cur->key (),
147             (defined ($next) ? ($next->key ()) : ()))
148             : ($cur->val ()));
149 8         95 $cur
150             = $next;
151             ## .
152 8         40 @r;
153 2         10 };
154              
155 2 100       6 if (defined ($may_be_key)) {
156 1         17 ($$fn_ref, $cur)
157             = ($fn, $self->lookup_leq ($may_be_key));
158             ## .
159 1         4 return $fn->();
160             } else {
161 1         5 my $n
162             = $self->min_node ();
163 1         14 ($$fn_ref, $cur)
164             = ($fn, $n);
165             ## .
166             return (wantarray ()
167 1 50       13 ? ($self->leftmost_value (),
    50          
168             undef,
169             (defined ($n) ? ($n->key ()) : ()))
170             : $self->leftmost_value ());
171             }
172             }
173              
174             sub prepare_range_iter_dsc {
175 2     2 0 4 my ($self, $fn_ref, $may_be_key) = @_;
176              
177 2         13 my $cur;
178             my $fn = sub {
179             ## .
180             return
181 11 100   11   33 unless (defined ($cur));
182 9         27 my $prev
183             = $cur->predecessor ();
184             my @r
185 9 100       145 = (wantarray ()
    0          
    50          
186             ? ((defined ($prev)
187             ? ($prev->val (), $prev->key ())
188             : ($self->leftmost_value (), undef)),
189             $cur->key ())
190             : (defined ($prev)
191             ? $prev->val ()
192             : $self->leftmost_value ()));
193 9         89 $cur
194             = $prev;
195             ## .
196 9         47 @r;
197 2         11 };
198              
199 2 100       5 if (defined ($may_be_key)) {
200 1         7 my $n
201             = $self->lookup_geq ($may_be_key);
202             ## FIXME: a crude ->lookup_gt ()
203 1 50 33     11 $n
204             = $n->successor ()
205             if (defined ($n)
206             && ($self->cmp_fn ()->($may_be_key,
207             $n->key ()) == 0));
208 1         22 ($$fn_ref, $cur)
209             = ($fn, $n);
210             ## .
211 1         4 return $fn->();
212             } else {
213 1         6 my $n
214             = $self->max_node ();
215 1         15 ($$fn_ref, $cur)
216             = ($fn, $n);
217             ## .
218             return (wantarray ()
219 1 50       7 ? (defined ($n)
    0          
    50          
220             ? ($n->val (), $n->key ())
221             : ($self->leftmost_value ()))
222             : (defined ($n)
223             ? $n->val ()
224             : $self->leftmost_value ()));
225             }
226             }
227              
228             sub range_iter_closure {
229 4     4 1 9 my ($self, $may_be_key, $may_be_reverse_p) = @_;
230              
231 4         4 my $fn
232             = undef;
233              
234             ## .
235             sub {
236             ## .
237 23 100   23   15709 return (defined ($fn)
    100          
238             ? $fn->()
239             : ($may_be_reverse_p
240             ? $self->prepare_range_iter_dsc (\$fn, $may_be_key)
241             : $self->prepare_range_iter_asc (\$fn, $may_be_key)));
242             }
243 4         22 }
244              
245             sub range_set {
246 58     58 1 47196 my ($self, $low, $high, $value) = @_;
247 58         186 my $cmp
248             = $self->cmp_fn ();
249 58 50       164 Carp::croak ("Upper bound (", $high,
250             ") must be greater than the lower (", $low,
251             ") one")
252             unless (&$cmp ($high, $low) > 0);
253              
254             ## | min | | | max |
255             ## .. Left a A b B c C d D e E ..
256              
257 58         374 my $left
258             = $self->lookup_geq ($low);
259 58 100       155 if (! defined ($left)) {
260             ## $low, and thus $high, are higher than max
261             # print STDERR ("-g: ", scalar (Data::Dump::dump ({ $low => $value, $high => $self->leftmost_value () })), "\n");
262 5         28 $self->put ($low, $value);
263 5         301 $self->put ($high, $self->leftmost_value ());
264             } else {
265             ## preserve the value, if any
266 53         165 my $pre
267             = $left->predecessor ();
268 53 100       687 my $pre_v
269             = (defined ($pre)
270             ? $pre->val ()
271             : $self->leftmost_value ());
272             ## remove everything up to the boundary at $high
273 53         362 my $last_ref
274             = del_range ($self, $left, $cmp, $high);
275 53 100       429 my $last
276             = (defined ($last_ref)
277             ? $last_ref->[1]
278             : $pre_v);
279             ## there either already is a boundary at $low,
280             ## or we add it now
281 53         186 my $eq_u
282             = $self->value_equal_p_fn ();
283 53   66     128 my $eq_l
284             = (safe_eq ($value, $pre_v) || $eq_u->($value, $pre_v));
285 53   66     238 my $eq_h
286             = (safe_eq ($value, $last) || $eq_u->($value, $last));
287             # print STDERR ("-g: ", scalar (Data::Dump::dump ({ (! $eq_l ? ($low => $value) : ()), (! $eq_h ? ($high => $last) : ()) })), "\n");
288 53 100       210 if (! $eq_l) {
289 52         156 $self->put ($low, $value);
290             } else {
291             ## merge the segments
292 1         5 $self->delete ($low);
293             }
294 53 100       3743 if (! $eq_h) {
295 52         153 $self->put ($high, $last);
296             } else {
297             ## merge the segments
298 1         4 $self->delete ($high);
299             }
300             }
301              
302             ## .
303             }
304              
305             *range_set_over
306             = \&range_set;
307              
308             1;
309              
310             ### Emacs trailer
311             ## Local variables:
312             ## coding: us-ascii
313             ## fill-column: 72
314             ## indent-tabs-mode: nil
315             ## ispell-local-dictionary: "american"
316             ## End:
317             ### base.pm ends here