-
Notifications
You must be signed in to change notification settings - Fork 308
Description
Description
When a new type is inserted in the middle of the type enumeration, the type_code_ of all subsequent types increases by a fixed offset. Since the hash of every expression includes its type_code_ as the initial seed, like this:
hash_t seed = this->get_type_code();
hash_combine(seed, *a_);
hash_combine(seed, *b_);
However, because hash_combine uses a nonlinear mixing function, such as:
seed ^= static_cast<hash_t>(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
Even a uniform shift in the initial seed (i.e., type_code_) does not preserve the relative ordering of final hash values. Two expressions A and B that previously satisfied hash(A) < hash(B) may end up with hash(A) > hash(B) after the insertion—despite both their type codes being increased by the same amount.
This nonlinearity means that the iteration order in std::set<RCP<...>, RCPBasicKeyLess> can change unpredictably, breaking tests or logic that rely on deterministic expression ordering.
For example
Inserting SYMENGINE_INTEGRAL between SYMENGINE_DERIVATIVE and SYMENGINE_SUBS will cause the test_printing.cpp to fail.
SYMENGINE_ENUM(SYMENGINE_DERIVATIVE, Derivative)
SYMENGINE_ENUM(SYMENGINE_INTEGRAL, Integral)
SYMENGINE_ENUM(SYMENGINE_SUBS, Subs)
FAILED:
CHECK( s == reinterpret_cast<const char *>(u8"-1 \u2260 x \u2227 x < 0 \u2227 x < 2") )
with expansion:
"-1 ≠ x ∧ x < 2 ∧ x < 0"
==
"-1 ≠ x ∧ x < 0 ∧ x < 2"
FAILED:
CHECK( s == reinterpret_cast<const char *>(u8"-1 \u2260 x \u2228 x < 0 \u2228 x < 2") )
with expansion:
"-1 ≠ x ∨ x < 2 ∨ x < 0"
==
"-1 ≠ x ∨ x < 0 ∨ x < 2"
FAILED:
CHECK( s == reinterpret_cast<const char *>(u8"-1 \u2260 x \u22BB x < 0 \u22BB x < 2") )
with expansion:
"-1 ≠ x ⊻ x < 2 ⊻ x < 0"
==
"-1 ≠ x ⊻ x < 0 ⊻ x < 2"
Solution
We try to add the new SYMENGINE_ENUM at the end of the type_codes.inc file. Or after testing, it was found that inserting it in the middle could also pass all the tests.
Specifically, if we want to add SYMENGINE_INTEGRAL. Ensure that all tests are passed, and place it together with SYMENGINE_DERIVATIVE. We can put them all at the end of the type_codes.inc file. After I try, it is good.