#!/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;