#!/usr/local/bin/bc -l
### Primes.BC - Primes and factorisation (rudimentary)
## All factor finding is done by trial division meaning that many
## functions will eat CPU for long periods when encountering
## certain numbers. Primality testing uses better techniques and
## is much faster if no factors are required.
## e.g. 2^503-1 is identified as non-prime by the primality testers
## but no factors will be found in any sensible amount of time
## through trial division.
##
## Steps have been taken to make the trial division as fast as
## possible, meaning much code re-use.
max_array_ = 4^8-1
# Greatest common divisor of x and y - stolen from funcs.bc
define int_gcd(x,y) {
auto r,os;
os=scale;scale=0
x/=1;y/=1
while(y>0){r=x%y;x=y;y=r}
scale=os
return(x)
}
### Primality testing ###
# workhorse function for int_modpow and others
define int_modpow_(x,y,m) {
auto r, y2;
if(y==0)return(1)
if(y==1)return(x%m)
y2=y/2
r=int_modpow_(x,y2,m); if(r>=m)r%=m
r*=r ; if(r>=m)r%=m
if(y%2){r*=x ; if(r>=m)r%=m}
return( r )
}
# Raise x to the y-th power, modulo m
define int_modpow(x,y,m) {
auto os;
os=scale;scale=0
x/=1;y/=1;m/=1
if(x< 0){print "int_modpow error: base is negative\n"; x*=-1}
if(y< 0){print "int_modpow error: exponent is negative\n";y*=-1}
if(m< 0){print "int_modpow error: modulus is negative\n"; m*=-1}
if(m==0){print "int_modpow error: modulus is zero\n"; return 0}
x=int_modpow_(x,y,m)
scale=os
return( x )
}
## Pseudoprime tests
# uses a shortcut for numbers < 300 decimal digits
define is_rabin_miller_pseudoprime(p) {
auto os,a,inc,top,next_a,q,r,s,x,c4;
os=scale;scale=0
if(p!=p/1){scale=os;return 0}
if(p<=(q=F+2)){scale=os;return(p==2||p==3||p==5||p==7||p==B||p==D||p==q)}
s=0;d=q=p-1;while(d%2==0){.=s++;d/=2}
if(p(n=A^5))sx=n;# 100000(decimal) upper limit
if(prime[A^4]){ #primes-db is present
for(n=4;(p=prime[n])<=sx;n++)if(x%p==0){scale=os;return 0}
} else {
ji=7;p=7;n=2*A-1
while(p<=sx){
if(x%p==0){scale=os;return 0}
if(ji++==8)ji=0;p+=j[ji];
if(p>n)while(p%7==0||p%B==0||p%D==0||p%n==0){if(ji++==8)ji=0;p+=j[ji]}
}
}
scale=os;return(1)
}
## Primality / Power-freedom tests
define is_prime(x) {
# It is estimated that all numbers will not be misidentified
# using the tests below, but it may take time
if(!is_small_division_pseudoprime(x))return 0
if(x7)ji=4#assume p is now prime[max_array_]
n=2*A-1
while(p<=sx){
if(x%p==0){scale=os;return 0}
if(ji++==8)ji=0;p+=j[ji];
if(p>n)while(p%7==0||p%B==0||p%D==0||p%n==0){if(ji++==8)ji=0;p+=j[ji]}
}
scale=os;return(1)
}
# Determine whether x is y-th power-free
# e.g. has_freedom(51, 2) will return whether 51 is square-free
# + sign of result indicates (-1)^[number of prime factors]
# making has_freedom(x,2) equivalent to the mobius function
# Special case: has_freedom(x, 1) returns whether x is prime
# Pseudo-boolean, since always returns 0 for false, but not always 1 for true
define has_freedom(x,y) {
auto os,j[],ji,p,py,n,c,m;#oldscale,jump,jump-index,prime,nth,count,mobius
os=scale;scale=0;if(x<0)x*=-1
if(x!=x/1){scale=os;return 0}
if(x==1){scale=os;return 1}
if(y==1){scale=os;return is_prime(x)}
if(x>A^A)if(is_prime(x)){scale=os;return 1}
if(x<0||y<1){scale=os;return 0}
j[0]=4;j[1]=2;j[2]=4;j[3]=2;j[4]=4;j[5]=6;j[6]=2;j[7]=6
m=1
for(p=2;p<7;p+=p-1){
if(x==p){scale=os;return -m}
if(x%(p^y)==0){scale=os;return 0}
for(c=0;x%p==0;c--)x/=p;if(c)m*=c
}
ji=7;p=7
if(prime[max_array_])for(n=4;n<=max_array_&&(py=(p=prime[n])^y)<=x;n++){
if(x==p){scale=os;return -m}
if(x%py==0){scale=os;return 0}
for(c=0;x%p==0;c--)x/=p;if(c)m*=c
}
if(p>7)ji=4#assume p is now prime[max_array_]
py=p^y;n=2*A-1
while(py<=x){
if(x==p){scale=os;return -m}
if(x%py==0){scale=os;return 0}
for(c=0;x%p==0;c--)x/=p;if(c)m*=c
if(ji++==8)ji=0;p+=j[ji];
if(p>n)while(p%7==0||p%B==0||p%D==0||p%n==0){if(ji++==8)ji=0;p+=j[ji]}
py=p^y
}
if(x>1)m*=-1
scale=os;return m
}
### Storage and output of prime factorisations ###
# Output the given array interpreted as prime factors and powers thereof
# . this function plus fac_store() make for a "delayed" equivalent
# . to the fac_print() function
define printfactorpow(fp[]) {
auto i,c;
for(i=0;fp[i];i+=2){
print fp[i]
if((c=fp[i+1])>1)print "^",c
if(fp[i+2])print " * "
}
print"\n"
return (fp[1]==1&&fp[2]==0) # fp[] is prime?
}
# Workhorse function for the below
# . for retaining a copy of the last calculated factorisation
# . in factorpow global array to save time if further functions
# . are to be called on same number
define factorpow_set_(fp[]) {
auto i;
for(i=0;fp[i];i++)factorpow[i]=fp[i]
return factorpow[i]=factorpow[i+1]=0;
}
# Workhorse function for the below
# . appends newly found factor and power thereof to the provided array
# . outputs that information if the print_ flag is set
define fac_store_(*fp[],m,p,c,print_) {
auto z;
if(!m%2).=m++ # m should be position of last element and thus odd
# even elements are prime factor, odd elements are how many.
# 9 -> {3,2} -> 3^2 , 60 -> {2,2,3,1,5,1} -> 2^2*3^1*5^1
# negative c means we know this is the end and we can write two zeroes
z=0;if(c<0){z=1;c*=-1}
fp[++m]=p;fp[++m]=c
if(print_){
print p;if(c>1)print "^",c
if(!z){print " * "}else{print "\n"};
}
if(z){fp[++m]=0;fp[++m]=0}
return m
}
# Workhorse function for the below
# . performs action that otherwise occurs three times
# . relies on inherited scope (POSIX style)
# . returns 0 if parent should also return
define fac_sp_innerloop_() {
for(c=0;x%p==0;c++)x/=p
if(c){
if(x==1)c=-c
m=fac_store_(fp[],m,p,c,print_);
if(x==1)return factorpow_set_(fp[]); # = 0
if(is_prime(x)){
m=fac_store_(fp[],m,x,-1,print_);
return factorpow_set_(fp[]); # = 0
}
}
return 1;
}
# Workhorse function for the below
# . factorises x through trial division, using the above functions
# . for output, storage, retention, etc.
define fac_sp_(*fp[],x,print_) {
auto os,j[],ji,sx,p,c,n,m,f;#oldscale,jump,jump-index,sqrtx,prime,count,nth,mth
os=scale;scale=0;x/=1
# Check to see if last calculation was the same as this one - save work
f=1;for(m=0;p=factorpow[m]&&f<=x;m+=2)f*=(fp[m]=p)^(fp[m+1]=factorpow[m+1]);
if(f==x){
if(print_).=printfactorpow(fp[]);
scale=os;return fp[m]=fp[m+1]=0;
}
# Main algorithm
m=-1
if(x<0){m=fac_store_(fp[],m,-1,1,print_);x*=-1}
if(x<=1||is_prime(x)){m=fac_store_(fp[],m,x,-1,print_);scale=os;return (x>1)}
j[0]=4;j[1]=2;j[2]=4;j[3]=2;j[4]=4;j[5]=6;j[6]=2;j[7]=6
for(p=2;p<7;p+=p-1)if(!fac_sp_innerloop_()){scale=os;return 0} #1
sx=sqrt(x);p=7;ji=7
if(prime[max_array_])for(n=4;n<=max_array_&&(p=prime[n])<=sx;n++){
if(!fac_sp_innerloop_()){scale=os;return 0} #2
}
if(p>7)ji=4#assume p is now prime[max_array_]
n=2*A-1;sx=sqrt(x)
while(p<=sx){
if(!fac_sp_innerloop_()){scale=os;return 0} #3
if(c)sx=sqrt(x)
if(ji++==8)ji=0;p+=j[ji];
if(p>n)while(p%7==0||p%B==0||p%D==0||p%n==0){if(ji++==8)ji=0;p+=j[ji]}
}
if(x>1)m=fac_store_(fp[],m,x,-1,print_)
scale=os;return factorpow_set_(fp[]);
}
# Output the prime factors of x with powers thereof
# . displays factors as they are found which allows more chance
# . of some factors being output before becoming bogged down
# . finding larger factors by trial division
define fac_print(x) { auto a[];x=fac_sp_(a[],x,1);return (a[1]==1&&a[2]==0); }
# Store the prime factors of x into the given array
define fac_store(*fp[],x) { x=fac_sp_( fp[],x,0);return ( fp[1]==1&& fp[2]==0); }
# Can be slow in the case of a large spf
define smallest_prime_factor(x) {
auto os,j[],ji,sx,p,n;#oldscale,jump,jump-index,sqrtx,prime,nth
os=scale;scale=0;x/=1
if(x<0){scale=os;return -1}
if(x<=1||is_prime(x)){scale=os;return x}
j[0]=4;j[1]=2;j[2]=4;j[3]=2;j[4]=4;j[5]=6;j[6]=2;j[7]=6
for(p=2;p<7;p+=p-1)if(x%p==0){scale=os;return p}
sx=sqrt(x);p=7;ji=7
if(prime[max_array_])for(n=4;n<=max_array_&&(p=prime[n])<=sx;n++)if(x%p==0){scale=os;return p}
if(p>7)ji=4#assume p is now prime[max_array_]
n=2*A-1;sx=sqrt(x)
while(p<=sx){
if(x%p==0){scale=os;return p}
if(ji++==8)ji=0;p+=j[ji];
if(p>n)while(p%7==0||p%B==0||p%D==0||p%n==0){if(ji++==8)ji=0;p+=j[ji]}
}
scale=os;return(-sx) #if we ever get here, something is wrong!
}
# Non trivial = slow
define largest_prime_factor(x) {
auto i,fp[];
.=fac_store(fp[],x);
for(i=0;fp[i];i+=2){}
return fp[i-2];
}
# Return powerfree kernel of x (largest powerfree number which divides x)
define rad(x) {
auto i,r,f,fp[];
.=fac_store(fp[],x)
r=1;for(i=0;f=fp[i];i+=2)r*=f
return r;
}
# Return square part of x
define squarepart(x) {
auto os,i,r,f,p,fp[];
.=fac_store(fp[],x)
os=scale;scale=0
r=1;for(i=0;f=fp[i];i+=2){p=fp[i+1];p-=p%2;r*=f^p}
scale=os;return r
}
# Return squarefree core of x
define core(x) {
auto os;
os=scale;scale=0
x/=squarepart(x)
scale=os;return x
}
# Count the number of (non-unique) prime factors of x
# e.g. 60 = 2^2*3^1*5^1 -> 2 + 1 + 1 = 4
define count_factors(x) {
auto i,c,fp[];
if(x<0)return count_factors(-x)+1;
if(x==0||x==1)return 0;
.=fac_store(fp[],x)
for(i=0;fp[i];i+=2)c+=fp[i+1]
return c;
}
# Count the number of unique prime factors of x
# e.g. 84 = [2]^2*[3]^1*[7]^1 -> #{2,3,7} = 3
define count_unique_factors(x) {
auto i,d,fp[];
if(x<0)return count_unique_factors(-x)+1;
if(x==0||x==1)return 0;
.=fac_store(fp[],x);
for(i=0;fp[i];i+=2).=d++
return d;
}
# Returns 0 if non-squarefree,
# 1 for 1 and all products of an even number of unique primes
# -1 otherwise
define mobius(x) {
return has_freedom(x,2);
}
# Determine the so-called arithmetic derivative of x
define arithmetic_derivative(x) {
auto os,i,f,n,d,fp[];
if(x<0)return -arithmetic_derivative(-x)
os=scale;scale=0;x/=1
if(x==0||x==1){scale=os;return 0}
.=fac_store(fp[],x);n=0;d=1
for(i=0;f=fp[i];i+=2){n=n*f+d*fp[i+1];d*=f}
n=(n*x)/d
scale=os;return n
}
### Storage and output of divisors of a number ###
# Count the number of divisors of x (prime or otherwise)
define count_divisors(x) {
auto i,c,p,fp[];
i=scale;x/=1;scale=i
if(x<0)return 2*count_divisors(-x);
if(x==0||x==1)return x
.=fac_store(fp[],x);
c=1;for(i=1;p=fp[i];i+=2)c*=1+p
return c
}
# Workhorse function for the below
define divisors_sp_(*divs[],x,print_) {
# opts: 1 -> print; 0 -> store
auto os,s,c,p,m,i,j,k,f,fp[]
os=scale;scale=0;x/=1
if(x==0||x==1){
if(!print_){divs[0]=x;divs[1]=0}
scale=os;return x;
}
.=fac_store(fp[],x)
c=1;for(i=1;(p=++fp[i++])>1;i++)c*=p
.=c--
s=1;if(x<0){s=-1;x=-x}
if(print_){
print s,", "
} else {
divs[0]=s
if(c>max_array_){print "too many divisors to store. storing as many as possible\n"}
}
for(i=1;imax_array_-1)c=max_array-1;divs[c]=s*x;divs[c+1]=0}
scale=os;return s*x
}
# Displays all divisors of x in a logical but unsorted order
define divisors_print( x) { auto d[]; return divisors_sp_(d[],x,1); }
# Store calculated divisors in given array in same logical but unsorted order
# . see array.bc for sorting and rudimentary printing of arrays
define divisors_store(*d[],x) { return divisors_sp_(d[],x,0); }
# Returns the sum of the divisors of x where each divisor is raised
# to the power n; e.g. sigma(2,6) -> 1^2 + 2^2 + 3^2 + 6^2 = 50
# . only supports integer n at present
define sigma(n,x) {
auto os,i,u,d,f,fp[];
os=scale;scale=0;x/=1;n/=1
if(n==0){scale=os;return count_divisors(x)}
if(n<0){scale=os;n*=-1;return sigma(n,x)/x^n}
.=fac_store(fp[],x);u=d=1
if(x<0)x=0
if(x==0||x==1)return x;
for(i=0;fp[i];i+=2){u*=(f=fp[i]^n)^(fp[i+1]+1)-1;d*=f-1}
u/=d;scale=os;return u;
}
/* Old slow version of sigma
supports integer and half-integer n
define sigma_slow(n,x) {
auto os,c,p,m,h,i,j,k,f,sum,fp[];
if(n==0)return count_divisors(x);
n+=n
os=scale;scale=0;x/=1;n/=1
if(x<0)x=0
if(x==0||x==1){scale=os;return x;}
p=h=n;n/=2;h-=n+n
if(p<0){scale=os;n=-n;sum=sigma(-p/2,x)/x^n;if(h)sum/=sqrt(x);return sum}
.=fac_store(fp[],x)
c=1;for(i=1;(p=++fp[i++])>1;i++)c*=p
.=c--;p=x^n;if(h){scale=os;p*=sqrt(x);scale=0};sum=1+p
for(i=1;i prime + 1, 2^x -> 2^(x+1)-1, 6 -> 12
define sum_of_divisors(x) {
return sigma(1,x);
}
# Computes the Euler totient function for x
define totient(x) {
auto i,t,f,fp[];
.=fac_store(fp[],x);t=1
if(x==0||x==1)return x;
for(i=0;fp[i];i+=2)t*=((f=fp[i])-1)*f^(fp[i+1]-1)
return t
}
# Computes Ramanujan's c_q function for n (given q)
define ramanujan_c(q,n) {
auto i,c,d,t,f,p,fp[];
if(q<0||n<0)return 0;
if(q==1)return 1;
if(n==1)return mobius(q);
if(n==q)return totient(q);
n=q/int_gcd(q,n)
if(n==1)return totient(q);
.=fac_store(fp[],n);t=1;c=d=0;
for(i=0;fp[i];i+=2){
t*=((f=fp[i])-1)*f^((p=fp[i+1])-1)
c+=p;.=d++
}
if(c!=d){c=0}else{c=(-1)^d}
return c*totient(q)/t # mobius(n')*totient(q)/totient(n')
}
### Exploring prime neighbourhood ###
# Finds and returns the nearest prime > x
define nextprime(x) {
auto os,ox;
if(x<0)return -prevprime(-x)
if(x<2)return 2
if(x<3)return 3
os=scale;scale=0
ox=x
if(x/1= x
define nextprime_ifnotprime(x) {
if(is_prime(x))return x;
return nextprime(x)
}
# Finds and returns the nearest prime < x
define prevprime(x) {
auto os,ox;
if(x<0)return -nextprime(-x)
if(x<=2)return -2
if(x<=3)return 2
if(x<=5)return 3
os=scale;scale=0
ox=x;x/=1
if(x%2){if(x==ox)x-=2}else{.=x--}
while(!is_prime(x)&&x>0)x-=2
if(x<2)return x=-2
scale=os;return x
}
# nearest prime <= x
define prevprime_ifnotprime(x) {
if(is_prime(x))return x;
return prevprime(x)
}
# Find the nearest prime to x (inclusive)
define nearestprime(x) {
auto np,pp;
if(is_prime(x))return x;
np=nextprime(x)
pp=prevprime(x)
if(np-x>x-pp)return pp
return np
}
### Relatives of the Primorial / Factorial
# Primorial
define primorial(n) {
auto i,pm,p;
pm=1;p=2
if(prime[max_array_])for(i=1;i<=max_array_&&p=prime[i]<=n;i++)pm*=p
for(.=.;p<=n;p=nextprime(p))pm*=p
return pm
}
# Subprimorial
define subprimorial(n) {
auto i,pm,p;
pm=1;p=2
if(prime[max_array_])for(i=2;i<=max_array_&&p=prime[i]<=n;i++)pm*=p-1
for(.=.;p<=n;p=nextprime(p))pm*=p-1
return pm
}