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, 2015 Kevin Ryde

# This file is part of Math-PlanePath.
#
# Math-PlanePath 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 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.  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;
foreach my $seq_filename ('lib/Math/NumSeq/PlanePathCoord.pm',
                          'lib/Math/NumSeq/PlanePathN.pm',
                          'lib/Math/NumSeq/PlanePathDelta.pm',
                          'lib/Math/NumSeq/PlanePathTurn.pm',
                         ) {
  open my $fh, '<', $seq_filename or die "Cannot open $seq_filename";
  while (<$fh>) {
    if (/^\s*# OEIS-(Catalogue|Other): +(A\d+)([^#]+)/) {
      my $anum = $2;
      my @args = split /\s/, $3;
      my %args = map { split /=/, $_, 2 } @args;
      ### %args
      my $planepath = $args{'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) {
    next if $anum eq 'A191689'; # CCurve fractal dimension
    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;