]> www.ginac.de Git - ginac.git/blobdiff - doc/tutorial/ginac.texi
- documented map(), find(), and new symmetry specifications for indexed objects
[ginac.git] / doc / tutorial / ginac.texi
index 5511a2d1ea22e2e495afdc5b3acd893f4efdc09a..6ed69023339a20a225bb1a9a48fbfca61da1a06e 100644 (file)
@@ -1135,6 +1135,8 @@ canonical form.
 @cindex @code{op()}
 @cindex @code{append()}
 @cindex @code{prepend()}
+@cindex @code{remove_first()}
+@cindex @code{remove_last()}
 
 The GiNaC class @code{lst} serves for holding a @dfn{list} of arbitrary
 expressions. These are sometimes used to supply a variable number of
@@ -1162,13 +1164,23 @@ a list and the @code{op()} method to access individual elements:
     // ...
 @end example
 
-Finally you can append or prepend an expression to a list with the
-@code{append()} and @code{prepend()} methods:
+You can append or prepend an expression to a list with the @code{append()}
+and @code{prepend()} methods:
 
 @example
     // ...
     l.append(4*x);   // l is now @{x, 2, y, x+y, 4*x@}
     l.prepend(0);    // l is now @{0, x, 2, y, x+y, 4*x@}
+    // ...
+@end example
+
+Finally you can remove the first or last element of a list with
+@code{remove_first()} and @code{remove_last()}:
+
+@example
+    // ...
+    l.remove_first();   // l is now @{x, 2, y, x+y, 4*x@}
+    l.remove_last();    // l is now @{x, 2, y, x+y@}
 @}
 @end example
 
@@ -1531,7 +1543,7 @@ ex idx::get_dimension(void);
 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.
+@code{ex_to<idx>()} on the expression.
 
 There are also the methods
 
@@ -1574,7 +1586,7 @@ 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()}
+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
 
@@ -1618,7 +1630,7 @@ 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).
+@code{ex_to<spinidx>()} to get the object reference from an expression).
 Finally, the two methods
 
 @example
@@ -1685,22 +1697,99 @@ expected:
 @end example
 
 @subsection Symmetries
+@cindex @code{symmetry} (class)
+@cindex @code{sy_none()}
+@cindex @code{sy_symm()}
+@cindex @code{sy_anti()}
+@cindex @code{sy_cycl()}
 
-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:
+Indexed objects can have certain symmetry properties with respect to their
+indices. Symmetries are specified as a tree of objects of class @code{symmetry}
+that is constructed with the helper functions
 
 @example
+symmetry sy_none(...);
+symmetry sy_symm(...);
+symmetry sy_anti(...);
+symmetry sy_cycl(...);
+@end example
+
+@code{sy_none()} stands for no symmetry, @code{sy_symm()} and @code{sy_anti()}
+specify fully symmetric or antisymmetric, respectively, and @code{sy_cycl()}
+represents a cyclic symmetry. Each of these functions accepts up to four
+arguments which can be either symmetry objects themselves or unsigned integer
+numbers that represent an index position (counting from 0). A symmetry
+specification that consists of only a single @code{sy_symm()}, @code{sy_anti()}
+or @code{sy_cycl()} with no arguments specifies the respective symmetry for
+all indices.
+
+Here are some examples of symmetry definitions:
+
+@example
+    ...
+    // No symmetry:
+    e = indexed(A, i, j);
+    e = indexed(A, sy_none(), i, j);     // equivalent
+    e = indexed(A, sy_none(0, 1), i, j); // equivalent
+
+    // Symmetric in all three indices:
+    e = indexed(A, sy_symm(), i, j, k);
+    e = indexed(A, sy_symm(0, 1, 2), i, j, k); // equivalent
+    e = indexed(A, sy_symm(2, 0, 1), i, j, k); // same symmetry, but yields a
+                                               // different canonical order
+
+    // Symmetric in the first two indices only:
+    e = indexed(A, sy_symm(0, 1), i, j, k);
+    e = indexed(A, sy_none(sy_symm(0, 1), 2), i, j, k); // equivalent
+
+    // Antisymmetric in the first and last index only (index ranges need not
+    // be contiguous):
+    e = indexed(A, sy_anti(0, 2), i, j, k);
+    e = indexed(A, sy_none(sy_anti(0, 2), 1), i, j, k); // equivalent
+
+    // An example of a mixed symmetry: antisymmetric in the first two and
+    // last two indices, symmetric when swapping the first and last index
+    // pairs (like the Riemann curvature tensor):
+    e = indexed(A, sy_symm(sy_anti(0, 1), sy_anti(2, 3)), i, j, k, l);
+
+    // Cyclic symmetry in all three indices:
+    e = indexed(A, sy_cycl(), i, j, k);
+    e = indexed(A, sy_cycl(0, 1, 2), i, j, k); // equivalent
+
+    // The following examples are invalid constructions that will throw
+    // an exception at run time.
+
+    // An index may not appear multiple times:
+    e = indexed(A, sy_symm(0, 0, 1), i, j, k); // ERROR
+    e = indexed(A, sy_none(sy_symm(0, 1), sy_anti(0, 2)), i, j, k); // ERROR
+
+    // Every child of sy_symm(), sy_anti() and sy_cycl() must refer to the
+    // same number of indices:
+    e = indexed(A, sy_symm(sy_anti(0, 1), 2), i, j, k); // ERROR
+
+    // And of course, you cannot specify indices which are not there:
+    e = indexed(A, sy_symm(0, 1, 2, 3), i, j, k); // ERROR
     ...
-    cout << indexed(A, indexed::symmetric, i, j)
-          + indexed(A, indexed::symmetric, j, i) << endl;
+@end example
+
+If you need to specify more than four indices, you have to use the
+@code{.add()} method of the @code{symmetry} class. For example, to specify
+full symmetry in the first six indices you would write
+@code{sy_symm(0, 1, 2, 3).add(4).add(5)}.
+
+If an indexed object has a symmetry, GiNaC will automatically bring the
+indices into a canonical order which allows for some immediate simplifications:
+
+@example
+    ...
+    cout << indexed(A, sy_symm(), i, j)
+          + indexed(A, sy_symm(), j, i) << endl;
      // -> 2*A.j.i
-    cout << indexed(B, indexed::antisymmetric, i, j)
-          + indexed(B, indexed::antisymmetric, j, j) << endl;
+    cout << indexed(B, sy_anti(), i, j)
+          + indexed(B, sy_anti(), j, i) << endl;
      // -> -B.j.i
-    cout << indexed(B, indexed::antisymmetric, i, j)
-          + indexed(B, indexed::antisymmetric, j, i) << endl;
+    cout << indexed(B, sy_anti(), i, j, k)
+          + indexed(B, sy_anti(), j, i, k) << endl;
      // -> 0
     ...
 @end example
@@ -2290,7 +2379,7 @@ QED:
            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);
+    e = e.collect(lst(l, ldotq, m));
     cout << e << endl;
      // -> (8-4*D)*l^2+(8-4*D)*ldotq+4*D*m^2
 @}
@@ -2465,6 +2554,7 @@ avoided.
 * Information About Expressions::
 * Substituting Expressions::
 * Pattern Matching and Advanced Substitutions::
+* Applying a Function on Subexpressions::
 * Polynomial Arithmetic::           Working with polynomials.
 * Rational Expressions::            Working with rational functions.
 * Symbolic Differentiation::
@@ -2480,41 +2570,42 @@ avoided.
 @section Getting information about expressions
 
 @subsection Checking expression types
-@cindex @code{is_ex_of_type()}
-@cindex @code{ex_to_numeric()}
-@cindex @code{ex_to_@dots{}}
-@cindex @code{Converting ex to other classes}
+@cindex @code{is_a<@dots{}>()}
+@cindex @code{is_exactly_a<@dots{}>()}
+@cindex @code{ex_to<@dots{}>()}
+@cindex Converting @code{ex} to other classes
 @cindex @code{info()}
 @cindex @code{return_type()}
 @cindex @code{return_type_tinfo()}
 
 Sometimes it's useful to check whether a given expression is a plain number,
 a sum, a polynomial with integer coefficients, or of some other specific type.
-GiNaC provides a couple of functions for this (the first one is actually a macro):
+GiNaC provides a couple of functions for this:
 
 @example
-bool is_ex_of_type(const ex & e, TYPENAME t);
+bool is_a<T>(const ex & e);
+bool is_exactly_a<T>(const ex & e);
 bool ex::info(unsigned flag);
 unsigned ex::return_type(void) const;
 unsigned ex::return_type_tinfo(void) const;
 @end example
 
-When the test made by @code{is_ex_of_type()} returns true, it is safe to
-call one of the functions @code{ex_to_@dots{}}, where @code{@dots{}} is
-one of the class names (@xref{The Class Hierarchy}, for a list of all
-classes). For example, assuming @code{e} is an @code{ex}:
+When the test made by @code{is_a<T>()} returns true, it is safe to call
+one of the functions @code{ex_to<T>()}, where @code{T} is one of the
+class names (@xref{The Class Hierarchy}, for a list of all classes). For
+example, assuming @code{e} is an @code{ex}:
 
 @example
 @{
     @dots{}
-    if (is_ex_of_type(e, numeric))
-        numeric n = ex_to_numeric(e);
+    if (is_a<numeric>(e))
+        numeric n = ex_to<numeric>(e);
     @dots{}
 @}
 @end example
 
-@code{is_ex_of_type()} allows you to check whether the top-level object of
-an expression @samp{e} is an instance of the GiNaC class @samp{t}
+@code{is_a<T>(e)} allows you to check whether the top-level object of
+an expression @samp{e} is an instance of the GiNaC class @samp{T}
 (@xref{The Class Hierarchy}, for a list of all classes). This is most useful,
 e.g., for checking whether an expression is a number, a sum, or a product:
 
@@ -2523,15 +2614,19 @@ e.g., for checking whether an expression is a number, a sum, or a product:
     symbol x("x");
     ex e1 = 42;
     ex e2 = 4*x - 3;
-    is_ex_of_type(e1, numeric);  // true
-    is_ex_of_type(e2, numeric);  // false
-    is_ex_of_type(e1, add);      // false
-    is_ex_of_type(e2, add);      // true
-    is_ex_of_type(e1, mul);      // false
-    is_ex_of_type(e2, mul);      // false
+    is_a<numeric>(e1);  // true
+    is_a<numeric>(e2);  // false
+    is_a<add>(e1);      // false
+    is_a<add>(e2);      // true
+    is_a<mul>(e1);      // false
+    is_a<mul>(e2);      // false
 @}
 @end example
 
+In contrast, @code{is_exactly_a<T>(e)} allows you to check whether the
+top-level object of an expression @samp{e} is an instance of the GiNaC
+class @samp{T}, not including parent classes.
+
 The @code{info()} method is used for checking certain attributes of
 expressions. The possible values for the @code{flag} argument are defined
 in @file{ginac/flags.h}, the most important being explained in the following
@@ -2541,7 +2636,7 @@ table:
 @multitable @columnfractions .30 .70
 @item @strong{Flag} @tab @strong{Returns true if the object is@dots{}}
 @item @code{numeric}
-@tab @dots{}a number (same as @code{is_ex_of_type(..., numeric)})
+@tab @dots{}a number (same as @code{is_<numeric>(...)})
 @item @code{real}
 @tab @dots{}a real integer, rational or float (i.e. is not complex)
 @item @code{rational}
@@ -2571,7 +2666,7 @@ table:
 @item @code{prime}
 @tab @dots{}a prime integer (probabilistic primality test)
 @item @code{relation}
-@tab @dots{}a relation (same as @code{is_ex_of_type(..., relational)})
+@tab @dots{}a relation (same as @code{is_a<relational>(...)})
 @item @code{relation_equal}
 @tab @dots{}a @code{==} relation
 @item @code{relation_not_equal}
@@ -2585,9 +2680,9 @@ table:
 @item @code{relation_greater_or_equal}
 @tab @dots{}a @code{>=} relation
 @item @code{symbol}
-@tab @dots{}a symbol (same as @code{is_ex_of_type(..., symbol)})
+@tab @dots{}a symbol (same as @code{is_a<symbol>(...)})
 @item @code{list}
-@tab @dots{}a list (same as @code{is_ex_of_type(..., lst)})
+@tab @dots{}a list (same as @code{is_a<lst>(...)})
 @item @code{polynomial}
 @tab @dots{}a polynomial (i.e. only consists of sums and products of numbers and symbols with positive integer powers)
 @item @code{integer_polynomial}
@@ -2738,7 +2833,7 @@ A more powerful form of substitution using wildcards is described in the
 next section.
 
 
-@node Pattern Matching and Advanced Substitutions, Polynomial Arithmetic, Substituting Expressions, Methods and Functions
+@node Pattern Matching and Advanced Substitutions, Applying a Function on Subexpressions, Substituting Expressions, Methods and Functions
 @c    node-name, next, previous, up
 @section Pattern matching and advanced substitutions
 @cindex @code{wildcard} (class)
@@ -2753,7 +2848,7 @@ A @dfn{wildcard} is a special kind of object (of class @code{wildcard}) that
 represents an arbitrary expression. Every wildcard has a @dfn{label} which is
 an unsigned integer number to allow having multiple different wildcards in a
 pattern. Wildcards are printed as @samp{$label} (this is also the way they
-are specified in @command{ginsh}. In C++ code, wildcard objects are created
+are specified in @command{ginsh}). In C++ code, wildcard objects are created
 with the call
 
 @example
@@ -2893,7 +2988,7 @@ FAIL
 > match(a*b^2,a^$1*b^$2);
 FAIL
   (The matching is syntactic, not algebraic, and "a" doesn't match "a^$1"
-   even if a==a^1.)
+   even though a==a^1.)
 > match(x*atan2(x,x^2),$0*atan2($0,$0^2));
 @{$0==x@}
 > match(atan2(y,x^2),atan2(y,$0));
@@ -2941,6 +3036,33 @@ Again some examples in @command{ginsh} for illustration (in @command{ginsh},
    contains a linear term you should use the coeff() function instead.)
 @end example
 
+@cindex @code{find()}
+The method
+
+@example
+bool ex::find(const ex & pattern, lst & found);
+@end example
+
+works a bit like @code{has()} but it doesn't stop upon finding the first
+match. Instead, it appends all found matches to the specified list. If there
+are multiple occurrences of the same expression, it is entered only once to
+the list. @code{find()} returns false if no matches were found (in
+@command{ginsh}, it returns an empty list):
+
+@example
+> find(1+x+x^2+x^3,x);
+@{x@}
+> find(1+x+x^2+x^3,y);
+@{@}
+> find(1+x+x^2+x^3,x^$1);
+@{x^3,x^2@}
+  (Note the absence of "x".)
+> expand((sin(x)+sin(y))*(a+b));
+sin(y)*a+sin(x)*b+sin(x)*a+sin(y)*b
+> find(",sin($1));
+@{sin(y),sin(x)@}
+@end example
+
 @cindex @code{subs()}
 Probably the most useful application of patterns is to use them for
 substituting expressions with the @code{subs()} method. Wildcards can be
@@ -2984,7 +3106,115 @@ The last example would be written in C++ in this way:
 @end example
 
 
-@node Polynomial Arithmetic, Rational Expressions, Pattern Matching and Advanced Substitutions, Methods and Functions
+@node Applying a Function on Subexpressions, Polynomial Arithmetic, Pattern Matching and Advanced Substitutions, Methods and Functions
+@c    node-name, next, previous, up
+@section Applying a Function on Subexpressions
+@cindex Tree traversal
+@cindex @code{map()}
+
+Sometimes you may want to perform an operation on specific parts of an
+expression while leaving the general structure of it intact. An example
+of this would be a matrix trace operation: the trace of a sum is the sum
+of the traces of the individual terms. That is, the trace should @dfn{map}
+on the sum, by applying itself to each of the sum's operands. It is possible
+to do this manually which usually results in code like this:
+
+@example
+ex calc_trace(ex e)
+@{
+    if (is_a<matrix>(e))
+        return ex_to<matrix>(e).trace();
+    else if (is_a<add>(e)) @{
+        ex sum = 0;
+        for (unsigned i=0; i<e.nops(); i++)
+            sum += calc_trace(e.op(i));
+        return sum;
+    @} else if (is_a<mul>)(e)) @{
+        ...
+    @} else @{
+        ...
+    @}
+@}
+@end example
+
+This is, however, slightly inefficient (if the sum is very large it can take
+a long time to add the terms one-by-one), and its applicability is limited to
+a rather small class of expressions. If @code{calc_trace()} is called with
+a relation or a list as its argument, you will probably want the trace to
+be taken on both sides of the relation or of all elements of the list.
+
+GiNaC offers the @code{map()} method to aid in the implementation of such
+operations:
+
+@example
+static ex ex::map(map_function & f) const;
+static ex ex::map(ex (*f)(const ex & e)) const;
+@end example
+
+In the first (preferred) form, @code{map()} takes a function object that
+is subclassed from the @code{map_function} class. In the second form, it
+takes a pointer to a function that accepts and returns an expression.
+@code{map()} constructs a new expression of the same type, applying the
+specified function on all subexpressions (in the sense of @code{op()}),
+non-recursively.
+
+The use of a function object makes it possible to supply more arguments to
+the function that is being mapped, or to keep local state information.
+The @code{map_function} class declares a virtual function call operator
+that you can overload. Here is a sample implementation of @code{calc_trace()}
+that uses @code{map()} in a recursive fashion:
+
+@example
+struct calc_trace : public map_function @{
+    ex operator()(const ex &e)
+    @{
+        if (is_a<matrix>(e))
+            return ex_to<matrix>(e).trace();
+        else if (is_a<mul>(e)) @{
+            ...
+        @} else
+            return e.map(*this);
+    @}
+@};
+@end example
+
+This function object could then be used like this:
+
+@example
+@{
+    ex M = ... // expression with matrices
+    calc_trace do_trace;
+    ex tr = do_trace(M);
+@}
+@end example
+
+@command{ginsh} offers a slightly different implementation of @code{map()}
+that allows applying algebraic functions to operands. The second argument
+to @code{map()} is an expression containing the wildcard @samp{$0} which
+acts as the placeholder for the operands:
+
+@example
+> map(a*b,sin($0));
+sin(a)*sin(b)
+> map(a+2*b,sin($0));
+sin(a)+sin(2*b)
+> map(@{a,b,c@},$0^2+$0);
+@{a^2+a,b^2+b,c^2+c@}
+@end example
+
+Note that it is only possible to use algebraic functions in the second
+argument. You can not use functions like @samp{diff()}, @samp{op()},
+@samp{subs()} etc. because these are evaluated immediately:
+
+@example
+> map(@{a,b,c@},diff($0,a));
+@{0,0,0@}
+  This is because "diff($0,a)" evaluates to "0", so the command is equivalent
+  to "map(@{a,b,c@},0)".
+@end example
+
+
+@node Polynomial Arithmetic, Rational Expressions, Applying a Function on Subexpressions, Methods and Functions
 @c    node-name, next, previous, up
 @section Polynomial arithmetic
 
@@ -3029,8 +3259,25 @@ case the result is either a recursively collected polynomial, or a polynomial
 in a distributed form with terms like @math{c*x1^e1*...*xn^en}, as specified
 by the @code{distributed} flag.
 
-Note that the original polynomial needs to be in expanded form in order
-for @code{collect()} to be able to find the coefficients properly.
+Note that the original polynomial needs to be in expanded form (for the
+variables concerned) in order for @code{collect()} to be able to find the
+coefficients properly.
+
+The following @command{ginsh} transcript shows an application of @code{collect()}
+together with @code{find()}:
+
+@example
+> a=expand((sin(x)+sin(y))*(1+p+q)*(1+d));
+d*p*sin(x)+p*sin(x)+q*d*sin(x)+q*sin(y)+d*sin(x)+q*d*sin(y)+sin(y)+d*sin(y)+q*sin(x)+d*sin(y)*p+sin(x)+sin(y)*p
+> collect(a,@{p,q@});
+d*sin(x)+(d*sin(x)+sin(y)+d*sin(y)+sin(x))*p+(d*sin(x)+sin(y)+d*sin(y)+sin(x))*q+sin(y)+d*sin(y)+sin(x)
+> collect(a,find(a,sin($1)));
+(1+q+d+q*d+d*p+p)*sin(y)+(1+q+d+q*d+d*p+p)*sin(x)
+> collect(a,@{find(a,sin($1)),p,q@});
+(1+(1+d)*p+d+q*(1+d))*sin(x)+(1+(1+d)*p+d+q*(1+d))*sin(y)
+> collect(a,@{find(a,sin($1)),d@});
+(1+q+d*(1+q+p)+p)*sin(y)+(1+q+d*(1+q+p)+p)*sin(x)
+@end example
 
 @subsection Degree and coefficients
 @cindex @code{degree()}
@@ -3541,23 +3788,26 @@ program, it will type out:
 @section Symmetrization
 @cindex @code{symmetrize()}
 @cindex @code{antisymmetrize()}
+@cindex @code{symmetrize_cyclic()}
 
-The two methods
+The three methods
 
 @example
 ex ex::symmetrize(const lst & l);
 ex ex::antisymmetrize(const lst & l);
+ex ex::symmetrize_cyclic(const lst & l);
 @end example
 
-symmetrize an expression by returning the symmetric or antisymmetric sum
-over all permutations of the specified list of objects, weighted by the
-number of permutations.
+symmetrize an expression by returning the sum over all symmetric,
+antisymmetric or cyclic permutations of the specified list of objects,
+weighted by the number of permutations.
 
-The two additional methods
+The three additional methods
 
 @example
 ex ex::symmetrize();
 ex ex::antisymmetrize();
+ex ex::symmetrize_cyclic();
 @end example
 
 symmetrize or antisymmetrize an expression over its free indices.
@@ -3574,8 +3824,8 @@ almost any kind of object (anything that is @code{subs()}able):
      // -> 1/2*A.j.i+1/2*A.i.j
     cout << indexed(A, i, j, k).antisymmetrize(lst(i, j)) << endl;
      // -> -1/2*A.j.i.k+1/2*A.i.j.k
-    cout << lst(a, b, c).symmetrize(lst(a, b, c)) << endl;
-     // -> 1/6*@{a,b,c@}+1/6*@{c,a,b@}+1/6*@{b,a,c@}+1/6*@{c,b,a@}+1/6*@{b,c,a@}+1/6*@{a,c,b@}
+    cout << lst(a, b, c).symmetrize_cyclic(lst(a, b, c)) << endl;
+     // -> 1/3*@{a,b,c@}+1/3*@{b,c,a@}+1/3*@{c,a,b@}
 @}
 @end example
 
@@ -3784,6 +4034,7 @@ will print out:
     @{(-\ln(x))@}+@{(-\gamma_E)@} x+@{(1/12 \pi^2)@} x^@{2@}+\mathcal@{O@}(x^3)
 @end example
 
+@cindex Tree traversal
 If you need any fancy special output format, e.g. for interfacing GiNaC
 with other algebra systems or for producing code for different
 programming languages, you can always traverse the expression tree yourself:
@@ -3791,8 +4042,8 @@ programming languages, you can always traverse the expression tree yourself:
 @example
 static void my_print(const ex & e)
 @{
-    if (is_ex_of_type(e, function))
-        cout << ex_to_function(e).get_name();
+    if (is_a<function>(e))
+        cout << ex_to<function>(e).get_name();
     else
         cout << e.bp->class_name();
     cout << "(";
@@ -4139,7 +4390,7 @@ function that does so, in this case the one in class @code{numeric}:
 @example
 static ex cos_evalf(const ex & x)
 @{
-    return cos(ex_to_numeric(x));
+    return cos(ex_to<numeric>(x));
 @}
 @end example
 
@@ -4308,7 +4559,7 @@ private:
     string str;
 @};
 
-GIANC_IMPLEMENT_REGISTERED_CLASS(mystring, basic)
+GINAC_IMPLEMENT_REGISTERED_CLASS(mystring, basic)
 @end example
 
 The @code{GINAC_DECLARE_REGISTERED_CLASS} and @code{GINAC_IMPLEMENT_REGISTERED_CLASS}
@@ -4537,7 +4788,7 @@ strings in algebraic expressions. Let's confirm that the RTTI works:
 
 @example
 ex e = mystring("Hello, world!");
-cout << is_ex_of_type(e, mystring) << endl;
+cout << is_a<mystring>(e) << endl;
  // -> 1 (true)
 
 cout << e.bp->class_name() << endl;
@@ -4588,7 +4839,7 @@ cout << e << endl;
  // -> "GiNaC rulez"+"Hello, world!"
 @end example
 
-(note that GiNaC's automatic term reordering is in effect here), or even
+(GiNaC's automatic term reordering is in effect here), or even
 
 @example
 e = pow(mystring("One string"), 2*sin(Pi-mystring("Another string")));
@@ -4687,19 +4938,12 @@ implement for terminal classes like the described string class is
 which will allow GiNaC to compare and canonicalize expressions much more
 efficiently.
 
-You can, of course, also add your own new member functions. In this case you
-will probably want to define a little helper function like
-
-@example
-inline const mystring &ex_to_mystring(const ex &e)
-@{
-    return static_cast<const mystring &>(*e.bp);
-@}
-@end example
-
-that let's you get at the object inside an expression (after you have
-verified that the type is correct) so you can call member functions that are
-specific to the class.
+You can, of course, also add your own new member functions. Remember,
+that the RTTI may be used to get information about what kinds of objects
+you are dealing with (the position in the class hierarchy) and that you
+can always extract the bare object from an @code{ex} by stripping the
+@code{ex} off using the @code{ex_to<mystring>(e)} function when that
+should become a need.
 
 That's it. May the source be with you!