#!/usr/bin/env perl

# Copyright (c) 2015-2020 Christian Jaeger, copying@christianjaeger.ch
# This is free software. See the file COPYING.md that came bundled
# with this file.

use strict;
use warnings;
use warnings FATAL => 'uninitialized';

use Test::Requires qw(BSD::Resource);
import BSD::Resource;
use Scalar::Util "weaken";
use FP::Carp;

sub rss {
    @_ == 0 or fp_croak_nargs 0;
    (BSD::Resource::getrusage(BSD::Resource::RUSAGE_SELF()))[2]
}

sub naturals {
    my $f;
    $f = sub {
        my ($n) = @_;
        my $f = $f;
        sub {
            if ($n > 0) { [$n, &$f($n - 1)] }
            else {
                undef
            }
        }
    };
    my $f_ = $f;
    weaken $f;
    goto &$f_;
}

sub stream_sum {
    my ($s) = @_;

    #weaken $_[0];
    # ^ not necessary here, since, unlike with FP::Lazy::Promise,
    # resulting value is not saved in its 'generating container'
    my $tot = 0;
LP: {
        if (my $fs = &$s) {
            ($tot, $s) = ($$fs[0] + $tot, $$fs[1]);
            goto LP;
        } else {
            $tot
        }
    }
}

@ARGV == 2 or die "usage: $0 n N";
my ($n, $N) = @ARGV;

my $start = rss;

my $res;
for (1 .. $N) {
    my $ns = naturals $n;
    $res = stream_sum $ns;
}

print $res, "\n";

my $end = rss;

if (($end - $start) / $start > 1.5) {
    die "leaked: $start .. $end";
}

