+ return self * other;
+}
+
+/** Try to contract two indexed expressions that appear in the same product.
+ * If a contraction exists, the function overwrites one or both of the
+ * expressions and returns true. Otherwise it returns false. It is
+ * guaranteed that both expressions are of class indexed (or a subclass)
+ * and that at least one dummy index has been found. This functions is
+ * used internally by simplify_indexed().
+ *
+ * @param self Pointer to first indexed expression; it's base object is *this
+ * @param other Pointer to second indexed expression
+ * @param v The complete vector of factors
+ * @return true if the contraction was successful, false otherwise
+ * @see ex::simplify_indexed() */
+bool basic::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
+{
+ // Do nothing
+ return false;
+}
+
+/** Check whether the expression matches a given pattern. For every wildcard
+ * object in the pattern, an expression of the form "wildcard == matching_expression"
+ * is added to repl_lst. */
+bool basic::match(const ex & pattern, lst & repl_lst) const
+{
+/*
+ Sweet sweet shapes, sweet sweet shapes,
+ That's the key thing, right right.
+ Feed feed face, feed feed shapes,
+ But who is the king tonight?
+ Who is the king tonight?
+ Pattern is the thing, the key thing-a-ling,
+ But who is the king of Pattern?
+ But who is the king, the king thing-a-ling,
+ Who is the king of Pattern?
+ Bog is the king, the king thing-a-ling,
+ Bog is the king of Pattern.
+ Ba bu-bu-bu-bu bu-bu-bu-bu-bu-bu bu-bu
+ Bog is the king of Pattern.
+*/
+
+ if (is_exactly_a<wildcard>(pattern)) {
+
+ // Wildcard matches anything, but check whether we already have found
+ // a match for that wildcard first (if so, it the earlier match must
+ // be the same expression)
+ for (lst::const_iterator it = repl_lst.begin(); it != repl_lst.end(); ++it) {
+ if (it->op(0).is_equal(pattern))
+ return is_equal(ex_to<basic>(it->op(1)));
+ }
+ repl_lst.append(pattern == *this);
+ return true;
+
+ } else {
+
+ // Expression must be of the same type as the pattern
+ if (tinfo() != ex_to<basic>(pattern).tinfo())
+ return false;
+
+ // Number of subexpressions must match
+ if (nops() != pattern.nops())
+ return false;
+
+ // No subexpressions? Then just compare the objects (there can't be
+ // wildcards in the pattern)
+ if (nops() == 0)
+ return is_equal_same_type(ex_to<basic>(pattern));
+
+ // Check whether attributes that are not subexpressions match
+ if (!match_same_type(ex_to<basic>(pattern)))
+ return false;
+
+ // Otherwise the subexpressions must match one-to-one
+ for (size_t i=0; i<nops(); i++)
+ if (!op(i).match(pattern.op(i), repl_lst))
+ return false;
+
+ // Looks similar enough, match found
+ return true;
+ }
+}
+
+/** Helper function for subs(). Does not recurse into subexpressions. */
+ex basic::subs_one_level(const exmap & m, unsigned options) const
+{
+ exmap::const_iterator it;
+
+ if (options & subs_options::no_pattern) {
+ it = m.find(*this);
+ if (it != m.end())
+ return it->second;
+ } else {
+ for (it = m.begin(); it != m.end(); ++it) {
+ lst repl_lst;
+ if (match(ex_to<basic>(it->first), repl_lst))
+ return it->second.subs(repl_lst, options | subs_options::no_pattern); // avoid infinite recursion when re-substituting the wildcards
+ }
+ }
+