File Coverage

blib/lib/Algorithm/SkipList.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Algorithm::SkipList - Perl implementation of skip lists
4              
5             =begin readme
6              
7             =head1 REQUIREMENTS
8              
9             The following non-core modules are used:
10              
11             Tree::Node
12              
13             =head1 INSTALLATION
14              
15             Installation can be done using the traditional F or the
16             newer F method .
17              
18             Using Makefile.PL:
19              
20             perl Makefile.PL
21             make test
22             make install
23              
24             (On Windows platforms you should use F instead.)
25              
26             Using Build.PL (if you have L installed):
27              
28             perl Build.PL
29             perl Build test
30             perl Build install
31              
32             =end readme
33              
34             =head1 SYNOPSIS
35              
36             my $list = new Algorithm::SkipList();
37              
38             $list->insert( 'key1', 'value' );
39             $list->insert( 'key2', 'another value' );
40              
41             $value = $list->find('key2');
42              
43             $list->delete('key1');
44              
45             =head1 DESCRIPTION
46              
47             This is an implementation of skip lists in Perl.
48              
49             Skip lists are an alternative to balanced trees. They are ordered
50             linked lists with random links at various I that allow
51             searches to skip over sections of the list, like so:
52              
53             4 +---------------------------> +----------------------> +
54             | | |
55             3 +------------> +------------> +-------> +-------> +--> +
56             | | | | | |
57             2 +-------> +--> +-------> +--> +--> +--> +-------> +--> +
58             | | | | | | | | |
59             1 +--> +--> +--> +--> +--> +--> +--> +--> +--> +--> +--> +
60             A B C D E F G H I J NIL
61              
62             A search would start at the top level: if the link to the right
63             exceeds the target key, then it descends a level.
64              
65             Skip lists generally perform as well as balanced trees for searching
66             but do not have the overhead with respect to reblanacing the structure.
67             And on average, they use less memory than trees.
68              
69             They also use less memory than hashes, and so are appropriate for
70             large collections.
71              
72             =for readme stop
73              
74             For more information on skip lists, see the L section below.
75              
76             =head2 METHODS
77              
78             =cut
79              
80             package Algorithm::SkipList;
81              
82 5     5   72785 use 5.006;
  5         20  
  5         198  
83 5     5   28 use strict;
  5         12  
  5         188  
84 5     5   50 use warnings::register __PACKAGE__;
  5         7  
  5         1454  
85              
86             our $VERSION = '2.00_02';
87             $VERSION = eval $VERSION;
88              
89 5     5   5959 use self;
  0            
  0            
90              
91             use Carp qw( carp croak );
92             use Tree::Node;
93             use Algorithm::SkipList::Header;
94              
95             use constant MAX_LEVEL => 31;
96             use constant MIN_LEVEL => 1;
97              
98             use constant DEFAULT_P => 0.25;
99             use constant DEFAULT_K => 0;
100              
101             # We could use Algorithm::SkipList::Node, which exists as a stub. But
102             # there's just no point to it.
103              
104             use constant DEFAULT_NODE_CLASS => 'Tree::Node';
105              
106             # This is an internal routine which defines valid configuration options and
107             # their default values. It is meant to be called by the BEGIN block (see
108             # source code below) that creates access methods for these options.
109              
110             my %CONFIG_OPTIONS;
111              
112             sub _set_config_options {
113             %CONFIG_OPTIONS = (
114             p => DEFAULT_P,
115             k => DEFAULT_K,
116             min_level => MIN_LEVEL,
117             max_level => MAX_LEVEL,
118             node_class => DEFAULT_NODE_CLASS,
119             allow_duplicates => 0,
120             );
121             }
122              
123             =over
124              
125             =item new
126              
127             $list = new Algorithm::SkipList();
128              
129             Creates a new skip list.
130              
131             If you need to use a different L for using
132             customized L routines, you will need to specify a
133             different class:
134              
135             $list = new Algorithm::SkipList( node_class => 'MyNodeClass' );
136              
137             See the L section below.
138              
139             Specialized internal parameters may be configured:
140              
141             $list = new Algorithm::SkipList( max_level => 31 );
142              
143             Defines a different maximum list level (the default is 31).
144              
145             The initial list (see the L method) will start out at one
146             level, and will increase as the size of the list doubles, up intil
147             it reaches the maximum level.
148              
149             The default minimum level can be changed:
150              
151             $list = new Algorithm::SkipList( min_level => 4 );
152              
153             You can also control the probability used to determine level sizes for
154             each node by setting the L and k values:
155              
156             $list = new Algorithm::SkipList( p => 0.25, k => 1 );
157              
158             See L

for more information on this parameter.
159              
160             You can enable duplicate keys by using the following:
161              
162             $list = new Algorithm::SkipList( allow_duplicates => 1 );
163              
164             This is an experimental feature. See the L section
165             below.
166              
167             =cut
168              
169             sub new {
170             my $class = shift || __PACKAGE__;
171             my $self = {
172             p => DEFAULT_P,
173             k => DEFAULT_K,
174             p_levels => [ ], # array used by random_level
175             };
176              
177             bless $self, $class;
178              
179             # Set default values
180              
181             foreach my $field (CORE::keys %CONFIG_OPTIONS) {
182             my $method = "_set_" . $field;
183             $self->$method( $CONFIG_OPTIONS{$field} );
184             }
185              
186             # Update user-settings
187              
188             if (@_) {
189             while (my $arg = shift) {
190             if (exists $CONFIG_OPTIONS{$arg}) {
191             my $value = shift;
192             my $method = "_set_" . $arg;
193             $self->$method($value);
194             }
195             else {
196              
197             croak "Unknown option: \'$arg\'";
198             }
199             }
200             }
201              
202             # Additional user-settings checks, since the practical way to check is
203             # after all of the settings have been changed.
204              
205             if ($self->min_level > $self->max_level) {
206             croak "min_level > max_level";
207             }
208              
209             $self->clear;
210              
211             return $self;
212             }
213              
214             =item list
215              
216             $node = $list->list;
217              
218             Returns the initial node in the list. Accessing accessors other than
219             C and C will trigger an error.
220              
221             This method is meant for internal use.
222              
223             =cut
224              
225             sub list {
226             return $self->{list};
227             }
228              
229             =item level
230              
231             $level = $list->level;
232              
233             Returns the current maximum level (number of forward pointers) that
234             any node can have. It will not be larger than L, nor
235             does it correspond to the number of nodes in L.
236              
237             This method is meant for internal use.
238              
239             =cut
240              
241             sub level {
242             return $self->{level};
243             }
244              
245             =item clear
246              
247             $list->clear;
248              
249             Erases existing nodes and resets the list.
250              
251             =cut
252              
253             sub clear {
254             $self->{list} = Algorithm::SkipList::Header->new(MAX_LEVEL);
255             $self->{level} = MIN_LEVEL;
256              
257             if ($self->{max_level} > $self->{list}->child_count) {
258             $self->{max_level} = $self->{list}->child_count;
259             carp sprintf('max_level downgraded to %d due to limits of list header',
260             $self->{list}->child_count) if (warnings::enabled);
261             }
262              
263             $self->{size} = 0;
264              
265             $self->{size_threshold} = 2**($self->{level});
266             $self->{last_size_threshold} = $self->{size};
267              
268             $self->{last_node} = undef;
269              
270             $self->_reset_iterator;
271             }
272              
273             =begin internal
274              
275             =item _search
276              
277             ($node, $cmp) = $list->_search( $key );
278              
279             ($node, $cmp) = $list->_search( $key, $finger );
280              
281             Same as L, only that no search finger is returned.
282              
283             This is useful for searches where a finger is not needed. The speed
284             of searching is improved.
285              
286             Note that as of version 2.00, the order of return values has been
287             changed.
288              
289             =end internal
290              
291             =cut
292              
293             sub _search {
294             my ($key, $finger) = @args;
295              
296             use integer;
297              
298             my $list = $self->list;
299             my $level = $self->level-1;
300              
301             my $node = $finger->[ $level ] || $list;
302              
303             my $fwd;
304             my $cmp = -1;
305              
306             do {
307             while ( ($fwd = $node->get_child($level)) &&
308             ($cmp = $fwd->key_cmp($key)) < 0) {
309             $node = $fwd;
310             }
311             } while ((--$level>=0) && $cmp);
312              
313             $node = $fwd, unless ($cmp);
314              
315             return ($node, $cmp);
316             }
317              
318             =begin internal
319              
320             =item _search_with_finger
321              
322             ($node, $cmp, $finger) = $list->_search_with_finger( $key );
323              
324             Searches for the node with a key. If the key is found, that node is
325             returned along with a L. If the key is not found, the previous
326             node from where the node would be if it existed is returned.
327              
328             Note that the value of C<$cmp>
329              
330             $cmp = $node->key_cmp( $key )
331              
332             is returned because it is already determined by L.
333              
334             Search fingers may also be specified:
335              
336             ($node, $cmp, $finger) = $list->_search_with_finger( $key, $finger );
337              
338             See the section L below.
339              
340             Note that as of version 2.00, the order of return values has been
341             changed.
342              
343             =end internal
344              
345             =cut
346              
347             sub _search_with_finger {
348             my ($key, $finger) = @args;
349              
350             use integer;
351              
352             my $list = $self->list;
353             my $level = $self->level-1;
354              
355             my $node = $finger->[ $level ] || $list;
356              
357             my $fwd;
358             my $cmp = -1;
359              
360             do {
361             while ( ($fwd = $node->get_child($level)) &&
362             ($cmp = $fwd->key_cmp($key)) < 0) {
363             $node = $fwd;
364             }
365             $finger->[$level] = $node;
366             } while (--$level>=0);
367              
368             # Ideally we could stop when $cmp == 0, but the update vector would
369             # not be complete for levels below $level.
370              
371             $node = $fwd, unless ($cmp);
372              
373             return ($node, $cmp, $finger);
374             }
375              
376             =item exists
377              
378             if ($list->exists( $key )) { ... }
379              
380             Returns true if there exists a node associated with the key, false
381             otherwise.
382              
383             This may also be used with L:
384              
385             if ($list->exists( $key, $finger )) { ... }
386              
387             =cut
388              
389             sub exists {
390             # my ($key, $finger) = @args;
391             (($self->_search(@args))[1] == 0);
392             }
393              
394             =item find_with_finger
395              
396             $value = $list->find_with_finger( $key );
397              
398             Searches for the node associated with the key, and returns the value. If
399             the key cannot be found, returns C.
400              
401             L may also be used:
402              
403             $value = $list->find_with_finger( $key, $finger );
404              
405             To obtain the search finger for a key, call L in a
406             list context:
407              
408             ($value, $finger) = $list->find_with_finger( $key );
409              
410             =cut
411              
412             sub find_with_finger {
413             ##my ($key, $finger) = @args;
414             my ($node, $cmp);
415             ($node, $cmp, $args[1]) = $self->_search_with_finger(@args);
416              
417             if ($cmp) {
418             return;
419             } else {
420             return (wantarray) ? ($node->value, $args[1]) : $node->value;
421             }
422             }
423              
424             =item find
425              
426             $value = $list->find( $key );
427              
428             $value = $list->find( $key, $finger );
429              
430             Searches for the node associated with the key, and returns the value. If
431             the key cannot be found, returns C.
432              
433             This method is slightly faster than L since it does
434             not return a search finger when called in list context.
435              
436             If you are searching for duplicate keys, you must use
437             L or L.
438              
439             =cut
440              
441             sub find {
442             ## my ($key, $finger) = @args;
443             my ($node, $cmp) = $self->_search(@args);
444              
445             if ($cmp) {
446             return;
447             } else {
448             return $node->value;
449             }
450             }
451              
452             =item insert
453              
454             $list->insert( $key, $value );
455              
456             Inserts a new node into the list.
457              
458             Only alphanumeric keys are supported "out of the box". To use numeric
459             or other types of keys, see L below.
460              
461             You may also use a L with insert,
462             provided that the finger is for a key that occurs earlier in the list:
463              
464             $list->insert( $key, $value, $finger );
465              
466             Using fingers for inserts is I recommended since there is a risk
467             of producing corrupted lists.
468              
469             =cut
470              
471             sub insert {
472             my ($key, $value, $finger) = @args;
473              
474             use integer;
475              
476             # TODO: Track last node inserted and use it's update vector if the
477             # key to be inserted is greater.
478              
479             my ($node, $cmp);
480             ($node, $cmp, $finger) = $self->_search_with_finger($key, $finger);
481              
482             if ($cmp || $self->{allow_duplicates}) {
483              
484             my $level = $self->_new_node_level;
485             $node = $self->node_class->new($level);
486             $node->set_key($key);
487             $node->set_value($value);
488              
489             my $list = $self->list;
490             for(my $i=0; $i<$level; $i++) {
491             $node->set_child($i, ($finger->[$i]||$list)->get_child($i));
492             ($finger->[$i]||$list)->set_child($i, $node);
493             }
494              
495             $self->{size}++;
496             $self->_adjust_level_threshold;
497              
498             # Tracking the last node in the list. We cannot save the finger
499             # since something could be inserted between $finger->[0] and the
500             # last_node.
501              
502             $self->{last_node} = $node
503             unless ($node->get_child(0));
504              
505             }
506             else {
507             $node->set_value($value);
508             }
509             }
510              
511             =item delete
512              
513             $value = $list->delete( $key );
514              
515             Deletes the node associated with the key, and returns the value. If
516             the key cannot be found, returns C.
517              
518             L may also be used:
519              
520             $value = $list->delete( $key, $finger );
521              
522             Calling L in a list context I return a search
523             finger.
524              
525             =cut
526              
527             sub delete {
528             my ($key, $finger) = @args;
529              
530             use integer;
531              
532             my ($node, $cmp);
533             ($node, $cmp, $finger) = $self->_search_with_finger(@args);
534              
535             if ($cmp) {
536             return;
537             }
538             else {
539             my $list = $self->list;
540             for(my $i=0; $i<$node->child_count; $i++) {
541             ($finger->[$i]||$list)->set_child($i, $node->get_child($i));
542             }
543             $self->{size}--;
544              
545             # It is only practical to adjust the level during inserts. If we
546             # do this during deletes, we run into some problems.
547              
548             # $self->_adjust_level_threshold;
549              
550             $self->{last_node} = $finger->[0]
551             unless ($node->get_child(0));
552              
553             return $node->value;
554             }
555             }
556              
557             =begin internal
558              
559             $list->_build_distribution;
560              
561             This is an internal routine to update the probabilities for each node
562             level. It is meant to be called each time L

or L are updated.
563              
564             =end internal
565              
566             =cut
567              
568             sub _build_distribution {
569             no integer;
570              
571             my $p = $self->p;
572             my $k = $self->k;
573              
574             $self->{p_levels} = [ (0) x MAX_LEVEL ];
575             for my $i (0..MAX_LEVEL) {
576             $self->{p_levels}->[$i] = $p**($i+$k);
577             }
578             }
579              
580             sub _set_p {
581             no integer;
582              
583             my ($p) = @args;
584              
585             unless ( ($p>0) && ($p<1) ) {
586             croak "Invalid value for P (must be between 0 and 1)";
587             }
588              
589             $self->{p} = $p;
590             $self->_build_distribution;
591             }
592              
593             sub _set_k {
594              
595             my ($k) = @args;
596              
597             unless ( $k>=0 ) {
598             croak "Invalid value for K (must be at least 0)";
599             }
600              
601             $self->{k} = $k;
602             $self->_build_distribution;
603             }
604              
605             sub _set_min_level {
606             my ($min_level) = @args;
607              
608             if ($self->size) {
609             croak "min_level can only be set on an empty skip list";
610             }
611              
612             if ( ($min_level < MIN_LEVEL) || ($min_level > MAX_LEVEL) ) {
613             croak sprintf("Invalid value for min_level (must be between %d and %d)",
614             MIN_LEVEL, MAX_LEVEL);
615             }
616              
617             $self->{min_level} = $min_level;
618             }
619              
620             sub _set_max_level {
621             my ($max_level) = @args;
622              
623             # We want to make sure that the user-supplied does not exceed the
624             # maximum level of the list node (even though we specify that the
625             # list node has MAX_LEVEL by default).
626              
627             my $max = MAX_LEVEL;
628             if ((defined $self->list) && ($self->list->child_count < $max)) {
629             $max = $self->list->child_count;
630             }
631             my $min = $self->min_level || MIN_LEVEL;
632              
633             if ( ($max_level < $min) || ($max_level > $max) ) {
634             croak sprintf("Invalid value for max_level (must be between %d and %d)",
635             $min, $max);
636             }
637             $self->{max_level} = $max_level;
638             }
639              
640             sub _adjust_level_threshold {
641             use integer;
642              
643             if ($self->{size} == $self->{size_threshold}) {
644             # $self->{last_size_threshold} = $self->{size_threshold};
645             $self->{size_threshold} += $self->{size_threshold};
646             $self->{level}++,
647             if ($self->{level} < $self->{max_level});
648             }
649              
650             # elsif ($self->{size} < $self->{last_size_threshold}) {
651             # $self->{size_threshold} = $self->{last_size_threshold};
652             # $self->{last_size_threshold} = $self->{last_size_threshold} / 2;
653             #
654             # # We cannot practically decrease the level without readjusting the
655             # # levels of all the nodes globally, which isn't worthwhile.
656             #
657             # # $self->{level}--,
658             # # if ($self->{level} > MIN_LEVEL);
659             # }
660             }
661              
662             sub _new_node_level {
663             no integer;
664              
665             my $n = rand();
666             my $level = 1;
667              
668             while (($n < $self->{p_levels}->[$level]) &&
669             ($level++ < $self->{level})) {
670             }
671              
672             return $level;
673             }
674              
675             sub _set_node_class {
676             my ($node_class) = @args;
677             unless ($node_class->isa( DEFAULT_NODE_CLASS )) {
678             croak "$node_class is not a " . DEFAULT_NODE_CLASS;
679             }
680             $self->{node_class} = $node_class;
681             }
682              
683             =item size
684              
685             $size = $list->size;
686              
687             Returns the number of nodes in the list.
688              
689             =cut
690              
691             sub size {
692             return $self->{size};
693             }
694              
695             =item reset
696              
697             $list->reset;
698              
699             Resets the iterator used by L and L.
700              
701             =begin internal
702              
703             =item _reset_iterator
704              
705             This is the internal alias for L.
706              
707             =end internal
708              
709             =cut
710              
711             sub _reset_iterator {
712             $self->{iterator} = undef;
713             }
714              
715             sub _set_iterator_by_key {
716             ## my ($key, $finger) = @args;
717             my ($node, $cmp) = $self->_search(@args);
718             if ($cmp) {
719             carp "key \'$args[0]\' not found" if (warnings::enabled);
720             return $self->_reset_iterator;
721             } else {
722             return $self->{iterator} = $node;
723             }
724             }
725              
726             sub _first_node {
727             $self->_reset_iterator;
728             $self->_next_node;
729             }
730              
731             sub _next_node {
732             $self->{iterator} = ($self->{iterator} || $self->list)->get_child(0);
733             }
734              
735             sub _last_node {
736             return $self->{last_node};
737             }
738              
739             =item first_key
740              
741             $key = $list->first_key;
742              
743             Returns the first key in the list. Implicitly calls the iterator L
744             method.
745              
746             =cut
747              
748             sub first_key {
749             my $node = $self->_first_node;
750             return $node->key;
751             }
752              
753             =item next_key
754              
755             $key = $list->next_key;
756              
757             Returns the next key in the series.
758              
759             $key = $list->next_key($last_key);
760              
761             Returns the key that follows the C<$last_key>.
762              
763             $key = $list->next_key($last_key, $finger);
764              
765             Same as above, using the C<$finger> to search for the key.
766              
767             =cut
768              
769             sub next_key {
770             my ($last_key, $finger) = @args;
771             if (defined $last_key) {
772             $self->_set_iterator_by_key($last_key, $finger);
773             }
774              
775             my $node = $self->_next_node;
776             return $node->key if ($node);
777             return;
778             }
779              
780              
781             sub _error {
782             croak "Method unimplemented";
783             }
784              
785             BEGIN {
786             *TIEHASH = \&new;
787             *STORE = \&insert;
788             *FETCH = \&find;
789             *EXISTS = \&exists;
790             *CLEAR = \&clear;
791             *DELETE = \&delete;
792             *FIRSTKEY = \&first_key;
793             *NEXTKEY = \&next_key;
794              
795             *reset = \&_reset_iterator;
796             *search = \&find;
797              
798             *merge = \&_error;
799             *find_duplicates = \&_error;
800             *_node_by_index = \&_error;
801             *key_by_index = \&_error;
802             *index_by_key = \&_error;
803             *value_by_index = \&_error;
804              
805             *_prev = \&_error;
806             *_prev_key = \&_error;
807              
808             _set_config_options();
809             foreach my $field (CORE::keys %CONFIG_OPTIONS) {
810             my $set_method = "_set_" . $field;
811             no strict 'refs';
812             *$field = sub {
813             my $self = shift;
814             if (@_) {
815             $self->$set_method($field);
816             } else {
817             return $self->{$field};
818             }
819             };
820             unless (__PACKAGE__->can($set_method)) {
821             *$set_method = sub {
822             my $self = shift;
823             $self->{$field} = shift;
824             };
825             }
826             }
827             }
828              
829             1;
830              
831             # __END__
832              
833             =item least
834              
835             ($key, $value) = $list->least;
836              
837             Returns the least key and value in the list, or C if the list
838             is empty.
839              
840             =cut
841              
842             sub least {
843             my $node = $self->_first_node || return;
844             return ($node->key, $node->value);
845             }
846              
847             =item greatest
848              
849             ($key, $value) = $list->greatest;
850              
851             Returns the greatest key and value in the list, or C if the list
852             is empty.
853              
854             =cut
855              
856             sub greatest {
857             my $node = $self->_last_node || return;
858             return ($node->key, $node->value);
859              
860             }
861              
862             =item next
863              
864             ($key, $value) = $list->next( $last_key, $finger );
865              
866             Returns the next key-value pair.
867              
868             C<$last_key> and C<$finger> are optional.
869              
870             =cut
871              
872             sub next {
873             my ($last_key, $finger) = @args;
874             if (defined $last_key) {
875             $self->_set_iterator_by_key($last_key, $finger);
876             }
877             my $node = $self->_next_node;
878             return ($node->key, $node->value);
879             }
880              
881             =item keys
882              
883             @keys = $list->keys;
884              
885             Returns a list of keys, in the order that they occur.
886              
887             @keys = $list->keys( $low, $high);
888              
889             Returns a list of keys between C<$low> and C<$high>, inclusive. (This
890             is only available in versions 1.02 and later.)
891              
892             =cut
893              
894             sub keys {
895             my ($low, $high, $finger) = @args;
896             my @result = ( );
897             if (defined $low) {
898             push @result, $self->_set_iterator_by_key($low, $finger)->key;
899             }
900             else {
901             $self->_reset_iterator;
902             }
903              
904             my $node;
905             while ( ($node = $self->_next_node) &&
906             ((!defined $high) || ($node->key_cmp($high) < 1) )) {
907             push @result, $node->key;
908             }
909             return @result;
910             }
911              
912             =item values
913              
914             @values = $list->values;
915              
916             Returns a list of values corresponding to the keys returned by the
917             L method. You can also request the values between a pair of
918             keys:
919              
920             @values = $list->values( $low, $high );
921              
922             =cut
923              
924             sub values {
925             my ($low, $high, $finger) = @args;
926             my @result = ( );
927             if (defined $low) {
928             push @result, $self->_set_iterator_by_key($low, $finger)->value;
929             }
930             else {
931             $self->_reset_iterator;
932             }
933              
934             my $node;
935             while ( ($node = $self->_next_node) &&
936             ((!defined $high) || ($node->key_cmp($high) < 1) )) {
937             push @result, $node->value;
938             }
939             return @result;
940             }
941              
942              
943             =item copy
944              
945             $list2 = $list1->copy;
946              
947             Makes a copy of a list. The configuration options passed to L are
948             used, although the exact structure of node levels is not cloned.
949              
950             $list2 = $list1->copy( $key_from, $key_to, $finger );
951              
952             Copy the list between C<$key_from> and C<$key_to> (inclusive). If
953             C<$finger> is defined, it will be used as a search finger to find
954             C<$key_from>. If C<$key_to> is not specified, then it will be assumed
955             to be the end of the list.
956              
957             If C<$key_from> does not exist, C will be returned.
958              
959             Note: the order of arguments has been changed since version 2.00!
960              
961             =cut
962              
963             sub copy {
964             my ($low, $high, $finger) = @args;
965             my %opts = map { $_ => $self->$_ } (CORE::keys %CONFIG_OPTIONS);
966             my $copy = Algorithm::SkipList->new( %opts );
967              
968             if (defined $low) {
969             my $node = $self->_set_iterator_by_key($low, $finger);
970             $copy->insert($node->key, $node->value), if ($node);
971             }
972             else {
973             $self->_reset_iterator;
974             }
975              
976             my $node;
977             while ( ($node = $self->_next_node) &&
978             ((!defined $high) || ($node->key_cmp($high) < 1) )) {
979             $copy->insert($node->key, $node->value);
980             }
981              
982             return $copy;
983             }
984              
985             sub truncate {
986             my ($key, $finger) = @args;
987              
988             my ($node, $cmp);
989             ($node, $cmp, $finger) = $self->_search_with_finger($key, $finger);
990              
991             if ($cmp) {
992             return;
993             }
994             else {
995             my %opts = map { $_ => $self->$_ } (CORE::keys %CONFIG_OPTIONS);
996             my $tail = Algorithm::SkipList->new( %opts );
997             my $list = $tail->list;
998              
999             for(my $i=0; $i<@$finger; $i++) {
1000             $list->set_child($i, $finger->[$i]->get_child($i));
1001             $finger->[$i]->set_child($i, undef);
1002             }
1003             $self->{last_node} = $finger->[0];
1004             return $tail;
1005             }
1006             }
1007              
1008             sub append {
1009             my ($head, $tail) = @_;
1010              
1011             my $left = $head->_last_node;
1012             my $right = $tail->_first_node;
1013              
1014             # Note: the behavior is not different when one of the skip lists is
1015             # empty. In particular, the tail is not cleared, although the user
1016             # should assume that it is.
1017              
1018             unless ($head->size) { return $tail; }
1019             unless ($tail->size) { return $head; }
1020              
1021             if ( (($left->key_cmp($right->key)<0) && ($right->key_cmp($left->key)>0)) ||
1022             ($head->allow_duplicates && ($left->key_cmp($right->key)==0)) ) {
1023              
1024             # We need to build an update vector for the last node on each
1025             # level. There's really no other way to do this but to use a
1026             # specialized search.
1027              
1028             my $finger = [ ($head->list) x $head->level ];
1029             {
1030             my $i = $head->level-1;
1031             my $node = $head->list->get_child($i);
1032             if ($node) {
1033             do {
1034             while (my $fwd = $node->get_child($i)) {
1035             $node = $fwd;
1036             }
1037             $finger->[$i] = $node;
1038             } while (--$i >= 0);
1039             }
1040             }
1041              
1042             for(my $i=0; $i<$head->level; $i++) {
1043             $finger->[$i]->set_child($i, $tail->list->get_child($i));
1044             }
1045              
1046             # If the tail has a greater height than the head, we increase it
1047              
1048             if ($tail->level > $head->level) {
1049             for (my $i=$head->level; $i<$tail->level; $i++) {
1050             $head->list->set_child($i, $tail->list->get_child($i));
1051             }
1052             $head->{level} = $tail->level;
1053             }
1054             $head->{size} += $tail->size;
1055             $tail->clear;
1056              
1057             return $head;
1058             }
1059             else {
1060             croak "Cannot append: first key of tail is less than last key of head";
1061             }
1062             }
1063              
1064             =begin internal
1065              
1066             =item _debug
1067              
1068             $list->_debug;
1069              
1070             This is an internal routine for dumping the contents and structure of
1071             a skiplist to STDERR. It is intended for debugging.
1072              
1073             =end internal
1074              
1075             =cut
1076              
1077             sub _debug {
1078             my ($fh) = @args;
1079              
1080             $fh = \*STDERR, unless ($fh);
1081              
1082             my $node = $self->list;
1083              
1084             while ($node) {
1085             if ($node->isa("Algorithm::SkipList::Header")) {
1086             print $fh "undef=undef (header) ", $node, "\n";
1087             }
1088             else {
1089             print $fh
1090             $node->key||'undef', "=", $node->value||'undef'," ", $node,"\n";
1091             }
1092              
1093             for(my $i=0; $i<$node->child_count; $i++) {
1094             print $fh " ", $i," ", $node->get_child($i)
1095             || 'undef', "\n";
1096             }
1097             print $fh "\n";
1098              
1099             $node = $node->get_child(0);
1100             }
1101             }
1102              
1103             1;
1104              
1105             __END__