Sidef

 view release on metacpan or  search on metacpan

lib/Sidef/Types/Number/Number.pm  view on Meta::CPAN

    # die ((caller(1))[3], ' -- ', (caller(0))[2]) if index(ref($x), 'Sidef::') == 0;

    $x = (_any2mpz($x) // return undef) if ref($x) ne 'Math::GMPz';

    Math::GMPz::Rmpz_fits_ulong_p($x)
      ? Math::GMPz::Rmpz_get_ui($x)
      : ((Math::GMPz::Rmpz_sgn($x) >= 0) ? Math::GMPz::Rmpz_get_str($x, 10) : undef);
}

# Big to positive integer-string
sub _big2pistr {
    my ($x) = @_;

    $x = $$x if ref($x) eq __PACKAGE__;
    ref($x) || return (($x > 0) ? $x : undef);

    # die ((caller(1))[3], ' -- ', (caller(0))[2]) if index(ref($x), 'Sidef::') == 0;

    $x = (_any2mpz($x) // return undef) if ref($x) ne 'Math::GMPz';

    if (Math::GMPz::Rmpz_fits_ulong_p($x)) {
        my $ui = Math::GMPz::Rmpz_get_ui($x);
        return (($ui > 0) ? $ui : undef);
    }

    (Math::GMPz::Rmpz_sgn($x) > 0)
      ? Math::GMPz::Rmpz_get_str($x, 10)
      : undef;
}

sub _array {
    Sidef::Types::Array::Array->new(@_);
}

sub _sanitize_mpz {
    my ($n) = @_;

    if (ref($n) ne 'Math::GMPz') {
        __is_int__($n) || return undef;
        $n = _any2mpz($n) // return undef;
    }

    $n;
}

sub _normalize_numeric_type {
    my ($n) = @_;
    ref($n) eq 'Math::GMPz'
      ? (
           Math::GMPz::Rmpz_fits_ulong_p($n)
         ? Math::GMPz::Rmpz_get_ui($n)
         : Math::GMPz::Rmpz_get_str($n, 10)
        )
      : $n;
}

sub _execute_pari_gp {
    my ($code) = @_;
    say STDERR ":: Executing PARI/GP with: $code" if $VERBOSE;

    state $_x = require IPC::Open2;

    # Maintain persistent handles for the lifetime of the interpreter
    state ($gp_in, $gp_out, $gp_pid);

    state $gp_cmd = 'gp -q -f --default parisize=500M --default parisizemax=0 --default threadsizemax=0';

    # Lazy initialization: only spawn the process on the first call
    if (!$gp_pid) {
        $gp_pid = IPC::Open2::open2($gp_out, $gp_in, $gp_cmd);

        # Disable buffering on the input handle to ensure GP gets commands immediately
        my $old_fh = select($gp_in);
        $| = 1;
        select($old_fh);
    }

    # Unique sentinel to mark the end of the GP output stream
    my $sentinel = "---END_OF_OUTPUT---";

    # Protect against script crashes if the GP process was killed externally
    local $SIG{PIPE} = 'IGNORE';

    # Send the code.
    # The sentinel is sent on a new line so that even if $code triggers
    # a GP syntax error, the sentinel is still evaluated and prevents a deadlock.
    my $write_ok = print $gp_in "$code\nprint(\"$sentinel\");\n";

    # Handle GP process death and auto-restart
    if (!$write_ok) {
        say STDERR "[WARNING] PARI/GP pipe broke, restarting..." if $VERBOSE;
        close $gp_in                                             if $gp_in;
        close $gp_out                                            if $gp_out;
        undef $gp_pid;

        # Re-initialize and retry exactly once
        $gp_pid = IPC::Open2::open2($gp_out, $gp_in, $gp_cmd);
        my $old_fh = select($gp_in);
        $| = 1;
        select($old_fh);

        print $gp_in "$code\nprint(\"$sentinel\");\n" or return undef;
    }

    my @output;
    while (my $line = <$gp_out>) {
        chomp $line;
        last if $line eq $sentinel;
        push @output, $line;
    }

    # GP errors are printed to STDERR natively, bypassing @output.
    # If $code fails or returns nothing, @output will be safely empty.
    my $res = join("\n", @output);
    return undef if $res eq '';
    return $res;
}

sub _execute_in_tmpdir {
    my ($cmd) = @_;

lib/Sidef/Types/Number/Number.pm  view on Meta::CPAN


        if ($v == 0) {
            return bless \$x;
        }

        if ($v == 1) {
            my $q =
              HAS_PRIME_UTIL
              ? Math::Prime::Util::divint($x, $y)
              : Math::Prime::Util::GMP::divint($x, $y);
            return bless \$q;
        }

        my $q =
          HAS_PRIME_UTIL
          ? Math::Prime::Util::divint($x, Math::Prime::Util::powint($y, $v))
          : Math::Prime::Util::GMP::divint($x, Math::Prime::Util::GMP::powint($y, $v));
        return bless \$q;
    }

    $x = _any2mpz($x) // goto &nan;
    $y = _any2mpz($y) // goto &nan;

    Math::GMPz::Rmpz_cmpabs_ui($y, 1) <= 0 and return $_[0];

    my $r = Math::GMPz::Rmpz_init();
    Math::GMPz::Rmpz_remove($r, $x, $y);
    bless \$r;
}

*remdiv = \&remove;

sub make_coprime {
    my ($n, $k) = @_;

    ref($k) eq __PACKAGE__ or _valid(\$k);

    $n = _any2mpz($$n) // goto &nan;
    $k = _any2mpz($$k) // goto &nan;

    if (Math::GMPz::Rmpz_sgn($n) == 0) {
        return _set_int($n);
    }

    my $r = Math::GMPz::Rmpz_init_set($n);
    my $g = Math::GMPz::Rmpz_init();

    Math::GMPz::Rmpz_gcd($g, $r, $k);

    while (Math::GMPz::Rmpz_cmp_ui($g, 1) > 0) {
        Math::GMPz::Rmpz_remove($r, $r, $g);
        Math::GMPz::Rmpz_gcd($g, $r, $g);
    }

    bless \$r;
}

sub useed {
    my ($n) = @_;

    state $_x = require Digest::SHA;

    my $z = _any2mpz($$n) // die "[ERROR] Number.useed(): invalid seed value <<$n>> (expected an integer)";

    my $hex = Math::GMPz::Rmpz_get_str($z, 16);
    $hex = substr($hex, 1) if (substr($hex, 0, 1) eq '-');

    my $bin  = CORE::pack('H*', $hex);
    my $seed = Digest::SHA::sha512($bin);

    while (CORE::length($seed) < 1024) {
        $seed = Digest::SHA::sha512($seed) . CORE::reverse($seed);
    }

    Math::Prime::Util::GMP::seed_csprng(1024, $seed);
    return $TRUE;
}

*iseed = \&useed;

sub urandomm {
    my ($n, $m) = @_;

    if (defined($m)) {
        ref($m) eq __PACKAGE__ or _valid(\$m);

        $n = $$n;
        $m = $$m;

        $n = (ref($n) ? _any2mpz($n) : $n) // return ZERO;
        $m = (ref($m) ? _any2mpz($m) : $m) // return ZERO;

        if (__cmp__($n, $m) > 0) {
            ($n, $m) = ($m, $n);
        }

        if (ref($n)) {
            $n = _big2uistr($n) // 0;
        }
        else {
            $n >= 0 or do { $n = 0 };
        }

        if (ref($m)) {
            $m = _big2uistr($m) // 0;
        }
        else {
            $m >= 0 or do { $m = 0 };
        }

        return _set_int(Math::Prime::Util::GMP::urandomr($n, $m) || return ZERO);
    }

    _set_int(Math::Prime::Util::GMP::urandomm(_big2uistr($$n) || return ZERO) || return ZERO);
}

*urand = \&urandomm;

sub irand {
    my ($n, $m) = @_;



( run in 0.577 second using v1.01-cache-2.11-cpan-81fc1098f69 )