diff --git a/poincare/Makefile b/poincare/Makefile index 6300d73fd..0170decbf 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -35,7 +35,9 @@ tests += $(addprefix poincare/test/,\ addition.cpp\ fraction.cpp\ integer.cpp\ + identity.cpp\ product.cpp\ + simplify_utils.cpp\ simplify_addition.cpp\ simplify_product.cpp\ trigo.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/binary_operation.cpp b/poincare/src/binary_operation.cpp index 76b948c01..91360c586 100644 --- a/poincare/src/binary_operation.cpp +++ b/poincare/src/binary_operation.cpp @@ -26,7 +26,8 @@ int BinaryOperation::numberOfOperands() { } Expression * BinaryOperation::operand(int i) { - assert(i>0 && i<=2); + assert(i >= 0); + assert(i < 2); return m_operands[i]; } diff --git a/poincare/src/commutative_operation.cpp b/poincare/src/commutative_operation.cpp index 514c8eb10..eb1917597 100644 --- a/poincare/src/commutative_operation.cpp +++ b/poincare/src/commutative_operation.cpp @@ -34,6 +34,7 @@ int CommutativeOperation::numberOfOperands() { } Expression * CommutativeOperation::operand(int i) { + assert(i >= 0); assert(i < m_numberOfOperands); return m_operands[i]; } diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index f5e90bcc6..be72325ed 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -34,23 +34,28 @@ Expression * Expression::simplify() { return this->clone(); } - /* We recursively simplify the children expressions. - * Note that we are sure to get the samne number of children as we had before - */ - Expression ** simplifiedOperands = (Expression**) malloc(this->numberOfOperands() * sizeof(Expression*)); - for (int i = 0; i < this->numberOfOperands(); i++) { - simplifiedOperands[i] = this->operand(i)->simplify(); - } - - /* Note that we don't need to clone the simplified children because they are - * already cloned before. */ - Expression * result = this->cloneWithDifferentOperands(simplifiedOperands, this->numberOfOperands(), false); - - // The table is no longer needed. - free(simplifiedOperands); + Expression * result = this->clone(); + Expression * tmp = nullptr; bool simplification_pass_was_useful = true; while (simplification_pass_was_useful) { + /* We recursively simplify the children expressions. + * Note that we are sure to get the samne number of children as we had before + */ + Expression ** simplifiedOperands = (Expression**) malloc(result->numberOfOperands() * sizeof(Expression*)); + for (int i = 0; i < result->numberOfOperands(); i++) { + simplifiedOperands[i] = result->operand(i)->simplify(); + } + + /* Note that we don't need to clone the simplified children because they are + * already cloned before. */ + tmp = result->cloneWithDifferentOperands(simplifiedOperands, result->numberOfOperands(), false); + delete result; + result = tmp; + + // The table is no longer needed. + free(simplifiedOperands); + simplification_pass_was_useful = false; 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/src/leaf_expression.cpp b/poincare/src/leaf_expression.cpp index 8d9a0eadc..a72884ac3 100644 --- a/poincare/src/leaf_expression.cpp +++ b/poincare/src/leaf_expression.cpp @@ -15,6 +15,6 @@ Expression * LeafExpression::operand(int i) { Expression * LeafExpression::cloneWithDifferentOperands(Expression** newOperands, int numberOfOperands, bool cloneOperands) { - assert(false); - return nullptr; + assert(numberOfOperands == 0); + return this->clone(); } diff --git a/poincare/src/simplify/expression_selector.cpp b/poincare/src/simplify/expression_selector.cpp index 635b091c0..36236dd1f 100644 --- a/poincare/src/simplify/expression_selector.cpp +++ b/poincare/src/simplify/expression_selector.cpp @@ -119,7 +119,7 @@ int ExpressionSelector::commutativeMatch(Expression * e, ExpressionMatch * match } bool hasWildcard = child(m_numberOfChildren-1)->m_match == ExpressionSelector::Match::Wildcard; - uint8_t * selectorMatched = (uint8_t *)malloc(e->numberOfOperands()); + uint8_t * selectorMatched = (uint8_t *)malloc(e->numberOfOperands()*sizeof(uint8_t)); /* Initialize the selectors matched to unmatched (0xff), here we assume that * we never have more than 255 direct children selector. */ diff --git a/poincare/src/simplify/rules.pr b/poincare/src/simplify/rules.pr index 9c0ac8825..c1423bc0e 100644 --- a/poincare/src/simplify/rules.pr +++ b/poincare/src/simplify/rules.pr @@ -3,3 +3,5 @@ Addition(Integer.a,Integer.b)->$AddIntegers(a,b); Addition(Integer.a,Integer.b,c*)->Addition($AddIntegers(a,b),c*); Product(Product(a*),b*)->Product(a*,b*); Product(Integer[0],a*)->Integer[0]; +Product(Integer.a,Integer.b)->$MultiplyIntegers(a,b); +Product(Integer.a,Integer.b,c*)->Product($MultiplyIntegers(a,b),c*); diff --git a/poincare/src/simplify/simplification_generator.cpp b/poincare/src/simplify/simplification_generator.cpp index 05df45b68..8d5d31382 100644 --- a/poincare/src/simplify/simplification_generator.cpp +++ b/poincare/src/simplify/simplification_generator.cpp @@ -8,8 +8,22 @@ Expression * SimplificationGenerator::AddIntegers(Expression ** parameters, int Integer * result = new Integer((native_int_t)0); for (int i=0; itype() == Expression::Type::Integer); - // FIXME: get rid of this operator overloading. - *result = *result + *(Integer *)parameters[i]; + *result = result->add(*(Integer *)parameters[i], false); + /* Note We have to delete the parameters as they have been cloned before and + * we are the last ones to use them here. */ + delete parameters[i]; + } + return result; +} + +Expression * SimplificationGenerator::MultiplyIntegers(Expression ** parameters, int numberOfParameters) { + Integer * result = new Integer((native_int_t)1); + for (int i=0; itype() == Expression::Type::Integer); + // FIXME: get rid of this operator overloading, there are to many stars. + *result = *result * *(Integer *)parameters[i]; + /* Note We have to delete the parameters as they have been cloned before and + * we are the last ones to use them here. */ delete parameters[i]; } return result; diff --git a/poincare/src/simplify/simplification_generator.h b/poincare/src/simplify/simplification_generator.h index 422f3d6d9..2055c846b 100644 --- a/poincare/src/simplify/simplification_generator.h +++ b/poincare/src/simplify/simplification_generator.h @@ -6,6 +6,7 @@ class SimplificationGenerator { public: static Expression * AddIntegers(Expression ** parameters, int numberOfParameters); + static Expression * MultiplyIntegers(Expression ** parameters, int numberOfParameters); }; #endif 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")); +} diff --git a/poincare/test/simplify_addition.cpp b/poincare/test/simplify_addition.cpp index 8e2b5e048..51300e033 100644 --- a/poincare/test/simplify_addition.cpp +++ b/poincare/test/simplify_addition.cpp @@ -1,53 +1,12 @@ -#include -#include #include - -#if POINCARE_TESTS_PRINT_EXPRESSIONS -#include "../src/expression_debug.h" -#include -using namespace std; -#endif - -void assert_simplifies_to(const char * input_string, const char * expected_string) { -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "---- Simplification Run ----" << endl; - cout << input_string << " -> " << expected_string << endl; -#endif - //Expression* tab[3] = {new Integer(1), new Integer(2), new Integer(3)}; - //Expression* input = new Addition(tab, 3, false); - Expression * input = Expression::parse(input_string); - assert(input != nullptr); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "Input = " << endl; - print_expression(input); -#endif - - Expression * simplified = input->simplify(); - assert(simplified != nullptr); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "Simplified = " << endl; - print_expression(simplified); -#endif - - Expression * expected = Expression::parse(expected_string); - assert(expected != nullptr); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "Expected = " << endl; - print_expression(expected); -#endif - - assert(simplified->isIdenticalTo(expected)); - delete expected; - if (simplified != input) { - delete simplified; - } - delete input; -} +#include +#include "simplify_utils.h" QUIZ_CASE(poincare_simplify_addition_integer) { - assert_simplifies_to("1", "1"); - assert_simplifies_to("1+2", "3"); - assert_simplifies_to("1+a", "1+a"); - assert_simplifies_to("1+2+3+4+5+6+7", "28"); - assert_simplifies_to("1+2+3+4+5+a+6+7", "28+a"); + assert(simplifies_to("1", "1")); + assert(simplifies_to("1+2", "3")); + assert(simplifies_to("1+a", "1+a")); + assert(simplifies_to("1+2+3+4+5+6+7", "28")); + assert(simplifies_to("1+2+3+4+5+a+6+7", "28+a")); + assert(simplifies_to("a*(0+0)", "0")); } diff --git a/poincare/test/simplify_product.cpp b/poincare/test/simplify_product.cpp index 952ffd5a6..dfb7479dd 100644 --- a/poincare/test/simplify_product.cpp +++ b/poincare/test/simplify_product.cpp @@ -1,16 +1,16 @@ -#include -#include #include +#include +#include "simplify_utils.h" -QUIZ_CASE(poincare_simplify) { - { - Expression * e = Expression::parse("3*0"); - Expression * e2 = e->simplify(); - assert(e2->type() == Expression::Type::Integer); - } - { - Expression * e = Expression::parse("0*foo"); - Expression * e2 = e->simplify(); - assert(e2->type() == Expression::Type::Integer); - } +QUIZ_CASE(poincare_simplify_product_by_zero) { + assert(simplifies_to("3*0", "0")); + assert(simplifies_to("foo*0", "0")); + + assert(simplifies_to("0*3", "0")); + assert(simplifies_to("0*foo", "0")); + + assert(simplifies_to("3*5", "15")); + assert(simplifies_to("8*6", "48")); + + assert(simplifies_to("3*(5+4)", "27")); } diff --git a/poincare/test/simplify_utils.cpp b/poincare/test/simplify_utils.cpp new file mode 100644 index 000000000..7beb120f0 --- /dev/null +++ b/poincare/test/simplify_utils.cpp @@ -0,0 +1,66 @@ +#include +#include + +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif + +bool simplifies_to(const char * input_string, const char * expected_string) { +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- Simplification Run ----" << endl; + cout << input_string << " -> " << expected_string << endl; +#endif + Expression * input = Expression::parse(input_string); + assert(input != nullptr); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "Input = " << endl; + print_expression(input); +#endif + + Expression * simplified = input->simplify(); + assert(simplified != nullptr); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "Simplified = " << endl; + print_expression(simplified); +#endif + + Expression * expected = Expression::parse(expected_string); + assert(expected != nullptr); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "Expected = " << endl; + print_expression(expected); +#endif + + bool isIdentical = simplified->isIdenticalTo(expected); + + delete expected; + delete simplified; + delete input; + + return isIdentical; +} + +bool identical_to(const char * input_string, const char * expected_string) { + Expression * input = Expression::parse(input_string); + assert(input != nullptr); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "Input = " << endl; + print_expression(input); +#endif + + Expression * expected = Expression::parse(expected_string); + assert(expected != nullptr); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "Expected = " << endl; + print_expression(expected); +#endif + + bool isIdentical = input->isIdenticalTo(expected); + + delete expected; + delete input; + + return isIdentical; +} diff --git a/poincare/test/simplify_utils.h b/poincare/test/simplify_utils.h new file mode 100644 index 000000000..81598564d --- /dev/null +++ b/poincare/test/simplify_utils.h @@ -0,0 +1,10 @@ +#ifndef POINCARE_TEST_SIMPLIFY_UTILS_H +#define POINCARE_TEST_SIMPLIFY_UTILS_H + +/* Tests that the first expression simplifies to the second. */ +bool simplifies_to(const char * input_string, const char * expected_string); + +/* Tests that the first expression is identical to the second. */ +bool identical_to(const char * input_string, const char * expected_string); + +#endif // POINCARE_TEST_SIMPLIFY_UTILS_H