#!/usr/bin/env perl
use strict;
use warnings;
use lib 'blib/lib', 'blib/arch';
use Benchmark qw(cmpthese timethese);
use util qw(memo pipeline compose lazy force dig);

print "=" x 60, "\n";
print "util.pm Benchmark - Comparing XS vs Pure Perl\n";
print "=" x 60, "\n\n";

#===============================================
# Memo Benchmark
#===============================================
print "-" x 40, "\n";
print "MEMO (memoization)\n";
print "-" x 40, "\n";

# XS memoized function
my $xs_fib;
$xs_fib = memo(sub {
    my $n = shift;
    return $n if $n < 2;
    return $xs_fib->($n-1) + $xs_fib->($n-2);
});

# Pure Perl memoization
{
    my %cache;
    sub pure_memo {
        my $func = shift;
        return sub {
            my $key = join("\0", map { defined $_ ? $_ : "\x01" } @_);
            return $cache{$key} if exists $cache{$key};
            return $cache{$key} = $func->(@_);
        };
    }
}

my $pp_fib;
$pp_fib = pure_memo(sub {
    my $n = shift;
    return $n if $n < 2;
    return $pp_fib->($n-1) + $pp_fib->($n-2);
});

# Pre-compute to fill caches
$xs_fib->(20);
$pp_fib->(20);

print "Testing memo cache hit (fib(20) already computed):\n";
cmpthese(-2, {
    'XS memo' => sub { $xs_fib->(20) },
    'PP memo' => sub { $pp_fib->(20) },
});
print "\n";

#===============================================
# Pipeline Benchmark
#===============================================
print "-" x 40, "\n";
print "PIPELINE (function chaining)\n";
print "-" x 40, "\n";

my $double = sub { $_[0] * 2 };
my $add10 = sub { $_[0] + 10 };
my $square = sub { $_[0] * $_[0] };

# Pure Perl pipeline
sub pp_pipeline {
    my $val = shift;
    for my $fn (@_) {
        $val = $fn->($val);
    }
    return $val;
}

print "Pipeline 3 functions:\n";
cmpthese(-2, {
    'XS pipeline' => sub { pipeline(5, $double, $add10, $square) },
    'PP pipeline' => sub { pp_pipeline(5, $double, $add10, $square) },
    'Nested calls' => sub { $square->($add10->($double->(5))) },
});
print "\n";

#===============================================
# Compose Benchmark
#===============================================
print "-" x 40, "\n";
print "COMPOSE (create reusable pipeline)\n";
print "-" x 40, "\n";

my $xs_composed = compose($square, $add10, $double);

# Pure Perl compose
sub pp_compose {
    my @funcs = @_;
    return sub {
        my $val = $_[0];
        for my $fn (reverse @funcs) {
            $val = $fn->($val);
        }
        return $val;
    };
}

my $pp_composed = pp_compose($square, $add10, $double);

print "Call composed function:\n";
cmpthese(-2, {
    'XS compose' => sub { $xs_composed->(5) },
    'PP compose' => sub { $pp_composed->(5) },
});
print "\n";

#===============================================
# Lazy/Force Benchmark
#===============================================
print "-" x 40, "\n";
print "LAZY/FORCE (deferred evaluation)\n";
print "-" x 40, "\n";

# Create fresh lazy values for each iteration would be unfair
# Instead test force on already-computed lazy values
my $xs_lazy = lazy(sub { 42 });
force($xs_lazy);  # Pre-compute

# Pure Perl lazy
{
    package PP::Lazy;
    sub new {
        my ($class, $thunk) = @_;
        return bless { thunk => $thunk, forced => 0 }, $class;
    }
    sub force {
        my $self = shift;
        unless ($self->{forced}) {
            $self->{value} = $self->{thunk}->();
            $self->{forced} = 1;
        }
        return $self->{value};
    }
}

my $pp_lazy = PP::Lazy->new(sub { 42 });
$pp_lazy->force();  # Pre-compute

print "Force cached lazy value:\n";
cmpthese(-2, {
    'XS force' => sub { force($xs_lazy) },
    'PP force' => sub { $pp_lazy->force() },
});
print "\n";

#===============================================
# Dig Benchmark
#===============================================
print "-" x 40, "\n";
print "DIG (safe hash navigation)\n";
print "-" x 40, "\n";

my $deep_hash = {
    a => {
        b => {
            c => {
                d => 'value'
            }
        }
    }
};

# Pure Perl dig
sub pp_dig {
    my $hash = shift;
    for my $key (@_) {
        return undef unless ref($hash) eq 'HASH';
        return undef unless exists $hash->{$key};
        $hash = $hash->{$key};
    }
    return $hash;
}

print "Dig 4 levels deep:\n";
cmpthese(-2, {
    'XS dig' => sub { dig($deep_hash, 'a', 'b', 'c', 'd') },
    'PP dig' => sub { pp_dig($deep_hash, 'a', 'b', 'c', 'd') },
    'Direct' => sub { $deep_hash->{a}{b}{c}{d} },
});
print "\n";

print "Dig with missing key (returns undef):\n";
cmpthese(-2, {
    'XS dig' => sub { dig($deep_hash, 'a', 'x', 'c', 'd') },
    'PP dig' => sub { pp_dig($deep_hash, 'a', 'x', 'c', 'd') },
});
print "\n";

#===============================================
# Summary
#===============================================
print "=" x 60, "\n";
print "Benchmark complete!\n";
print "=" x 60, "\n";
