#!/usr/bin/env raku
use v6.d;

use Math::NIntegrate;

my %*SUB-MAIN-OPTS = :named-anywhere;

#| Compute numerical integral estimate.
sub MAIN(
        *@args,
        *%args
         ) {

    my $help = %args<help>:exists;
    %args<help>:delete;

    if $help || @args.elems < 2 {
        USAGE();
        exit($help ?? 0 !! 1);
    }

    my $integrand;
    my @ranges;
    try {
        use MONKEY-SEE-NO-EVAL;
        $integrand = EVAL '$integrand = ' ~ @args.head;
        @ranges = EVAL '@ranges = ' ~ (@args.elems > 2 ?? @args.tail(*-1).join(', ') !! "[{@args[1]}],");
    }

    if $integrand !~~ Callable:D {
        die 'Cannot evaluate the integrand specification to a callable.'
    }

    if @ranges !~~ Positional:D {
        die 'Cannot evaluate the ranges specification into a list.'
    }

    # Attempt integration
    try {
        my @argsLocal = $integrand, |@ranges;
        say nintegrate(|@argsLocal, |%args)
    }

    if $! {
        die 'Cannot process the numerical integration specification.'
    }
}

sub USAGE() {
    say "Usage: nintegrate INTEGRAND RANGE-SPEC ... [OPTIONS]\n\n"
            ~ q:to/USAGE/;
The first positional argument is Raku code describing the integrand.

The remaining positional arguments are Raku code range specifications.

Examples of integrands:
  '{$^x ** 2}'
  'sub ($x, $y) { $x + $y }'

Examples of range specifications:
  '<x 0 1>'
  '["x", -2, 20]'

Named options are passed to Math::NIntegrate, including:
  --precision-goal=VALUE
  --method=VALUE
  --max-recursion=VALUE
  --OPTION=VALUE

Examples:
  program '{$^x ** 2}' '<x 0 1>' --precision-goal=10
  program 'sub ($x, $y) { $x + $y }' '["x", 0, 1]' '["y", 0, 2]' --method=adaptive

Use --help to display this message.
USAGE
}
