Data-DDSketch-Shared

 view release on metacpan or  search on metacpan

t/01-basic.t  view on Meta::CPAN

use strict;
use warnings;
use Test::More;
use Data::DDSketch::Shared;

# constructor + introspection
{
    my $dd = Data::DDSketch::Shared->new(undef, 0.01, 1024);
    isa_ok $dd, 'Data::DDSketch::Shared';
    cmp_ok abs($dd->alpha - 0.01), '<', 1e-9, 'alpha';
    is $dd->num_buckets, 1024, 'num_buckets';
    cmp_ok abs($dd->gamma - (1.01/0.99)), '<', 1e-12, 'gamma == (1+a)/(1-a)';
    is $dd->count, 0, 'empty: count 0';
    is $dd->zero_count, 0, 'empty: zero_count 0';
    ok !defined($dd->quantile(0.5)), 'empty: quantile undef';
    ok !defined($dd->min), 'empty: min undef';
    ok !defined($dd->max), 'empty: max undef';
    ok !defined($dd->mean), 'empty: mean undef';
    is $dd->sum, 0, 'empty: sum 0';
}

# quantile accuracy: relative error <= alpha on a uniform distribution
{
    my $alpha = 0.01;
    my $dd = Data::DDSketch::Shared->new(undef, $alpha);
    my $N = 10000;
    $dd->add($_) for 1 .. $N;

    is $dd->count, $N, 'count == number added';
    is $dd->min, 1, 'min exact';
    is $dd->max, $N, 'max exact';
    cmp_ok abs($dd->mean - ($N + 1) / 2), '<', 1e-6, 'mean exact';
    is $dd->sum, $N * ($N + 1) / 2, 'sum exact';

    my $worst = 0;
    for my $q (0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99, 0.999) {
        my $est  = $dd->quantile($q);
        my $true = int($q * ($N - 1)) + 1;     # nearest-rank true value
        my $rel  = abs($est - $true) / $true;
        $worst = $rel if $rel > $worst;
        ok $rel <= $alpha * 1.001, sprintf("q=%.3f: rel err %.4f <= alpha", $q, $rel)
            or diag "est=$est true=$true";
    }
    diag sprintf("worst relative error: %.5f (alpha=%.3f)", $worst, $alpha);

    # quantile endpoints
    cmp_ok abs($dd->quantile(0) - 1) / 1, '<=', $alpha * 1.001, 'q=0 ~ min';
    cmp_ok abs($dd->quantile(1) - $N) / $N, '<=', $alpha * 1.001, 'q=1 ~ max';
    # median convenience
    is $dd->median, $dd->quantile(0.5), 'median == quantile(0.5)';
}

# negatives and zeros
{
    my $dd = Data::DDSketch::Shared->new(undef, 0.01);
    $dd->add($_) for -1000 .. -1;
    $dd->add(0) for 1 .. 50;
    $dd->add($_) for 1 .. 1000;
    is $dd->count, 2050, 'count includes negatives and zeros';
    is $dd->zero_count, 50, 'zero_count';
    is $dd->min, -1000, 'min is the most negative';
    is $dd->max, 1000, 'max is the most positive';
    # symmetric distribution -> median is 0 (the zero bucket sits in the middle)
    is $dd->quantile(0.5), 0, 'median of a symmetric set with a zero mass is 0';
    cmp_ok $dd->quantile(0.1), '<', 0, 'low quantile is negative';
    cmp_ok $dd->quantile(0.9), '>', 0, 'high quantile is positive';
    # negatives keep the relative-error guarantee (mirror of positives)
    cmp_ok abs($dd->quantile(0.9) + $dd->quantile(0.1)), '<', 1, 'symmetric quantiles cancel';
}

# add returns the running count; add_many
{
    my $dd = Data::DDSketch::Shared->new(undef, 0.02);
    is $dd->add(5), 1, 'add returns 1 for the first value';
    is $dd->add(7), 2, 'add returns the running count';
    my $n = $dd->add_many([ 1 .. 100 ]);
    is $n, 100, 'add_many returns the batch size';
    is $dd->count, 102, 'count after add_many';
    # add_many matches a loop of add
    my $a = Data::DDSketch::Shared->new(undef, 0.02);
    my $b = Data::DDSketch::Shared->new(undef, 0.02);
    $a->add_many([ map { $_ * 1.5 } 1 .. 200 ]);
    $b->add($_ * 1.5) for 1 .. 200;
    is $a->quantile(0.5), $b->quantile(0.5), 'add_many == a loop of add (same quantile)';
}

# clear
{
    my $dd = Data::DDSketch::Shared->new(undef, 0.01);
    $dd->add($_) for 1 .. 100;
    $dd->clear;
    is $dd->count, 0, 'clear resets count';
    is $dd->zero_count, 0, 'clear resets zero_count';
    ok !defined($dd->quantile(0.5)), 'clear -> quantile undef';
    ok !defined($dd->min), 'clear -> min undef';
    # usable after clear
    $dd->add(42);
    cmp_ok abs($dd->quantile(0.5) - 42) / 42, '<=', 0.011, 'usable after clear';
}

# merge: same geometry combines the distributions
{
    my $a = Data::DDSketch::Shared->new(undef, 0.01, 2048);
    my $b = Data::DDSketch::Shared->new(undef, 0.01, 2048);
    $a->add($_) for 1 .. 5000;
    $b->add($_) for 5001 .. 10000;



( run in 2.247 seconds using v1.01-cache-2.11-cpan-cb04b646d8c )