+/** Substitute symbols in expression and return the result as a new expression.
+ * There are two valid types of replacement arguments: 1) a relational like
+ * symbol==ex and 2) a list of relationals lst(symbol1==ex1,symbol2==ex2,...),
+ * which is converted to subs(lst(symbol1,symbol2,...),lst(ex1,ex2,...)).
+ * In addition, an object of class idx can be used instead of a symbol. */
+ex basic::subs(const ex & e) const
+{
+ if (e.info(info_flags::relation_equal)) {
+ return subs(lst(e));
+ }
+ if (!e.info(info_flags::list)) {
+ throw(std::invalid_argument("basic::subs(ex): argument must be a list"));
+ }
+ lst ls;
+ lst lr;
+ for (unsigned i=0; i<e.nops(); i++) {
+ if (!e.op(i).info(info_flags::relation_equal)) {
+ throw(std::invalid_argument("basic::subs(ex): argument must be a list or equations"));
+ }
+ if (!e.op(i).op(0).info(info_flags::symbol)) {
+ if (!e.op(i).op(0).info(info_flags::idx)) {
+ throw(std::invalid_argument("basic::subs(ex): lhs must be a symbol or an idx"));
+ }
+ }
+ ls.append(e.op(i).op(0));
+ lr.append(e.op(i).op(1));
+ }
+ return subs(ls,lr);
+}
+
+/** Compare objects to establish canonical ordering.
+ * All compare functions return: -1 for *this less than other, 0 equal,
+ * 1 greater. */
+int basic::compare(const basic & other) const
+{
+ unsigned hash_this = gethash();
+ unsigned hash_other = other.gethash();
+
+ if (hash_this<hash_other) return -1;
+ if (hash_this>hash_other) return 1;
+
+ unsigned typeid_this = tinfo();
+ unsigned typeid_other = other.tinfo();
+
+ if (typeid_this<typeid_other) {
+// std::cout << "hash collision, different types: "
+// << *this << " and " << other << std::endl;
+// this->printraw(std::cout);
+// std::cout << " and ";
+// other.printraw(std::cout);
+// std::cout << std::endl;
+ return -1;
+ }
+ if (typeid_this>typeid_other) {
+// std::cout << "hash collision, different types: "
+// << *this << " and " << other << std::endl;
+// this->printraw(std::cout);
+// std::cout << " and ";
+// other.printraw(std::cout);
+// std::cout << std::endl;
+ return 1;
+ }
+
+ GINAC_ASSERT(typeid(*this)==typeid(other));
+
+// int cmpval = compare_same_type(other);
+// if ((cmpval!=0) && (hash_this<0x80000000U)) {
+// std::cout << "hash collision, same type: "
+// << *this << " and " << other << std::endl;
+// this->printraw(std::cout);
+// std::cout << " and ";
+// other.printraw(std::cout);
+// std::cout << std::endl;
+// }
+// return cmpval;
+
+ return compare_same_type(other);
+}
+
+/** Test for equality.
+ * This is only a quick test, meaning objects should be in the same domain.
+ * You might have to .expand(), .normal() objects first, depending on the
+ * domain of your computation, to get a more reliable answer.
+ *
+ * @see is_equal_same_type */
+bool basic::is_equal(const basic & other) const
+{
+ if (this->gethash()!=other.gethash())
+ return false;
+ if (this->tinfo()!=other.tinfo())
+ return false;
+
+ GINAC_ASSERT(typeid(*this)==typeid(other));
+
+ return this->is_equal_same_type(other);