The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
use strict;
use warnings;

use Test::More;
use Math::Prime::Util qw/moebius mertens/;

my $extra = defined $ENV{EXTENDED_TESTING} && $ENV{EXTENDED_TESTING};
my $usexs = Math::Prime::Util::prime_get_config->{'xs'};
my $usegmp= Math::Prime::Util::prime_get_config->{'gmp'};
my $use64 = Math::Prime::Util::prime_get_config->{'maxbits'} > 32;
$use64 = 0 if $use64 && 18446744073709550592 == ~0;

my %mertens = (
        1 =>    1,
        2 =>    0,
        3 =>   -1,
        4 =>   -1,
        5 =>   -2,
       10 =>   -1,
      100 =>    1,
     1000 =>    2,
    10000 =>  -23,
        8 =>   -2,
       16 =>   -1,
       32 =>   -4,
       64 =>   -1,
      128 =>   -2,
      256 =>   -1,
      512 =>   -4,
     1024 =>   -4,
     2048 =>    7,
     4096 =>  -19,
     8192 =>   22,
);
my %big_mertens = (
   100000 =>  -48,
  1000000 =>  212,
 10000000 => 1037,
);
delete $big_mertens{10000000} unless $extra || $usexs;

if ($extra && $use64) {
  %big_mertens = ( %big_mertens,
          2 =>  0,      # A087987, mertens at primorials
          6 => -1,
         30 => -3,
        210 => -1,
       2310 => -1,
      30030 => 16,
     510510 => -25,
    9699690 => 278,
  223092870 => 3516,

    6433477 => 900,     # 30^2
  109851909 => -4096,   # A084235, 2^12

      2**14 =>  -32,    # A084236
      2**15 =>   26,
      2**16 =>   14,
      2**17 =>  -20,
      2**18 =>   24,
      2**19 => -125,
      2**20 =>  257,
      2**21 => -362,
      2**22 =>  228,
      2**23 =>  -10,

     10**8  => 1928,
#     10**9  => -222,
#  1*10**10 => -33722,  # From Deleglise and Rivat
#  2*10**10 => -48723,  # Too slow with current method
  );
}
# These are slow with XS, and *really* slow with PP.
if (!$usexs) {
  %big_mertens = map { $_ => $big_mertens{$_} }
                 grep { $_ < 100000000 }
                 keys %big_mertens;
}

plan tests => 1 + 5 + 2 + 3 + scalar(keys %big_mertens);

ok(!eval { moebius(0); }, "moebius(0)");

is_deeply( [map { moebius($_) } 1 .. 20],
           [1,-1,-1,0,-1,1,-1,0,0,1,-1,0,-1,1,1,0,-1,0,-1,0],
           "moebius 1 .. 20 (single)" );
is_deeply( [moebius(1,20)],
           [1,-1,-1,0,-1,1,-1,0,0,1,-1,0,-1,1,1,0,-1,0,-1,0],
           "moebius 1 .. 20 (range)" );
# moebius uses |n| so negative inputs reflect about zero.
is_deeply( [map { moebius(-$_) } 1 .. 20],
           [1,-1,-1,0,-1,1,-1,0,0,1,-1,0,-1,1,1,0,-1,0,-1,0],
           "moebius -1 .. -20 (single)" );
is_deeply( [moebius(-14,-9)],
           [1,-1,0,-1,1,0],
           "moebius -14 .. -9 (range)" );
is_deeply( [moebius(-7,5)],
           [-1,1,-1,0,-1,-1,1,0,1,-1,-1,0,-1],
           "moebius -7 .. 5 (range)" );

is( moebius(3*5*7*11*13), -1, "moebius(3*5*7*11*13) = -1" );
is( moebius("20364840299624512075310661735"), 1, "moebius(73#/2) = 1" );

{
  my(@mert_sum1, @mert_sum2, @mertens, @expect, $M);
  while (my($n, $val) = each (%mertens)) {
    $M = 0;  $M += moebius($_) for 1 .. $n;
    push @mert_sum1, $M;
    $M = 0;  $M += $_ for moebius(1,$n);
    push @mert_sum2, $M;
    push @mertens, mertens($n);
    push @expect, $val;
  }
  is_deeply( \@mert_sum1, \@expect, "sum(moebius(k) for k=1..n)   small n" );
  is_deeply( \@mert_sum2, \@expect, "sum(moebius(1,n))   small n" );
  is_deeply( \@mertens, \@expect, "mertens(n)   small n" );
}
while (my($n, $mertens) = each (%big_mertens)) {
  is( mertens($n), $mertens, "mertens($n)" );
}