GiNaC  1.6.2
basic.cpp
Go to the documentation of this file.
00001 
00005 /*
00006  *  GiNaC Copyright (C) 1999-2011 Johannes Gutenberg University Mainz, Germany
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021  */
00022 
00023 #include "basic.h"
00024 #include "ex.h"
00025 #include "numeric.h"
00026 #include "power.h"
00027 #include "add.h"
00028 #include "symbol.h"
00029 #include "lst.h"
00030 #include "ncmul.h"
00031 #include "relational.h"
00032 #include "operators.h"
00033 #include "wildcard.h"
00034 #include "archive.h"
00035 #include "utils.h"
00036 #include "hash_seed.h"
00037 #include "inifcns.h"
00038 
00039 #include <iostream>
00040 #include <stdexcept>
00041 #include <typeinfo>
00042 
00043 namespace GiNaC {
00044 
00045 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(basic, void,
00046   print_func<print_context>(&basic::do_print).
00047   print_func<print_tree>(&basic::do_print_tree).
00048   print_func<print_python_repr>(&basic::do_print_python_repr))
00049 
00050 
00051 // default constructor, destructor, copy constructor and assignment operator
00053 
00054 // public
00055 
00056 
00059 basic::basic(const basic & other) : flags(other.flags & ~status_flags::dynallocated), hashvalue(other.hashvalue)
00060 {
00061 }
00062 
00064 const basic & basic::operator=(const basic & other)
00065 {
00066     unsigned fl = other.flags & ~status_flags::dynallocated;
00067     if (typeid(*this) != typeid(other)) {
00068         // The other object is of a derived class, so clear the flags as they
00069         // might no longer apply (especially hash_calculated). Oh, and don't
00070         // copy the tinfo_key: it is already set correctly for this object.
00071         fl &= ~(status_flags::evaluated | status_flags::expanded | status_flags::hash_calculated);
00072     } else {
00073         // The objects are of the exact same class, so copy the hash value.
00074         hashvalue = other.hashvalue;
00075     }
00076     flags = fl;
00077     set_refcount(0);
00078     return *this;
00079 }
00080 
00081 // protected
00082 
00083 // none (all inlined)
00084 
00086 // other constructors
00088 
00089 // none (all inlined)
00090 
00092 // archiving
00094 
00096 void basic::read_archive(const archive_node& n, lst& syms)
00097 { }
00098 
00100 void basic::archive(archive_node &n) const
00101 {
00102     n.add_string("class", class_name());
00103 }
00104 
00106 // new virtual functions which can be overridden by derived classes
00108 
00109 // public
00110 
00116 void basic::print(const print_context & c, unsigned level) const
00117 {
00118     print_dispatch(get_class_info(), c, level);
00119 }
00120 
00126 void basic::print_dispatch(const registered_class_info & ri, const print_context & c, unsigned level) const
00127 {
00128     // Double dispatch on object type and print_context type
00129     const registered_class_info * reg_info = &ri;
00130     const print_context_class_info * pc_info = &c.get_class_info();
00131 
00132 next_class:
00133     const std::vector<print_functor> & pdt = reg_info->options.get_print_dispatch_table();
00134 
00135 next_context:
00136     unsigned id = pc_info->options.get_id();
00137     if (id >= pdt.size() || !(pdt[id].is_valid())) {
00138 
00139         // Method not found, try parent print_context class
00140         const print_context_class_info * parent_pc_info = pc_info->get_parent();
00141         if (parent_pc_info) {
00142             pc_info = parent_pc_info;
00143             goto next_context;
00144         }
00145 
00146         // Method still not found, try parent class
00147         const registered_class_info * parent_reg_info = reg_info->get_parent();
00148         if (parent_reg_info) {
00149             reg_info = parent_reg_info;
00150             pc_info = &c.get_class_info();
00151             goto next_class;
00152         }
00153 
00154         // Method still not found. This shouldn't happen because basic (the
00155         // base class of the algebraic hierarchy) registers a method for
00156         // print_context (the base class of the print context hierarchy),
00157         // so if we end up here, there's something wrong with the class
00158         // registry.
00159         throw (std::runtime_error(std::string("basic::print(): method for ") + class_name() + "/" + c.class_name() + " not found"));
00160 
00161     } else {
00162 
00163         // Call method
00164         pdt[id](*this, c, level);
00165     }
00166 }
00167 
00169 void basic::do_print(const print_context & c, unsigned level) const
00170 {
00171     c.s << "[" << class_name() << " object]";
00172 }
00173 
00175 void basic::do_print_tree(const print_tree & c, unsigned level) const
00176 {
00177     c.s << std::string(level, ' ') << class_name() << " @" << this
00178         << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec;
00179     if (nops())
00180         c.s << ", nops=" << nops();
00181     c.s << std::endl;
00182     for (size_t i=0; i<nops(); ++i)
00183         op(i).print(c, level + c.delta_indent);
00184 }
00185 
00187 void basic::do_print_python_repr(const print_python_repr & c, unsigned level) const
00188 {
00189     c.s << class_name() << "()";
00190 }
00191 
00199 void basic::dbgprint() const
00200 {
00201     this->print(print_dflt(std::cerr));
00202     std::cerr << std::endl;
00203 }
00204 
00208 void basic::dbgprinttree() const
00209 {
00210     this->print(print_tree(std::cerr));
00211 }
00212 
00214 unsigned basic::precedence() const
00215 {
00216     return 70;
00217 }
00218 
00222 bool basic::info(unsigned inf) const
00223 {
00224     // all possible properties are false for basic objects
00225     return false;
00226 }
00227 
00229 size_t basic::nops() const
00230 {
00231     // iterating from 0 to nops() on atomic objects should be an empty loop,
00232     // and accessing their elements is a range error.  Container objects should
00233     // override this.
00234     return 0;
00235 }
00236 
00238 ex basic::op(size_t i) const
00239 {
00240     throw(std::range_error(std::string("basic::op(): ") + class_name() + std::string(" has no operands")));
00241 }
00242 
00244 ex & basic::let_op(size_t i)
00245 {
00246     ensure_if_modifiable();
00247     throw(std::range_error(std::string("basic::let_op(): ") + class_name() + std::string(" has no operands")));
00248 }
00249 
00250 ex basic::operator[](const ex & index) const
00251 {
00252     if (is_exactly_a<numeric>(index))
00253         return op(static_cast<size_t>(ex_to<numeric>(index).to_int()));
00254 
00255     throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
00256 }
00257 
00258 ex basic::operator[](size_t i) const
00259 {
00260     return op(i);
00261 }
00262 
00263 ex & basic::operator[](const ex & index)
00264 {
00265     if (is_exactly_a<numeric>(index))
00266         return let_op(ex_to<numeric>(index).to_int());
00267 
00268     throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
00269 }
00270 
00271 ex & basic::operator[](size_t i)
00272 {
00273     return let_op(i);
00274 }
00275 
00280 bool basic::has(const ex & pattern, unsigned options) const
00281 {
00282     exmap repl_lst;
00283     if (match(pattern, repl_lst))
00284         return true;
00285     for (size_t i=0; i<nops(); i++)
00286         if (op(i).has(pattern, options))
00287             return true;
00288     
00289     return false;
00290 }
00291 
00294 ex basic::map(map_function & f) const
00295 {
00296     size_t num = nops();
00297     if (num == 0)
00298         return *this;
00299 
00300     basic *copy = NULL;
00301     for (size_t i=0; i<num; i++) {
00302         const ex & o = op(i);
00303         const ex & n = f(o);
00304         if (!are_ex_trivially_equal(o, n)) {
00305             if (copy == NULL)
00306                 copy = duplicate();
00307             copy->let_op(i) = n;
00308         }
00309     }
00310 
00311     if (copy) {
00312         copy->setflag(status_flags::dynallocated);
00313         copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
00314         return *copy;
00315     } else
00316         return *this;
00317 }
00318 
00320 bool basic::is_polynomial(const ex & var) const
00321 {
00322     return !has(var) || is_equal(ex_to<basic>(var));
00323 }
00324 
00326 int basic::degree(const ex & s) const
00327 {
00328     return is_equal(ex_to<basic>(s)) ? 1 : 0;
00329 }
00330 
00332 int basic::ldegree(const ex & s) const
00333 {
00334     return is_equal(ex_to<basic>(s)) ? 1 : 0;
00335 }
00336 
00338 ex basic::coeff(const ex & s, int n) const
00339 {
00340     if (is_equal(ex_to<basic>(s)))
00341         return n==1 ? _ex1 : _ex0;
00342     else
00343         return n==0 ? *this : _ex0;
00344 }
00345 
00349 ex basic::collect(const ex & s, bool distributed) const
00350 {
00351     ex x;
00352     if (is_a<lst>(s)) {
00353 
00354         // List of objects specified
00355         if (s.nops() == 0)
00356             return *this;
00357         if (s.nops() == 1)
00358             return collect(s.op(0));
00359 
00360         else if (distributed) {
00361 
00362             x = this->expand();
00363             if (! is_a<add>(x))
00364                 return x; 
00365             const lst& l(ex_to<lst>(s));
00366 
00367             exmap cmap;
00368             cmap[_ex1] = _ex0;
00369             for (const_iterator xi=x.begin(); xi!=x.end(); ++xi) {
00370                 ex key = _ex1;
00371                 ex pre_coeff = *xi;
00372                 for (lst::const_iterator li=l.begin(); li!=l.end(); ++li) {
00373                     int cexp = pre_coeff.degree(*li);
00374                     pre_coeff = pre_coeff.coeff(*li, cexp);
00375                     key *= pow(*li, cexp);
00376                 }
00377                 exmap::iterator ci = cmap.find(key);
00378                 if (ci != cmap.end())
00379                     ci->second += pre_coeff;
00380                 else
00381                     cmap.insert(exmap::value_type(key, pre_coeff));
00382             }
00383 
00384             exvector resv;
00385             for (exmap::const_iterator mi=cmap.begin(); mi != cmap.end(); ++mi)
00386                 resv.push_back((mi->first)*(mi->second));
00387             return (new add(resv))->setflag(status_flags::dynallocated);
00388 
00389         } else {
00390 
00391             // Recursive form
00392             x = *this;
00393             size_t n = s.nops() - 1;
00394             while (true) {
00395                 x = x.collect(s[n]);
00396                 if (n == 0)
00397                     break;
00398                 n--;
00399             }
00400         }
00401 
00402     } else {
00403 
00404         // Only one object specified
00405         for (int n=this->ldegree(s); n<=this->degree(s); ++n)
00406             x += this->coeff(s,n)*power(s,n);
00407     }
00408     
00409     // correct for lost fractional arguments and return
00410     return x + (*this - x).expand();
00411 }
00412 
00414 ex basic::eval(int level) const
00415 {
00416     // There is nothing to do for basic objects:
00417     return hold();
00418 }
00419 
00421 struct evalf_map_function : public map_function {
00422     int level;
00423     evalf_map_function(int l) : level(l) {}
00424     ex operator()(const ex & e) { return evalf(e, level); }
00425 };
00426 
00428 ex basic::evalf(int level) const
00429 {
00430     if (nops() == 0)
00431         return *this;
00432     else {
00433         if (level == 1)
00434             return *this;
00435         else if (level == -max_recursion_level)
00436             throw(std::runtime_error("max recursion level reached"));
00437         else {
00438             evalf_map_function map_evalf(level - 1);
00439             return map(map_evalf);
00440         }
00441     }
00442 }
00443 
00445 struct evalm_map_function : public map_function {
00446     ex operator()(const ex & e) { return evalm(e); }
00447 } map_evalm;
00448 
00450 ex basic::evalm() const
00451 {
00452     if (nops() == 0)
00453         return *this;
00454     else
00455         return map(map_evalm);
00456 }
00457 
00459 struct eval_integ_map_function : public map_function {
00460     ex operator()(const ex & e) { return eval_integ(e); }
00461 } map_eval_integ;
00462 
00464 ex basic::eval_integ() const
00465 {
00466     if (nops() == 0)
00467         return *this;
00468     else
00469         return map(map_eval_integ);
00470 }
00471 
00474 ex basic::eval_indexed(const basic & i) const
00475  // this function can't take a "const ex & i" because that would result
00476  // in an infinite eval() loop
00477 {
00478     // There is nothing to do for basic objects
00479     return i.hold();
00480 }
00481 
00490 ex basic::add_indexed(const ex & self, const ex & other) const
00491 {
00492     return self + other;
00493 }
00494 
00502 ex basic::scalar_mul_indexed(const ex & self, const numeric & other) const
00503 {
00504     return self * other;
00505 }
00506 
00519 bool basic::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
00520 {
00521     // Do nothing
00522     return false;
00523 }
00524 
00528 bool basic::match(const ex & pattern, exmap& repl_lst) const
00529 {
00530 /*
00531     Sweet sweet shapes, sweet sweet shapes,
00532     That's the key thing, right right.
00533     Feed feed face, feed feed shapes,
00534     But who is the king tonight?
00535     Who is the king tonight?
00536     Pattern is the thing, the key thing-a-ling,
00537     But who is the king of Pattern?
00538     But who is the king, the king thing-a-ling,
00539     Who is the king of Pattern?
00540     Bog is the king, the king thing-a-ling,
00541     Bog is the king of Pattern.
00542     Ba bu-bu-bu-bu bu-bu-bu-bu-bu-bu bu-bu
00543     Bog is the king of Pattern.
00544 */
00545 
00546     if (is_exactly_a<wildcard>(pattern)) {
00547 
00548         // Wildcard matches anything, but check whether we already have found
00549         // a match for that wildcard first (if so, the earlier match must be
00550         // the same expression)
00551         for (exmap::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
00552             if (it->first.is_equal(pattern))
00553                 return is_equal(ex_to<basic>(it->second));
00554         }
00555         repl_lst[pattern] = *this;
00556         return true;
00557 
00558     } else {
00559 
00560         // Expression must be of the same type as the pattern
00561         if (typeid(*this) != typeid(ex_to<basic>(pattern)))
00562             return false;
00563 
00564         // Number of subexpressions must match
00565         if (nops() != pattern.nops())
00566             return false;
00567 
00568         // No subexpressions? Then just compare the objects (there can't be
00569         // wildcards in the pattern)
00570         if (nops() == 0)
00571             return is_equal_same_type(ex_to<basic>(pattern));
00572 
00573         // Check whether attributes that are not subexpressions match
00574         if (!match_same_type(ex_to<basic>(pattern)))
00575             return false;
00576 
00577         // Even if the expression does not match the pattern, some of
00578         // its subexpressions could match it. For example, x^5*y^(-1)
00579         // does not match the pattern $0^5, but its subexpression x^5
00580         // does. So, save repl_lst in order to not add bogus entries.
00581         exmap tmp_repl = repl_lst;
00582         // Otherwise the subexpressions must match one-to-one
00583         for (size_t i=0; i<nops(); i++)
00584             if (!op(i).match(pattern.op(i), tmp_repl))
00585                 return false;
00586 
00587         // Looks similar enough, match found
00588         repl_lst = tmp_repl;
00589         return true;
00590     }
00591 }
00592 
00594 ex basic::subs_one_level(const exmap & m, unsigned options) const
00595 {
00596     exmap::const_iterator it;
00597 
00598     if (options & subs_options::no_pattern) {
00599         ex thisex = *this;
00600         it = m.find(thisex);
00601         if (it != m.end())
00602             return it->second;
00603         return thisex;
00604     } else {
00605         for (it = m.begin(); it != m.end(); ++it) {
00606             exmap repl_lst;
00607             if (match(ex_to<basic>(it->first), repl_lst))
00608                 return it->second.subs(repl_lst, options | subs_options::no_pattern);
00609             // avoid infinite recursion when re-substituting the wildcards
00610         }
00611     }
00612 
00613     return *this;
00614 }
00615 
00618 ex basic::subs(const exmap & m, unsigned options) const
00619 {
00620     size_t num = nops();
00621     if (num) {
00622 
00623         // Substitute in subexpressions
00624         for (size_t i=0; i<num; i++) {
00625             const ex & orig_op = op(i);
00626             const ex & subsed_op = orig_op.subs(m, options);
00627             if (!are_ex_trivially_equal(orig_op, subsed_op)) {
00628 
00629                 // Something changed, clone the object
00630                 basic *copy = duplicate();
00631                 copy->setflag(status_flags::dynallocated);
00632                 copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
00633 
00634                 // Substitute the changed operand
00635                 copy->let_op(i++) = subsed_op;
00636 
00637                 // Substitute the other operands
00638                 for (; i<num; i++)
00639                     copy->let_op(i) = op(i).subs(m, options);
00640 
00641                 // Perform substitutions on the new object as a whole
00642                 return copy->subs_one_level(m, options);
00643             }
00644         }
00645     }
00646 
00647     // Nothing changed or no subexpressions
00648     return subs_one_level(m, options);
00649 }
00650 
00658 ex basic::diff(const symbol & s, unsigned nth) const
00659 {
00660     // trivial: zeroth derivative
00661     if (nth==0)
00662         return ex(*this);
00663     
00664     // evaluate unevaluated *this before differentiating
00665     if (!(flags & status_flags::evaluated))
00666         return ex(*this).diff(s, nth);
00667     
00668     ex ndiff = this->derivative(s);
00669     while (!ndiff.is_zero() &&    // stop differentiating zeros
00670            nth>1) {
00671         ndiff = ndiff.diff(s);
00672         --nth;
00673     }
00674     return ndiff;
00675 }
00676 
00678 exvector basic::get_free_indices() const
00679 {
00680     return exvector(); // return an empty exvector
00681 }
00682 
00683 ex basic::conjugate() const
00684 {
00685     return *this;
00686 }
00687 
00688 ex basic::real_part() const
00689 {
00690     return real_part_function(*this).hold();
00691 }
00692 
00693 ex basic::imag_part() const
00694 {
00695     return imag_part_function(*this).hold();
00696 }
00697 
00698 ex basic::eval_ncmul(const exvector & v) const
00699 {
00700     return hold_ncmul(v);
00701 }
00702 
00703 // protected
00704 
00706 struct derivative_map_function : public map_function {
00707     const symbol &s;
00708     derivative_map_function(const symbol &sym) : s(sym) {}
00709     ex operator()(const ex & e) { return diff(e, s); }
00710 };
00711 
00716 ex basic::derivative(const symbol & s) const
00717 {
00718     if (nops() == 0)
00719         return _ex0;
00720     else {
00721         derivative_map_function map_derivative(s);
00722         return map(map_derivative);
00723     }
00724 }
00725 
00731 int basic::compare_same_type(const basic & other) const
00732 {
00733     return compare_pointers(this, &other);
00734 }
00735 
00741 bool basic::is_equal_same_type(const basic & other) const
00742 {
00743     return compare_same_type(other)==0;
00744 }
00745 
00756 bool basic::match_same_type(const basic & other) const
00757 {
00758     // The default is to only consider subexpressions, but not any other
00759     // attributes
00760     return true;
00761 }
00762 
00763 unsigned basic::return_type() const
00764 {
00765     return return_types::commutative;
00766 }
00767 
00768 return_type_t basic::return_type_tinfo() const
00769 {
00770     return_type_t rt;
00771     rt.tinfo = &typeid(*this);
00772     rt.rl = 0;
00773     return rt;
00774 }
00775 
00782 unsigned basic::calchash() const
00783 {
00784     unsigned v = make_hash_seed(typeid(*this));
00785     for (size_t i=0; i<nops(); i++) {
00786         v = rotate_left(v);
00787         v ^= this->op(i).gethash();
00788     }
00789 
00790     // store calculated hash value only if object is already evaluated
00791     if (flags & status_flags::evaluated) {
00792         setflag(status_flags::hash_calculated);
00793         hashvalue = v;
00794     }
00795 
00796     return v;
00797 }
00798 
00800 struct expand_map_function : public map_function {
00801     unsigned options;
00802     expand_map_function(unsigned o) : options(o) {}
00803     ex operator()(const ex & e) { return e.expand(options); }
00804 };
00805 
00808 ex basic::expand(unsigned options) const
00809 {
00810     if (nops() == 0)
00811         return (options == 0) ? setflag(status_flags::expanded) : *this;
00812     else {
00813         expand_map_function map_expand(options);
00814         return ex_to<basic>(map(map_expand)).setflag(options == 0 ? status_flags::expanded : 0);
00815     }
00816 }
00817 
00818 
00820 // non-virtual functions in this class
00822 
00823 // public
00824 
00828 int basic::compare(const basic & other) const
00829 {
00830 #ifdef GINAC_COMPARE_STATISTICS
00831     compare_statistics.total_basic_compares++;
00832 #endif
00833     const unsigned hash_this = gethash();
00834     const unsigned hash_other = other.gethash();
00835     if (hash_this<hash_other) return -1;
00836     if (hash_this>hash_other) return 1;
00837 #ifdef GINAC_COMPARE_STATISTICS
00838     compare_statistics.compare_same_hashvalue++;
00839 #endif
00840 
00841     const std::type_info& typeid_this = typeid(*this);
00842     const std::type_info& typeid_other = typeid(other);
00843     if (typeid_this == typeid_other) {
00844 //      int cmpval = compare_same_type(other);
00845 //      if (cmpval!=0) {
00846 //          std::cout << "hash collision, same type: " 
00847 //                    << *this << " and " << other << std::endl;
00848 //          this->print(print_tree(std::cout));
00849 //          std::cout << " and ";
00850 //          other.print(print_tree(std::cout));
00851 //          std::cout << std::endl;
00852 //      }
00853 //      return cmpval;
00854 #ifdef GINAC_COMPARE_STATISTICS
00855         compare_statistics.compare_same_type++;
00856 #endif
00857         return compare_same_type(other);
00858     } else {
00859 //      std::cout << "hash collision, different types: " 
00860 //                << *this << " and " << other << std::endl;
00861 //      this->print(print_tree(std::cout));
00862 //      std::cout << " and ";
00863 //      other.print(print_tree(std::cout));
00864 //      std::cout << std::endl;
00865         return (typeid_this.before(typeid_other) ? -1 : 1);
00866     }
00867 }
00868 
00875 bool basic::is_equal(const basic & other) const
00876 {
00877 #ifdef GINAC_COMPARE_STATISTICS
00878     compare_statistics.total_basic_is_equals++;
00879 #endif
00880     if (this->gethash()!=other.gethash())
00881         return false;
00882 #ifdef GINAC_COMPARE_STATISTICS
00883     compare_statistics.is_equal_same_hashvalue++;
00884 #endif
00885     if (typeid(*this) != typeid(other))
00886         return false;
00887     
00888 #ifdef GINAC_COMPARE_STATISTICS
00889     compare_statistics.is_equal_same_type++;
00890 #endif
00891     return is_equal_same_type(other);
00892 }
00893 
00894 // protected
00895 
00899 const basic & basic::hold() const
00900 {
00901     return setflag(status_flags::evaluated);
00902 }
00903 
00906 void basic::ensure_if_modifiable() const
00907 {
00908     if (get_refcount() > 1)
00909         throw(std::runtime_error("cannot modify multiply referenced object"));
00910     clearflag(status_flags::hash_calculated | status_flags::evaluated);
00911 }
00912 
00914 // global variables
00916 
00917 int max_recursion_level = 1024;
00918 
00919 
00920 #ifdef GINAC_COMPARE_STATISTICS
00921 compare_statistics_t::~compare_statistics_t()
00922 {
00923     std::clog << "ex::compare() called " << total_compares << " times" << std::endl;
00924     std::clog << "nontrivial compares: " << nontrivial_compares << " times" << std::endl;
00925     std::clog << "basic::compare() called " << total_basic_compares << " times" << std::endl;
00926     std::clog << "same hashvalue in compare(): " << compare_same_hashvalue << " times" << std::endl;
00927     std::clog << "compare_same_type() called " << compare_same_type << " times" << std::endl;
00928     std::clog << std::endl;
00929     std::clog << "ex::is_equal() called " << total_is_equals << " times" << std::endl;
00930     std::clog << "nontrivial is_equals: " << nontrivial_is_equals << " times" << std::endl;
00931     std::clog << "basic::is_equal() called " << total_basic_is_equals << " times" << std::endl;
00932     std::clog << "same hashvalue in is_equal(): " << is_equal_same_hashvalue << " times" << std::endl;
00933     std::clog << "is_equal_same_type() called " << is_equal_same_type << " times" << std::endl;
00934     std::clog << std::endl;
00935     std::clog << "basic::gethash() called " << total_gethash << " times" << std::endl;
00936     std::clog << "used cached hashvalue " << gethash_cached << " times" << std::endl;
00937 }
00938 
00939 compare_statistics_t compare_statistics;
00940 #endif
00941 
00942 } // namespace GiNaC

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.