Anion Homepage
Updates
11-04-2008 - Version 4 released. Changes include:
- Fixed incompatibilities with the complex library.
- Added multiple new functions.
- Added ability to specify default complex axis.
05-04-2007 - Major changes - now supports a whole new batch of functions,
some bugs have been fixed, and the code has been generally cleaned up.
26-03-2007 - Added support for left and right power functions.
22-03-2007 - Added support for exponential and log functions.
22-03-2007 - Added support for trigonometric functions.
22-03-2007 - Added support for hyperbolic functions.
22-03-2007 - Added support for arg function.
22-03-2007 - Added support for polar function.
22-03-2007 - Added support for pow function (real exponent).
22-03-2007 - Added support for pow function (real base).
22-03-2007 - Added support for square root function.
Anion - a real, complex, quaternion and octonion (and above) class for C++
Anion is a C++ class designed to operate like (and be compatible with)
the standard complex C++ class, but for more general objects (including but
not restricted to reals, complex numbers, quaternions and octonions). It is
built around the Cayley-Dickson construction, which defines a method of
extending the reals to form the complex numbers, extending the complex
numbers to form the quaternions and so on. For more detail, search for the
Cayley-Dickson construction.
The class is designed as a work-alike for the std::complex<double>
type in C++, and supports all of the associated features (with some minor
required changes). It is also interoperable with the
std::complex<double> class, so for example addition, subtraction and
multiplication (though not division, as in general we are not dealing with
commutative objects) between objects works as would be expected. The
following list summarises the main differences:
- The polar function takes different arguments (described below), as it is
not possible in general to describe an arbitrary anion with two real
numbers.
- Because the anions are non-commutative in general, the function pow(a,b)
where both a and b are anions is somewhat ambiguous, as we have two
possible definitions, namely exp(b*log(a)) and exp(log(a)*b). To
overcome this we have defined pow(a,b) = (powl(a,b)+powr(a,b))/2, where
we have defined:
- Left power function: powl(a,b) = exp(b*log(a)).
- Right power function: powr(a,b) = exp(log(a)*b).
- Logarithm to an arbitrary base is likewise split into left and right
logarithms.
- Anion does not currently support templating of anions, so only anions
constructed of doubles are supported at present.
- Division by an anion is not defined (use either a*inv(b) or inv(b)*a
instead).
- Anion supports a large number of additional mathematical operations
which are not supported by the usual C++ complex class.
d_anion()
Constructors are:
- d_anion() - construct an anion with default value 0.
- d_anion(a) - construct anion with value a. In this case a may be a
double, complex, anionic or a string from which the value is to be
read.
- d_anion(a,b) - construct anion with value (a,b). In this case a and b
may be real, complex or anionic, or any combination of same.
Assignment operators:
- the operator = for assignment to an anion is overloaded for doubles,
complex, anions and strings.
Casting operators:
- anions may be cast as complex numbers using (complex<double>) a
(where a is an anion). If the anion a is *not* a complex then the real
parts of the left and right parts of a will be used.
Parenthesis:
- a[i] (for some non-negative integer i) will return a reference to the
ith component of a, after first 0 filling (noting that this will not
change the number represented) to ensure that this element is present in
the Cayley-Dickson tree. For example, if a=(1,(2,3)) then a[1] will
extend this to ((1,0),(2,3)) and then return a reference to the 0 in this
representation.
Miscellaneous member functions:
- a.order() returns the order of a, which is the maximum depth of the
Cayley-Dickson tree representing a (so 0 for real, 1 for complex, 2 for
quaternion etc).
- a.iscompl() returns 0 if a is real or 1 otherwise.
- a.leftpart() returns a reference to the left-hand part of the
Cayley-Dickson construction of a. In so doing it will first ensure that
a.order() >= 1 (by 0 filling if required, noting that this does not
change the number represented).
- a.rightpart() returns a reference to the right-hand part of the
Cayley-Dickson construction of a. In so doing it will first ensure that
a.order() >= 1 (by 0 filling if required, noting that this does not
change the number represented).
- a.realpart() returns the real component of a.
Macros:
- COMMUTATOR(a,b) = ((a)(b) - (b)(a))/2
- ANTICOMMUTATOR(a,b) = ((a)(b) + (b)(a))/2
- ASSOCIATOR(a,b,c) = ((a)((b)(c)) - ((a)(b))(c))/2
- ANTIASSOCIATOR(a,b,c) = ((a)((b)(c)) + ((a)(b))(c))/2
Operators: All arithmetic operators should work as expected,
except division by quaternions which we have chosen not to implement (one
may define both right and left division, and it is not clear which should
be chosen for the division operator). Stream input and output operators
are also defined. Anionic multiplication is implemented as defined by the
Cayley-Dickson construction. That is, if x = (a,b) and y = (c,d) are two
anions then the anionic product is given by:
Function and Macros
Complex functions of a single variable can be extended to the quaternion
case by noting that the set of numbers of the form R + q.I, where R and I are
real and q is an imaginary unit anion are isomorphic to complex numbers by
simple interchange of q and i (noting that q commutes with reals, qq = -1,
and the conjugate of q is -q). So, given a quaternion a, write a = R + M,
where R is real and M is entirely imaginary. If we further define I = abs(M)
and q = angle(M) we may write x as:
If f is a complex valued function of a single complex variable then,
writing:
then, given that q is indistinguishable from the complex imaginery element
i so long as q is the only imaginery present, the natural extension of f to
the anions is:
For functions of more than one variable, however, there are difficulties
due to the presence of multiple unit imaginary anions which do not in
general act like i. In particular, they will not in general commute or
multiply to give -1 in general. For this reason we largely avoid defining
such functions here.
In some cases where functions are well defined for the complex case, for
example acosh(-2), we could define an infinite number of results, as we
are going from real to complex without a well defined unit anion q to base
our result. If we assume q is complex (and we do) then q is defined up to
sign and the sign can be taken care of. If, however, we don't so restrict
q then we have an infinite family to chose from, and they all give valid
answers. We use two approaches to this problem. By default, we give a
complex result with q = i (the complex imaginary unit). Alternatively we
provide an additional form of these function with a new argument that is
used as the unit imaginary q. If the unit given is 0, however, this version
reverts to q = i.
In what follows we define:
a = R + M = R + q.I (where R,I are real and q is unit)
b = S + N = S + r.T (where S,T are real and r is unit)
x = real
y = real
The following functions are provided>
- abs(a): returns the absolute value (magnitude) |a| of a.
- arg(a): returns the argument |imagx(log(a))|*sgn(a[1]) (or |imagx(log(a))| if sgn(a[1]) = 0) of a.
- argd(a): returns the unit imaginary imagx(log(a))/arg(a) (or (0,1) if arg(a) = 0) of a.*
- argx(a): returns the complete argument imagx(log(a)) of a.
- norm(a): returns the square of the magnitude |a|^2 of a.
- angle(a): returns the direction a/|a| (or 0 if a = 0) of a.
- polar(x,y,q): returns the value x*exp(y*q) defined by the polar form (x,y*q).
- polard(x,y,q):returns the value x*exp(y*q) defined by the polar form (x,y*q).
- polarx(x,a): returns the value x*exp(a) defined by the polar form (x,a).
- sgn(a): returns the elementwise sign a.
- real(a): returns the real part of a.
- imag(a): returns the imaginary sign-corrected magnitude |imagx(a)|*sgn(a[1]) (or |imagx(a)| if sgn(a[1]) = 0) of a.
- imagd(a): returns the unit imaginary imagx(a)/imag(a) (or (0,1) if imag(a) = 0) of a (equivalent to argd(a)).*
- imagx(a): returns the complete imaginary part of a.
- conj(a): returns the conjugate R-M of a.
- inv(a): returns the inverse cong(a)/norm(a) of a.
- powl(a,b): returns the left power a^b - that is, exp(b*log(a)).*,****
- powr(a,b): returns the right power a^b - that is, exp(log(a)*b).*,****
- pow(a,b): returns the symmetrised power (powl(a,b)+powr(a,b))/2.*
- sqrt(a): returns the square root exp(log(a)/2) of a.*
- exp(a): returns the natural exponent exp(R)*cos(I) + q*exp(R)*sin(I) of a.
- log(a): returns the natural logarithm log(abs(a)) + q*atan2(I,R) of a.*,***
- log10(a): returns the base 10 logarithm log(a)/2.3025851 of a.*,***
- logbl(a,b): returns the left base b logarithm log(a)*inv(log(b)) of a.*,***,****
- logbr(a,b): returns the right base b logarithm inv(log(b))*log(a) of a.*,***,****
- logb(a,b): returns the symmetrised base b logarithm (logbl(a,b)+logbr(a,b))/2 of a.*,**
- sin(a): returns the sine sin(R)*cosh(I) + q*cos(R)*sinh(I) of a.
- cos(a): returns the cosine cos(R)*cosh(I) - q*sin(R)*sinh(I) of a.
- tan(a): returns the tangent sin(a)*inv(cos(a)) of a.
- cosec(a): returns the cosecant inv(sin(a)) of a.
- sec(a): returns the secant inv(cos(a)) of a.
- cot(a): returns the cotangent inv(tan(a)) of a.
- asin(a): returns the inverse sine of a.*,*****
- acos(a): returns the inverse cosine of a.*,*****
- atan(a): returns the inverse tangent of a.*,*****
- acosec(a): returns the inverse cosecant asin(inv(a)) of a.*
- asec(a): returns the inverse secant acos(inv(a)) of a.*
- acot(a): returns the inverse cotangent atan(inv(a)) of a.*
- sinc(a): returns the sinc sin(a)*inv(a) if |a| != 0 (1 if |a| = 0) of a.
- tanc(a): returns the tanc tan(a)*inv(a) if |a| != 0 (1 if |a| = 0) of a.
- vers(a): returns the versed sine 1-cos(a) of a.
- covers(a): returns the coversed sine 1-sin(a) of a.
- hav(a): returns the half versed sine vers(a)/2 of a.
- excosec(a): returns the external cosecant cosec(a)-1 of a.
- exsec(a): returns the exsecant sec(a)-1 of a.
- avers(a): returns the inverse versed sine acos(a+1) of a.*
- acovers(a): returns the inverse coversed sine asin(a+1) of a.*
- ahav(a): returns the inverse half versed sine avers(2*a) of a.*
- aexcosec(a): returns the inverse external cosecant acosec(a+1) of a.*
- aexsec(a): returns the inverse external secant asec(a+1) of a.*
- sinh(a): returns the hyperbolic sine sinh(R)*cos(I) + q*cosh(R)*sin(I) of a.
- cosh(a): returns the hyperbolic cosine cosh(R)*cos(I) + q*sinh(R)*sin(I) of a.
- tanh(a): returns the hyperbolic tangent sinh(a)*inv(cosh(a)) of a.
- cosech(a): returns the hyperbolic cosecant inv(sinh(a)) of a.
- sech(a): returns the hyperbolic secant inv(cosh(a)) of a.
- coth(a): returns the hyperbolic cotangent inv(tanh(a)) of a.
- asinh(a): returns the inverse hyperbolic sine of a.*,******
- acosh(a): returns the inverse hyperbolic cosine of a.*,******
- atanh(a): returns the inverse hyperbolic tangent of a.*,******
- acosech(a): returns the inverse hyperbolic cosecant asinh(inv(a)) of a.*
- asech(a): returns the inverse hyperbolic secant acosh(inv(a)) of a.*
- acoth(a): returns the inverse hyperbolic cotangent atanh(inv(a)) of a.*
- sinhc(a): returns the hyperbolic sinc sinh(a)*inv(a) if |a| != 0 (1 if |a| = 0) of a.
- tanhc(a): returns the hyperbolic tanc tanh(a)*inv(a) if |a| != 0 (1 if |a| = 0) of a.
- versh(a): returns the hyperbolic versed sine 1-cosh(a) of a.
- coversh(a): returns the hyperbolic coversed sine 1-sinh(a) of a.
- havh(a): returns the hyperbolic half versed sine versh(a)/2 of a.
- excosech(a): returns the hyperbolic external cosecant cosech(a)-1 of a.
- exsech(a): returns the hyperbolic external secant sech(a)-1 of a.
- aversh(a): returns the inverse hyperbolic versed sine acosh(a+1) of a.*
- acovrsh(a): returns the inverse hyperbolic coversed sine asinh(a+1) of a.*
- ahavh(a): returns the inverse hyperbolic half versed sine aversh(2*a) of a.*
- aexcosech(a): returns the inverse hyperbolic external cosecant acosech(a+1) of a.*
- aexsech(a): returns the inverse hyperbolic external secant asech(a+1) of a.*
- sigm(a): returns the sigmoid inv(1+exp(a)) of a.*
- gd(a): returns the gudermannian 2*atan(tanh(a/2)) of a.*
- asigm(a): returns the inverse sigmoid log(inv(a)-1) of a.*
- agd(a): returns the inverse gudermannian 2*atanh(tan(a/2)) of a.*
Notes:
* for these functions a second version is provided where the complex
imaginary q is explicitly defined as a final argument.
*** log(a) is calculated assuming a branch-cut on the negative real
axis of the imaginary component.
**** for octonions and below, powl(b,logbl(a,b)) = a (likewise powr).
***** asin(a), acos(a) and atan(a) follow the recipe of Abramowitz and
Stegun for calculation using code borrowed from gsl:

****** asinh(a), acosh(a) and atanh(a) are calculated using the formula:

Download Anion
Anion is written by Alistair Shilton (a p s h at ee dot unimelb dot edu dot au), and is
released under the
GPL licence. Source code is available for download
here. Source includes makefiles for
both unix and djgpp, and should be relatively easy to adapt to other systems.
Old versions
Version 1 source.
Version 1 library pre-compiled for DJGPP.
Old beta source.
Old beta library pre-compiled for DJGPP.
Old alpha source.
Old alpha library pre-compiled for DJGPP.
This page, its contents and style, are the responsibility of the author
and do not necessarily represent the views, policies or opinions of The
University of Melbourne.
Created : 14th October, 2004
Last Modified : 20th March 2007
Maintained by : Alistair Shilton (a p s h at ee dot unimelb dot edu dot au)