#!/usr/bin/perl
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Copyright (C) 2008 Vino Fernando Crescini
# Google Treasure Hunt 2008 Puzzle 4
use strict;
use bigint;
# we assume that the file contains a list of prime numbers, one prime per
# line, and that they're sorted in ascending order
sub get_primes
{
my ($lparray, $fname) = @_;
if (!open(FH, "<", $fname))
{
return 0;
}
# instead of just loading the primes, we load the partial sums
while(my $line = )
{
if (!(scalar @$lparray))
{
push(@$lparray, $line);
}
else
{
push(@$lparray, $line + $lparray->[$#{$lparray}]);
}
}
close(FH);
return 1;
}
# naive, obviously
sub is_prime
{
my $num = $_[0];
my $sqrt = int sqrt($num);
if ($num <= 1)
{
return 0;
}
if ($num <= 3)
{
return 1;
}
if (($num % 2) == 0 || ($num % 3) == 0)
{
return 0;
}
for (my $i = 1; 1; $i++)
{
my $t1 = (6 * $i) + 1;
my $t2 = (6 * $i) - 1;
if (($t1 <= $sqrt && ($num % $t1) == 0) || ($t2 <= $sqrt && ($num % $t2) == 0))
{
return 0;
}
if ($t1 > $sqrt && $t2 > $sqrt)
{
return 1;
}
}
}
# return the sum of the first n prime numbers starting from offset
sub sum_primes
{
my ($lparray, $offset, $n) = @_;
return $lparray->[$n + $offset - 1] - ($offset ? $lparray->[$offset - 1] : 0);
}
# find the smallest prime number that can be expressed as the sum of
# A0, A1, A2, ..., An consecutive prime numbers
sub solve
{
my ($lparray, $lnarray, $sum, $verbose) = @_;
my $isum = 0;
my $n;
if (!scalar(@{$lnarray}))
{
return $sum;
}
$n = pop(@$lnarray);
for(my $i = 0, my $stop = 0; $i < ($#{$lparray} - $n) && !$stop; $i++)
{
$isum = sum_primes($lparray, $i, $n);
if (!$sum)
{
# first time
if (is_prime($isum))
{
$verbose && printf(" found prime %s at %s\n", $isum, $n);
if (solve($lparray, $lnarray, $isum, $verbose))
{
# finally!
return $isum;
}
}
}
else
{
# not the first call so we need not check for primality
if ($isum == $sum)
{
$verbose && printf(" matched %s at %s\n", $isum, $n);
if (solve($lparray, $lnarray, $isum, $verbose))
{
return $isum;
}
}
# there is really no point to go on if we exceed the number
if ($isum > $sum)
{
$verbose && printf(" exceeded %s at %s\n", $isum, $n);
$stop = 1;
}
}
}
# if we got here, we've exhausted all the primes
# so put it back
$verbose && printf(" no match for %s at %s\n", $isum, $n);
push(@$lnarray, $n);
return 0;
}
my @parray;
my @narray;
if (@ARGV < 2)
{
printf(STDERR "usage: $0 [ [...]]\n");
exit 1;
}
for (my $i = 0; $i < @ARGV - 1; $i++)
{
if ($ARGV[$i + 1] !~ m/^[1-9][0-9]*$/)
{
printf(STDERR "\"%s\" is not a positive integer\n", $ARGV[$i + 1]);
exit 2;
}
push(@narray, int $ARGV[$i + 1]);
}
if (!get_primes(\@parray, $ARGV[0]))
{
printf(STDERR "can't open \"%s\" for reading\n", $ARGV[0]);
exit 3;
}
@narray = sort { $a <=> $b } @narray;
printf("%s\n", solve(\@parray, \@narray, 0, ($ENV{'DEBUG'} != 0)));
exit 0;