use v6-alpha;

use Test;

plan 13;

# Mostly copied from Perl 5.8.4 s t/op/recurse.t

sub gcd {
    return gcd(@_[0] - @_[1], @_[1]) if (@_[0] > @_[1]);
    return gcd(@_[0], @_[1] - @_[0]) if (@_[0] < @_[1]);
    @_[0];
}

sub factorial {
    @_[0] < 2 ?? 1 !! @_[0] * factorial(@_[0] - 1);
}

sub fibonacci {
    @_[0] < 2 ?? 1 !! &?ROUTINE(@_[0] - 2) + &?ROUTINE(@_[0] - 1);
}

# Highly recursive, highly aggressive.
# Kids, do not try this at home.
#
# For example ackermann(4,1) will take quite a long time.
# It will simply eat away your memory. Trust me.

sub ackermann {
    return @_[1] + 1               if (@_[0] == 0);
    return ackermann(@_[0] - 1, 1) if (@_[1] == 0);
    ackermann(@_[0] - 1, ackermann(@_[0], @_[1] - 1));
}

# Highly recursive, highly boring.

sub takeuchi {
    # for the script failure here, see Parser.hs:589
    @_[1] < @_[0] ??
        takeuchi(takeuchi(@_[0] - 1, @_[1], @_[2]),
                 takeuchi(@_[1] - 1, @_[2], @_[0]),
                 takeuchi(@_[2] - 1, @_[0], @_[1]))
            !! @_[2];
}

ok(gcd(1147, 1271) == 31);
ok(gcd(1908, 2016) == 36);
ok(factorial(10) == 3628800);
ok(factorial(factorial(3)) == 720);
ok(fibonacci(10) == 89);

# ok(fibonacci(fibonacci(7)) == 17711);
# takes too long
# skip("Takes too long to wait for");

# dunno if these could use some shorter/simpler sub names, but I'm not
# thinking of anything offhand.

# what the silly sub names mean:
#  - 'mod' means it makes a local copy of the variable, modified as
#    necessary
#  - 'nomod' means it passes the modified value directly to the next call
#  - 'named' means it uses named parameters
#  - 'unnamed' means it uses @_ for parameters

sub countup_nomod_unnamed {
    my ($num) = @_;
    return $num if $num <= 0;
    return countup_nomod_unnamed($num-1), $num;
}

sub countdown_nomod_unnamed {
    my ($num) = @_;
    return $num if $num <= 0;
    return $num, countdown_nomod_unnamed($num-1);
}

sub countup_nomod_named ($num) {
    return $num if $num <= 0;
    return countup_nomod_named($num-1), $num;
}

sub countdown_nomod_named ($num) {
    return $num if $num <= 0;
    return $num, countdown_nomod_named($num-1);
}

sub countup_mod_unnamed {
    my ($num) = @_;
    my $n = $num - 1;
    return $num if $num <= 0;
    return countup_mod_unnamed($n), $num;
}

sub countdown_mod_unnamed {
    my ($num) = @_;
    my $n = $num - 1;
    return $num if $num <= 0;
    return $num, countdown_mod_unnamed($n);
}

sub countup_mod_named ($num) {
    my $n = $num - 1;
    return $num if $num <= 0;
    return countup_mod_named($n), $num;
}

sub countdown_mod_named ($num) {
    my $n = $num - 1;
    return $num if $num <= 0;
    return $num, countdown_mod_named($n);
}

is(  countup_nomod_named(5).join(""),   "012345", "recursive count up: named param, no modified value");
is(countdown_nomod_named(5).join(""),   "543210", "recursive count down: named param, no modified value");
is(  countup_nomod_unnamed(5).join(""), "012345", "recursive count up: unnamed param, no modified value");
is(countdown_nomod_unnamed(5).join(""), "543210", "recursive count down: unnamed param, no modified value");
is(  countup_mod_named(5).join(""),     "012345", "recursive count up: named param, modified value");
is(countdown_mod_named(5).join(""),     "543210", "recursive count down: named param, modified value");
is(  countup_mod_unnamed(5).join(""),   "012345", "recursive count up: unnamed param, modified value");
is(countdown_mod_unnamed(5).join(""),   "543210", "recursive count down: unnamed param, modified value");
