The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

# Copyright 2012, 2013 Kevin Ryde

# This file is part of Math-PlanePath-Toothpick.
#
# Math-PlanePath-Toothpick 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, or (at your option) any
# later version.
#
# Math-PlanePath-Toothpick 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 Math-PlanePath-Toothpick.  If not, see <http://www.gnu.org/licenses/>.


# Check that OEIS A-numbers listed in lib/Math/PlanePath/Foo.pm files have
# code exercising them in one of the xt/oeis/*-oeis.t scripts.
#
# Check that A-numbers are not duplicated among the .pm files, since that's
# often a cut-and-paste mistake.
#
# Check that A-numbers are not duplicated among xt/oeis/*-oeis.t scripts,
# since normally only need to exercise a claimed path sequence once.  Except
# often that's not true since the same sequence can arise in separate ways.
# But for now demand duplication is explicitly listed here.
#


use 5.005;
use strict;
use FindBin;
use ExtUtils::Manifest;
use File::Spec;
use File::Slurp;
use Test::More;
use List::MoreUtils;

use lib 't','xt';
use MyTestHelpers;
BEGIN { MyTestHelpers::nowarnings() }

# uncomment this to run the ### lines
# use Smart::Comments;


# new in 5.6, so unless got it separately with 5.005
plan tests => 1;

my $toplevel_dir = File::Spec->catdir ($FindBin::Bin, File::Spec->updir);
my $manifest_file = File::Spec->catfile ($toplevel_dir, 'MANIFEST');
my $manifest = ExtUtils::Manifest::maniread ($manifest_file);
my $bad = 0;


my $RE_OEIS_anum = qr/A\d{6,7}/;


#------------------------------------------------------------------------------

my %path_seq_anums;
require 'lib/Math/NumSeq/OEIS/Catalogue/Plugin/PlanePathToothpick.pm';
my $aref = Math::NumSeq::OEIS::Catalogue::Plugin::PlanePathToothpick->info_arrayref;
foreach my $info (@$aref) {
  my $anum = $info->{'anum'};
  my %parameters = @{$info->{'parameters'}};
  my $planepath = $parameters{'planepath'}
    || die "Oops, no planepath parameter";
  my ($path_name, @path_args) = split /,/, $planepath;
  push @{$path_seq_anums{$path_name}}, $anum;
}
foreach (values %path_seq_anums) {
  $_ = [  List::MoreUtils::uniq(@$_) ];
}

#------------------------------------------------------------------------------

my @module_filenames
  = grep {m{^lib/Math/PlanePath/[^/]+\.pm$}} keys %$manifest;
@module_filenames = sort @module_filenames;
diag "module count ",scalar(@module_filenames);
my @path_names = map {m{([^/]+)\.pm$}
                        or die "Oops, unmatched module filename $_";
                      $1} @module_filenames;

sub path_pod_anums {
  my ($path_name) = @_;
  my $filename = "lib/Math/PlanePath/$path_name.pm";
  open my $fh, '<', $filename
    or die "Oops, cannot open module filename $filename";
  my @ret;
  while (<$fh>) {
    if (/^ +($RE_OEIS_anum)/) {
      push @ret, $1;
    }
  }
  return @ret;
}

sub path_checked_anums {
  my ($path_name) = @_;
  return (path_xt_anums ($path_name),
          @{$path_seq_anums{$path_name} || []});
}
sub path_xt_anums {
  my ($path_name) = @_;
  my @ret;
  if (open my $fh, '<', "xt/oeis/$path_name-oeis.t") {
    while (<$fh>) {
      if (/^[^#]*\$anum = '($RE_OEIS_anum)'/mg) {
        push @ret, $1;
      }
      if (/^[^#]*anum => '($RE_OEIS_anum)'/mg) {
        push @ret, $1;
      }
    }
  }
  return @ret;
}

sub str_duplicates {
  my %seen;
  return map {$seen{$_}++ == 1 ? ($_) : ()} @_;
}

foreach my $path_name (@path_names) {
  my @pod_anums = path_pod_anums ($path_name);
  my @checked_anums = path_checked_anums ($path_name);

  my %pod_anums = map {$_=>1} @pod_anums;
  my %checked_anums = map {$_=>1} @checked_anums;

  foreach my $anum (str_duplicates(@pod_anums)) {
    diag "Math::PlanePath::$path_name duplicate pod $anum";
  }
  @pod_anums = List::MoreUtils::uniq(@pod_anums);

  foreach my $anum (str_duplicates(@checked_anums)) {
    next if $anum eq 'A000012'; # all ones
    next if $anum eq 'A000027'; # 1,2,3 naturals
    next if $anum eq 'A005408'; # odd 2n+1
    diag "Math::PlanePath::$path_name duplicate check $anum";
  }
  @checked_anums = List::MoreUtils::uniq(@checked_anums);
  diag "";

  foreach my $anum (@pod_anums) {
    if (! exists $checked_anums{$anum}) {
      diag "Math::PlanePath::$path_name pod anum $anum not checked";
    }
  }

  foreach my $anum (@checked_anums) {
    next if $anum eq 'A000004'; # all zeros
    next if $anum eq 'A000012'; # all ones
    next if $anum eq 'A001477'; # integers 0,1,2,3
    next if $anum eq 'A001489'; # negative integers 0,-1,-2,-3
    next if $anum eq 'A081274'; # oeis duplicate
    next if $anum eq 'A000035'; # 0,1 reps
    next if $anum eq 'A059841'; # 1,0 reps
    next if $anum eq 'A165211'; # 0,1,0,1, 1,0,1,0, repeating
    if (! exists $pod_anums{$anum}) {
      diag "Math::PlanePath::$path_name checked anum $anum not in pod";
    }
  }
}
is ($bad, 0);

#------------------------------------------------------------------------------

exit 0;