diff --git a/poincare/Makefile b/poincare/Makefile index b092569d9..0170decbf 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -35,6 +35,7 @@ tests += $(addprefix poincare/test/,\ addition.cpp\ fraction.cpp\ integer.cpp\ + identity.cpp\ product.cpp\ simplify_utils.cpp\ simplify_addition.cpp\ diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index bfb12d9f7..994850cfb 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -45,6 +45,11 @@ class Expression { virtual bool isCommutative(); virtual float approximate(Context& context) = 0; + private: + bool sequentialOperandsIdentity(Expression * e); + bool commutativeOperandsIdentity(Expression * e); + bool combinatoryCommutativeOperandsIdentity(Expression * e, bool * operandMatched, + int leftToMatch); }; #endif diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index f5e90bcc6..35ae077c3 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -66,12 +66,75 @@ Expression * Expression::simplify() { return result; } +bool Expression::sequentialOperandsIdentity(Expression * e) { + /* Here we simply test all operands for identity in the order they are defined + * in. */ + for (int i=0; inumberOfOperands(); i++) { + if (!e->operand(i)->isIdenticalTo(this->operand(i))) { + return false; + } + } + return true; +} + +bool Expression::combinatoryCommutativeOperandsIdentity(Expression * e, + bool * operandMatched, int leftToMatch) { + if (leftToMatch == 0) { + return true; + } + + // We try to test for equality the i-th operand of our first expression. + int i = this->numberOfOperands() - leftToMatch; + for (int j = 0; jnumberOfOperands(); j++) { + /* If the operand of the second expression has already been associated with + * a previous operand we skip it */ + if (operandMatched[j]) { + continue; + } + if (this->operand(i)->isIdenticalTo(e->operand(j))) { + // We managed to match this operand. + operandMatched[j] = true; + /* We check that we can match the rest in this configuration, if so we + * are good. */ + if (this->combinatoryCommutativeOperandsIdentity(e, operandMatched, leftToMatch - 1)) { + return true; + } + // Otherwise we backtrack. + operandMatched[j] = false; + } + } + + return false; +} + +bool Expression::commutativeOperandsIdentity(Expression * e) { + int leftToMatch = this->numberOfOperands(); + + /* We create a table allowing us to know which operands of the second + * expression have been associated with one of the operands of the first + * expression */ + bool * operandMatched = (bool *) malloc (this->numberOfOperands() * sizeof(bool)); + for (int i(0); inumberOfOperands(); i++) { + operandMatched[i] = false; + } + + // We call our recursive helper. + bool commutativelyIdentical = this->combinatoryCommutativeOperandsIdentity(e, operandMatched, leftToMatch); + + free(operandMatched); + return commutativelyIdentical; +} + bool Expression::isIdenticalTo(Expression * e) { if (e->type() != this->type() || e->numberOfOperands() != this->numberOfOperands()) { return false; } - for (int i=0; inumberOfOperands(); i++) { - if (!e->operand(i)->isIdenticalTo(this->operand(i))) { + if (this->isCommutative()) { + if (!this->commutativeOperandsIdentity(e)) { + return false; + } + } else { + if (!this->sequentialOperandsIdentity(e)) { return false; } } diff --git a/poincare/test/identity.cpp b/poincare/test/identity.cpp new file mode 100644 index 000000000..379d0f521 --- /dev/null +++ b/poincare/test/identity.cpp @@ -0,0 +1,36 @@ +#include +#include +#include "simplify_utils.h" + +QUIZ_CASE(poincare_identity_simple_term) { + assert(identical_to("1", "1")); + assert(!identical_to("1", "2")); + + assert(identical_to("a", "a")); + assert(!identical_to("a", "b")); + + assert(identical_to("1+2", "1+2")); + assert(!identical_to("1+2", "1+3")); + + assert(identical_to("1-2", "1-2")); + assert(!identical_to("1-2", "1-3")); + + assert(identical_to("1*2", "1*2")); + assert(!identical_to("1*2", "1*3")); + + assert(identical_to("1/2", "1/2")); + assert(!identical_to("1/2", "1/3")); + + assert(identical_to("1^2", "1^2")); + assert(!identical_to("1^2", "1^3")); + + assert(identical_to("cos(1)", "cos(1)")); + assert(!identical_to("cos(1)", "cos(2)")); +} + +QUIZ_CASE(poincare_identity_commutativity) { + assert(identical_to("1+2", "2+1")); + assert(identical_to("1*2", "2*1")); + assert(!identical_to("1-2", "2-1")); + assert(!identical_to("1/2", "2/1")); +}