]> www.ginac.de Git - ginac.git/blobdiff - doc/tutorial/ginac.texi
archive property example uses propinfovector
[ginac.git] / doc / tutorial / ginac.texi
index fe09793d0f8b91e24b5f867d004be03c8870dc98..8c060cfba62eb2dfa6caffd26421b0ccfad83c2a 100644 (file)
@@ -180,6 +180,7 @@ pointless) bivariate polynomial with some large coefficients:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -213,6 +214,7 @@ generates Hermite polynomials in a specified free variable.
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 ex HermitePoly(const symbol & x, int n)
@@ -798,7 +800,7 @@ for instance) will always reveal their difference.  Watch out, please.
 Although symbols can be assigned expressions for internal reasons, you
 should not do it (and we are not going to tell you how it is done).  If
 you want to replace a symbol with something else in an expression, you
-can use the expression's @code{.subs()} method (@xref{Substituting Symbols},
+can use the expression's @code{.subs()} method (@xref{Substituting Expressions},
 for more information).
 
 
@@ -849,7 +851,7 @@ int main()
     // Trott's constant in scientific notation:
     numeric trott("1.0841015122311136151E-2");
     
-    cout << two*p << endl;  // floating point 6.283...
+    std::cout << two*p << std::endl;  // floating point 6.283...
 @}
 @end example
 
@@ -888,6 +890,7 @@ digits:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 void foo()
@@ -938,6 +941,7 @@ some multiple of its denominator and test what comes out:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 // some very important constants:
@@ -1042,21 +1046,16 @@ Simple polynomial expressions are written down in GiNaC pretty much like
 in other CAS or like expressions involving numerical variables in C.
 The necessary operators @code{+}, @code{-}, @code{*} and @code{/} have
 been overloaded to achieve this goal.  When you run the following
-program, the constructor for an object of type @code{mul} is
+code snippet, the constructor for an object of type @code{mul} is
 automatically called to hold the product of @code{a} and @code{b} and
 then the constructor for an object of type @code{add} is called to hold
 the sum of that @code{mul} object and the number one:
 
 @example
-#include <ginac/ginac.h>
-using namespace GiNaC;
-
-int main()
-@{
+    ...
     symbol a("a"), b("b");
     ex MyTerm = 1+a*b;
-    // ...
-@}
+    ...
 @end example
 
 @cindex @code{pow()}
@@ -1064,7 +1063,7 @@ For exponentiation, you have already seen the somewhat clumsy (though C-ish)
 statement @code{pow(x,2);} to represent @code{x} squared.  This direct
 construction is necessary since we cannot safely overload the constructor
 @code{^} in C++ to construct a @code{power} object.  If we did, it would
-have several counterintuitive effects:
+have several counterintuitive and undesired effects:
 
 @itemize @bullet
 @item
@@ -1173,37 +1172,27 @@ There are quite a number of useful functions hard-wired into GiNaC.  For
 instance, all trigonometric and hyperbolic functions are implemented
 (@xref{Built-in Functions}, for a complete list).
 
-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:
+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
-#include <ginac/ginac.h>
-using namespace GiNaC;
-
-int main()
-@{
-    symbol x("x"), y("y");
-    
+    ...
+    symbol x("x"), y("y");    
     ex foo = x+y/2;
-    cout << "tgamma(" << foo << ") -> " << tgamma(foo) << endl;
+    cout << tgamma(foo) << endl;
+     // -> tgamma(x+(1/2)*y)
     ex bar = foo.subs(y==1);
-    cout << "tgamma(" << bar << ") -> " << tgamma(bar) << endl;
+    cout << tgamma(bar) << endl;
+     // -> tgamma(x+1/2)
     ex foobar = bar.subs(x==7);
-    cout << "tgamma(" << foobar << ") -> " << tgamma(foobar) << endl;
-@}
-@end example
-
-This program shows how the function returns itself twice and finally an
-expression that may be really useful:
-
-@example
-tgamma(x+(1/2)*y) -> tgamma(x+(1/2)*y)
-tgamma(x+1/2) -> tgamma(x+1/2)
-tgamma(15/2) -> (135135/128)*Pi^(1/2)
+    cout << tgamma(foobar) << endl;
+     // -> (135135/128)*Pi^(1/2)
+    ...
 @end example
 
 Besides evaluation most of these functions allow differentiation, series
@@ -1261,6 +1250,9 @@ Indexed expressions in GiNaC are constructed of two special types of 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
@@ -1283,6 +1275,7 @@ A simple example shall illustrate the concepts:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -1420,7 +1413,7 @@ variance. By using it you only have to define the index once.
 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 Symbols}).
+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:
@@ -1491,6 +1484,7 @@ simplifications:
 @end example
 
 @cindex @code{get_free_indices()}
+@cindex Dummy index
 @subsection Dummy indices
 
 GiNaC treats certain symbolic index pairs as @dfn{dummy indices} meaning
@@ -1724,17 +1718,15 @@ tensor).
 @subsection Linear algebra
 
 The @code{matrix} class can be used with indices to do some simple linear
-algebra (products of vectors and matrices, traces and scalar products):
+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), X(2, 1);
-    A.set(0, 0, 1); A.set(0, 1, 2);
-    A.set(1, 0, 3); A.set(1, 1, 4);
-    X.set(0, 0, x); X.set(1, 0, y);
+    matrix A(2, 2, lst(1, 2, 3, 4)), X(2, 1, lst(x, y));
 
     cout << indexed(A, i, i) << endl;
      // -> 5
@@ -1743,20 +1735,20 @@ algebra (products of vectors and matrices, traces and scalar products):
     cout << e.simplify_indexed() << endl;
      // -> [[ [[2*y+x]], [[4*y+3*x]] ]].i
 
-    e = indexed(A, i, j) * indexed(X, i);
+    e = indexed(A, i, j) * indexed(X, i) + indexed(X, j) * 2;
     cout << e.simplify_indexed() << endl;
-     // -> [[ [[3*y+x,4*y+2*x]] ]].j
+     // -> [[ [[3*y+3*x,6*y+2*x]] ]].j
 @}
 @end example
 
-You can of course obtain the same results with the @code{matrix::mul()}
-and @code{matrix::trace()} methods but with indices you don't have to
-worry about transposing matrices.
+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 columnt vector). Other matrices must have two indices.
+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
@@ -1778,16 +1770,10 @@ alternatively call it in a functional way as shown in the simple
 example:
 
 @example
-#include <ginac/ginac.h>
-using namespace GiNaC;
-
-int main()
-@{
-    ex x = numeric(1.0);
-    
-    cout << "As method:   " << sin(x).evalf() << endl;
-    cout << "As function: " << evalf(sin(x)) << endl;
-@}
+    ...
+    cout << "As method:   " << sin(1).evalf() << endl;
+    cout << "As function: " << evalf(sin(1)) << endl;
+    ...
 @end example
 
 @cindex @code{subs()}
@@ -1813,7 +1799,7 @@ avoided.
 
 @menu
 * Information About Expressions::
-* Substituting Symbols::
+* Substituting Expressions::
 * Polynomial Arithmetic::           Working with polynomials.
 * Rational Expressions::            Working with rational functions.
 * Symbolic Differentiation::
@@ -1823,7 +1809,7 @@ avoided.
 @end menu
 
 
-@node Information About Expressions, Substituting Symbols, Methods and Functions, Methods and Functions
+@node Information About Expressions, Substituting Expressions, Methods and Functions, Methods and Functions
 @c    node-name, next, previous, up
 @section Getting information about expressions
 
@@ -2024,12 +2010,13 @@ GiNaC to establish a canonical sort order for terms, and using it to compare
 expressions will give very surprising results.
 
 
-@node Substituting Symbols, Polynomial Arithmetic, Information About Expressions, Methods and Functions
+@node Substituting Expressions, Polynomial Arithmetic, Information About Expressions, Methods and Functions
 @c    node-name, next, previous, up
-@section Substituting symbols
+@section Substituting expressions
 @cindex @code{subs()}
 
-Symbols can be replaced with expressions via the @code{.subs()} method:
+Algebraic objects inside expressions can be replaced with arbitrary
+expressions via the @code{.subs()} method:
 
 @example
 ex ex::subs(const ex & e);
@@ -2037,30 +2024,55 @@ ex ex::subs(const lst & syms, const lst & repls);
 @end example
 
 In the first form, @code{subs()} accepts a relational of the form
-@samp{symbol == expression} or a @code{lst} of such relationals. E.g.
+@samp{object == expression} or a @code{lst} of such relationals:
 
 @example
 @{
     symbol x("x"), y("y");
+
     ex e1 = 2*x^2-4*x+3;
     cout << "e1(7) = " << e1.subs(x == 7) << endl;
+     // -> 73
+
     ex e2 = x*y + x;
     cout << "e2(-2, 4) = " << e2.subs(lst(x == -2, y == 4)) << endl;
+     // -> -10
 @}
 @end example
 
-will print @samp{73} and @samp{-10}, respectively.
+@code{subs()} performs syntactic substitution of any complete algebraic
+object; it does not try to match sub-expressions as is demonstrated by the
+following example:
+
+@example
+@{
+    symbol x("x"), y("y"), z("z");
+
+    ex e1 = pow(x+y, 2);
+    cout << e1.subs(x+y == 4) << endl;
+     // -> 16
+
+    ex e2 = sin(x)*cos(x);
+    cout << e2.subs(sin(x) == cos(x)) << endl;
+     // -> cos(x)^2
+
+    ex e3 = x+y+z;
+    cout << e3.subs(x+y == 4) << endl;
+     // -> x+y+z
+     // (and not 4+z as one might expect)
+@}
+@end example
 
 If you specify multiple substitutions, they are performed in parallel, so e.g.
 @code{subs(lst(x == y, y == x))} exchanges @samp{x} and @samp{y}.
 
-The second form of @code{subs()} takes two lists, one for the symbols and
-one for the expressions to be substituted (both lists must contain the same
-number of elements). Using this form, you would write @code{subs(lst(x, y), lst(y, x))}
-to exchange @samp{x} and @samp{y}.
+The second form of @code{subs()} takes two lists, one for the objects to be
+replaced and one for the expressions to be substituted (both lists must
+contain the same number of elements). Using this form, you would write
+@code{subs(lst(x, y), lst(y, x))} to exchange @samp{x} and @samp{y}.
 
 
-@node Polynomial Arithmetic, Rational Expressions, Substituting Symbols, Methods and Functions
+@node Polynomial Arithmetic, Rational Expressions, Substituting Expressions, Methods and Functions
 @c    node-name, next, previous, up
 @section Polynomial arithmetic
 
@@ -2097,7 +2109,7 @@ being polynomials in the remaining variables.  The method
 @code{collect()} accomplishes this task:
 
 @example
-ex ex::collect(const symbol & s);
+ex ex::collect(const ex & s);
 @end example
 
 Note that the original polynomial needs to be in expanded form in order
@@ -2112,8 +2124,8 @@ The degree and low degree of a polynomial can be obtained using the two
 methods
 
 @example
-int ex::degree(const symbol & s);
-int ex::ldegree(const symbol & s);
+int ex::degree(const ex & s);
+int ex::ldegree(const ex & s);
 @end example
 
 which also work reliably on non-expanded input polynomials (they even work
@@ -2121,14 +2133,14 @@ on rational functions, returning the asymptotic degree). To extract
 a coefficient with a certain power from an expanded polynomial you use
 
 @example
-ex ex::coeff(const symbol & s, int n);
+ex ex::coeff(const ex & s, int n);
 @end example
 
 You can also obtain the leading and trailing coefficients with the methods
 
 @example
-ex ex::lcoeff(const symbol & s);
-ex ex::tcoeff(const symbol & s);
+ex ex::lcoeff(const ex & s);
+ex ex::tcoeff(const ex & s);
 @end example
 
 which are equivalent to @code{coeff(s, degree(s))} and @code{coeff(s, ldegree(s))},
@@ -2139,6 +2151,7 @@ polynomial is analyzed:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -2171,6 +2184,29 @@ As always, the exact output may vary between different versions of GiNaC
 or even from run to run since the internal canonical ordering is not
 within the user's sphere of influence.
 
+@code{degree()}, @code{ldegree()}, @code{coeff()}, @code{lcoeff()},
+@code{tcoeff()} and @code{collect()} can also be used to a certain degree
+with non-polynomial expressions as they not only work with symbols but with
+constants, functions and indexed objects as well:
+
+@example
+@{
+    symbol a("a"), b("b"), c("c");
+    idx i(symbol("i"), 3);
+
+    ex e = pow(sin(x) - cos(x), 4);
+    cout << e.degree(cos(x)) << endl;
+     // -> 4
+    cout << e.expand().coeff(sin(x), 3) << endl;
+     // -> -4*cos(x)
+
+    e = indexed(a+b, i) * indexed(b+c, i); 
+    e = e.expand(expand_options::expand_indexed);
+    cout << e.collect(indexed(b, i)) << endl;
+     // -> a.i*c.i+(a.i+c.i)*b.i+b.i^2
+@}
+@end example
+
 
 @subsection Polynomial division
 @cindex polynomial division
@@ -2271,6 +2307,39 @@ int main()
 @end example
 
 
+@subsection Square-free decomposition
+@cindex square-free decomposition
+@cindex factorization
+@cindex @code{sqrfree()}
+
+GiNaC still lacks proper factorization support.  Some form of
+factorization is, however, easily implemented by noting that factors
+appearing in a polynomial with power two or more also appear in the
+derivative and hence can easily be found by computing the GCD of the
+original polynomial and its derivatives.  Any system has an interface
+for this so called square-free factorization.  So we provide one, too:
+@example
+ex sqrfree(const ex & a, const lst & l = lst());
+@end example
+Here is an example that by the way illustrates how the result may depend
+on the order of differentiation:
+@example
+    ...
+    symbol x("x"), y("y");
+    ex BiVarPol = expand(pow(x-2*y*x,3) * pow(x+y,2) * (x-y));
+
+    cout << sqrfree(BiVarPol, lst(x,y)) << endl;
+     // -> (y+x)^2*(-1+6*y+8*y^3-12*y^2)*(y-x)*x^3
+
+    cout << sqrfree(BiVarPol, lst(y,x)) << endl;
+     // -> (1-2*y)^3*(y+x)^2*(-y+x)*x^3
+
+    cout << sqrfree(BiVarPol) << endl;
+     // -> depending on luck, any of the above
+    ...
+@end example
+
+
 @node Rational Expressions, Symbolic Differentiation, Polynomial Arithmetic, Methods and Functions
 @c    node-name, next, previous, up
 @section Rational expressions
@@ -2306,8 +2375,8 @@ int main()
     symbol x("x");
     ex t1 = (pow(x,2) + 2*x + 1)/(x + 1);
     ex t2 = (pow(sin(x),2) + 2*sin(x) + 1)/(sin(x) + 1);
-    cout << "t1 is " << t1.normal() << endl;
-    cout << "t2 is " << t2.normal() << endl;
+    std::cout << "t1 is " << t1.normal() << std::endl;
+    std::cout << "t2 is " << t2.normal() << std::endl;
 @}
 @end example
 
@@ -2426,7 +2495,7 @@ ex EulerNumber(unsigned n)
 int main()
 @{
     for (unsigned i=0; i<11; i+=2)
-        cout << EulerNumber(i) << endl;
+        std::cout << EulerNumber(i) << std::endl;
     return 0;
 @}
 @end example
@@ -2456,6 +2525,7 @@ term).  A sample application from special relativity could read:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -2513,6 +2583,8 @@ ex mechain_pi(int degr)
 
 int main()
 @{
+    using std::cout;  // just for fun, another way of...
+    using std::endl;  // ...dealing with this namespace std.
     ex pi_frac;
     for (int i=2; i<12; i+=2) @{
         pi_frac = mechain_pi(i);
@@ -2647,7 +2719,7 @@ The easiest way to print an expression is to write it to a stream:
 @{
     symbol x("x");
     ex e = 4.5+pow(x,2)*3/2;
-    cout << e << endl;    // prints '4.5+3/2*x^2'
+    cout << e << endl;    // prints '(4.5)+3/2*x^2'
     // ...
 @end example
 
@@ -2656,62 +2728,117 @@ to that used by most computer algebra systems, but not directly pastable
 into a GiNaC C++ program (note that in the above example, @code{pow(x,2)}
 is printed as @samp{x^2}).
 
-To print an expression in a way that can be directly used in a C or C++
-program, you use the method
+It is possible to print expressions in a number of different formats with
+the method
 
 @example
-void ex::printcsrc(ostream & os, unsigned type, const char *name);
+void ex::print(const print_context & c, unsigned level = 0);
 @end example
 
-This outputs a line in the form of a variable definition @code{<type> <name> = <expression>}.
-The possible types are defined in @file{ginac/flags.h} (@code{csrc_types})
-and mostly affect the way in which floating point numbers are written:
+The type of @code{print_context} object passed in determines the format
+of the output. The possible types are defined in @file{ginac/print.h}.
+All constructors of @code{print_context} and derived classes take an
+@code{ostream &} as their first argument.
+
+To print an expression in a way that can be directly used in a C or C++
+program, you pass a @code{print_csrc} object like this:
 
 @example
     // ...
-    e.printcsrc(cout, csrc_types::ctype_float, "f");
-    e.printcsrc(cout, csrc_types::ctype_double, "d");
-    e.printcsrc(cout, csrc_types::ctype_cl_N, "n");
+    cout << "float f = ";
+    e.print(print_csrc_float(cout));
+    cout << ";\n";
+
+    cout << "double d = ";
+    e.print(print_csrc_double(cout));
+    cout << ";\n";
+
+    cout << "cl_N n = ";
+    e.print(print_csrc_cl_N(cout));
+    cout << ";\n";
     // ...
 @end example
 
+The three possible types mostly affect the way in which floating point
+numbers are written.
+
 The above example will produce (note the @code{x^2} being converted to @code{x*x}):
 
 @example
 float f = (3.000000e+00/2.000000e+00)*(x*x)+4.500000e+00;
 double d = (3.000000e+00/2.000000e+00)*(x*x)+4.500000e+00;
-cl_N n = (cl_F("3.0")/cl_F("2.0"))*(x*x)+cl_F("4.5");
+cl_N n = (cln::cl_F("3.0")/cln::cl_F("2.0"))*(x*x)+cln::cl_F("4.5");
 @end example
 
-Finally, there are the two methods @code{printraw()} and @code{printtree()} intended for GiNaC
-developers, that provide a dump of the internal structure of an expression for
-debugging purposes:
+The @code{print_context} type @code{print_tree} provdes a dump of the
+internal structure of an expression for debugging purposes:
 
 @example
     // ...
-    e.printraw(cout); cout << endl << endl;
-    e.printtree(cout);
+    e.print(print_tree(cout));
 @}
 @end example
 
 produces
 
 @example
-ex(+((power(ex(symbol(name=x,serial=1,hash=150875740,flags=11)),ex(numeric(2)),hash=2,flags=3),numeric(3/2)),,hash=0,flags=3))
-
-type=Q25GiNaC3add, hash=0 (0x0), flags=3, nops=2
-    power: hash=2 (0x2), flags=3
-        x (symbol): serial=1, hash=150875740 (0x8fe2e5c), flags=11
-        2 (numeric): hash=2147483714 (0x80000042), flags=11
-    3/2 (numeric): hash=2147483745 (0x80000061), flags=11
+add, hash=0x0, flags=0x3, nops=2
+    power, hash=0x9, flags=0x3, nops=2
+        x (symbol), serial=3, hash=0x44a113a6, flags=0xf
+        2 (numeric), hash=0x80000042, flags=0xf
+    3/2 (numeric), hash=0x80000061, flags=0xf
     -----
     overall_coeff
-    4.5L0 (numeric): hash=2147483723 (0x8000004b), flags=11
+    4.5L0 (numeric), hash=0x8000004b, flags=0xf
     =====
 @end example
 
-The @code{printtree()} method is also available in @command{ginsh} as the
-@code{print()} function.
+This kind of output is also available in @command{ginsh} as the @code{print()}
+function.
+
+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:
+
+@example
+static void my_print(const ex & e)
+@{
+    if (is_ex_of_type(e, function))
+        cout << ex_to_function(e).get_name();
+    else
+        cout << e.bp->class_name();
+    cout << "(";
+    unsigned n = e.nops();
+    if (n)
+        for (unsigned i=0; i<n; i++) @{
+            my_print(e.op(i));
+            if (i != n-1)
+                cout << ",";
+        @}
+    else
+        cout << e;
+    cout << ")";
+@}
+
+int main(void)
+@{
+    my_print(pow(3, x) - 2 * sin(y / Pi)); cout << endl;
+    return 0;
+@}
+@end example
+
+This will produce
+
+@example
+add(power(numeric(3),symbol(x)),mul(sin(mul(power(constant(Pi),numeric(-1)),
+symbol(y))),numeric(-2)))
+@end example
+
+If you need an output format that makes it possible to accurately
+reconstruct an expression by feeding the output to a suitable parser or
+object factory, you should consider storing the expression in an
+@code{archive} object and reading the object properties from there.
+See the section on archiving for more information.
 
 
 @subsection Expression input
@@ -2745,6 +2872,7 @@ With this constructor, it's also easy to implement interactive GiNaC programs:
 #include <string>
 #include <stdexcept>
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -2776,8 +2904,9 @@ of class @code{archive} and archive expressions in it, giving each
 expression a unique name:
 
 @example
-#include <ginac/ginac.h>
 #include <fstream>
+using namespace std;
+#include <ginac/ginac.h>
 using namespace GiNaC;
 
 int main()
@@ -2851,6 +2980,92 @@ have had no effect because the @code{x} in @code{ex1} would have been a
 different symbol than the @code{x} which was defined at the beginning of
 the program, altough both would appear as @samp{x} when printed.
 
+You can also use the information stored in an @code{archive} object to
+output expressions in a format suitable for exact reconstruction. The
+@code{archive} and @code{archive_node} classes have a couple of member
+functions that let you access the stored properties:
+
+@example
+static void my_print2(const archive_node & n)
+@{
+    string class_name;
+    n.find_string("class", class_name);
+    cout << class_name << "(";
+
+    archive_node::propinfovector p;
+    n.get_properties(p);
+
+    unsigned num = p.size();
+    for (unsigned i=0; i<num; i++) @{
+        const string &name = p[i].name;
+        if (name == "class")
+            continue;
+        cout << name << "=";
+
+        unsigned count = p[i].count;
+        if (count > 1)
+            cout << "@{";
+
+        for (unsigned j=0; j<count; j++) @{
+            switch (p[i].type) @{
+                case archive_node::PTYPE_BOOL: @{
+                    bool x;
+                    n.find_bool(name, x);
+                    cout << (x ? "true" : "false");
+                    break;
+                @}
+                case archive_node::PTYPE_UNSIGNED: @{
+                    unsigned x;
+                    n.find_unsigned(name, x);
+                    cout << x;
+                    break;
+                @}
+                case archive_node::PTYPE_STRING: @{
+                    string x;
+                    n.find_string(name, x);
+                    cout << '\"' << x << '\"';
+                    break;
+                @}
+                case archive_node::PTYPE_NODE: @{
+                    const archive_node &x = n.find_ex_node(name, j);
+                    my_print2(x);
+                    break;
+                @}
+            @}
+
+            if (j != count-1)
+                cout << ",";
+        @}
+
+        if (count > 1)
+            cout << "@}";
+
+        if (i != num-1)
+            cout << ",";
+    @}
+
+    cout << ")";
+@}
+
+int main(void)
+@{
+    ex e = pow(2, x) - y;
+    archive ar(e, "e");
+    my_print2(ar.get_top_node(0)); cout << endl;
+    return 0;
+@}
+@end example
+
+This will produce:
+
+@example
+add(rest=@{power(basis=numeric(number="2"),exponent=symbol(name="x")),
+symbol(name="y")@},coeff=@{numeric(number="1"),numeric(number="-1")@},
+overall_coeff=numeric(number="0"))
+@end example
+
+Be warned, however, that the set of properties and their meaning for each
+class may change between GiNaC versions.
 
 
 @node Extending GiNaC, What does not belong into GiNaC, Input/Output, Top
@@ -3358,18 +3573,19 @@ class mystring : public basic
 @{
     ...
 public:
-    void print(ostream &os, unsigned upper_precedence) const;
+    void print(const print_context &c, unsigned level = 0) const;
     ...
 @};
 
-void mystring::print(ostream &os, unsigned upper_precedence) const
+void mystring::print(const print_context &c, unsigned level) const
 @{
-    os << '\"' << str << '\"';
+    // print_context::s is a reference to an ostream
+    c.s << '\"' << str << '\"';
 @}
 @end example
 
-The @code{upper_precedence} argument is only required for container classes
-to correctly parenthesize the output. Let's try again to print the expression:
+The @code{level} argument is only required for container classes to
+correctly parenthesize the output. Let's try again to print the expression:
 
 @example
 cout << e << endl;
@@ -3664,6 +3880,7 @@ as you try to change the second.  Consider the simple sequence of code:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -3695,6 +3912,7 @@ can be:
 
 @example
 #include <ginac/ginac.h>
+using namespace std;
 using namespace GiNaC;
 
 int main()
@@ -3988,13 +4206,13 @@ and the @samp{AM_PATH_GINAC} macro. The program used here is @file{simple.cpp}:
 
 @example
 #include <ginac/ginac.h>
-using namespace GiNaC;
 
 int main(void)
 @{
-    symbol x("x");
-    ex a = sin(x); 
-    cout << "Derivative of " << a << " is " << a.diff(x) << endl;
+    GiNaC::symbol x("x");
+    GiNaC::ex a = GiNaC::sin(x);
+    std::cout << "Derivative of " << a 
+              << " is " << a.diff(x) << std::endl;
     return 0;
 @}
 @end example