#!/usr/bin/env perl6
use v6;
=NAME t2-code
=AUTHOR Yary Hluchan
=SYNOPSIS
t2-code R
=begin DESCRIPTION
Shows first N sums of 2 cubes, where N is given on the command line.
This program is a solution to Task 2 of the 2011 Perl6 Code Contest.
It does this by building finite streams, S₁, S₂, S₃... where Sq
represents the sums q³+1³, q³+2³, q³+3³...q³+q³
By consuming the least sum from each stream, and creating a new stream
when consuming the first element of the greatest stream, it creates a
never-decreasing stream of sums.
=end DESCRIPTION
#= Represent a sum of cubes in a human-readable string vs. a numeric result
class sum_of_cubes {
has Int $computed;
has Str $readable;
# Rely on the caller to tell us the values
# All we do is return the result in numeric context, and the
# calculation in string context.
method new(:$computed, :$q, :$r) {
self.bless(*,
computed=> $computed,
readable=> "$q ** 3 + $r ** 3"
)
}
# I'd like to use a "blessall" method, submethod BUILD is not handsome
submethod BUILD(:$!computed, :$!readable ) { }
method Str { $readable }
method Numeric { $computed }
}
#= Stream of sums of cubes q³+1³, q³+2³
class sum_of_cubes_stream {
# I would like to implement this as an iterator, but the array is so easy.
# Sorry for thinking in P5 :-/
has Int $.q;
has @stream_of_sums;
# Took me some time on #perl6 to learn how to initialize both
# public and private attributes, without using named attributes
# in "new". I hope "blessall" becomes reality soon.
submethod BUILD(:$!q, :@!stream_of_sums) { }
method new(Int $q) {
self.bless(*,
:q( $q ),
stream_of_sums => (1..$q) »**» 3 »+» $q**3
)
}
#= Have we consumed anything from this stream yet?
method is_fresh { @stream_of_sums == $.q };
#= Peek at the current sum from this stream- or 0 if empty
method current { @stream_of_sums[0] // 0 };
#= Get the current element and prime for the next one
method consume {
# Here, "r" relies on arguments being evaluated left-to-right
sum_of_cubes.new(
computed=>shift(@stream_of_sums),
:q($.q),
:r($.q-@stream_of_sums)
)
}
}
# I would like to use this declaration, not yet impletmented.
# sub MAIN(Int $count_to_find);
sub MAIN(Int $count_to_find is rw) {
# Initialize streams collection with 1³+1³
my @streams=sum_of_cubes_stream.new(1);
while ($count_to_find) {
my $min_sum = @streams.map({.current}).min;
my @result = @streams.grep({.current == $min_sum}).map: {.consume}
if @result > 1 {
say (+@result[0],@result).join: ' = ';
--$count_to_find;
}
# Add a new stream if the last stream is not fresh
unless @streams[*-1].is_fresh {
push @streams,sum_of_cubes_stream.new(1 + @streams[*-1].q);
}
# Remove any empty streams from consideration,
# by keeping only streams with a value
@streams=@streams.grep: {.current}
}
}