#!/usr/bin/env perl
use warnings;
use strict;
use Math::Prime::Util qw/:all/;
use Math::GMPz;

# Sum of all n where is_power(euler_phi(n^2),3) = 1

# Simple but very slow way.  The brute force method later in this file is
# basically the same thing, but using the more efficient ranged moebius and
# totient calls over intervals.
#
#  Pari:
#    s=0; for(n=2,limit,if(ispower(n*eulerphi(n),3),s=s+n)); print(s)
#  Perl/MPU:
#    my $s=0;
#    for my $n (2..$limit) { $s += $n if is_power($n*euler_phi($n),3); }
#    say $s;
#
# TIMING:
#              10^7    2*10^7   10^8     10^10
# Clever       0.06s    0.09s   0.24s    5s
# Brute        5.0s    10.2s   52.9s     5 hours
# Simple MPU  10.8s    24.6s  159s       1 day?
# Simple Pari 13.6s    33.4s  277s       5 days?
#

my $limit = shift || 10**10-1;
my $method = lc(shift || 'clever');
die "Method must be 'clever' or 'brute'\n" unless $method =~ /^(clever|brute)$/;
my $sum = 0;


if ($method eq 'clever') {
  # About 5 seconds for 10^10-1
  my $cblimit = int( ($limit*$limit) ** 0.3334 + 0.01 );
  foreach my $k (2 .. $cblimit) {
    next if $k & 1;
    my($p, $e) = @{ (factor_exp($k))[-1] };
    $e *= 3;
    next unless $e & 1;
    my $m = int($k / ($p ** int($e/3)));
    $m **= 3;
    next if $m % ($p-1);
    $m = int($m / ($p-1));
    my $n = $p ** (($e+1) >> 1);
    next if $n >= $limit;
    while ($m > 1) {
      my ($p,$e) = @{ (factor_exp($m))[-1] };
      last unless $e & 1;
      last if $m % ($p-1);
      $n *= $p ** (($e+1) >> 1);
      last if $n >= $limit;
      $m = int($m / ( ($p-1) * ($p**$e) ) );
    }
    if ($m == 1) {
      #print "$n\n";
      $sum += $n;
    }
  }
} else {
  # About 5 hours for 10^10-1
  my $interval = 10_000_000;   # Window size for moebius/totient
  #prime_precalc(10**9);        # Slightly faster ranged phi
  my($beg,$end) = (0,0);
  while ($beg < $limit) {
    $end = $beg + $interval - 1;
    $end = $limit if $end > $limit;
    my $start = ($beg<2)?2:$beg;

    my $glim = int(~0 / $end);
    my @m = moebius($beg, $end);
    my @t = euler_phi($beg, $end);

    if ($end <= $glim) {   # Totient($n) * $n will always be < ~0
      foreach my $n ($start .. $end) {
        next unless $m[$n-$beg] == 0;
        my $totn2 = $n * $t[$n-$beg];
        if (is_power($totn2,3)) {
          # print "$n\n";
          $sum += $n
        }
      }
    } else {
      foreach my $n ($start .. $end) {
        next unless $m[$n-$beg] == 0;
        my $tot = $t[$n-$beg];
        if ($tot <= $glim) {
          print "$n\n" if is_power($n * $tot, 3);
        } else {
          $tot = Math::GMPz->new($n) * $tot;
          print "$n\n" if Math::GMPz::Rmpz_perfect_power_p($tot) && is_power($tot,3);
        }
      }
    }
    $beg = $end+1;
  }
}
print "$sum\n";
