package Math::Prime::Util::PPFE;
use strict;
use warnings;
use Math::Prime::Util::PP;
use Math::Prime::Util::Entropy;

# The PP front end, only loaded if XS is not used.
# It is intended to load directly into the MPU namespace.

package Math::Prime::Util;
use Carp qw/carp croak confess/;
use Scalar::Util qw/reftype/;

*_validate_integer = \&Math::Prime::Util::PP::_validate_integer;
*_validate_integer_nonneg = \&Math::Prime::Util::PP::_validate_integer_nonneg;
*_validate_integer_positive = \&Math::Prime::Util::PP::_validate_integer_positive;
*_validate_integer_abs = \&Math::Prime::Util::PP::_validate_integer_abs;

*_canonicalized_integer = \&Math::Prime::Util::PP::_canonicalized_integer;

sub _canonicalize_integers {
  my($r) = @_;
  my $type = reftype($r);
  if (defined $type && $type eq 'ARRAY') {

    for my $i (0..$#$r) {
      next unless exists $r->[$i] && defined $r->[$i];
      _validate_integer($r->[$i]);
      $r->[$i] = _to_bigint($r->[$i]) if ref($r->[$i]);
    }

  } elsif (defined $type && ($type eq 'SCALAR' || $type eq 'REF')) {

    if (defined $$r) {
      # Later we can optimize to do only what we need.
      _validate_integer($$r);
      $$r = _to_bigint($$r) if ref($$r);
    }

  } else {
    croak "_canonicalize_integers: expected scalar or array reference";
  }
  return;  # Explicitly return nothing
}

*_prime_memfreeall = \&Math::Prime::Util::PP::_prime_memfreeall;
*prime_memfree  = \&Math::Prime::Util::PP::prime_memfree;
*prime_precalc  = \&Math::Prime::Util::PP::prime_precalc;

use Math::Prime::Util::ChaCha;
*_is_csprng_well_seeded = \&Math::Prime::Util::ChaCha::_is_csprng_well_seeded;
*_csrand = \&Math::Prime::Util::ChaCha::csrand;
*_srand = \&Math::Prime::Util::ChaCha::srand;
*random_bytes = \&Math::Prime::Util::ChaCha::random_bytes;
*irand = \&Math::Prime::Util::ChaCha::irand;
*irand32 = \&Math::Prime::Util::ChaCha::irand32;

if (MPU_MAXBITS == 32) {
  *irand64 = sub {
    my ($hi, $lo) = unpack("V2", Math::Prime::Util::ChaCha::random_bytes(8));
    return $lo unless $hi;  # Return native if possible
    Math::Prime::Util::PP::_frombytes(pack("N2", $hi, $lo));
  };
} else {
  *irand64 = \&Math::Prime::Util::ChaCha::irand64;
}

sub srand {
  croak "srand: expected zero or one argument" if @_ > 1;
  my $has_seed = @_ > 0;
  my $seed = $has_seed ? $_[0] : undef;
  croak "secure option set, manual seeding disabled" if prime_get_config()->{'secure'};
  if (!$has_seed) {
    my $nbytes = (~0 == 4294967295) ? 4 : 8;
    $seed = entropy_bytes( $nbytes );
    $seed = unpack(($nbytes==4) ? "L" : "Q", $seed);
  } else {
    $seed = 0 if !defined $seed || (!ref($seed) && $seed eq '');
    my $uvpack = MPU_MAXBITS == 32 ? "L" : "Q";
    $seed = unpack($uvpack, pack($uvpack, $seed));
  }
  my $seedstr;
  if ($seed <= 4294967295) {
    $seedstr = pack("V", $seed);
  } else {
    my($hi,$lo) = divrem($seed, 4294967296);
    $seedstr = pack("V2", $lo, $hi);
  }
  if ($Math::Prime::Util::_GMPfunc{"seed_csprng"}) {
    Math::Prime::Util::GMP::seed_csprng(length($seedstr), $seedstr);
  }
  Math::Prime::Util::_csrand($seedstr);
  $seed;
}
sub csrand {
  croak "csrand: expected zero or one argument" if @_ > 1;
  my($seed) = @_;
  croak "secure option set, manual seeding disabled" if defined $seed && prime_get_config()->{'secure'};
  if (defined $seed) {
    Math::Prime::Util::GMP::seed_csprng(length($seed),$seed)
      if $Math::Prime::Util::_GMPfunc{"seed_csprng"};
  } else {
    $seed = entropy_bytes(64);
    if ($Math::Prime::Util::_GMPfunc{"seed_csprng"}) {
      my $gmpseed = entropy_bytes(64);
      Math::Prime::Util::GMP::seed_csprng(length($gmpseed),$gmpseed);
    }
  }
  Math::Prime::Util::_csrand($seed);
  return;
}
sub CLONE {
  Math::Prime::Util::Entropy::_clear_method();
  Math::Prime::Util::_csrand(entropy_bytes(64));
}
sub entropy_bytes {
  my($bytes) = @_;
  my $n = defined $bytes ? "$bytes" : "";
  croak "entropy_bytes: input must be an integer between 0 and 2147483646"
      if $n !~ /^\+?\d+\z/ || 0+$n > 2147483646;
  $n = 0+$n;
  return '' if $n == 0;
  my $data = Math::Prime::Util::Entropy::entropy_bytes($n);
  if (!defined $data) {
    my $seed = Math::Prime::Util::Entropy::_timer_seed();
    Math::Prime::Util::_csrand($seed);
    $data = random_bytes($n);
  }
  croak "entropy_bytes internal got wrong amount!" unless length($data) == $n;
  $data;
}

# Fill all the mantissa bits for our NV, regardless of 32-bit or 64-bit Perl.
{
  use Config;
  my $nvbits = (defined $Config{nvmantbits})  ? $Config{nvmantbits}
             : (defined $Config{usequadmath}) ? 112
             : $Config{nvsize} == 32 ? 236
             : $Config{nvsize} == 16 ? 112
             : $Config{nvsize} ==  8 ?  52
             : $Config{nvsize} ==  4 ?  23
             : 52;
  my $nvdigits = int(($nvbits+1) / 3.322);
  my $uvbits = (~0 > 4294967295) ? 64 : 32;
  my $rsub;
  my $_tonv_32  = 1.0;        $_tonv_32 /= 2.0 for 1..32;
  my $_tonv_64  = $_tonv_32;  $_tonv_64 /= 2.0 for 1..32;
  my $_tonv_96  = $_tonv_64;  $_tonv_96 /= 2.0 for 1..32;
  my $_tonv_128 = $_tonv_96;  $_tonv_128/= 2.0 for 1..32;
  if ($uvbits == 64) {
    if ($nvbits <= 32) {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do { $d = irand() * $_tonv_32; } while $d >= 1.0;
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    } elsif ($nvbits <= 64) {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do { $d = irand64() * $_tonv_64; } while $d >= 1.0;
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    } else {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do {
          $d = irand64() * $_tonv_64 + irand64() * $_tonv_128;
        } while $d >= 1.0;
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    }
  } else {
    if ($nvbits <= 32) {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do { $d = irand() * $_tonv_32; } while $d >= 1.0;
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    } elsif ($nvbits <= 64) {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do {
          my $a = irand();
          my $b = irand();
          $d = $a * $_tonv_32 + $b * $_tonv_64;
        } while ($d >= 1.0);
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    } else {
      *drand = sub {
        croak "drand: expected zero or one argument" if @_ > 1;
        my $d;
        do {
          $d = irand() * $_tonv_32 + irand() * $_tonv_64
             + irand() * $_tonv_96 + irand() * $_tonv_128;
        } while $d >= 1.0;
        $d *= $_[0] if defined($_[0]) && $_[0] != 0;
        $d;
      };
    }
  }
  *rand = \&drand;

  { my $ivsize = $Config{ivsize};   *_ivsize = sub { return $ivsize; }; }
  { my $uvsize = $Config{uvsize};   *_uvsize = sub { return $uvsize; }; }
  { my $uvbits = $Config{uvsize}*8; *_uvbits = sub { return $uvbits; }; }
  { my $nvsize = $Config{nvsize};   *_nvsize = sub { return $nvsize; }; }
  *_nvmantbits = sub { return $nvbits; };
  *_nvmantdigits = sub { return $nvdigits; };
}


*urandomb = \&Math::Prime::Util::PP::urandomb;
*urandomm = \&Math::Prime::Util::PP::urandomm;
*urandomr = \&Math::Prime::Util::PP::urandomr;
*toint    = \&Math::Prime::Util::PP::toint;

*sumdigits = \&Math::Prime::Util::PP::sumdigits;
*reverse_digits = \&Math::Prime::Util::PP::reverse_digits;
*todigits = \&Math::Prime::Util::PP::todigits;
*todigitstring = \&Math::Prime::Util::PP::todigitstring;
*fromdigits = \&Math::Prime::Util::PP::fromdigits;
*inverse_li = \&Math::Prime::Util::PP::inverse_li;
*inverse_li_nv = \&Math::Prime::Util::PP::inverse_li_nv;
*sieve_prime_cluster = \&Math::Prime::Util::PP::sieve_prime_cluster;
*sieve_range = \&Math::Prime::Util::PP::sieve_range;
*lucky_numbers = \&Math::Prime::Util::PP::lucky_numbers;
*powerful_numbers = \&Math::Prime::Util::PP::powerful_numbers;

*prime_count = \&Math::Prime::Util::PP::prime_count;
*prime_power_count = \&Math::Prime::Util::PP::prime_power_count;
*twin_prime_count = \&Math::Prime::Util::PP::twin_prime_count;
*semiprime_count = \&Math::Prime::Util::PP::semiprime_count;
*almost_prime_count = \&Math::Prime::Util::PP::almost_prime_count;
*omega_prime_count = \&Math::Prime::Util::PP::omega_prime_count;
*ramanujan_prime_count = \&Math::Prime::Util::PP::ramanujan_prime_count;
*lucky_count = \&Math::Prime::Util::PP::lucky_count;
*smooth_count = \&Math::Prime::Util::PP::smooth_count;
*rough_count = \&Math::Prime::Util::PP::rough_count;

*sum_primes = \&Math::Prime::Util::PP::sum_primes;
*print_primes = \&Math::Prime::Util::PP::print_primes;

*is_prime           = \&Math::Prime::Util::PP::is_prime;
*is_prob_prime      = \&Math::Prime::Util::PP::is_prob_prime;
*is_provable_prime  = \&Math::Prime::Util::PP::is_provable_prime;
*is_bpsw_prime      = \&Math::Prime::Util::PP::is_bpsw_prime;
*is_aks_prime       = \&Math::Prime::Util::PP::is_aks_prime;
*is_mersenne_prime  = \&Math::Prime::Util::PP::is_mersenne_prime;
*is_ramanujan_prime = \&Math::Prime::Util::PP::is_ramanujan_prime;

*is_pseudoprime = \&Math::Prime::Util::PP::is_pseudoprime;
*is_euler_pseudoprime = \&Math::Prime::Util::PP::is_euler_pseudoprime;
*is_strong_pseudoprime = \&Math::Prime::Util::PP::is_strong_pseudoprime;
*is_euler_plumb_pseudoprime = \&Math::Prime::Util::PP::is_euler_plumb_pseudoprime;
*is_perrin_pseudoprime = \&Math::Prime::Util::PP::is_perrin_pseudoprime;
*is_frobenius_pseudoprime = \&Math::Prime::Util::PP::is_frobenius_pseudoprime;
*is_catalan_pseudoprime = \&Math::Prime::Util::PP::is_catalan_pseudoprime;
*is_frobenius_underwood_pseudoprime = \&Math::Prime::Util::PP::is_frobenius_underwood_pseudoprime;
*is_frobenius_khashin_pseudoprime = \&Math::Prime::Util::PP::is_frobenius_khashin_pseudoprime;
*is_lucas_pseudoprime = \&Math::Prime::Util::PP::is_lucas_pseudoprime;
*is_strong_lucas_pseudoprime = \&Math::Prime::Util::PP::is_strong_lucas_pseudoprime;
*is_extra_strong_lucas_pseudoprime = \&Math::Prime::Util::PP::is_extra_strong_lucas_pseudoprime;
*is_almost_extra_strong_lucas_pseudoprime = \&Math::Prime::Util::PP::is_almost_extra_strong_lucas_pseudoprime;

*is_cyclic = \&Math::Prime::Util::PP::is_cyclic;
*is_carmichael = \&Math::Prime::Util::PP::is_carmichael;
*is_quasi_carmichael = \&Math::Prime::Util::PP::is_quasi_carmichael;
*is_practical = \&Math::Prime::Util::PP::is_practical;
*is_pillai = \&Math::Prime::Util::PP::is_pillai;
*is_fundamental = \&Math::Prime::Util::PP::is_fundamental;
*is_semiprime = \&Math::Prime::Util::PP::is_semiprime;
*is_almost_prime = \&Math::Prime::Util::PP::is_almost_prime;
*is_chen_prime = \&Math::Prime::Util::PP::is_chen_prime;
*is_omega_prime = \&Math::Prime::Util::PP::is_omega_prime;
*is_totient = \&Math::Prime::Util::PP::is_totient;
*is_square = \&Math::Prime::Util::PP::is_square;
*is_prime_power = \&Math::Prime::Util::PP::is_prime_power;
*is_lucky = \&Math::Prime::Util::PP::is_lucky;
*is_gaussian_prime = \&Math::Prime::Util::PP::is_gaussian_prime;
*is_polygonal = \&Math::Prime::Util::PP::is_polygonal;
*is_smooth = \&Math::Prime::Util::PP::is_smooth;
*is_rough = \&Math::Prime::Util::PP::is_rough;
*is_perfect_power = \&Math::Prime::Util::PP::is_perfect_power;
*is_powerful = \&Math::Prime::Util::PP::is_powerful;
*is_odd = \&Math::Prime::Util::PP::is_odd;
*is_even = \&Math::Prime::Util::PP::is_even;
*is_divisible = \&Math::Prime::Util::PP::is_divisible;
*is_congruent = \&Math::Prime::Util::PP::is_congruent;
*is_congruent_number = \&Math::Prime::Util::PP::is_congruent_number;
*is_perfect_number = \&Math::Prime::Util::PP::is_perfect_number;
*is_delicate_prime = \&Math::Prime::Util::PP::is_delicate_prime;
*is_safe_prime = \&Math::Prime::Util::PP::is_safe_prime;
*is_happy = \&Math::Prime::Util::PP::is_happy;
*is_harshad = \&Math::Prime::Util::PP::is_harshad;
*is_palindrome = \&Math::Prime::Util::PP::is_palindrome;
*powerful_count = \&Math::Prime::Util::PP::powerful_count;
*nth_powerful = \&Math::Prime::Util::PP::nth_powerful;
*sumpowerful = \&Math::Prime::Util::PP::sumpowerful;
*perfect_power_count = \&Math::Prime::Util::PP::perfect_power_count;
*is_power = \&Math::Prime::Util::PP::is_power;
*is_square_free = \&Math::Prime::Util::PP::is_square_free;
*is_sum_of_squares = \&Math::Prime::Util::PP::is_sum_of_squares;
*is_powerfree = \&Math::Prime::Util::PP::is_powerfree;
*powerfree_count = \&Math::Prime::Util::PP::powerfree_count;
*nth_powerfree = \&Math::Prime::Util::PP::nth_powerfree;
*powerfree_sum = \&Math::Prime::Util::PP::powerfree_sum;
*powerfree_part = \&Math::Prime::Util::PP::powerfree_part;
*powerfree_part_sum = \&Math::Prime::Util::PP::powerfree_part_sum;
*squarefree_kernel = \&Math::Prime::Util::PP::squarefree_kernel;
# TODO: Should this do validation here?
*powersum = \&Math::Prime::Util::PP::powersum;
*next_chen_prime = \&Math::Prime::Util::PP::next_chen_prime;

*random_prime = \&Math::Prime::Util::PP::random_prime;
*random_ndigit_prime = \&Math::Prime::Util::PP::random_ndigit_prime;
*random_nbit_prime = \&Math::Prime::Util::PP::random_nbit_prime;
*random_proven_prime = \&Math::Prime::Util::PP::random_maurer_prime; # redir
*random_safe_prime = \&Math::Prime::Util::PP::random_safe_prime;
*random_strong_prime = \&Math::Prime::Util::PP::random_strong_prime;
*random_maurer_prime = \&Math::Prime::Util::PP::random_maurer_prime;
*random_shawe_taylor_prime =\&Math::Prime::Util::PP::random_shawe_taylor_prime;
*miller_rabin_random = \&Math::Prime::Util::PP::miller_rabin_random;
*random_semiprime = \&Math::Prime::Util::PP::random_semiprime;
*random_unrestricted_semiprime = \&Math::Prime::Util::PP::random_unrestricted_semiprime;
*random_factored_integer = \&Math::Prime::Util::PP::random_factored_integer;

*next_prime = \&Math::Prime::Util::PP::next_prime;
*prev_prime = \&Math::Prime::Util::PP::prev_prime;
*next_prime_power = \&Math::Prime::Util::PP::next_prime_power;
*prev_prime_power = \&Math::Prime::Util::PP::prev_prime_power;
*next_perfect_power = \&Math::Prime::Util::PP::next_perfect_power;
*prev_perfect_power = \&Math::Prime::Util::PP::prev_perfect_power;

*numtoperm = \&Math::Prime::Util::PP::numtoperm;
*permtonum = \&Math::Prime::Util::PP::permtonum;
*randperm = \&Math::Prime::Util::PP::randperm;
*shuffle = \&Math::Prime::Util::PP::shuffle;

*lcm = \&Math::Prime::Util::PP::lcm;
*gcdext = \&Math::Prime::Util::PP::gcdext;
*prime_bigomega = \&Math::Prime::Util::PP::prime_bigomega;
*prime_omega = \&Math::Prime::Util::PP::prime_omega;
*moebius = \&Math::Prime::Util::PP::moebius;
*euler_phi = \&Math::Prime::Util::PP::euler_phi;
*inverse_totient = \&Math::Prime::Util::PP::inverse_totient;
*dedekind_psi = \&Math::Prime::Util::PP::dedekind_psi;
*divisor_sum = \&Math::Prime::Util::PP::divisor_sum;
*aliquot_sum = \&Math::Prime::Util::PP::aliquot_sum;
*sumtotient = \&Math::Prime::Util::PP::sumtotient;
*jordan_totient = \&Math::Prime::Util::PP::jordan_totient;
*inverse_sigma0 = \&Math::Prime::Util::PP::inverse_sigma0;
*inverse_sigma0_count = \&Math::Prime::Util::PP::inverse_sigma0_count;
*ramanujan_sum = \&Math::Prime::Util::PP::ramanujan_sum;
*mertens = \&Math::Prime::Util::PP::mertens;
*valuation = \&Math::Prime::Util::PP::valuation;
*remove_factors = \&Math::Prime::Util::PP::remove_factors;
*remove_factors_exp = \&Math::Prime::Util::PP::remove_factors_exp;
*floor_sum = \&Math::Prime::Util::PP::floor_sum;
*hammingweight = \&Math::Prime::Util::PP::hammingweight;
*chinese = \&Math::Prime::Util::PP::chinese;
*chinese2 = \&Math::Prime::Util::PP::chinese2;
*cornacchia = \&Math::Prime::Util::PP::cornacchia;
*primorial = \&Math::Prime::Util::PP::primorial;
*pn_primorial = \&Math::Prime::Util::PP::pn_primorial;
*divisors = \&Math::Prime::Util::PP::divisors;
*partitions = \&Math::Prime::Util::PP::partitions;
*partitionsq = \&Math::Prime::Util::PP::partitionsq;
*consecutive_integer_lcm = \&Math::Prime::Util::PP::consecutive_integer_lcm;
*carmichael_lambda = \&Math::Prime::Util::PP::carmichael_lambda;
*exp_mangoldt = \&Math::Prime::Util::PP::exp_mangoldt;
*liouville = \&Math::Prime::Util::PP::liouville;
*sumliouville = \&Math::Prime::Util::PP::sumliouville;
*frobenius_number = \&Math::Prime::Util::PP::frobenius_number;
*binomial = \&Math::Prime::Util::PP::binomial;
*factorial = \&Math::Prime::Util::PP::factorial;
*multifactorial = \&Math::Prime::Util::PP::multifactorial;
*subfactorial = \&Math::Prime::Util::PP::subfactorial;
*stirling = \&Math::Prime::Util::PP::stirling;
*catalan_number = \&Math::Prime::Util::PP::catalan_number;
*bell_number = \&Math::Prime::Util::PP::bell_number;
*fubini = \&Math::Prime::Util::PP::fubini;
*integer_complexity = \&Math::Prime::Util::PP::integer_complexity;
*falling_factorial = \&Math::Prime::Util::PP::falling_factorial;
*rising_factorial = \&Math::Prime::Util::PP::rising_factorial;

*prime_signature = \&Math::Prime::Util::PP::prime_signature;
*sopfr = \&Math::Prime::Util::PP::sopfr;
*sopf = \&Math::Prime::Util::PP::sopf;
*digital_root = \&Math::Prime::Util::PP::digital_root;
*mult_digital_root = \&Math::Prime::Util::PP::mult_digital_root;
*abundance = \&Math::Prime::Util::PP::abundance;

*chebyshev_theta = \&Math::Prime::Util::PP::chebyshev_theta;
*chebyshev_psi = \&Math::Prime::Util::PP::chebyshev_psi;
*hclassno = \&Math::Prime::Util::PP::hclassno;
*ramanujan_tau = \&Math::Prime::Util::PP::ramanujan_tau;
*legendre_phi = \&Math::Prime::Util::PP::legendre_phi;

*bernfrac = \&Math::Prime::Util::PP::bernfrac;
*harmfrac = \&Math::Prime::Util::PP::harmfrac;
*contfrac = \&Math::Prime::Util::PP::contfrac;
*from_contfrac = \&Math::Prime::Util::PP::from_contfrac;
*convergents = \&Math::Prime::Util::PP::convergents;
*bestrational = \&Math::Prime::Util::PP::bestrational;
*next_calkin_wilf = \&Math::Prime::Util::PP::next_calkin_wilf;
*next_stern_brocot = \&Math::Prime::Util::PP::next_stern_brocot;
*calkin_wilf_n = \&Math::Prime::Util::PP::calkin_wilf_n;
*stern_brocot_n = \&Math::Prime::Util::PP::stern_brocot_n;
*nth_calkin_wilf = \&Math::Prime::Util::PP::nth_calkin_wilf;
*nth_stern_brocot = \&Math::Prime::Util::PP::nth_stern_brocot;
*nth_stern_diatomic = \&Math::Prime::Util::PP::nth_stern_diatomic;
*farey = \&Math::Prime::Util::PP::farey;
*next_farey = \&Math::Prime::Util::PP::next_farey;
*farey_rank = \&Math::Prime::Util::PP::farey_rank;


*addint     = \&Math::Prime::Util::PP::addint;
*subint     = \&Math::Prime::Util::PP::subint;
*add1int    = \&Math::Prime::Util::PP::add1int;
*sub1int    = \&Math::Prime::Util::PP::sub1int;
*lshiftint  = \&Math::Prime::Util::PP::lshiftint;
*rshiftint  = \&Math::Prime::Util::PP::rshiftint;
*rashiftint = \&Math::Prime::Util::PP::rashiftint;
*mulint     = \&Math::Prime::Util::PP::mulint;
*powint     = \&Math::Prime::Util::PP::powint;
*divint     = \&Math::Prime::Util::PP::divint;
*modint     = \&Math::Prime::Util::PP::modint;
*cdivint    = \&Math::Prime::Util::PP::cdivint;
*divrem     = \&Math::Prime::Util::PP::divrem;
*tdivrem    = \&Math::Prime::Util::PP::tdivrem;
*fdivrem    = \&Math::Prime::Util::PP::fdivrem;
*cdivrem    = \&Math::Prime::Util::PP::cdivrem;
*muladdint  = \&Math::Prime::Util::PP::muladdint;
*mulsubint  = \&Math::Prime::Util::PP::mulsubint;
*absint     = \&Math::Prime::Util::PP::absint;
*negint     = \&Math::Prime::Util::PP::negint;
*signint    = \&Math::Prime::Util::PP::signint;
*cmpint     = \&Math::Prime::Util::PP::cmpint;
*sqrtint    = \&Math::Prime::Util::PP::sqrtint;
*rootint    = \&Math::Prime::Util::PP::rootint;
*crootint   = \&Math::Prime::Util::PP::crootint;
*logint     = \&Math::Prime::Util::PP::logint;

*addmod     = \&Math::Prime::Util::PP::addmod;
*submod     = \&Math::Prime::Util::PP::submod;
*mulmod     = \&Math::Prime::Util::PP::mulmod;
*divmod     = \&Math::Prime::Util::PP::divmod;
*powmod     = \&Math::Prime::Util::PP::powmod;
*invmod     = \&Math::Prime::Util::PP::invmod;
*muladdmod  = \&Math::Prime::Util::PP::muladdmod;
*mulsubmod  = \&Math::Prime::Util::PP::mulsubmod;
*negmod     = \&Math::Prime::Util::PP::negmod;
*sqrtmod    = \&Math::Prime::Util::PP::sqrtmod;
*allsqrtmod = \&Math::Prime::Util::PP::allsqrtmod;
*rootmod    = \&Math::Prime::Util::PP::rootmod;
*allrootmod = \&Math::Prime::Util::PP::allrootmod;

*factorialmod   = \&Math::Prime::Util::PP::factorialmod;
*binomialmod    = \&Math::Prime::Util::PP::binomialmod;
*lucas_sequence = \&Math::Prime::Util::PP::lucas_sequence;
*lucasu         = \&Math::Prime::Util::PP::lucasu;
*lucasv         = \&Math::Prime::Util::PP::lucasv;
*lucasuv        = \&Math::Prime::Util::PP::lucasuv;
*lucasumod      = \&Math::Prime::Util::PP::lucasumod;
*lucasvmod      = \&Math::Prime::Util::PP::lucasvmod;
*lucasuvmod     = \&Math::Prime::Util::PP::lucasuvmod;
*fibonacci      = \&Math::Prime::Util::PP::fibonacci;
*lucas_number   = \&Math::Prime::Util::PP::lucas_number;
*pisano_period  = \&Math::Prime::Util::PP::pisano_period;
*znlog          = \&Math::Prime::Util::PP::znlog;
*znorder        = \&Math::Prime::Util::PP::znorder;
*znprimroot     = \&Math::Prime::Util::PP::znprimroot;
*qnr            = \&Math::Prime::Util::PP::qnr;
*is_qr          = \&Math::Prime::Util::PP::is_qr;
*kronecker      = \&Math::Prime::Util::PP::kronecker;
*is_primitive_root = \&Math::Prime::Util::PP::is_primitive_root;

*vecequal       = \&Math::Prime::Util::PP::vecequal;
*vecuniq        = \&Math::Prime::Util::PP::vecuniq;
*vecfreq        = \&Math::Prime::Util::PP::vecfreq;
*vecsingleton   = \&Math::Prime::Util::PP::vecsingleton;
*vecprefixsum   = \&Math::Prime::Util::PP::vecprefixsum;
*vecwindow      = \&Math::Prime::Util::PP::vecwindow;
*vecsample      = \&Math::Prime::Util::PP::vecsample;
*vecsort        = \&Math::Prime::Util::PP::vecsort;
*vecsorti       = \&Math::Prime::Util::PP::vecsorti;
*vecrsort       = \&Math::Prime::Util::PP::vecrsort;
*vecrsorti      = \&Math::Prime::Util::PP::vecrsorti;
*vecmex         = \&Math::Prime::Util::PP::vecmex;
*vecpmex        = \&Math::Prime::Util::PP::vecpmex;
*vecextract     = \&Math::Prime::Util::PP::vecextract;

*setbinop       = \&Math::Prime::Util::PP::setbinop;
*sumset         = \&Math::Prime::Util::PP::sumset;
*setunion       = \&Math::Prime::Util::PP::setunion;
*setintersect   = \&Math::Prime::Util::PP::setintersect;
*setminus       = \&Math::Prime::Util::PP::setminus;
*setdelta       = \&Math::Prime::Util::PP::setdelta;
*setinsert      = \&Math::Prime::Util::PP::setinsert;
*setremove      = \&Math::Prime::Util::PP::setremove;
*setinvert      = \&Math::Prime::Util::PP::setinvert;
*setcontains    = \&Math::Prime::Util::PP::setcontains;
*setcontainsany = \&Math::Prime::Util::PP::setcontainsany;
*toset          = \&Math::Prime::Util::PP::toset;
*is_sidon_set   = \&Math::Prime::Util::PP::is_sidon_set;
*is_sumfree_set = \&Math::Prime::Util::PP::is_sumfree_set;
*set_is_disjoint= \&Math::Prime::Util::PP::set_is_disjoint;
*set_is_equal   = \&Math::Prime::Util::PP::set_is_equal;
*set_is_subset  = \&Math::Prime::Util::PP::set_is_subset;
*set_is_proper_subset       = \&Math::Prime::Util::PP::set_is_proper_subset;
*set_is_superset            = \&Math::Prime::Util::PP::set_is_superset;
*set_is_proper_superset     = \&Math::Prime::Util::PP::set_is_proper_superset;
*set_is_proper_intersection = \&Math::Prime::Util::PP::set_is_proper_intersection;

*tozeckendorf   = \&Math::Prime::Util::PP::tozeckendorf;
*fromzeckendorf = \&Math::Prime::Util::PP::fromzeckendorf;

*goldbach_pairs = \&Math::Prime::Util::PP::goldbach_pairs;
*goldbach_pair_count = \&Math::Prime::Util::PP::goldbach_pair_count;
*minimal_goldbach_pair = \&Math::Prime::Util::PP::minimal_goldbach_pair;

*prime_count_approx = \&Math::Prime::Util::PP::prime_count_approx;
*prime_count_lower = \&Math::Prime::Util::PP::prime_count_lower;
*prime_count_upper = \&Math::Prime::Util::PP::prime_count_upper;
*nth_prime = \&Math::Prime::Util::PP::nth_prime;
*nth_prime_approx = \&Math::Prime::Util::PP::nth_prime_approx;
*nth_prime_lower = \&Math::Prime::Util::PP::nth_prime_lower;
*nth_prime_upper = \&Math::Prime::Util::PP::nth_prime_upper;
*prime_power_count_approx = \&Math::Prime::Util::PP::prime_power_count_approx;
*prime_power_count_lower = \&Math::Prime::Util::PP::prime_power_count_lower;
*prime_power_count_upper = \&Math::Prime::Util::PP::prime_power_count_upper;
*perfect_power_count_approx = \&Math::Prime::Util::PP::perfect_power_count_approx;
*perfect_power_count_lower = \&Math::Prime::Util::PP::perfect_power_count_lower;
*perfect_power_count_upper = \&Math::Prime::Util::PP::perfect_power_count_upper;
*lucky_count_approx = \&Math::Prime::Util::PP::lucky_count_approx;
*lucky_count_lower = \&Math::Prime::Util::PP::lucky_count_lower;
*lucky_count_upper = \&Math::Prime::Util::PP::lucky_count_upper;
*nth_prime_power = \&Math::Prime::Util::PP::nth_prime_power;
*nth_prime_power_approx = \&Math::Prime::Util::PP::nth_prime_power_approx;
*nth_prime_power_lower = \&Math::Prime::Util::PP::nth_prime_power_lower;
*nth_prime_power_upper = \&Math::Prime::Util::PP::nth_prime_power_upper;
*nth_perfect_power = \&Math::Prime::Util::PP::nth_perfect_power;
*nth_perfect_power_approx = \&Math::Prime::Util::PP::nth_perfect_power_approx;
*nth_perfect_power_lower = \&Math::Prime::Util::PP::nth_perfect_power_lower;
*nth_perfect_power_upper = \&Math::Prime::Util::PP::nth_perfect_power_upper;
*nth_lucky = \&Math::Prime::Util::PP::nth_lucky;
*nth_lucky_approx = \&Math::Prime::Util::PP::nth_lucky_approx;
*nth_lucky_lower = \&Math::Prime::Util::PP::nth_lucky_lower;
*nth_lucky_upper = \&Math::Prime::Util::PP::nth_lucky_upper;

*nth_twin_prime = \&Math::Prime::Util::PP::nth_twin_prime;
*nth_twin_prime_approx = \&Math::Prime::Util::PP::nth_twin_prime_approx;
*twin_prime_count_approx = \&Math::Prime::Util::PP::twin_prime_count_approx;
*nth_semiprime = \&Math::Prime::Util::PP::nth_semiprime;
*nth_semiprime_approx = \&Math::Prime::Util::PP::nth_semiprime_approx;
*semiprime_count_approx = \&Math::Prime::Util::PP::semiprime_count_approx;

*nth_almost_prime = \&Math::Prime::Util::PP::nth_almost_prime;
*nth_almost_prime_approx = \&Math::Prime::Util::PP::nth_almost_prime_approx;
*nth_almost_prime_lower = \&Math::Prime::Util::PP::nth_almost_prime_lower;
*nth_almost_prime_upper = \&Math::Prime::Util::PP::nth_almost_prime_upper;
*almost_prime_count_approx = \&Math::Prime::Util::PP::almost_prime_count_approx;
*almost_prime_count_lower  = \&Math::Prime::Util::PP::almost_prime_count_lower;
*almost_prime_count_upper  = \&Math::Prime::Util::PP::almost_prime_count_upper;

*nth_omega_prime = \&Math::Prime::Util::PP::nth_omega_prime;

*ramanujan_prime_count_approx= \&Math::Prime::Util::PP::ramanujan_prime_count_approx;
*ramanujan_prime_count_lower = \&Math::Prime::Util::PP::ramanujan_prime_count_lower;
*ramanujan_prime_count_upper = \&Math::Prime::Util::PP::ramanujan_prime_count_upper;
*nth_ramanujan_prime       =\&Math::Prime::Util::PP::nth_ramanujan_prime;
*nth_ramanujan_prime_lower =\&Math::Prime::Util::PP::nth_ramanujan_prime_lower;
*nth_ramanujan_prime_upper =\&Math::Prime::Util::PP::nth_ramanujan_prime_upper;
*nth_ramanujan_prime_approx=\&Math::Prime::Util::PP::nth_ramanujan_prime_approx;

*factor         = \&Math::Prime::Util::PP::factor;
*factor_exp     = \&Math::Prime::Util::PP::factor_exp;
*trial_factor   = \&Math::Prime::Util::PP::trial_factor;
*prho_factor    = \&Math::Prime::Util::PP::prho_factor;
*pbrent_factor  = \&Math::Prime::Util::PP::pbrent_factor;
*ecm_factor     = \&Math::Prime::Util::PP::ecm_factor;
*fermat_factor  = \&Math::Prime::Util::PP::fermat_factor;
*holf_factor    = \&Math::Prime::Util::PP::holf_factor;
*squfof_factor  = \&Math::Prime::Util::PP::squfof_factor;
*lehman_factor  = \&Math::Prime::Util::PP::lehman_factor;
*pminus1_factor = \&Math::Prime::Util::PP::pminus1_factor;
*pplus1_factor  = \&Math::Prime::Util::PP::pplus1_factor;
*cheb_factor    = \&Math::Prime::Util::PP::cheb_factor;

*primes           = \&Math::Prime::Util::PP::primes;
*prime_powers     = \&Math::Prime::Util::PP::prime_powers;
*twin_primes      = \&Math::Prime::Util::PP::twin_primes;
*semi_primes      = \&Math::Prime::Util::PP::semi_primes;
*ramanujan_primes = \&Math::Prime::Util::PP::ramanujan_primes;
*almost_primes    = \&Math::Prime::Util::PP::almost_primes;
*omega_primes     = \&Math::Prime::Util::PP::omega_primes;

*Pi = \&Math::Prime::Util::PP::Pi;


# We are doing the validation here so the PP code doesn't have to do it.

sub gcd {
  my(@v) = @_;
  _validate_integer($_) for @v;
  return Math::Prime::Util::PP::gcd(@v);
}
sub vecsum {
  my(@v) = @_;
  _validate_integer($_) for @v;
  return Math::Prime::Util::PP::vecsum(@v);
}
sub vecprod {
  my(@v) = @_;
  _validate_integer($_) for @v;
  return Math::Prime::Util::PP::vecprod(@v);
}
sub vecmin {
  my(@v) = @_;
  _validate_integer($_) for @v;
  return Math::Prime::Util::PP::vecmin(@v);
}
sub vecmax {
  my(@v) = @_;
  _validate_integer($_) for @v;
  return Math::Prime::Util::PP::vecmax(@v);
}

#############################################################################

my $_exitloop = 0;
my $_forcount = 0;
sub lastfor { croak "lastfor called outside a loop" if $_forcount == 0; $_exitloop = 1; }
sub _get_forexit { $_exitloop; }
sub _start_for_loop { $_forcount++; my $old = $_exitloop; $_exitloop = 0; $old; }
sub _end_for_loop { $_forcount--; $_exitloop = shift; }

no warnings 'prototype';

*forprimes        = \&Math::Prime::Util::PP::forprimes;
*forcomposites    = \&Math::Prime::Util::PP::forcomposites;
*foroddcomposites = \&Math::Prime::Util::PP::foroddcomposites;
*forsemiprimes    = \&Math::Prime::Util::PP::forsemiprimes;
*foralmostprimes  = \&Math::Prime::Util::PP::foralmostprimes;
*forfactored      = \&Math::Prime::Util::PP::forfactored;
*forsquarefree    = \&Math::Prime::Util::PP::forsquarefree;
*forsquarefreeint = \&Math::Prime::Util::PP::forsquarefreeint;
*forsetproduct    = \&Math::Prime::Util::PP::forsetproduct;

*fordivisors = \&Math::Prime::Util::PP::fordivisors;
*forpart     = \&Math::Prime::Util::PP::forpart;
*forcomp     = \&Math::Prime::Util::PP::forcomp;
*forcomb     = \&Math::Prime::Util::PP::forcomb;
*forperm     = \&Math::Prime::Util::PP::forperm;
*forderange  = \&Math::Prime::Util::PP::forderange;

sub vecreduce (&@) {    ## no critic qw(ProhibitSubroutinePrototypes)
  my($sub, @v) = @_;

  # Mastering Perl page 162, works with old Perl
  my $caller = caller();
  no strict 'refs'; ## no critic(strict)
  local(*{$caller.'::a'}) = \my $a;
  local(*{$caller.'::b'}) = \my $b;
  $a = shift @v;
  for my $v (@v) {
    $b = $v;
    $a = $sub->();
  }
  $a;
}
sub vecslide (&@) {    ## no critic qw(ProhibitSubroutinePrototypes)
  my($sub, @v) = @_;

  my $caller = caller();
  no strict 'refs'; ## no critic(strict)
  local(*{$caller.'::a'}) = \my $a;
  local(*{$caller.'::b'}) = \my $b;
  map { $a = $v[$_-1];  $b = $v[$_];  scalar $sub->(); } 1..$#v;
}
sub vecpairwise (&$$) {    ## no critic qw(ProhibitSubroutinePrototypes)
  my($sub, $ra, $rb) = @_;
  croak 'Not a subroutine reference'
    unless ref($sub) && (reftype($sub)||'') eq 'CODE';
  croak 'vecpairwise: expected two array references'
    unless ref($ra) && (reftype($ra)||'') eq 'ARRAY' &&
           ref($rb) && (reftype($rb)||'') eq 'ARRAY';

  my $n = (@$ra < @$rb) ? scalar(@$ra) : scalar(@$rb);
  my $caller = caller();
  no strict 'refs'; ## no critic(strict)
  my($a, $b);
  local(*{$caller.'::a'}) = \$a;
  local(*{$caller.'::b'}) = \$b;

  if (!wantarray) {
    my $count = 0;
    for my $i (0 .. $n-1) {
      $a = exists $ra->[$i] ? $ra->[$i] : undef;
      $b = exists $rb->[$i] ? $rb->[$i] : undef;
      my @r = $sub->();
      $count += scalar(@r);
    }
    return $count;
  } else {
    my @result;
    for my $i (0 .. $n-1) {
      $a = exists $ra->[$i] ? $ra->[$i] : undef;
      $b = exists $rb->[$i] ? $rb->[$i] : undef;
      push @result, $sub->();
    }
    return @result;
  }
}

sub vecany (&@) {       ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  $sub->() and return 1 foreach @_;
  0;
}
sub vecall (&@) {       ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  $sub->() or return 0 foreach @_;
  1;
}
sub vecnone (&@) {      ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  $sub->() and return 0 foreach @_;
  1;
}
sub vecnotall (&@) {    ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  $sub->() or return 1 foreach @_;
  0;
}

sub vecfirst (&@) {     ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  $sub->() and return $_ foreach @_;
  undef;
}

sub vecfirstidx (&@) {     ## no critic qw(ProhibitSubroutinePrototypes)
  my $sub = shift;
  my $i = 0;
  ++$i and $sub->() and return $i-1 foreach @_;
  -1;
}

1;

__END__

=pod

=head1 NAME

Math::Prime::Util::PPFE - PP front end for Math::Prime::Util

=head1 SYNOPSIS

This loads the PP code and adds input validation front ends.  It is only
meant to be used when XS is not used.

=head1 DESCRIPTION

Loads PP module and implements PP front-end functions for all XS code.
This is used only if the XS code is not loaded.

=head1 SEE ALSO

L<Math::Prime::Util>

L<Math::Prime::Util::PP>

=head1 AUTHORS

Dana Jacobsen E<lt>dana@acm.orgE<gt>


=head1 COPYRIGHT

Copyright 2014-2024 by Dana Jacobsen E<lt>dana@acm.orgE<gt>

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=cut
