-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Small improvements and further tests
- Loading branch information
Showing
7 changed files
with
232 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
""" | ||
Integer Functions: | ||
iroot(k, n) integer k'th root of n | ||
ilog(b, n) integer log of n with respect ot base b | ||
perfect_square(n) test if a number is a perfect square | ||
perfect_power(n) test if a number is a perfect power | ||
prime_power(n) test if a number is a prime power | ||
""" | ||
from math import isqrt | ||
from .primes import sieve_eratosthenes, is_prime | ||
|
||
|
||
def iroot(k: int, n: int) -> int: | ||
"""For a given positive integers `n` and `k`, finds the largest integer `r` such that `r**k <= n`.""" | ||
if not isinstance(k, int) or k < 1: | ||
raise ValueError(f"k={k} must be a positive integer.") | ||
if not isinstance(n, int) or n < 0: | ||
raise ValueError(f"n={n} must be a nonegative integer.") | ||
if n <= 1 or k == 1: | ||
return n | ||
if k == 2: | ||
return isqrt(n) | ||
rr = n | ||
kk = k | ||
while kk > 1: | ||
kk //= 2 | ||
rr = isqrt(rr) | ||
r = rr + 1 | ||
k1 = k-1 | ||
while rr < r: | ||
r = rr | ||
rr = (k1 * rr + n // rr ** k1) // k # Newton iteration | ||
return r | ||
|
||
def ilog(b: int, n:int) -> int: | ||
"""For a given positive integer `n`, finds the largest integer `l` such that `b**l <= n`.""" | ||
# https://stackoverflow.com/questions/39190815/how-to-make-perfect-power-algorithm-more-efficient/39191163#39191163 | ||
if not isinstance(b, int) or b < 2: | ||
raise ValueError(f"base b={b} must a positive integer larger then one.") | ||
if not isinstance(n, int) or n <= 0: | ||
raise ValueError(f"n={n} must be a positive integer.") | ||
if n == 1: | ||
return 0 | ||
if b == 2: | ||
return n.bit_length() - 1 | ||
lo, blo, hi, bhi = 0, 1, 1, b | ||
while bhi < n: | ||
lo, blo, hi, bhi = hi, bhi, hi + hi, bhi * bhi | ||
while 1 < (hi - lo): | ||
mid = (lo + hi) // 2 | ||
bmid = blo * pow(b, mid - lo) | ||
if n < bmid: | ||
hi, bhi = mid, bmid | ||
elif bmid < n: | ||
lo, blo = mid, bmid | ||
else: return mid | ||
if bhi == n: return hi | ||
return lo | ||
|
||
|
||
def perfect_square(n: int) -> int|None: | ||
"""Returns the root if `n` is a perfect square and None else.""" | ||
s = isqrt(n) | ||
if s*s == n: | ||
return s | ||
|
||
def perfect_power(n: int) -> tuple|None: | ||
"""Returns integers `(m, p)` with `m**p == n` if `n` is a perfect power and None else.""" | ||
if n == 0: | ||
return 0, 1 | ||
if n == 1: | ||
return 1, 1 | ||
for p in sieve_eratosthenes(n.bit_length() - 1): | ||
m = iroot(p, n) | ||
if pow(m, p) == n: | ||
return m, p | ||
|
||
def prime_power(n: int) -> tuple|None: | ||
"""Returns integers `(p, k)` with `p**k == n` if `n` is a prime power and None else.""" | ||
if is_prime(n): | ||
return (n, 1) | ||
r = n | ||
k = 1 | ||
while r.bit_length() > 20: | ||
k += 1 | ||
r = iroot(k, n) | ||
if r**k == n and is_prime(r): | ||
return (r, k) | ||
for p in sieve_eratosthenes(r - 1): | ||
k = 0 | ||
while n % p == 0: | ||
k += 1 | ||
n //= p | ||
if k > 1: | ||
if n == 1: | ||
return (p, k) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import pytest | ||
from random import randint, seed | ||
from kryptools import sieve_eratosthenes | ||
from kryptools import iroot, ilog, perfect_square, perfect_power, prime_power | ||
seed(0) | ||
|
||
|
||
def test_iroot(): | ||
assert iroot(3, 0) == 0 | ||
assert iroot(3, 1) == 1 | ||
assert iroot(1, 3) == 3 | ||
for _ in range(100): | ||
n = randint(0, 10**10) | ||
for k in range(2, 5): | ||
r = iroot(k, n) | ||
assert r ** k <= n and (r+1) ** k > n | ||
|
||
def test_ilog(): | ||
for n in range(1, 10000): | ||
for b in range(2,5): | ||
l = ilog(b, n) | ||
assert b**l <= n and b**(l+1) > n | ||
for _ in range(1000): | ||
n = randint(1, 10**10) | ||
for b in range(2,5): | ||
l = ilog(b, n) | ||
assert b**l <= n and b**(l+1) > n | ||
|
||
def test_perfect_power(): | ||
for n in (3* 7, 4 * 3, 4 * 3 * 5 * 7): | ||
assert perfect_power(n) == None | ||
for k in sieve_eratosthenes(11): | ||
assert perfect_power(n**k) == (n, k) | ||
|
||
def test_prime_power(): | ||
for p in sieve_eratosthenes(30): | ||
for k in range(1,6): | ||
assert prime_power(p**k) == (p, k) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters