# 109 CHEVIE utility functions -- Decimal and complex numbers

The original incentive for the functions described in this file was to get the ability to decide if a cyclotomic number which happens to be real is positive or negative (this is needed to tell if a root of an arbitrary Coxeter group is negative or positive). Of course, there are other uses for fixed-precision real and complex numbers, which the functions described here provide. A special feature of the present implementation is that to make evaluation of cyclotomics relatively fast, a cache of primitive roots of unity is maintained (the cached values are kept for the largest precision ever used to compute them).

We first describe a general facility to build complex numbers as pairs of real numbers. The real numbers in the pairs can be of any type that GAP3 knows about: integers, rationals, cyclotomics, or elements of any ring actually.

## 109.1 Complex

`Complex( r[, i] )`

In the first form, defines a complex number whose real part is r and imaginary part is i. If omitted, i is taken to be 0. There are two special cases when there is only one argument: if r is already a complex, it is returned; and if r is a cyclotomic number, it is converted to the pair of its real and imaginary part and returned as a complex.

```    gap> Complex(0,1);
I
gap> Complex(E(3));
-1/2+ER(3)/2I
gap> Complex(E(3)^2);
-1/2-ER(3)/2I
gap> x:=X(Rationals);;x.name:="x";;Complex(0,x);
xI```

The last line shows that the arguments to `Complex` can be of any ring. Complex numbers are represented as a record with two fields, `.r` and `.i` holding the real and imaginary part respectively.

## 109.2 Operations for complex numbers

The arithmetic operations `+`, `-`, `*`, `/` and `^` work for complex numbers. They also have `Print` and `String` methods.

```    gap> Complex(0,1);
I
gap> last+1;
1+I
gap> last^2;
2I
gap> last^2;
-4
gap> last+last2;
-4+2I
gap> x:=X(Rationals);;x.name:="x";;Complex(0,x);
xI
gap> last^2;
-x^2```

Finally we should mention the `FormatGAP` method, which allows to print complex numbers in a way such that they can be read back in GAP3:

```    gap> a:=Complex(1/2,1/3);
1/2+1/3I
gap> FormatGAP(a);
"Complex(1/2,1/3)"```

## 109.3 ComplexConjugate

`ComplexConjugate( c)`

This function applies complex conjugation to its argument. It knows ho to do this for cyclotomic numbers (it then just calls `GaloisCyc(c,-1)`), complex numbers, lists (it conjugates each element of the list), polynomials (it conjugates each coefficient), and can be taught to conjugate elements `x` of an arbitrary domain by defining `x.operations.ComplexConjugate`.

```    gap> ComplexConjugate(Complex(0,1));
-I
gap> ComplexConjugate(E(3));
E(3)^2
gap> x:=X(Cyclotomics);;x.name:="x";;ComplexConjugate(x+E(3));
x + (E(3)^2)```

## 109.4 IsComplex

`IsComplex( c)`

This function returns `true` iff its argument is a complex number.

```    gap> IsComplex(Complex(1,0));
true
gap> IsComplex(E(4));
false```

## 109.5 evalf

`evalf( c [, prec] )`

The name of this function intentionally mimics that of a Maple function. It computes a fixed-precision decimal number with prec digits after the decimal point approximating its argument; if not given, prec is taken to be 10 (this can be changed via the function `SetDecimalPrecision`, see below). Trailing zeroes are not shown on output, so the actual precision may be more than the number of digits shown.

```    gap> evalf(1/3);
0.3333333333```

As one can see, the resulting `decimal numbers` have an appropriate Print method (which uses the String method).

```    gap> evalf(1/3,20);
0.33333333333333333333```

`evalf` can also be applied to cyclotomic or complex numbers, yielding a complex which is a pair of decimal numbers.

```    gap> evalf(E(3));
-0.5+0.8660254038I```

`evalf` works also for strings (the result is truncated if too precise)

```    gap> evalf(".3450000000000000000001"); # precision is 10
0.345```

and for lists (it is applied recursively to each element).

```    gap> evalf([E(5),1/7]);
[ 0.3090169944+0.9510565163I, 0.1428571429 ]```

Finally, an `evalf` method can be defined for elements of any domain. One has been defined in CHEVIE for complex numbers:

```    gap> a:=Complex(1/2,1/3);
1/2+1/3I
gap> evalf(a);
0.5+0.3333333333I```

## 109.6 Rational

`Rational(d)`

d is a `decimal` number. The function returns the rational number which is actually represented by d

```    gap> evalf(1/3);
0.3333333333
gap> Rational(last);
33333333333/100000000000```

## 109.7 SetDecimalPrecision

`SetDecimalPrecision( prec )`

This function sets the default precision to be used when converting numbers to decimal numbers without giving a second argument to `evalf`.

```    gap> SetDecimalPrecision(20);
gap> evalf(1/3);
0.33333333333333333333
gap> SetDecimalPrecision(10);```

## 109.8 Operations for decimal numbers

The arithmetic operations `+`, `-`, `*`, `/` and `^` work for decimal numbers, as well as the function `GetRoot` and the comparison functions `<`, `>`, etc... The precision of the result of an operation is that of the least precise number used. They can be raised to a fractional power: `GetRoot(d,n)` is equivalent to `d^(1/n)`. Decimal numbers also have `Print` and `String` methods.

```    gap> evalf(1/3)+1;
1.3333333333
gap> last^3;
2.3703703704
gap> evalf(E(3));
-0.5+0.8660254038I
gap> last^3;
1
gap> evalf(ER(2));
1.4142135624
gap> GetRoot(evalf(2),2);
1.4142135624
gap> evalf(2)^(1/2);
1.4142135624
gap> evalf(1/3,20);
0.33333333333333333333
gap> last+evalf(1);
1.3333333333
gap> last2+1;
1.33333333333333333333```

Finally we should mention the `FormatGAP` method, which, given option `GAP`, allows to print decimal numbers in a way such that they can be read back in GAP3:

```    gap> FormatGAP(evalf(1/3));
"evalf(33333333333/100000000000,10)"```

## 109.9 Pi

`Pi( [prec])`

This function returns a decimal approximation to π, with prec digits (or if prec is omitted with the default umber of digits defined by `SetDecimalPrecision`, initially 10).

```    gap> Pi();
3.1415926536
gap> Pi(34);
3.1415926535897932384626433832795029```

## 109.10 Exp

`Exp( x)`

This function returns the complex exponential of x. The argument should be a decimal or a decimal complex. The result has as many digits of precision as the argument.

```    gap> Exp(evalf(1));
2.7182818285
gap> Exp(evalf(1,20));
2.71828182845904523536
gap> Exp(Pi()*E(4));
-1```

The code of `Exp` shows how easy it is to use decimal numbers.

```    gap> Print(Exp,"\n");
function ( x )
local  res, i, p, z;
if IsCyc( x )  then
x := evalf( x );
fi;
z := 0 * x;
res := z;
p := 1;
i := 1;
while p <> z  do
res := p + res;
p := 1 / i * p * x;
i := i + 1;
od;
return res;
end```

## 109.11 IsDecimal

`IsDecimal( x)`

returns `true` iff x is a decimal number.

```    gap> IsDecimal(evalf(1));
true
gap> IsDecimal(evalf(E(3)));
false```

gap3-jm
24 Apr 2021