use v6-alpha;

# See http://perlmonks.org/?node_id=440531 and 441347 for details
# Produces an infinite streams of merged primes that is evaluated lazily

# original code (http://www.perlmonks.org/index.pl?node_id=453215)
sub lazy_merge1 (@list is copy) returns Ref {
    my $last = 0;

    my $by_n = sub { my ($n, $k) = (shift(@_), 0); return sub { @_[0] ?? $k += $n !! $k } };
    for @list { $_ = $by_n( $_ ) }

    return sub {
        my $low;
        for ( @list ) {
            my $val = $_();
            $val = $_( 'next' ) if $val <= $last;
            $low = $val if (! defined $low) || ($val < $low);
        }
        return $last = $low;
    };
}

# further perl6-ification (http://www.perlmonks.org/index.pl?node_id=453402)
sub lazy_merge2 (@list is copy) returns Sub {
    my $last = 0;

    my $by_n = sub ($n) { my $k = 0; return -> $x { $x ?? $k += $n !! $k } };
    @list = @list.map:{ $by_n( $_ ) };

    return sub {
        my $low;
        for ( @list ) -> $sub {
            my $val = $sub();
            $val = $sub(<next>) if $val <= $last;
            $low = $val if !$low.defined || $val < $low;
        }
        return $last = $low;
    };
}

# And again, golfing a bit as well (http://perlmonks.org/?node_id=463610)
sub lazy_merge3 (@list is copy) returns Sub {
    my $last = 0;

    my &by_n = -> $n { my $k = 0; -> $x { $x ?? $k += $n !! $k } };
    @list .= map:{ by_n $_ };

    sub {
        my $low;
        for @list -> $sub {
            my $val = $sub();
            $val = $sub(<next>) if $val <= $last;
            $low = $val unless $low && $low < $val;
        }
        $last = $low;
    }
}

my $end   = @*ARGS[0] // 22;
my @prime = (2, 3, 5);
my &next1 = lazy_merge1( @prime );
my &next2 = lazy_merge2( @prime );
my &next3 = lazy_merge3( @prime );
for 1..$end { say "v1: {next1}    v2: {next2}    v3: {next3}" };
