3 * Implementation of GiNaC's ABC. */
6 * GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #ifdef DO_GINAC_ASSERT
37 #include "relational.h"
38 #include "operators.h"
46 GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(basic, void,
47 print_func<print_context>(&basic::do_print).
48 print_func<print_tree>(&basic::do_print_tree).
49 print_func<print_python_repr>(&basic::do_print_python_repr))
52 // default constructor, destructor, copy constructor and assignment operator
57 /** basic copy constructor: implicitly assumes that the other class is of
58 * the exact same type (as it's used by duplicate()), so it can copy the
59 * tinfo_key and the hash value. */
60 basic::basic(const basic & other) : tinfo_key(other.tinfo_key), flags(other.flags & ~status_flags::dynallocated), hashvalue(other.hashvalue)
64 /** basic assignment operator: the other object might be of a derived class. */
65 const basic & basic::operator=(const basic & other)
67 unsigned fl = other.flags & ~status_flags::dynallocated;
68 if (tinfo_key != other.tinfo_key) {
69 // The other object is of a derived class, so clear the flags as they
70 // might no longer apply (especially hash_calculated). Oh, and don't
71 // copy the tinfo_key: it is already set correctly for this object.
72 fl &= ~(status_flags::evaluated | status_flags::expanded | status_flags::hash_calculated);
74 // The objects are of the exact same class, so copy the hash value.
75 hashvalue = other.hashvalue;
96 /** Construct object from archive_node. */
97 basic::basic(const archive_node &n, lst &sym_lst) : flags(0)
99 // Reconstruct tinfo_key from class name
100 std::string class_name;
101 if (n.find_string("class", class_name))
102 tinfo_key = find_tinfo_key(class_name);
104 throw (std::runtime_error("archive node contains no class name"));
107 /** Unarchive the object. */
108 DEFAULT_UNARCHIVE(basic)
110 /** Archive the object. */
111 void basic::archive(archive_node &n) const
113 n.add_string("class", class_name());
117 // new virtual functions which can be overridden by derived classes
122 /** Output to stream. This performs double dispatch on the dynamic type of
123 * *this and the dynamic type of the supplied print context.
124 * @param c print context object that describes the output formatting
125 * @param level value that is used to identify the precedence or indentation
126 * level for placing parentheses and formatting */
127 void basic::print(const print_context & c, unsigned level) const
129 print_dispatch(get_class_info(), c, level);
132 /** Like print(), but dispatch to the specified class. Can be used by
133 * implementations of print methods to dispatch to the method of the
136 * @see basic::print */
137 void basic::print_dispatch(const registered_class_info & ri, const print_context & c, unsigned level) const
139 // Double dispatch on object type and print_context type
140 const registered_class_info * reg_info = &ri;
141 const print_context_class_info * pc_info = &c.get_class_info();
144 const std::vector<print_functor> & pdt = reg_info->options.get_print_dispatch_table();
147 unsigned id = pc_info->options.get_id();
148 if (id >= pdt.size() || !(pdt[id].is_valid())) {
150 // Method not found, try parent print_context class
151 const print_context_class_info * parent_pc_info = pc_info->get_parent();
152 if (parent_pc_info) {
153 pc_info = parent_pc_info;
157 // Method still not found, try parent class
158 const registered_class_info * parent_reg_info = reg_info->get_parent();
159 if (parent_reg_info) {
160 reg_info = parent_reg_info;
161 pc_info = &c.get_class_info();
165 // Method still not found. This shouldn't happen because basic (the
166 // base class of the algebraic hierarchy) registers a method for
167 // print_context (the base class of the print context hierarchy),
168 // so if we end up here, there's something wrong with the class
170 throw (std::runtime_error(std::string("basic::print(): method for ") + class_name() + "/" + c.class_name() + " not found"));
175 pdt[id](*this, c, level);
179 /** Default output to stream. */
180 void basic::do_print(const print_context & c, unsigned level) const
182 c.s << "[" << class_name() << " object]";
185 /** Tree output to stream. */
186 void basic::do_print_tree(const print_tree & c, unsigned level) const
188 c.s << std::string(level, ' ') << class_name() << " @" << this
189 << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec;
191 c.s << ", nops=" << nops();
193 for (size_t i=0; i<nops(); ++i)
194 op(i).print(c, level + c.delta_indent);
197 /** Python parsable output to stream. */
198 void basic::do_print_python_repr(const print_python_repr & c, unsigned level) const
200 c.s << class_name() << "()";
203 /** Little wrapper around print to be called within a debugger.
204 * This is needed because you cannot call foo.print(cout) from within the
205 * debugger because it might not know what cout is. This method can be
206 * invoked with no argument and it will simply print to stdout.
209 * @see basic::dbgprinttree */
210 void basic::dbgprint() const
212 this->print(print_dflt(std::cerr));
213 std::cerr << std::endl;
216 /** Little wrapper around printtree to be called within a debugger.
218 * @see basic::dbgprint */
219 void basic::dbgprinttree() const
221 this->print(print_tree(std::cerr));
224 /** Return relative operator precedence (for parenthezing output). */
225 unsigned basic::precedence() const
230 /** Information about the object.
232 * @see class info_flags */
233 bool basic::info(unsigned inf) const
235 // all possible properties are false for basic objects
239 /** Number of operands/members. */
240 size_t basic::nops() const
242 // iterating from 0 to nops() on atomic objects should be an empty loop,
243 // and accessing their elements is a range error. Container objects should
248 /** Return operand/member at position i. */
249 ex basic::op(size_t i) const
251 throw(std::range_error(std::string("basic::op(): ") + class_name() + std::string(" has no operands")));
254 /** Return modifyable operand/member at position i. */
255 ex & basic::let_op(size_t i)
257 ensure_if_modifiable();
258 throw(std::range_error(std::string("basic::let_op(): ") + class_name() + std::string(" has no operands")));
261 ex basic::operator[](const ex & index) const
263 if (is_exactly_a<numeric>(index))
264 return op(static_cast<size_t>(ex_to<numeric>(index).to_int()));
266 throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
269 ex basic::operator[](size_t i) const
274 ex & basic::operator[](const ex & index)
276 if (is_exactly_a<numeric>(index))
277 return let_op(ex_to<numeric>(index).to_int());
279 throw(std::invalid_argument(std::string("non-numeric indices not supported by ") + class_name()));
282 ex & basic::operator[](size_t i)
287 /** Test for occurrence of a pattern. An object 'has' a pattern if it matches
288 * the pattern itself or one of the children 'has' it. As a consequence
289 * (according to the definition of children) given e=x+y+z, e.has(x) is true
290 * but e.has(x+y) is false. */
291 bool basic::has(const ex & pattern, unsigned options) const
294 if (match(pattern, repl_lst))
296 for (size_t i=0; i<nops(); i++)
297 if (op(i).has(pattern, options))
303 /** Construct new expression by applying the specified function to all
304 * sub-expressions (one level only, not recursively). */
305 ex basic::map(map_function & f) const
312 for (size_t i=0; i<num; i++) {
313 const ex & o = op(i);
315 if (!are_ex_trivially_equal(o, n)) {
323 copy->setflag(status_flags::dynallocated);
324 copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
330 /** Check whether this is a polynomial in the given variables. */
331 bool basic::is_polynomial(const ex & var) const
333 return !has(var) || is_equal(ex_to<basic>(var));
336 /** Return degree of highest power in object s. */
337 int basic::degree(const ex & s) const
339 return is_equal(ex_to<basic>(s)) ? 1 : 0;
342 /** Return degree of lowest power in object s. */
343 int basic::ldegree(const ex & s) const
345 return is_equal(ex_to<basic>(s)) ? 1 : 0;
348 /** Return coefficient of degree n in object s. */
349 ex basic::coeff(const ex & s, int n) const
351 if (is_equal(ex_to<basic>(s)))
352 return n==1 ? _ex1 : _ex0;
354 return n==0 ? *this : _ex0;
357 /** Sort expanded expression in terms of powers of some object(s).
358 * @param s object(s) to sort in
359 * @param distributed recursive or distributed form (only used when s is a list) */
360 ex basic::collect(const ex & s, bool distributed) const
365 // List of objects specified
369 return collect(s.op(0));
371 else if (distributed) {
376 const lst& l(ex_to<lst>(s));
380 for (const_iterator xi=x.begin(); xi!=x.end(); ++xi) {
383 for (lst::const_iterator li=l.begin(); li!=l.end(); ++li) {
384 int cexp = pre_coeff.degree(*li);
385 pre_coeff = pre_coeff.coeff(*li, cexp);
386 key *= pow(*li, cexp);
388 exmap::iterator ci = cmap.find(key);
389 if (ci != cmap.end())
390 ci->second += pre_coeff;
392 cmap.insert(exmap::value_type(key, pre_coeff));
396 for (exmap::const_iterator mi=cmap.begin(); mi != cmap.end(); ++mi)
397 resv.push_back((mi->first)*(mi->second));
398 return (new add(resv))->setflag(status_flags::dynallocated);
404 size_t n = s.nops() - 1;
415 // Only one object specified
416 for (int n=this->ldegree(s); n<=this->degree(s); ++n)
417 x += this->coeff(s,n)*power(s,n);
420 // correct for lost fractional arguments and return
421 return x + (*this - x).expand();
424 /** Perform automatic non-interruptive term rewriting rules. */
425 ex basic::eval(int level) const
427 // There is nothing to do for basic objects:
431 /** Function object to be applied by basic::evalf(). */
432 struct evalf_map_function : public map_function {
434 evalf_map_function(int l) : level(l) {}
435 ex operator()(const ex & e) { return evalf(e, level); }
438 /** Evaluate object numerically. */
439 ex basic::evalf(int level) const
446 else if (level == -max_recursion_level)
447 throw(std::runtime_error("max recursion level reached"));
449 evalf_map_function map_evalf(level - 1);
450 return map(map_evalf);
455 /** Function object to be applied by basic::evalm(). */
456 struct evalm_map_function : public map_function {
457 ex operator()(const ex & e) { return evalm(e); }
460 /** Evaluate sums, products and integer powers of matrices. */
461 ex basic::evalm() const
466 return map(map_evalm);
469 /** Function object to be applied by basic::eval_integ(). */
470 struct eval_integ_map_function : public map_function {
471 ex operator()(const ex & e) { return eval_integ(e); }
474 /** Evaluate integrals, if result is known. */
475 ex basic::eval_integ() const
480 return map(map_eval_integ);
483 /** Perform automatic symbolic evaluations on indexed expression that
484 * contains this object as the base expression. */
485 ex basic::eval_indexed(const basic & i) const
486 // this function can't take a "const ex & i" because that would result
487 // in an infinite eval() loop
489 // There is nothing to do for basic objects
493 /** Add two indexed expressions. They are guaranteed to be of class indexed
494 * (or a subclass) and their indices are compatible. This function is used
495 * internally by simplify_indexed().
497 * @param self First indexed expression; its base object is *this
498 * @param other Second indexed expression
499 * @return sum of self and other
500 * @see ex::simplify_indexed() */
501 ex basic::add_indexed(const ex & self, const ex & other) const
506 /** Multiply an indexed expression with a scalar. This function is used
507 * internally by simplify_indexed().
509 * @param self Indexed expression; its base object is *this
510 * @param other Numeric value
511 * @return product of self and other
512 * @see ex::simplify_indexed() */
513 ex basic::scalar_mul_indexed(const ex & self, const numeric & other) const
518 /** Try to contract two indexed expressions that appear in the same product.
519 * If a contraction exists, the function overwrites one or both of the
520 * expressions and returns true. Otherwise it returns false. It is
521 * guaranteed that both expressions are of class indexed (or a subclass)
522 * and that at least one dummy index has been found. This functions is
523 * used internally by simplify_indexed().
525 * @param self Pointer to first indexed expression; its base object is *this
526 * @param other Pointer to second indexed expression
527 * @param v The complete vector of factors
528 * @return true if the contraction was successful, false otherwise
529 * @see ex::simplify_indexed() */
530 bool basic::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
536 /** Check whether the expression matches a given pattern. For every wildcard
537 * object in the pattern, a pair with the wildcard as a key and matching
538 * expression as a value is added to repl_lst. */
539 bool basic::match(const ex & pattern, exmap& repl_lst) const
542 Sweet sweet shapes, sweet sweet shapes,
543 That's the key thing, right right.
544 Feed feed face, feed feed shapes,
545 But who is the king tonight?
546 Who is the king tonight?
547 Pattern is the thing, the key thing-a-ling,
548 But who is the king of Pattern?
549 But who is the king, the king thing-a-ling,
550 Who is the king of Pattern?
551 Bog is the king, the king thing-a-ling,
552 Bog is the king of Pattern.
553 Ba bu-bu-bu-bu bu-bu-bu-bu-bu-bu bu-bu
554 Bog is the king of Pattern.
557 if (is_exactly_a<wildcard>(pattern)) {
559 // Wildcard matches anything, but check whether we already have found
560 // a match for that wildcard first (if so, the earlier match must be
561 // the same expression)
562 for (exmap::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
563 if (it->first.is_equal(pattern))
564 return is_equal(ex_to<basic>(it->second));
566 repl_lst[pattern] = *this;
571 // Expression must be of the same type as the pattern
572 if (tinfo() != ex_to<basic>(pattern).tinfo())
575 // Number of subexpressions must match
576 if (nops() != pattern.nops())
579 // No subexpressions? Then just compare the objects (there can't be
580 // wildcards in the pattern)
582 return is_equal_same_type(ex_to<basic>(pattern));
584 // Check whether attributes that are not subexpressions match
585 if (!match_same_type(ex_to<basic>(pattern)))
588 // Even if the expression does not match the pattern, some of
589 // its subexpressions could match it. For example, x^5*y^(-1)
590 // does not match the pattern $0^5, but its subexpression x^5
591 // does. So, save repl_lst in order to not add bogus entries.
592 exmap tmp_repl = repl_lst;
593 // Otherwise the subexpressions must match one-to-one
594 for (size_t i=0; i<nops(); i++)
595 if (!op(i).match(pattern.op(i), tmp_repl))
598 // Looks similar enough, match found
604 /** Helper function for subs(). Does not recurse into subexpressions. */
605 ex basic::subs_one_level(const exmap & m, unsigned options) const
607 exmap::const_iterator it;
609 if (options & subs_options::no_pattern) {
616 for (it = m.begin(); it != m.end(); ++it) {
618 if (match(ex_to<basic>(it->first), repl_lst))
619 return it->second.subs(repl_lst, options | subs_options::no_pattern);
620 // avoid infinite recursion when re-substituting the wildcards
627 /** Substitute a set of objects by arbitrary expressions. The ex returned
628 * will already be evaluated. */
629 ex basic::subs(const exmap & m, unsigned options) const
634 // Substitute in subexpressions
635 for (size_t i=0; i<num; i++) {
636 const ex & orig_op = op(i);
637 const ex & subsed_op = orig_op.subs(m, options);
638 if (!are_ex_trivially_equal(orig_op, subsed_op)) {
640 // Something changed, clone the object
641 basic *copy = duplicate();
642 copy->setflag(status_flags::dynallocated);
643 copy->clearflag(status_flags::hash_calculated | status_flags::expanded);
645 // Substitute the changed operand
646 copy->let_op(i++) = subsed_op;
648 // Substitute the other operands
650 copy->let_op(i) = op(i).subs(m, options);
652 // Perform substitutions on the new object as a whole
653 return copy->subs_one_level(m, options);
658 // Nothing changed or no subexpressions
659 return subs_one_level(m, options);
662 /** Default interface of nth derivative ex::diff(s, n). It should be called
663 * instead of ::derivative(s) for first derivatives and for nth derivatives it
664 * just recurses down.
666 * @param s symbol to differentiate in
667 * @param nth order of differentiation
669 ex basic::diff(const symbol & s, unsigned nth) const
671 // trivial: zeroth derivative
675 // evaluate unevaluated *this before differentiating
676 if (!(flags & status_flags::evaluated))
677 return ex(*this).diff(s, nth);
679 ex ndiff = this->derivative(s);
680 while (!ndiff.is_zero() && // stop differentiating zeros
682 ndiff = ndiff.diff(s);
688 /** Return a vector containing the free indices of an expression. */
689 exvector basic::get_free_indices() const
691 return exvector(); // return an empty exvector
694 ex basic::conjugate() const
699 ex basic::real_part() const
701 return real_part_function(*this).hold();
704 ex basic::imag_part() const
706 return imag_part_function(*this).hold();
709 ex basic::eval_ncmul(const exvector & v) const
711 return hold_ncmul(v);
716 /** Function object to be applied by basic::derivative(). */
717 struct derivative_map_function : public map_function {
719 derivative_map_function(const symbol &sym) : s(sym) {}
720 ex operator()(const ex & e) { return diff(e, s); }
723 /** Default implementation of ex::diff(). It maps the operation on the
724 * operands (or returns 0 when the object has no operands).
727 ex basic::derivative(const symbol & s) const
732 derivative_map_function map_derivative(s);
733 return map(map_derivative);
737 /** Returns order relation between two objects of same type. This needs to be
738 * implemented by each class. It may never return anything else than 0,
739 * signalling equality, or +1 and -1 signalling inequality and determining
740 * the canonical ordering. (Perl hackers will wonder why C++ doesn't feature
741 * the spaceship operator <=> for denoting just this.) */
742 int basic::compare_same_type(const basic & other) const
744 return compare_pointers(this, &other);
747 /** Returns true if two objects of same type are equal. Normally needs
748 * not be reimplemented as long as it wasn't overwritten by some parent
749 * class, since it just calls compare_same_type(). The reason why this
750 * function exists is that sometimes it is easier to determine equality
751 * than an order relation and then it can be overridden. */
752 bool basic::is_equal_same_type(const basic & other) const
754 return compare_same_type(other)==0;
757 /** Returns true if the attributes of two objects are similar enough for
758 * a match. This function must not match subexpressions (this is already
759 * done by basic::match()). Only attributes not accessible by op() should
760 * be compared. This is also the reason why this function doesn't take the
761 * wildcard replacement list from match() as an argument: only subexpressions
762 * are subject to wildcard matches. Also, this function only needs to be
763 * implemented for container classes because is_equal_same_type() is
764 * automatically used instead of match_same_type() if nops() == 0.
766 * @see basic::match */
767 bool basic::match_same_type(const basic & other) const
769 // The default is to only consider subexpressions, but not any other
774 unsigned basic::return_type() const
776 return return_types::commutative;
779 tinfo_t basic::return_type_tinfo() const
784 /** Compute the hash value of an object and if it makes sense to store it in
785 * the objects status_flags, do so. The method inherited from class basic
786 * computes a hash value based on the type and hash values of possible
787 * members. For this reason it is well suited for container classes but
788 * atomic classes should override this implementation because otherwise they
789 * would all end up with the same hashvalue. */
790 unsigned basic::calchash() const
792 unsigned v = golden_ratio_hash((p_int)tinfo());
793 for (size_t i=0; i<nops(); i++) {
795 v ^= this->op(i).gethash();
798 // store calculated hash value only if object is already evaluated
799 if (flags & status_flags::evaluated) {
800 setflag(status_flags::hash_calculated);
807 /** Function object to be applied by basic::expand(). */
808 struct expand_map_function : public map_function {
810 expand_map_function(unsigned o) : options(o) {}
811 ex operator()(const ex & e) { return e.expand(options); }
814 /** Expand expression, i.e. multiply it out and return the result as a new
816 ex basic::expand(unsigned options) const
819 return (options == 0) ? setflag(status_flags::expanded) : *this;
821 expand_map_function map_expand(options);
822 return ex_to<basic>(map(map_expand)).setflag(options == 0 ? status_flags::expanded : 0);
828 // non-virtual functions in this class
833 /** Compare objects syntactically to establish canonical ordering.
834 * All compare functions return: -1 for *this less than other, 0 equal,
836 int basic::compare(const basic & other) const
838 #ifdef GINAC_COMPARE_STATISTICS
839 compare_statistics.total_basic_compares++;
841 const unsigned hash_this = gethash();
842 const unsigned hash_other = other.gethash();
843 if (hash_this<hash_other) return -1;
844 if (hash_this>hash_other) return 1;
845 #ifdef GINAC_COMPARE_STATISTICS
846 compare_statistics.compare_same_hashvalue++;
849 const tinfo_t typeid_this = tinfo();
850 const tinfo_t typeid_other = other.tinfo();
851 if (typeid_this==typeid_other) {
852 GINAC_ASSERT(typeid(*this)==typeid(other));
853 // int cmpval = compare_same_type(other);
855 // std::cout << "hash collision, same type: "
856 // << *this << " and " << other << std::endl;
857 // this->print(print_tree(std::cout));
858 // std::cout << " and ";
859 // other.print(print_tree(std::cout));
860 // std::cout << std::endl;
863 #ifdef GINAC_COMPARE_STATISTICS
864 compare_statistics.compare_same_type++;
866 return compare_same_type(other);
868 // std::cout << "hash collision, different types: "
869 // << *this << " and " << other << std::endl;
870 // this->print(print_tree(std::cout));
871 // std::cout << " and ";
872 // other.print(print_tree(std::cout));
873 // std::cout << std::endl;
874 return (typeid_this<typeid_other ? -1 : 1);
878 /** Test for syntactic equality.
879 * This is only a quick test, meaning objects should be in the same domain.
880 * You might have to .expand(), .normal() objects first, depending on the
881 * domain of your computation, to get a more reliable answer.
883 * @see is_equal_same_type */
884 bool basic::is_equal(const basic & other) const
886 #ifdef GINAC_COMPARE_STATISTICS
887 compare_statistics.total_basic_is_equals++;
889 if (this->gethash()!=other.gethash())
891 #ifdef GINAC_COMPARE_STATISTICS
892 compare_statistics.is_equal_same_hashvalue++;
894 if (this->tinfo()!=other.tinfo())
897 GINAC_ASSERT(typeid(*this)==typeid(other));
899 #ifdef GINAC_COMPARE_STATISTICS
900 compare_statistics.is_equal_same_type++;
902 return is_equal_same_type(other);
907 /** Stop further evaluation.
909 * @see basic::eval */
910 const basic & basic::hold() const
912 return setflag(status_flags::evaluated);
915 /** Ensure the object may be modified without hurting others, throws if this
916 * is not the case. */
917 void basic::ensure_if_modifiable() const
919 if (get_refcount() > 1)
920 throw(std::runtime_error("cannot modify multiply referenced object"));
921 clearflag(status_flags::hash_calculated | status_flags::evaluated);
928 int max_recursion_level = 1024;
931 #ifdef GINAC_COMPARE_STATISTICS
932 compare_statistics_t::~compare_statistics_t()
934 std::clog << "ex::compare() called " << total_compares << " times" << std::endl;
935 std::clog << "nontrivial compares: " << nontrivial_compares << " times" << std::endl;
936 std::clog << "basic::compare() called " << total_basic_compares << " times" << std::endl;
937 std::clog << "same hashvalue in compare(): " << compare_same_hashvalue << " times" << std::endl;
938 std::clog << "compare_same_type() called " << compare_same_type << " times" << std::endl;
939 std::clog << std::endl;
940 std::clog << "ex::is_equal() called " << total_is_equals << " times" << std::endl;
941 std::clog << "nontrivial is_equals: " << nontrivial_is_equals << " times" << std::endl;
942 std::clog << "basic::is_equal() called " << total_basic_is_equals << " times" << std::endl;
943 std::clog << "same hashvalue in is_equal(): " << is_equal_same_hashvalue << " times" << std::endl;
944 std::clog << "is_equal_same_type() called " << is_equal_same_type << " times" << std::endl;
945 std::clog << std::endl;
946 std::clog << "basic::gethash() called " << total_gethash << " times" << std::endl;
947 std::clog << "used cached hashvalue " << gethash_cached << " times" << std::endl;
950 compare_statistics_t compare_statistics;