#!/usr/bin/perl -w
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl t/99_speed.t'

BEGIN { $^W = 1 };
use strict;
use Benchmark;
use FindBin qw($Bin);

use Test::More tests => 2;

my $cachegrind  = 0;
my $simple_only = 1;
my $dirty       = 0;

my $calibrate	= 5;

# Don't use insanely much memory even on very fast computers
my $max_size = 1e6;

my $hires;
BEGIN {
    $hires = eval 'use Time::HiRes qw(time); 1';
    @Heap::Simple::implementors = qw(Heap::Simple::Perl) unless
        @Heap::Simple::implementors;
};

my $option_file = "$Bin/options";
my %options;
open(my $fh, $option_file) || die "Could not open '$option_file': $!";
{
    local $_;
    while (<$fh>) {
        s/#.*//;
        next unless /\S/;
        my ($name, $value) =
            /^\s*(BENCHMARK|BENCHMARK_OTHERS|DIRTY|VALGRIND)\s*=\s*(\S+)\s*$/ or
            die "Could not parse line $. of $option_file\n";
        $options{$name} = $value;
    }
}
close($fh) || die "Could not close '$option_file': $!";

SKIP: {
    skip "No benchmark requested", 2 unless $options{BENCHMARK};
    use_ok("Heap::Simple");
    is(Heap::Simple->implementation, "Heap::Simple::Perl");
}
exit if !$options{BENCHMARK};

$simple_only = !$options{BENCHMARK_OTHERS} if
    exists $options{BENCHMARK_OTHERS};
$cachegrind = $options{VALGRIND} if exists $options{VALGRIND};
$dirty      = $options{DIRTY}    if exists $options{DIRTY};

my $class = Heap::Simple->implementation;
$dirty = 1 if !defined $dirty && $class eq "Heap::Simple::XS";
$dirty = $dirty ? 1 : 0;

my $exit = 0;
my @run;

sub run {
    my $script = shift;
    if (my $rc = system(@run, "$Bin/$script", @_)) {
        $rc /= 256 unless $rc % 256;
        $exit = $rc;
    }
}

sub mark {
    print STDERR "------\n";
}

my $do_fibonacci = !$simple_only && !$cachegrind && eval '
    use Heap::Fibonacci;
    use Heap::Elem::Num qw(NumElem);
    use Heap::Elem::Str qw(StrElem);
    1';
my $do_binary = !$simple_only && !$cachegrind && eval '
    use Heap::Binary;
    use Heap::Elem::Num qw(NumElem);
    use Heap::Elem::Str qw(StrElem);
    1';
my $do_binomial = !$simple_only && !$cachegrind && eval '
    use Heap::Binomial;
    use Heap::Elem::Num qw(NumElem);
    use Heap::Elem::Str qw(StrElem);
    1';
my $do_priority = !$simple_only && !$cachegrind && eval '
    use Heap::Priority;
    1';
my $do_array = !$simple_only && !$cachegrind && eval '
    use Array::Heap2;
    1';

print STDERR "\n";
my $size;
if ($cachegrind) {
    @run = ("valgrind", "--tool=cachegrind", $^X);
    $size = 10000;
} else {
    @run = ($^X);
    # Calibrate perl speed
    mark();
    print STDERR "Calibrating. Should take about $calibrate seconds\n";
    my $i = 0;
    my $from;
    if (!$hires) {
        $from = time;
        1 while $from == time;
    }
    $from = $calibrate+time;
    do {
        $i++ for 1..10000;
    } while $from > time;
    $size = int($i/$calibrate/24);
    $size *= 5 if
        !$do_fibonacci && !$do_binary && !$do_binomial && !$do_priority;
    $size =~ s/\B./0/g;
    $size = $max_size if $size > $max_size;
}

my $inc = join(":", map {
    my $a = $_;
    $a =~ s/(.)/"%" . ord($1)/esg;
    $a;
} @INC);
for my $string (0) {
    my %options = (INC	  => $inc,
                   size   => $size,
                   srand  => 832516216,	# We want repeatable benchmarks
                   string => $string);

    if (!$simple_only && !$cachegrind) {
        run("speed_fibonacci", %options) if $do_fibonacci;
        run("speed_binary",    %options) if $do_binary;
        run("speed_binomial",  %options) if $do_binomial;
        run("speed_priority",  %options) if $do_priority;
        run("speed_array_heap2",  %options) if $do_array;
    }
    if ($cachegrind) {
        run("speed_array",  %options, class  => $class, dirty => $dirty);
    } else {
        run("speed_hash",   %options, class  => $class, dirty => $dirty);
        run("speed_array",  %options, class  => $class, dirty => $dirty);
        run("speed_scalar", %options, class  => $class, dirty => $dirty);
        run("speed_scalar", %options, class  => $class, dirty => 1) if
            !$dirty && $class ne "Heap::Simple::Perl";
    }
}
exit $exit;
