diff --git a/hpcgap/lib/integer.gi b/hpcgap/lib/integer.gi index dc02e3c85d..90e576f71b 100644 --- a/hpcgap/lib/integer.gi +++ b/hpcgap/lib/integer.gi @@ -923,8 +923,51 @@ InstallGlobalFunction( IsOddInt, n -> n mod 2 = 1 ); ## #F IsPrimePowerInt( ) . . . . . . . . . . . test for a power of a prime ## -InstallGlobalFunction( IsPrimePowerInt, - n -> IsPrimeInt( SmallestRootInt( n ) ) ); +InstallGlobalFunction( IsPrimePowerInt, function(n) + local k, r, s, p, l, q, i; + + # check the argument + if n > 1 then k := 2; s := 1; + elif n < -1 then k := 3; s := -1; n := -n; + else return false; + fi; + + # exclude small divisors, and thereby large exponents + for p in Primes do + if p*p > n then return true; fi; # n is prime + r := PVALUATION_INT(n, p); + if r > 0 then + if s = -1 and IsEvenInt(r) then return false; fi; + return n = p^r; + fi; + od; + l := LogInt( n, p ); + + # loop over the possible prime divisors of exponents + # use Fermat's little theorem to cast out impossible ones: + # for suppose we had r such that n = r^k. Then by Fermat, + # n^((q-1)/k) = r^(q-1) is congruent 0 or 1 mod q + i := Position(Primes, k); + while k <= l do + q := 2*k+1; while not IsPrimeInt(q) do q := q+2*k; od; + if PowerModInt( n, (q-1)/k, q ) <= 1 then + r := RootInt( n, k ); + if r ^ k = n then + n := r; + l := QuoInt( l, k ); + continue; + fi; + fi; + if i <> fail and i < Length(Primes) then + i := i + 1; + k := Primes[i]; + else + k := NextPrimeInt( k ); + fi; + od; + + return IsPrimeInt(n); +end); ############################################################################# @@ -1118,7 +1161,7 @@ InstallGlobalFunction( SignInt, SIGN_RAT ); # support rationals for backwards co #F SmallestRootInt( ) . . . . . . . . . . . smallest root of an integer ## InstallGlobalFunction(SmallestRootInt,function ( n ) - local k, r, s, p, l, q; + local k, r, s, p, l, q, i; # check the argument if n > 0 then k := 2; s := 1; @@ -1127,15 +1170,17 @@ InstallGlobalFunction(SmallestRootInt,function ( n ) fi; # exclude small divisors, and thereby large exponents - if n mod 2 = 0 then - p := 2; - else - p := 3; while p < 100 and n mod p <> 0 do p := p+2; od; - fi; + for p in Primes do + if p*p > n then return s * n; fi; + if n mod p = 0 then break; fi; + od; l := LogInt( n, p ); # loop over the possible prime divisors of exponents - # use Euler's criterion to cast out impossible ones + # use Fermat's little theorem to cast out impossible ones: + # for suppose we had r such that n = r^k. Then by Fermat, + # n^((q-1)/k) = r^(q-1) is congruent 0 or 1 mod q + i := Position(Primes, k); while k <= l do q := 2*k+1; while not IsPrimeInt(q) do q := q+2*k; od; if PowerModInt( n, (q-1)/k, q ) <= 1 then @@ -1143,9 +1188,12 @@ InstallGlobalFunction(SmallestRootInt,function ( n ) if r ^ k = n then n := r; l := QuoInt( l, k ); - else - k := NextPrimeInt( k ); + continue; fi; + fi; + if i <> fail and i < Length(Primes) then + i := i + 1; + k := Primes[i]; else k := NextPrimeInt( k ); fi; diff --git a/lib/integer.gi b/lib/integer.gi index f5124ad943..ad17c73fbd 100644 --- a/lib/integer.gi +++ b/lib/integer.gi @@ -910,8 +910,51 @@ InstallGlobalFunction( IsOddInt, n -> n mod 2 = 1 ); ## #F IsPrimePowerInt( ) . . . . . . . . . . . test for a power of a prime ## -InstallGlobalFunction( IsPrimePowerInt, - n -> IsPrimeInt( SmallestRootInt( n ) ) ); +InstallGlobalFunction( IsPrimePowerInt, function(n) + local k, r, s, p, l, q, i; + + # check the argument + if n > 1 then k := 2; s := 1; + elif n < -1 then k := 3; s := -1; n := -n; + else return false; + fi; + + # exclude small divisors, and thereby large exponents + for p in Primes do + if p*p > n then return true; fi; # n is prime + r := PVALUATION_INT(n, p); + if r > 0 then + if s = -1 and IsEvenInt(r) then return false; fi; + return n = p^r; + fi; + od; + l := LogInt( n, p ); + + # loop over the possible prime divisors of exponents + # use Fermat's little theorem to cast out impossible ones: + # for suppose we had r such that n = r^k. Then by Fermat, + # n^((q-1)/k) = r^(q-1) is congruent 0 or 1 mod q + i := Position(Primes, k); + while k <= l do + q := 2*k+1; while not IsPrimeInt(q) do q := q+2*k; od; + if PowerModInt( n, (q-1)/k, q ) <= 1 then + r := RootInt( n, k ); + if r ^ k = n then + n := r; + l := QuoInt( l, k ); + continue; + fi; + fi; + if i <> fail and i < Length(Primes) then + i := i + 1; + k := Primes[i]; + else + k := NextPrimeInt( k ); + fi; + od; + + return IsPrimeInt(n); +end); ############################################################################# @@ -1105,7 +1148,7 @@ InstallGlobalFunction( SignInt, SIGN_RAT ); # support rationals for backwards co #F SmallestRootInt( ) . . . . . . . . . . . smallest root of an integer ## InstallGlobalFunction(SmallestRootInt,function ( n ) - local k, r, s, p, l, q; + local k, r, s, p, l, q, i; # check the argument if n > 0 then k := 2; s := 1; @@ -1114,15 +1157,17 @@ InstallGlobalFunction(SmallestRootInt,function ( n ) fi; # exclude small divisors, and thereby large exponents - if n mod 2 = 0 then - p := 2; - else - p := 3; while p < 100 and n mod p <> 0 do p := p+2; od; - fi; + for p in Primes do + if p*p > n then return s * n; fi; + if n mod p = 0 then break; fi; + od; l := LogInt( n, p ); # loop over the possible prime divisors of exponents - # use Euler's criterion to cast out impossible ones + # use Fermat's little theorem to cast out impossible ones: + # for suppose we had r such that n = r^k. Then by Fermat, + # n^((q-1)/k) = r^(q-1) is congruent 0 or 1 mod q + i := Position(Primes, k); while k <= l do q := 2*k+1; while not IsPrimeInt(q) do q := q+2*k; od; if PowerModInt( n, (q-1)/k, q ) <= 1 then @@ -1130,9 +1175,12 @@ InstallGlobalFunction(SmallestRootInt,function ( n ) if r ^ k = n then n := r; l := QuoInt( l, k ); - else - k := NextPrimeInt( k ); + continue; fi; + fi; + if i <> fail and i < Length(Primes) then + i := i + 1; + k := Primes[i]; else k := NextPrimeInt( k ); fi; diff --git a/src/integer.c b/src/integer.c index 8c00873062..d13661ae8b 100644 --- a/src/integer.c +++ b/src/integer.c @@ -690,6 +690,20 @@ UInt8 UInt8_ObjInt(Obj i) #endif } +// This function returns an immediate integer, or +// an integer object with exactly one limb, and returns +// its absolute value as an unsigned integer. +static inline UInt AbsOfSmallInt(Obj x) +{ + if (!IS_INTOBJ(x)) { + GAP_ASSERT(SIZE_INT(x) == 1); + return VAL_LIMB0(x); + } + Int val = INT_INTOBJ(x); + return val > 0 ? val : -val; +} + + /**************************************************************************** ** *F PrintInt( ) . . . . . . . . . . . . . . . . print a GMP constant @@ -2163,6 +2177,18 @@ Obj GcdInt ( Obj opL, Obj opR ) sizeL = SIZE_INT_OR_INTOBJ(opL); sizeR = SIZE_INT_OR_INTOBJ(opR); + // for small inputs, run Euclid directly + if (sizeL == 1 || sizeR == 1) { + if (sizeR != 1) { + SWAP(Obj, opL, opR); + } + UInt r = AbsOfSmallInt(opR); + FAKEMPZ_GMPorINTOBJ(mpzL, opL); + r = mpz_gcd_ui(0, MPZ_FAKEMPZ(mpzL), r); + CHECK_FAKEMPZ(mpzL); + return ObjInt_UInt(r); + } + NEW_FAKEMPZ( mpzResult, sizeL < sizeR ? sizeL : sizeR ); FAKEMPZ_GMPorINTOBJ( mpzL, opL ); FAKEMPZ_GMPorINTOBJ( mpzR, opR ); @@ -2396,6 +2422,18 @@ Obj FuncPVALUATION_INT(Obj self, Obj n, Obj p) if ( p == INTOBJ_INT(0) ) ErrorMayQuit( "PValuation:

must be nonzero", 0L, 0L ); + if (SIZE_INT_OR_INTOBJ(n) == 1 && SIZE_INT_OR_INTOBJ(p) == 1) { + UInt N = AbsOfSmallInt(n); + UInt P = AbsOfSmallInt(p); + if (N == 0 || P == 1) return INTOBJ_INT(0); + k = 0; + while (N % P == 0) { + N /= P; + k++; + } + return INTOBJ_INT(k); + } + /* For certain values of p, mpz_remove replaces its "dest" argument and tries to deallocate the original mpz_t in it. This means we cannot use a fake_mpz_t for it. However, we are not really diff --git a/tst/testinstall/intarith.tst b/tst/testinstall/intarith.tst index c0f11fe614..63bbb7d145 100644 --- a/tst/testinstall/intarith.tst +++ b/tst/testinstall/intarith.tst @@ -1332,13 +1332,41 @@ gap> for m in [1..100] do # gap> checkPValuationInt:=function(n,p) > local k, m; -> if n = 0 then return true; fi; > k:=PVALUATION_INT(n,p); +> if n = 0 or p = 1 or p = -1 then return k = 0; fi; > m:=n/p^k; > return IsInt(m) and (m mod p) <> 0; > end;; -gap> ForAll([-10000 .. 10000], n-> ForAll([2,3,5,7,251], p -> checkPValuationInt(n,p))); +gap> ps := [-2^60-1,-2^60,-2^28-1,-2^28];; +gap> Append(ps, [-6..-1]); +gap> Append(ps, [1..20]); +gap> Append(ps, [250..260]); +gap> Append(ps, [2^28-1,2^28,2^60-1,2^60]);; +gap> SetX([-1000 .. 1000], ps, checkPValuationInt); +[ true ] +gap> SetX([-1000 .. 1000], ps, {n,p}->checkPValuationInt(n+2^100,p)); +[ true ] +gap> SetX([-1000 .. 1000], ps, {n,p}->checkPValuationInt(n-2^100,p)); +[ true ] + +# +gap> p:=2^255-19;; # big prime +gap> ForAll([1..30], k-> PVALUATION_INT((p+1)^k,2)=k); true +gap> ForAll([1..30], k-> PVALUATION_INT((p+1)^k,p)=0); +true +gap> ForAll([1..30], k-> PVALUATION_INT(p^k+1,2)=1); +true +gap> ForAll([1..30], k-> PVALUATION_INT(p^k+1,p)=0); +true + +# +gap> SetX(data, dataNonZero, checkPValuationInt); +[ true ] +gap> List([2..6], p->List(data, n->PVALUATION_INT(n,p))); +[ [ 0, 20, 4, 0, 0, 0, 4, 20, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ], + [ 0, 10, 2, 0, 0, 0, 2, 10, 0 ], [ 0, 20, 4, 0, 0, 0, 4, 20, 0 ], + [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ] gap> PVALUATION_INT(10,0); Error, PValuation:

must be nonzero gap> PVALUATION_INT(0,0); @@ -1366,6 +1394,45 @@ Error, IsProbablyPrimeInt: must be a small positive integer gap> IS_PROBAB_PRIME_INT(1, 0); Error, IsProbablyPrimeInt: must be a small positive integer +# +# test SmallestRootInt +# +gap> SetX(Primes, [1..30], {p,k}->SmallestRootInt(p^k)=p); +[ true ] +gap> SetX(Primes, [1..30], {p,k}->SmallestRootInt(p^k*1009)=p^k*1009); +[ true ] +gap> SetX(Primes, [1..30], {p,k}->SmallestRootInt((p*1009)^k)=p*1009); +[ true ] +gap> p:=2^255-19;; # big prime +gap> ForAll([1..30], k-> SmallestRootInt(p^k) = p); +true +gap> List([-10..10], SmallestRootInt); +[ -10, -9, -2, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 2, 5, 6, 7, 2, 3, 10 ] +gap> List(data, SmallestRootInt); +[ -100000000000000000001, -10000, -10000, -1, 0, 1, 10, 10, + 100000000000000000001 ] +gap> List([-2^101,-2^100,2^100,2^101], SmallestRootInt); +[ -2, -16, 2, 2 ] + +# +# test IsPrimePowerInt +# +gap> P:=2^255-19;; # big prime +gap> Filtered([-10..10], IsPrimePowerInt); +[ -8, -7, -5, -3, -2, 2, 3, 4, 5, 7, 8, 9 ] +gap> SetX(Primes, [1..30], {p,k}->IsPrimePowerInt(p^k)); +[ true ] +gap> SetX(Primes, [1..10], {p,k}->IsPrimePowerInt(P*p^k)); +[ false ] +gap> SetX(Primes, [1..10], {p,k}->IsPrimePowerInt((P*p)^k)); +[ false ] +gap> ForAll([1..30], k->IsPrimePowerInt(P^k)); +true +gap> ForAll([1..10], k->not IsPrimePowerInt(1009*P^k)); +true +gap> ForAll([1..30], k->not IsPrimePowerInt((1009*P)^k)); +true + # gap> STOP_TEST( "intarith.tst", 1);