+These functions are all objects of class @code{function}. They accept
+one or more expressions as arguments and return one expression. If the
+arguments are not numerical, the evaluation of the function may be
+halted, as it does in the next example, showing how a function returns
+itself twice and finally an expression that may be really useful:
+
+@cindex Gamma function
+@cindex @code{subs()}
+@example
+ ...
+ symbol x("x"), y("y");
+ ex foo = x+y/2;
+ cout << tgamma(foo) << endl;
+ // -> tgamma(x+(1/2)*y)
+ ex bar = foo.subs(y==1);
+ cout << tgamma(bar) << endl;
+ // -> tgamma(x+1/2)
+ ex foobar = bar.subs(x==7);
+ cout << tgamma(foobar) << endl;
+ // -> (135135/128)*Pi^(1/2)
+ ...
+@end example
+
+Besides evaluation most of these functions allow differentiation, series
+expansion and so on. Read the next chapter in order to learn more about
+this.
+
+
+@node Relations, Indexed objects, Mathematical functions, Basic Concepts
+@c node-name, next, previous, up
+@section Relations
+@cindex @code{relational} (class)
+
+Sometimes, a relation holding between two expressions must be stored
+somehow. The class @code{relational} is a convenient container for such
+purposes. A relation is by definition a container for two @code{ex} and
+a relation between them that signals equality, inequality and so on.
+They are created by simply using the C++ operators @code{==}, @code{!=},
+@code{<}, @code{<=}, @code{>} and @code{>=} between two expressions.
+
+@xref{Mathematical functions}, for examples where various applications
+of the @code{.subs()} method show how objects of class relational are
+used as arguments. There they provide an intuitive syntax for
+substitutions. They are also used as arguments to the @code{ex::series}
+method, where the left hand side of the relation specifies the variable
+to expand in and the right hand side the expansion point. They can also
+be used for creating systems of equations that are to be solved for
+unknown variables. But the most common usage of objects of this class
+is rather inconspicuous in statements of the form @code{if
+(expand(pow(a+b,2))==a*a+2*a*b+b*b) @{...@}}. Here, an implicit
+conversion from @code{relational} to @code{bool} takes place. Note,
+however, that @code{==} here does not perform any simplifications, hence
+@code{expand()} must be called explicitly.
+
+
+@node Indexed objects, Non-commutative objects, Relations, Basic Concepts
+@c node-name, next, previous, up
+@section Indexed objects
+
+GiNaC allows you to handle expressions containing general indexed objects in
+arbitrary spaces. It is also able to canonicalize and simplify such
+expressions and perform symbolic dummy index summations. There are a number
+of predefined indexed objects provided, like delta and metric tensors.
+
+There are few restrictions placed on indexed objects and their indices and
+it is easy to construct nonsense expressions, but our intention is to
+provide a general framework that allows you to implement algorithms with
+indexed quantities, getting in the way as little as possible.
+
+@cindex @code{idx} (class)
+@cindex @code{indexed} (class)
+@subsection Indexed quantities and their indices
+
+Indexed expressions in GiNaC are constructed of two special types of objects,
+@dfn{index objects} and @dfn{indexed objects}.
+
+@itemize @bullet
+
+@cindex contravariant
+@cindex covariant
+@cindex variance
+@item Index objects are of class @code{idx} or a subclass. Every index has
+a @dfn{value} and a @dfn{dimension} (which is the dimension of the space
+the index lives in) which can both be arbitrary expressions but are usually
+a number or a simple symbol. In addition, indices of class @code{varidx} have
+a @dfn{variance} (they can be co- or contravariant), and indices of class
+@code{spinidx} have a variance and can be @dfn{dotted} or @dfn{undotted}.
+
+@item Indexed objects are of class @code{indexed} or a subclass. They
+contain a @dfn{base expression} (which is the expression being indexed), and
+one or more indices.
+
+@end itemize
+
+@strong{Note:} when printing expressions, covariant indices and indices
+without variance are denoted @samp{.i} while contravariant indices are
+denoted @samp{~i}. Dotted indices have a @samp{*} in front of the index
+value. In the following, we are going to use that notation in the text so
+instead of @math{A^i_jk} we will write @samp{A~i.j.k}. Index dimensions are
+not visible in the output.
+
+A simple example shall illustrate the concepts:
+
+@example
+#include <ginac/ginac.h>
+using namespace std;
+using namespace GiNaC;
+
+int main()
+@{
+ symbol i_sym("i"), j_sym("j");
+ idx i(i_sym, 3), j(j_sym, 3);
+
+ symbol A("A");
+ cout << indexed(A, i, j) << endl;
+ // -> A.i.j
+ ...
+@end example
+
+The @code{idx} constructor takes two arguments, the index value and the
+index dimension. First we define two index objects, @code{i} and @code{j},
+both with the numeric dimension 3. The value of the index @code{i} is the
+symbol @code{i_sym} (which prints as @samp{i}) and the value of the index
+@code{j} is the symbol @code{j_sym} (which prints as @samp{j}). Next we
+construct an expression containing one indexed object, @samp{A.i.j}. It has
+the symbol @code{A} as its base expression and the two indices @code{i} and
+@code{j}.
+
+Note the difference between the indices @code{i} and @code{j} which are of
+class @code{idx}, and the index values which are the sybols @code{i_sym}
+and @code{j_sym}. The indices of indexed objects cannot directly be symbols
+or numbers but must be index objects. For example, the following is not
+correct and will raise an exception:
+
+@example
+symbol i("i"), j("j");
+e = indexed(A, i, j); // ERROR: indices must be of type idx
+@end example
+
+You can have multiple indexed objects in an expression, index values can
+be numeric, and index dimensions symbolic:
+
+@example
+ ...
+ symbol B("B"), dim("dim");
+ cout << 4 * indexed(A, i)
+ + indexed(B, idx(j_sym, 4), idx(2, 3), idx(i_sym, dim)) << endl;
+ // -> B.j.2.i+4*A.i
+ ...
+@end example
+
+@code{B} has a 4-dimensional symbolic index @samp{k}, a 3-dimensional numeric
+index of value 2, and a symbolic index @samp{i} with the symbolic dimension
+@samp{dim}. Note that GiNaC doesn't automatically notify you that the free
+indices of @samp{A} and @samp{B} in the sum don't match (you have to call
+@code{simplify_indexed()} for that, see below).
+
+In fact, base expressions, index values and index dimensions can be
+arbitrary expressions:
+
+@example
+ ...
+ cout << indexed(A+B, idx(2*i_sym+1, dim/2)) << endl;
+ // -> (B+A).(1+2*i)
+ ...
+@end example
+
+It's also possible to construct nonsense like @samp{Pi.sin(x)}. You will not
+get an error message from this but you will probably not be able to do
+anything useful with it.
+
+@cindex @code{get_value()}
+@cindex @code{get_dimension()}
+The methods
+
+@example
+ex idx::get_value(void);
+ex idx::get_dimension(void);
+@end example
+
+return the value and dimension of an @code{idx} object. If you have an index
+in an expression, such as returned by calling @code{.op()} on an indexed
+object, you can get a reference to the @code{idx} object with the function
+@code{ex_to_idx()} on the expression.
+
+There are also the methods
+
+@example
+bool idx::is_numeric(void);
+bool idx::is_symbolic(void);
+bool idx::is_dim_numeric(void);
+bool idx::is_dim_symbolic(void);
+@end example
+
+for checking whether the value and dimension are numeric or symbolic
+(non-numeric). Using the @code{info()} method of an index (see @ref{Information
+About Expressions}) returns information about the index value.
+
+@cindex @code{varidx} (class)
+If you need co- and contravariant indices, use the @code{varidx} class:
+
+@example
+ ...
+ symbol mu_sym("mu"), nu_sym("nu");
+ varidx mu(mu_sym, 4), nu(nu_sym, 4); // default is contravariant ~mu, ~nu
+ varidx mu_co(mu_sym, 4, true); // covariant index .mu
+
+ cout << indexed(A, mu, nu) << endl;
+ // -> A~mu~nu
+ cout << indexed(A, mu_co, nu) << endl;
+ // -> A.mu~nu
+ cout << indexed(A, mu.toggle_variance(), nu) << endl;
+ // -> A.mu~nu
+ ...
+@end example
+
+A @code{varidx} is an @code{idx} with an additional flag that marks it as
+co- or contravariant. The default is a contravariant (upper) index, but
+this can be overridden by supplying a third argument to the @code{varidx}
+constructor. The two methods
+
+@example
+bool varidx::is_covariant(void);
+bool varidx::is_contravariant(void);
+@end example
+
+allow you to check the variance of a @code{varidx} object (use @code{ex_to_varidx()}
+to get the object reference from an expression). There's also the very useful
+method
+
+@example
+ex varidx::toggle_variance(void);
+@end example
+
+which makes a new index with the same value and dimension but the opposite
+variance. By using it you only have to define the index once.
+
+@cindex @code{spinidx} (class)
+The @code{spinidx} class provides dotted and undotted variant indices, as
+used in the Weyl-van-der-Waerden spinor formalism:
+
+@example
+ ...
+ symbol K("K"), C_sym("C"), D_sym("D");
+ spinidx C(C_sym, 2), D(D_sym); // default is 2-dimensional,
+ // contravariant, undotted
+ spinidx C_co(C_sym, 2, true); // covariant index
+ spinidx D_dot(D_sym, 2, false, true); // contravariant, dotted
+ spinidx D_co_dot(D_sym, 2, true, true); // covariant, dotted
+
+ cout << indexed(K, C, D) << endl;
+ // -> K~C~D
+ cout << indexed(K, C_co, D_dot) << endl;
+ // -> K.C~*D
+ cout << indexed(K, D_co_dot, D) << endl;
+ // -> K.*D~D
+ ...
+@end example
+
+A @code{spinidx} is a @code{varidx} with an additional flag that marks it as
+dotted or undotted. The default is undotted but this can be overridden by
+supplying a fourth argument to the @code{spinidx} constructor. The two
+methods
+
+@example
+bool spinidx::is_dotted(void);
+bool spinidx::is_undotted(void);
+@end example
+
+allow you to check whether or not a @code{spinidx} object is dotted (use
+@code{ex_to_spinidx()} to get the object reference from an expression).
+Finally, the two methods
+
+@example
+ex spinidx::toggle_dot(void);
+ex spinidx::toggle_variance_dot(void);
+@end example
+
+create a new index with the same value and dimension but opposite dottedness
+and the same or opposite variance.
+
+@subsection Substituting indices
+
+@cindex @code{subs()}
+Sometimes you will want to substitute one symbolic index with another
+symbolic or numeric index, for example when calculating one specific element
+of a tensor expression. This is done with the @code{.subs()} method, as it
+is done for symbols (see @ref{Substituting Expressions}).
+
+You have two possibilities here. You can either substitute the whole index
+by another index or expression:
+
+@example
+ ...
+ ex e = indexed(A, mu_co);
+ cout << e << " becomes " << e.subs(mu_co == nu) << endl;
+ // -> A.mu becomes A~nu
+ cout << e << " becomes " << e.subs(mu_co == varidx(0, 4)) << endl;
+ // -> A.mu becomes A~0
+ cout << e << " becomes " << e.subs(mu_co == 0) << endl;
+ // -> A.mu becomes A.0
+ ...
+@end example
+
+The third example shows that trying to replace an index with something that
+is not an index will substitute the index value instead.
+
+Alternatively, you can substitute the @emph{symbol} of a symbolic index by
+another expression:
+
+@example
+ ...
+ ex e = indexed(A, mu_co);
+ cout << e << " becomes " << e.subs(mu_sym == nu_sym) << endl;
+ // -> A.mu becomes A.nu
+ cout << e << " becomes " << e.subs(mu_sym == 0) << endl;
+ // -> A.mu becomes A.0
+ ...
+@end example
+
+As you see, with the second method only the value of the index will get
+substituted. Its other properties, including its dimension, remain unchanged.
+If you want to change the dimension of an index you have to substitute the
+whole index by another one with the new dimension.
+
+Finally, substituting the base expression of an indexed object works as
+expected:
+
+@example
+ ...
+ ex e = indexed(A, mu_co);
+ cout << e << " becomes " << e.subs(A == A+B) << endl;
+ // -> A.mu becomes (B+A).mu
+ ...
+@end example
+
+@subsection Symmetries
+
+Indexed objects can be declared as being totally symmetric or antisymmetric
+with respect to their indices. In this case, GiNaC will automatically bring
+the indices into a canonical order which allows for some immediate
+simplifications:
+
+@example
+ ...
+ cout << indexed(A, indexed::symmetric, i, j)
+ + indexed(A, indexed::symmetric, j, i) << endl;
+ // -> 2*A.j.i
+ cout << indexed(B, indexed::antisymmetric, i, j)
+ + indexed(B, indexed::antisymmetric, j, j) << endl;
+ // -> -B.j.i
+ cout << indexed(B, indexed::antisymmetric, i, j)
+ + indexed(B, indexed::antisymmetric, j, i) << endl;
+ // -> 0
+ ...
+@end example
+
+@cindex @code{get_free_indices()}
+@cindex Dummy index
+@subsection Dummy indices
+
+GiNaC treats certain symbolic index pairs as @dfn{dummy indices} meaning
+that a summation over the index range is implied. Symbolic indices which are
+not dummy indices are called @dfn{free indices}. Numeric indices are neither
+dummy nor free indices.
+
+To be recognized as a dummy index pair, the two indices must be of the same
+class and dimension and their value must be the same single symbol (an index
+like @samp{2*n+1} is never a dummy index). If the indices are of class
+@code{varidx} they must also be of opposite variance; if they are of class
+@code{spinidx} they must be both dotted or both undotted.
+
+The method @code{.get_free_indices()} returns a vector containing the free
+indices of an expression. It also checks that the free indices of the terms
+of a sum are consistent:
+
+@example
+@{
+ symbol A("A"), B("B"), C("C");
+
+ symbol i_sym("i"), j_sym("j"), k_sym("k"), l_sym("l");
+ idx i(i_sym, 3), j(j_sym, 3), k(k_sym, 3), l(l_sym, 3);
+
+ ex e = indexed(A, i, j) * indexed(B, j, k) + indexed(C, k, l, i, l);
+ cout << exprseq(e.get_free_indices()) << endl;
+ // -> (.i,.k)
+ // 'j' and 'l' are dummy indices
+
+ symbol mu_sym("mu"), nu_sym("nu"), rho_sym("rho"), sigma_sym("sigma");
+ varidx mu(mu_sym, 4), nu(nu_sym, 4), rho(rho_sym, 4), sigma(sigma_sym, 4);
+
+ e = indexed(A, mu, nu) * indexed(B, nu.toggle_variance(), rho)
+ + indexed(C, mu, sigma, rho, sigma.toggle_variance());
+ cout << exprseq(e.get_free_indices()) << endl;
+ // -> (~mu,~rho)
+ // 'nu' is a dummy index, but 'sigma' is not
+
+ e = indexed(A, mu, mu);
+ cout << exprseq(e.get_free_indices()) << endl;
+ // -> (~mu)
+ // 'mu' is not a dummy index because it appears twice with the same
+ // variance
+
+ e = indexed(A, mu, nu) + 42;
+ cout << exprseq(e.get_free_indices()) << endl; // ERROR
+ // this will throw an exception:
+ // "add::get_free_indices: inconsistent indices in sum"
+@}
+@end example
+
+@cindex @code{simplify_indexed()}
+@subsection Simplifying indexed expressions
+
+In addition to the few automatic simplifications that GiNaC performs on
+indexed expressions (such as re-ordering the indices of symmetric tensors
+and calculating traces and convolutions of matrices and predefined tensors)
+there is the method
+
+@example
+ex ex::simplify_indexed(void);
+ex ex::simplify_indexed(const scalar_products & sp);
+@end example
+
+that performs some more expensive operations:
+
+@itemize
+@item it checks the consistency of free indices in sums in the same way
+ @code{get_free_indices()} does
+@item it tries to give dumy indices that appear in different terms of a sum
+ the same name to allow simplifications like @math{a_i*b_i-a_j*b_j=0}
+@item it (symbolically) calculates all possible dummy index summations/contractions
+ with the predefined tensors (this will be explained in more detail in the
+ next section)
+@item as a special case of dummy index summation, it can replace scalar products
+ of two tensors with a user-defined value
+@end itemize
+
+The last point is done with the help of the @code{scalar_products} class
+which is used to store scalar products with known values (this is not an
+arithmetic class, you just pass it to @code{simplify_indexed()}):
+
+@example
+@{
+ symbol A("A"), B("B"), C("C"), i_sym("i");
+ idx i(i_sym, 3);
+
+ scalar_products sp;
+ sp.add(A, B, 0); // A and B are orthogonal
+ sp.add(A, C, 0); // A and C are orthogonal
+ sp.add(A, A, 4); // A^2 = 4 (A has length 2)
+
+ e = indexed(A + B, i) * indexed(A + C, i);
+ cout << e << endl;
+ // -> (B+A).i*(A+C).i
+
+ cout << e.expand(expand_options::expand_indexed).simplify_indexed(sp)
+ << endl;
+ // -> 4+C.i*B.i
+@}
+@end example
+
+The @code{scalar_products} object @code{sp} acts as a storage for the
+scalar products added to it with the @code{.add()} method. This method
+takes three arguments: the two expressions of which the scalar product is
+taken, and the expression to replace it with. After @code{sp.add(A, B, 0)},
+@code{simplify_indexed()} will replace all scalar products of indexed
+objects that have the symbols @code{A} and @code{B} as base expressions
+with the single value 0. The number, type and dimension of the indices
+don't matter; @samp{A~mu~nu*B.mu.nu} would also be replaced by 0.
+
+@cindex @code{expand()}
+The example above also illustrates a feature of the @code{expand()} method:
+if passed the @code{expand_indexed} option it will distribute indices
+over sums, so @samp{(A+B).i} becomes @samp{A.i+B.i}.
+
+@cindex @code{tensor} (class)
+@subsection Predefined tensors
+
+Some frequently used special tensors such as the delta, epsilon and metric
+tensors are predefined in GiNaC. They have special properties when
+contracted with other tensor expressions and some of them have constant
+matrix representations (they will evaluate to a number when numeric
+indices are specified).
+
+@cindex @code{delta_tensor()}
+@subsubsection Delta tensor
+
+The delta tensor takes two indices, is symmetric and has the matrix
+representation @code{diag(1,1,1,...)}. It is constructed by the function
+@code{delta_tensor()}:
+
+@example
+@{
+ symbol A("A"), B("B");
+
+ idx i(symbol("i"), 3), j(symbol("j"), 3),
+ k(symbol("k"), 3), l(symbol("l"), 3);
+
+ ex e = indexed(A, i, j) * indexed(B, k, l)
+ * delta_tensor(i, k) * delta_tensor(j, l) << endl;
+ cout << e.simplify_indexed() << endl;
+ // -> B.i.j*A.i.j
+
+ cout << delta_tensor(i, i) << endl;
+ // -> 3
+@}
+@end example
+
+@cindex @code{metric_tensor()}
+@subsubsection General metric tensor
+
+The function @code{metric_tensor()} creates a general symmetric metric
+tensor with two indices that can be used to raise/lower tensor indices. The
+metric tensor is denoted as @samp{g} in the output and if its indices are of
+mixed variance it is automatically replaced by a delta tensor:
+
+@example
+@{
+ symbol A("A");
+
+ varidx mu(symbol("mu"), 4), nu(symbol("nu"), 4), rho(symbol("rho"), 4);
+
+ ex e = metric_tensor(mu, nu) * indexed(A, nu.toggle_variance(), rho);
+ cout << e.simplify_indexed() << endl;
+ // -> A~mu~rho
+
+ e = delta_tensor(mu, nu.toggle_variance()) * metric_tensor(nu, rho);
+ cout << e.simplify_indexed() << endl;
+ // -> g~mu~rho
+
+ e = metric_tensor(mu.toggle_variance(), nu.toggle_variance())
+ * metric_tensor(nu, rho);
+ cout << e.simplify_indexed() << endl;
+ // -> delta.mu~rho
+
+ e = metric_tensor(nu.toggle_variance(), rho.toggle_variance())
+ * metric_tensor(mu, nu) * (delta_tensor(mu.toggle_variance(), rho)
+ + indexed(A, mu.toggle_variance(), rho));
+ cout << e.simplify_indexed() << endl;
+ // -> 4+A.rho~rho
+@}
+@end example
+
+@cindex @code{lorentz_g()}
+@subsubsection Minkowski metric tensor
+
+The Minkowski metric tensor is a special metric tensor with a constant
+matrix representation which is either @code{diag(1, -1, -1, ...)} (negative
+signature, the default) or @code{diag(-1, 1, 1, ...)} (positive signature).
+It is created with the function @code{lorentz_g()} (although it is output as
+@samp{eta}):
+
+@example
+@{
+ varidx mu(symbol("mu"), 4);
+
+ e = delta_tensor(varidx(0, 4), mu.toggle_variance())
+ * lorentz_g(mu, varidx(0, 4)); // negative signature
+ cout << e.simplify_indexed() << endl;
+ // -> 1
+
+ e = delta_tensor(varidx(0, 4), mu.toggle_variance())
+ * lorentz_g(mu, varidx(0, 4), true); // positive signature
+ cout << e.simplify_indexed() << endl;
+ // -> -1
+@}
+@end example
+
+@cindex @code{spinor_metric()}
+@subsubsection Spinor metric tensor
+
+The function @code{spinor_metric()} creates an antisymmetric tensor with
+two indices that is used to raise/lower indices of 2-component spinors.
+It is output as @samp{eps}:
+
+@example
+@{
+ symbol psi("psi");
+
+ spinidx A(symbol("A")), B(symbol("B")), C(symbol("C"));
+ ex A_co = A.toggle_variance(), B_co = B.toggle_variance();
+
+ e = spinor_metric(A, B) * indexed(psi, B_co);
+ cout << e.simplify_indexed() << endl;
+ // -> psi~A
+
+ e = spinor_metric(A, B) * indexed(psi, A_co);
+ cout << e.simplify_indexed() << endl;
+ // -> -psi~B
+
+ e = spinor_metric(A_co, B_co) * indexed(psi, B);
+ cout << e.simplify_indexed() << endl;
+ // -> -psi.A
+
+ e = spinor_metric(A_co, B_co) * indexed(psi, A);
+ cout << e.simplify_indexed() << endl;
+ // -> psi.B
+
+ e = spinor_metric(A_co, B_co) * spinor_metric(A, B);
+ cout << e.simplify_indexed() << endl;
+ // -> 2
+
+ e = spinor_metric(A_co, B_co) * spinor_metric(B, C);
+ cout << e.simplify_indexed() << endl;
+ // -> -delta.A~C
+@}
+@end example
+
+The matrix representation of the spinor metric is @code{[[ [[ 0, 1 ]], [[ -1, 0 ]] ]]}.
+
+@cindex @code{epsilon_tensor()}
+@cindex @code{lorentz_eps()}
+@subsubsection Epsilon tensor
+
+The epsilon tensor is totally antisymmetric, its number of indices is equal
+to the dimension of the index space (the indices must all be of the same
+numeric dimension), and @samp{eps.1.2.3...} (resp. @samp{eps~0~1~2...}) is
+defined to be 1. Its behaviour with indices that have a variance also
+depends on the signature of the metric. Epsilon tensors are output as
+@samp{eps}.
+
+There are three functions defined to create epsilon tensors in 2, 3 and 4
+dimensions:
+
+@example
+ex epsilon_tensor(const ex & i1, const ex & i2);
+ex epsilon_tensor(const ex & i1, const ex & i2, const ex & i3);
+ex lorentz_eps(const ex & i1, const ex & i2, const ex & i3, const ex & i4, bool pos_sig = false);
+@end example
+
+The first two functions create an epsilon tensor in 2 or 3 Euclidean
+dimensions, the last function creates an epsilon tensor in a 4-dimensional
+Minkowski space (the last @code{bool} argument specifies whether the metric
+has negative or positive signature, as in the case of the Minkowski metric
+tensor).
+
+@subsection Linear algebra
+
+The @code{matrix} class can be used with indices to do some simple linear
+algebra (linear combinations and products of vectors and matrices, traces
+and scalar products):
+
+@example
+@{
+ idx i(symbol("i"), 2), j(symbol("j"), 2);
+ symbol x("x"), y("y");
+
+ matrix A(2, 2, lst(1, 2, 3, 4)), X(2, 1, lst(x, y));
+
+ cout << indexed(A, i, i) << endl;
+ // -> 5
+
+ ex e = indexed(A, i, j) * indexed(X, j);
+ cout << e.simplify_indexed() << endl;
+ // -> [[ [[2*y+x]], [[4*y+3*x]] ]].i
+
+ e = indexed(A, i, j) * indexed(X, i) + indexed(X, j) * 2;
+ cout << e.simplify_indexed() << endl;
+ // -> [[ [[3*y+3*x,6*y+2*x]] ]].j
+@}
+@end example
+
+You can of course obtain the same results with the @code{matrix::add()},
+@code{matrix::mul()} and @code{matrix::trace()} methods but with indices you
+don't have to worry about transposing matrices.
+
+Matrix indices always start at 0 and their dimension must match the number
+of rows/columns of the matrix. Matrices with one row or one column are
+vectors and can have one or two indices (it doesn't matter whether it's a
+row or a column vector). Other matrices must have two indices.
+
+You should be careful when using indices with variance on matrices. GiNaC
+doesn't look at the variance and doesn't know that @samp{F~mu~nu} and
+@samp{F.mu.nu} are different matrices. In this case you should use only
+one form for @samp{F} and explicitly multiply it with a matrix representation
+of the metric tensor.
+
+
+@node Non-commutative objects, Methods and Functions, Indexed objects, Basic Concepts
+@c node-name, next, previous, up
+@section Non-commutative objects
+
+GiNaC is equipped to handle certain non-commutative algebras. Three classes of
+non-commutative objects are built-in which are mostly of use in high energy
+physics:
+
+@itemize
+@item Clifford (Dirac) algebra (class @code{clifford})
+@item su(3) Lie algebra (class @code{color})
+@item Matrices (unindexed) (class @code{matrix})
+@end itemize
+
+The @code{clifford} and @code{color} classes are subclasses of
+@code{indexed} because the elements of these algebras ususally carry
+indices.
+
+Unlike most computer algebra systems, GiNaC does not primarily provide an
+operator (often denoted @samp{&*}) for representing inert products of
+arbitrary objects. Rather, non-commutativity in GiNaC is a property of the
+classes of objects involved, and non-commutative products are formed with
+the usual @samp{*} operator, as are ordinary products. GiNaC is capable of
+figuring out by itself which objects commute and will group the factors
+by their class. Consider this example:
+
+@example
+ ...
+ varidx mu(symbol("mu"), 4), nu(symbol("nu"), 4);
+ idx a(symbol("a"), 8), b(symbol("b"), 8);
+ ex e = -dirac_gamma(mu) * (2*color_T(a)) * 8 * color_T(b) * dirac_gamma(nu);
+ cout << e << endl;
+ // -> -16*(gamma~mu*gamma~nu)*(T.a*T.b)
+ ...
+@end example
+
+As can be seen, GiNaC pulls out the overall commutative factor @samp{-16} and
+groups the non-commutative factors (the gammas and the su(3) generators)
+together while preserving the order of factors within each class (because
+Clifford objects commute with color objects). The resulting expression is a
+@emph{commutative} product with two factors that are themselves non-commutative
+products (@samp{gamma~mu*gamma~nu} and @samp{T.a*T.b}). For clarification,
+parentheses are placed around the non-commutative products in the output.
+
+@cindex @code{ncmul} (class)
+Non-commutative products are internally represented by objects of the class
+@code{ncmul}, as opposed to commutative products which are handled by the
+@code{mul} class. You will normally not have to worry about this distinction,
+though.
+
+The advantage of this approach is that you never have to worry about using
+(or forgetting to use) a special operator when constructing non-commutative
+expressions. Also, non-commutative products in GiNaC are more intelligent
+than in other computer algebra systems; they can, for example, automatically
+canonicalize themselves according to rules specified in the implementation
+of the non-commutative classes. The drawback is that to work with other than
+the built-in algebras you have to implement new classes yourself. Symbols
+always commute and it's not possible to construct non-commutative products
+using symbols to represent the algebra elements or generators. User-defined
+functions can, however, be specified as being non-commutative.
+
+@cindex @code{return_type()}
+@cindex @code{return_type_tinfo()}
+Information about the commutativity of an object or expression can be
+obtained with the two member functions
+
+@example
+unsigned ex::return_type(void) const;
+unsigned ex::return_type_tinfo(void) const;
+@end example
+
+The @code{return_type()} function returns one of three values (defined in
+the header file @file{flags.h}), corresponding to three categories of
+expressions in GiNaC:
+
+@itemize
+@item @code{return_types::commutative}: Commutes with everything. Most GiNaC
+ classes are of this kind.
+@item @code{return_types::noncommutative}: Non-commutative, belonging to a
+ certain class of non-commutative objects which can be determined with the
+ @code{return_type_tinfo()} method. Expressions of this category commute
+ with everything except @code{noncommutative} expressions of the same
+ class.
+@item @code{return_types::noncommutative_composite}: Non-commutative, composed
+ of non-commutative objects of different classes. Expressions of this
+ category don't commute with any other @code{noncommutative} or
+ @code{noncommutative_composite} expressions.
+@end itemize
+
+The value returned by the @code{return_type_tinfo()} method is valid only
+when the return type of the expression is @code{noncommutative}. It is a
+value that is unique to the class of the object and usually one of the
+constants in @file{tinfos.h}, or derived therefrom.
+
+Here are a couple of examples:
+
+@cartouche
+@multitable @columnfractions 0.33 0.33 0.34
+@item @strong{Expression} @tab @strong{@code{return_type()}} @tab @strong{@code{return_type_tinfo()}}
+@item @code{42} @tab @code{commutative} @tab -
+@item @code{2*x-y} @tab @code{commutative} @tab -
+@item @code{dirac_ONE()} @tab @code{noncommutative} @tab @code{TINFO_clifford}
+@item @code{dirac_gamma(mu)*dirac_gamma(nu)} @tab @code{noncommutative} @tab @code{TINFO_clifford}
+@item @code{2*color_T(a)} @tab @code{noncommutative} @tab @code{TINFO_color}
+@item @code{dirac_ONE()*color_T(a)} @tab @code{noncommutative_composite} @tab -
+@end multitable
+@end cartouche
+
+Note: the @code{return_type_tinfo()} of Clifford objects is only equal to
+@code{TINFO_clifford} for objects with a representation label of zero.
+Other representation labels yield a different @code{return_type_tinfo()},
+but it's the same for any two objects with the same label. This is also true
+for color objects.
+
+As a last note, powers of non-commutative objects are not allowed in GiNaC.
+You can use the function
+
+@example
+ex ncpow(const ex & basis, unsigned exponent)
+@end example
+
+instead which returns an expanded product (e.g. @code{ncpow(a*b, 2)} becomes
+@samp{a*b*a*b} if @samp{a} and @samp{b} are non-commutative expressions).
+
+
+@cindex @code{clifford} (class)
+@subsection Clifford algebra
+
+@cindex @code{dirac_gamma()}
+Clifford algebra elements (also called Dirac gamma matrices, although GiNaC
+doesn't treat them as matrices) are designated as @samp{gamma~mu} and satisfy
+@samp{gamma~mu*gamma~nu + gamma~nu*gamma~mu = 2*eta~mu~nu} where @samp{eta~mu~nu}
+is the Minkowski metric tensor. Dirac gammas are constructed by the function
+
+@example
+ex dirac_gamma(const ex & mu, unsigned char rl = 0);
+@end example
+
+which takes two arguments: the index and a @dfn{representation label} in the
+range 0 to 255 which is used to distinguish elements of different Clifford
+algebras (this is also called a @dfn{spin line index}). Gammas with different
+labels commute with each other. The dimension of the index can be 4 or (in
+the framework of dimensional regularization) any symbolic value. Spinor
+indices on Dirac gammas are not supported in GiNaC.
+
+@cindex @code{dirac_ONE()}
+The unity element of a Clifford algebra is constructed by
+
+@example
+ex dirac_ONE(unsigned char rl = 0);
+@end example
+
+@cindex @code{dirac_gamma5()}
+and there's a special element @samp{gamma5} that commutes with all other
+gammas and in 4 dimensions equals @samp{gamma~0 gamma~1 gamma~2 gamma~3},
+provided by
+
+@example
+ex dirac_gamma5(unsigned char rl = 0);
+@end example
+
+@cindex @code{dirac_gamma6()}
+@cindex @code{dirac_gamma7()}
+The two additional functions
+
+@example
+ex dirac_gamma6(unsigned char rl = 0);
+ex dirac_gamma7(unsigned char rl = 0);
+@end example
+
+return @code{dirac_ONE(rl) + dirac_gamma5(rl)} and @code{dirac_ONE(rl) - dirac_gamma5(rl)},
+respectively.
+
+@cindex @code{dirac_slash()}
+Finally, the function
+
+@example
+ex dirac_slash(const ex & e, const ex & dim, unsigned char rl = 0);
+@end example
+
+creates a term of the form @samp{e.mu gamma~mu} with a new and unique index
+whose dimension is given by the @code{dim} argument.
+
+In products of dirac gammas, superfluous unity elements are automatically
+removed, squares are replaced by their values and @samp{gamma5} is
+anticommuted to the front. The @code{simplify_indexed()} function performs
+contractions in gamma strings, for example
+
+@example
+@{
+ ...
+ symbol a("a"), b("b"), D("D");
+ varidx mu(symbol("mu"), D);
+ ex e = dirac_gamma(mu) * dirac_slash(a, D)
+ * dirac_gamma(mu.toggle_variance());
+ cout << e << endl;
+ // -> (gamma~mu*gamma~symbol10*gamma.mu)*a.symbol10
+ e = e.simplify_indexed();
+ cout << e << endl;
+ // -> -gamma~symbol10*a.symbol10*D+2*gamma~symbol10*a.symbol10
+ cout << e.subs(D == 4) << endl;
+ // -> -2*gamma~symbol10*a.symbol10
+ // [ == -2 * dirac_slash(a, D) ]
+ ...
+@}
+@end example
+
+@cindex @code{dirac_trace()}
+To calculate the trace of an expression containing strings of Dirac gammas
+you use the function
+
+@example
+ex dirac_trace(const ex & e, unsigned char rl = 0, const ex & trONE = 4);
+@end example
+
+This function takes the trace of all gammas with the specified representation
+label; gammas with other labels are left standing. The last argument to
+@code{dirac_trace()} is the value to be returned for the trace of the unity
+element, which defaults to 4. The @code{dirac_trace()} function is a linear
+functional that is equal to the usual trace only in @math{D = 4} dimensions.
+In particular, the functional is not cyclic in @math{D != 4} dimensions when
+acting on expressions containing @samp{gamma5}, so it's not a proper trace.
+This @samp{gamma5} scheme is described in greater detail in
+@cite{The Role of gamma5 in Dimensional Regularization}.
+
+The value of the trace itself is also usually different in 4 and in
+@math{D != 4} dimensions:
+
+@example
+@{
+ // 4 dimensions
+ varidx mu(symbol("mu"), 4), nu(symbol("nu"), 4), rho(symbol("rho"), 4);
+ ex e = dirac_gamma(mu) * dirac_gamma(nu) *
+ dirac_gamma(mu.toggle_variance()) * dirac_gamma(rho);
+ cout << dirac_trace(e).simplify_indexed() << endl;
+ // -> -8*eta~rho~nu
+@}
+...
+@{
+ // D dimensions
+ symbol D("D");
+ varidx mu(symbol("mu"), D), nu(symbol("nu"), D), rho(symbol("rho"), D);
+ ex e = dirac_gamma(mu) * dirac_gamma(nu) *
+ dirac_gamma(mu.toggle_variance()) * dirac_gamma(rho);
+ cout << dirac_trace(e).simplify_indexed() << endl;
+ // -> 8*eta~rho~nu-4*eta~rho~nu*D
+@}
+@end example
+
+Here is an example for using @code{dirac_trace()} to compute a value that
+appears in the calculation of the one-loop vacuum polarization amplitude in
+QED:
+
+@example
+@{
+ symbol q("q"), l("l"), m("m"), ldotq("ldotq"), D("D");
+ varidx mu(symbol("mu"), D), nu(symbol("nu"), D);
+
+ scalar_products sp;
+ sp.add(l, l, pow(l, 2));
+ sp.add(l, q, ldotq);
+
+ ex e = dirac_gamma(mu) *
+ (dirac_slash(l, D) + dirac_slash(q, D) + m * dirac_ONE()) *
+ dirac_gamma(mu.toggle_variance()) *
+ (dirac_slash(l, D) + m * dirac_ONE());
+ e = dirac_trace(e).simplify_indexed(sp);
+ e = e.collect(lst(l, ldotq, m), true);
+ cout << e << endl;
+ // -> (8-4*D)*l^2+(8-4*D)*ldotq+4*D*m^2
+@}
+@end example
+
+The @code{canonicalize_clifford()} function reorders all gamma products that
+appear in an expression to a canonical (but not necessarily simple) form.
+You can use this to compare two expressions or for further simplifications:
+
+@example
+@{
+ varidx mu(symbol("mu"), 4), nu(symbol("nu"), 4);
+ ex e = dirac_gamma(mu) * dirac_gamma(nu) + dirac_gamma(nu) * dirac_gamma(mu);
+ cout << e << endl;
+ // -> gamma~mu*gamma~nu+gamma~nu*gamma~mu
+
+ e = canonicalize_clifford(e);
+ cout << e << endl;
+ // -> 2*eta~mu~nu
+@}
+@end example
+
+
+@cindex @code{color} (class)
+@subsection Color algebra
+
+@cindex @code{color_T()}
+For computations in quantum chromodynamics, GiNaC implements the base elements
+and structure constants of the su(3) Lie algebra (color algebra). The base
+elements @math{T_a} are constructed by the function
+
+@example
+ex color_T(const ex & a, unsigned char rl = 0);
+@end example
+
+which takes two arguments: the index and a @dfn{representation label} in the
+range 0 to 255 which is used to distinguish elements of different color
+algebras. Objects with different labels commute with each other. The
+dimension of the index must be exactly 8 and it should be of class @code{idx},
+not @code{varidx}.
+
+@cindex @code{color_ONE()}
+The unity element of a color algebra is constructed by
+
+@example
+ex color_ONE(unsigned char rl = 0);
+@end example
+
+@cindex @code{color_d()}
+@cindex @code{color_f()}
+and the functions