#!/usr/bin/perl
use 5.016;
use warnings;

package _Stream;

sub new {
	my $class = shift;
	return bless [ @_ ], $class;
}

sub value { $_[0]->[0] }
sub next { ref $_[0]->[1] eq 'CODE' ? $_[0]->[1] = $_[0]->[1]->() : $_[0]->[1] }

sub take {
	my ($f, $n) = @_;

	my @list;
	while(defined $n ? (--$n >= 0) : defined $f->value){
		push @list, $f->value;
		$f = $f->next;
	}

	return \@list;
}

package main;

my @dir = ([1, 0], [0, 1], [-1, 0], [0, -1]);

sub f_next {
	my ($x, $y, $length, $c, $s, $d) = @_;

	if ($c < 1){
		if (--$s < 1){
			$length++;
			$s = 2;
		}
		$c = $length;
		$d = ($d + 1) % 4;
	}

	my ($dx, $dy) = @{$dir[$d]};

	return _Stream->new(
		[$x, $y],
		sub{ f_next($x + $dx, $y + $dy, $length, $c - 1, $s, $d) }
	);
}

sub f { f_next(0, 0, 1, 1, 2, 0) }

sub g_next {
	my ($n, $f, $filter) = @_;

	while(($n > 0) and not $filter->(@{$f->value})){
		$f = $f->next;
		--$n;
	}
	($n > 0) or return _Stream->new(undef, sub{});

	return _Stream->new(
		$f->value,
		sub{ g_next($n - 1, $f->next, $filter) },
	);
}

sub g {
	my ($r) = @_;

	my $n = (2 * $r + 1) ** 2;
	my $filter = sub{ $_[0] ** 2 + $_[1] ** 2 <= $r ** 2 };
	return g_next($n, f(), $filter);
}

use Data::Dumper;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Indent = 0;

say Dumper(f()->take(15));
say Dumper(g(2)->take);
