home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Binomial.pm < prev    next >
Encoding:
Perl POD Document  |  2003-12-04  |  9.1 KB  |  470 lines

  1. package Heap::Binomial;
  2.  
  3. use strict;
  4. use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  5.  
  6. require Exporter;
  7. require AutoLoader;
  8.  
  9. @ISA = qw(Exporter AutoLoader);
  10.  
  11. # No names exported.
  12. # No names available for export.
  13. @EXPORT = ( );
  14.  
  15. $VERSION = '0.70';
  16.  
  17.  
  18. # Preloaded methods go here.
  19.  
  20.  
  21. # common names
  22. #    h    - heap head
  23. #    el    - linkable element, contains user-provided value
  24. #    v    - user-provided value
  25.  
  26. ################################################# debugging control
  27.  
  28. my $debug = 0;
  29. my $validate = 0;
  30.  
  31. # enable/disable debugging output
  32. sub debug {
  33.     @_ ? ($debug = shift) : $debug;
  34. }
  35.  
  36. # enable/disable validation checks on values
  37. sub validate {
  38.     @_ ? ($validate = shift) : $validate;
  39. }
  40.  
  41. my $width = 3;
  42. my $bar = ' | ';
  43. my $corner = ' +-';
  44. my $vfmt = "%3d";
  45.  
  46. sub set_width {
  47.     $width = shift;
  48.     $width = 2 if $width < 2;
  49.  
  50.     $vfmt = "%${width}d";
  51.     $bar = $corner = ' ' x $width;
  52.     substr($bar,-2,1) = '|';
  53.     substr($corner,-2,2) = '+-';
  54. }
  55.  
  56. sub hdump {
  57.     my $el = shift;
  58.     my $l1 = shift;
  59.     my $b = shift;
  60.  
  61.     my $ch;
  62.  
  63.     unless( $el ) {
  64.     print $l1, "\n";
  65.     return;
  66.     }
  67.  
  68.     hdump( $ch = $el->{child},
  69.     $l1 . sprintf( $vfmt, $el->{val}->val),
  70.     $b . $bar );
  71.  
  72.     while( $ch = $ch->{sib} ) {
  73.     hdump( $ch, $b . $corner, $b . $bar );
  74.     }
  75. }
  76.  
  77. sub heapdump {
  78.     my $h;
  79.  
  80.     while( $h = shift ) {
  81.     my $el;
  82.  
  83.     for( $el = $$h; $el; $el = $el->{sib} ) {
  84.         hdump( $el, sprintf( "%02d: ", $el->{degree}), '    ' );
  85.     }
  86.     print "\n";
  87.     }
  88. }
  89.  
  90. sub bhcheck {
  91.  
  92.     my $pel = shift;
  93.     my $pdeg = $pel->{degree};
  94.     my $pv = $pel->{val};
  95.     my $cel;
  96.     for( $cel = $pel->{child}; $cel; $cel = $cel->{sib} ) {
  97.            die "degree not decreasing in heap"
  98.         unless --$pdeg == $cel->{degree};
  99.     die "heap order not preserved"
  100.         unless $pv->cmp($cel->{val}) <= 0;
  101.     bhcheck($cel);
  102.     }
  103.     die "degree did not decrease to zero"
  104.     unless $pdeg == 0;
  105. }
  106.  
  107.  
  108. sub heapcheck {
  109.     my $h;
  110.     while( $h = shift ) {
  111.     heapdump $h if $validate >= 2;
  112.     my $el = $$h or next;
  113.     my $pdeg = -1;
  114.     for( ; $el; $el = $el->{sib} ) {
  115.         $el->{degree} > $pdeg
  116.         or die "degree not increasing in list";
  117.         $pdeg = $el->{degree};
  118.         bhcheck($el);
  119.     }
  120.     }
  121. }
  122.  
  123.  
  124. ################################################# forward declarations
  125.  
  126. sub elem;
  127. sub elem_DESTROY;
  128. sub link_to;
  129. sub moveto;
  130.  
  131. ################################################# heap methods
  132.  
  133.  
  134. sub new {
  135.     my $self = shift;
  136.     my $class = ref($self) || $self;
  137.     my $h = undef;
  138.     bless \$h, $class;
  139. }
  140.  
  141. sub DESTROY {
  142.     my $h = shift;
  143.  
  144.     elem_DESTROY $$h;
  145. }
  146.  
  147. sub add {
  148.     my $h = shift;
  149.     my $v = shift;
  150.     $validate && do {
  151.     die "Method 'heap' required for element on heap"
  152.         unless $v->can('heap');
  153.     die "Method 'cmp' required for element on heap"
  154.         unless $v->can('cmp');
  155.     };
  156.     $$h = elem $v, $$h;
  157.     $h->self_union_once;
  158. }
  159.  
  160. sub top {
  161.     my $h = shift;
  162.     my $el = $$h or return undef;
  163.     my $top = $el->{val};
  164.     while( $el = $el->{sib} ) {
  165.     $top = $el->{val}
  166.         if $top->cmp($el->{val}) > 0;
  167.     }
  168.     $top;
  169. }
  170.  
  171. *minimum = \⊤
  172.  
  173. sub extract_top {
  174.     my $h = shift;
  175.     my $mel = $$h or return undef;
  176.     my $top = $mel->{val};
  177.     my $mpred = $h;
  178.     my $el = $mel;
  179.     my $pred = $h;
  180.  
  181.     # find the heap with the lowest value on it
  182.     while( $pred = \$el->{sib}, $el = $$pred ) {
  183.     if( $top->cmp($el->{val}) > 0 ) {
  184.         $top = $el->{val};
  185.         $mel = $el;
  186.         $mpred = $pred;
  187.     }
  188.     }
  189.  
  190.     # found it, $mpred points to it, $mel is its container, $val is it
  191.     # unlink it from the chain
  192.     $$mpred = $mel->{sib};
  193.  
  194.     # we're going to return the value from $mel, but all of its children
  195.     # must be retained in the heap.  Make a second heap with the children
  196.     # and then merge the heaps.
  197.     $h->absorb_children($mel);
  198.  
  199.     # finally break all of its pointers, so that we won't leave any
  200.     # memory loops when we forget about the pointer to $mel
  201.     $mel->{p} = $mel->{child} = $mel->{sib} = $mel->{val} = undef;
  202.  
  203.     # break the back link
  204.     $top->heap(undef);
  205.  
  206.     # and return the value
  207.     $top;
  208. }
  209.  
  210. *extract_minimum = \&extract_top;
  211.  
  212. sub absorb {
  213.     my $h = shift;
  214.     my $h2 = shift;
  215.  
  216.     my $dest_link = $h;
  217.     my $el1 = $$h;
  218.     my $el2 = $$h2;
  219.     my $anymerge = $el1 && $el2;
  220.     while( $el1 && $el2 ) {
  221.     if( $el1->{degree} <= $el2->{degree} ) {
  222.         # advance on h's list, it's already linked
  223.         $dest_link = \$el1->{sib};
  224.         $el1 = $$dest_link;
  225.     } else {
  226.         # move next h2 elem to head of h list
  227.         $$dest_link = $el2;
  228.         $dest_link = \$el2->{sib};
  229.         $el2 = $$dest_link;
  230.         $$dest_link = $el1;
  231.     }
  232.     }
  233.  
  234.     # if h ran out first, move rest of h2 onto end
  235.     if( $el2 ) {
  236.     $$dest_link = $el2;
  237.     }
  238.  
  239.     # clean out h2, all of its elements have been move to h
  240.     $$h2 = undef;
  241.  
  242.     # fix up h - it can have multiple items at the same degree if we
  243.     #    actually merged two non-empty lists
  244.     $anymerge ? $h->self_union: $h;
  245. }
  246.  
  247. # a key has been decreased, it may have to percolate up in its heap
  248. sub decrease_key {
  249.     my $h = shift;
  250.     my $v = shift;
  251.     my $el = $v->heap or return undef;
  252.     my $p;
  253.  
  254.     while( $p = $el->{p} ) {
  255.     last if $v->cmp($p->{val}) >= 0;
  256.     moveto $el, $p->{val};
  257.     $el = $p;
  258.     }
  259.  
  260.     moveto $el, $v;
  261.  
  262.     $v;
  263. }
  264.  
  265. # to delete an item, we bubble it to the top of its heap (as if its key
  266. # had been decreased to -infinity), and then remove it (as in extract_top)
  267. sub delete {
  268.     my $h = shift;
  269.     my $v = shift;
  270.     my $el = $v->heap or return undef;
  271.  
  272.     # bubble it to the top of its heap
  273.     my $p;
  274.     while( $p = $el->{p} ) {
  275.     moveto $el, $p->{val};
  276.     $el = $p;
  277.     }
  278.  
  279.     # find it on the main list, to remove it and split up the children
  280.     my $n;
  281.     for( $p = $h; ($n = $$p) && $n != $el; $p = \$n->{sib} ) {
  282.     ;
  283.     }
  284.  
  285.     # remove it from the main list
  286.     $$p = $el->{sib};
  287.  
  288.     # put any children back onto the main list
  289.     $h->absorb_children($el);
  290.  
  291.     # remove the link to $el
  292.     $v->heap(undef);
  293.  
  294.     return $v;
  295. }
  296.  
  297.  
  298. ################################################# internal utility functions
  299.  
  300. sub elem {
  301.     my $v = shift;
  302.     my $sib = shift;
  303.     my $el = {
  304.     p    =>    undef,
  305.     degree    =>    0,
  306.     child    =>    undef,
  307.     val    =>    $v,
  308.     sib    =>    $sib,
  309.     };
  310.     $v->heap($el);
  311.     $el;
  312. }
  313.  
  314. sub elem_DESTROY {
  315.     my $el = shift;
  316.     my $ch;
  317.     my $next;
  318.  
  319.     while( $el ) {
  320.     $ch = $el->{child} and elem_DESTROY $ch;
  321.     $next = $el->{sib};
  322.  
  323.     $el->{val}->heap(undef);
  324.     $el->{child} = $el->{sib} = $el->{p} = $el->{val} = undef;
  325.     $el = $next;
  326.     }
  327. }
  328.  
  329. sub link_to {
  330.     my $el = shift;
  331.     my $p = shift;
  332.  
  333.     $el->{p} = $p;
  334.     $el->{sib} = $p->{child};
  335.     $p->{child} = $el;
  336.     $p->{degree}++;
  337. }
  338.  
  339. sub moveto {
  340.     my $el = shift;
  341.     my $v = shift;
  342.  
  343.     $el->{val} = $v;
  344.     $v->heap($el);
  345. }
  346.  
  347. # we've merged two lists in degree order.  Traverse the list and link
  348. # together any pairs (adding 1 + 1 to get 10 in binary) to the next
  349. # higher degree.  After such a merge, there may be a triple at the
  350. # next degree - skip one and merge the others (adding 1 + 1 + carry
  351. # of 1 to get 11 in binary).
  352. sub self_union {
  353.     my $h = shift;
  354.     my $prev = $h;
  355.     my $cur = $$h;
  356.     my $next;
  357.     my $n2;
  358.  
  359.     while( $next = $cur->{sib} ) {
  360.     if( $cur->{degree} != $next->{degree} ) {
  361.         $prev = \$cur->{sib};
  362.         $cur = $next;
  363.         next;
  364.     }
  365.  
  366.     # two or three of same degree, need to do a merge. First though,
  367.     # skip over the leading one of there are three (it is the result
  368.     # [carry] from the previous merge)
  369.     if( ($n2 = $next->{sib}) && $n2->{degree} == $cur->{degree} ) {
  370.         $prev = \$cur->{sib};
  371.         $cur = $next;
  372.         $next = $n2;
  373.     }
  374.  
  375.     # and now the merge
  376.     if( $cur->{val}->cmp($next->{val}) <= 0 ) {
  377.         $cur->{sib} = $next->{sib};
  378.         link_to $next, $cur;
  379.     } else {
  380.         $$prev = $next;
  381.         link_to $cur, $next;
  382.         $cur = $next;
  383.     }
  384.     }
  385.     $h;
  386. }
  387.  
  388. # we've added one element at the front, keep merging pairs until there isn't
  389. # one of the same degree (change all the low order one bits to zero and the
  390. # lowest order zero bit to one)
  391. sub self_union_once {
  392.     my $h = shift;
  393.     my $cur = $$h;
  394.     my $next;
  395.  
  396.     while( $next = $cur->{sib} ) {
  397.     return if $cur->{degree} != $next->{degree};
  398.  
  399.     # merge
  400.     if( $cur->{val}->cmp($next->{val}) <= 0 ) {
  401.         $cur->{sib} = $next->{sib};
  402.         link_to $next, $cur;
  403.     } else {
  404.         $$h = $next;
  405.         link_to $cur, $next;
  406.         $cur = $next;
  407.     }
  408.     }
  409.     $h;
  410. }
  411.  
  412. # absorb all the children of an element into a heap
  413. sub absorb_children {
  414.     my $h = shift;
  415.     my $el = shift;
  416.  
  417.     my $h2 = $h->new;
  418.     my $child = $el->{child};
  419.     while(  $child ) {
  420.     my $sib = $child->{sib};
  421.     $child->{sib} = $$h2;
  422.     $child->{p} = undef;
  423.     $$h2 = $child;
  424.     $child = $sib;
  425.     }
  426.  
  427.     # merge them all in
  428.     $h->absorb($h2);
  429. }
  430.  
  431.  
  432. 1;
  433.  
  434. __END__
  435.  
  436. =head1 NAME
  437.  
  438. Heap::Binomial - a Perl extension for keeping data partially sorted
  439.  
  440. =head1 SYNOPSIS
  441.  
  442.   use Heap::Binomial;
  443.  
  444.   $heap = Heap::Binomial->new;
  445.   # see Heap(3) for usage
  446.  
  447. =head1 DESCRIPTION
  448.  
  449. Keeps elements in heap order using a linked list of binomial trees.
  450. The I<heap> method of an element is used to store a reference to
  451. the node in the list that refers to the element.
  452.  
  453. See L<Heap> for details on using this module.
  454.  
  455. =head1 AUTHOR
  456.  
  457. John Macdonald, jmm@perlwolf.com
  458.  
  459. =head1 COPYRIGHT
  460.  
  461. Copyright 1998-2003, O'Reilly & Associates.
  462.  
  463. This code is distributed under the same copyright terms as perl itself.
  464.  
  465. =head1 SEE ALSO
  466.  
  467. Heap(3), Heap::Elem(3).
  468.  
  469. =cut
  470.