Binary-Heap-Array

 view release on metacpan or  search on metacpan

lib/Binary/Heap/Array.pm  view on Meta::CPAN

   {if ($f =~ m/$_/i)
     {  $f =~ s/$_//gi;
      $name .= uc($_);
     }
    else
     {$name .= lc($_);
     }
   }
  $f =~ /\A\s*\Z/ or confess "Invalid flags '$f' in '$flags'";                  # Check flags syntax

  return bless [], $name;                                                       # Bless into appropriately optimized package
 } # new

sub code {<<'END'}                                                              # Code to be optimised
sub speedInUse{(speedNo>>0)%2}                                                  ## Use a vec() in each array to mark which sub arrays can be reused rather than being freed immediately
sub speedPp   {(speedNo>>1)%2}                                                  ## Use pre/post to skip over elements at the start/end at the cost of two additional fields per array
sub speedSize {(speedNo>>2)%2}                                                  ## Cache the current size of the array at the cost of an additional field per array
sub speedWidth{(speedNo>>3)%2}                                                  ## Cache the current width of the array at the cost of an additional field per array

sub subarray                                                                    ## An array, always a power of 2 wide, containing sub arrays which contain the caller's data or slots which are empty, each of the sub arrays is a power of 2 wide which d...
 {my ($array) = @_;
  no overloading;
  $array->[0] //= []                                                            # Field 1
 }
sub speed :lvalue                                                               ## Algorithm to use
 {my ($array) = @_;
  no overloading;
  $array->[1] //= (my $v = 0)                                                   # Field 2
 }
sub inUse :lvalue                                                               ## A vec() of bits, the same width as subarray where each bit tells us whether the corresponding sub array is in use or not.
 {my ($array) = @_;
  no overloading;
  confess unless speedInUse;
  $array->[2] //= (my $v = '')                                                  # Field 3
 }
sub pre :lvalue                                                                 ## The number of entries to ignore at the beginning to assist with shift/unshift
 {my ($array) = @_;
  no overloading;
  confess unless speedPp;
  $array->[3] //= (my $v = 0)                                                   # Field 4
 }
sub post :lvalue                                                                ## The number of entries to ignore at the end to assist with pop/push
 {my ($array) = @_;
  no overloading;
  confess unless speedPp;
  $array->[4] //= (my $v = 0)                                                   # Field 5
 }
sub currentSize :lvalue                                                         ## The current size of the array
 {my ($array) = @_;
  no overloading;
  confess unless speedSize;
  $array->[5] //= (my $v = 0)                                                   # Field 6
 }
sub currentWidth :lvalue                                                        ## The current width of the array
 {my ($array) = @_;
  no overloading;
  confess unless speedWidth;
  $array->[6] //= (my $v = 0)                                                   # Field 7
 }

sub at($$) :lvalue                                                              # Address the element at a specified index so that it can get set or got
 {my ($array, $index) = @_;                                                     # Array, index of element
  my $n = size($array);                                                         # Array size
  return undef if $index < -$n or $index >= $n;                                 # Index out of range
  return &atUp(@_) if $index >= 0;
  &atDown(@_)
 } # at                                                                         # It would be nice to use overload @{} here but this requires flattening the array which would be very expensive on large arrays

sub inUseVector ($) :lvalue                                                     ## Sub arrays in use
 {my ($array) = @_;
  return inUse($array) if speedInUse;
  my $v = '';
  my @a = @{subarray($array)};
  vec($v, $_, 1) = !!$a[$_] for 0..$#a;
  $v
 }

sub pop($)                                                                      # Pop the topmost element from the leading full array and spread the remainder of its contents as sub arrays of the correct size for each preceding empty slot
 {my ($array) = @_;                                                             # Array from which an element is to be popped
  my $N = size($array);                                                         # Size of array
  return undef unless $N;                                                       # Cannot pop from an empty array

  if (speedPp)                                                                  # Fast with pre and post
   {my $element = at($array, -1);
    post($array)++;
    currentSize($array)-- if speedSize;                                         # Decrease cached size of array if possible - has to be done late to avoid confusion over at
    return $element;
   }
  else
   {currentSize($array)-- if speedSize;                                         # Decrease cached size of array if possible - has to be done late to avoid confusion over at
    my $S = subarray($array);                                                   # Sub array list for this array
    my $v = inUseVector($array);                                                # Sub arrays in use

    for my $i(keys @$S)                                                         # Index to each sub array
     {my $s = $S->[$i];                                                         # Sub array
      if (vec($v, $i, 1))                                                       # Full sub array
       {my $pop = CORE::pop @$s;                                                # Pop an element off the first full sub array
        for my $I(0..$i-1)                                                      # Distribute the remaining elements of this sub array so that each sub array is always a power of two wide which depends on teh position of the sub array in the array of ...
         {my $j = 1<<$I;
          splice @{$S->[$I]}, 0, $j, splice @$s, -$j, $j;                       # Copy block across
          vec(inUse($array), $I, 1) = 1 if speedInUse;                          # Mark this sub array as in use
         }
        if ($N == 1)                                                            # We are popping the last element in a binary heap array
         {$#{subarray($array)} = -1;                                            # Remove all sub arrays
          inUse($array)        = '' if speedInUse;                              # Mark all sub arrays as not in use and shorten the vec() string at the same time
          currentWidth($array) =  0 if speedWidth;                              # New width of array of sub arrays
          @$S = ();                                                             # Empty the array of sub arrays
         }
        else                                                                    # Pop an element that is not the last element in a binary heap array
         {if (speedInUse)
           {vec(inUse($array), $i, 1) = 0;                                      # Mark sub array as not in use
           }
          else
           {$S->[$i] = undef;                                                   # Free sub array as it is no longer in use
           }
          my $W = width($array);                                                # Get current width
          my $w = containingPowerOfTwo($W);                                     # Current width is contained by this power of two
          inUse($array) = substr(inUse($array), 0, 1<<($w-3)) if speedInUse;    # Keep vec() string length in bounds - the 3 is because there 2**3 bits in a byte as used by vec()
          splice @$S, 1<<$w if @$S > 1<<$w;                                     # Shorten the array of sub arrays while leaving some room for a return to growth
          $S->[$_] = undef for $W..(1<<$w)-1;                                   # Remove outer inactive arrays but keep inner inactive arrays to reduce the allocation rate - the whole point of the inUse array
          currentWidth($array) = $w+1                                           # New width of array of sub arrays



( run in 0.469 second using v1.01-cache-2.11-cpan-5b529ec07f3 )