Statistics-Sampler-Multinomial

 view release on metacpan or  search on metacpan

lib/Statistics/Sampler/Multinomial/Indexed.pm  view on Meta::CPAN

package Statistics::Sampler::Multinomial::Indexed;

use 5.014;
use warnings;
use strict;

our $VERSION = '1.02';

use Carp;
use Ref::Util qw /is_arrayref/;
use List::Util 1.29 qw /min max sum pairmap/;
#use List::MoreUtils qw /first_index/;
use Scalar::Util qw /blessed looks_like_number/;

#use POSIX qw /ceil floor/;

use parent qw /Statistics::Sampler::Multinomial/;

sub new {
    my $pkg = shift;
    my $self = Statistics::Sampler::Multinomial->new(@_);
    bless $self, __PACKAGE__;
    
    $self->build_index;
    
    return $self;
}

sub _clone_inner {
    my $self = shift;
    #  parent class handles most details
    my $clone = $self->SUPER::_clone_inner;
    
    #  generate a dup of the index array-of-arrays
    $clone->{index} = [map {[@$_]} @{$self->{index}}];

    return $clone;
}


#  Build a tree based index of cumulative values.
#  This will help the single draw methods.
#  Idea from 
#  https://www.chiark.greenend.org.uk/~sgtatham/algorithms/cumulative.html
sub build_index {
    my $self = shift;
    my $data = $self->{data};

    #my $max_depth = 1 + logb (scalar @$data);
    my $max_depth = 1 + int (log (scalar @$data) / log (2));

    # each index entry contains the cumulative sum of its terminals
    # and each level is half the length of the one below 
    my @indexed;

    #  bottom is just the data
    $indexed[$max_depth] = $data;

    for my $level (reverse (0..$max_depth-1)) {
        my $pop;
        if (@{$indexed[$level+1]} % 2) {
            push @{$indexed[$level+1]}, 0;
            $pop = 1;
        }
        @{$indexed[$level]} = pairmap {($a//0)+($b//0)} @{$indexed[$level+1]};
        if ($pop) {
            pop @{$indexed[$level+1]};
        }
    }
    



( run in 0.688 second using v1.01-cache-2.11-cpan-39bf76dae61 )