- symbols can have a LaTeX name, e.g. symbol s("s", "\\sigma");
authorChristian Bauer <Christian.Bauer@uni-mainz.de>
Fri, 4 May 2001 23:55:36 +0000 (23:55 +0000)
committerChristian Bauer <Christian.Bauer@uni-mainz.de>
Fri, 4 May 2001 23:55:36 +0000 (23:55 +0000)
- LaTeX output of indexed objects and tensors is much nicer
- simplify_indexed() can do arbitrary contractions in Dirac gamma strings
- constructing expressions like
    (x * dirac_ONE() + dirac_ONE()) * dirac_gamma(mu)
  resulted in an exception because mul::simplify_ncmul() wasn't implemented
  (maybe afrink didn't anticipate that an implementation would really be
  needed...)

ginac/clifford.cpp
ginac/color.cpp
ginac/constant.cpp
ginac/constant.h
ginac/idx.cpp
ginac/indexed.cpp
ginac/mul.cpp
ginac/symbol.cpp
ginac/symbol.h
ginac/tensor.cpp
ginac/utils.h

index 8a8bfe0..5a69cfd 100644 (file)
@@ -139,9 +139,9 @@ DEFAULT_COMPARE(diracone)
 DEFAULT_COMPARE(diracgamma)
 DEFAULT_COMPARE(diracgamma5)
 
-DEFAULT_PRINT(diracone, "ONE")
-DEFAULT_PRINT(diracgamma, "gamma")
-DEFAULT_PRINT(diracgamma5, "gamma5")
+DEFAULT_PRINT_LATEX(diracone, "ONE", "\\mathbb{1}")
+DEFAULT_PRINT_LATEX(diracgamma, "gamma", "\\gamma")
+DEFAULT_PRINT_LATEX(diracgamma5, "gamma5", "{\\gamma^5}")
 
 /** Contraction of a gamma matrix with something else. */
 bool diracgamma::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
@@ -151,34 +151,35 @@ bool diracgamma::contract_with(exvector::iterator self, exvector::iterator other
        GINAC_ASSERT(is_ex_of_type(self->op(0), diracgamma));
        unsigned char rl = ex_to_clifford(*self).get_representation_label();
 
-       if (is_ex_of_type(other->op(0), diracgamma)) {
+       if (is_ex_of_type(*other, clifford)) {
 
                ex dim = ex_to_idx(self->op(1)).get_dim();
 
-               // gamma~mu*gamma.mu = dim*ONE
+               // gamma~mu gamma.mu = dim ONE
                if (other - self == 1) {
                        *self = dim;
                        *other = dirac_ONE(rl);
                        return true;
 
-               // gamma~mu*gamma~alpha*gamma.mu = (2-dim)*gamma~alpha
+               // gamma~mu gamma~alpha gamma.mu = (2-dim) gamma~alpha
                } else if (other - self == 2
                        && is_ex_of_type(self[1], clifford)) {
                        *self = 2 - dim;
                        *other = _ex1();
                        return true;
 
-               // gamma~mu*gamma~alpha*gamma~beta*gamma.mu = 4*g~alpha~beta+(dim-4)*gamam~alpha*gamma~beta
+               // gamma~mu gamma~alpha gamma~beta gamma.mu = 4 g~alpha~beta + (dim-4) gamam~alpha gamma~beta
                } else if (other - self == 3
                        && is_ex_of_type(self[1], clifford)
                        && is_ex_of_type(self[2], clifford)) {
-                       *self = 4 * metric_tensor(self[1].op(1), self[2].op(1)) * dirac_ONE(rl) + (dim - 4) * self[1] * self[2];
+                       *self = 4 * lorentz_g(self[1].op(1), self[2].op(1)) * dirac_ONE(rl) + (dim - 4) * self[1] * self[2];
                        self[1] = _ex1();
                        self[2] = _ex1();
                        *other = _ex1();
                        return true;
 
-               // gamma~mu*gamma~alpha*gamma~beta*gamma~delta*gamma.mu = -2*gamma~delta*gamma~beta*gamma~alpha+(4-dim)*gamma~alpha*gamma~beta*gamma~delta
+#if 0
+               // gamma~mu gamma~alpha gamma~beta gamma~delta gamma.mu = -2 gamma~delta gamma~beta gamma~alpha + (4-dim) gamma~alpha gamma~beta gamma~delta
                } else if (other - self == 4
                        && is_ex_of_type(self[1], clifford)
                        && is_ex_of_type(self[2], clifford)
@@ -189,6 +190,30 @@ bool diracgamma::contract_with(exvector::iterator self, exvector::iterator other
                        self[3] = _ex1();
                        *other = _ex1();
                        return true;
+#endif
+
+               // gamma~mu S gamma~alpha gamma.mu = 2 gamma~alpha S - gamma~mu S gamma.mu gamma~alpha
+               // (commutate contracted indices towards each other, simplify_indexed()
+               // will re-expand and re-run the simplification)
+               } else {
+                       exvector::iterator it = self + 1, next_to_last = other - 1;
+                       while (it != other) {
+                               if (!is_ex_of_type(*it, clifford))
+                                       return false;
+                               it++;
+                       }
+
+                       it = self + 1;
+                       ex S = _ex1();
+                       while (it != next_to_last) {
+                               S *= *it;
+                               *it++ = _ex1();
+                       }
+
+                       *self = 2 * (*next_to_last) * S - (*self) * S * (*other) * (*next_to_last);
+                       *next_to_last = _ex1();
+                       *other = _ex1();
+                       return true;
                }
        }
 
@@ -364,12 +389,12 @@ ex dirac_trace(const ex & e, unsigned char rl)
                if (has_gamma5) {
 
                        // Trace of gamma5 * odd number of gammas and trace of
-                       // gamma5 * gamma_mu * gamma_nu are zero
+                       // gamma5 * gamma.mu * gamma.nu are zero
                        if ((num & 1) == 0 || num == 3)
                                return _ex0();
 
                        // Tr gamma5 S_2k =
-                       //   epsilon0123_mu1_mu2_mu3_mu4 * Tr gamma_mu1 gamma_mu2 gamma_mu3 gamma_mu4 S_2k
+                       //   epsilon0123.mu1.mu2.mu3.mu4 * Tr gamma.mu1 gamma.mu2 gamma.mu3 gamma.mu4 S_2k
                        ex dim = ex_to_idx(e.op(1).op(1)).get_dim();
                        varidx mu1((new symbol)->setflag(status_flags::dynallocated), dim),
                               mu2((new symbol)->setflag(status_flags::dynallocated), dim),
@@ -393,17 +418,17 @@ ex dirac_trace(const ex & e, unsigned char rl)
                        if ((num & 1) == 1)
                                return _ex0();
 
-                       // Tr gamma_mu gamma_nu = 4 g_mu_nu
+                       // Tr gamma.mu gamma.nu = 4 g.mu.nu
                        if (num == 2)
                                return 4 * lorentz_g(e.op(0).op(1), e.op(1).op(1));
 
                        // Traces of 4 or more gammas are computed recursively:
-                       // Tr gamma_mu1 gamma_mu2 ... gamma_mun =
-                       //   + eta_mu1_mu2 * Tr gamma_mu3 ... gamma_mun
-                       //   - eta_mu1_mu3 * Tr gamma_mu2 gamma_mu4 ... gamma_mun
-                       //   + eta_mu1_mu4 * Tr gamma_mu3 gamma_mu3 gamma_mu5 ... gamma_mun
+                       // Tr gamma.mu1 gamma.mu2 ... gamma.mun =
+                       //   + g.mu1.mu2 * Tr gamma.mu3 ... gamma.mun
+                       //   - g.mu1.mu3 * Tr gamma.mu2 gamma.mu4 ... gamma.mun
+                       //   + g.mu1.mu4 * Tr gamma.mu3 gamma.mu3 gamma.mu5 ... gamma.mun
                        //   - ...
-                       //   + eta_mu1_mun * Tr gamma_mu2 ... gamma_mu(n-1)
+                       //   + g.mu1.mun * Tr gamma.mu2 ... gamma.mu(n-1)
                        exvector v(num - 2);
                        int sign = 1;
                        const ex &ix1 = e.op(0).op(1);
index e36c1cb..77cf89c 100644 (file)
@@ -145,7 +145,7 @@ DEFAULT_COMPARE(su3t)
 DEFAULT_COMPARE(su3f)
 DEFAULT_COMPARE(su3d)
 
-DEFAULT_PRINT(su3one, "ONE")
+DEFAULT_PRINT_LATEX(su3one, "ONE", "\\mathbb{1}")
 DEFAULT_PRINT(su3t, "T")
 DEFAULT_PRINT(su3f, "f")
 DEFAULT_PRINT(su3d, "d")
index c056a96..e86a296 100644 (file)
@@ -41,7 +41,7 @@ GINAC_IMPLEMENT_REGISTERED_CLASS(constant, basic)
 
 // public
 
-constant::constant() : basic(TINFO_constant), name(""), ef(0), number(0), serial(next_serial++)
+constant::constant() : basic(TINFO_constant), ef(0), number(0), serial(next_serial++)
 {
        debugmsg("constant default ctor",LOGLEVEL_CONSTRUCT);
 }
@@ -53,6 +53,7 @@ void constant::copy(const constant & other)
 {
        inherited::copy(other);
        name = other.name;
+       TeX_name = other.TeX_name;
        serial = other.serial;
        ef = other.ef;
        if (other.number != 0)
@@ -74,17 +75,25 @@ void constant::destroy(bool call_parent)
 
 // public
 
-constant::constant(const std::string & initname, evalffunctype efun)
+constant::constant(const std::string & initname, evalffunctype efun, const std::string & texname)
   : basic(TINFO_constant), name(initname), ef(efun), number(0), serial(next_serial++)
 {
        debugmsg("constant ctor from string, function",LOGLEVEL_CONSTRUCT);
+       if (texname.empty())
+               TeX_name = "\\mbox{" + name + "}";
+       else
+               TeX_name = texname;
        setflag(status_flags::evaluated);
 }
 
-constant::constant(const std::string & initname, const numeric & initnumber)
+constant::constant(const std::string & initname, const numeric & initnumber, const std::string & texname)
   : basic(TINFO_constant), name(initname), ef(0), number(new numeric(initnumber)), serial(next_serial++)
 {
        debugmsg("constant ctor from string, numeric",LOGLEVEL_CONSTRUCT);
+       if (texname.empty())
+               TeX_name = "\\mbox{" + name + "}";
+       else
+               TeX_name = texname;
        setflag(status_flags::evaluated);
 }
 
@@ -135,16 +144,9 @@ void constant::print(const print_context & c, unsigned level) const
                c.s << std::string(level, ' ') << name << " (" << class_name() << ")"
                    << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
                    << std::endl;
-       } else if (is_of_type(c, print_latex)) {
-               if (name=="Pi")
-                       c.s << "\\pi";
-               else if (name=="Euler")
-                       c.s << "\\gamma_E";
-               else if (name=="Catalan")
-                       c.s << "G";
-               else
-                       c.s << "\\mbox{"+name+"}";
-       } else
+       } else if (is_of_type(c, print_latex))
+               c.s << TeX_name;
+       else
                c.s << name;
 }
 
@@ -237,13 +239,13 @@ unsigned constant::next_serial = 0;
 //////////
 
 /**  Pi. (3.14159...)  Diverts straight into CLN for evalf(). */
-const constant Pi("Pi", PiEvalf);
+const constant Pi("Pi", PiEvalf, "\\pi");
 
 /** Euler's constant. (0.57721...)  Sometimes called Euler-Mascheroni constant.
  *  Diverts straight into CLN for evalf(). */
-const constant Euler("Euler", EulerEvalf);
+const constant Euler("Euler", EulerEvalf, "\\gamma_E");
 
 /** Catalan's constant. (0.91597...)  Diverts straight into CLN for evalf(). */
-const constant Catalan("Catalan", CatalanEvalf);
+const constant Catalan("Catalan", CatalanEvalf, "G");
 
 } // namespace GiNaC
index fdf0661..bf85b38 100644 (file)
@@ -43,8 +43,8 @@ class constant : public basic
        
        // other ctors
 public:
-       constant(const std::string & initname, evalffunctype efun = 0);
-       constant(const std::string & initname, const numeric & initnumber);
+       constant(const std::string & initname, evalffunctype efun = 0, const std::string & texname = std::string());
+       constant(const std::string & initname, const numeric & initnumber, const std::string & texname = std::string());
        
        // functions overriding virtual functions from bases classes
 public:
@@ -67,10 +67,11 @@ protected:
 // member variables
        
 private:
-       std::string name;   ///< printname of this constant
+       std::string name;     ///< printname of this constant
+       std::string TeX_name; ///< LaTeX name
        evalffunctype ef;
-       numeric *number;    ///< numerical value this constant evalf()s to
-       unsigned serial;    ///< unique serial number for comparison
+       numeric *number;      ///< numerical value this constant evalf()s to
+       unsigned serial;      ///< unique serial number for comparison
        static unsigned next_serial;
 };
 
index 9f31a9a..44427a6 100644 (file)
@@ -136,11 +136,12 @@ void idx::print(const print_context & c, unsigned level) const
 
        } else {
 
-               c.s << ".";
+               if (!is_of_type(c, print_latex))
+                       c.s << ".";
                bool need_parens = !(is_ex_exactly_of_type(value, numeric) || is_ex_of_type(value, symbol));
                if (need_parens)
                        c.s << "(";
-               c.s << value;
+               value.print(c);
                if (need_parens)
                        c.s << ")";
        }
@@ -162,14 +163,16 @@ void varidx::print(const print_context & c, unsigned level) const
 
        } else {
 
-               if (covariant)
-                       c.s << ".";
-               else
-                       c.s << "~";
+               if (!is_of_type(c, print_latex)) {
+                       if (covariant)
+                               c.s << ".";
+                       else
+                               c.s << "~";
+               }
                bool need_parens = !(is_ex_exactly_of_type(value, numeric) || is_ex_of_type(value, symbol));
                if (need_parens)
                        c.s << "(";
-               c.s << value;
+               value.print(c);
                if (need_parens)
                        c.s << ")";
        }
index 6fbe40b..3c7005e 100644 (file)
@@ -199,14 +199,20 @@ void indexed::print(const print_context & c, unsigned level) const
 
        } else {
 
+               bool is_tex = is_of_type(c, print_latex);
                const ex & base = seq[0];
                bool need_parens = is_ex_exactly_of_type(base, add) || is_ex_exactly_of_type(base, mul)
-                               || is_ex_exactly_of_type(base, ncmul) || is_ex_exactly_of_type(base, power);
+                               || is_ex_exactly_of_type(base, ncmul) || is_ex_exactly_of_type(base, power)
+                               || is_ex_of_type(base, indexed);
+               if (is_tex)
+                       c.s << "{";
                if (need_parens)
                        c.s << "(";
                base.print(c);
                if (need_parens)
                        c.s << ")";
+               if (is_tex)
+                       c.s << "}";
                printindices(c, level);
        }
 }
@@ -409,10 +415,40 @@ ex indexed::expand(unsigned options) const
 void indexed::printindices(const print_context & c, unsigned level) const
 {
        if (seq.size() > 1) {
+
                exvector::const_iterator it=seq.begin() + 1, itend = seq.end();
-               while (it != itend) {
-                       it->print(c, level);
-                       it++;
+
+               if (is_of_type(c, print_latex)) {
+
+                       // TeX output: group by variance
+                       bool first = true;
+                       bool covariant = true;
+
+                       while (it != itend) {
+                               bool cur_covariant = (is_ex_of_type(*it, varidx) ? ex_to_varidx(*it).is_covariant() : true);
+                               if (first || cur_covariant != covariant) {
+                                       if (!first)
+                                               c.s << "}";
+                                       covariant = cur_covariant;
+                                       if (covariant)
+                                               c.s << "_{";
+                                       else
+                                               c.s << "^{";
+                               }
+                               it->print(c, level);
+                               c.s << " ";
+                               first = false;
+                               it++;
+                       }
+                       c.s << "}";
+
+               } else {
+
+                       // Ordinary output
+                       while (it != itend) {
+                               it->print(c, level);
+                               it++;
+                       }
                }
        }
 }
@@ -631,11 +667,12 @@ try_again:
                        if (contracted) {
 contraction_done:
                                if (is_ex_exactly_of_type(*it1, add) || is_ex_exactly_of_type(*it2, add)
-                                || is_ex_exactly_of_type(*it1, mul) || is_ex_exactly_of_type(*it2, mul)) {
+                                || is_ex_exactly_of_type(*it1, mul) || is_ex_exactly_of_type(*it2, mul)
+                                || is_ex_exactly_of_type(*it1, ncmul) || is_ex_exactly_of_type(*it2, ncmul)) {
 
                                        // One of the factors became a sum or product:
                                        // re-expand expression and run again
-                                       ex r = non_commutative ? ex(ncmul(v)) : ex(mul(v));
+                                       ex r = (non_commutative ? ex(ncmul(v)) : ex(mul(v)));
                                        return simplify_indexed(r, free_indices, sp);
                                }
 
index 27451f6..75f74b4 100644 (file)
@@ -405,7 +405,16 @@ ex mul::evalf(int level) const
 
 ex mul::simplify_ncmul(const exvector & v) const
 {
-       throw(std::logic_error("mul::simplify_ncmul() should never have been called!"));
+       if (seq.size()==0) {
+               return inherited::simplify_ncmul(v);
+       }
+
+       // Find first noncommutative element and call its simplify_ncmul()
+       for (epvector::const_iterator cit=seq.begin(); cit!=seq.end(); ++cit) {
+               if (cit->rest.return_type() == return_types::noncommutative)
+                       return cit->rest.simplify_ncmul(v);
+       }
+       return inherited::simplify_ncmul(v);
 }
 
 // protected
index bebb877..29d46bb 100644 (file)
@@ -41,7 +41,7 @@ GINAC_IMPLEMENT_REGISTERED_CLASS_NO_CTORS(symbol, basic)
 symbol::symbol() : inherited(TINFO_symbol), serial(next_serial++)
 {
        debugmsg("symbol default ctor", LOGLEVEL_CONSTRUCT);
-       name = autoname_prefix()+ToString(serial);
+       name = TeX_name = autoname_prefix()+ToString(serial);
        asexinfop = new assigned_ex_info;
        setflag(status_flags::evaluated | status_flags::expanded);
 }
@@ -51,6 +51,7 @@ void symbol::copy(const symbol & other)
 {
        inherited::copy(other);
        name = other.name;
+       TeX_name = other.TeX_name;
        serial = other.serial;
        asexinfop = other.asexinfop;
        ++asexinfop->refcount;
@@ -80,6 +81,17 @@ symbol::symbol(const std::string & initname) : inherited(TINFO_symbol)
 {
        debugmsg("symbol ctor from string", LOGLEVEL_CONSTRUCT);
        name = initname;
+       TeX_name = default_TeX_name();
+       serial = next_serial++;
+       asexinfop = new assigned_ex_info;
+       setflag(status_flags::evaluated | status_flags::expanded);
+}
+
+symbol::symbol(const std::string & initname, const std::string & texname) : inherited(TINFO_symbol)
+{
+       debugmsg("symbol ctor from string", LOGLEVEL_CONSTRUCT);
+       name = initname;
+       TeX_name = texname;
        serial = next_serial++;
        asexinfop = new assigned_ex_info;
        setflag(status_flags::evaluated | status_flags::expanded);
@@ -96,6 +108,8 @@ symbol::symbol(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst
        serial = next_serial++;
        if (!(n.find_string("name", name)))
                name = autoname_prefix() + ToString(serial);
+       if (!(n.find_string("TeXname", TeX_name)))
+               TeX_name = default_TeX_name();
        asexinfop = new assigned_ex_info;
        setflag(status_flags::evaluated);
 }
@@ -118,6 +132,8 @@ void symbol::archive(archive_node &n) const
 {
        inherited::archive(n);
        n.add_string("name", name);
+       if (TeX_name != default_TeX_name())
+               n.add_string("TeX_name", TeX_name);
 }
 
 //////////
@@ -143,25 +159,9 @@ void symbol::print(const print_context & c, unsigned level) const
                    << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec
                    << std::endl;
 
-       } else if (is_of_type(c, print_latex)) {
-               if (name=="alpha"        || name=="beta"         || name=="gamma"
-                || name=="delta"        || name=="epsilon"      || name=="varepsilon"
-                || name=="zeta"         || name=="eta"          || name=="theta"
-                || name=="vartheta"     || name=="iota"         || name=="kappa"
-                || name=="lambda"       || name=="mu"           || name=="nu"
-                || name=="xi"           || name=="omicron"      || name=="pi"
-                || name=="varpi"        || name=="rho"          || name=="varrho"
-                || name=="sigma"        || name=="varsigma"     || name=="tau"
-                || name=="upsilon"      || name=="phi"          || name=="varphix"
-                || name=="chi"          || name=="psi"          || name=="omega"
-                || name=="Gamma"        || name=="Delta"        || name=="Theta"
-                || name=="Lambda"       || name=="Xi"           || name=="Pi"
-                || name=="Sigma"        || name=="Upsilon"      || name=="Phi"
-                || name=="Psi"          || name=="Omega")
-                       c.s << "\\" << name;
-               else
-                       c.s << name;
-       } else
+       } else if (is_of_type(c, print_latex))
+               c.s << TeX_name;
+       else
                c.s << name;
 }
 
@@ -313,6 +313,28 @@ std::string & symbol::autoname_prefix(void)
        return *s;
 }
 
+/** Return default TeX name for symbol. This recognizes some greek letters. */
+std::string symbol::default_TeX_name(void) const
+{
+       if (name=="alpha"        || name=="beta"         || name=="gamma"
+        || name=="delta"        || name=="epsilon"      || name=="varepsilon"
+        || name=="zeta"         || name=="eta"          || name=="theta"
+        || name=="vartheta"     || name=="iota"         || name=="kappa"
+        || name=="lambda"       || name=="mu"           || name=="nu"
+        || name=="xi"           || name=="omicron"      || name=="pi"
+        || name=="varpi"        || name=="rho"          || name=="varrho"
+        || name=="sigma"        || name=="varsigma"     || name=="tau"
+        || name=="upsilon"      || name=="phi"          || name=="varphix"
+        || name=="chi"          || name=="psi"          || name=="omega"
+        || name=="Gamma"        || name=="Delta"        || name=="Theta"
+        || name=="Lambda"       || name=="Xi"           || name=="Pi"
+        || name=="Sigma"        || name=="Upsilon"      || name=="Phi"
+        || name=="Psi"          || name=="Omega")
+               return "\\" + name;
+       else
+               return name;
+}
+
 //////////
 // static member variables
 //////////
index 1f494cf..aec27ae 100644 (file)
@@ -68,6 +68,7 @@ protected:
        // other ctors
 public:
        explicit symbol(const std::string & initname);
+       explicit symbol(const std::string & initname, const std::string & texname);
        
        // functions overriding virtual functions from base classes
 public:
@@ -99,13 +100,15 @@ public:
        std::string get_name(void) const { return name; }
 private:
        std::string & autoname_prefix(void);
+       std::string default_TeX_name(void) const;
 
 // member variables
 
 protected:
        assigned_ex_info * asexinfop;   ///< ptr to assigned expression, deprecated
-       unsigned serial;    ///< unique serial number for comparison
-       std::string name;   ///< printname of this symbol
+       unsigned serial;       ///< unique serial number for comparison
+       std::string name;      ///< printname of this symbol
+       std::string TeX_name;  ///< LaTeX name of this symbol
 private:
        static unsigned next_serial;
 };
index d16bbfc..928c656 100644 (file)
@@ -162,10 +162,10 @@ int tensepsilon::compare_same_type(const basic & other) const
                return inherited::compare_same_type(other);
 }
 
-DEFAULT_PRINT(tensdelta, "delta")
+DEFAULT_PRINT_LATEX(tensdelta, "delta", "\\delta")
 DEFAULT_PRINT(tensmetric, "g")
-DEFAULT_PRINT(minkmetric, "eta")
-DEFAULT_PRINT(tensepsilon, "eps")
+DEFAULT_PRINT_LATEX(minkmetric, "eta", "\\eta")
+DEFAULT_PRINT_LATEX(tensepsilon, "eps", "\\epsilon")
 
 /** Automatic symbolic evaluation of an indexed delta tensor. */
 ex tensdelta::eval_indexed(const basic & i) const
index c2a5f23..79719fe 100644 (file)
@@ -301,6 +301,18 @@ void classname::print(const print_context & c, unsigned level) const \
                c.s << text; \
 }
 
+#define DEFAULT_PRINT_LATEX(classname, text, latex) \
+void classname::print(const print_context & c, unsigned level) const \
+{ \
+       debugmsg(#classname " print", LOGLEVEL_PRINT); \
+       if (is_of_type(c, print_tree)) \
+               inherited::print(c, level); \
+       else if (is_of_type(c, print_latex)) \
+               c.s << latex; \
+       else \
+               c.s << text; \
+}
+
 } // namespace GiNaC
 
 #endif // ndef __GINAC_UTILS_H__