The class BigInt
is intended to represent (signed) integers of
practically unlimited range; it is currently based on the
implementation in the GMP big integer library. This code forms
the interface between CoCoALib and the big integer library upon which
it relies. It seems most unlikely that GMP will be displaced from its
position as the foremost library for big integer arithmetic; as a
consequence the class BigInt
may eventually be replaced by GMP's
own C++ interface.
The usual arithmetic operations are available with standard C++ syntax
but generally these incur run-time overhead since results are returned
through temporaries which are created and destroyed silently by the
compiler. Thus if the variables a
, b
and c
are each of
type BigInt
then a = b+c;
is a valid C++ statement for placing
the sum of b
and c
in a
, but the sum is first computed
into a hidden temporary which is then copied to a
, and then
finally the temporary is destroyed.
There is an important exception to the natural syntax: ^
does not
denote exponentiation; you must use the function power
instead.
We have chosen not to define operator^
to perform exponentiation
because it is too easy to write misleading code: for instance,
a*b^2
is interpreted by the compiler as (a*b)^2
. There is no
way to make the C++ compiler use the expected interpretation.
A single arithmetic operation and assignment may be effected slightly
faster using a less natural notation; this approach avoids using the
hidden temporaries required with the natural notation. Thus instead
of a = b+c;
one can write add(a, b, c);
. The reason for
offering both syntaxes is to allow simpler and more natural code to be
written for first versions; the time-critical parts can then be
recoded using the faster but less natural notation (after suitable
profiling tests, of course).
Arithmetic may also be performed between a BigInt
and a machine
integer. The result is always of type BigInt
(with the sole
exception of remainder by a machine integer). Do remember, though,
that operations between two machine integers are handled directly by
C++, and problems of overflow can occur.
It is important not to confuse values of type BigInt
with values of type
RingElem
which happen to belong to the ring RingZZ
. In summary, the
operations available for RingElem
are those applicable to elements of
any ordered commutative ring, whereas the range of operations on BigInt
values is wider (since we have explicit knowledge of the type).
A value of type BigInt
may be created from:
BigInt
mpz_t
value; in this case the ctor call must have first
argument CopyFromMPZ
and the second argument is the mpz_t
value to be copied
No constructor for creating a BigInt
from a std::string
(or a
char*
) is provided. This is for two reasons: (A) a technical
ambiguity in BigInt(0)
since 0
is valid as a char*
; (B) conversion
from a decimal string representation is sufficiently costly that it
should be highly visible. Conversion from a string to a value of type
BigInt
can be effected using the convert
function (see convert
) or
using operator>>
and std::istringstream
from the C++ library.
+
the sum
-
the difference
*
the product
/
floor quotient (divisor must be positive)
%
least non-negative remainder (divisor must be positive)
=
assignment
+=
, -=
, *=
, /=
, %=
-- definitions as expected; LHS must be of type BigInt
==
, !=
<
, <=
, >
, >=
-- comparison (using the normal arithmetic ordering)
-- see also the cmp
function below.
++
, --
(prefix, e.g. ++a
) use these if you can
++
, --
(postfix, e.g. a++
) avoid these if you can, as they create temporaries
IsZero(n)
-- true iff n
is zero
IsOne(n)
-- true iff n
is 1
IsMinusOne(n)
-- true iff n
is -1
IsEven(n)
-- true iff n
is even
IsOdd(n)
-- true iff n
is odd
IsSquare(n)
-- true iff n
is a perfect square
IsPower(n)
-- true iff n
is a perfect k-th power for some k > 1
IsExactIroot(X,N,r)
-- true iff N
is a perfect r
-th power, assigns iroot(N,r)
to X
sign(n)
result is machine integer: -1 if n<0
, 0 if n==0
, and +1 if n>0
.
power(a, b)
-- returns a
to the power b
(result is always a BigInt
)
cmp(a, b)
-- returns an int
which is < 0
if a < b
, or == 0
if a == b
, or > 0
if a > b
.
NumTheory
)
factorial(n)
-- factorial for non-negative n
binomial(n, r)
-- binomial coefficient
fibonacci(n)
-- n
-th Fibonacci number
abs(n)
-- the absolute value of n
log(n)
-- natural logarithm of the absolute value of n
(as a double
)
RandomBigInt(GlobalRandomSource(),lo, hi)
-- a uniform random
integer N
s.t. lo <= N <= hi
; error if lo > hi
(was
random(lo,hi)
up to v0.9946)
-- see also RandomLongStream
and RandomZZStream
.
RoundDiv(N,D)
-- rounded division of N
by D
, halves round towards +infinity
iroot(N,r)
-- the (truncated) integer part of the r
-th root of N
(see also IsExactIroot
)
isqrt(N)
-- the (truncated) integer part of the square root of N
ILogBase(N,b)
-- the integer part of log(abs(N))/log(b); error if N=0 or b<2
mantissa(n)
-- n represented as a floating-point number.
if n
is zero, produces 0.0
otherwise if n>0
a value between 0.5 and 0.999...
otherwise (when n<0
) a value between -0.5 and -0.999...
The bits of the floating point result are the topmost
bits of the binary representation of n
.
exponent(n)
-- result is a long
whose value is the least integer e such that
2^e > abs(n). If n
is zero, result is zero.
NumDigits(n, b)
-- the number of digits n
has when written in base b
(the result may sometimes to be too large by 1)
add(a, b, c)
-- a = b+c
sub(a, b, c)
-- a = b-c
mul(a, b, c)
-- a = b*c
div(a, b, c)
-- a = b/c (floor division, divisor must be positive)
mod(a, b, c)
-- a = b%c (least non-negative remainder, divisor must be positive)
quorem(a, b, c, d)
-- a = c/d, b = c%d (divisor must be positive)
divexact(a, b, c)
-- a = b/c (fast, but division must be exact)
power(a, b, c)
-- a = b^c
neg(a, b)
-- a = -b
abs(a, b)
-- a = abs(b)
mpzref(n)
-- this gives a (const) reference to the mpz_t
value inside a BigInt
object.
You should use this accessor very sparingly (but
it is handy for calling GMP functions directly).
Error conditions are signalled by exceptions. Examples of error conditions
are impossible arithmetic operations such as division by zero, overly large
arguments (e.g. second argument to binomial must fit into a machine
long
), and exhaustion of resources.
Currently the exception structure is very simplistic:
CoCoA::ErrorInfo
exception; for instance
ERR::ArgTooBig |
value supplied is too large for the answer to be computed |
ERR::BadArg |
unsuitable arg(s) supplied (or input number too large) |
ERR::BadNumBase |
the base must be between 2 and 36 |
ERR::DivByZero |
division by zero |
ERR::ExpTooBig |
exponent is too large |
ERR::IntDivByNeg |
inexact integer division by a negative divisor |
ERR::NegExp |
negative exponent |
ERR::ZeroModulus |
the modulus specified is zero |
The implementation is structurally very simple, just rather long and
tedious. The value of a BigInt
object is represented as an mpz_t
;
this is a private data member, but to facilitate interfacing with code
which uses mpz_t
values directly I have supplied the two functions
called mpzref
which allow access to this data member.
The output function turned out to be trickier than one might guess.
Part of the problem was wanting to respect the ostream
settings.
Of course, input is a mess. Nothing clever here.
Check also the documentation for MachineInt
to understand how
that class is used.
Currently functions which return BigInt
values will copy the result (upon
each return) -- an attempt to avoid the waste with proxy classes caused a
problem see
The official GMP interface is certainly more efficient, so the CoCoA library will presumably eventually switch to using GMP directly.
The power functions should handle power(0,0)
correctly; maybe they
should also allow high powers of -1,0,1 (without complaining about
the exponent being too big).
No bit operations: bit setting and checking, and/or/xor/not.
Only partial access to all the various division functions offered by the C interface to GMP. Many other GMP functions are not directly accessible.
The code is long, tedious and unilluminating. Are there any volunteers to improve it?
IsExactIroot has rather a lot of signatures.
2011
ZZ
renamed into BigInt
:
avoid confusion with RingZZ
and its name in CoCoA system
random
has changed (was random(lo,hi)
):
see RandomZZStream
, RandomLongStream