The functions in the NumTheory
file are predominantly basic
operations from number theory. Most of the functions may be
applied to machine integers or big integers (i.e. values of
type ZZ
). Please recall that computational number theory is
not the primary remit of CoCoALib, so do not expect to find a
complete collection of operations here -- you would do better to
look at Victor Shoup's NTL (Number Theory Library), or PARI/GP,
or some other specialized library/system.
Several of these functions give errors if they are handed unsuitable values:
unless otherwise indicated below the error is of type ERR::BadArg
.
The main functions available are:
gcd(m,n)
computes the non-negative gcd of m
and n
. If both args are machine integers, the result is of type unsigned long
; otherwise result is of type ZZ
.
ExtGcd(a,b,m,n)
computes the non-negative gcd of m
and n
; also sets a
and b
so that gcd = a*m+b*n
. If m
and n
are machine integers then a
and b
must be of type (signed) long
. If m
and n
are of type ZZ
then a
and b
must also be of type ZZ
.
InvMod(r,m)
computes the least positive inverse of r
modulo m
; returns 0 if the inverse does not exist. Gives error if m < 2
. Result is of type unsigned long
if m
is a machine integer; otherwise result is of type ZZ
.
lcm(m,n)
computes the non-negative lcm of m
and n
. If both args are machine integers, the result is of type unsigned long
; otherwise result is of type ZZ
. Gives error ERR::ArgTooBig
if the lcm of two machine integers is too large to fit into an unsigned long
.
IsPrime(n)
tests the positive number n
for primality (may be very slow for larger numbers). Gives error if n <= 0
.
IsProbPrime(n)
tests the positive number n
for primality (fairly fast for large numbers, but in very rare cases may falsely declare a number to be prime). Gives error if n <= 0
.
IsProbPrime(n,iters)
tests the positive number n
for primality; performs iters
iterations of the Miller-Rabin test (default value is 25). Gives error if n <= 0
.
NextPrime(n)
and PrevPrime(n)
compute next or previous positive prime (fitting into a machine integer); returns 0 if none exists. Gives error if n <= 0
.
NextProbPrime(N)
and PrevProbPrime(N)
compute next or previous positive probable prime (uses IsProbPrime
). Gives error if N <= 0
.
SmoothFactor(n,limit)
finds small prime factors of n
(up to the specified limit
); result is a factorization
object. Gives error if limit
is too large to fit into a long
.
factor(n)
finds the complete factorization of n
(may be very slow for large numbers); NB implementation incomplete
EulerPhi(n)
computes Euler's totient function of the positive number n
(i.e. the number of integers up to n
which are coprime to n
, or the degree of the n
-th cyclotomic polynomial). Gives error if n <= 0
.
PrimitiveRoot(p)
computes the least positive primitive root for the positive prime p
. Gives error if p
is not a positive prime. May be very slow for large p
(because it must factorize p-1
).
MultiplicativeOrder(res,mod)
computes multiplicative order of res
modulo mod
. Gives error if mod < 2
or gcd(res,mod)
is not 1.
PowerMod(base,exp,modulus)
computes base
to the power exp
modulo modulus
; result is least non-negative residue. If modulus
is a machine integer then the result is of type unsigned long
, otherwise the result is of type ZZ
. Gives error if modulus <= 1
. Gives ERR::DivByZero
if exp
is negative and base
cannot be inverted. If base
and exp
are both zero, it produces 1.
Correctness of ExtendedEuclideanAlg
is not immediately clear,
because the cofactor variables could conceivably overflow -- in fact
this cannot happen (at least on a binary computer): for a proof see
Shoup's book A Computational Introduction to Number Theory and Algebra,
in particular Theorem 4.3 and the comment immediately following it. There is
just one line where a harmless "overflow" could occur -- it is commented in
the code.
Several functions are more complicated than you might expect because I wanted them
to be correct for all possible machine integer inputs (e.g. including the
most negative long
value).
In some cases the function which does all the work is implemented as a file
local function operating on unsigned long
values: the function should normally
be used only via the "dispatch" functions whose args are of type MachineInteger
or ZZ
.
Several functions return unsigned long
values (which could cause
nasty surprises to unwary users unfamiliar with the foibles of unsigned values in C++).
Should there also be procedural forms of functions which return ZZ
values?
(e.g. gcd, lcm, InvMod, PowerMod, and so on).
Certain implementations of PowerMod
should be improved (e.g. to use
PowerModSmallModulus
whenever possible). Is behaviour for 0^0 correct?
LucasTest
should produce a certificate.