From 43aadf41fe9d4ca3cf8b469c7358e30071de5e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 10:28:38 +0100 Subject: [PATCH 01/77] [poincare] In store, do not evaluate twice stored value: evaluate only once in global context Change-Id: I7e0ec3122e4d84500ea4f3e157a8398d0ed09d50 --- poincare/src/global_context.cpp | 8 ++++---- poincare/src/store.cpp | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/poincare/src/global_context.cpp b/poincare/src/global_context.cpp index 4400804e9..f53998574 100644 --- a/poincare/src/global_context.cpp +++ b/poincare/src/global_context.cpp @@ -77,12 +77,12 @@ void GlobalContext::setExpressionForSymbolName(const Expression * expression, co if (symbol->isMatrixSymbol()) { int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; assert(indexMatrix >= 0 && indexMatrix < k_maxNumberOfMatrixExpressions); + Expression * evaluation = expression ? expression->evaluate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate M1+2->M1) if (m_matrixExpressions[indexMatrix] != nullptr) { delete m_matrixExpressions[indexMatrix]; m_matrixExpressions[indexMatrix] = nullptr; } - if (expression != nullptr) { - Expression * evaluation = expression->evaluate(context); + if (evaluation != nullptr) { if (evaluation->type() == Expression::Type::Complex) { m_matrixExpressions[indexMatrix] = new Matrix(&evaluation, 1, 1, false); } else { @@ -95,14 +95,14 @@ void GlobalContext::setExpressionForSymbolName(const Expression * expression, co if (index < 0 || index >= k_maxNumberOfScalarExpressions) { return; } + Expression * evaluation = expression ? expression->evaluate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate A+2->A) if (m_expressions[index] != nullptr) { delete m_expressions[index]; m_expressions[index] = nullptr; } - if (expression == nullptr) { + if (evaluation == nullptr) { return; } - Expression * evaluation = expression->evaluate(context); if (evaluation->type() == Expression::Type::Complex) { m_expressions[index] = static_cast *>(evaluation); } else { diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index ad930fac3..e4616d889 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -38,9 +38,7 @@ ExpressionLayout * Store::privateCreateLayout(FloatDisplayMode floatDisplayMode, template Expression * Store::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * valueEvaluation = value()->evaluate(context, angleUnit); - context.setExpressionForSymbolName(valueEvaluation, symbol(), context); - delete valueEvaluation; + context.setExpressionForSymbolName(value(), symbol(), context); if (context.expressionForSymbol(symbol()) != nullptr) { return context.expressionForSymbol(symbol())->evaluate(context, angleUnit); } From 091a0ff10ee3519a86441a4140da8eedd0940c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 11:07:47 +0100 Subject: [PATCH 02/77] [poincare] Fix bug: print 0 in polar mode should print 0 and not undef Change-Id: Ic38168a2c80bc97fa539963e6c6da2f408ec5659 --- poincare/src/complex.cpp | 2 +- poincare/test/complex.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index cdfa5eba6..1026c26a9 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -442,7 +442,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl char bufferSuperscript[k_maxFloatBufferLength+2]; int numberOfCharInSuperscript = 0; - if (std::isnan(r()) || std::isnan(th())) { + if (std::isnan(r()) || (std::isnan(th()) && r() != 0)) { numberOfCharInBase = convertFloatToText(NAN, bufferBase, k_maxComplexBufferLength, k_numberOfSignificantDigits, floatDisplayMode); return new StringLayout(bufferBase, numberOfCharInBase); } diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index d08b3ab16..ced07e9f1 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -91,6 +91,12 @@ QUIZ_CASE(poincare_complex_cartesian_to_text) { assert_cartesian_complex_converts_to(-1.3f, -2.444f, "-1.3-2.444*i", Decimal, Cartesian); assert_cartesian_complex_converts_to(64078208.0, 119229408.0, "6.407821E7+1.192294E8*i", Decimal, Cartesian); assert_cartesian_complex_converts_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", Decimal, Polar); + assert_cartesian_complex_converts_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", Decimal, Polar); + assert_cartesian_complex_converts_to(INFINITY, 119229408.0f, "inf", Decimal, Polar); + assert_cartesian_complex_converts_to(0.0f, 0.0f, "0", Decimal, Polar); + assert_cartesian_complex_converts_to(NAN, 0.0f, "undef", Decimal, Polar); + assert_cartesian_complex_converts_to(0.0f, NAN, "undef", Decimal, Polar); + assert_cartesian_complex_converts_to(NAN, NAN, "undef", Decimal, Polar); } QUIZ_CASE(poincare_complex_evaluate) { From b126c1927b118c9bd027c7e8868a04b92f7f184a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 11:59:12 +0100 Subject: [PATCH 03/77] [poincare] Modify power rule: 0^0 = undef and x^0 = 1 Change-Id: I5e0d04c55d730a04c2a671465a49c592f0f1819a --- poincare/src/power.cpp | 7 +++++++ poincare/test/simplify_easy.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index ecbae6c98..229398a47 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -192,6 +192,13 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { const Rational * b = static_cast(operand(1)); // x^0 if (b->isZero()) { + // 0^0 = undef + if (operand(0)->type() == Type::Rational && static_cast(operand(0))->isZero()) { + return replaceWith(new Undefined(), true); + } + /* Warning: in all other case but 0^0, we replace x^0 by one. This is + * almost always true except when x = 0. However, not substituting x^0 by + * one would prevent from simplifying many expressions like x/x->1. */ return replaceWith(new Rational(1), true); } // x^1 diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 3cd135ba1..274173870 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -204,7 +204,7 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("1^x", "1"); assert_parsed_expression_simplify_to("x^1", "x"); assert_parsed_expression_simplify_to("0^3", "0"); - assert_parsed_expression_simplify_to("0^0", "1"); + assert_parsed_expression_simplify_to("0^0", "undef"); assert_parsed_expression_simplify_to("0^(-3)", "undef"); assert_parsed_expression_simplify_to("0*x+B", "B"); assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); @@ -498,6 +498,11 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); assert_parsed_expression_simplify_to("999^999", "999^999"); assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); + assert_parsed_expression_simplify_to("0^0", "undef"); + assert_parsed_expression_simplify_to("x^0", "1"); + assert_parsed_expression_simplify_to("P^0", "1"); + assert_parsed_expression_simplify_to("A^0", "1"); + assert_parsed_expression_simplify_to("(-3)^0", "1"); /* This does not work but should not as it is above k_primorial32 = 1*3*5*7*11*... (product of first 32 primes. */ //assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "123456789123456789"); From ff0a72177c9999c224d05a701c7409c6397ea650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 13:57:02 +0100 Subject: [PATCH 04/77] [poincare] Fix error in write in buffer: conjugate(2)->conj(2) Change-Id: I68056b192afc76f50117fd4157d85d5f9fa029db --- poincare/include/poincare/conjugate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/include/poincare/conjugate.h b/poincare/include/poincare/conjugate.h index 9eb33eaa6..3bb1754d9 100644 --- a/poincare/include/poincare/conjugate.h +++ b/poincare/include/poincare/conjugate.h @@ -16,7 +16,7 @@ private: /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override { - return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "conjugate"); + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "conj"); } /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; From a536ef58cf869bcca64ea12cb6efc28e2f05d1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 14:18:51 +0100 Subject: [PATCH 05/77] [poincare] Fix writeTextInBuffer of Power and Division: add parenthesis to x^(2/3) and x^y^z Change-Id: I61bed286260a21b42712eff04d46fc664724c203 --- poincare/include/poincare/division.h | 2 +- poincare/include/poincare/power.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poincare/include/poincare/division.h b/poincare/include/poincare/division.h index d30fe3bd3..631a2e094 100644 --- a/poincare/include/poincare/division.h +++ b/poincare/include/poincare/division.h @@ -20,7 +20,7 @@ private: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override { return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, "/", [](const Expression * e) { - return e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite; + return e->type() == Type::Division || e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite || (e->type() == Type::Rational && !static_cast(e)->denominator().isOne()); }); } /* Simplification */ diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index ca2e54e12..e43cad633 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -26,7 +26,7 @@ private: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override { return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, name(), [](const Expression * e) { - return e->type() == Type::Division || e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite; + return e->type() == Type::Power || e->type() == Type::Division || e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite || (e->type() == Type::Rational && !static_cast(e)->denominator().isOne()); }); } static const char * name() { return "^"; } From 7ae6241acd83a26815b48ebb30bfa7dc45477b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 14:38:08 +0100 Subject: [PATCH 06/77] [poincare] Fix all layouts: redefine baseline Change-Id: Iff04bf9bee946050721fc095d1617586dc251d6b --- poincare/src/layout/fraction_layout.cpp | 3 +-- poincare/src/layout/grid_layout.cpp | 2 +- poincare/src/layout/product_layout.cpp | 6 +++--- poincare/src/layout/sequence_layout.cpp | 8 ++++---- poincare/src/layout/string_layout.cpp | 4 ++-- poincare/src/layout/sum_layout.cpp | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/poincare/src/layout/fraction_layout.cpp b/poincare/src/layout/fraction_layout.cpp index 44b77435e..e71a263db 100644 --- a/poincare/src/layout/fraction_layout.cpp +++ b/poincare/src/layout/fraction_layout.cpp @@ -9,8 +9,7 @@ ExpressionLayout(), m_numerator_layout(numerator_layout), m_denominator_layout(d m_numerator_layout->setParent(this); m_denominator_layout->setParent(this); m_baseline = m_numerator_layout->size().height() - + k_fractionLineMargin + k_fractionLineHeight - + KDText::charSize().height()/2; + + k_fractionLineMargin + k_fractionLineHeight; } FractionLayout::~FractionLayout() { diff --git a/poincare/src/layout/grid_layout.cpp b/poincare/src/layout/grid_layout.cpp index b40f7adc5..495e4e9c7 100644 --- a/poincare/src/layout/grid_layout.cpp +++ b/poincare/src/layout/grid_layout.cpp @@ -16,7 +16,7 @@ GridLayout::GridLayout(ExpressionLayout ** entryLayouts, int numberOfRows, int n m_entryLayouts[i] = entryLayouts[i]; m_entryLayouts[i]->setParent(this); } - m_baseline = height()/2 + KDText::charSize().height()/2; + m_baseline = (height()+1)/2; } GridLayout::~GridLayout() { diff --git a/poincare/src/layout/product_layout.cpp b/poincare/src/layout/product_layout.cpp index 4699da3c5..cb573cf4b 100644 --- a/poincare/src/layout/product_layout.cpp +++ b/poincare/src/layout/product_layout.cpp @@ -8,13 +8,13 @@ void ProductLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDSize upperBoundSize = m_upperBoundLayout->size(); KDSize lowerBoundSize = m_lowerBoundLayout->size(); ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2), - p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight), + p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-(k_symbolHeight+1)/2), k_lineThickness, k_symbolHeight), expressionColor); ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2), - p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight), + p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-(k_symbolHeight+1)/2), k_symbolWidth, k_lineThickness), expressionColor); ctx->fillRect(KDRect(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2)+k_symbolWidth, - p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight), + p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-(k_symbolHeight+1)/2), k_lineThickness, k_symbolHeight), expressionColor); } diff --git a/poincare/src/layout/sequence_layout.cpp b/poincare/src/layout/sequence_layout.cpp index 927d0ee08..52ed5dc95 100644 --- a/poincare/src/layout/sequence_layout.cpp +++ b/poincare/src/layout/sequence_layout.cpp @@ -13,7 +13,7 @@ SequenceLayout::SequenceLayout(ExpressionLayout * lowerBoundLayout, ExpressionLa m_lowerBoundLayout->setParent(this); m_upperBoundLayout->setParent(this); m_argumentLayout->setParent(this); - m_baseline = max(m_upperBoundLayout->size().height()+k_boundHeightMargin+k_symbolHeight, m_argumentLayout->baseline()); + m_baseline = max(m_upperBoundLayout->size().height()+k_boundHeightMargin+(k_symbolHeight+1)/2, m_argumentLayout->baseline()); } SequenceLayout::~SequenceLayout() { @@ -28,7 +28,7 @@ KDSize SequenceLayout::computeSize() { KDSize upperBoundSize = m_upperBoundLayout->size(); return KDSize( max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin+argumentSize.width(), - m_baseline + max(k_boundHeightMargin+lowerBoundSize.height(), argumentSize.height() - m_argumentLayout->baseline()) + m_baseline + max(k_symbolHeight/2+k_boundHeightMargin+lowerBoundSize.height(), argumentSize.height() - m_argumentLayout->baseline()) ); } @@ -52,10 +52,10 @@ KDPoint SequenceLayout::positionOfChild(ExpressionLayout * child) { KDCoordinate y = 0; if (child == m_lowerBoundLayout) { x = max(max(0, (k_symbolWidth-lowerBoundSize.width())/2), (upperBoundSize.width()-lowerBoundSize.width())/2); - y = m_baseline + k_boundHeightMargin; + y = m_baseline + k_symbolHeight/2 + k_boundHeightMargin; } else if (child == m_upperBoundLayout) { x = max(max(0, (k_symbolWidth-upperBoundSize.width())/2), (lowerBoundSize.width()-upperBoundSize.width())/2); - y = m_baseline - k_symbolHeight- k_boundHeightMargin-upperBoundSize.height(); + y = m_baseline - (k_symbolHeight+1)/2- k_boundHeightMargin-upperBoundSize.height(); } else if (child == m_argumentLayout) { x = max(max(k_symbolWidth, lowerBoundSize.width()), upperBoundSize.width())+k_argumentWidthMargin; y = m_baseline - m_argumentLayout->baseline(); diff --git a/poincare/src/layout/string_layout.cpp b/poincare/src/layout/string_layout.cpp index 4cb1de862..62bbbb68c 100644 --- a/poincare/src/layout/string_layout.cpp +++ b/poincare/src/layout/string_layout.cpp @@ -11,8 +11,8 @@ StringLayout::StringLayout(const char * string, size_t length, KDText::FontSize m_string = new char[length+1]; memcpy(m_string, string, length); m_string[length] = 0; - // Height of the font. - m_baseline = KDText::charSize(m_fontSize).height(); + // Half height of the font. + m_baseline = (KDText::charSize(m_fontSize).height()+1)/2; } StringLayout::~StringLayout() { diff --git a/poincare/src/layout/sum_layout.cpp b/poincare/src/layout/sum_layout.cpp index 17bb0a995..95c376bee 100644 --- a/poincare/src/layout/sum_layout.cpp +++ b/poincare/src/layout/sum_layout.cpp @@ -27,7 +27,7 @@ void SumLayout::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDCo KDSize lowerBoundSize = m_lowerBoundLayout->size(); KDColor workingBuffer[k_symbolWidth*k_symbolHeight]; KDRect symbolFrame(p.x() + max(max(0, (upperBoundSize.width()-k_symbolWidth)/2), (lowerBoundSize.width()-k_symbolWidth)/2), - p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-k_symbolHeight), + p.y() + max(upperBoundSize.height()+k_boundHeightMargin, m_argumentLayout->baseline()-(k_symbolHeight+1)/2), k_symbolWidth, k_symbolHeight); ctx->blendRectWithMask(symbolFrame, expressionColor, (const uint8_t *)symbolPixel, (KDColor *)workingBuffer); } From 8f4c49d8c14b3c74d836e567b53a18e46e7cbfa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 14:38:53 +0100 Subject: [PATCH 07/77] [apps] In calculation, fix the almostEqual aligmnent Change-Id: I26eca4c9c2b5a3b1ad05567e8d5daf76922f2cb7 --- apps/calculation/output_expressions_view.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/calculation/output_expressions_view.cpp b/apps/calculation/output_expressions_view.cpp index e24bc7bf3..e7fd721af 100644 --- a/apps/calculation/output_expressions_view.cpp +++ b/apps/calculation/output_expressions_view.cpp @@ -57,8 +57,11 @@ KDSize OutputExpressionsView::minimalSizeForOptimalDisplay() const { return approximateExpressionSize; } KDSize exactExpressionSize = m_exactExpressionView.minimalSizeForOptimalDisplay(); + KDCoordinate exactBaseline = m_exactExpressionView.expressionLayout()->baseline(); + KDCoordinate approximateBaseline = m_approximateExpressionView.expressionLayout()->baseline(); + KDCoordinate height = max(exactBaseline, approximateBaseline) + max(exactExpressionSize.height()-exactBaseline, approximateExpressionSize.height()-approximateBaseline); KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); - return KDSize(exactExpressionSize.width()+approximateSignSize.width()+approximateExpressionSize.width()+2*k_digitHorizontalMargin, exactExpressionSize.height() > approximateExpressionSize.height() ? exactExpressionSize.height() : approximateExpressionSize.height()); + return KDSize(exactExpressionSize.width()+approximateSignSize.width()+approximateExpressionSize.width()+2*k_digitHorizontalMargin, height); } void OutputExpressionsView::didBecomeFirstResponder() { @@ -115,11 +118,14 @@ void OutputExpressionsView::layoutSubviews() { m_approximateExpressionView.setFrame(KDRect(0, 0, approximateExpressionSize.width(), height)); return; } + KDCoordinate exactBaseline = m_exactExpressionView.expressionLayout()->baseline(); + KDCoordinate approximateBaseline = m_approximateExpressionView.expressionLayout()->baseline(); + KDCoordinate baseline = max(exactBaseline, approximateBaseline); KDSize exactExpressionSize = m_exactExpressionView.minimalSizeForOptimalDisplay(); - m_exactExpressionView.setFrame(KDRect(0, 0, exactExpressionSize.width(), height)); KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); - m_approximateSign.setFrame(KDRect(k_digitHorizontalMargin+exactExpressionSize.width(), 0, approximateSignSize.width(), height)); - m_approximateExpressionView.setFrame(KDRect(2*k_digitHorizontalMargin+exactExpressionSize.width()+approximateSignSize.width(), 0, approximateExpressionSize.width(), height)); + m_exactExpressionView.setFrame(KDRect(0, baseline-exactBaseline, exactExpressionSize)); + m_approximateExpressionView.setFrame(KDRect(2*k_digitHorizontalMargin+exactExpressionSize.width()+approximateSignSize.width(), baseline-approximateBaseline, approximateExpressionSize)); + m_approximateSign.setFrame(KDRect(k_digitHorizontalMargin+exactExpressionSize.width(), baseline-approximateSignSize.height()/2, approximateSignSize)); } } From f5983c97e6a8fe3916735112cdbc381f2c2acbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 16:46:49 +0100 Subject: [PATCH 08/77] [poincare] Fix confusion between double and float! Change-Id: I57a12f85f3e24a80c09de48a3f33e4e81593fc85 --- poincare/include/poincare/confidence_interval.h | 2 +- poincare/include/poincare/prediction_interval.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poincare/include/poincare/confidence_interval.h b/poincare/include/poincare/confidence_interval.h index 9f953e0af..72c26f5fd 100644 --- a/poincare/include/poincare/confidence_interval.h +++ b/poincare/include/poincare/confidence_interval.h @@ -24,7 +24,7 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; diff --git a/poincare/include/poincare/prediction_interval.h b/poincare/include/poincare/prediction_interval.h index bef5d3963..8018a17b9 100644 --- a/poincare/include/poincare/prediction_interval.h +++ b/poincare/include/poincare/prediction_interval.h @@ -24,7 +24,7 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; From 156e08dc12af48be6b15b532f72e5a90b4b07601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 16:47:39 +0100 Subject: [PATCH 09/77] [poincare] Fix unitary tests (part I) Change-Id: Ib971a3562a7dbf98dda3a02ad1b7064ad7b57fdf --- poincare/Makefile | 9 ++- poincare/test/complex.cpp | 90 +++++++++++++------------- poincare/test/function.cpp | 128 ++++++++++++++++++++++++++++++------- poincare/test/helper.cpp | 15 +++-- poincare/test/helper.h | 4 +- 5 files changed, 165 insertions(+), 81 deletions(-) diff --git a/poincare/Makefile b/poincare/Makefile index c62d58696..52e0ef4e3 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -101,19 +101,18 @@ objs += $(addprefix poincare/src/layout/,\ ) tests += $(addprefix poincare/test/,\ + addition.cpp\ arithmetic.cpp\ + complex.cpp\ convert_expression_to_text.cpp\ + division.cpp\ + function.cpp\ helper.cpp\ integer.cpp\ simplify_easy.cpp\ ) testsi += $(addprefix poincare/test/,\ - addition.cpp\ - arithmetic.cpp\ - complex.cpp\ - division.cpp\ - function.cpp\ helper.cpp\ integer.cpp\ matrix.cpp\ diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index ced07e9f1..ac4a6e63f 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -7,13 +7,13 @@ #include using namespace Poincare; -constexpr Expression::FloatDisplayMode Decimal = Expression::FloatDisplayMode::Decimal; -constexpr Expression::FloatDisplayMode Scientific = Expression::FloatDisplayMode::Scientific; +constexpr Expression::FloatDisplayMode DecimalDisplay = Expression::FloatDisplayMode::Decimal; +constexpr Expression::FloatDisplayMode ScientificDisplay = Expression::FloatDisplayMode::Scientific; constexpr Expression::ComplexFormat Cartesian = Expression::ComplexFormat::Cartesian; constexpr Expression::ComplexFormat Polar = Expression::ComplexFormat::Polar; template -void assert_cartesian_complex_converts_to(T a, T b, const char * result, Expression::FloatDisplayMode mode = Scientific, Expression::ComplexFormat format = Cartesian, int significantDigits = 7, int bufferSize = 13+13+7+1) { +void assert_cartesian_complex_prints_to(T a, T b, const char * result, Expression::FloatDisplayMode mode = ScientificDisplay, Expression::ComplexFormat format = Cartesian, int significantDigits = 7, int bufferSize = 13+13+7+1) { quiz_print(result); int tagSize = 8; @@ -56,62 +56,64 @@ void assert_cartesian_complex_converts_to(T a, T b, const char * result, Express QUIZ_CASE(poincare_complex_to_text) { /* We expect 7 significative numbers but do not display 0 */ - assert_cartesian_complex_converts_to(123.456f, 0.0f, "1.23456E2"); - assert_cartesian_complex_converts_to(1.234567891011, 0.0, "1.234568"); - assert_cartesian_complex_converts_to(2.0f, 0.0f, "2"); - assert_cartesian_complex_converts_to(123456789.0, 0.0, "1.234568E8"); - assert_cartesian_complex_converts_to(0.00000123456789f, 0.0f, "1.234568E-6"); - assert_cartesian_complex_converts_to(0.99, 0.0, "9.9E-1"); - assert_cartesian_complex_converts_to(-123.456789f, 0.0f, "-1.234568E2"); - assert_cartesian_complex_converts_to(-0.000123456789, 0.0, "-1.234568E-4"); - assert_cartesian_complex_converts_to(0.0f, 0.0f, "0"); - assert_cartesian_complex_converts_to(10000000000000000000000000000.0, 0.0, "1E28"); + assert_cartesian_complex_prints_to(123.456f, 0.0f, "1.23456E2"); + assert_cartesian_complex_prints_to(1.234567891011, 0.0, "1.234568"); + assert_cartesian_complex_prints_to(2.0f, 0.0f, "2"); + assert_cartesian_complex_prints_to(123456789.0, 0.0, "1.234568E8"); + assert_cartesian_complex_prints_to(0.00000123456789f, 0.0f, "1.234568E-6"); + assert_cartesian_complex_prints_to(0.99, 0.0, "9.9E-1"); + assert_cartesian_complex_prints_to(-123.456789f, 0.0f, "-1.234568E2"); + assert_cartesian_complex_prints_to(-0.000123456789, 0.0, "-1.234568E-4"); + assert_cartesian_complex_prints_to(0.0f, 0.0f, "0"); + assert_cartesian_complex_prints_to(10000000000000000000000000000.0, 0.0, "1E28"); /* Converting 10000000000000000000000000000.0f into a decimal display would * overflow the number of significant digits set to 7. When this is the case, the * display mode is automatically set to scientific. */ - assert_cartesian_complex_converts_to(10000000000000000000000000000.0, 0.0, "1E28", Decimal); - assert_cartesian_complex_converts_to(1000000.0, 0.0, "1000000", Decimal); - assert_cartesian_complex_converts_to(10000000.0f, 0.0f, "1E7", Decimal); - assert_cartesian_complex_converts_to(0.000001, 0.0, "0.000001", Decimal); + assert_cartesian_complex_prints_to(10000000000000000000000000000.0, 0.0, "1E28", DecimalDisplay); + assert_cartesian_complex_prints_to(1000000.0, 0.0, "1000000", DecimalDisplay); + assert_cartesian_complex_prints_to(10000000.0f, 0.0f, "1E7", DecimalDisplay); + assert_cartesian_complex_prints_to(0.000001, 0.0, "0.000001", DecimalDisplay); /* Converting 0.00000001f into a decimal display would also overflow the * number of significant digits set to 7. */ - assert_cartesian_complex_converts_to(0.0000001f, 0.0f, "1E-7", Decimal); - assert_cartesian_complex_converts_to(-0.000000000000000000000000000000009090018, 0.0, "-9.090018E-33"); - assert_cartesian_complex_converts_to(123.421f, 0.0f, "123.4", Decimal, Cartesian, 4, 6); - assert_cartesian_complex_converts_to(123.421, 0.0, "1.2E2", Decimal, Cartesian, 5, 6); - assert_cartesian_complex_converts_to(9.999999f, 0.0f, "10", Decimal, Cartesian, 6); - assert_cartesian_complex_converts_to(-9.99999904, 0.0, "-10", Decimal, Cartesian, 6); + assert_cartesian_complex_prints_to(0.0000001f, 0.0f, "1E-7", DecimalDisplay); + assert_cartesian_complex_prints_to(-0.000000000000000000000000000000009090018, 0.0, "-9.090018E-33"); + assert_cartesian_complex_prints_to(123.421f, 0.0f, "123.4", DecimalDisplay, Cartesian, 4, 6); + assert_cartesian_complex_prints_to(123.421, 0.0, "1.2E2", DecimalDisplay, Cartesian, 5, 6); + assert_cartesian_complex_prints_to(9.999999f, 0.0f, "10", DecimalDisplay, Cartesian, 6); + assert_cartesian_complex_prints_to(-9.99999904, 0.0, "-10", DecimalDisplay, Cartesian, 6); } QUIZ_CASE(poincare_complex_cartesian_to_text) { - assert_cartesian_complex_converts_to(1.0, 2.0, "1+2*i", Decimal, Cartesian); - assert_cartesian_complex_converts_to(1.0f, 2.0f, "2.236068*e^(1.107149*i)", Decimal, Polar); - assert_cartesian_complex_converts_to(-1.3f, 2.444f, "-1.3+2.444*i", Decimal, Cartesian); - assert_cartesian_complex_converts_to(-1.3, 2.444, "2.768237*e^(2.059649*i)", Decimal, Polar); - assert_cartesian_complex_converts_to(-1.3f, -2.444f, "-1.3-2.444*i", Decimal, Cartesian); - assert_cartesian_complex_converts_to(64078208.0, 119229408.0, "6.407821E7+1.192294E8*i", Decimal, Cartesian); - assert_cartesian_complex_converts_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", Decimal, Polar); - assert_cartesian_complex_converts_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", Decimal, Polar); - assert_cartesian_complex_converts_to(INFINITY, 119229408.0f, "inf", Decimal, Polar); - assert_cartesian_complex_converts_to(0.0f, 0.0f, "0", Decimal, Polar); - assert_cartesian_complex_converts_to(NAN, 0.0f, "undef", Decimal, Polar); - assert_cartesian_complex_converts_to(0.0f, NAN, "undef", Decimal, Polar); - assert_cartesian_complex_converts_to(NAN, NAN, "undef", Decimal, Polar); + assert_cartesian_complex_prints_to(1.0, 2.0, "1+2*i", DecimalDisplay, Cartesian); + assert_cartesian_complex_prints_to(1.0f, 2.0f, "2.236068*e^(1.107149*i)", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(-1.3f, 2.444f, "-1.3+2.444*i", DecimalDisplay, Cartesian); + assert_cartesian_complex_prints_to(-1.3, 2.444, "2.768237*e^(2.059649*i)", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(-1.3f, -2.444f, "-1.3-2.444*i", DecimalDisplay, Cartesian); + assert_cartesian_complex_prints_to(64078208.0, 119229408.0, "6.407821E7+1.192294E8*i", DecimalDisplay, Cartesian); + assert_cartesian_complex_prints_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(INFINITY, 119229408.0f, "inf", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(0.0f, 0.0f, "0", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(NAN, 0.0f, "undef", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(0.0f, NAN, "undef", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(NAN, NAN, "undef", DecimalDisplay, Polar); } QUIZ_CASE(poincare_complex_evaluate) { GlobalContext globalContext; Expression * a = new Complex(Complex::Float(123.456f)); - Evaluation * m = a->evaluate(globalContext); - assert(std::fabs(m->complexOperand(0)->a() - 123.456) < 0.00001); - assert(m->complexOperand(0)->b() == 0.0); - assert(m->numberOfOperands() == 1); + Expression * m = a->evaluate(globalContext); + assert(m->type() == Expression::Type::Complex); + Complex * mc = static_cast *>(m); + assert(std::fabs(mc->a() - 123.456) < 0.00001); + assert(mc->b() == 0.0); delete m; - Evaluation * n = a->evaluate(globalContext); - assert(n->complexOperand(0)->a() == 123.456f); - assert(n->complexOperand(0)->b() == 0.0f); - assert(n->numberOfOperands() == 1); + Expression * n = a->evaluate(globalContext); + assert(n->type() == Expression::Type::Complex); + Complex * nc = static_cast *>(n); + assert(nc->a() == 123.456f); + assert(nc->b() == 0.0f); delete n; delete a; diff --git a/poincare/test/function.cpp b/poincare/test/function.cpp index c7f58f6e3..7bd11dabb 100644 --- a/poincare/test/function.cpp +++ b/poincare/test/function.cpp @@ -50,134 +50,216 @@ QUIZ_CASE(poincare_parse_function) { QUIZ_CASE(poincare_function_evaluate) { - Complex a0[1] = {Complex::Float(1.0)}; + Complex a0[1] = {Complex::Float(1.0)}; assert_parsed_expression_evaluates_to("abs(-1)", a0); + Complex ad0[1] = {Complex::Float(1.0)}; + assert_parsed_expression_evaluates_to("abs(-1)", ad0); Complex a1[1] = {Complex::Float(std::sqrt(3.0f*3.0f+2.0f*2.0f))}; assert_parsed_expression_evaluates_to("abs(3+2I)", a1); + Complex ad1[1] = {Complex::Float(std::sqrt(3.0*3.0+2.0*2.0))}; + assert_parsed_expression_evaluates_to("abs(3+2I)", ad1); - Complex a2[4] = {Complex::Float(1.0), Complex::Float(2.0), Complex::Float(3.0), Complex::Float(4.0)}; + Complex a2[4] = {Complex::Float(1.0), Complex::Float(2.0), Complex::Float(3.0), Complex::Float(4.0)}; assert_parsed_expression_evaluates_to("abs([[1,-2][3,-4]])", a2, 2, 2); + Complex ad2[4] = {Complex::Float(1.0), Complex::Float(2.0), Complex::Float(3.0), Complex::Float(4.0)}; + assert_parsed_expression_evaluates_to("abs([[1,-2][3,-4]])", ad2, 2, 2); Complex a3[4] = {Complex::Float(std::sqrt(3.0f*3.0f+2.0f*2.0f)), Complex::Float(std::sqrt(3.0f*3.0f+4.0f*4.0f)), Complex::Float(std::sqrt(5.0f*5.0f+2.0f*2.0f)), Complex::Float(std::sqrt(3.0f*3.0f+2.0f*2.0f))}; assert_parsed_expression_evaluates_to("abs([[3+2I,3+4I][5+2I,3+2I]])", a3, 2, 2); + Complex ad3[4] = {Complex::Float(std::sqrt(3.0f*3.0f+2.0f*2.0f)), Complex::Float(std::sqrt(3.0f*3.0f+4.0f*4.0f)), Complex::Float(std::sqrt(5.0f*5.0f+2.0f*2.0f)), Complex::Float(std::sqrt(3.0f*3.0f+2.0f*2.0f))}; + assert_parsed_expression_evaluates_to("abs([[3+2I,3+4I][5+2I,3+2I]])", ad3, 2, 2); - Complex b[1] = {Complex::Float(210.0)}; + Complex b[1] = {Complex::Float(210.0)}; assert_parsed_expression_evaluates_to("binomial(10, 4)", b); + Complex bd[1] = {Complex::Float(210.0)}; + assert_parsed_expression_evaluates_to("binomial(10, 4)", bd); Complex c[1] = {Complex::Float(1.0f)}; assert_parsed_expression_evaluates_to("ceil(0.2)", c); + Complex cd[1] = {Complex::Float(1.0f)}; + assert_parsed_expression_evaluates_to("ceil(0.2)", cd); - Complex d[1] = {Complex::Float(2.0)}; + Complex d[1] = {Complex::Float(2.0)}; assert_parsed_expression_evaluates_to("diff(2*x, 2)", d); + Complex dd[1] = {Complex::Float(2.0)}; + assert_parsed_expression_evaluates_to("diff(2*x, 2)", dd); #if MATRICES_ARE_DEFINED Complex e[1] = {Complex::Float(126.0f)}; assert_parsed_expression_evaluates_to("det([[1,23,3][4,5,6][7,8,9]])", e); + Complex ed[1] = {Complex::Float(126.0f)}; + assert_parsed_expression_evaluates_to("det([[1,23,3][4,5,6][7,8,9]])", ed); #endif - Complex f[1] = {Complex::Float(2.0)}; + Complex f[1] = {Complex::Float(2.0)}; assert_parsed_expression_evaluates_to("floor(2.3)", f); + Complex fd[1] = {Complex::Float(2.0)}; + assert_parsed_expression_evaluates_to("floor(2.3)", fd); Complex g[1] = {Complex::Float(0.3f)}; assert_parsed_expression_evaluates_to("frac(2.3)", g); + Complex gd[1] = {Complex::Float(0.3f)}; + assert_parsed_expression_evaluates_to("frac(2.3)", gd); - Complex h[1] = {Complex::Float(2.0)}; + Complex h[1] = {Complex::Float(2.0)}; assert_parsed_expression_evaluates_to("gcd(234,394)", h); + Complex hd[1] = {Complex::Float(2.0)}; + assert_parsed_expression_evaluates_to("gcd(234,394)", hd); Complex i[1] = {Complex::Float(3.0f)}; assert_parsed_expression_evaluates_to("im(2+3I)", i); + Complex id[1] = {Complex::Float(3.0f)}; + assert_parsed_expression_evaluates_to("im(2+3I)", id); - Complex j[1] = {Complex::Float(3.0/2.0)}; + Complex j[1] = {Complex::Float(3.0/2.0)}; assert_parsed_expression_evaluates_to("int(x, 1, 2)", j); + Complex jd[1] = {Complex::Float(3.0/2.0)}; + assert_parsed_expression_evaluates_to("int(x, 1, 2)", jd); Complex k[1] = {Complex::Float(46098.0f)}; assert_parsed_expression_evaluates_to("lcm(234,394)", k); + Complex kd[1] = {Complex::Float(46098.0f)}; + assert_parsed_expression_evaluates_to("lcm(234,394)", kd); - Complex l[1] = {Complex::Float(std::log(2.0))}; + Complex l[1] = {Complex::Float(std::log(2.0))}; assert_parsed_expression_evaluates_to("ln(2)", l); + Complex ld[1] = {Complex::Float(std::log(2.0))}; + assert_parsed_expression_evaluates_to("ln(2)", ld); Complex m[1] = {Complex::Float(std::log10(2.0f))}; assert_parsed_expression_evaluates_to("log(2)", m); + Complex md[1] = {Complex::Float(std::log10(2.0f))}; + assert_parsed_expression_evaluates_to("log(2)", md); - Complex n[1] = {Complex::Float(5040.0)}; + Complex n[1] = {Complex::Float(5040.0)}; assert_parsed_expression_evaluates_to("permute(10, 4)", n); + Complex nd[1] = {Complex::Float(5040.0)}; + assert_parsed_expression_evaluates_to("permute(10, 4)", nd); Complex o[1] = {Complex::Float(604800.0f)}; assert_parsed_expression_evaluates_to("product(n, 4, 10)", o); + Complex od[1] = {Complex::Float(604800.0f)}; + assert_parsed_expression_evaluates_to("product(n, 4, 10)", od); - Complex p[1] = {Complex::Float(2.0)}; + Complex p[1] = {Complex::Float(2.0)}; assert_parsed_expression_evaluates_to("re(2+I)", p); + Complex pd[1] = {Complex::Float(2.0)}; + assert_parsed_expression_evaluates_to("re(2+I)", pd); Complex q[1] = {Complex::Float(9.0f)}; assert_parsed_expression_evaluates_to("rem(29, 10)", q); + Complex qd[1] = {Complex::Float(9.0f)}; + assert_parsed_expression_evaluates_to("rem(29, 10)", qd); - Complex r[1] = {Complex::Float(std::pow(2.0, 1.0/3.0))}; + Complex r[1] = {Complex::Float(std::pow(2.0, 1.0/3.0))}; assert_parsed_expression_evaluates_to("root(2,3)", r); + Complex rd[1] = {Complex::Float(std::pow(2.0, 1.0/3.0))}; + assert_parsed_expression_evaluates_to("root(2,3)", rd); Complex s[1] = {Complex::Float(std::sqrt(2.0f))}; assert_parsed_expression_evaluates_to("R(2)", s); + Complex sd[1] = {Complex::Float(std::sqrt(2.0f))}; + assert_parsed_expression_evaluates_to("R(2)", sd); - Complex t[1] = {Complex::Float(49.0)}; + Complex t[1] = {Complex::Float(49.0)}; assert_parsed_expression_evaluates_to("sum(n, 4, 10)", t); + Complex td[1] = {Complex::Float(49.0)}; + assert_parsed_expression_evaluates_to("sum(n, 4, 10)", td); #if MATRICES_ARE_DEFINED Complex u[1] = {Complex::Float(15.0f)}; assert_parsed_expression_evaluates_to("trace([[1,2,3][4,5,6][7,8,9]])", u); + Complex ud[1] = {Complex::Float(15.0f)}; + assert_parsed_expression_evaluates_to("trace([[1,2,3][4,5,6][7,8,9]])", ud); #endif - Complex v[2] = {Complex::Float(0.1 - std::sqrt(1.0/100.0)), Complex::Float(0.1 + std::sqrt(1.0/100.0))}; + Complex v[2] = {Complex::Float(0.1 - std::sqrt(1.0/100.0)), Complex::Float(0.1 + std::sqrt(1.0/100.0))}; assert_parsed_expression_evaluates_to("confidence(0.1, 100)", v, 1, 2); + Complex vd[2] = {Complex::Float(0.1 - std::sqrt(1.0/100.0)), Complex::Float(0.1 + std::sqrt(1.0/100.0))}; + assert_parsed_expression_evaluates_to("confidence(0.1, 100)", vd, 1, 2); #if MATRICES_ARE_DEFINED Complex w[2] = {Complex::Float(2.0f), Complex::Float(3.0f)}; assert_parsed_expression_evaluates_to("dim([[1,2,3][4,5,-6]])", w, 1, 2); + Complex wd[2] = {Complex::Float(2.0f), Complex::Float(3.0f)}; + assert_parsed_expression_evaluates_to("dim([[1,2,3][4,5,-6]])", wd, 1, 2); #endif - Complex x[1] = {Complex::Cartesian(3.0, -2.0)}; + Complex x[1] = {Complex::Cartesian(3.0, -2.0)}; assert_parsed_expression_evaluates_to("conj(3+2*I)", x); + Complex xd[1] = {Complex::Cartesian(3.0, -2.0)}; + assert_parsed_expression_evaluates_to("conj(3+2*I)", xd); #if MATRICES_ARE_DEFINED Complex y[9] = {Complex::Float(-31.0f/24.0f), Complex::Float(-1.0f/12.0f), Complex::Float(3.0f/8.0f), Complex::Float(13.0f/12.0f), Complex::Float(1.0f/6.0f), Complex::Float(-1.0f/4.0f), Complex::Float(1.0f/24.0f),Complex::Float(-1.0f/12.0f), Complex::Float(1.0f/24.0f)}; assert_parsed_expression_evaluates_to("inverse([[1,2,3][4,5,-6][7,8,9]])", y, 3, 3); + Complex yd[9] = {Complex::Float(-31.0f/24.0f), Complex::Float(-1.0f/12.0f), Complex::Float(3.0f/8.0f), Complex::Float(13.0f/12.0f), Complex::Float(1.0f/6.0f), Complex::Float(-1.0f/4.0f), Complex::Float(1.0f/24.0f),Complex::Float(-1.0f/12.0f), Complex::Float(1.0f/24.0f)}; + assert_parsed_expression_evaluates_to("inverse([[1,2,3][4,5,-6][7,8,9]])", yd, 3, 3); #endif - Complex z[2] = {Complex::Float(0.1-std::sqrt(1.0/100.0)), Complex::Float(0.1+std::sqrt(1.0/100.0))}; + Complex z[2] = {Complex::Float(0.1-std::sqrt(1.0/100.0)), Complex::Float(0.1+std::sqrt(1.0/100.0))}; assert_parsed_expression_evaluates_to("prediction(0.1, 100)", z, 1, 2); + Complex zd[2] = {Complex::Float(0.1-std::sqrt(1.0/100.0)), Complex::Float(0.1+std::sqrt(1.0/100.0))}; + assert_parsed_expression_evaluates_to("prediction(0.1, 100)", zd, 1, 2); Complex aa[2] = {Complex::Float(0.1f-1.96f*std::sqrt((0.1f*(1.0f-0.1f))/100.0f)), Complex::Float(0.1f+1.96f*std::sqrt((0.1f*(1.0f-0.1f))/100.0f))}; assert_parsed_expression_evaluates_to("prediction95(0.1, 100)", aa, 1, 2); + Complex aad[2] = {Complex::Float(0.1f-1.96f*std::sqrt((0.1f*(1.0f-0.1f))/100.0f)), Complex::Float(0.1f+1.96f*std::sqrt((0.1f*(1.0f-0.1f))/100.0f))}; + assert_parsed_expression_evaluates_to("prediction95(0.1, 100)", aad, 1, 2); - Complex ab[1] = {Complex::Cartesian(-100.0, -540.0)}; + Complex ab[1] = {Complex::Cartesian(-100.0, -540.0)}; assert_parsed_expression_evaluates_to("product(2+n*I, 1, 5)", ab); + Complex abd[1] = {Complex::Cartesian(-100.0, -540.0)}; + assert_parsed_expression_evaluates_to("product(2+n*I, 1, 5)", abd); Complex ac[1] = {Complex::Cartesian(1.4593656008f, 0.1571201229f)}; assert_parsed_expression_evaluates_to("root(3+I, 3)", ac); + Complex acd[1] = {Complex::Cartesian(1.4593656008f, 0.1571201229f)}; + assert_parsed_expression_evaluates_to("root(3+I, 3)", acd); - Complex ad[1] = {Complex::Cartesian(1.38200696233, -0.152442779)}; - assert_parsed_expression_evaluates_to("root(3, 3+I)", ad); + Complex add[1] = {Complex::Cartesian(1.38200696233, -0.152442779)}; + assert_parsed_expression_evaluates_to("root(3, 3+I)", add); + Complex addd[1] = {Complex::Cartesian(1.38200696233, -0.152442779)}; + assert_parsed_expression_evaluates_to("root(3, 3+I)", addd); Complex ae[1] = {Complex::Cartesian(1.75532f, 0.28485f)}; assert_parsed_expression_evaluates_to("R(3+I)", ae); + Complex aed[1] = {Complex::Cartesian(1.75532f, 0.28485f)}; + assert_parsed_expression_evaluates_to("R(3+I)", aed); - Complex af[1] = {Complex::Cartesian(10.0, 15.0)}; + Complex af[1] = {Complex::Cartesian(10.0, 15.0)}; assert_parsed_expression_evaluates_to("sum(2+n*I,1,5)", af); + Complex afd[1] = {Complex::Cartesian(10.0, 15.0)}; + assert_parsed_expression_evaluates_to("sum(2+n*I,1,5)", afd); #if MATRICES_ARE_DEFINED - Complex ag[9] = {Complex::Float(1.0), Complex::Float(4.0), Complex::Float(7.0), Complex::Float(2.0), Complex::Float(5.0), Complex::Float(8.0), Complex::Float(3.0), Complex::Float(-6.0), Complex::Float(9.0)}; + Complex ag[9] = {Complex::Float(1.0), Complex::Float(4.0), Complex::Float(7.0), Complex::Float(2.0), Complex::Float(5.0), Complex::Float(8.0), Complex::Float(3.0), Complex::Float(-6.0), Complex::Float(9.0)}; assert_parsed_expression_evaluates_to("transpose([[1,2,3][4,5,-6][7,8,9]])", ag, 3, 3); assert_parsed_expression_evaluates_to("transpose([[1,7,5][4,2,8]])", ag, 3, 2); assert_parsed_expression_evaluates_to("transpose([[1,2][4,5][7,8]])", ag, 2, 3); + Complex agd[9] = {Complex::Float(1.0), Complex::Float(4.0), Complex::Float(7.0), Complex::Float(2.0), Complex::Float(5.0), Complex::Float(8.0), Complex::Float(3.0), Complex::Float(-6.0), Complex::Float(9.0)}; + assert_parsed_expression_evaluates_to("transpose([[1,2,3][4,5,-6][7,8,9]])", agd, 3, 3); + assert_parsed_expression_evaluates_to("transpose([[1,7,5][4,2,8]])", agd, 3, 2); + assert_parsed_expression_evaluates_to("transpose([[1,2][4,5][7,8]])", agd, 2, 3); #endif Complex ah[1] = {Complex::Float(2.325f)}; - assert_parsed_expression_evaluates_to("round(2.3245,3)", ah); + assert_parsed_expression_evaluates_to("round(2.3246,3)", ah); + Complex ahd[1] = {Complex::Float(2.325f)}; + assert_parsed_expression_evaluates_to("round(2.3245,3)", ahd); - Complex ai[1] = {Complex::Float(720.0f)}; + Complex ai[1] = {Complex::Float(720.0f)}; assert_parsed_expression_evaluates_to("6!", ai); + Complex aid[1] = {Complex::Float(720.0f)}; + assert_parsed_expression_evaluates_to("6!", aid); Complex aj[1] = {Complex::Cartesian(0.0f, 1.0f)}; assert_parsed_expression_evaluates_to("R(-1)", aj); + Complex ajd[1] = {Complex::Cartesian(0.0f, 1.0f)}; + assert_parsed_expression_evaluates_to("R(-1)", ajd); - Complex ak[1] = {Complex::Cartesian(0.5, 0.86602540378443864676)}; + Complex ak[1] = {Complex::Cartesian(0.5, 0.86602540378443864676)}; assert_parsed_expression_evaluates_to("root(-1,3)", ak); + Complex akd[1] = {Complex::Cartesian(0.5, 0.86602540378443864676)}; + assert_parsed_expression_evaluates_to("root(-1,3)", akd); } diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index 279ec9931..1694d6d3e 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -38,17 +38,18 @@ void assert_parsed_expression_evaluates_to(const char * expression, Complex * Expression * a = parse_expression(expression); Expression * m = a->evaluate(globalContext, angleUnit); assert(m); - assert(m->numberOfOperands() == numberOfRows*numberOfColumns); + assert(m->numberOfOperands() == 0 || m->numberOfOperands() == numberOfRows*numberOfColumns); if (m->type() == Expression::Type::Matrix) { assert(static_cast(m)->numberOfRows() == numberOfRows); assert(static_cast(m)->numberOfColumns() == numberOfColumns); - for (int i = 0; i < m->numberOfOperands(); i++) { - assert(std::fabs(static_cast *>(m->operand(i))->a() - results[i].a()) < 0.0001f); - assert(std::fabs(static_cast *>(m->operand(i))->b() - results[i].b()) < 0.0001f); + for (int i = 0; i < m->numberOfOperands(); i++) { + assert(std::fabs(static_cast *>(m->operand(i))->a() - results[i].a()) < 0.0001f); + assert(std::fabs(static_cast *>(m->operand(i))->b() - results[i].b()) < 0.0001f); + } + } else { + assert(std::fabs(static_cast *>(m)->a() - results[0].a()) < 0.0001f); + assert(std::fabs(static_cast *>(m)->b() - results[0].b()) < 0.0001f); } - } - assert(std::fabs(static_cast *>(m)->a() - results[0].a()) < 0.0001f); - assert(std::fabs(static_cast *>(m)->b() - results[0].b()) < 0.0001f); delete a; delete m; } diff --git a/poincare/test/helper.h b/poincare/test/helper.h index bb4e63350..798e1a59c 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -6,8 +6,8 @@ constexpr Poincare::Expression::AngleUnit Radian = Poincare::Expression::AngleUn Poincare::Expression * parse_expression(const char * expression); void assert_parsed_expression_type(const char * expression, Poincare::Expression::Type type); template -void assert_parsed_expression_evaluates_to(const char * expression, Poincare::Complex * results, int numberOfRows, int numberOfColumns = 1, Poincare::Expression::AngleUnit angleUnit = Degree); +void assert_parsed_expression_evaluates_to(const char * expression, Poincare::Complex * results, int numberOfRows, int numberOfColumns, Poincare::Expression::AngleUnit angleUnit = Degree); template void assert_parsed_expression_evaluates_to(const char * expression, Poincare::Complex * results, Poincare::Expression::AngleUnit angleUnit = Degree) { - assert_parsed_expression_evaluates_to(expression, results, 1, 1, angleUnit); + assert_parsed_expression_evaluates_to(expression, results, 0, 0, angleUnit); } From 7503a6aedd1565355e565fc28dfefc7f9549a3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 17:20:01 +0100 Subject: [PATCH 10/77] [poincare] implement Round::shallowReduce for rationals Change-Id: I552570bceed2b52dbf6db9b6d47979e0f87b7a4f --- poincare/src/round.cpp | 17 +++++++++++++++++ poincare/test/simplify_easy.cpp | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index ac379508d..60cc6bedc 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -1,5 +1,6 @@ #include #include +#include extern "C" { #include @@ -27,6 +28,22 @@ Expression * Round::shallowReduce(Context& context, AngleUnit angleUnit) { return replaceWith(new Undefined(), true); } #endif + if (operand(0)->type() == Type::Rational && operand(1)->type() == Type::Rational) { + Rational * r1 = static_cast(editableOperand(0)); + Rational * r2 = static_cast(editableOperand(1)); + if (!r2->denominator().isOne()) { + return replaceWith(new Undefined(), true); + } + Rational err = Rational::Power(Rational(10), r2->numerator()); + Rational mult = Rational::Multiplication(*r1, Rational(err)); + IntegerDivision d = Integer::Division(mult.numerator(), mult.denominator()); + Integer rounding = d.quotient; + if (Rational::NaturalOrder(Rational(d.remainder, mult.denominator()), Rational(1,2)) >= 0) { + rounding = Integer::Addition(rounding, Integer(1)); + } + Rational result = Rational::Multiplication(rounding, Rational::Power(Rational(1,10), r2->numerator())); + return replaceWith(new Rational(result), true); + } return this; // TODO: implement for rationals! } diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 274173870..276a8647a 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -149,6 +149,11 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); assert_parsed_expression_simplify_to("lcm(11,121)", "121"); assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); + assert_parsed_expression_simplify_to("round(4.235,2)", "4.24"); + assert_parsed_expression_simplify_to("round(4.23,0)", "4"); + assert_parsed_expression_simplify_to("round(4.9,0)", "5"); + assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); + assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); assert_parsed_expression_simplify_to("re(1/2)", "1/2"); From cbea9aa0918ee7d80758c2430edc6ff5cee6e835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 22 Nov 2017 17:51:08 +0100 Subject: [PATCH 11/77] [poincare] Do not split in prime factors integers that are too big (to avoid lagging) Change-Id: Icb70a5ca96fbd8b3eb1c829f8de798df43d54de5 --- poincare/include/poincare/arithmetic.h | 1 + poincare/src/arithmetic.cpp | 1 + poincare/src/logarithm.cpp | 6 ++++-- poincare/src/power.cpp | 5 +++-- poincare/test/simplify_easy.cpp | 9 +++++---- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/poincare/include/poincare/arithmetic.h b/poincare/include/poincare/arithmetic.h index 0d0d14d3b..ab7cefc04 100644 --- a/poincare/include/poincare/arithmetic.h +++ b/poincare/include/poincare/arithmetic.h @@ -11,6 +11,7 @@ public: static void PrimeFactorization(const Integer * i, Integer * outputFactors, Integer * outputCoefficients, int outputLength); constexpr static int k_numberOfPrimeFactors = 1000; constexpr static int k_maxNumberOfPrimeFactors = 32; + static const Integer k_biggestPrimeFactorizedInteger; static const Integer k_primorial32; }; diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp index f5fe302d1..ed93623c0 100644 --- a/poincare/src/arithmetic.cpp +++ b/poincare/src/arithmetic.cpp @@ -3,6 +3,7 @@ namespace Poincare { +const Integer Arithmetic::k_biggestPrimeFactorizedInteger("10000000000000"); // 1E13 const Integer Arithmetic::k_primorial32("525896479052627740771371797072411912900610967452630"); Integer Arithmetic::LCM(const Integer * a, const Integer * b) { diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index b8ece9432..22588fe3f 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -148,8 +148,10 @@ Expression * Logarithm::splitInteger(Integer i, bool isDenominator, Context & co return new Rational(0); } assert(!i.isOne()); - if (Arithmetic::k_primorial32.isLowerThan(i)) { - // We do not want to break i in prime factor because it might be take too many factors... More than k_maxNumberOfPrimeFactors. + if (Arithmetic::k_primorial32.isLowerThan(i) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(i)) { + /* We do not want to break i in prime factor because + * - either it might be take too many factors... More than k_maxNumberOfPrimeFactors. + * - Or, it might takes too much time */ Expression * e = clone(); e->replaceOperand(operand(0), new Rational(i), true); if (!isDenominator) { diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 229398a47..b376b0c42 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -397,9 +397,10 @@ Expression * Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational * r if (i.isOne()) { return new Rational(1); } - if (Arithmetic::k_primorial32.isLowerThan(i)) { + if (Arithmetic::k_primorial32.isLowerThan(i) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(i)) { r->setSign(isDenominator ? Sign::Negative : Sign::Positive); - // We do not want to break i in prime factor because it might be take too many factors... More than k_maxNumberOfPrimeFactors. + /* We do not want to break i in prime factor because it might be take too + * many factors... More than k_maxNumberOfPrimeFactors; or too much time!*/ return new Power(new Rational(i), r->clone(), false); } Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 276a8647a..77e6f6dab 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -261,7 +261,8 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); assert_parsed_expression_simplify_to("R(3^2)", "3"); assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); - assert_parsed_expression_simplify_to("R(15241578780673678515622620750190521)", "123456789123456789"); + assert_parsed_expression_simplify_to("R(15241578780673678515622620750190521)", "R(15241578780673678515622620750190521)"); + assert_parsed_expression_simplify_to("R(154355776)", "12424"); assert_parsed_expression_simplify_to("R(P)^2", "P"); assert_parsed_expression_simplify_to("R(P^2)", "P"); assert_parsed_expression_simplify_to("R((-P)^2)", "P"); @@ -509,8 +510,8 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("A^0", "1"); assert_parsed_expression_simplify_to("(-3)^0", "1"); - /* This does not work but should not as it is above k_primorial32 = 1*3*5*7*11*... (product of first 32 primes. */ - //assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "123456789123456789"); + assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); - //assert_parsed_expression_simplify_to("1/sqrt(2)", "sqrt(2)/2"); + // This does not work but should not as it is above k_biggestPrimeFactorizedInteger + assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "1881676377434183981909562699940347954480361860897069^(1/3)"); } From 920a12027f3b63162393bdaa763f70907cb4ea2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 09:52:00 +0100 Subject: [PATCH 12/77] [poincare] In complex writeToText, change '*' to MiddleDot Change-Id: If5e56f5c1a96ce0d0d5136272086dc78887a1cec --- poincare/src/complex.cpp | 10 +++++----- poincare/test/complex.cpp | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 1026c26a9..6a40feb18 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -263,7 +263,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: if (r() != 1 || th() == 0) { numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode); if (r() != 0 && th() != 0 && bufferSize > numberOfChars+1) { - buffer[numberOfChars++] = '*'; + buffer[numberOfChars++] = Ion::Charset::MiddleDot; // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } @@ -278,7 +278,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); if (bufferSize > numberOfChars+3) { - buffer[numberOfChars++] = '*'; + buffer[numberOfChars++] = Ion::Charset::MiddleDot; buffer[numberOfChars++] = Ion::Charset::IComplex; buffer[numberOfChars++] = ')'; buffer[numberOfChars] = 0; @@ -297,7 +297,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } if (m_b != 1 && m_b != -1 && m_b != 0) { numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); - buffer[numberOfChars++] = '*'; + buffer[numberOfChars++] = Ion::Charset::MiddleDot; } if (m_b == -1 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '-'; @@ -449,7 +449,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl if (r() != 1 || th() == 0) { numberOfCharInBase = convertFloatToText(r(), bufferBase, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); if (r() != 0 && th() != 0) { - bufferBase[numberOfCharInBase++] = '*'; + bufferBase[numberOfCharInBase++] = Ion::Charset::MiddleDot; } } if (r() != 0 && th() != 0) { @@ -459,7 +459,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl if (r() != 0 && th() != 0) { numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); - bufferSuperscript[numberOfCharInSuperscript++] = '*'; + bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::MiddleDot; bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex; bufferSuperscript[numberOfCharInSuperscript] = 0; } diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index ac4a6e63f..3c8a0c9ce 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -47,6 +47,9 @@ void assert_cartesian_complex_prints_to(T a, T b, const char * result, Expressio if (buffer[i] == Ion::Charset::IComplex) { buffer[i] = 'i'; } + if (buffer[i] == Ion::Charset::MiddleDot) { + buffer[i] = '*'; + } } assert(strcmp(buffer, result) == 0); From 942e8c5bd45e7f925f1b6b85693df5e333e445da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 10:41:43 +0100 Subject: [PATCH 13/77] [poincare] Multiplication::shallowReduce : factorizing factors can lead to new rational that have to be merge afterwards (ie 2^(1/2)*2^(1/2) or i*i or pi*pi^(-1)) Change-Id: Ibeb21e305ff065524210a5cfb46182b0de2f49c9 --- poincare/src/multiplication.cpp | 38 ++++++++++++++++++--------------- poincare/test/simplify_easy.cpp | 1 + 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 5b6207f0f..5a65709b5 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -229,12 +229,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a while (i < numberOfOperands()-1) { Expression * oi = editableOperand(i); Expression * oi1 = editableOperand(i+1); - if (oi->type() == Type::Rational && oi1->type() == Type::Rational) { - Rational a = Rational::Multiplication(*(static_cast(oi)), *(static_cast(oi1))); - replaceOperand(oi, new Rational(a), true); - removeOperand(oi1, true); - continue; - } else if (TermsHaveIdenticalBase(oi, oi1)) { + if (TermsHaveIdenticalBase(oi, oi1)) { bool shouldFactorizeBase = true; if (TermHasRationalBase(oi)) { /* Combining powers of a given rational isn't straightforward. Indeed, @@ -277,30 +272,39 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a * shallowReduce */ sortOperands(SimplificationOrder); - /* Step 6: Let's remove ones if there's any. It's important to do this after - * having factorized because factorization can lead to new ones. For example - * pi^(-1)*pi. We don't remove the last one if it's the only operand left - * though. - * Same comment for -1 that can appear when reducing i*i. */ - i = 0; + /* Step 6: We remove rational operands that appeared in the middle of sorted + * operands. It's important to do this after having factorized because + * factorization can lead to new ones. Indeed: + * pi^(-1)*pi-> 1 + * i*i -> -1 + * 2^(1/2)*2^(1/2) -> 2 + * Last, we remove the only rational operand if it is one and not the only + * operand. */ + i = 1; while (i < numberOfOperands()) { Expression * o = editableOperand(i); - if (o->type() == Type::Rational && static_cast(o)->isOne() && numberOfOperands() > 1) { + if (o->type() == Type::Rational && static_cast(o)->isOne()) { removeOperand(o, true); continue; } - if (o->type() == Type::Rational && static_cast(o)->isMinusOne() && numberOfOperands() > 1 && i > 0) { - removeOperand(o, operand(0)->type() == Type::Rational); + if (o->type() == Type::Rational) { if (operand(0)->type() == Type::Rational) { - Rational * r = static_cast(editableOperand(0)); - r->setSign(r->sign() == Sign::Positive ? Sign::Negative : Sign::Positive); + Rational * o0 = static_cast(editableOperand(0)); + Rational m = Rational::Multiplication(*o0, *(static_cast(o))); + replaceOperand(o0, new Rational(m), true); + removeOperand(o, true); } else { + removeOperand(o, false); addOperandAtIndex(o, 0); } continue; } i++; } + if (operand(0)->type() == Type::Rational && static_cast(editableOperand(0))->isOne() && numberOfOperands() > 1) { + removeOperand(editableOperand(0), true); + } + /* Step 7: Expand multiplication over addition operands if any. For example, * turn (a+b)*c into a*c + b*c. We do not want to do this step right now if diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 77e6f6dab..4382c3117 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -509,6 +509,7 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("P^0", "1"); assert_parsed_expression_simplify_to("A^0", "1"); assert_parsed_expression_simplify_to("(-3)^0", "1"); + assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); From 7f0c54e6e14d000be15bd078ca897728b96a6c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 11:01:56 +0100 Subject: [PATCH 14/77] [poincare] Add a todo with commented test Change-Id: I9cc1a758d77c3ee5267c019dc2b4f40834f198ca --- poincare/test/simplify_easy.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 4382c3117..755e43eef 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -34,6 +34,7 @@ void assert_parsed_expression_simplify_to(const char * expression, const char * QUIZ_CASE(poincare_simplify_easy) { //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); + //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? // Addition Matrix #if MATRIX_EXACT_REDUCING assert_parsed_expression_simplify_to("1+[[1,2,3][4,5,6]]", "[[2,3,4][5,6,7]]"); From a5c984fce33a5c49f97d9520790d2f648a78c026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 11:49:52 +0100 Subject: [PATCH 15/77] [apps] Calculation: change the grey of approximate output Change-Id: Iac001fc581544af0634ad1d7765074f69aa07809 --- apps/calculation/output_expressions_view.cpp | 4 ++-- escher/include/escher/palette.h | 1 + escher/src/palette.cpp | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/calculation/output_expressions_view.cpp b/apps/calculation/output_expressions_view.cpp index e7fd721af..43caefc23 100644 --- a/apps/calculation/output_expressions_view.cpp +++ b/apps/calculation/output_expressions_view.cpp @@ -9,7 +9,7 @@ namespace Calculation { OutputExpressionsView::OutputExpressionsView(Responder * parentResponder) : Responder(parentResponder), m_approximateExpressionView(), - m_approximateSign(KDText::FontSize::Large, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyDark), + m_approximateSign(KDText::FontSize::Large, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyVeryDark), m_exactExpressionView(), m_selectedSubviewType(OutputExpressionsView::SubviewType::ExactOutput) { @@ -46,7 +46,7 @@ void OutputExpressionsView::reloadCell() { if (numberOfSubviews() == 1) { m_approximateExpressionView.setTextColor(KDColorBlack); } else { - m_approximateExpressionView.setTextColor(Palette::GreyDark); + m_approximateExpressionView.setTextColor(Palette::GreyVeryDark); } layoutSubviews(); } diff --git a/escher/include/escher/palette.h b/escher/include/escher/palette.h index 5b6230678..ad47ce19d 100644 --- a/escher/include/escher/palette.h +++ b/escher/include/escher/palette.h @@ -13,6 +13,7 @@ public: constexpr static KDColor GreyBright = KDColor::RGB24(0xececec); constexpr static KDColor GreyMiddle = KDColor::RGB24(0xd9d9d9); constexpr static KDColor GreyDark = KDColor::RGB24(0xa7a7a7); + constexpr static KDColor GreyVeryDark = KDColor::RGB24(0x8c8c8c); constexpr static KDColor Select = KDColor::RGB24(0xd4d7e0); constexpr static KDColor SelectDark = KDColor::RGB24(0xb0b8d8); constexpr static KDColor WallScreen = KDColor::RGB24(0xf7f9fa); diff --git a/escher/src/palette.cpp b/escher/src/palette.cpp index 0602d3ca7..59da5fbb7 100644 --- a/escher/src/palette.cpp +++ b/escher/src/palette.cpp @@ -8,6 +8,7 @@ constexpr KDColor Palette::GreyWhite; constexpr KDColor Palette::GreyBright; constexpr KDColor Palette::GreyMiddle; constexpr KDColor Palette::GreyDark; +constexpr KDColor Palette::GreyVeryDark; constexpr KDColor Palette::Select; constexpr KDColor Palette::SelectDark; constexpr KDColor Palette::WallScreen; From 888d1a6df101952a3076217066d311b5879fd194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 11:50:29 +0100 Subject: [PATCH 16/77] [poincare] In DynamicHierarchy: speed up sort operands as we know there is no matrix among operands Change-Id: Ia673fbcdf7ae8d07138efa6b43a81b3de19a5444 --- poincare/src/dynamic_hierarchy.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/poincare/src/dynamic_hierarchy.cpp b/poincare/src/dynamic_hierarchy.cpp index cb60ca350..d52a6079f 100644 --- a/poincare/src/dynamic_hierarchy.cpp +++ b/poincare/src/dynamic_hierarchy.cpp @@ -102,7 +102,11 @@ void DynamicHierarchy::sortOperands(ExpressionOrder order) { for (int j = 0; j < numberOfOperands()-1; j++) { /* Warning: Matrix operations are not always commutative (ie, * multiplication) so we never swap 2 matrices. */ +#if MATRIX_EXACT_REDUCING if (order(operand(j), operand(j+1)) > 0 && (!operand(j)->recursivelyMatches(Expression::IsMatrix) || !operand(j+1)->recursivelyMatches(Expression::IsMatrix))) { +#else + if (order(operand(j), operand(j+1)) > 0) { +#endif swapOperands(j, j+1); isSorted = false; } From a90228a48f4332cd95f23889e8dc41fe6014ca97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 13:47:29 +0100 Subject: [PATCH 17/77] [poincare] Power of true complexes is undefined (otherwise, some rules cannot be applied: (x^y)^z) Change-Id: I44b64a9fb80997abb7d957b9ac9c1d4d38f8de8e --- poincare/src/power.cpp | 13 ++++++++++++- poincare/test/simplify_easy.cpp | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index b376b0c42..7b65bee9d 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -181,7 +181,18 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { } #endif - /* Step 0: We look for square root and sum of square roots (two terms maximum + /* Step 0: if both operands are true complexes, the result is undefined. + * We can assert that evaluations is a complex as matrix are not simplified */ + Complex * op0 = static_cast *>(operand(0)->evaluate(context, angleUnit)); + Complex * op1 = static_cast *>(operand(1)->evaluate(context, angleUnit)); + bool bothOperandsComplexes = op0->b() != 0 && op1->b() != 0; + delete op0; + delete op1; + if (bothOperandsComplexes) { + return replaceWith(new Undefined(), true); + } + + /* Step 1: We look for square root and sum of square roots (two terms maximum * so far) at the denominator and move them to the numerator. */ Expression * r = removeSquareRootsFromDenominator(context, angleUnit); if (r) { diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 755e43eef..5b57e16b9 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -511,6 +511,8 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("A^0", "1"); assert_parsed_expression_simplify_to("(-3)^0", "1"); assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); + assert_parsed_expression_simplify_to("root(5^(-I)3^9,I)", "undef"); + assert_parsed_expression_simplify_to("I^I", "undef"); assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); From 6f75b54e8bc3b7f79b10fd797cc12c2ed71b497f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 13:48:19 +0100 Subject: [PATCH 18/77] [poincare] Fix: Do not reduce too big power (999^-999) Change-Id: I46bab384d1700cfc7d8069eb68b0c77efc2b343d --- poincare/include/poincare/power.h | 1 + poincare/src/power.cpp | 7 ++++++- poincare/test/simplify_easy.cpp | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index e43cad633..1c972497b 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -20,6 +20,7 @@ public: Sign sign() const override; template static Complex compute(const Complex c, const Complex d); private: + constexpr static int k_maxIntegerPower = 100; /* Property */ Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override; /* Layout */ diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 7b65bee9d..7870ea2f9 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -242,10 +242,15 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { double p = a->approximate(context, angleUnit); double q = operand(1)->approximate(context, angleUnit); - double approx = std::pow(std::fabs(p), q); + double approx = std::pow(std::fabs(p), std::fabs(q)); if (std::isinf(approx) || std::isnan(approx) || std::fabs(approx)> 1E100) { return this; } + Integer n = static_cast(operand(1))->numerator(); + n.setNegative(false); + if (Integer::NaturalOrder(n, Integer(k_maxIntegerPower)) > 0) { + return this; + } return simplifyRationalRationalPower(this, a, static_cast(editableOperand(1)), context, angleUnit); } } diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 5b57e16b9..331b7f4f4 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -504,6 +504,7 @@ QUIZ_CASE(poincare_simplify_easy) { assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2(2R(2)+2R(2)*I)^2)"); assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); assert_parsed_expression_simplify_to("999^999", "999^999"); + assert_parsed_expression_simplify_to("999^-999", "1/999^999"); assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); assert_parsed_expression_simplify_to("0^0", "undef"); assert_parsed_expression_simplify_to("x^0", "1"); From 5acafb0cdbdb721f5847f45ed53f37f97b321ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 14:17:35 +0100 Subject: [PATCH 19/77] [poincare] Cap the length of integer that are printed Change-Id: I6d5e1f1201d1354ca48fee6f84dc0400143b7347 --- poincare/src/integer.cpp | 9 ++++----- poincare/test/convert_expression_to_text.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index 8a49f1dab..3b673e6d3 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -550,13 +550,12 @@ int Integer::writeTextInBuffer(char * buffer, int bufferSize) const { return -1; } buffer[bufferSize-1] = 0; - /* If the integer is too long, this method may overflow the stack. + /* If the integer is too long, this method may be too slow. * Experimentally, we can display at most integer whose number of digits is - * around 7. However, to avoid crashing when the stack is already half full, - * we decide not to display integers whose number of digits > 5. */ - /*if (m_numberOfDigits > 12) { + * around 25. */ + if (m_numberOfDigits > 25) { return strlcpy(buffer, "inf", 4); - }*/ + } Integer base = Integer(10); Integer abs = *this; diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index 5f69ac26b..e51a2d3b6 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -8,7 +8,7 @@ using namespace Poincare; -void assert_expression_prints_to(Expression * e, const char * result, int bufferSize = 100) { +void assert_expression_prints_to(Expression * e, const char * result, int bufferSize = 250) { quiz_print(result); char * buffer = new char[bufferSize]; @@ -34,8 +34,12 @@ void assert_expression_prints_to(Expression * e, const char * result, int buffer } QUIZ_CASE(poincare_rational_to_text) { - Rational r(2,3); - assert_expression_prints_to(&r, "2/3"); + Rational r0(2,3); + assert_expression_prints_to(&r0, "2/3"); + Rational r1("12345678910111213","123456789101112131"); + assert_expression_prints_to(&r1, "12345678910111213/123456789101112131"); + Rational r2("123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789","1"); + assert_expression_prints_to(&r2, "123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789"); } QUIZ_CASE(poincare_decimal_to_text) { From 42e407cad5f10b4e79f61ae8f2f0d051eadb9c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 14:51:14 +0100 Subject: [PATCH 20/77] [poincare] Improve implementation of Division::shallowReduce Change-Id: Id6b1f6606e5d4aa11b2543151fbba3c7be54551c --- poincare/include/poincare/power.h | 1 + poincare/src/division.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 1c972497b..eab93f84a 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -14,6 +14,7 @@ class Power : public StaticHierarchy<2> { friend class NthRoot; friend class SquareRoot; friend class Addition; + friend class Division; public: Type type() const override; Expression * clone() const override; diff --git a/poincare/src/division.cpp b/poincare/src/division.cpp index 8d03c7509..2badf3742 100644 --- a/poincare/src/division.cpp +++ b/poincare/src/division.cpp @@ -31,7 +31,7 @@ Expression * Division::shallowReduce(Context& context, AngleUnit angleUnit) { Power * p = new Power(operand(1), new Rational(-1), false); Multiplication * m = new Multiplication(operand(0), p, false); detachOperands(); - p->deepReduce(context, angleUnit); + p->shallowReduce(context, angleUnit); replaceWith(m, true); return m->shallowReduce(context, angleUnit); } From 54fbcfe15cbd0ce5dec016482ff893ef952431bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 23 Nov 2017 14:54:29 +0100 Subject: [PATCH 21/77] [poincare] In trigonometry, avoid to loop on trigo table if the expression does have the right type Change-Id: I2cd5169df6709943aeddf96d098aa8d8275465d0 --- poincare/src/trigonometry.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 37b87d77c..753f25b75 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -180,6 +180,17 @@ Expression * Trigonometry::table(const Expression * e, Expression::Type type, Co int trigonometricFunctionIndex = type == Expression::Type::Cosine || type == Expression::Type::ArcCosine ? 2 : (type == Expression::Type::Sine || type == Expression::Type::ArcSine ? 3 : 4); int inputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? trigonometricFunctionIndex : angleUnitIndex; int outputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? angleUnitIndex : trigonometricFunctionIndex; + + /* Avoid looping if we can exclude quickly that the e is in the table */ + if (inputIndex == 0 && e->type() != Expression::Type::Rational) { + return nullptr; + } + if (inputIndex == 1 && e->type() != Expression::Type::Rational && e->type() != Expression::Type::Multiplication && e->type() != Expression::Type::Symbol) { + return nullptr; + } + if (inputIndex >1 && e->type() != Expression::Type::Rational && e->type() != Expression::Type::Multiplication && e->type() != Expression::Type::Power && e->type() != Expression::Type::Addition) { + return nullptr; + } for (int i = 0; i < k_numberOfEntries; i++) { Expression * input = Expression::parse(cheatTable[i][inputIndex]); if (input == nullptr) { From 051f7c4a8accd9c9ea7165e6675584c99f514472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 09:35:13 +0100 Subject: [PATCH 22/77] [poincare] Multiplication: fix shallowReduce Change-Id: I95a1423e19d3d6e020a60736838be27124dc79d4 --- poincare/include/poincare/multiplication.h | 1 - poincare/src/multiplication.cpp | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index a4e6e1358..72d6cf741 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -44,7 +44,6 @@ private: static bool TermsHaveIdenticalBase(const Expression * e1, const Expression * e2); static bool TermsHaveIdenticalExponent(const Expression * e1, const Expression * e2); static bool TermHasRationalBase(const Expression * e); - static bool TermHasIntegerExponent(const Expression * e); static bool TermHasRationalExponent(const Expression * e); static const Expression * CreateExponent(Expression * e); Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 5a65709b5..198ae01b1 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -236,7 +236,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a * there are two cases we want to deal with: * - 2*2^(1/2) or 2*2^pi, we want to keep as-is * - 2^(1/2)*2^(3/2) we want to combine. */ - shouldFactorizeBase = !TermHasIntegerExponent(oi) && !TermHasIntegerExponent(oi1); + shouldFactorizeBase = oi->type() == Type::Power && oi1->type() == Type::Power; } if (shouldFactorizeBase) { factorizeBase(oi, oi1, context, angleUnit); @@ -460,17 +460,6 @@ bool Multiplication::TermHasRationalBase(const Expression * e) { return Base(e)->type() == Type::Rational; } -bool Multiplication::TermHasIntegerExponent(const Expression * e) { - if (e->type() != Type::Power) { - return true; - } - if (e->operand(1)->type() == Type::Rational) { - const Rational * r = static_cast(e->operand(1)); - return r->denominator().isOne(); - } - return false; -} - bool Multiplication::TermHasRationalExponent(const Expression * e) { if (e->type() != Type::Power) { return true; From 1fed5c15cf6e28d3c64b6e3948fc85cc6fad8578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 09:36:21 +0100 Subject: [PATCH 23/77] [poincare] Power: do not reduce too big negative power (999^-999) Change-Id: I52fdd31dcea53bc8926ad14945d6bb42ea18f668 --- poincare/src/power.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 7870ea2f9..cef3247cc 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -413,7 +413,9 @@ Expression * Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational * r if (i.isOne()) { return new Rational(1); } - if (Arithmetic::k_primorial32.isLowerThan(i) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(i)) { + Integer absI = i; + absI.setNegative(false); + if (Arithmetic::k_primorial32.isLowerThan(absI) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(absI)) { r->setSign(isDenominator ? Sign::Negative : Sign::Positive); /* We do not want to break i in prime factor because it might be take too * many factors... More than k_maxNumberOfPrimeFactors; or too much time!*/ From 914e9e880607a463f4e3fc9ab82033ee7b244f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 09:36:54 +0100 Subject: [PATCH 24/77] [poincare] In Integral evalutation, decrease the maximal number of iteration (for time complexity) Change-Id: Ie2af42071e128818e5f144c82720d36cac62c51b --- poincare/include/poincare/integral.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 593f1e14a..7c407e69d 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -31,7 +31,7 @@ private: T integral; T absoluteError; }; - constexpr static int k_maxNumberOfIterations = 100; + constexpr static int k_maxNumberOfIterations = 10; #ifdef LAGRANGE_METHOD template T lagrangeGaussQuadrature(T a, T b, VariableContext xContext, AngleUnit angleUnit) const; #else From b6d261c2b24a98c26624f97ce9a5c22267229584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 09:39:05 +0100 Subject: [PATCH 25/77] [poincare] Factorial: simplification order was needlessly (?) complicated. Change-Id: Id0987b734f63bb6306d5c36d90573a770f541728 --- poincare/include/poincare/factorial.h | 2 ++ poincare/src/factorial.cpp | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/poincare/include/poincare/factorial.h b/poincare/include/poincare/factorial.h index e0c04785a..97b80b185 100644 --- a/poincare/include/poincare/factorial.h +++ b/poincare/include/poincare/factorial.h @@ -23,8 +23,10 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; +#if 0 int simplificationOrderGreaterType(const Expression * e) const override; int simplificationOrderSameType(const Expression * e) const override; +#endif }; } diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index b751e361b..097802b33 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -82,13 +82,6 @@ ExpressionLayout * Factorial::privateCreateLayout(FloatDisplayMode floatDisplayM return new HorizontalLayout(childrenLayouts, 2); } -int Factorial::simplificationOrderGreaterType(const Expression * e) const { - if (SimplificationOrder(operand(0),e) == 0) { - return 1; - } - return SimplificationOrder(operand(0), e); -} - int Factorial::writeTextInBuffer(char * buffer, int bufferSize) const { if (bufferSize == 0) { return -1; @@ -103,8 +96,17 @@ int Factorial::writeTextInBuffer(char * buffer, int bufferSize) const { return numberOfChar; } +#if 0 +int Factorial::simplificationOrderGreaterType(const Expression * e) const { + if (SimplificationOrder(operand(0),e) == 0) { + return 1; + } + return SimplificationOrder(operand(0), e); +} + int Factorial::simplificationOrderSameType(const Expression * e) const { return SimplificationOrder(operand(0), e->operand(0)); } +#endif } From d8f269529b40840b64bff8a6aeff1afa9bd73986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 09:56:54 +0100 Subject: [PATCH 26/77] [poincare] Add all previous evaluation tests Change-Id: I7667c8c23d4097c33e3abef696f20d9f460b5068 --- poincare/Makefile | 11 +++-------- poincare/test/symbol.cpp | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/poincare/Makefile b/poincare/Makefile index 52e0ef4e3..070643ad7 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -107,23 +107,18 @@ tests += $(addprefix poincare/test/,\ convert_expression_to_text.cpp\ division.cpp\ function.cpp\ - helper.cpp\ - integer.cpp\ - simplify_easy.cpp\ -) - -testsi += $(addprefix poincare/test/,\ helper.cpp\ integer.cpp\ matrix.cpp\ parser.cpp\ - product.cpp\ power.cpp\ + product.cpp\ + simplify_easy.cpp\ subtraction.cpp\ - simplify_utils.cpp\ symbol.cpp\ trigo.cpp\ ) +# simplify_utils.cpp\ ifdef POINCARE_TESTS_PRINT_EXPRESSIONS tests += poincare/src/expression_debug.o diff --git a/poincare/test/symbol.cpp b/poincare/test/symbol.cpp index 36222cf3a..8183cb388 100644 --- a/poincare/test/symbol.cpp +++ b/poincare/test/symbol.cpp @@ -10,8 +10,8 @@ using namespace Poincare; QUIZ_CASE(poincare_parse_symbol) { assert_parsed_expression_type("P", Expression::Type::Symbol); assert_parsed_expression_type("X", Expression::Type::Symbol); - assert_parsed_expression_type("I", Expression::Type::Complex); - assert_parsed_expression_type("1.2E3", Expression::Type::Complex); + assert_parsed_expression_type("I", Expression::Type::Symbol); + assert_parsed_expression_type("1.2E3", Expression::Type::Decimal); assert_parsed_expression_type("ans", Expression::Type::Symbol); } From e6762bb25c5526ad7d0cee78d4ea6a35dc732a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 11:53:39 +0100 Subject: [PATCH 27/77] [poincare] Merge Hierarchy into Expression class Change-Id: Iea40f14e76dede947ffff1c63d53f6727d730e49 --- poincare/Makefile | 1 - poincare/include/poincare/dynamic_hierarchy.h | 4 +- poincare/include/poincare/expression.h | 16 +++- poincare/include/poincare/hierarchy.h | 25 ------ poincare/include/poincare/static_hierarchy.h | 4 +- poincare/src/dynamic_hierarchy.cpp | 4 +- poincare/src/expression.cpp | 83 +++++++++++++++++-- poincare/src/hierarchy.cpp | 79 ------------------ poincare/src/multiplication.cpp | 3 +- poincare/src/static_hierarchy.cpp | 4 +- 10 files changed, 98 insertions(+), 125 deletions(-) delete mode 100644 poincare/include/poincare/hierarchy.h delete mode 100644 poincare/src/hierarchy.cpp diff --git a/poincare/Makefile b/poincare/Makefile index 070643ad7..80bf9006c 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -34,7 +34,6 @@ objs += $(addprefix poincare/src/,\ frac_part.o\ global_context.o\ great_common_divisor.o\ - hierarchy.o\ hyperbolic_arc_cosine.o\ hyperbolic_arc_sine.o\ hyperbolic_arc_tangent.o\ diff --git a/poincare/include/poincare/dynamic_hierarchy.h b/poincare/include/poincare/dynamic_hierarchy.h index 2c94e079c..437ca624b 100644 --- a/poincare/include/poincare/dynamic_hierarchy.h +++ b/poincare/include/poincare/dynamic_hierarchy.h @@ -1,12 +1,12 @@ #ifndef POINCARE_DYNAMIC_HIERARCHY_H #define POINCARE_DYNAMIC_HIERARCHY_H -#include +#include #include namespace Poincare { -class DynamicHierarchy : public Hierarchy { +class DynamicHierarchy : public Expression { public: DynamicHierarchy(); DynamicHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands = true); diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index df9a1f6a2..318d3b1b6 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -167,15 +167,20 @@ public: static bool shouldStopProcessing(); /* Hierarchy */ - virtual const Expression * operand(int i) const = 0; + virtual const Expression * const * operands() const = 0; + const Expression * operand(int i) const; Expression * editableOperand(int i) { return const_cast(operand(i)); } virtual int numberOfOperands() const = 0; + + Expression * replaceWith(Expression * newOperand, bool deleteAfterReplace = true); + void replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand = true); + void detachOperand(const Expression * e); // Removes an operand WITHOUT deleting it + void detachOperands(); // Removes all operands WITHOUT deleting them + void swapOperands(int i, int j); + Expression * parent() const { return m_parent; } void setParent(Expression * parent) { m_parent = parent; } bool hasAncestor(const Expression * e) const; - virtual void replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand = true) = 0; - Expression * replaceWith(Expression * newOperand, bool deleteAfterReplace = true); - virtual void swapOperands(int i, int j) = 0; /* Properties */ enum class Sign { @@ -214,6 +219,9 @@ public: protected: /* Constructor */ Expression() : m_parent(nullptr) {} + static const Expression * const * ExpressionArray(const Expression * e1, const Expression * e2); + /* Hierarchy */ + void detachOperandAtIndex(int i); /* Evaluation Engine */ typedef float SinglePrecision; typedef double DoublePrecision; diff --git a/poincare/include/poincare/hierarchy.h b/poincare/include/poincare/hierarchy.h deleted file mode 100644 index 43f6769b0..000000000 --- a/poincare/include/poincare/hierarchy.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef POINCARE_HIERARCHY_H -#define POINCARE_HIERARCHY_H - -#include - -namespace Poincare { - -class Hierarchy : public Expression { -public: - using Expression::Expression; - const Expression * operand(int i) const override; - void swapOperands(int i, int j) override; - void replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand = true) override; - void detachOperand(const Expression * e); // Removes an operand WITHOUT deleting it - void detachOperands(); // Removes all operands WITHOUT deleting them - virtual const Expression * const * operands() const = 0; -protected: - static const Expression * const * ExpressionArray(const Expression * e1, const Expression * e2); -private: - void detachOperandAtIndex(int i); -}; - -} - -#endif diff --git a/poincare/include/poincare/static_hierarchy.h b/poincare/include/poincare/static_hierarchy.h index 26c2e33a7..24edcd9d0 100644 --- a/poincare/include/poincare/static_hierarchy.h +++ b/poincare/include/poincare/static_hierarchy.h @@ -1,13 +1,13 @@ #ifndef POINCARE_STATIC_HIERARCHY_H #define POINCARE_STATIC_HIERARCHY_H -#include +#include #include namespace Poincare { template -class StaticHierarchy : public Hierarchy { +class StaticHierarchy : public Expression { public: StaticHierarchy(); StaticHierarchy(const Expression * const * operands, bool cloneOperands = true); diff --git a/poincare/src/dynamic_hierarchy.cpp b/poincare/src/dynamic_hierarchy.cpp index d52a6079f..3aa805d7c 100644 --- a/poincare/src/dynamic_hierarchy.cpp +++ b/poincare/src/dynamic_hierarchy.cpp @@ -7,14 +7,14 @@ extern "C" { namespace Poincare { DynamicHierarchy::DynamicHierarchy() : - Hierarchy(), + Expression(), m_operands(nullptr), m_numberOfOperands(0) { } DynamicHierarchy::DynamicHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands) : - Hierarchy(), + Expression(), m_numberOfOperands(numberOfOperands) { assert(operands != nullptr); diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 06e605079..e30cccbed 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -40,6 +40,13 @@ Expression * Expression::parse(char const * string) { return expression; } +const Expression * const * Expression::ExpressionArray(const Expression * e1, const Expression * e2) { + static const Expression * result[2] = {nullptr, nullptr}; + result[0] = e1; + result[1] = e2; + return result; +} + /* Circuit breaker */ static Expression::CircuitBreaker sCircuitBreaker = nullptr; @@ -57,6 +64,76 @@ bool Expression::shouldStopProcessing() { /* Hierarchy */ +const Expression * Expression::operand(int i) const { + assert(i >= 0); + assert(i < numberOfOperands()); + assert(operands()[i]->parent() == nullptr || operands()[i]->parent() == this); + return operands()[i]; +} + +Expression * Expression::replaceWith(Expression * newOperand, bool deleteAfterReplace) { + assert(m_parent != nullptr); + m_parent->replaceOperand(this, newOperand, deleteAfterReplace); + return newOperand; +} + +void Expression::replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand) { + assert(newOperand != nullptr); + // Caution: handle the case where we replace an operand with a descendant of ours. + if (newOperand->hasAncestor(this)) { + newOperand->parent()->detachOperand(newOperand); + } + Expression ** op = const_cast(operands()); + for (int i=0; iparent() == this) { + const_cast(oldOperand)->setParent(nullptr); + } + if (deleteOldOperand) { + delete oldOperand; + } + if (newOperand != nullptr) { + const_cast(newOperand)->setParent(this); + } + op[i] = newOperand; + break; + } + } +} + +void Expression::detachOperand(const Expression * e) { + Expression ** op = const_cast(operands()); + for (int i=0; i(operands()); + // When detachOperands is called, it's very likely that said operands have been stolen + if (op[i] != nullptr && op[i]->parent() == this) { + const_cast(op[i])->setParent(nullptr); + } + op[i] = nullptr; +} + +void Expression::swapOperands(int i, int j) { + assert(i >= 0 && i < numberOfOperands()); + assert(j >= 0 && j < numberOfOperands()); + Expression ** op = const_cast(operands()); + Expression * temp = op[i]; + op[i] = op[j]; + op[j] = temp; +} + bool Expression::hasAncestor(const Expression * e) const { assert(m_parent != this); if (m_parent == e) { @@ -68,12 +145,6 @@ bool Expression::hasAncestor(const Expression * e) const { return m_parent->hasAncestor(e); } -Expression * Expression::replaceWith(Expression * newOperand, bool deleteAfterReplace) { - assert(m_parent != nullptr); - m_parent->replaceOperand(this, newOperand, deleteAfterReplace); - return newOperand; -} - /* Properties */ bool Expression::recursivelyMatches(ExpressionTest test) const { diff --git a/poincare/src/hierarchy.cpp b/poincare/src/hierarchy.cpp deleted file mode 100644 index a0adebaa6..000000000 --- a/poincare/src/hierarchy.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -extern "C" { -#include -} - -namespace Poincare { - -const Expression * Hierarchy::operand(int i) const { - assert(i >= 0); - assert(i < numberOfOperands()); - assert(operands()[i]->parent() == nullptr || operands()[i]->parent() == this); - return operands()[i]; -} - -void Hierarchy::swapOperands(int i, int j) { - assert(i >= 0 && i < numberOfOperands()); - assert(j >= 0 && j < numberOfOperands()); - Expression ** op = const_cast(operands()); - Expression * temp = op[i]; - op[i] = op[j]; - op[j] = temp; -} - -void Hierarchy::detachOperand(const Expression * e) { - Expression ** op = const_cast(operands()); - for (int i=0; ihasAncestor(this)) { - static_cast(newOperand->parent())->detachOperand(newOperand); - } - Expression ** op = const_cast(operands()); - for (int i=0; iparent() == this) { - const_cast(oldOperand)->setParent(nullptr); - } - if (deleteOldOperand) { - delete oldOperand; - } - if (newOperand != nullptr) { - const_cast(newOperand)->setParent(this); - } - op[i] = newOperand; - break; - } - } -} - -const Expression * const * Hierarchy::ExpressionArray(const Expression * e1, const Expression * e2) { - static const Expression * result[2] = {nullptr, nullptr}; - result[0] = e1; - result[1] = e2; - return result; -} - -void Hierarchy::detachOperandAtIndex(int i) { - Expression ** op = const_cast(operands()); - // When detachOperands is called, it's very likely that said operands have been stolen - if (op[i] != nullptr && op[i]->parent() == this) { - const_cast(op[i])->setParent(nullptr); - } - op[i] = nullptr; -} - -} diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 198ae01b1..ffac92d0d 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -417,8 +417,7 @@ void Multiplication::factorizeExponent(Expression * e1, Expression * e2, Context const Expression * base1 = e1->operand(0)->clone(); const Expression * base2 = e2->operand(0); - // TODO: remove cast, everything is a hierarchy - static_cast(e2)->detachOperand(base2); + e2->detachOperand(base2); Expression * m = new Multiplication(base1, base2, false); removeOperand(e2, true); e1->replaceOperand(e1->operand(0), m, true); diff --git a/poincare/src/static_hierarchy.cpp b/poincare/src/static_hierarchy.cpp index 62ec51d82..6716ecd12 100644 --- a/poincare/src/static_hierarchy.cpp +++ b/poincare/src/static_hierarchy.cpp @@ -7,14 +7,14 @@ namespace Poincare { template StaticHierarchy::StaticHierarchy() : - Hierarchy(), + Expression(), m_operands{} { } template StaticHierarchy::StaticHierarchy(const Expression * const * operands, bool cloneOperands) : - Hierarchy() + Expression() { build(operands, T, cloneOperands); } From 53ea983307632944a7de33db43a7ad02d46ff865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 11:53:55 +0100 Subject: [PATCH 28/77] [poincare] Read-me (first version) Change-Id: I4f88b79eb992c1a08149b555f14955c127a862dd --- poincare/README.txt | 111 +++++++++++++++++++++++----- poincare/include/poincare/complex.h | 4 + 2 files changed, 97 insertions(+), 18 deletions(-) diff --git a/poincare/README.txt b/poincare/README.txt index bb9e908a3..a7aedc554 100644 --- a/poincare/README.txt +++ b/poincare/README.txt @@ -1,20 +1,95 @@ -Things we will want to simplify: +# Poincare - 2.y.3 -> 6.y - y.x.3 + x^2 +1 -> x^2 + 3*x*y + 1 -Zeroes and ones - x + 0 -> x - 1*x -> x - x^1 -> x - x^0 -> 1 -Simplify fractions - (x^2+5.x+6)/(x+2) -> x+3 -Polynomials - (x+1)^2-x^2 -> 2*x+1 -Divisions - (2*x)/(x^2-1) - 1/(x-1) -> 1/(x+1) -Functional identities - ln(2x) - ln(x) -> ln(2) - y*sin^2(x) + y*cos^2(x) -> y +Expressions have a structure of tree, storing: +- Pointers to the operand expressions (ie children) +- A pointer to the parent expression +Expressions are typed (ie Type::Addition, Type::Multiplication...) and some expressions also hold values (ie Type::Rational). +Multiplication and Addition are the only type that can hold an infinite number of operands. Other expressions have a fixed number of operands (ie AbsoluteValue). -Note : The simplification process can be interrupted! -> It probably browses a graph +## Simplification + +Expression simplification is done in-place and modify directly the expression. +Simplify is a two-phase method: +- It first reduces the expression +- Then, it beautify the expression +So far, we excluded matrices from the simplificaiton process to avoid increasing complexity due to non commutativity in multiplications. + +### Order + +We define an simplification order on expressions with the following features: +- The order is total on types and values: + Rational(-2/3) < Rational(0) < ... < Multiplication < Power < Addition < ... +- The order relationship is depth-first recursive: if two expressions are equal in type (and values), we compare their operands starting with the last. +To compare two expressions, we first sort their commutative children to ensure the unicity of expression representations. This guarantee that the order is total on expressions. + +Moreover, the simplification order have some other specificities for some types: +- for addition and multiplication, any rational operand is always the first operands after sorting (by commutativity). +- Comparing an addition expression with an expression of different type called e is equivalent to comparing the addition with an addition with a single operand e. +- Idem for multiplications. +- To compare a power expression with an expression of different type called e, we compare the power with e^1. +The order groups like terms together to avoid quadratic complexity when factorizing addition or multiplication. For example, it groups terms with same bases together (ie Pi, Pi^3) and with same non-rational factors together (ie Pi, 2*Pi). + +Last comment; as the order is total, we can easily check if two expressions are identical by using this order. + + +### Reduce + +The reducing phase is the most important part of simplification. +It happens recursively and bottom-up: we first reduce the operands of an expression before reducing the expression itself. That way, when reducing itself, an expression can assert that its operands are reduced (and thus have some properties explained in the following). +Every type of expression has its own rules of reducing. + +To decrease the pool of possible expression types in reduced expressions, we converte subtraction to addition, division to power etc ... at reduction: +a-b -> a+(-1)*b +a/b -> a*b^(-1) +-a -> (-1)*a +sqrt(x) -> x^(1/2) +root(x,y) -> x^(1/y) +ln(x) -> log(x,e) + +Here is a short tour of shallow reduction for the main types: + +1. Additions are reduced by applying mathematics rules, ie: +- Associativity: (a+b)+c -> a+b+c +- Commutativity: a+b -> b+c which enables to sort operands and group like-terms together +- Factorization: a+5a -> 6a +- a+0->a +- We also reduce addition to same denominator. + +2. Multiplications apply the following rules: +- Associativity +- Commutativity (which is true because we do no reduce matrices yet) +- a*0 -> 0 +- Factorization +- sin/cos -> tan +- Distribution: a*(b+c) -> ab+ac + +3. Powers apply the following rules: +- We get rid of square roots at denominator and of sum of 2 square roots at denominator +- x^0-> 1 if x != 0 +- x^1 -> x +- 0^x -> 0 if x>0, undefined otherwise +- 1^x +- (a^b)^c -> a^(b+c) if a > 0 or c is integer +- (a*b*c*...)^n = a^n*b^n*c^n*... if n integer +- (a*b*...)^r -> |a|^r*(sign(a)*b*...)^r if a rational +- a^(b+c) -> (a^b)*a^c with a and b rational +- r^s with r, s rationals can be simplified using the factorisation in primes of r (ie, 8^(1/2) -> 2*2^(1/2)) +- i^(p/q)-> exp^(i*Pi*p/(2q)) with p, q integers +- exp^(i*Pi*p/q) -> cos(Pi*p/q)+i*sin(Pi*p/q) with p, q integers +- x^log(y,x)->y if y > 0 +- 10^log(y) + +To avoid infinite loop, reduction is contextualized on the parent expression (ie, reducing addition to same denominator). This forces to reduce an expression only once it is included in a expression. + +### Beautify + +This phase turns expressions in a more readable way. Division, subtraction, Naperian logarithm reappear at this step. Parenthesis are also added to be able to print the tree in infix notation without any ambiguity. +This phase is also recursive and top-down: we first beautify the node expression and the beautify its operands. + +## Approximation + +Expressions can be approximate which return another (dynamically allocated) expression that can be either: +- a complex +- a matrix of complex + +To approximate an expression, we first approximate its operands (which are ensured to be either complex or matrix of complex) and then approximate the expression using the operand approximations depending on its type. diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index a61500a03..60bc090e3 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -47,6 +47,10 @@ public: Complex * clone() const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Simplification: complex does not implement simplificationOrderSameType + * because Complex expressions do not appear before evaluation. The sorting + * step is part of simplificaiton process which thus handles no complex. */ + /* The parameter 'DisplayMode' refers to the way to display float 'scientific' * or 'auto'. The scientific mode returns float with style -1.2E2 whereas * the auto mode tries to return 'natural' float like (0.021) and switches From 76cbd2678d7adca86a178ea241cc74b4521eeeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 12:06:35 +0100 Subject: [PATCH 29/77] [poincare] Change names: approximate->approximateToScalar Change-Id: I701451b35909bb577dd729e0ea76a405b9543f23 --- apps/graph/cartesian_function.cpp | 2 +- apps/probability/calculation_controller.cpp | 2 +- apps/sequence/sequence.cpp | 18 +++++++++--------- .../editable_cell_table_view_controller.cpp | 2 +- apps/shared/float_parameter_controller.cpp | 2 +- apps/shared/function.cpp | 2 +- poincare/include/poincare/expression.h | 4 ++-- poincare/src/expression.cpp | 14 +++++++------- poincare/src/expression_debug.cpp | 2 +- poincare/src/power.cpp | 4 ++-- poincare/src/trigonometry.cpp | 8 ++++---- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/graph/cartesian_function.cpp b/apps/graph/cartesian_function.cpp index 1f7077865..50359d6ec 100644 --- a/apps/graph/cartesian_function.cpp +++ b/apps/graph/cartesian_function.cpp @@ -20,7 +20,7 @@ double CartesianFunction::approximateDerivative(double x, Poincare::Context * co Poincare::Complex abscissa = Poincare::Complex::Float(x); Poincare::Expression * args[2] = {expression(), &abscissa}; Poincare::Derivative derivative(args, true); - return derivative.approximate(*context); + return derivative.approximateToScalar(*context); } char CartesianFunction::symbol() const { diff --git a/apps/probability/calculation_controller.cpp b/apps/probability/calculation_controller.cpp index c0fbbcc8f..be820cc8e 100644 --- a/apps/probability/calculation_controller.cpp +++ b/apps/probability/calculation_controller.cpp @@ -218,7 +218,7 @@ bool CalculationController::textFieldShouldFinishEditing(TextField * textField, bool CalculationController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { App * probaApp = (App *)app(); Context * globalContext = probaApp->container()->globalContext(); - double floatBody = Expression::approximate(text, *globalContext); + double floatBody = Expression::approximateToScalar(text, *globalContext); if (std::isnan(floatBody) || std::isinf(floatBody)) { app()->displayWarning(I18n::Message::UndefinedValue); return false; diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index 2e8b1aca0..3f1089d03 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -282,18 +282,18 @@ T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const } if (n == 0) { setBufferIndexValue(0,0); - setBufferValue(firstInitialConditionExpression()->approximate(*context), 0); + setBufferValue(firstInitialConditionExpression()->approximateToScalar(*context), 0); return bufferValue(0); } LocalContext subContext = LocalContext(context); Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) < 0 || indexBuffer(0) > n ? 0 : indexBuffer(0); - T un = indexBuffer(0) < 0 || indexBuffer(0) > n ? firstInitialConditionExpression()->approximate(*context) : bufferValue(0); + T un = indexBuffer(0) < 0 || indexBuffer(0) > n ? firstInitialConditionExpression()->approximateToScalar(*context) : bufferValue(0); for (int i = start; i < n; i++) { subContext.setValueForSequenceRank(un, name(), 0); Poincare::Complex e = Poincare::Complex::Float(i); subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); - un = expression()->approximate(subContext); + un = expression()->approximateToScalar(subContext); } setBufferValue(un, 0); setBufferIndexValue(n, 0); @@ -305,27 +305,27 @@ T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const return NAN; } if (n == 0) { - return firstInitialConditionExpression()->approximate(*context); + return firstInitialConditionExpression()->approximateToScalar(*context); } if (n == 1) { setBufferIndexValue(0, 0); - setBufferValue(firstInitialConditionExpression()->approximate(*context), 0); + setBufferValue(firstInitialConditionExpression()->approximateToScalar(*context), 0); setBufferIndexValue(1, 1); - setBufferValue(secondInitialConditionExpression()->approximate(*context), 1); + setBufferValue(secondInitialConditionExpression()->approximateToScalar(*context), 1); return bufferValue(1); } LocalContext subContext = LocalContext(context); Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? indexBuffer(0) : 0; - T un = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(0) : firstInitialConditionExpression()->approximate(*context); - T un1 = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(1) : secondInitialConditionExpression()->approximate(*context); + T un = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(0) : firstInitialConditionExpression()->approximateToScalar(*context); + T un1 = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(1) : secondInitialConditionExpression()->approximateToScalar(*context); for (int i = start; i < n-1; i++) { subContext.setValueForSequenceRank(un, name(), 0); subContext.setValueForSequenceRank(un1, name(), 1); Poincare::Complex e = Poincare::Complex::Float(i); subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); un = un1; - un1 = expression()->approximate(subContext); + un1 = expression()->approximateToScalar(subContext); } setBufferValue(un, 0); setBufferIndexValue(n-1, 0); diff --git a/apps/shared/editable_cell_table_view_controller.cpp b/apps/shared/editable_cell_table_view_controller.cpp index 18337bcbe..3ef8b3eaa 100644 --- a/apps/shared/editable_cell_table_view_controller.cpp +++ b/apps/shared/editable_cell_table_view_controller.cpp @@ -24,7 +24,7 @@ bool EditableCellTableViewController::textFieldShouldFinishEditing(TextField * t bool EditableCellTableViewController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); Context * globalContext = appsContainer->globalContext(); - double floatBody = Expression::approximate(text, *globalContext); + double floatBody = Expression::approximateToScalar(text, *globalContext); if (std::isnan(floatBody) || std::isinf(floatBody)) { app()->displayWarning(I18n::Message::UndefinedValue); return false; diff --git a/apps/shared/float_parameter_controller.cpp b/apps/shared/float_parameter_controller.cpp index 04f925f6c..f4cc97c54 100644 --- a/apps/shared/float_parameter_controller.cpp +++ b/apps/shared/float_parameter_controller.cpp @@ -119,7 +119,7 @@ bool FloatParameterController::textFieldShouldFinishEditing(TextField * textFiel bool FloatParameterController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { AppsContainer * appsContainer = ((TextFieldDelegateApp *)app())->container(); Context * globalContext = appsContainer->globalContext(); - double floatBody = Expression::approximate(text, *globalContext); + double floatBody = Expression::approximateToScalar(text, *globalContext); if (std::isnan(floatBody) || std::isinf(floatBody)) { app()->displayWarning(I18n::Message::UndefinedValue); return false; diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index 36bca4846..ffe6b113b 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -105,7 +105,7 @@ T Function::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const Poincare::Symbol xSymbol(symbol()); Poincare::Complex e = Poincare::Complex::Float(x); variableContext.setExpressionForSymbolName(&e, &xSymbol, variableContext); - return expression()->approximate(variableContext); + return expression()->approximateToScalar(variableContext); } void Function::tidy() { diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 318d3b1b6..7cef1dbec 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -214,8 +214,8 @@ public: * The function evaluate creates a new expression and thus mallocs memory. * Do not forget to delete the new expression to avoid leaking. */ template Expression * evaluate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; - template T approximate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; - template static T approximate(const char * text, Context& context, AngleUnit angleUnit = AngleUnit::Default); + template T approximateToScalar(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; + template static T approximateToScalar(const char * text, Context& context, AngleUnit angleUnit = AngleUnit::Default); protected: /* Constructor */ Expression() : m_parent(nullptr) {} diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index e30cccbed..36d3f1544 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -262,7 +262,7 @@ template Expression * Expression::evaluate(Context& context, AngleUn } } -template T Expression::approximate(Context& context, AngleUnit angleUnit) const { +template T Expression::approximateToScalar(Context& context, AngleUnit angleUnit) const { Expression * evaluation = evaluate(context, angleUnit); assert(evaluation->type() == Type::Complex || evaluation->type() == Type::Matrix); T result = NAN; @@ -278,9 +278,9 @@ template T Expression::approximate(Context& context, AngleUnit angle return result; } -template T Expression::approximate(const char * text, Context& context, AngleUnit angleUnit) { +template T Expression::approximateToScalar(const char * text, Context& context, AngleUnit angleUnit) { Expression * exp = parse(text); - T result = exp->approximate(context, angleUnit); + T result = exp->approximateToScalar(context, angleUnit); delete exp; return result; } @@ -294,9 +294,9 @@ template T Expression::epsilon() { template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; -template double Poincare::Expression::approximate(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); -template float Poincare::Expression::approximate(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); -template double Poincare::Expression::approximate(Poincare::Context&, Poincare::Expression::AngleUnit) const; -template float Poincare::Expression::approximate(Poincare::Context&, Poincare::Expression::AngleUnit) const; +template double Poincare::Expression::approximateToScalar(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); +template float Poincare::Expression::approximateToScalar(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); +template double Poincare::Expression::approximateToScalar(Poincare::Context&, Poincare::Expression::AngleUnit) const; +template float Poincare::Expression::approximateToScalar(Poincare::Context&, Poincare::Expression::AngleUnit) const; template double Poincare::Expression::epsilon(); template float Poincare::Expression::epsilon(); diff --git a/poincare/src/expression_debug.cpp b/poincare/src/expression_debug.cpp index 527317f2f..956522d00 100644 --- a/poincare/src/expression_debug.cpp +++ b/poincare/src/expression_debug.cpp @@ -60,7 +60,7 @@ void print_expression(const Expression * e, int indentationLevel) { break; case Expression::Type::Decimal: std::cout << "Decimal("; - std::cout << e->approximate(context, Expression::AngleUnit::Radian); + std::cout << e->approximateToScalar(context, Expression::AngleUnit::Radian); std::cout << ")"; break; case Expression::Type::Derivative: diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index cef3247cc..0566c1041 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -240,8 +240,8 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { } // p^q with p, q rationals if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { - double p = a->approximate(context, angleUnit); - double q = operand(1)->approximate(context, angleUnit); + double p = a->approximateToScalar(context, angleUnit); + double q = operand(1)->approximateToScalar(context, angleUnit); double approx = std::pow(std::fabs(p), std::fabs(q)); if (std::isinf(approx) || std::isnan(approx) || std::fabs(approx)> 1E100) { return this; diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 753f25b75..633029590 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -22,7 +22,7 @@ Expression * Trigonometry::shallowReduceDirectFunction(Expression * e, Context& } Expression::Type correspondingType = e->type() == Expression::Type::Cosine ? Expression::Type::ArcCosine : (e->type() == Expression::Type::Sine ? Expression::Type::ArcSine : Expression::Type::ArcTangent); if (e->operand(0)->type() == correspondingType) { - float trigoOp = e->operand(0)->operand(0)->approximate(context, angleUnit); + float trigoOp = e->operand(0)->operand(0)->approximateToScalar(context, angleUnit); if (e->type() == Expression::Type::Tangent || (trigoOp >= -1.0f && trigoOp <= 1.0f)) { return e->replaceWith(e->editableOperand(0)->editableOperand(0), true); } @@ -83,7 +83,7 @@ bool Trigonometry::ExpressionIsEquivalentToTangent(const Expression * e) { Expression * Trigonometry::shallowReduceInverseFunction(Expression * e, Context& context, Expression::AngleUnit angleUnit) { assert(e->type() == Expression::Type::ArcCosine || e->type() == Expression::Type::ArcSine || e->type() == Expression::Type::ArcTangent); if (e->type() != Expression::Type::ArcTangent) { - float approxOp = e->operand(0)->approximate(context, angleUnit); + float approxOp = e->operand(0)->approximateToScalar(context, angleUnit); if (approxOp > 1.0f || approxOp < -1.0f) { return e->replaceWith(new Undefined(), true); } @@ -91,7 +91,7 @@ Expression * Trigonometry::shallowReduceInverseFunction(Expression * e, Context& Expression::Type correspondingType = e->type() == Expression::Type::ArcCosine ? Expression::Type::Cosine : (e->type() == Expression::Type::ArcSine ? Expression::Type::Sine : Expression::Type::Tangent); float pi = angleUnit == Expression::AngleUnit::Radian ? M_PI : 180; if (e->operand(0)->type() == correspondingType) { - float trigoOp = e->operand(0)->operand(0)->approximate(context, angleUnit); + float trigoOp = e->operand(0)->operand(0)->approximateToScalar(context, angleUnit); if ((e->type() == Expression::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) || (e->type() == Expression::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) || (e->type() == Expression::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) { @@ -100,7 +100,7 @@ Expression * Trigonometry::shallowReduceInverseFunction(Expression * e, Context& } // Special case for arctan(sin(x)/cos(x)) if (e->type() == Expression::Type::ArcTangent && ExpressionIsEquivalentToTangent(e->operand(0))) { - float trigoOp = e->operand(0)->operand(1)->operand(0)->approximate(context, angleUnit); + float trigoOp = e->operand(0)->operand(1)->operand(0)->approximateToScalar(context, angleUnit); if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { return e->replaceWith(e->editableOperand(0)->editableOperand(1)->editableOperand(0), true); } From fda382cc0d52adbe268ebcdf5b386374c42b88e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 13:25:27 +0100 Subject: [PATCH 30/77] [poincare] Change name: evaluate->approximate Change-Id: I649a7c640190713dcf063a4148decd6038e62796 --- apps/calculation/calculation.cpp | 4 +-- apps/probability/law/binomial_law.cpp | 6 ++-- apps/probability/law/binomial_law.h | 6 ++-- apps/probability/law/poisson_law.cpp | 6 ++-- apps/probability/law/poisson_law.h | 6 ++-- apps/sequence/sequence.cpp | 2 +- apps/sequence/sequence.h | 6 ++-- apps/shared/function.cpp | 6 ++-- apps/shared/function.h | 6 ++-- poincare/Makefile | 2 +- poincare/include/poincare/absolute_value.h | 10 +++--- poincare/include/poincare/addition.h | 16 +++++----- ...uation_engine.h => approximation_engine.h} | 6 ++-- poincare/include/poincare/arc_cosine.h | 10 +++--- poincare/include/poincare/arc_sine.h | 10 +++--- poincare/include/poincare/arc_tangent.h | 10 +++--- .../include/poincare/binomial_coefficient.h | 6 ++-- poincare/include/poincare/ceiling.h | 10 +++--- poincare/include/poincare/complex.h | 6 ++-- poincare/include/poincare/complex_argument.h | 10 +++--- .../include/poincare/confidence_interval.h | 6 ++-- poincare/include/poincare/conjugate.h | 10 +++--- poincare/include/poincare/cosine.h | 10 +++--- poincare/include/poincare/decimal.h | 6 ++-- poincare/include/poincare/derivative.h | 6 ++-- poincare/include/poincare/determinant.h | 6 ++-- poincare/include/poincare/division.h | 12 +++---- poincare/include/poincare/division_quotient.h | 6 ++-- .../include/poincare/division_remainder.h | 6 ++-- poincare/include/poincare/expression.h | 8 ++--- poincare/include/poincare/factorial.h | 10 +++--- poincare/include/poincare/floor.h | 10 +++--- poincare/include/poincare/frac_part.h | 10 +++--- .../include/poincare/great_common_divisor.h | 6 ++-- .../include/poincare/hyperbolic_arc_cosine.h | 10 +++--- .../include/poincare/hyperbolic_arc_sine.h | 10 +++--- .../include/poincare/hyperbolic_arc_tangent.h | 10 +++--- poincare/include/poincare/hyperbolic_cosine.h | 10 +++--- poincare/include/poincare/hyperbolic_sine.h | 10 +++--- .../include/poincare/hyperbolic_tangent.h | 10 +++--- poincare/include/poincare/imaginary_part.h | 10 +++--- poincare/include/poincare/integral.h | 6 ++-- .../include/poincare/least_common_multiple.h | 6 ++-- poincare/include/poincare/logarithm.h | 6 ++-- poincare/include/poincare/matrix.h | 6 ++-- poincare/include/poincare/matrix_dimension.h | 6 ++-- poincare/include/poincare/matrix_inverse.h | 6 ++-- poincare/include/poincare/matrix_trace.h | 6 ++-- poincare/include/poincare/matrix_transpose.h | 6 ++-- poincare/include/poincare/multiplication.h | 14 ++++---- .../include/poincare/naperian_logarithm.h | 10 +++--- poincare/include/poincare/nth_root.h | 6 ++-- poincare/include/poincare/opposite.h | 10 +++--- poincare/include/poincare/parenthesis.h | 6 ++-- .../include/poincare/permute_coefficient.h | 6 ++-- poincare/include/poincare/power.h | 10 +++--- .../include/poincare/prediction_interval.h | 6 ++-- poincare/include/poincare/product.h | 6 ++-- poincare/include/poincare/rational.h | 6 ++-- poincare/include/poincare/real_part.h | 10 +++--- poincare/include/poincare/round.h | 6 ++-- poincare/include/poincare/sequence.h | 8 ++--- .../include/poincare/simplification_root.h | 4 +-- poincare/include/poincare/sine.h | 10 +++--- poincare/include/poincare/square_root.h | 10 +++--- poincare/include/poincare/store.h | 6 ++-- poincare/include/poincare/subtraction.h | 14 ++++---- poincare/include/poincare/sum.h | 6 ++-- poincare/include/poincare/symbol.h | 6 ++-- poincare/include/poincare/tangent.h | 10 +++--- poincare/include/poincare/undefined.h | 6 ++-- ...on_engine.cpp => approximation_engine.cpp} | 32 +++++++++---------- poincare/src/binomial_coefficient.cpp | 6 ++-- poincare/src/complex.cpp | 10 +++--- poincare/src/confidence_interval.cpp | 6 ++-- poincare/src/decimal.cpp | 2 +- poincare/src/derivative.cpp | 16 +++++----- poincare/src/determinant.cpp | 4 +-- poincare/src/division_quotient.cpp | 6 ++-- poincare/src/division_remainder.cpp | 6 ++-- poincare/src/expression.cpp | 12 +++---- poincare/src/global_context.cpp | 4 +-- poincare/src/great_common_divisor.cpp | 6 ++-- poincare/src/integral.cpp | 8 ++--- poincare/src/least_common_multiple.cpp | 6 ++-- poincare/src/logarithm.cpp | 10 +++--- poincare/src/matrix.cpp | 4 +-- poincare/src/matrix_dimension.cpp | 4 +-- poincare/src/matrix_inverse.cpp | 4 +-- poincare/src/matrix_trace.cpp | 4 +-- poincare/src/matrix_transpose.cpp | 4 +-- poincare/src/nth_root.cpp | 6 ++-- poincare/src/parenthesis.cpp | 4 +-- poincare/src/permute_coefficient.cpp | 6 ++-- poincare/src/power.cpp | 4 +-- poincare/src/prediction_interval.cpp | 6 ++-- poincare/src/product.cpp | 2 +- poincare/src/rational.cpp | 2 +- poincare/src/round.cpp | 6 ++-- poincare/src/sequence.cpp | 8 ++--- .../simplification/expression_simplify.cpp | 4 +-- poincare/src/store.cpp | 4 +-- poincare/src/sum.cpp | 2 +- poincare/src/symbol.cpp | 4 +-- poincare/src/undefined.cpp | 2 +- poincare/test/complex.cpp | 4 +-- poincare/test/helper.cpp | 2 +- 107 files changed, 385 insertions(+), 385 deletions(-) rename poincare/include/poincare/{evaluation_engine.h => approximation_engine.h} (93%) rename poincare/src/{evaluation_engine.cpp => approximation_engine.cpp} (50%) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 72a9ba3cf..399e6dd89 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -70,7 +70,7 @@ void Calculation::setContent(const char * c, Context * context) { m_exactOutput = input()->clone(); Expression::Simplify(&m_exactOutput, *context); m_exactOutput->writeTextInBuffer(m_exactOutputText, sizeof(m_exactOutputText)); - m_approximateOutput = m_exactOutput->evaluate(*context); + m_approximateOutput = m_exactOutput->approximate(*context); m_approximateOutput->writeTextInBuffer(m_approximateOutputText, sizeof(m_approximateOutputText)); } @@ -189,7 +189,7 @@ Expression * Calculation::approximateOutput(Context * context) { * call 'evaluate'. */ Expression * exp = Expression::parse(m_approximateOutputText); if (exp != nullptr) { - m_approximateOutput = exp->evaluate(*context); + m_approximateOutput = exp->approximate(*context); delete exp; } else { m_approximateOutput = new Complex(Complex::Float(NAN)); diff --git a/apps/probability/law/binomial_law.cpp b/apps/probability/law/binomial_law.cpp index b1adcdee3..5f973b4b4 100644 --- a/apps/probability/law/binomial_law.cpp +++ b/apps/probability/law/binomial_law.cpp @@ -102,7 +102,7 @@ double BinomialLaw::rightIntegralInverseForProbability(double * probability) { } template -T BinomialLaw::templatedEvaluateAtAbscissa(T x) const { +T BinomialLaw::templatedApproximateAtAbscissa(T x) const { if (m_parameter1 == 0) { if (m_parameter2 == 0 || m_parameter2 == 1) { return NAN; @@ -134,5 +134,5 @@ T BinomialLaw::templatedEvaluateAtAbscissa(T x) const { } -template float Probability::BinomialLaw::templatedEvaluateAtAbscissa(float x) const; -template double Probability::BinomialLaw::templatedEvaluateAtAbscissa(double x) const; +template float Probability::BinomialLaw::templatedApproximateAtAbscissa(float x) const; +template double Probability::BinomialLaw::templatedApproximateAtAbscissa(double x) const; diff --git a/apps/probability/law/binomial_law.h b/apps/probability/law/binomial_law.h index 2e81fa6cd..54efc013a 100644 --- a/apps/probability/law/binomial_law.h +++ b/apps/probability/law/binomial_law.h @@ -18,16 +18,16 @@ public: I18n::Message parameterNameAtIndex(int index) override; I18n::Message parameterDefinitionAtIndex(int index) override; float evaluateAtAbscissa(float x) const override { - return templatedEvaluateAtAbscissa(x); + return templatedApproximateAtAbscissa(x); } bool authorizedValueAtIndex(float x, int index) const override; double cumulativeDistributiveInverseForProbability(double * probability) override; double rightIntegralInverseForProbability(double * probability) override; protected: double evaluateAtDiscreteAbscissa(int k) const override { - return templatedEvaluateAtAbscissa((double)k); + return templatedApproximateAtAbscissa((double)k); } - template T templatedEvaluateAtAbscissa(T x) const; + template T templatedApproximateAtAbscissa(T x) const; }; } diff --git a/apps/probability/law/poisson_law.cpp b/apps/probability/law/poisson_law.cpp index fe1369d32..4cafce3e4 100644 --- a/apps/probability/law/poisson_law.cpp +++ b/apps/probability/law/poisson_law.cpp @@ -63,7 +63,7 @@ bool PoissonLaw::authorizedValueAtIndex(float x, int index) const { } template -T PoissonLaw::templatedEvaluateAtAbscissa(T x) const { +T PoissonLaw::templatedApproximateAtAbscissa(T x) const { if (x < 0) { return NAN; } @@ -73,5 +73,5 @@ T PoissonLaw::templatedEvaluateAtAbscissa(T x) const { } -template float Probability::PoissonLaw::templatedEvaluateAtAbscissa(float x) const; -template double Probability::PoissonLaw::templatedEvaluateAtAbscissa(double x) const; +template float Probability::PoissonLaw::templatedApproximateAtAbscissa(float x) const; +template double Probability::PoissonLaw::templatedApproximateAtAbscissa(double x) const; diff --git a/apps/probability/law/poisson_law.h b/apps/probability/law/poisson_law.h index d78399e51..3ae372a1b 100644 --- a/apps/probability/law/poisson_law.h +++ b/apps/probability/law/poisson_law.h @@ -18,14 +18,14 @@ public: I18n::Message parameterNameAtIndex(int index) override; I18n::Message parameterDefinitionAtIndex(int index) override; float evaluateAtAbscissa(float x) const override { - return templatedEvaluateAtAbscissa(x); + return templatedApproximateAtAbscissa(x); } bool authorizedValueAtIndex(float x, int index) const override; private: double evaluateAtDiscreteAbscissa(int k) const override { - return templatedEvaluateAtAbscissa((double)k); + return templatedApproximateAtAbscissa((double)k); } - template T templatedEvaluateAtAbscissa(T x) const; + template T templatedApproximateAtAbscissa(T x) const; }; } diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index 3f1089d03..7993e8c03 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -267,7 +267,7 @@ bool Sequence::isEmpty() { } template -T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const { +T Sequence::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const { T n = std::round(x); switch (m_type) { case Type::Explicite: diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index c14687fbf..6cc16b383 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -39,10 +39,10 @@ public: bool isDefined() override; bool isEmpty() override; float evaluateAtAbscissa(float x, Poincare::Context * context) const override { - return templatedEvaluateAtAbscissa(x, context); + return templatedApproximateAtAbscissa(x, context); } double evaluateAtAbscissa(double x, Poincare::Context * context) const override { - return templatedEvaluateAtAbscissa(x, context); + return templatedApproximateAtAbscissa(x, context); } double sumOfTermsBetweenAbscissa(double start, double end, Poincare::Context * context); void tidy() override; @@ -52,7 +52,7 @@ private: constexpr static size_t k_dataLengthInBytes = (3*TextField::maxBufferSize()+3)*sizeof(char)+1; static_assert((k_dataLengthInBytes & 0x3) == 0, "The sequence data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4 char symbol() const override; - template T templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const; + template T templatedApproximateAtAbscissa(T x, Poincare::Context * context) const; Type m_type; char m_firstInitialConditionText[TextField::maxBufferSize()]; char m_secondInitialConditionText[TextField::maxBufferSize()]; diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index ffe6b113b..a4e64108d 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -100,7 +100,7 @@ bool Function::isEmpty() { } template -T Function::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const { +T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const { Poincare::VariableContext variableContext = Poincare::VariableContext(symbol(), context); Poincare::Symbol xSymbol(symbol()); Poincare::Complex e = Poincare::Complex::Float(x); @@ -121,5 +121,5 @@ void Function::tidy() { } -template float Shared::Function::templatedEvaluateAtAbscissa(float, Poincare::Context*) const; -template double Shared::Function::templatedEvaluateAtAbscissa(double, Poincare::Context*) const; +template float Shared::Function::templatedApproximateAtAbscissa(float, Poincare::Context*) const; +template double Shared::Function::templatedApproximateAtAbscissa(double, Poincare::Context*) const; diff --git a/apps/shared/function.h b/apps/shared/function.h index 2e6303bfe..6d3adc71f 100644 --- a/apps/shared/function.h +++ b/apps/shared/function.h @@ -28,16 +28,16 @@ public: virtual void setContent(const char * c); void setColor(KDColor m_color); virtual float evaluateAtAbscissa(float x, Poincare::Context * context) const { - return templatedEvaluateAtAbscissa(x, context); + return templatedApproximateAtAbscissa(x, context); } virtual double evaluateAtAbscissa(double x, Poincare::Context * context) const { - return templatedEvaluateAtAbscissa(x, context); + return templatedApproximateAtAbscissa(x, context); } virtual void tidy(); private: constexpr static size_t k_dataLengthInBytes = (TextField::maxBufferSize()+2)*sizeof(char)+2; static_assert((k_dataLengthInBytes & 0x3) == 0, "The function data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4 - template T templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const; + template T templatedApproximateAtAbscissa(T x, Poincare::Context * context) const; virtual char symbol() const = 0; mutable Poincare::Expression * m_expression; char m_text[TextField::maxBufferSize()]; diff --git a/poincare/Makefile b/poincare/Makefile index 80bf9006c..a4bd1d233 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -25,7 +25,7 @@ objs += $(addprefix poincare/src/,\ division_quotient.o\ division_remainder.o\ dynamic_hierarchy.o\ - evaluation_engine.o\ + approximation_engine.o\ expression.o\ expression_lexer.o\ expression_parser.o\ diff --git a/poincare/include/poincare/absolute_value.h b/poincare/include/poincare/absolute_value.h index e4a6030d5..870facc18 100644 --- a/poincare/include/poincare/absolute_value.h +++ b/poincare/include/poincare/absolute_value.h @@ -2,7 +2,7 @@ #define POINCARE_ABSOLUTE_VALUE_H #include -#include +#include #include namespace Poincare { @@ -24,11 +24,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index 9f2a6a9d5..7652bf7f5 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace Poincare { @@ -19,10 +19,10 @@ public: /* Evaluation */ template static Complex compute(const Complex c, const Complex d); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { - return EvaluationEngine::elementWiseOnComplexMatrices(m, n, compute); + return ApproximationEngine::elementWiseOnComplexMatrices(m, n, compute); } template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } private: /* Layout */ @@ -43,13 +43,13 @@ private: static bool TermsHaveIdenticalNonRationalFactors(const Expression * e1, const Expression * e2); /* Evaluation */ template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/evaluation_engine.h b/poincare/include/poincare/approximation_engine.h similarity index 93% rename from poincare/include/poincare/evaluation_engine.h rename to poincare/include/poincare/approximation_engine.h index 9d256c101..673aa5226 100644 --- a/poincare/include/poincare/evaluation_engine.h +++ b/poincare/include/poincare/approximation_engine.h @@ -1,5 +1,5 @@ -#ifndef POINCARE_EVALUATION_ENGINE_H -#define POINCARE_EVALUATION_ENGINE_H +#ifndef POINCARE_APPROXIMATION_ENGINE_H +#define POINCARE_APPROXIMATION_ENGINE_H #include #include @@ -7,7 +7,7 @@ namespace Poincare { -class EvaluationEngine { +class ApproximationEngine { public: template using ComplexCompute = Complex(*)(const Complex, Expression::AngleUnit angleUnit); template static Expression * map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute); diff --git a/poincare/include/poincare/arc_cosine.h b/poincare/include/poincare/arc_cosine.h index 81e29eb21..7b27c7e8a 100644 --- a/poincare/include/poincare/arc_cosine.h +++ b/poincare/include/poincare/arc_cosine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -27,11 +27,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/arc_sine.h b/poincare/include/poincare/arc_sine.h index dcceec881..d2d0737ca 100644 --- a/poincare/include/poincare/arc_sine.h +++ b/poincare/include/poincare/arc_sine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/arc_tangent.h b/poincare/include/poincare/arc_tangent.h index b9a34a536..d8fb48580 100644 --- a/poincare/include/poincare/arc_tangent.h +++ b/poincare/include/poincare/arc_tangent.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/binomial_coefficient.h b/poincare/include/poincare/binomial_coefficient.h index f0215e7c8..95e562c37 100644 --- a/poincare/include/poincare/binomial_coefficient.h +++ b/poincare/include/poincare/binomial_coefficient.h @@ -21,9 +21,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/ceiling.h b/poincare/include/poincare/ceiling.h index 3bd704e49..e0e1363e2 100644 --- a/poincare/include/poincare/ceiling.h +++ b/poincare/include/poincare/ceiling.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -23,11 +23,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index 60bc090e3..c5120e6a8 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -67,9 +67,9 @@ private: Complex(T a, T b); constexpr static int k_numberOfSignificantDigits = 7; ExpressionLayout * privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const override; - Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + Expression * privateApproximate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const; /* We here define the buffer size to write the lengthest float possible. * At maximum, the number has 7 significant digits so, in the worst case it * has the form -1.999999e-308 (7+7+1 char) (the auto mode is always diff --git a/poincare/include/poincare/complex_argument.h b/poincare/include/poincare/complex_argument.h index cee734982..6d7f03659 100644 --- a/poincare/include/poincare/complex_argument.h +++ b/poincare/include/poincare/complex_argument.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/confidence_interval.h b/poincare/include/poincare/confidence_interval.h index 72c26f5fd..269a47afd 100644 --- a/poincare/include/poincare/confidence_interval.h +++ b/poincare/include/poincare/confidence_interval.h @@ -23,9 +23,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/conjugate.h b/poincare/include/poincare/conjugate.h index 3bb1754d9..2e199020c 100644 --- a/poincare/include/poincare/conjugate.h +++ b/poincare/include/poincare/conjugate.h @@ -2,7 +2,7 @@ #define POINCARE_CONJUGATE_H #include -#include +#include #include namespace Poincare { @@ -22,11 +22,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h index 2c23dc3a9..0fd5e471d 100644 --- a/poincare/include/poincare/cosine.h +++ b/poincare/include/poincare/cosine.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include namespace Poincare { @@ -27,11 +27,11 @@ private: /* Simplication */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/decimal.h b/poincare/include/poincare/decimal.h index 72242b183..823e1c98d 100644 --- a/poincare/include/poincare/decimal.h +++ b/poincare/include/poincare/decimal.h @@ -37,9 +37,9 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; Expression * shallowBeautify(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const; constexpr static int k_maxLength = 10; Integer m_mantissa; diff --git a/poincare/include/poincare/derivative.h b/poincare/include/poincare/derivative.h index 351fed8a0..c88c18aa4 100644 --- a/poincare/include/poincare/derivative.h +++ b/poincare/include/poincare/derivative.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; template T growthRateAroundAbscissa(T x, T h, VariableContext variableContext, AngleUnit angleUnit) const; template T approximateDerivate2(T x, T h, VariableContext xContext, AngleUnit angleUnit) const; // TODO: Change coefficients? diff --git a/poincare/include/poincare/determinant.h b/poincare/include/poincare/determinant.h index 6bb607695..39ad84723 100644 --- a/poincare/include/poincare/determinant.h +++ b/poincare/include/poincare/determinant.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/division.h b/poincare/include/poincare/division.h index 631a2e094..8b114ef98 100644 --- a/poincare/include/poincare/division.h +++ b/poincare/include/poincare/division.h @@ -2,7 +2,7 @@ #define POINCARE_DIVISION_H #include -#include +#include #include namespace Poincare { @@ -27,16 +27,16 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n); - virtual Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + virtual Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - virtual Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + virtual Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/division_quotient.h b/poincare/include/poincare/division_quotient.h index 4c2171187..b9e84b9dd 100644 --- a/poincare/include/poincare/division_quotient.h +++ b/poincare/include/poincare/division_quotient.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/division_remainder.h b/poincare/include/poincare/division_remainder.h index 3c4b1dc87..8316ca63a 100644 --- a/poincare/include/poincare/division_remainder.h +++ b/poincare/include/poincare/division_remainder.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 7cef1dbec..a2150512d 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -72,7 +72,7 @@ class Expression { friend class SimplificationRoot; friend class Sequence; friend class Trigonometry; - friend class EvaluationEngine; + friend class ApproximationEngine; friend class SimplificationEngine; public: @@ -213,7 +213,7 @@ public: /* Evaluation Engine * The function evaluate creates a new expression and thus mallocs memory. * Do not forget to delete the new expression to avoid leaking. */ - template Expression * evaluate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; + template Expression * approximate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template T approximateToScalar(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template static T approximateToScalar(const char * text, Context& context, AngleUnit angleUnit = AngleUnit::Default); protected: @@ -267,8 +267,8 @@ private: return nullptr; } /* Evaluation Engine */ - virtual Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const = 0; - virtual Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const = 0; + virtual Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const = 0; + virtual Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const = 0; Expression * m_parent; }; diff --git a/poincare/include/poincare/factorial.h b/poincare/include/poincare/factorial.h index 97b80b185..da1109b2a 100644 --- a/poincare/include/poincare/factorial.h +++ b/poincare/include/poincare/factorial.h @@ -2,7 +2,7 @@ #define POINCARE_FACTORIAL_H #include -#include +#include namespace Poincare { @@ -14,11 +14,11 @@ public: private: constexpr static int k_maxOperandValue = 100; template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; diff --git a/poincare/include/poincare/floor.h b/poincare/include/poincare/floor.h index 8ad78242a..78bfa51f4 100644 --- a/poincare/include/poincare/floor.h +++ b/poincare/include/poincare/floor.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -23,11 +23,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/frac_part.h b/poincare/include/poincare/frac_part.h index 745034d24..21f478c15 100644 --- a/poincare/include/poincare/frac_part.h +++ b/poincare/include/poincare/frac_part.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/great_common_divisor.h b/poincare/include/poincare/great_common_divisor.h index 4005715f0..63a5d0d02 100644 --- a/poincare/include/poincare/great_common_divisor.h +++ b/poincare/include/poincare/great_common_divisor.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/hyperbolic_arc_cosine.h b/poincare/include/poincare/hyperbolic_arc_cosine.h index 4122a0e20..ccab941bc 100644 --- a/poincare/include/poincare/hyperbolic_arc_cosine.h +++ b/poincare/include/poincare/hyperbolic_arc_cosine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_arc_sine.h b/poincare/include/poincare/hyperbolic_arc_sine.h index e0ddecba9..25defc87f 100644 --- a/poincare/include/poincare/hyperbolic_arc_sine.h +++ b/poincare/include/poincare/hyperbolic_arc_sine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_arc_tangent.h b/poincare/include/poincare/hyperbolic_arc_tangent.h index 755394c64..694973778 100644 --- a/poincare/include/poincare/hyperbolic_arc_tangent.h +++ b/poincare/include/poincare/hyperbolic_arc_tangent.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_cosine.h b/poincare/include/poincare/hyperbolic_cosine.h index 31dc054fc..ac543dd48 100644 --- a/poincare/include/poincare/hyperbolic_cosine.h +++ b/poincare/include/poincare/hyperbolic_cosine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_sine.h b/poincare/include/poincare/hyperbolic_sine.h index 244bb7d5e..b9921d4ea 100644 --- a/poincare/include/poincare/hyperbolic_sine.h +++ b/poincare/include/poincare/hyperbolic_sine.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_tangent.h b/poincare/include/poincare/hyperbolic_tangent.h index e18448eb8..1ca7f1842 100644 --- a/poincare/include/poincare/hyperbolic_tangent.h +++ b/poincare/include/poincare/hyperbolic_tangent.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/imaginary_part.h b/poincare/include/poincare/imaginary_part.h index f2f713d74..6ece73ab0 100644 --- a/poincare/include/poincare/imaginary_part.h +++ b/poincare/include/poincare/imaginary_part.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 7c407e69d..f7c7c864f 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -22,9 +22,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; template struct DetailedResult { diff --git a/poincare/include/poincare/least_common_multiple.h b/poincare/include/poincare/least_common_multiple.h index d151b9c0d..977cea272 100644 --- a/poincare/include/poincare/least_common_multiple.h +++ b/poincare/include/poincare/least_common_multiple.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/logarithm.h b/poincare/include/poincare/logarithm.h index 5b3b0dcef..70c31e83f 100644 --- a/poincare/include/poincare/logarithm.h +++ b/poincare/include/poincare/logarithm.h @@ -26,9 +26,9 @@ private: Expression * splitInteger(Integer i, bool isDenominator, Context & context, AngleUnit angleUnit); /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix.h b/poincare/include/poincare/matrix.h index 1ed00cbc6..fd7ea221b 100644 --- a/poincare/include/poincare/matrix.h +++ b/poincare/include/poincare/matrix.h @@ -33,9 +33,9 @@ private: /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; int m_numberOfRows; }; diff --git a/poincare/include/poincare/matrix_dimension.h b/poincare/include/poincare/matrix_dimension.h index a3922e12b..928790558 100644 --- a/poincare/include/poincare/matrix_dimension.h +++ b/poincare/include/poincare/matrix_dimension.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_inverse.h b/poincare/include/poincare/matrix_inverse.h index 7037ebf54..1fbdc7c01 100644 --- a/poincare/include/poincare/matrix_inverse.h +++ b/poincare/include/poincare/matrix_inverse.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_trace.h b/poincare/include/poincare/matrix_trace.h index e54ce0df5..128fe34d6 100644 --- a/poincare/include/poincare/matrix_trace.h +++ b/poincare/include/poincare/matrix_trace.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_transpose.h b/poincare/include/poincare/matrix_transpose.h index 13f27bb00..2be3c90cb 100644 --- a/poincare/include/poincare/matrix_transpose.h +++ b/poincare/include/poincare/matrix_transpose.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index 72d6cf741..64376d5d0 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -22,7 +22,7 @@ public: /* Evaluation */ template static Complex compute(const Complex c, const Complex d); template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n); private: @@ -52,13 +52,13 @@ private: /* Evaluation */ template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/naperian_logarithm.h b/poincare/include/poincare/naperian_logarithm.h index 5034cd456..9991b9a51 100644 --- a/poincare/include/poincare/naperian_logarithm.h +++ b/poincare/include/poincare/naperian_logarithm.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/nth_root.h b/poincare/include/poincare/nth_root.h index 3455772d0..facf310e4 100644 --- a/poincare/include/poincare/nth_root.h +++ b/poincare/include/poincare/nth_root.h @@ -22,9 +22,9 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex compute(const Complex c, const Complex d); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; diff --git a/poincare/include/poincare/opposite.h b/poincare/include/poincare/opposite.h index 954409bfc..1c4a0219b 100644 --- a/poincare/include/poincare/opposite.h +++ b/poincare/include/poincare/opposite.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -20,11 +20,11 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, compute); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, compute); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, compute); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, compute); } }; diff --git a/poincare/include/poincare/parenthesis.h b/poincare/include/poincare/parenthesis.h index 93c61a0be..a5a56d291 100644 --- a/poincare/include/poincare/parenthesis.h +++ b/poincare/include/poincare/parenthesis.h @@ -21,9 +21,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; diff --git a/poincare/include/poincare/permute_coefficient.h b/poincare/include/poincare/permute_coefficient.h index 0cf54c594..da3497fa2 100644 --- a/poincare/include/poincare/permute_coefficient.h +++ b/poincare/include/poincare/permute_coefficient.h @@ -25,9 +25,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index eab93f84a..01175c0d8 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -2,7 +2,7 @@ #define POINCARE_POWER_H #include -#include +#include #include #include @@ -55,11 +55,11 @@ private: template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n) { return nullptr; } template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * d); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { return nullptr; } - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/prediction_interval.h b/poincare/include/poincare/prediction_interval.h index 8018a17b9..7c3875e10 100644 --- a/poincare/include/poincare/prediction_interval.h +++ b/poincare/include/poincare/prediction_interval.h @@ -23,9 +23,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/product.h b/poincare/include/poincare/product.h index 031b85bf7..ba5fe91c5 100644 --- a/poincare/include/poincare/product.h +++ b/poincare/include/poincare/product.h @@ -15,12 +15,12 @@ private: int emptySequenceValue() const override; ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const override; Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const override { - return templatedEvaluateWithNextTerm(a, b); + return templatedApproximateWithNextTerm(a, b); } Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const override { - return templatedEvaluateWithNextTerm(a, b); + return templatedApproximateWithNextTerm(a, b); } - template Expression * templatedEvaluateWithNextTerm(Expression * a, Expression * b) const; + template Expression * templatedApproximateWithNextTerm(Expression * a, Expression * b) const; }; } diff --git a/poincare/include/poincare/rational.h b/poincare/include/poincare/rational.h index 776298378..52435dc8f 100644 --- a/poincare/include/poincare/rational.h +++ b/poincare/include/poincare/rational.h @@ -44,9 +44,9 @@ public: private: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const; Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; Expression * setSign(Sign s); Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override { diff --git a/poincare/include/poincare/real_part.h b/poincare/include/poincare/real_part.h index bcc7b2424..adfe7f0ee 100644 --- a/poincare/include/poincare/real_part.h +++ b/poincare/include/poincare/real_part.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -25,11 +25,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/round.h b/poincare/include/poincare/round.h index b9353bb7a..885dc0a7a 100644 --- a/poincare/include/poincare/round.h +++ b/poincare/include/poincare/round.h @@ -24,9 +24,9 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Complex */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/sequence.h b/poincare/include/poincare/sequence.h index 503b5529c..38959bc42 100644 --- a/poincare/include/poincare/sequence.h +++ b/poincare/include/poincare/sequence.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -17,9 +17,9 @@ private: virtual ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const = 0; virtual const char * name() const = 0; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; virtual int emptySequenceValue() const = 0; virtual Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const = 0; virtual Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const = 0; diff --git a/poincare/include/poincare/simplification_root.h b/poincare/include/poincare/simplification_root.h index 033c3e5b9..09e50ac08 100644 --- a/poincare/include/poincare/simplification_root.h +++ b/poincare/include/poincare/simplification_root.h @@ -23,11 +23,11 @@ public: return nullptr; } int writeTextInBuffer(char * buffer, int bufferSize) const override { return 0; } - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { assert(false); return nullptr; } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { assert(false); return nullptr; } diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h index 300b224d6..93fbc4670 100644 --- a/poincare/include/poincare/sine.h +++ b/poincare/include/poincare/sine.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include namespace Poincare { @@ -27,11 +27,11 @@ private: /* Simplication */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/square_root.h b/poincare/include/poincare/square_root.h index e567363cb..be1ee0094 100644 --- a/poincare/include/poincare/square_root.h +++ b/poincare/include/poincare/square_root.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -20,11 +20,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/store.h b/poincare/include/poincare/store.h index 4cbc28795..e58d4e961 100644 --- a/poincare/include/poincare/store.h +++ b/poincare/include/poincare/store.h @@ -18,9 +18,9 @@ private: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; /* Evalutation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; const Symbol * symbol() const { return static_cast(operand(1)); } const Expression * value() const { return operand(0); } diff --git a/poincare/include/poincare/subtraction.h b/poincare/include/poincare/subtraction.h index 2f249b9f1..3c604d651 100644 --- a/poincare/include/poincare/subtraction.h +++ b/poincare/include/poincare/subtraction.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -26,18 +26,18 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { - return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + return ApproximationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n); template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { - return EvaluationEngine::elementWiseOnComplexMatrices(m, n, compute); + return ApproximationEngine::elementWiseOnComplexMatrices(m, n, compute); } - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/sum.h b/poincare/include/poincare/sum.h index b4ceff686..854e59d57 100644 --- a/poincare/include/poincare/sum.h +++ b/poincare/include/poincare/sum.h @@ -15,12 +15,12 @@ private: int emptySequenceValue() const override; ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const override; Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const override { - return templatedEvaluateWithNextTerm(a, b); + return templatedApproximateWithNextTerm(a, b); } Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const override { - return templatedEvaluateWithNextTerm(a, b); + return templatedApproximateWithNextTerm(a, b); } - template Expression * templatedEvaluateWithNextTerm(Expression * a, Expression * b) const; + template Expression * templatedApproximateWithNextTerm(Expression * a, Expression * b) const; }; } diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index d071272bb..f18f9fe9d 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -44,9 +44,9 @@ private: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Expression * templatedApproximate(Context& context, AngleUnit angleUnit) const; const char m_name; }; diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h index 04f70029e..f201e4c69 100644 --- a/poincare/include/poincare/tangent.h +++ b/poincare/include/poincare/tangent.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace Poincare { @@ -26,11 +26,11 @@ private: Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/undefined.h b/poincare/include/poincare/undefined.h index 11154047d..672294b72 100644 --- a/poincare/include/poincare/undefined.h +++ b/poincare/include/poincare/undefined.h @@ -15,9 +15,9 @@ private: /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; /* Evaluation */ - Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + Expression * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + template Complex * templatedApproximate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/src/evaluation_engine.cpp b/poincare/src/approximation_engine.cpp similarity index 50% rename from poincare/src/evaluation_engine.cpp rename to poincare/src/approximation_engine.cpp index ddab709b7..da5902d51 100644 --- a/poincare/src/evaluation_engine.cpp +++ b/poincare/src/approximation_engine.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include extern "C" { @@ -7,9 +7,9 @@ extern "C" { namespace Poincare { -template Expression * EvaluationEngine::map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute) { +template Expression * ApproximationEngine::map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute) { assert(expression->numberOfOperands() == 1); - Expression * input = expression->operand(0)->evaluate(context, angleUnit); + Expression * input = expression->operand(0)->approximate(context, angleUnit); Expression * result = nullptr; if (input->type() == Expression::Type::Complex) { Complex * c = static_cast *>(input); @@ -29,11 +29,11 @@ template Expression * EvaluationEngine::map(const Expression * expre return result; } -template Expression * EvaluationEngine::mapReduce(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices) { - Expression * result = expression->operand(0)->evaluate(context, angleUnit); +template Expression * ApproximationEngine::mapReduce(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices) { + Expression * result = expression->operand(0)->approximate(context, angleUnit); for (int i = 1; i < expression->numberOfOperands(); i++) { Expression * intermediateResult = nullptr; - Expression * nextOperandEvaluation = expression->operand(i)->evaluate(context, angleUnit); + Expression * nextOperandEvaluation = expression->operand(i)->approximate(context, angleUnit); if (result->type() == Expression::Type::Complex && nextOperandEvaluation->type() == Expression::Type::Complex) { const Complex * c = static_cast *>(result); const Complex * d = static_cast *>(nextOperandEvaluation); @@ -65,7 +65,7 @@ template Expression * EvaluationEngine::mapReduce(const Expression * return result; } -template Matrix * EvaluationEngine::elementWiseOnComplexAndComplexMatrix(const Complex * c, const Matrix * m, ComplexAndComplexReduction computeOnComplexes) { +template Matrix * ApproximationEngine::elementWiseOnComplexAndComplexMatrix(const Complex * c, const Matrix * m, ComplexAndComplexReduction computeOnComplexes) { Expression ** operands = new Expression * [m->numberOfRows()*m->numberOfColumns()]; for (int i = 0; i < m->numberOfOperands(); i++) { const Complex * d = static_cast *>(m->operand(i)); @@ -76,7 +76,7 @@ template Matrix * EvaluationEngine::elementWiseOnComplexAndComplexMa return result; } -template Matrix * EvaluationEngine::elementWiseOnComplexMatrices(const Matrix * m, const Matrix * n, ComplexAndComplexReduction computeOnComplexes) { +template Matrix * ApproximationEngine::elementWiseOnComplexMatrices(const Matrix * m, const Matrix * n, ComplexAndComplexReduction computeOnComplexes) { if (m->numberOfRows() != n->numberOfRows() || m->numberOfColumns() != n->numberOfColumns()) { return nullptr; } @@ -91,14 +91,14 @@ template Matrix * EvaluationEngine::elementWiseOnComplexMatrices(con return result; } -template Poincare::Expression * Poincare::EvaluationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexCompute compute); -template Poincare::Expression * Poincare::EvaluationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexCompute compute); -template Poincare::Expression * Poincare::EvaluationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::EvaluationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::EvaluationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::EvaluationEngine::MatrixAndMatrixReduction computeOnMatrices); -template Poincare::Expression * Poincare::EvaluationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::EvaluationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::EvaluationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::EvaluationEngine::MatrixAndMatrixReduction computeOnMatrices); -template Poincare::Matrix * Poincare::EvaluationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix *, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); -template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); -template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); -template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Expression * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute compute); +template Poincare::Expression * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute compute); +template Poincare::Expression * Poincare::ApproximationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationEngine::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::Expression * Poincare::ApproximationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationEngine::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::Matrix * Poincare::ApproximationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix *, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::ApproximationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::ApproximationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::ApproximationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); } diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index ae2812a92..b1d947a48 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -83,9 +83,9 @@ ExpressionLayout * BinomialCoefficient::privateCreateLayout(FloatDisplayMode flo } template -Expression * BinomialCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * nInput = operand(0)->evaluate(context, angleUnit); - Expression * kInput = operand(1)->evaluate(context, angleUnit); +Expression * BinomialCoefficient::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * nInput = operand(0)->approximate(context, angleUnit); + Expression * kInput = operand(1)->approximate(context, angleUnit); if (nInput->type() != Type::Complex || kInput->type() != Type::Complex) { return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 6a40feb18..8ea42b682 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -248,7 +248,7 @@ ExpressionLayout * Complex::privateCreateLayout(Expression::FloatDisplayMode template template -Complex * Complex::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { +Complex * Complex::templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const { return new Complex(Complex::Cartesian((U)m_a, (U)m_b)); } @@ -478,10 +478,10 @@ ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMod template class Complex; template class Complex; -template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; -template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; -template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; -template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedApproximate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedApproximate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedApproximate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedApproximate(Context&, Expression::AngleUnit) const; } diff --git a/poincare/src/confidence_interval.cpp b/poincare/src/confidence_interval.cpp index 3ffc36764..3aa587249 100644 --- a/poincare/src/confidence_interval.cpp +++ b/poincare/src/confidence_interval.cpp @@ -59,9 +59,9 @@ Expression * ConfidenceInterval::shallowReduce(Context& context, AngleUnit angle } template -Expression * ConfidenceInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * fInput = operand(0)->evaluate(context, angleUnit); - Expression * nInput = operand(1)->evaluate(context, angleUnit); +Expression * ConfidenceInterval::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * fInput = operand(0)->approximate(context, angleUnit); + Expression * nInput = operand(1)->approximate(context, angleUnit); if (fInput->type() != Type::Complex || nInput->type() != Type::Complex) { return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index e7ac92254..8bf643a2a 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -107,7 +107,7 @@ Expression * Decimal::clone() const { return new Decimal(m_mantissa, m_exponent); } -template Expression * Decimal::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { +template Expression * Decimal::templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const { T m = m_mantissa.approximate(); int numberOfDigits = numberOfDigitsInMantissaWithoutSign(); return new Complex(Complex::Float(m*std::pow((T)10.0, (T)(m_exponent-numberOfDigits+1)))); diff --git a/poincare/src/derivative.cpp b/poincare/src/derivative.cpp index cfea6e23e..cb5b8d75c 100644 --- a/poincare/src/derivative.cpp +++ b/poincare/src/derivative.cpp @@ -35,17 +35,17 @@ Expression * Derivative::shallowReduce(Context& context, AngleUnit angleUnit) { } template -Expression * Derivative::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Derivative::templatedApproximate(Context& context, AngleUnit angleUnit) const { static T min = sizeof(T) == sizeof(double) ? DBL_MIN : FLT_MIN; static T max = sizeof(T) == sizeof(double) ? DBL_MAX : FLT_MAX; VariableContext xContext = VariableContext('x', &context); Symbol xSymbol('x'); - Expression * xInput = operand(1)->evaluate(context, angleUnit); + Expression * xInput = operand(1)->approximate(context, angleUnit); T x = xInput->type() == Type::Complex ? static_cast *>(xInput)->toScalar() : NAN; delete xInput; Complex e = Complex::Float(x); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + Expression * fInput = operand(0)->approximate(xContext, angleUnit); T functionValue = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; @@ -123,12 +123,12 @@ T Derivative::growthRateAroundAbscissa(T x, T h, VariableContext xContext, An Symbol xSymbol('x'); Complex e = Complex::Float(x + h); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + Expression * fInput = operand(0)->approximate(xContext, angleUnit); T expressionPlus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x-h); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - fInput = operand(0)->evaluate(xContext, angleUnit); + fInput = operand(0)->approximate(xContext, angleUnit); T expressionMinus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; return (expressionPlus - expressionMinus)/(2*h); @@ -139,17 +139,17 @@ T Derivative::approximateDerivate2(T x, T h, VariableContext xContext, AngleU Symbol xSymbol('x'); Complex e = Complex::Float(x + h); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + Expression * fInput = operand(0)->approximate(xContext, angleUnit); T expressionPlus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - fInput = operand(0)->evaluate(xContext, angleUnit); + fInput = operand(0)->approximate(xContext, angleUnit); T expression = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x-h); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - fInput = operand(0)->evaluate(xContext, angleUnit); + fInput = operand(0)->approximate(xContext, angleUnit); T expressionMinus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; return expressionPlus - 2.0*expression + expressionMinus; diff --git a/poincare/src/determinant.cpp b/poincare/src/determinant.cpp index cf19a054a..5f42249da 100644 --- a/poincare/src/determinant.cpp +++ b/poincare/src/determinant.cpp @@ -34,8 +34,8 @@ Expression * Determinant::shallowReduce(Context& context, AngleUnit angleUnit) { // TODO: handle this exactly in shallowReduce for small dimensions. template -Expression * Determinant::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * input = operand(0)->evaluate(context, angleUnit); +Expression * Determinant::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->approximate(context, angleUnit); Expression * result = nullptr; if (input->type() == Type::Complex) { result = input->clone(); diff --git a/poincare/src/division_quotient.cpp b/poincare/src/division_quotient.cpp index 938299b14..23032d0d4 100644 --- a/poincare/src/division_quotient.cpp +++ b/poincare/src/division_quotient.cpp @@ -58,9 +58,9 @@ Expression * DivisionQuotient::shallowReduce(Context& context, AngleUnit angleUn } template -Complex * DivisionQuotient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * f1Input = operand(0)->evaluate(context, angleUnit); - Expression * f2Input = operand(1)->evaluate(context, angleUnit); +Complex * DivisionQuotient::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->approximate(context, angleUnit); + Expression * f2Input = operand(1)->approximate(context, angleUnit); T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; diff --git a/poincare/src/division_remainder.cpp b/poincare/src/division_remainder.cpp index ab631b010..bf6157b8a 100644 --- a/poincare/src/division_remainder.cpp +++ b/poincare/src/division_remainder.cpp @@ -58,9 +58,9 @@ Expression * DivisionRemainder::shallowReduce(Context& context, AngleUnit angleU } template -Complex * DivisionRemainder::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * f1Input = operand(0)->evaluate(context, angleUnit); - Expression * f2Input = operand(1)->evaluate(context, angleUnit); +Complex * DivisionRemainder::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->approximate(context, angleUnit); + Expression * f2Input = operand(1)->approximate(context, angleUnit); T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 36d3f1544..a8822dace 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -253,17 +253,17 @@ Expression * Expression::deepBeautify(Context & context, AngleUnit angleUnit) { /* Evaluation */ -template Expression * Expression::evaluate(Context& context, AngleUnit angleUnit) const { +template Expression * Expression::approximate(Context& context, AngleUnit angleUnit) const { switch (angleUnit) { case AngleUnit::Default: - return privateEvaluate(T(), context, Preferences::sharedPreferences()->angleUnit()); + return privateApproximate(T(), context, Preferences::sharedPreferences()->angleUnit()); default: - return privateEvaluate(T(), context, angleUnit); + return privateApproximate(T(), context, angleUnit); } } template T Expression::approximateToScalar(Context& context, AngleUnit angleUnit) const { - Expression * evaluation = evaluate(context, angleUnit); + Expression * evaluation = approximate(context, angleUnit); assert(evaluation->type() == Type::Complex || evaluation->type() == Type::Matrix); T result = NAN; if (evaluation->type() == Type::Complex) { @@ -292,8 +292,8 @@ template T Expression::epsilon() { } -template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; -template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; +template Poincare::Expression * Poincare::Expression::approximate(Context& context, AngleUnit angleUnit) const; +template Poincare::Expression * Poincare::Expression::approximate(Context& context, AngleUnit angleUnit) const; template double Poincare::Expression::approximateToScalar(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); template float Poincare::Expression::approximateToScalar(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); template double Poincare::Expression::approximateToScalar(Poincare::Context&, Poincare::Expression::AngleUnit) const; diff --git a/poincare/src/global_context.cpp b/poincare/src/global_context.cpp index f53998574..a55a4edf4 100644 --- a/poincare/src/global_context.cpp +++ b/poincare/src/global_context.cpp @@ -77,7 +77,7 @@ void GlobalContext::setExpressionForSymbolName(const Expression * expression, co if (symbol->isMatrixSymbol()) { int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; assert(indexMatrix >= 0 && indexMatrix < k_maxNumberOfMatrixExpressions); - Expression * evaluation = expression ? expression->evaluate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate M1+2->M1) + Expression * evaluation = expression ? expression->approximate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate M1+2->M1) if (m_matrixExpressions[indexMatrix] != nullptr) { delete m_matrixExpressions[indexMatrix]; m_matrixExpressions[indexMatrix] = nullptr; @@ -95,7 +95,7 @@ void GlobalContext::setExpressionForSymbolName(const Expression * expression, co if (index < 0 || index >= k_maxNumberOfScalarExpressions) { return; } - Expression * evaluation = expression ? expression->evaluate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate A+2->A) + Expression * evaluation = expression ? expression->approximate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate A+2->A) if (m_expressions[index] != nullptr) { delete m_expressions[index]; m_expressions[index] = nullptr; diff --git a/poincare/src/great_common_divisor.cpp b/poincare/src/great_common_divisor.cpp index 53494e623..6cd10604c 100644 --- a/poincare/src/great_common_divisor.cpp +++ b/poincare/src/great_common_divisor.cpp @@ -57,9 +57,9 @@ Expression * GreatCommonDivisor::shallowReduce(Context& context, AngleUnit angle } template -Complex * GreatCommonDivisor::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * f1Input = operand(0)->evaluate(context, angleUnit); - Expression * f2Input = operand(1)->evaluate(context, angleUnit); +Complex * GreatCommonDivisor::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->approximate(context, angleUnit); + Expression * f2Input = operand(1)->approximate(context, angleUnit); T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index 9d02e67ef..a60268268 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -37,12 +37,12 @@ Expression * Integral::shallowReduce(Context& context, AngleUnit angleUnit) { } template -Complex * Integral::templatedEvaluate(Context & context, AngleUnit angleUnit) const { +Complex * Integral::templatedApproximate(Context & context, AngleUnit angleUnit) const { VariableContext xContext = VariableContext('x', &context); - Expression * aInput = operand(1)->evaluate(context, angleUnit); + Expression * aInput = operand(1)->approximate(context, angleUnit); T a = aInput->type() == Type::Complex ? static_cast *>(aInput)->toScalar() : NAN; delete aInput; - Expression * bInput = operand(2)->evaluate(context, angleUnit); + Expression * bInput = operand(2)->approximate(context, angleUnit); T b = bInput->type() == Type::Complex ? static_cast *>(bInput)->toScalar() : NAN; delete bInput; if (std::isnan(a) || std::isnan(b)) { @@ -70,7 +70,7 @@ T Integral::functionValueAtAbscissa(T x, VariableContext xContext, AngleUnit Complex e = Complex::Float(x); Symbol xSymbol('x'); xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); - Expression * f = operand(0)->evaluate(xContext, angleUnit); + Expression * f = operand(0)->approximate(xContext, angleUnit); T result = f->type() == Type::Complex ? static_cast *>(f)->toScalar() : NAN; delete f; return result; diff --git a/poincare/src/least_common_multiple.cpp b/poincare/src/least_common_multiple.cpp index f55d410dd..71f048e6d 100644 --- a/poincare/src/least_common_multiple.cpp +++ b/poincare/src/least_common_multiple.cpp @@ -57,9 +57,9 @@ Expression * LeastCommonMultiple::shallowReduce(Context& context, AngleUnit angl } template -Complex * LeastCommonMultiple::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * f1Input = operand(0)->evaluate(context, angleUnit); - Expression * f2Input = operand(1)->evaluate(context, angleUnit); +Complex * LeastCommonMultiple::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->approximate(context, angleUnit); + Expression * f2Input = operand(1)->approximate(context, angleUnit); T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index 22588fe3f..734f03efa 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include @@ -192,12 +192,12 @@ Expression * Logarithm::shallowBeautify(Context & context, AngleUnit angleUnit) } template -Expression * Logarithm::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Logarithm::templatedApproximate(Context& context, AngleUnit angleUnit) const { if (numberOfOperands() == 1) { - return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } - Expression * x = operand(0)->evaluate(context, angleUnit); - Expression * n = operand(1)->evaluate(context, angleUnit); + Expression * x = operand(0)->approximate(context, angleUnit); + Expression * n = operand(1)->approximate(context, angleUnit); Complex result = Complex::Float(NAN); if (x->type() == Type::Complex && n->type() == Type::Complex) { Complex * xc = static_cast *>(x); diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 8e23e7753..d9a67e939 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -312,10 +312,10 @@ Matrix * Matrix::createApproximateIdentity(int dim) { } template -Expression * Matrix::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Matrix::templatedApproximate(Context& context, AngleUnit angleUnit) const { Expression ** operands = new Expression * [numberOfOperands()]; for (int i = 0; i < numberOfOperands(); i++) { - Expression * operandEvaluation = operand(i)->evaluate(context, angleUnit); + Expression * operandEvaluation = operand(i)->approximate(context, angleUnit); if (operandEvaluation->type() != Type::Complex) { operands[i] = new Complex(Complex::Float(NAN)); delete operandEvaluation; diff --git a/poincare/src/matrix_dimension.cpp b/poincare/src/matrix_dimension.cpp index 8bbb5fb74..f582f93cc 100644 --- a/poincare/src/matrix_dimension.cpp +++ b/poincare/src/matrix_dimension.cpp @@ -40,8 +40,8 @@ Expression * MatrixDimension::shallowReduce(Context& context, AngleUnit angleUni } template -Expression * MatrixDimension::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * input = operand(0)->evaluate(context, angleUnit); +Expression * MatrixDimension::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->approximate(context, angleUnit); Expression * operands[2]; if (input->type() == Type::Matrix) { operands[0] = new Complex(Complex::Float((T)static_cast(input)->numberOfRows())); diff --git a/poincare/src/matrix_inverse.cpp b/poincare/src/matrix_inverse.cpp index 1d5271be5..988604eb0 100644 --- a/poincare/src/matrix_inverse.cpp +++ b/poincare/src/matrix_inverse.cpp @@ -46,8 +46,8 @@ Expression * MatrixInverse::shallowReduce(Context& context, AngleUnit angleUnit) // TODO: handle this exactly in shallowReduce for small dimensions. template -Expression * MatrixInverse::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * input = operand(0)->evaluate(context, angleUnit); +Expression * MatrixInverse::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->approximate(context, angleUnit); Expression * result = nullptr; if (input->type() == Type::Complex) { Complex * c = static_cast *>(input); diff --git a/poincare/src/matrix_trace.cpp b/poincare/src/matrix_trace.cpp index 4ae4ebfab..0525b1590 100644 --- a/poincare/src/matrix_trace.cpp +++ b/poincare/src/matrix_trace.cpp @@ -49,8 +49,8 @@ Expression * MatrixTrace::shallowReduce(Context& context, AngleUnit angleUnit) { } template -Expression * MatrixTrace::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * input = operand(0)->evaluate(context, angleUnit); +Expression * MatrixTrace::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->approximate(context, angleUnit); Expression * result = nullptr; if (input->type() == Type::Complex) { result = input->clone(); diff --git a/poincare/src/matrix_transpose.cpp b/poincare/src/matrix_transpose.cpp index 59bb4ec3f..c6deb029d 100644 --- a/poincare/src/matrix_transpose.cpp +++ b/poincare/src/matrix_transpose.cpp @@ -39,8 +39,8 @@ Expression * MatrixTranspose::shallowReduce(Context& context, AngleUnit angleUni } template -Expression * MatrixTranspose::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * input = operand(0)->evaluate(context, angleUnit); +Expression * MatrixTranspose::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->approximate(context, angleUnit); Expression * result = nullptr; if (input->type() == Type::Complex) { result = input->clone(); diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index 030dc13ff..e648818c0 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -54,9 +54,9 @@ Complex NthRoot::compute(const Complex c, const Complex d) { } template -Expression * NthRoot::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * base = operand(0)->evaluate(context, angleUnit); - Expression * index = operand(1)->evaluate(context, angleUnit); +Expression * NthRoot::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * base = operand(0)->approximate(context, angleUnit); + Expression * index = operand(1)->approximate(context, angleUnit); Complex result = Complex::Float(NAN); if (base->type() == Type::Complex && index->type() == Type::Complex) { Complex * basec = static_cast *>(base); diff --git a/poincare/src/parenthesis.cpp b/poincare/src/parenthesis.cpp index 9cc9c0829..e8a7985b6 100644 --- a/poincare/src/parenthesis.cpp +++ b/poincare/src/parenthesis.cpp @@ -32,8 +32,8 @@ Expression * Parenthesis::shallowReduce(Context& context, AngleUnit angleUnit) { } template -Expression * Parenthesis::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - return operand(0)->evaluate(context, angleUnit); +Expression * Parenthesis::templatedApproximate(Context& context, AngleUnit angleUnit) const { + return operand(0)->approximate(context, angleUnit); } } diff --git a/poincare/src/permute_coefficient.cpp b/poincare/src/permute_coefficient.cpp index f381173d4..4ecc692ef 100644 --- a/poincare/src/permute_coefficient.cpp +++ b/poincare/src/permute_coefficient.cpp @@ -68,9 +68,9 @@ Expression * PermuteCoefficient::shallowReduce(Context& context, AngleUnit angle } template -Complex * PermuteCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * nInput = operand(0)->evaluate(context, angleUnit); - Expression * kInput = operand(1)->evaluate(context, angleUnit); +Complex * PermuteCoefficient::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * nInput = operand(0)->approximate(context, angleUnit); + Expression * kInput = operand(1)->approximate(context, angleUnit); if (nInput->type() != Type::Complex || kInput->type() != Type::Complex) { return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 0566c1041..707dc5330 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -183,8 +183,8 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { /* Step 0: if both operands are true complexes, the result is undefined. * We can assert that evaluations is a complex as matrix are not simplified */ - Complex * op0 = static_cast *>(operand(0)->evaluate(context, angleUnit)); - Complex * op1 = static_cast *>(operand(1)->evaluate(context, angleUnit)); + Complex * op0 = static_cast *>(operand(0)->approximate(context, angleUnit)); + Complex * op1 = static_cast *>(operand(1)->approximate(context, angleUnit)); bool bothOperandsComplexes = op0->b() != 0 && op1->b() != 0; delete op0; delete op1; diff --git a/poincare/src/prediction_interval.cpp b/poincare/src/prediction_interval.cpp index 42f33e734..548f2442a 100644 --- a/poincare/src/prediction_interval.cpp +++ b/poincare/src/prediction_interval.cpp @@ -68,9 +68,9 @@ Expression * PredictionInterval::shallowReduce(Context& context, AngleUnit angle } template -Expression * PredictionInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * pInput = operand(0)->evaluate(context, angleUnit); - Expression * nInput = operand(1)->evaluate(context, angleUnit); +Expression * PredictionInterval::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * pInput = operand(0)->approximate(context, angleUnit); + Expression * nInput = operand(1)->approximate(context, angleUnit); if (pInput->type() != Type::Complex || nInput->type() != Type::Complex) { return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/product.cpp b/poincare/src/product.cpp index dcc8f40d0..d202cd807 100644 --- a/poincare/src/product.cpp +++ b/poincare/src/product.cpp @@ -31,7 +31,7 @@ ExpressionLayout * Product::createSequenceLayoutWithArgumentLayouts(ExpressionLa } template -Expression * Product::templatedEvaluateWithNextTerm(Expression * a, Expression * b) const { +Expression * Product::templatedApproximateWithNextTerm(Expression * a, Expression * b) const { if (a->type() == Type::Complex && b->type() == Type::Complex) { Complex * c = static_cast *>(a); Complex * d = static_cast *>(b); diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp index 78accb430..863a605f4 100644 --- a/poincare/src/rational.cpp +++ b/poincare/src/rational.cpp @@ -138,7 +138,7 @@ int Rational::simplificationOrderSameType(const Expression * e) const { return NaturalOrder(*this, *other); } -template Complex * Rational::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { +template Complex * Rational::templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const { T n = m_numerator.approximate(); T d = m_denominator.approximate(); return new Complex(Complex::Float(n/d)); diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index 60cc6bedc..77ba9ae5f 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -48,9 +48,9 @@ Expression * Round::shallowReduce(Context& context, AngleUnit angleUnit) { } template -Complex * Round::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * f1Input = operand(0)->evaluate(context, angleUnit); - Expression * f2Input = operand(1)->evaluate(context, angleUnit); +Complex * Round::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->approximate(context, angleUnit); + Expression * f2Input = operand(1)->approximate(context, angleUnit); T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; diff --git a/poincare/src/sequence.cpp b/poincare/src/sequence.cpp index 0b2b5bf1a..87386e816 100644 --- a/poincare/src/sequence.cpp +++ b/poincare/src/sequence.cpp @@ -23,9 +23,9 @@ ExpressionLayout * Sequence::privateCreateLayout(FloatDisplayMode floatDisplayMo } template -Expression * Sequence::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Expression * aInput = operand(1)->evaluate(context, angleUnit); - Expression * bInput = operand(2)->evaluate(context, angleUnit); +Expression * Sequence::templatedApproximate(Context& context, AngleUnit angleUnit) const { + Expression * aInput = operand(1)->approximate(context, angleUnit); + Expression * bInput = operand(2)->approximate(context, angleUnit); T start = aInput->type() == Type::Complex ? static_cast *>(aInput)->toScalar() : NAN; T end = bInput->type() == Type::Complex ? static_cast *>(bInput)->toScalar() : NAN; delete aInput; @@ -43,7 +43,7 @@ Expression * Sequence::templatedEvaluate(Context& context, AngleUnit angleUnit) } Complex iExpression = Complex::Float(i); nContext.setExpressionForSymbolName(&iExpression, &nSymbol, nContext); - Expression * expression = operand(0)->evaluate(nContext, angleUnit); + Expression * expression = operand(0)->approximate(nContext, angleUnit); Expression * newResult = evaluateWithNextTerm(T(), result, expression); delete result; delete expression; diff --git a/poincare/src/simplification/expression_simplify.cpp b/poincare/src/simplification/expression_simplify.cpp index 7ce61943c..2d9d5f216 100644 --- a/poincare/src/simplification/expression_simplify.cpp +++ b/poincare/src/simplification/expression_simplify.cpp @@ -22,10 +22,10 @@ public: ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { return nullptr; } - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + Evaluation * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return nullptr; } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + Evaluation * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return nullptr; } }; diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index e4616d889..34296a405 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -37,10 +37,10 @@ ExpressionLayout * Store::privateCreateLayout(FloatDisplayMode floatDisplayMode, } template -Expression * Store::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Store::templatedApproximate(Context& context, AngleUnit angleUnit) const { context.setExpressionForSymbolName(value(), symbol(), context); if (context.expressionForSymbol(symbol()) != nullptr) { - return context.expressionForSymbol(symbol())->evaluate(context, angleUnit); + return context.expressionForSymbol(symbol())->approximate(context, angleUnit); } return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/sum.cpp b/poincare/src/sum.cpp index 7e7e5c792..70a5168b7 100644 --- a/poincare/src/sum.cpp +++ b/poincare/src/sum.cpp @@ -31,7 +31,7 @@ ExpressionLayout * Sum::createSequenceLayoutWithArgumentLayouts(ExpressionLayout } template -Expression * Sum::templatedEvaluateWithNextTerm(Expression * a, Expression * b) const { +Expression * Sum::templatedApproximateWithNextTerm(Expression * a, Expression * b) const { if (a->type() == Type::Complex && b->type() == Type::Complex) { Complex * c = static_cast *>(a); Complex * d = static_cast *>(b); diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 440b9fdee..26c6f6abc 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -111,9 +111,9 @@ Expression::Sign Symbol::sign() const { } template -Expression * Symbol::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Symbol::templatedApproximate(Context& context, AngleUnit angleUnit) const { if (context.expressionForSymbol(this) != nullptr) { - return context.expressionForSymbol(this)->evaluate(context, angleUnit); + return context.expressionForSymbol(this)->approximate(context, angleUnit); } return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/undefined.cpp b/poincare/src/undefined.cpp index c8da8aac4..1fc6f27ad 100644 --- a/poincare/src/undefined.cpp +++ b/poincare/src/undefined.cpp @@ -14,7 +14,7 @@ Expression * Undefined::clone() const { return new Undefined(); } -template Complex * Undefined::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +template Complex * Undefined::templatedApproximate(Context& context, AngleUnit angleUnit) const { return new Complex(Complex::Float(NAN)); } diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index 3c8a0c9ce..a1c883c0b 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -105,14 +105,14 @@ QUIZ_CASE(poincare_complex_cartesian_to_text) { QUIZ_CASE(poincare_complex_evaluate) { GlobalContext globalContext; Expression * a = new Complex(Complex::Float(123.456f)); - Expression * m = a->evaluate(globalContext); + Expression * m = a->approximate(globalContext); assert(m->type() == Expression::Type::Complex); Complex * mc = static_cast *>(m); assert(std::fabs(mc->a() - 123.456) < 0.00001); assert(mc->b() == 0.0); delete m; - Expression * n = a->evaluate(globalContext); + Expression * n = a->approximate(globalContext); assert(n->type() == Expression::Type::Complex); Complex * nc = static_cast *>(n); assert(nc->a() == 123.456f); diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index 1694d6d3e..2e069679a 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -36,7 +36,7 @@ template void assert_parsed_expression_evaluates_to(const char * expression, Complex * results, int numberOfRows, int numberOfColumns, Expression::AngleUnit angleUnit) { GlobalContext globalContext; Expression * a = parse_expression(expression); - Expression * m = a->evaluate(globalContext, angleUnit); + Expression * m = a->approximate(globalContext, angleUnit); assert(m); assert(m->numberOfOperands() == 0 || m->numberOfOperands() == numberOfRows*numberOfColumns); if (m->type() == Expression::Type::Matrix) { From 2c06727f3651d455ab3f5a333cee0a1ee64e7431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 14:39:06 +0100 Subject: [PATCH 31/77] [apps] Simplify expressions in sequence and function applications Change-Id: I2708934d3b5c90e8692e50d939b3a13028b8615e --- apps/graph/cartesian_function.cpp | 5 +++- apps/sequence/sequence.cpp | 44 ++++++++++++++++++++----------- apps/sequence/sequence.h | 4 +-- apps/shared/function.cpp | 15 ++++++++--- apps/shared/function.h | 2 +- poincare/src/expression.cpp | 1 + 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/apps/graph/cartesian_function.cpp b/apps/graph/cartesian_function.cpp index 50359d6ec..99a34cc6a 100644 --- a/apps/graph/cartesian_function.cpp +++ b/apps/graph/cartesian_function.cpp @@ -18,8 +18,11 @@ void CartesianFunction::setDisplayDerivative(bool display) { double CartesianFunction::approximateDerivative(double x, Poincare::Context * context) const { Poincare::Complex abscissa = Poincare::Complex::Float(x); - Poincare::Expression * args[2] = {expression(), &abscissa}; + Poincare::Expression * args[2] = {expression(context), &abscissa}; Poincare::Derivative derivative(args, true); + /* TODO: when we will simplify derivative, we might want to simplify the + * derivative here. However, we might want to do it once for all x (to avoid + * lagging in the derivative table. */ return derivative.approximateToScalar(*context); } diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index 7993e8c03..ec79b0ecd 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -133,30 +133,44 @@ void Sequence::setType(Type type) { resetBuffer(); } -Poincare::Expression * Sequence::firstInitialConditionExpression() const { +Poincare::Expression * Sequence::firstInitialConditionExpression(Context * context) const { if (m_firstInitialConditionExpression == nullptr) { m_firstInitialConditionExpression = Poincare::Expression::parse(m_firstInitialConditionText); + if (m_firstInitialConditionExpression) { + Expression::Simplify(&m_firstInitialConditionExpression, *context); + } } return m_firstInitialConditionExpression; } -Poincare::Expression * Sequence::secondInitialConditionExpression() const { +Poincare::Expression * Sequence::secondInitialConditionExpression(Context * context) const { if (m_secondInitialConditionExpression == nullptr) { m_secondInitialConditionExpression = Poincare::Expression::parse(m_secondInitialConditionText); + if (m_secondInitialConditionExpression) { + Expression::Simplify(&m_secondInitialConditionExpression, *context); + } } return m_secondInitialConditionExpression; } Poincare::ExpressionLayout * Sequence::firstInitialConditionLayout() { - if (m_firstInitialConditionLayout == nullptr && firstInitialConditionExpression() != nullptr) { - m_firstInitialConditionLayout = firstInitialConditionExpression()->createLayout(Expression::FloatDisplayMode::Decimal); + if (m_firstInitialConditionLayout == nullptr) { + Expression * nonSimplifedExpression = Expression::parse(m_firstInitialConditionText); + if (nonSimplifedExpression) { + m_firstInitialConditionLayout = nonSimplifedExpression->createLayout(Expression::FloatDisplayMode::Decimal); + delete nonSimplifedExpression; + } } return m_firstInitialConditionLayout; } Poincare::ExpressionLayout * Sequence::secondInitialConditionLayout() { - if (m_secondInitialConditionLayout == nullptr && secondInitialConditionExpression()) { - m_secondInitialConditionLayout = secondInitialConditionExpression()->createLayout(Expression::FloatDisplayMode::Decimal); + if (m_secondInitialConditionLayout == nullptr) { + Expression * nonSimplifedExpression = Expression::parse(m_secondInitialConditionText); + if (nonSimplifedExpression) { + m_secondInitialConditionLayout = nonSimplifedExpression->createLayout(Expression::FloatDisplayMode::Decimal); + delete nonSimplifedExpression; + } } return m_secondInitialConditionLayout; } @@ -282,18 +296,18 @@ T Sequence::templatedApproximateAtAbscissa(T x, Poincare::Context * context) con } if (n == 0) { setBufferIndexValue(0,0); - setBufferValue(firstInitialConditionExpression()->approximateToScalar(*context), 0); + setBufferValue(firstInitialConditionExpression(context)->approximateToScalar(*context), 0); return bufferValue(0); } LocalContext subContext = LocalContext(context); Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) < 0 || indexBuffer(0) > n ? 0 : indexBuffer(0); - T un = indexBuffer(0) < 0 || indexBuffer(0) > n ? firstInitialConditionExpression()->approximateToScalar(*context) : bufferValue(0); + T un = indexBuffer(0) < 0 || indexBuffer(0) > n ? firstInitialConditionExpression(context)->approximateToScalar(*context) : bufferValue(0); for (int i = start; i < n; i++) { subContext.setValueForSequenceRank(un, name(), 0); Poincare::Complex e = Poincare::Complex::Float(i); subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); - un = expression()->approximateToScalar(subContext); + un = expression(&subContext)-> template approximateToScalar(subContext); } setBufferValue(un, 0); setBufferIndexValue(n, 0); @@ -305,27 +319,27 @@ T Sequence::templatedApproximateAtAbscissa(T x, Poincare::Context * context) con return NAN; } if (n == 0) { - return firstInitialConditionExpression()->approximateToScalar(*context); + return firstInitialConditionExpression(context)->approximateToScalar(*context); } if (n == 1) { setBufferIndexValue(0, 0); - setBufferValue(firstInitialConditionExpression()->approximateToScalar(*context), 0); + setBufferValue(firstInitialConditionExpression(context)->approximateToScalar(*context), 0); setBufferIndexValue(1, 1); - setBufferValue(secondInitialConditionExpression()->approximateToScalar(*context), 1); + setBufferValue(secondInitialConditionExpression(context)->approximateToScalar(*context), 1); return bufferValue(1); } LocalContext subContext = LocalContext(context); Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? indexBuffer(0) : 0; - T un = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(0) : firstInitialConditionExpression()->approximateToScalar(*context); - T un1 = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(1) : secondInitialConditionExpression()->approximateToScalar(*context); + T un = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(0) : firstInitialConditionExpression(context)->approximateToScalar(*context); + T un1 = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(1) : secondInitialConditionExpression(context)->approximateToScalar(*context); for (int i = start; i < n-1; i++) { subContext.setValueForSequenceRank(un, name(), 0); subContext.setValueForSequenceRank(un1, name(), 1); Poincare::Complex e = Poincare::Complex::Float(i); subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); un = un1; - un1 = expression()->approximateToScalar(subContext); + un1 = expression(&subContext)->template approximateToScalar(subContext); } setBufferValue(un, 0); setBufferIndexValue(n-1, 0); diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index 6cc16b383..8cdebacbb 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -24,8 +24,8 @@ public: void setType(Type type); const char * firstInitialConditionText(); const char * secondInitialConditionText(); - Poincare::Expression * firstInitialConditionExpression() const; - Poincare::Expression * secondInitialConditionExpression() const; + Poincare::Expression * firstInitialConditionExpression(Poincare::Context * context) const; + Poincare::Expression * secondInitialConditionExpression(Poincare::Context * context) const; Poincare::ExpressionLayout * firstInitialConditionLayout(); Poincare::ExpressionLayout * secondInitialConditionLayout(); void setContent(const char * c) override; diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index a4e64108d..dd4e46444 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -69,16 +69,23 @@ const char * Function::name() const { return m_name; } -Poincare::Expression * Function::expression() const { +Poincare::Expression * Function::expression(Poincare::Context * context) const { if (m_expression == nullptr) { m_expression = Expression::parse(m_text); + if (m_expression) { + Expression::Simplify(&m_expression, *context); + } } return m_expression; } Poincare::ExpressionLayout * Function::layout() { - if (m_layout == nullptr && expression() != nullptr) { - m_layout = expression()->createLayout(Expression::FloatDisplayMode::Decimal); + if (m_layout == nullptr) { + Expression * nonSimplifiedExpression = Expression::parse(m_text); + if (nonSimplifiedExpression != nullptr) { + m_layout = nonSimplifiedExpression->createLayout(Expression::FloatDisplayMode::Decimal); + delete nonSimplifiedExpression; + } } return m_layout; } @@ -105,7 +112,7 @@ T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) con Poincare::Symbol xSymbol(symbol()); Poincare::Complex e = Poincare::Complex::Float(x); variableContext.setExpressionForSymbolName(&e, &xSymbol, variableContext); - return expression()->approximateToScalar(variableContext); + return expression(context)->approximateToScalar(variableContext); } void Function::tidy() { diff --git a/apps/shared/function.h b/apps/shared/function.h index 6d3adc71f..c01525e83 100644 --- a/apps/shared/function.h +++ b/apps/shared/function.h @@ -19,7 +19,7 @@ public: const char * text() const; const char * name() const; KDColor color() const { return m_color; } - Poincare::Expression * expression() const; + Poincare::Expression * expression(Poincare::Context * context) const; Poincare::ExpressionLayout * layout(); virtual bool isDefined(); bool isActive(); diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index a8822dace..8093e56c3 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -280,6 +280,7 @@ template T Expression::approximateToScalar(Context& context, AngleUn template T Expression::approximateToScalar(const char * text, Context& context, AngleUnit angleUnit) { Expression * exp = parse(text); + Simplify(&exp, context, angleUnit); T result = exp->approximateToScalar(context, angleUnit); delete exp; return result; From 89485104b87e2b9b00b6a6272437adc571790055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 17:13:21 +0100 Subject: [PATCH 32/77] [apps] In calculation, fix history table row height Change-Id: Ia0ee485b80d6f4db81f480d0e3e8f91b73c912a2 --- apps/calculation/history_controller.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index ca2b1e699..5214000b5 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -160,9 +160,14 @@ KDCoordinate HistoryController::rowHeight(int j) { Calculation * calculation = m_calculationStore->calculationAtIndex(j); KDCoordinate inputHeight = calculation->inputLayout()->size().height(); App * calculationApp = (App *)app(); - KDCoordinate exactOutputHeight = calculation->exactOutputLayout(calculationApp->localContext())->size().height(); - KDCoordinate approximateOutputHeight = calculation->approximateOutputLayout(calculationApp->localContext())->size().height(); - KDCoordinate outputHeight = calculation->shouldApproximateOutput() || approximateOutputHeight > exactOutputHeight ? approximateOutputHeight : exactOutputHeight; + Poincare::ExpressionLayout * approximateLayout = calculation->approximateOutputLayout(calculationApp->localContext()); + KDCoordinate approximateOutputHeight = approximateLayout->size().height(); + if (calculation->shouldApproximateOutput()) { + return inputHeight + approximateOutputHeight + 3*HistoryViewCell::k_digitVerticalMargin; + } + Poincare::ExpressionLayout * exactLayout = calculation->exactOutputLayout(calculationApp->localContext()); + KDCoordinate exactOutputHeight = exactLayout->size().height(); + KDCoordinate outputHeight = max(exactLayout->baseline(), approximateLayout->baseline()) + max(exactOutputHeight-exactLayout->baseline(), approximateOutputHeight-approximateLayout->baseline()); return inputHeight + outputHeight + 3*HistoryViewCell::k_digitVerticalMargin; } From bd79b6c8a1693a87a8a46848652cd6fbaf2e5c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 17:13:48 +0100 Subject: [PATCH 33/77] [poincare] inf -> undef Change-Id: Ibf7ba53a462c1d57f0928f9edcfe4919587908ab --- poincare/src/complex.cpp | 6 +++--- poincare/test/complex.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 8ea42b682..cce5517fc 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -313,7 +313,7 @@ template int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Expression::FloatDisplayMode mode) { assert(mode != Expression::FloatDisplayMode::Default); assert(numberOfSignificantDigits > 0); - if (std::isinf(f)) { + /*if (std::isinf(f)) { int currentChar = 0; if (f < 0) { buffer[currentChar++] = '-'; @@ -323,9 +323,9 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif buffer[currentChar++] = 'f'; buffer[currentChar] = 0; return currentChar; - } + }*/ - if (std::isnan(f)) { + if (std::isinf(f) || std::isnan(f)) { int currentChar = 0; buffer[currentChar++] = 'u'; buffer[currentChar++] = 'n'; diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index a1c883c0b..1cfc82be3 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -95,7 +95,7 @@ QUIZ_CASE(poincare_complex_cartesian_to_text) { assert_cartesian_complex_prints_to(64078208.0, 119229408.0, "6.407821E7+1.192294E8*i", DecimalDisplay, Cartesian); assert_cartesian_complex_prints_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", DecimalDisplay, Polar); assert_cartesian_complex_prints_to(64078208.0f, 119229408.0f, "1.353576E8*e^(1.07765*i)", DecimalDisplay, Polar); - assert_cartesian_complex_prints_to(INFINITY, 119229408.0f, "inf", DecimalDisplay, Polar); + assert_cartesian_complex_prints_to(INFINITY, 119229408.0f, "undef", DecimalDisplay, Polar); assert_cartesian_complex_prints_to(0.0f, 0.0f, "0", DecimalDisplay, Polar); assert_cartesian_complex_prints_to(NAN, 0.0f, "undef", DecimalDisplay, Polar); assert_cartesian_complex_prints_to(0.0f, NAN, "undef", DecimalDisplay, Polar); From 36e889817842ea6237aaf6a277afffcb1e6ab656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 17:14:21 +0100 Subject: [PATCH 34/77] [poincare] Consider symbols M* as matrix expressions Change-Id: I0050fa16119b883c67a5098a215ddb5932ce0f2f --- poincare/src/expression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 8093e56c3..64045703e 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -160,7 +160,7 @@ bool Expression::recursivelyMatches(ExpressionTest test) const { } bool Expression::IsMatrix(const Expression * e) { - return e->type() == Type::Matrix || e->type() == Type::ConfidenceInterval || e->type() == Type::MatrixDimension || e->type() == Type::PredictionInterval || e->type() == Type::MatrixInverse || e->type() == Type::MatrixTranspose; + return e->type() == Type::Matrix || e->type() == Type::ConfidenceInterval || e->type() == Type::MatrixDimension || e->type() == Type::PredictionInterval || e->type() == Type::MatrixInverse || e->type() == Type::MatrixTranspose || (e->type() == Type::Symbol && static_cast(e)->isMatrixSymbol()); } /* Comparison */ From ef97fac5cac7feba8b775ffd2d19670780ad180a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 24 Nov 2017 18:27:47 +0100 Subject: [PATCH 35/77] [poincare] Fix error in matrix dimension name Change-Id: I6163cc4529cf9fc825dba208f2a20f4da4d142a1 --- poincare/include/poincare/matrix_dimension.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/include/poincare/matrix_dimension.h b/poincare/include/poincare/matrix_dimension.h index 928790558..2e1bc6ec0 100644 --- a/poincare/include/poincare/matrix_dimension.h +++ b/poincare/include/poincare/matrix_dimension.h @@ -20,7 +20,7 @@ private: int writeTextInBuffer(char * buffer, int bufferSize) const override { return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); } - const char * name() const { return "dimension"; } + const char * name() const { return "dim"; } /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ From 900fdeaefa5bc2554387948fca30594c4502d677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 11:09:19 +0100 Subject: [PATCH 36/77] [Poincare] Fix commit "Power: do not reduce too big negative power (999^-999)" Change-Id: I66073d1378b8c0e429547f4746697c28cc0a8ef8 --- poincare/include/poincare/power.h | 1 + poincare/src/power.cpp | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 01175c0d8..89e66576c 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -49,6 +49,7 @@ private: static bool TermIsARationalSquareRootOrRational(const Expression * e); static const Rational * RadicandInExpression(const Expression * e); static const Rational * RationalFactorInExpression(const Expression * e); + static bool RationalExponentInvolvesShouldNotBeReduced(const Rational * r); /* Evaluation */ constexpr static int k_maxApproximatePowerMatrix = 1000; constexpr static int k_maxExactPowerMatrix = 100; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 707dc5330..b9fdaf4a7 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -246,12 +246,13 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { if (std::isinf(approx) || std::isnan(approx) || std::fabs(approx)> 1E100) { return this; } - Integer n = static_cast(operand(1))->numerator(); - n.setNegative(false); - if (Integer::NaturalOrder(n, Integer(k_maxIntegerPower)) > 0) { + Rational * exp = static_cast(editableOperand(1)); + /* First, we check that the simplification does not involve too complex power + * of integers (ie 3^999) that would take too much time to compute. */ + if (RationalExponentInvolvesShouldNotBeReduced(exp)) { return this; } - return simplifyRationalRationalPower(this, a, static_cast(editableOperand(1)), context, angleUnit); + return simplifyRationalRationalPower(this, a, exp, context, angleUnit); } } // e^(i*Pi*r) with r rational @@ -332,6 +333,11 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { Addition * a = static_cast(editableOperand(1)); // Check is b is rational if (a->operand(0)->type() == Type::Rational) { + /* First, we check that the simplification does not involve too complex power + * of integers (ie 3^999) that would take too much time to compute. */ + if (RationalExponentInvolvesShouldNotBeReduced(static_cast(a->operand(0)))) { + return this; + } Power * p1 = static_cast(clone()); replaceOperand(a, a->editableOperand(1), true); Power * p2 = static_cast(clone()); @@ -675,6 +681,15 @@ bool Power::isNthRootOfUnity() const { return false; } +bool Power::RationalExponentInvolvesShouldNotBeReduced(const Rational * r) { + Integer maxIntegerExponent = r->numerator(); + maxIntegerExponent.setNegative(false); + if (Integer::NaturalOrder(maxIntegerExponent, Integer(k_maxIntegerPower)) > 0) { + return true; + } + return false; +} + template Complex Power::compute(Complex, Complex); template Complex Power::compute(Complex, Complex); } From 74a91ac214779d0043e10491252f345df120113b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 11:10:33 +0100 Subject: [PATCH 37/77] [apps] In Calculation, the input text can be longer that the text written by the user (omitted multiplication signs added, parenthesis ...) Change-Id: Icc9861da847c7e65939ea6a9c337ffb3d1f12e61 --- apps/calculation/calculation.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index e85e4dc2b..0ec0430b4 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -33,7 +33,10 @@ public: bool shouldApproximateOutput(); private: Poincare::Expression * exactOutput(Poincare::Context * context); - char m_inputText[::TextField::maxBufferSize()]; + /* Buffers holding text expressions have to be longer than the text written + * by user (of maximum length TextField::maxBufferSize()) because when we + * print an expression we add omitted signs (multiplications, parenthesis...) */ + char m_inputText[2*::TextField::maxBufferSize()]; char m_exactOutputText[2*::TextField::maxBufferSize()]; char m_approximateOutputText[2*::TextField::maxBufferSize()]; Poincare::Expression * m_input; From bf2a26d90e0cab24e29d1205e410b4ad700b597e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 12:07:35 +0100 Subject: [PATCH 38/77] [apps] In calculation, do not accept expression whose corresponding texts is too long Change-Id: Ic96e5ed42e249d43e21241f8e1a7b1e873f3cad3 --- apps/calculation/app.cpp | 23 +++++++++++++++++++++++ apps/calculation/app.h | 1 + apps/calculation/calculation.h | 7 ++++--- apps/shared/text_field_delegate_app.h | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 1ad45263a..c6ec10f9b 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -54,4 +54,27 @@ Context * App::localContext() { return &m_localContext; } +bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { + if (TextFieldDelegateApp::textFieldDidReceiveEvent(textField, event)) { + return true; + } + /* Here, we check that the expression entered by the user can be printed with + * less than k_printedExpressionLength characters. Otherwise, we prevent the + * user from adding this expression to the calculation store. */ + if (textField->isEditing() && textField->textFieldShouldFinishEditing(event)) { + Expression * exp = Expression::parse(textField->text()); + assert(exp != nullptr); + char buffer[Calculation::k_printedExpressionSize]; + int length = exp->writeTextInBuffer(buffer, sizeof(buffer)); + delete exp; + /* if the buffer is totally full, it is VERY likely that writeTextInBuffer + * escaped before printing utterly the expression. */ + if (length >= Calculation::k_printedExpressionSize-1) { + displayWarning(I18n::Message::SyntaxError); + return true; + } + } + return false; +} + } diff --git a/apps/calculation/app.h b/apps/calculation/app.h index f44c97e3c..922772f69 100644 --- a/apps/calculation/app.h +++ b/apps/calculation/app.h @@ -29,6 +29,7 @@ public: CalculationStore m_calculationStore; }; Poincare::Context * localContext() override; + bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override; private: App(Container * container, Snapshot * snapshot); LocalContext m_localContext; diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index 0ec0430b4..58ebf6a35 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -31,14 +31,15 @@ public: bool isEmpty(); void tidy(); bool shouldApproximateOutput(); + constexpr static int k_printedExpressionSize = 2*::TextField::maxBufferSize(); private: Poincare::Expression * exactOutput(Poincare::Context * context); /* Buffers holding text expressions have to be longer than the text written * by user (of maximum length TextField::maxBufferSize()) because when we * print an expression we add omitted signs (multiplications, parenthesis...) */ - char m_inputText[2*::TextField::maxBufferSize()]; - char m_exactOutputText[2*::TextField::maxBufferSize()]; - char m_approximateOutputText[2*::TextField::maxBufferSize()]; + char m_inputText[k_printedExpressionSize]; + char m_exactOutputText[k_printedExpressionSize]; + char m_approximateOutputText[k_printedExpressionSize]; Poincare::Expression * m_input; Poincare::ExpressionLayout * m_inputLayout; Poincare::Expression * m_exactOutput; diff --git a/apps/shared/text_field_delegate_app.h b/apps/shared/text_field_delegate_app.h index 63e532cd5..c5039a1ef 100644 --- a/apps/shared/text_field_delegate_app.h +++ b/apps/shared/text_field_delegate_app.h @@ -16,7 +16,7 @@ public: AppsContainer * container(); virtual const char * XNT(); bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; - bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; + virtual bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; Toolbox * toolboxForTextField(TextField * textField) override; protected: TextFieldDelegateApp(Container * container, Snapshot * snapshot, ViewController * rootViewController); From 60346032de267905c0ca012427783831bf2516d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 13:35:18 +0100 Subject: [PATCH 39/77] [poincare] In Multiplication: fix bug when factorizing sin^m*cos^n Change-Id: Idebee5110e52a5d39e093dca265ba0ab88fae16a --- poincare/src/multiplication.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index ffac92d0d..964ebb1d5 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -263,6 +263,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a Expression * o2 = editableOperand(j); if (Base(o2)->type() == Type::Cosine && TermHasRationalExponent(o2) && Base(o2)->operand(0)->isIdenticalTo(x)) { factorizeSineAndCosine(o1, o2, context, angleUnit); + break; } } } @@ -278,6 +279,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a * pi^(-1)*pi-> 1 * i*i -> -1 * 2^(1/2)*2^(1/2) -> 2 + * sin(x)*cos(x) -> 1*tan(x) * Last, we remove the only rational operand if it is one and not the only * operand. */ i = 1; From 11b2eaecce8a55ee3316aa71326d557cf7919051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 14:44:28 +0100 Subject: [PATCH 40/77] [poincare] Clean: delete old code that was forgotten Change-Id: If3d0d0ee794f6993ea1d05aa61ce42ad12f4cb79 --- poincare/src/power.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index b9fdaf4a7..9875a2c9a 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -240,12 +240,6 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { } // p^q with p, q rationals if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { - double p = a->approximateToScalar(context, angleUnit); - double q = operand(1)->approximateToScalar(context, angleUnit); - double approx = std::pow(std::fabs(p), std::fabs(q)); - if (std::isinf(approx) || std::isnan(approx) || std::fabs(approx)> 1E100) { - return this; - } Rational * exp = static_cast(editableOperand(1)); /* First, we check that the simplification does not involve too complex power * of integers (ie 3^999) that would take too much time to compute. */ From 2b9fe1b3f4050006379f9ff07ee05e5b8fb4611a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 14:46:03 +0100 Subject: [PATCH 41/77] [poincare] In Power::shallowReduce: handle simple cases (0^x,x^0...) first to avoid division by 0 afterwards Change-Id: Ic88af4f88a4802e52593a566640104ac5276f7a2 --- poincare/src/power.cpp | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 9875a2c9a..4dbb6f8fb 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -192,13 +192,10 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { return replaceWith(new Undefined(), true); } - /* Step 1: We look for square root and sum of square roots (two terms maximum - * so far) at the denominator and move them to the numerator. */ - Expression * r = removeSquareRootsFromDenominator(context, angleUnit); - if (r) { - return r; - } - + /* Step 1: We handle simple cases as x^0, x^1, 0^x and 1^x first for 2 reasons: + * - we can assert this step that there is no division by 0: + * for instance, 0^(-2)->undefined + * - we save computational time by early escaping for these cases. */ if (operand(1)->type() == Type::Rational) { const Rational * b = static_cast(operand(1)); // x^0 @@ -216,13 +213,7 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { if (b->isOne()) { return replaceWith(editableOperand(0), true); } - // i^(p/q) - if (operand(0)->type() == Type::Symbol && static_cast(operand(0))->name() == Ion::Charset::IComplex) { - Rational r = Rational::Multiplication(*b, Rational(1, 2)); - return replaceWith(CreateNthRootOfUnity(r))->shallowReduce(context, angleUnit); - } } - bool letPowerAtRoot = parentIsALogarithmOfSameBase(); if (operand(0)->type() == Type::Rational) { Rational * a = static_cast(editableOperand(0)); // 0^x @@ -238,6 +229,26 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { if (a->isOne()) { return replaceWith(new Rational(1), true); } + } + + /* Step 2: We look for square root and sum of square roots (two terms maximum + * so far) at the denominator and move them to the numerator. */ + Expression * r = removeSquareRootsFromDenominator(context, angleUnit); + if (r) { + return r; + } + + if (operand(1)->type() == Type::Rational) { + const Rational * b = static_cast(operand(1)); + // i^(p/q) + if (operand(0)->type() == Type::Symbol && static_cast(operand(0))->name() == Ion::Charset::IComplex) { + Rational r = Rational::Multiplication(*b, Rational(1, 2)); + return replaceWith(CreateNthRootOfUnity(r))->shallowReduce(context, angleUnit); + } + } + bool letPowerAtRoot = parentIsALogarithmOfSameBase(); + if (operand(0)->type() == Type::Rational) { + Rational * a = static_cast(editableOperand(0)); // p^q with p, q rationals if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { Rational * exp = static_cast(editableOperand(1)); @@ -570,6 +581,7 @@ Expression * Power::removeSquareRootsFromDenominator(Context & context, AngleUni * p and q integers. * We'll turn those into sqrt(p*q)/q (or sqrt(p*q)/p) . */ Integer p = static_cast(operand(0))->numerator(); + assert(!p.isZero()); // We eliminated case of form 0^(-1/2) at first step of shallowReduce Integer q = static_cast(operand(0))->denominator(); // We do nothing for terms of the form sqrt(p) if (!q.isOne() || static_cast(operand(1))->isMinusHalf()) { @@ -577,7 +589,7 @@ Expression * Power::removeSquareRootsFromDenominator(Context & context, AngleUni if (static_cast(operand(1))->isHalf()) { result = new Multiplication(new Rational(Integer(1), q), sqrt, false); } else { - result = new Multiplication(new Rational(Integer(1), p), sqrt, false); + result = new Multiplication(new Rational(Integer(1), p), sqrt, false); // We use here the assertion that p != 0 } sqrt->shallowReduce(context, angleUnit); } From eb4e2bcade84d3b4a1de512c8986fdaaf9b2434a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 16:13:04 +0100 Subject: [PATCH 42/77] [poincare] Fix bug in logarithm split integer Change-Id: I6b65443262e07e6ae3cc79eb7e5ea0c5caed1a06 --- poincare/src/logarithm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index 734f03efa..ff17271ad 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -153,7 +153,7 @@ Expression * Logarithm::splitInteger(Integer i, bool isDenominator, Context & co * - either it might be take too many factors... More than k_maxNumberOfPrimeFactors. * - Or, it might takes too much time */ Expression * e = clone(); - e->replaceOperand(operand(0), new Rational(i), true); + e->replaceOperand(e->operand(0), new Rational(i), true); if (!isDenominator) { return e; } From 29587bf7626099c7b85a377da9aa00c46294eea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Mon, 27 Nov 2017 16:13:33 +0100 Subject: [PATCH 43/77] [poincare] Cap the integers that can be prime factorized Change-Id: Iaf8cd747ac99a586438910bffc90de53e18b7640 --- poincare/include/poincare/arithmetic.h | 4 ++++ poincare/src/arithmetic.cpp | 4 ++-- poincare/test/simplify_easy.cpp | 3 +++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/poincare/include/poincare/arithmetic.h b/poincare/include/poincare/arithmetic.h index ab7cefc04..298360796 100644 --- a/poincare/include/poincare/arithmetic.h +++ b/poincare/include/poincare/arithmetic.h @@ -13,6 +13,10 @@ public: constexpr static int k_maxNumberOfPrimeFactors = 32; static const Integer k_biggestPrimeFactorizedInteger; static const Integer k_primorial32; +private: + /* When decomposing an integer into primes factors, we look for its prime + * factors among integer from 2 to 100000000. */ + constexpr static int k_biggestPrimeFactor = 1E8; }; } diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp index ed93623c0..47c44eefe 100644 --- a/poincare/src/arithmetic.cpp +++ b/poincare/src/arithmetic.cpp @@ -3,7 +3,7 @@ namespace Poincare { -const Integer Arithmetic::k_biggestPrimeFactorizedInteger("10000000000000"); // 1E13 +const Integer Arithmetic::k_biggestPrimeFactorizedInteger("1000000000000"); // 1E12 const Integer Arithmetic::k_primorial32("525896479052627740771371797072411912900610967452630"); Integer Arithmetic::LCM(const Integer * a, const Integer * b) { @@ -74,7 +74,7 @@ void Arithmetic::PrimeFactorization(const Integer * n, Integer * outputFactors, k++; testedPrimeFactor = k < k_numberOfPrimeFactors ? Integer(primeFactors[k]) : Integer::Addition(testedPrimeFactor, Integer(1)); outputFactors[t] = testedPrimeFactor; - } while (stopCondition); + } while (stopCondition && testedPrimeFactor.isLowerThan(Integer(k_biggestPrimeFactor))); outputFactors[t] = std::move(m); outputCoefficients[t] = Integer::Addition(outputCoefficients[t], Integer(1)); } diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index 331b7f4f4..ce9c2efb6 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -519,4 +519,7 @@ QUIZ_CASE(poincare_simplify_easy) { // This does not work but should not as it is above k_biggestPrimeFactorizedInteger assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "1881676377434183981909562699940347954480361860897069^(1/3)"); + + assert_parsed_expression_simplify_to("log(26061622162116)", "log(26061622162116)"); + assert_parsed_expression_simplify_to("log(26061622162116/5)", "log(26061622162116)-log(5)"); } From 51e360aa6f39c19f1ccd64a2033fe5045110c890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:42:21 +0100 Subject: [PATCH 44/77] [apps] In calculation, default XNT is x Change-Id: I9bfef8920a4975b1162e2630aa4c4556537886b1 --- apps/calculation/app.cpp | 4 ++++ apps/calculation/app.h | 1 + 2 files changed, 5 insertions(+) diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index c6ec10f9b..2a947cf8c 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -77,4 +77,8 @@ bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event e return false; } +const char * App::XNT() { + return "x"; +} + } diff --git a/apps/calculation/app.h b/apps/calculation/app.h index 922772f69..a9b616599 100644 --- a/apps/calculation/app.h +++ b/apps/calculation/app.h @@ -30,6 +30,7 @@ public: }; Poincare::Context * localContext() override; bool textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) override; + const char * XNT() override; private: App(Container * container, Snapshot * snapshot); LocalContext m_localContext; From 0b0f765f6bd7666f73149f21c7505b045723a9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:43:06 +0100 Subject: [PATCH 45/77] [apps] In calculation, return only approximation when input involves symbols A-Z Change-Id: Ibf50fb516b01850c5f93c5153f4d550d3e459028 --- apps/calculation/calculation.cpp | 2 +- poincare/include/poincare/symbol.h | 1 + poincare/src/symbol.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 399e6dd89..1194cb8f7 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -213,7 +213,7 @@ bool Calculation::shouldApproximateOutput() { return true; } return input()->recursivelyMatches([](const Expression * e) { - return e->type() == Expression::Type::Decimal || Expression::IsMatrix(e); + return e->type() == Expression::Type::Decimal || Expression::IsMatrix(e) || (e->type() == Expression::Type::Symbol && static_cast(e)->isScalarSymbol()); }); } diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index f18f9fe9d..9e7f3fb3f 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -36,6 +36,7 @@ public: Expression * clone() const override; Sign sign() const override; bool isMatrixSymbol() const; + bool isScalarSymbol() const; private: const char * textForSpecialSymbols(char name) const; /* Comparison */ diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 26c6f6abc..009ef353b 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -181,6 +181,13 @@ bool Symbol::isMatrixSymbol() const { return false; } +bool Symbol::isScalarSymbol() const { + if (m_name >= 'A' && m_name <= 'Z') { + return true; + } + return false; +} + int Symbol::simplificationOrderSameType(const Expression * e) const { assert(e->type() == Expression::Type::Symbol); if (m_name == ((Symbol *)e)->m_name) { From ea1af0cfda7b10131eadb9fc2a6320a72256c5ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:44:15 +0100 Subject: [PATCH 46/77] [poincare] Clean global context Change-Id: I52eb6d6bdd4288700a6d22ca2835f6190b4bde82 --- poincare/src/global_context.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/poincare/src/global_context.cpp b/poincare/src/global_context.cpp index a55a4edf4..fcecae12c 100644 --- a/poincare/src/global_context.cpp +++ b/poincare/src/global_context.cpp @@ -45,8 +45,13 @@ Complex * GlobalContext::defaultExpression() { } int GlobalContext::symbolIndex(const Symbol * symbol) const { - int index = symbol->name() - 'A'; - return index; + if (symbol->isMatrixSymbol()) { + return symbol->name() - (char)Symbol::SpecialSymbols::M0; + } + if (symbol->isScalarSymbol()) { + return symbol->name() - 'A'; + } + return -1; } const Expression * GlobalContext::expressionForSymbol(const Symbol * symbol) { @@ -59,11 +64,10 @@ const Expression * GlobalContext::expressionForSymbol(const Symbol * symbol) { if (symbol->name() == Ion::Charset::IComplex) { return &m_i; } - if (symbol->isMatrixSymbol()) { - int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; - return m_matrixExpressions[indexMatrix]; - } int index = symbolIndex(symbol); + if (symbol->isMatrixSymbol()) { + return m_matrixExpressions[index]; + } if (index < 0 || index >= k_maxNumberOfScalarExpressions) { return nullptr; } @@ -74,7 +78,8 @@ const Expression * GlobalContext::expressionForSymbol(const Symbol * symbol) { } void GlobalContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { - if (symbol->isMatrixSymbol()) { + int index = symbolIndex(symbol); + if (symbol->isMatrixSymbol()) { int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; assert(indexMatrix >= 0 && indexMatrix < k_maxNumberOfMatrixExpressions); Expression * evaluation = expression ? expression->approximate(context) : nullptr; // evaluate before deleting anything (to be able to evaluate M1+2->M1) @@ -91,7 +96,6 @@ void GlobalContext::setExpressionForSymbolName(const Expression * expression, co } return; } - int index = symbolIndex(symbol); if (index < 0 || index >= k_maxNumberOfScalarExpressions) { return; } From 355c39ff43b2244cc66c41abcbe4ce8b2fc758f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:44:57 +0100 Subject: [PATCH 47/77] [poincare] In Power, change name: RationalExponentInvolvesShouldNotBeReduced -> RationalExponentShouldNotBeReduced Change-Id: I98aa93543f1dcf6a88f331c367a8dd3ca68f2d53 --- poincare/include/poincare/power.h | 2 +- poincare/src/power.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 89e66576c..6c8dcd95d 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -49,7 +49,7 @@ private: static bool TermIsARationalSquareRootOrRational(const Expression * e); static const Rational * RadicandInExpression(const Expression * e); static const Rational * RationalFactorInExpression(const Expression * e); - static bool RationalExponentInvolvesShouldNotBeReduced(const Rational * r); + static bool RationalExponentShouldNotBeReduced(const Rational * r); /* Evaluation */ constexpr static int k_maxApproximatePowerMatrix = 1000; constexpr static int k_maxExactPowerMatrix = 100; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 4dbb6f8fb..58e7e12f7 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -254,7 +254,7 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { Rational * exp = static_cast(editableOperand(1)); /* First, we check that the simplification does not involve too complex power * of integers (ie 3^999) that would take too much time to compute. */ - if (RationalExponentInvolvesShouldNotBeReduced(exp)) { + if (RationalExponentShouldNotBeReduced(exp)) { return this; } return simplifyRationalRationalPower(this, a, exp, context, angleUnit); @@ -340,7 +340,7 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { if (a->operand(0)->type() == Type::Rational) { /* First, we check that the simplification does not involve too complex power * of integers (ie 3^999) that would take too much time to compute. */ - if (RationalExponentInvolvesShouldNotBeReduced(static_cast(a->operand(0)))) { + if (RationalExponentShouldNotBeReduced(static_cast(a->operand(0)))) { return this; } Power * p1 = static_cast(clone()); @@ -687,7 +687,7 @@ bool Power::isNthRootOfUnity() const { return false; } -bool Power::RationalExponentInvolvesShouldNotBeReduced(const Rational * r) { +bool Power::RationalExponentShouldNotBeReduced(const Rational * r) { Integer maxIntegerExponent = r->numerator(); maxIntegerExponent.setNegative(false); if (Integer::NaturalOrder(maxIntegerExponent, Integer(k_maxIntegerPower)) > 0) { From 8328342826d9bcefb2121f0f4d607f902d25ae71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:45:37 +0100 Subject: [PATCH 48/77] [poincare] In round, do not shallow reduce when it involves big exponentiation Change-Id: Ia2d0e854eab7bd9dea22679c820935d37ccdc7bd --- poincare/include/poincare/power.h | 1 + poincare/src/round.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 6c8dcd95d..bc4e4433c 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -15,6 +15,7 @@ class Power : public StaticHierarchy<2> { friend class SquareRoot; friend class Addition; friend class Division; + friend class Round; public: Type type() const override; Expression * clone() const override; diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index 77ba9ae5f..cfdf2a3a9 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -1,6 +1,7 @@ #include #include #include +#include extern "C" { #include @@ -34,6 +35,9 @@ Expression * Round::shallowReduce(Context& context, AngleUnit angleUnit) { if (!r2->denominator().isOne()) { return replaceWith(new Undefined(), true); } + if (Power::RationalExponentShouldNotBeReduced(r2)) { + return this; + } Rational err = Rational::Power(Rational(10), r2->numerator()); Rational mult = Rational::Multiplication(*r1, Rational(err)); IntegerDivision d = Integer::Division(mult.numerator(), mult.denominator()); From 4e2c219179c45b4d361633c5a549929ecf0a40c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:46:23 +0100 Subject: [PATCH 49/77] [poincare] Correct mistake in comments Change-Id: Id276f31a08c693aed47afceb9f75847e09d07919 --- poincare/src/power.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 58e7e12f7..b5a6ab028 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -293,7 +293,7 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { return replaceWith(editableOperand(1)->editableOperand(0), true); } } - // (a^b)^c -> a^(b+c) if a > 0 or c is integer + // (a^b)^c -> a^(b*c) if a > 0 or c is integer if (operand(0)->type() == Type::Power) { Power * p = static_cast(editableOperand(0)); // Check is a > 0 or c is Integer From d886ce251ed5e9e2f29035cf090f0c63e13cbc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 11:47:01 +0100 Subject: [PATCH 50/77] [poincare] In Addition when reducing to same denominator: do not deep reduce because the common denominator might not be a multiplication anymore and make the numerator terms disappear. (ie 1/root(5,9)+3/root(25/9)) Change-Id: If72c73f47a36a97202ca3d1f7f44d40740f275b5 --- poincare/src/addition.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index 0580f915a..775f38352 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -165,14 +165,16 @@ Expression * Addition::factorizeOnCommonDenominator(Context & context, AngleUnit // want to create numerator = a/b*b*d + c/d*b*d + e/b*b*d Addition * numerator = new Addition(); for (int i=0; i < numberOfOperands(); i++) { - numerator->addOperand(new Multiplication(operand(i), commonDenominator, true)); + Expression * m = new Multiplication(operand(i), commonDenominator, true); + numerator->addOperand(m); + m->shallowReduce(context, angleUnit); } // Step 3: Add the denominator Power * inverseDenominator = new Power(commonDenominator, new Rational(-1), false); Multiplication * result = new Multiplication(numerator, inverseDenominator, false); // Step 4: Simplify the numerator to a*d + c*b + e*d - numerator->deepReduce(context, angleUnit); + numerator->shallowReduce(context, angleUnit); // Step 5: Simplify the denominator (in case it's a rational number) commonDenominator->deepReduce(context, angleUnit); From 65a87d77d60d3029eb4a384841c27ebbe1cad6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 13:29:27 +0100 Subject: [PATCH 51/77] [poincare] Cap prime factorizations: cap only prime factors Change-Id: I6384ced8021d264cbf3a903e1574aeb0a22b7c3f --- poincare/include/poincare/arithmetic.h | 5 ++--- poincare/src/arithmetic.cpp | 1 - poincare/src/expression_debug.cpp | 2 +- poincare/src/logarithm.cpp | 7 +++---- poincare/src/power.cpp | 4 ++-- poincare/test/arithmetic.cpp | 4 ++-- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/poincare/include/poincare/arithmetic.h b/poincare/include/poincare/arithmetic.h index 298360796..1d5c95adb 100644 --- a/poincare/include/poincare/arithmetic.h +++ b/poincare/include/poincare/arithmetic.h @@ -11,12 +11,11 @@ public: static void PrimeFactorization(const Integer * i, Integer * outputFactors, Integer * outputCoefficients, int outputLength); constexpr static int k_numberOfPrimeFactors = 1000; constexpr static int k_maxNumberOfPrimeFactors = 32; - static const Integer k_biggestPrimeFactorizedInteger; static const Integer k_primorial32; private: /* When decomposing an integer into primes factors, we look for its prime - * factors among integer from 2 to 100000000. */ - constexpr static int k_biggestPrimeFactor = 1E8; + * factors among integer from 2 to 10000. */ + constexpr static int k_biggestPrimeFactor = 10000; }; } diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp index 47c44eefe..161db6938 100644 --- a/poincare/src/arithmetic.cpp +++ b/poincare/src/arithmetic.cpp @@ -3,7 +3,6 @@ namespace Poincare { -const Integer Arithmetic::k_biggestPrimeFactorizedInteger("1000000000000"); // 1E12 const Integer Arithmetic::k_primorial32("525896479052627740771371797072411912900610967452630"); Integer Arithmetic::LCM(const Integer * a, const Integer * b) { diff --git a/poincare/src/expression_debug.cpp b/poincare/src/expression_debug.cpp index 956522d00..910aa94cd 100644 --- a/poincare/src/expression_debug.cpp +++ b/poincare/src/expression_debug.cpp @@ -235,7 +235,7 @@ void print_prime_factorization(Integer * outputFactors, Integer * outputCoeffici std::cout << outputFactors[index].approximate(); std::cout << "^"; std::cout << outputCoefficients[index].approximate(); - std::cout << "+"; + std::cout << "*"; } std::cout <<" "<< std::endl; } diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index ff17271ad..43cea1765 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -148,10 +148,9 @@ Expression * Logarithm::splitInteger(Integer i, bool isDenominator, Context & co return new Rational(0); } assert(!i.isOne()); - if (Arithmetic::k_primorial32.isLowerThan(i) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(i)) { - /* We do not want to break i in prime factor because - * - either it might be take too many factors... More than k_maxNumberOfPrimeFactors. - * - Or, it might takes too much time */ + if (Arithmetic::k_primorial32.isLowerThan(i)) { + /* We do not want to break i in prime factor because it might be take too + * many factors... More than k_maxNumberOfPrimeFactors. */ Expression * e = clone(); e->replaceOperand(e->operand(0), new Rational(i), true); if (!isDenominator) { diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index b5a6ab028..a81e2520a 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -426,10 +426,10 @@ Expression * Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational * r } Integer absI = i; absI.setNegative(false); - if (Arithmetic::k_primorial32.isLowerThan(absI) || Arithmetic::k_biggestPrimeFactorizedInteger.isLowerThan(absI)) { + if (Arithmetic::k_primorial32.isLowerThan(absI)) { r->setSign(isDenominator ? Sign::Negative : Sign::Positive); /* We do not want to break i in prime factor because it might be take too - * many factors... More than k_maxNumberOfPrimeFactors; or too much time!*/ + * many factors... More than k_maxNumberOfPrimeFactors. */ return new Power(new Rational(i), r->clone(), false); } Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; diff --git a/poincare/test/arithmetic.cpp b/poincare/test/arithmetic.cpp index accd9529e..841bd6a10 100644 --- a/poincare/test/arithmetic.cpp +++ b/poincare/test/arithmetic.cpp @@ -83,6 +83,6 @@ QUIZ_CASE(poincare_arithmetic) { int coefficients2[3] = {2,4,2}; assert_prime_factorization_equals_to(Integer(122500), factors2, coefficients2, 3); int factors3[8] = {3,7,11, 13, 19, 3607, 3803, 52579}; - int coefficients3[8] = {4,2,2,2,2,2,2,2}; - assert_prime_factorization_equals_to(Integer("15241578780673678515622620750190521"), factors3, coefficients3, 8); + int coefficients3[8] = {4,2,2,2,2,2,2,1}; + assert_prime_factorization_equals_to(Integer("289879586539753105148873518899"), factors3, coefficients3, 8); } From 97ab64296b379406b60e16b8a1a7a6612f3366cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 13:31:49 +0100 Subject: [PATCH 52/77] [poincare] clean warning Change-Id: I4549b237391f46fcb4a28a33c497fb9013c9659c --- poincare/src/decimal.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 8bf643a2a..be25e8e2f 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -169,8 +169,8 @@ int Decimal::writeTextInBuffer(char * buffer, int bufferSize) const { /* If mantissa is negative, m_mantissa.writeTextInBuffer is going to add an * unwanted '-' in place of the temp char. We store it to replace it back * after calling m_mantissa.writeTextInBuffer. */ - char tempChar; - int tempCharPosition; + char tempChar = 0; + int tempCharPosition = 0; if (m_mantissa.isNegative()) { currentChar--; tempChar = buffer[currentChar]; From 8874977a776f16671bcdef32dc163df852229010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 17:03:12 +0100 Subject: [PATCH 53/77] [poincare] Clean tests Change-Id: Ib1a36aa1fff91e41ec92da18631bbe9c8c9a5f5f --- poincare/test/helper.cpp | 28 +- poincare/test/helper.h | 2 + poincare/test/simplify_easy.cpp | 828 ++++++++++++++++---------------- 3 files changed, 448 insertions(+), 410 deletions(-) diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index 2e069679a..8095a5c2a 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -8,19 +8,37 @@ using namespace Poincare; -Expression * parse_expression(const char * expression) { - quiz_print(expression); - char buffer[200]; - strlcpy(buffer, expression, sizeof(buffer)); - for (char *c = buffer; *c; c++) { +void translate_in_special_chars(char * expression) { + for (char *c = expression; *c; c++) { switch (*c) { case 'E': *c = Ion::Charset::Exponent; break; case 'X': *c = Ion::Charset::Exponential; break; case 'I': *c = Ion::Charset::IComplex; break; case 'R': *c = Ion::Charset::Root; break; case 'P': *c = Ion::Charset::SmallPi; break; + case '*': *c = Ion::Charset::MultiplicationSign; break; } } +} + +void translate_in_ASCII_chars(char * expression) { + for (char *c = expression; *c; c++) { + switch (*c) { + case Ion::Charset::Exponent: *c = 'E'; break; + case Ion::Charset::Exponential: *c = 'X'; break; + case Ion::Charset::IComplex: *c = 'I'; break; + case Ion::Charset::Root: *c = 'R'; break; + case Ion::Charset::SmallPi: *c = 'P'; break; + case Ion::Charset::MultiplicationSign: *c = '*'; break; + } + } +} + +Expression * parse_expression(const char * expression) { + quiz_print(expression); + char buffer[200]; + strlcpy(buffer, expression, sizeof(buffer)); + translate_in_special_chars(buffer); Expression * result = Expression::parse(buffer); assert(result); return result; diff --git a/poincare/test/helper.h b/poincare/test/helper.h index 798e1a59c..6f5627871 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -3,6 +3,8 @@ constexpr Poincare::Expression::AngleUnit Degree = Poincare::Expression::AngleUnit::Degree; constexpr Poincare::Expression::AngleUnit Radian = Poincare::Expression::AngleUnit::Radian; +void translate_in_special_chars(char * expression); +void translate_in_ASCII_chars(char * expression); Poincare::Expression * parse_expression(const char * expression); void assert_parsed_expression_type(const char * expression, Poincare::Expression::Type type); template diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp index ce9c2efb6..a7ec02c6d 100644 --- a/poincare/test/simplify_easy.cpp +++ b/poincare/test/simplify_easy.cpp @@ -18,25 +18,438 @@ void assert_parsed_expression_simplify_to(const char * expression, const char * cout << "---- Simplify: " << expression << "----" << endl; #endif Expression::Simplify(&e, globalContext, angleUnit); + char buffer[200]; + e->writeTextInBuffer(buffer, sizeof(buffer)); + translate_in_ASCII_chars(buffer); #if POINCARE_TESTS_PRINT_EXPRESSIONS print_expression(e, 0); + cout << "---- serialize to: " << buffer << " ----" << endl; + cout << "----- compared to: " << simplifiedExpression << " ----\n" << endl; #endif - Expression * f = parse_expression(simplifiedExpression); - Expression::Simplify(&f, globalContext, angleUnit); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "---- compared to: " << simplifiedExpression << "----" << endl; - print_expression(f, 0); -#endif - assert(e->isIdenticalTo(f)); + assert(strcmp(buffer, simplifiedExpression) == 0); delete e; - delete f; } QUIZ_CASE(poincare_simplify_easy) { - //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); + + // Rational + assert_parsed_expression_simplify_to("-1/3", "-1/3"); + assert_parsed_expression_simplify_to("22355/45325", "4471/9065"); + assert_parsed_expression_simplify_to("0000.000000", "0"); + assert_parsed_expression_simplify_to(".000000", "0"); + assert_parsed_expression_simplify_to("0000", "0"); + assert_parsed_expression_simplify_to("0.1234567", "1234567/10000000"); + assert_parsed_expression_simplify_to("123.4567", "1234567/10000"); + assert_parsed_expression_simplify_to("0.1234", "617/5000"); + assert_parsed_expression_simplify_to("0.1234000", "617/5000"); + assert_parsed_expression_simplify_to("001234000", "1234000"); + assert_parsed_expression_simplify_to("001.234000E3", "1234"); + assert_parsed_expression_simplify_to("001234000E-4", "617/5"); + assert_parsed_expression_simplify_to("3/4+5/4-12+1/567", "-5669/567"); + assert_parsed_expression_simplify_to("34/78+67^(-1)", "1178/2613"); + assert_parsed_expression_simplify_to("12348/34564", "3087/8641"); + assert_parsed_expression_simplify_to("1-0.3-0.7", "0"); + assert_parsed_expression_simplify_to("123456789123456789+112233445566778899", "235690234690235688"); + assert_parsed_expression_simplify_to("56^56", "79164324866862966607842406018063254671922245312646690223362402918484170424104310169552592050323456"); + assert_parsed_expression_simplify_to("999^999", "999^999"); + assert_parsed_expression_simplify_to("999^-999", "1/999^999"); + assert_parsed_expression_simplify_to("0^0", "undef"); + assert_parsed_expression_simplify_to("x^0", "1"); + assert_parsed_expression_simplify_to("P^0", "1"); + assert_parsed_expression_simplify_to("A^0", "1"); + assert_parsed_expression_simplify_to("(-3)^0", "1"); + + // Addition + assert_parsed_expression_simplify_to("2+0", "2"); + assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10*cos(2)"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "(2347+882*ln(2))/882"); + assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); + assert_parsed_expression_simplify_to("-5P+3P", "-2*P"); + assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "(-7)-A"); + assert_parsed_expression_simplify_to("1+2", "3"); + assert_parsed_expression_simplify_to("A-A", "0"); + assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); + assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); + assert_parsed_expression_simplify_to("-A", "-A"); + assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+P+x)/(2+P+2*x+P*x)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(P*x^2)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+P*x)/(P*x^3)"); + assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); + assert_parsed_expression_simplify_to("A+B-A-B", "0"); + assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); + + // Multiplication + assert_parsed_expression_simplify_to("0*x+B", "B"); + assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); + assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "(72*A^4*B^(2+x)+36*A^6*B^(2+x))/5"); + assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "3*A*B+3*A*C+A*B*D+A*C*D"); + assert_parsed_expression_simplify_to("A/B", "A/B"); + assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); + assert_parsed_expression_simplify_to("(1/2)*A/B", "A/(2*B)"); + assert_parsed_expression_simplify_to("1+2+3+4+5+6", "21"); + assert_parsed_expression_simplify_to("1-2+3-4+5-6", "-3"); + assert_parsed_expression_simplify_to("987654321123456789*998877665544332211", "986545842648570754445552922919330479"); + assert_parsed_expression_simplify_to("2/3", "2/3"); + assert_parsed_expression_simplify_to("9/17+5/4", "121/68"); + assert_parsed_expression_simplify_to("1/2*3/4", "3/8"); + assert_parsed_expression_simplify_to("0*2/3", "0"); + assert_parsed_expression_simplify_to("1+(1/(1+1/(1+1/(1+1))))", "8/5"); + assert_parsed_expression_simplify_to("1+2/(3+4/(5+6/(7+8)))", "155/101"); + assert_parsed_expression_simplify_to("3/4*16/12", "1"); + assert_parsed_expression_simplify_to("3/4*(8+8)/12", "1"); + assert_parsed_expression_simplify_to("916791/794976477", "305597/264992159"); + assert_parsed_expression_simplify_to("321654987123456789/112233445566778899", "3249040273974311/1133671167341201"); + assert_parsed_expression_simplify_to("0.1+0.2", "3/10"); + assert_parsed_expression_simplify_to("2^3", "8"); + assert_parsed_expression_simplify_to("(-1)*(-1)", "1"); + assert_parsed_expression_simplify_to("(-2)^2", "4"); + assert_parsed_expression_simplify_to("(-3)^3", "-27"); + assert_parsed_expression_simplify_to("(1/2)^-1", "2"); + assert_parsed_expression_simplify_to("R(2)*R(3)", "R(6)"); + assert_parsed_expression_simplify_to("2*2^P", "2*2^P"); + assert_parsed_expression_simplify_to("A^3*B*A^(-3)", "B"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("(x+1)*(x+2)", "2+3*x+x^2"); + assert_parsed_expression_simplify_to("(x+1)*(x-1)", "(-1)+x^2"); + assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); + assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3*P)"); + assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3*P)"); + assert_parsed_expression_simplify_to("A^2*BA^(-2)*B^(-2)", "1/B"); + assert_parsed_expression_simplify_to("A^(-1)*B^(-1)", "1/(A*B)"); + + // Power + assert_parsed_expression_simplify_to("3^4", "81"); + assert_parsed_expression_simplify_to("3^(-4)", "1/81"); + assert_parsed_expression_simplify_to("1256^(1/3)*x", "2*root(157,3)*x"); + assert_parsed_expression_simplify_to("1256^(-1/3)", "1/(2*root(157,3))"); + assert_parsed_expression_simplify_to("32^(-1/5)", "1/2"); + assert_parsed_expression_simplify_to("(2+3-4)^(x)", "1"); + assert_parsed_expression_simplify_to("1^x", "1"); + assert_parsed_expression_simplify_to("x^1", "x"); + assert_parsed_expression_simplify_to("0^3", "0"); + assert_parsed_expression_simplify_to("0^0", "undef"); + assert_parsed_expression_simplify_to("0^(-3)", "undef"); + assert_parsed_expression_simplify_to("4^0.5", "2"); + assert_parsed_expression_simplify_to("8^0.5", "2*R(2)"); + assert_parsed_expression_simplify_to("(12^4*3)^(0.5)", "144*R(3)"); + assert_parsed_expression_simplify_to("(2^A)^B", "2^(A*B)"); + assert_parsed_expression_simplify_to("(2*A)^B", "2^B*A^B"); + assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144*R(x)"); + assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); + assert_parsed_expression_simplify_to("R(3^2)", "3"); + assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); + assert_parsed_expression_simplify_to("R(5513219850886344455940081)", "2348024669991"); + assert_parsed_expression_simplify_to("R(154355776)", "12424"); + assert_parsed_expression_simplify_to("R(P)^2", "P"); + assert_parsed_expression_simplify_to("R(P^2)", "P"); + assert_parsed_expression_simplify_to("R((-P)^2)", "P"); + assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(P)*R(x)"); + assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); + assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); + assert_parsed_expression_simplify_to("x^(1/7)", "root(x,7)"); + assert_parsed_expression_simplify_to("x^(-1/7)", "1/root(x,7)"); + assert_parsed_expression_simplify_to("1/(3R(2))", "R(2)/6"); + assert_parsed_expression_simplify_to("X^ln(3)", "3"); + assert_parsed_expression_simplify_to("X^ln(R(3))", "R(3)"); + assert_parsed_expression_simplify_to("P^log(R(3),P)", "R(3)"); + assert_parsed_expression_simplify_to("10^log(P)", "P"); + assert_parsed_expression_simplify_to("X^ln(65)", "65"); + assert_parsed_expression_simplify_to("X^ln(PX)", "P*X"); + assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); + assert_parsed_expression_simplify_to("R(X^2)", "X"); + assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); + /* This does not reduce but should not as the integer is above + * k_maxNumberOfPrimeFactors and thus it prime decomposition might overflow + * 32 factors. */ + assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "root(1881676377434183981909562699940347954480361860897069,3)"); + + // Complex + assert_parsed_expression_simplify_to("I", "I"); + assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); + assert_parsed_expression_simplify_to("I^(3/5)", "(R(2)*R(5-R(5))+I+R(5)*I)/4"); + assert_parsed_expression_simplify_to("IIII", "1"); + assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); + assert_parsed_expression_simplify_to("Acos(9)IIln(2)", "-cos(9)*ln(2)*A"); + assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2*(2*R(2)+2*R(2)*I)^2)"); + assert_parsed_expression_simplify_to("root(5^(-I)3^9,I)", "undef"); + assert_parsed_expression_simplify_to("I^I", "undef"); + + //Functions + assert_parsed_expression_simplify_to("abs(P)", "P"); + assert_parsed_expression_simplify_to("abs(-P)", "P"); + assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); + assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); + assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); + assert_parsed_expression_simplify_to("conj(1/2)", "1/2"); + assert_parsed_expression_simplify_to("quo(19,3)", "6"); + assert_parsed_expression_simplify_to("quo(19,0)", "undef"); + assert_parsed_expression_simplify_to("quo(-19,3)", "-7"); + assert_parsed_expression_simplify_to("rem(19,3)", "1"); + assert_parsed_expression_simplify_to("rem(-19,3)", "2"); + assert_parsed_expression_simplify_to("rem(19,0)", "undef"); + assert_parsed_expression_simplify_to("99!", "933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000"); + assert_parsed_expression_simplify_to("floor(-1.3)", "-2"); + assert_parsed_expression_simplify_to("frac(-1.3)", "7/10"); + assert_parsed_expression_simplify_to("gcd(123,278)", "1"); + assert_parsed_expression_simplify_to("gcd(11,121)", "11"); + assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); + assert_parsed_expression_simplify_to("lcm(11,121)", "121"); + assert_parsed_expression_simplify_to("R(4)", "2"); + assert_parsed_expression_simplify_to("root(4,3)", "root(4,3)"); + assert_parsed_expression_simplify_to("root(4,P)", "4^(1/P)"); + assert_parsed_expression_simplify_to("root(27,3)", "3"); + assert_parsed_expression_simplify_to("round(4.235,2)", "106/25"); + assert_parsed_expression_simplify_to("round(4.23,0)", "4"); + assert_parsed_expression_simplify_to("round(4.9,0)", "5"); + assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); + assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); + assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); + assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); + assert_parsed_expression_simplify_to("re(1/2)", "1/2"); + + // Trigonometry + // -- sin/cos -> tan + assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); + assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); + assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "tan(x)*P"); + assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); + assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); + assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); + assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos((5*P)/21)/sin((8*P)/17)"); + assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-(3*P)/tan(P/21)"); + assert_parsed_expression_simplify_to("cos(62P/21)/(P*3*sin(62P/21))", "-1/(3*tan(P/21)*P)"); + assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3*tan(P/21)*P"); + assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3*P)"); + assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)*P)"); + assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-(2*ln(3))/(tan(P/62)*P)"); + // -- cos + assert_parsed_expression_simplify_to("cos(0)", "1"); + assert_parsed_expression_simplify_to("cos(P)", "-1"); + assert_parsed_expression_simplify_to("cos(P*4/7)", "-cos((3*P)/7)"); + assert_parsed_expression_simplify_to("cos(P*35/29)", "-cos((6*P)/29)"); + assert_parsed_expression_simplify_to("cos(-P*35/29)", "-cos((6*P)/29)"); + assert_parsed_expression_simplify_to("cos(P*340000)", "1"); + assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); + assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(R(2)*P)"); + assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); + assert_parsed_expression_simplify_to("cos(P/12)", "(R(2)+R(6))/4"); + assert_parsed_expression_simplify_to("cos(-P/12)", "(R(2)+R(6))/4"); + assert_parsed_expression_simplify_to("cos(-P17/8)", "R(2+R(2))/2"); + assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); + assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); + assert_parsed_expression_simplify_to("cos(41P/5)", "(1+R(5))/4"); + assert_parsed_expression_simplify_to("cos(7P/10)", "-(R(2)*R(5-R(5)))/4"); + assert_parsed_expression_simplify_to("cos(0)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(61200000)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(39330)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-765/2)", "R(2+R(2))/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-60)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/5)", "(1+R(5))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); + // -- sin + assert_parsed_expression_simplify_to("sin(0)", "0"); + assert_parsed_expression_simplify_to("sin(P)", "0"); + assert_parsed_expression_simplify_to("sin(P*35/29)", "-sin((6*P)/29)"); + assert_parsed_expression_simplify_to("sin(-P*35/29)", "sin((6*P)/29)"); + assert_parsed_expression_simplify_to("sin(P*340000)", "0"); + assert_parsed_expression_simplify_to("sin(P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(P/12)", "((-R(2))+R(6))/4"); + assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); + assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(R(2)*P)"); + assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); + assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(2-R(2))/2"); + assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); + assert_parsed_expression_simplify_to("sin(-3P/10)", "((-1)-R(5))/4"); + assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); + assert_parsed_expression_simplify_to("sin(17P/5)", "-(R(2)*R(5+R(5)))/4"); + assert_parsed_expression_simplify_to("sin(P/5)", "(R(2)*R(5-R(5)))/4"); + assert_parsed_expression_simplify_to("sin(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(15)", "((-R(2))+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(39330)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-765/2)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(1230)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(612)", "-(R(2)*R(5+R(5)))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(36)", "(R(2)*R(5-R(5)))/4", Expression::AngleUnit::Degree); + // -- tan + assert_parsed_expression_simplify_to("tan(0)", "0"); + assert_parsed_expression_simplify_to("tan(P)", "0"); + assert_parsed_expression_simplify_to("tan(P*35/29)", "tan((6*P)/29)"); + assert_parsed_expression_simplify_to("tan(-P*35/29)", "-tan((6*P)/29)"); + assert_parsed_expression_simplify_to("tan(P*340000)", "0"); + assert_parsed_expression_simplify_to("tan(P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/12)", "(-2)+R(3)"); + assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(R(2)*P)"); + assert_parsed_expression_simplify_to("tan(1311P/6)", "undef"); + assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); + assert_parsed_expression_simplify_to("tan(41P/6)", "-R(3)/3"); + assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); + assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/10)", "-(R(5)*R(5-2*R(5)))/5"); + assert_parsed_expression_simplify_to("tan(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-15)", "(-2)+R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(39330)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(1230)", "-R(3)/3", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180045)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); + // -- acos + assert_parsed_expression_simplify_to("acos(-1/2)", "(2*P)/3"); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef"); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(3/2))", "3/2"); + assert_parsed_expression_simplify_to("cos(acos(3/2))", "undef"); + assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); + assert_parsed_expression_simplify_to("acos(cos(4P/7))", "(4*P)/7"); + assert_parsed_expression_simplify_to("acos(-cos(2))", "(-2)+P"); + assert_parsed_expression_simplify_to("acos(-1/2)", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(190))", "170", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(190))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(12))", "12", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Expression::AngleUnit::Degree); + // -- asin + assert_parsed_expression_simplify_to("asin(-1/2)", "-P/6"); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef"); + assert_parsed_expression_simplify_to("asin(sin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(3/2))", "undef"); + assert_parsed_expression_simplify_to("asin(sin(3/2))", "3/2"); + assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); + assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); + assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(190))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(32))", "32", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(400))", "40", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Expression::AngleUnit::Degree); + // -- atan + assert_parsed_expression_simplify_to("atan(-1)", "-P/4"); + assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)"); + assert_parsed_expression_simplify_to("atan(tan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(5/2))", "5/2"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); + assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); + assert_parsed_expression_simplify_to("atan(-1)", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(120))", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(R(3))", "60", Expression::AngleUnit::Degree); + + // Logarithm + assert_parsed_expression_simplify_to("log(12925)", "2*log(5)+log(11)+log(47)"); + assert_parsed_expression_simplify_to("ln(12925)", "2*ln(5)+ln(11)+ln(47)"); + assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "(-2*log(5,6))+log(7,6)+3*log(11,6)+log(17,6)-log(47,6)"); + assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); + assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "undef"); + assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); + assert_parsed_expression_simplify_to("ln(X^3)", "3"); + assert_parsed_expression_simplify_to("log(10)", "1"); + assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); + assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); + assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); + assert_parsed_expression_simplify_to("ln(X^(IP/7))", "(P*I)/7"); + assert_parsed_expression_simplify_to("log(10^24)", "24"); + assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); + assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); + assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); + assert_parsed_expression_simplify_to("log(26061622162116)", "2*log(2)+log(3)+log(2171801846843)"); + assert_parsed_expression_simplify_to("log(26061622162116/5)", "2*log(2)+log(3)-log(5)+log(2171801846843)"); + + // Factorial + assert_parsed_expression_simplify_to("1/3!", "1/6"); + assert_parsed_expression_simplify_to("5!", "120"); + assert_parsed_expression_simplify_to("(1/3)!", "undef"); + assert_parsed_expression_simplify_to("P!", "undef"); + assert_parsed_expression_simplify_to("X!", "undef"); + + // Root at denominator + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "(-R(2))+R(3)"); + assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); + assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); + assert_parsed_expression_simplify_to("1/(2R(2)-4)", "((-2)-R(2))/4"); + assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "(2+R(2))/4"); + assert_parsed_expression_simplify_to("45^2", "2025"); + assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2*ln(3))"); + assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); + + // Common operation mix + assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); + assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3*R(2)-2*R(3)+25*P)/25"); + assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); + assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "(-40*cos(2))+ln(3)+cos(3)*ln(5)+4*A*B-3*A*B*C"); + assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "4+3*cos(2)+6*ln(5)+7*A"); + assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "(887+600*cos(2)+1200*ln(5)+1460*A)/200"); + assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "(70*A^2+23*A*B+72*A*B*C)/10"); + assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "4*A^3+A^3*B^2"); + assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13*A^3"); + assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "4*A^3+A^5"); + assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "((-9)-5*cos(4)+30*A*B+5*A*B*C*D)/5"); + assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); + assert_parsed_expression_simplify_to("(2+R(6))^2", "(2+R(6))^2"); + assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)*ln(2)+P"); + + + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); // TODO: Newton binome //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? - // Addition Matrix + + // Matrix #if MATRIX_EXACT_REDUCING + // Addition Matrix assert_parsed_expression_simplify_to("1+[[1,2,3][4,5,6]]", "[[2,3,4][5,6,7]]"); assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+1", "[[2,3,4][5,6,7]]"); assert_parsed_expression_simplify_to("[[1,2][3,4]]+[[1,2,3][4,5,6]]", "undef"); @@ -126,400 +539,5 @@ QUIZ_CASE(poincare_simplify_easy) { #else assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "R([[4,2][P/7,1]])"); #endif - /* Complex */ - assert_parsed_expression_simplify_to("I", "I"); - assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); - assert_parsed_expression_simplify_to("I^(3/5)", "X^(IP3/10)"); - //Functions - assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); - assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); - assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); - assert_parsed_expression_simplify_to("conj(1/2)", "1/2"); - assert_parsed_expression_simplify_to("quo(19,3)", "6"); - assert_parsed_expression_simplify_to("quo(19,0)", "undef"); - assert_parsed_expression_simplify_to("quo(-19,3)", "-7"); - assert_parsed_expression_simplify_to("rem(19,3)", "1"); - assert_parsed_expression_simplify_to("rem(-19,3)", "2"); - assert_parsed_expression_simplify_to("rem(19,0)", "undef"); - assert_parsed_expression_simplify_to("99!", "933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000"); - assert_parsed_expression_simplify_to("floor(-1.3)", "-2"); - assert_parsed_expression_simplify_to("frac(-1.3)", "0.7"); - assert_parsed_expression_simplify_to("gcd(123,278)", "1"); - assert_parsed_expression_simplify_to("gcd(11,121)", "11"); - assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); - assert_parsed_expression_simplify_to("lcm(11,121)", "121"); - assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); - assert_parsed_expression_simplify_to("round(4.235,2)", "4.24"); - assert_parsed_expression_simplify_to("round(4.23,0)", "4"); - assert_parsed_expression_simplify_to("round(4.9,0)", "5"); - assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); - assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); - assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); - assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); - assert_parsed_expression_simplify_to("re(1/2)", "1/2"); - - assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); - assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3R(2)-2R(3)+25P)/25"); - assert_parsed_expression_simplify_to("-1/3", "-1/3"); - assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10cos(2)"); - assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "-(R(2)-R(3))"); - assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); - assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); - assert_parsed_expression_simplify_to("1/(2R(2)-4)", "-R(2)/4-1/2"); - assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "R(2)/4+1/2"); - assert_parsed_expression_simplify_to("5!", "120"); - assert_parsed_expression_simplify_to("1/3!", "1/6"); - assert_parsed_expression_simplify_to("(1/3)!", "undef"); - assert_parsed_expression_simplify_to("P!", "undef"); - assert_parsed_expression_simplify_to("X!", "undef"); - assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); - assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos(5P/21)/sin(8P/17)"); - assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-3P/tan(P/21)"); - assert_parsed_expression_simplify_to("cos(62P/21)/(P*3sin(62P/21))", "-1/(3Ptan(P/21))"); - assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3Ptan(P/21)"); - assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3P)"); - assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)P)"); - assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-2ln(3)/(tan(P/62)P)"); - assert_parsed_expression_simplify_to("0000.000000", "0"); - assert_parsed_expression_simplify_to(".000000", "0"); - assert_parsed_expression_simplify_to("0000", "0"); - assert_parsed_expression_simplify_to("0.1234567", "1234567/10000000"); - assert_parsed_expression_simplify_to("123.4567", "1234567/10000"); - assert_parsed_expression_simplify_to("0.1234", "1234/10000"); - assert_parsed_expression_simplify_to("0.1234000", "1234/10000"); - assert_parsed_expression_simplify_to("001234000", "1234000"); - assert_parsed_expression_simplify_to("001.234000E3", "1234"); - assert_parsed_expression_simplify_to("001234000E-4", "1234/10"); - assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); - assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); - assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); - assert_parsed_expression_simplify_to("3/4+5/4-12+1/567", "-5669/567"); - assert_parsed_expression_simplify_to("34/78+67^(-1)", "1178/2613"); - assert_parsed_expression_simplify_to("root(4,5)", "4^(1/5)"); - assert_parsed_expression_simplify_to("R(4)", "2"); - assert_parsed_expression_simplify_to("3^4", "81"); - assert_parsed_expression_simplify_to("3^(-4)", "1/81"); - assert_parsed_expression_simplify_to("12348/34564", "3087/8641"); - assert_parsed_expression_simplify_to("1256^(1/3)*x", "2*157^(1/3)*x"); - assert_parsed_expression_simplify_to("1256^(-1/3)", "2^(-1)*157^(-1/3)"); - assert_parsed_expression_simplify_to("32^(-1/5)", "1/2"); - assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); - assert_parsed_expression_simplify_to("1-0.3-0.7", "0"); - assert_parsed_expression_simplify_to("(2+3-4)^(x)", "1"); - assert_parsed_expression_simplify_to("1^x", "1"); - assert_parsed_expression_simplify_to("x^1", "x"); - assert_parsed_expression_simplify_to("0^3", "0"); - assert_parsed_expression_simplify_to("0^0", "undef"); - assert_parsed_expression_simplify_to("0^(-3)", "undef"); - assert_parsed_expression_simplify_to("0*x+B", "B"); - assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); - assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); - assert_parsed_expression_simplify_to("2+0", "2"); - assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "-3ABC+4AB-40cos(2)+cos(3)ln(5)+ln(3)"); - assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "7A+3cos(2)+6ln(5)+4"); - assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "73*A/10+3cos(2)+6ln(5)+4435/1000"); - assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "36/5*ABC+23/10*AB+7A^2"); - assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "36/5*A^6*B^(x+2)+72/5*A^4*B^(x+2)"); - assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "ABD+3AB+ACD+3AC"); - assert_parsed_expression_simplify_to("A/B", "A/B"); - assert_parsed_expression_simplify_to("-5P+3P", "-2P"); - assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); - assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "A^3*B^2+4A^3"); - assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13A^3"); - assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "A^5+4A^3"); - assert_parsed_expression_simplify_to("4^0.5", "2"); - assert_parsed_expression_simplify_to("8^0.5", "2R(2)"); - assert_parsed_expression_simplify_to("(12^4*3)^(0.5)", "144*R(3)"); - assert_parsed_expression_simplify_to("(2^A)^B", "2^(AB)"); - assert_parsed_expression_simplify_to("(2*A)^B", "2^B*A^B"); - assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144R(x)"); - assert_parsed_expression_simplify_to("45^2", "2025"); - assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "-7-A"); - assert_parsed_expression_simplify_to("(1/2)*A/B", "A/(2B)"); - assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "ABCD+6AB-cos(4)-9/5"); - assert_parsed_expression_simplify_to("1+2", "3"); - assert_parsed_expression_simplify_to("123456789123456789+112233445566778899", "235690234690235688"); - assert_parsed_expression_simplify_to("1+2+3+4+5+6", "21"); - assert_parsed_expression_simplify_to("1-2+3-4+5-6", "-3"); - assert_parsed_expression_simplify_to("987654321123456789*998877665544332211", "986545842648570754445552922919330479"); - assert_parsed_expression_simplify_to("2/3", "2/3"); - assert_parsed_expression_simplify_to("9/17+5/4", "121/68"); - assert_parsed_expression_simplify_to("1/2*3/4", "3/8"); - assert_parsed_expression_simplify_to("0*2/3", "0"); - assert_parsed_expression_simplify_to("1+(1/(1+1/(1+1/(1+1))))", "8/5"); - assert_parsed_expression_simplify_to("1+2/(3+4/(5+6/(7+8)))", "155/101"); - assert_parsed_expression_simplify_to("3/4*16/12", "1"); - assert_parsed_expression_simplify_to("3/4*(8+8)/12", "1"); - assert_parsed_expression_simplify_to("916791/794976477", "305597/264992159"); - assert_parsed_expression_simplify_to("321654987123456789/112233445566778899", "3249040273974311/1133671167341201"); - assert_parsed_expression_simplify_to("0.1+0.2", "3/10"); - assert_parsed_expression_simplify_to("2^3", "8"); - assert_parsed_expression_simplify_to("(-1)*(-1)", "1"); - assert_parsed_expression_simplify_to("(-2)^2", "4"); - assert_parsed_expression_simplify_to("(-3)^3", "-27"); - assert_parsed_expression_simplify_to("(1/2)^-1", "2"); - assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); - assert_parsed_expression_simplify_to("R(3^2)", "3"); - assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); - assert_parsed_expression_simplify_to("R(15241578780673678515622620750190521)", "R(15241578780673678515622620750190521)"); - assert_parsed_expression_simplify_to("R(154355776)", "12424"); - assert_parsed_expression_simplify_to("R(P)^2", "P"); - assert_parsed_expression_simplify_to("R(P^2)", "P"); - assert_parsed_expression_simplify_to("R((-P)^2)", "P"); - assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(xP)"); - assert_parsed_expression_simplify_to("abs(P)", "P"); - assert_parsed_expression_simplify_to("abs(-P)", "P"); - assert_parsed_expression_simplify_to("R(2)*R(3)", "R(6)"); - assert_parsed_expression_simplify_to("2*2^P", "2*2^P"); - assert_parsed_expression_simplify_to("A-A", "0"); - assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); - assert_parsed_expression_simplify_to("A^3*B*A^(-3)", "B"); - assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); - assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); - assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); - assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); - assert_parsed_expression_simplify_to("(x+1)*(x+2)", "x^2+3x+2"); - - assert_parsed_expression_simplify_to("(x+1)*(x-1)", "-1+x^2"); - assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); - assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3P)"); - assert_parsed_expression_simplify_to("A^2*BA^(-2)*B^(-2)", "1/B"); - assert_parsed_expression_simplify_to("A^(-1)*B^(-1)", "1/(AB)"); - assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3P)"); - assert_parsed_expression_simplify_to("-A", "-A"); - assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(P+x+3)/((x+1)(P+2))"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(P+1)/(x^2*P)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(Px+1)/(x^3*P)"); - assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(4*x^2*P+3P)/(x^3*P)"); - assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); - assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); - assert_parsed_expression_simplify_to("x^(1/7)", "root(x, 7)"); - assert_parsed_expression_simplify_to("x^(-1/7)", "1/root(x, 7)"); - assert_parsed_expression_simplify_to("log(12925)", "log(47)+log(11)+2*log(5)"); - assert_parsed_expression_simplify_to("ln(12925)", "ln(47)+ln(11)+2*ln(5)"); - assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "log(7, 6)+3log(11, 6)+log(17,6)-log(47,6)-2*log(5,6)"); - assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); - assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "undef"); - assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); - assert_parsed_expression_simplify_to("(2+R(6))^2", "(2+R(6))^2"); // Check for parenthesis - assert_parsed_expression_simplify_to("cos(0)", "1"); - assert_parsed_expression_simplify_to("cos(P)", "-1"); - assert_parsed_expression_simplify_to("cos(P*4/7)", "-cos(3P/7)"); - assert_parsed_expression_simplify_to("cos(P*35/29)", "-cos(P*6/29)"); - assert_parsed_expression_simplify_to("cos(-P*35/29)", "-cos(P*6/29)"); - assert_parsed_expression_simplify_to("cos(P*340000)", "1"); - assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); - assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(P*R(2))"); - assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); - assert_parsed_expression_simplify_to("cos(P/12)", "(R(6)+R(2))/4"); - assert_parsed_expression_simplify_to("cos(-P/12)", "(R(6)+R(2))/4"); - assert_parsed_expression_simplify_to("cos(-P17/8)", "R(R(2)+2)/2"); - assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); - assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); - assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); - assert_parsed_expression_simplify_to("cos(41P/5)", "(5^(1/2)+1)*4^(-1)"); - assert_parsed_expression_simplify_to("cos(7P/10)", "-R(5/8-R(5)/8)"); - assert_parsed_expression_simplify_to("sin(0)", "0"); - assert_parsed_expression_simplify_to("sin(P)", "0"); - assert_parsed_expression_simplify_to("sin(P*35/29)", "-sin(P*6/29)"); - assert_parsed_expression_simplify_to("sin(-P*35/29)", "sin(P*6/29)"); - assert_parsed_expression_simplify_to("sin(P*340000)", "0"); - assert_parsed_expression_simplify_to("sin(P*340001)", "0"); - assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); - assert_parsed_expression_simplify_to("sin(P/12)", "(R(6)-R(2))/4"); - assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); - assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(P*R(2))"); - assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); - assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(-R(2)+2)/2"); - assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); - assert_parsed_expression_simplify_to("sin(-3P/10)", "(-1-R(5))/4"); - assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); - assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); - assert_parsed_expression_simplify_to("sin(17P/5)", "-R(5/8+R(5)/8)"); - assert_parsed_expression_simplify_to("sin(P/5)", "R(5/8-R(5)/8)"); - - assert_parsed_expression_simplify_to("tan(0)", "0"); - assert_parsed_expression_simplify_to("tan(P)", "0"); - assert_parsed_expression_simplify_to("tan(P*35/29)", "tan(P*6/29)"); - assert_parsed_expression_simplify_to("tan(-P*35/29)", "-tan(P*6/29)"); - assert_parsed_expression_simplify_to("tan(P*340000)", "0"); - assert_parsed_expression_simplify_to("tan(P*340001)", "0"); - assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); - assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/12)", "R(3)-2"); - assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(P*R(2))"); - assert_parsed_expression_simplify_to("tan(1311P/6)", "undef"); - assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); - assert_parsed_expression_simplify_to("tan(41P/6)", "-1/R(3)"); - assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); - assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/10)", "-R(1-2/R(5))"); - assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); - assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); - assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "P*tan(x)"); - assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); - assert_parsed_expression_simplify_to("56^56", "79164324866862966607842406018063254671922245312646690223362402918484170424104310169552592050323456"); - - assert_parsed_expression_simplify_to("acos(-1/2)", "P*2*3^(-1)"); - assert_parsed_expression_simplify_to("acos(-1.2)", "undef"); - assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3"); - assert_parsed_expression_simplify_to("acos(cos(3/2))", "3/2"); - assert_parsed_expression_simplify_to("cos(acos(3/2))", "undef"); - assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); - assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); - assert_parsed_expression_simplify_to("acos(cos(4P/7))", "4P/7"); - assert_parsed_expression_simplify_to("acos(-cos(2))", "P-2"); - assert_parsed_expression_simplify_to("asin(-1/2)", "P*(-6)^(-1)"); - assert_parsed_expression_simplify_to("asin(-1.2)", "undef"); - assert_parsed_expression_simplify_to("asin(sin(2/3))", "2/3"); - assert_parsed_expression_simplify_to("sin(asin(2/3))", "2/3"); - assert_parsed_expression_simplify_to("sin(asin(3/2))", "sin(asin(3/2))"); - assert_parsed_expression_simplify_to("asin(sin(3/2))", "3/2"); - assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); - assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); - assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); - assert_parsed_expression_simplify_to("atan(-1)", "P*(-4)^(-1)"); - assert_parsed_expression_simplify_to("atan(-1.2)", "atan(-1.2)"); - assert_parsed_expression_simplify_to("atan(tan(2/3))", "2/3"); - assert_parsed_expression_simplify_to("tan(atan(2/3))", "2/3"); - assert_parsed_expression_simplify_to("tan(atan(5/2))", "5/2"); - assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); - assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); - assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); - assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); - assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); - - assert_parsed_expression_simplify_to("cos(0)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180)", "-1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(61200000)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(39330)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(15)", "(R(6)+R(2))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-15)", "(R(6)+R(2))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-765/2)", "R(R(2)+2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-60)", "1/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/5)", "(5^(1/2)+1)*4^(-1)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(0)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200000)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(15)", "(R(6)-R(2))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(39330)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-765/2)", "-R(-R(2)+2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(1230)", "1/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(612)", "-R(5/8+R(5)/8)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(36)", "R(5/8-R(5)/8)", Expression::AngleUnit::Degree); - - assert_parsed_expression_simplify_to("tan(0)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200000)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-15)", "R(3)-2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(39330)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(1230)", "-1/R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180045)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(-1/2)", "120", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(-1.2)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(190))", "acos(cos(190))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(75))", "75", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(190))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(75))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(12))", "12", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(-1.2)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(75))", "75", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(75))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(190))", "sin(asin(190))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(32))", "32", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(400))", "asin(sin(400))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(-1)", "-45", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(-1.2)", "atan(-1.2)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(120))", "120", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(R(3))", "60", Expression::AngleUnit::Degree); - - assert_parsed_expression_simplify_to("1/(3R(2))", "R(2)/6"); - assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2ln(3))"); - - - assert_parsed_expression_simplify_to("A+B-A-B", "0"); - assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); - assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); - assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); - assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)ln(2)+P"); - assert_parsed_expression_simplify_to("ln(X^3)", "3"); - assert_parsed_expression_simplify_to("log(10)", "1"); - assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); - assert_parsed_expression_simplify_to("X^ln(3)", "3"); - assert_parsed_expression_simplify_to("X^ln(R(3))", "R(3)"); - assert_parsed_expression_simplify_to("P^log(R(3),P)", "R(3)"); - assert_parsed_expression_simplify_to("10^log(P)", "P"); - assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); - assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); - assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); - assert_parsed_expression_simplify_to("X^ln(65)", "65"); - assert_parsed_expression_simplify_to("X^ln(PX)", "PX"); - assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); - assert_parsed_expression_simplify_to("R(X^2)", "X"); - assert_parsed_expression_simplify_to("ln(X^(IP/7))", "IP/7"); - assert_parsed_expression_simplify_to("log(10^24)", "24"); - assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); - assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); - - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) - assert_parsed_expression_simplify_to("IIII", "1"); - assert_parsed_expression_simplify_to("Acos(9)IIln(2)", "-Acos(9)ln(2)"); - assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2(2R(2)+2R(2)*I)^2)"); - assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); - assert_parsed_expression_simplify_to("999^999", "999^999"); - assert_parsed_expression_simplify_to("999^-999", "1/999^999"); - assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); - assert_parsed_expression_simplify_to("0^0", "undef"); - assert_parsed_expression_simplify_to("x^0", "1"); - assert_parsed_expression_simplify_to("P^0", "1"); - assert_parsed_expression_simplify_to("A^0", "1"); - assert_parsed_expression_simplify_to("(-3)^0", "1"); - assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); - assert_parsed_expression_simplify_to("root(5^(-I)3^9,I)", "undef"); - assert_parsed_expression_simplify_to("I^I", "undef"); - - assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); - - // This does not work but should not as it is above k_biggestPrimeFactorizedInteger - assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "1881676377434183981909562699940347954480361860897069^(1/3)"); - - assert_parsed_expression_simplify_to("log(26061622162116)", "log(26061622162116)"); - assert_parsed_expression_simplify_to("log(26061622162116/5)", "log(26061622162116)-log(5)"); } From 01d456299f251c86c22d245ff54e998c13f45415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 17:03:33 +0100 Subject: [PATCH 54/77] [apps] Add comment Change-Id: I53bdeb08625a2699408f563b0b3ed6c45744d01d --- apps/calculation/calculation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 1194cb8f7..d86664b0b 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -66,6 +66,8 @@ void Calculation::reset() { void Calculation::setContent(const char * c, Context * context) { reset(); m_input = Expression::parse(c); + /* We do not store directly the text enter by the user but its serialization + * to be able to compare it to the exact ouput text. */ m_input->writeTextInBuffer(m_inputText, sizeof(m_inputText)); m_exactOutput = input()->clone(); Expression::Simplify(&m_exactOutput, *context); From 957cf30a0cbc7f192ddd4f11624a477c44f3490d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 17:03:54 +0100 Subject: [PATCH 55/77] [Makefile] Be able to print expressions when building app Change-Id: Iabc96d1e9534fd5377491f87325191af78310f32 --- poincare/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/poincare/Makefile b/poincare/Makefile index a4bd1d233..507b945c5 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -121,6 +121,7 @@ tests += $(addprefix poincare/test/,\ ifdef POINCARE_TESTS_PRINT_EXPRESSIONS tests += poincare/src/expression_debug.o +objs += poincare/src/expression_debug.o SFLAGS += -DPOINCARE_TESTS_PRINT_EXPRESSIONS=1 endif From 17a643a5a5c5ff9fd17aa726fba2c36eedb295fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 28 Nov 2017 17:42:06 +0100 Subject: [PATCH 56/77] [poincare] WirteToText always use MultiplicationSign and Layouts use MiddleDot Change-Id: I4ffff6e6634deba376f083ef382e6bec0ff0883b --- poincare/include/poincare/complex.h | 2 +- poincare/src/complex.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index c5120e6a8..bdd5ad43d 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -81,7 +81,7 @@ private: constexpr static int k_maxComplexBufferLength = 14+14+7+1; /* convertComplexToText and convertFloatToTextPrivate return the string length * of the buffer (does not count the 0 last char)*/ - int convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const; + int convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, char multiplicationSign) const; static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Expression::FloatDisplayMode mode); ExpressionLayout * createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const; ExpressionLayout * createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const; diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index cce5517fc..390c858c0 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -200,7 +200,7 @@ T Complex::toScalar() const { template int Complex::writeTextInBuffer(char * buffer, int bufferSize) const { - return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat()); + return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat(), Ion::Charset::MultiplicationSign); } template @@ -253,7 +253,7 @@ Complex * Complex::templatedApproximate(Context& context, Expression::Angl } template -int Complex::convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat) const { +int Complex::convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat, char multiplicationSpecialChar) const { assert(displayMode != Expression::FloatDisplayMode::Default); int numberOfChars = 0; if (std::isnan(m_a) || std::isnan(m_b)) { @@ -263,7 +263,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: if (r() != 1 || th() == 0) { numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode); if (r() != 0 && th() != 0 && bufferSize > numberOfChars+1) { - buffer[numberOfChars++] = Ion::Charset::MiddleDot; + buffer[numberOfChars++] = multiplicationSpecialChar; // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } @@ -278,7 +278,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); if (bufferSize > numberOfChars+3) { - buffer[numberOfChars++] = Ion::Charset::MiddleDot; + buffer[numberOfChars++] = multiplicationSpecialChar; buffer[numberOfChars++] = Ion::Charset::IComplex; buffer[numberOfChars++] = ')'; buffer[numberOfChars] = 0; @@ -297,7 +297,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } if (m_b != 1 && m_b != -1 && m_b != 0) { numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); - buffer[numberOfChars++] = Ion::Charset::MiddleDot; + buffer[numberOfChars++] = multiplicationSpecialChar; } if (m_b == -1 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '-'; @@ -472,7 +472,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl template ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const { char buffer[k_maxComplexBufferLength]; - int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, Expression::ComplexFormat::Cartesian); + int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot); return new StringLayout(buffer, numberOfChars); } From 40da35f17c03f5192335b9e9f9a6374da10da57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Nov 2017 09:44:35 +0100 Subject: [PATCH 57/77] [poincare] Clean and sort tests Change-Id: Ia6a66f5d6e568731ba4127c94a073f7fd62bc26a --- poincare/Makefile | 7 +- poincare/test/addition.cpp | 24 + poincare/test/complex.cpp | 107 +--- poincare/test/convert_expression_to_text.cpp | 122 ++++- poincare/test/factorial.cpp | 15 + poincare/test/function.cpp | 34 ++ poincare/test/helper.cpp | 24 + poincare/test/helper.h | 1 + poincare/test/logarithm.cpp | 28 + poincare/test/matrix.cpp | 96 ++++ poincare/test/multiplication.cpp | 84 +++ poincare/test/power.cpp | 49 ++ poincare/test/product.cpp | 33 -- poincare/test/rational.cpp | 54 ++ poincare/test/simplify_addition.cpp | 14 - poincare/test/simplify_easy.cpp | 543 ------------------- poincare/test/simplify_mix.cpp | 48 ++ poincare/test/simplify_product.cpp | 34 -- poincare/test/trigo.cpp | 178 ++++++ 19 files changed, 759 insertions(+), 736 deletions(-) create mode 100644 poincare/test/factorial.cpp create mode 100644 poincare/test/logarithm.cpp create mode 100644 poincare/test/multiplication.cpp delete mode 100644 poincare/test/product.cpp create mode 100644 poincare/test/rational.cpp delete mode 100644 poincare/test/simplify_addition.cpp delete mode 100644 poincare/test/simplify_easy.cpp create mode 100644 poincare/test/simplify_mix.cpp delete mode 100644 poincare/test/simplify_product.cpp diff --git a/poincare/Makefile b/poincare/Makefile index 507b945c5..ccb5d3ad2 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -105,14 +105,17 @@ tests += $(addprefix poincare/test/,\ complex.cpp\ convert_expression_to_text.cpp\ division.cpp\ + factorial.cpp\ function.cpp\ helper.cpp\ integer.cpp\ + logarithm.cpp\ matrix.cpp\ + multiplication.cpp\ parser.cpp\ power.cpp\ - product.cpp\ - simplify_easy.cpp\ + rational.cpp\ + simplify_mix.cpp\ subtraction.cpp\ symbol.cpp\ trigo.cpp\ diff --git a/poincare/test/addition.cpp b/poincare/test/addition.cpp index 58f7f8786..ce0fad89b 100644 --- a/poincare/test/addition.cpp +++ b/poincare/test/addition.cpp @@ -31,3 +31,27 @@ QUIZ_CASE(poincare_addition_evaluate) { assert_parsed_expression_evaluates_to("[[1,2+I][3,4][5,6]]+[[1,2+I][3,4][5,6]]", f, 3, 2); #endif } + +QUIZ_CASE(poincare_addition_simplify) { + assert_parsed_expression_simplify_to("2+1", "3"); + assert_parsed_expression_simplify_to("2+A", "2+A"); + assert_parsed_expression_simplify_to("1+2+3+4+5+6+7", "28"); + assert_parsed_expression_simplify_to("1+2+3+4+5+A+6+7", "28+A"); + assert_parsed_expression_simplify_to("(0+0)", "0"); + assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10*cos(2)"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "(2347+882*ln(2))/882"); + assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); + assert_parsed_expression_simplify_to("-5P+3P", "-2*P"); + assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "(-7)-A"); + assert_parsed_expression_simplify_to("1+2", "3"); + assert_parsed_expression_simplify_to("A-A", "0"); + assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); + assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); + assert_parsed_expression_simplify_to("-A", "-A"); + assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+P+x)/(2+P+2*x+P*x)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(P*x^2)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+P*x)/(P*x^3)"); + assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); + assert_parsed_expression_simplify_to("A+B-A-B", "0"); + assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); +} diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp index 1cfc82be3..c78628c7b 100644 --- a/poincare/test/complex.cpp +++ b/poincare/test/complex.cpp @@ -5,102 +5,9 @@ #include #include #include +#include "helper.h" using namespace Poincare; -constexpr Expression::FloatDisplayMode DecimalDisplay = Expression::FloatDisplayMode::Decimal; -constexpr Expression::FloatDisplayMode ScientificDisplay = Expression::FloatDisplayMode::Scientific; -constexpr Expression::ComplexFormat Cartesian = Expression::ComplexFormat::Cartesian; -constexpr Expression::ComplexFormat Polar = Expression::ComplexFormat::Polar; - -template -void assert_cartesian_complex_prints_to(T a, T b, const char * result, Expression::FloatDisplayMode mode = ScientificDisplay, Expression::ComplexFormat format = Cartesian, int significantDigits = 7, int bufferSize = 13+13+7+1) { - quiz_print(result); - - int tagSize = 8; - unsigned char tag = 'X'; - char * taggedBuffer = new char[bufferSize+2*tagSize]; - memset(taggedBuffer, tag, bufferSize+2*tagSize); - char * buffer = taggedBuffer + tagSize; - - if (b == 0) { - Complex::convertFloatToText(a, buffer, bufferSize, significantDigits, mode); - } else { - Preferences::sharedPreferences()->setComplexFormat(format); - Preferences::sharedPreferences()->setDisplayMode(mode); - Complex::Cartesian(a, b).writeTextInBuffer(buffer, bufferSize); - } - - for (int i=0; ir() == 1.0e155*M_SQRT2 && d->th() == -M_PI_4); delete d; } + +QUIZ_CASE(poincare_complex_simplify) { + assert_parsed_expression_simplify_to("I", "I"); + assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); + assert_parsed_expression_simplify_to("I^(3/5)", "(R(2)*R(5-R(5))+I+R(5)*I)/4"); + assert_parsed_expression_simplify_to("IIII", "1"); + assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); + assert_parsed_expression_simplify_to("Acos(9)IIln(2)", "-cos(9)*ln(2)*A"); + assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2*(2*R(2)+2*R(2)*I)^2)"); + assert_parsed_expression_simplify_to("root(5^(-I)3^9,I)", "undef"); + assert_parsed_expression_simplify_to("I^I", "undef"); +} diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index e51a2d3b6..b90e27e7d 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -5,32 +5,92 @@ #include #include #include +#include "helper.h" using namespace Poincare; +constexpr Expression::FloatDisplayMode DecimalDisplay = Expression::FloatDisplayMode::Decimal; +constexpr Expression::FloatDisplayMode ScientificDisplay = Expression::FloatDisplayMode::Scientific; +constexpr Expression::ComplexFormat Cartesian = Expression::ComplexFormat::Cartesian; +constexpr Expression::ComplexFormat Polar = Expression::ComplexFormat::Polar; -void assert_expression_prints_to(Expression * e, const char * result, int bufferSize = 250) { +template +void assert_float_prints_to(T a, const char * result, Expression::FloatDisplayMode mode = ScientificDisplay, int significantDigits = 7, int bufferSize = 250) { quiz_print(result); - char * buffer = new char[bufferSize]; - e->writeTextInBuffer(buffer, bufferSize); + int tagSize = 8; + unsigned char tag = 'O'; + char * taggedBuffer = new char[bufferSize+2*tagSize]; + memset(taggedBuffer, tag, bufferSize+2*tagSize); + char * buffer = taggedBuffer + tagSize; - char * currentChar = buffer; - while (*currentChar != 0) { - if (*currentChar == Ion::Charset::Exponent) { - *currentChar = 'E'; - } - if (*currentChar == Ion::Charset::Exponential) { - *currentChar = 'e'; - } - if (*currentChar == Ion::Charset::IComplex) { - *currentChar = 'i'; - } - currentChar++; + Complex::convertFloatToText(a, buffer, bufferSize, significantDigits, mode); + + for (int i=0; isetComplexFormat(format); + Preferences::sharedPreferences()->setDisplayMode(mode); + e->writeTextInBuffer(buffer, bufferSize); + translate_in_ASCII_chars(buffer); + + for (int i=0; i c0 = Complex::Cartesian(1.0, 2.0); + assert_expression_prints_to(&c0, "1+2*I", DecimalDisplay, Cartesian); + Complex c1 = Complex::Cartesian(1.0f, 2.0f); + assert_expression_prints_to(&c1, "2.236068*X^(1.107149*I)", DecimalDisplay, Polar); + Complex c2 = Complex::Cartesian(-1.3f, 2.444f); + assert_expression_prints_to(&c2, "-1.3+2.444*I", DecimalDisplay, Cartesian); + Complex c3 = Complex::Cartesian(-1.3, 2.444); + assert_expression_prints_to(&c3, "2.768237*X^(2.059649*I)", DecimalDisplay, Polar); + Complex c4 = Complex::Cartesian(-1.3f, -2.444f); + assert_expression_prints_to(&c4, "-1.3-2.444*I", DecimalDisplay, Cartesian); + Complex c5 = Complex::Cartesian(64078208.0, 119229408.0); + assert_expression_prints_to(&c5, "6.407821E7+1.192294E8*I", DecimalDisplay, Cartesian); + Complex c6 = Complex::Cartesian(64078208.0f, 119229408.0f); + assert_expression_prints_to(&c6, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar); + Complex c7 = Complex::Cartesian(64078208.0f, 119229408.0f); + assert_expression_prints_to(&c7, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar); + Complex c8 = Complex::Cartesian(INFINITY, 119229408.0f); + assert_expression_prints_to(&c8, "undef", DecimalDisplay, Polar); + Complex c9 = Complex::Cartesian(0.0f, 0.0f); + assert_expression_prints_to(&c9, "0", DecimalDisplay, Polar); + Complex c10 = Complex::Cartesian(NAN, 0.0f); + assert_expression_prints_to(&c10, "undef", DecimalDisplay, Polar); + Complex c11 = Complex::Cartesian(0.0f, NAN); + assert_expression_prints_to(&c11, "undef", DecimalDisplay, Polar); + Complex c12 = Complex::Cartesian(NAN, NAN); + assert_expression_prints_to(&c12, "undef", DecimalDisplay, Polar); + +} diff --git a/poincare/test/factorial.cpp b/poincare/test/factorial.cpp new file mode 100644 index 000000000..3151e20d9 --- /dev/null +++ b/poincare/test/factorial.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +QUIZ_CASE(poincare_factorial_simplify) { + assert_parsed_expression_simplify_to("1/3!", "1/6"); + assert_parsed_expression_simplify_to("5!", "120"); + assert_parsed_expression_simplify_to("(1/3)!", "undef"); + assert_parsed_expression_simplify_to("P!", "undef"); + assert_parsed_expression_simplify_to("X!", "undef"); +} diff --git a/poincare/test/function.cpp b/poincare/test/function.cpp index 7bd11dabb..e63d5d56f 100644 --- a/poincare/test/function.cpp +++ b/poincare/test/function.cpp @@ -263,3 +263,37 @@ QUIZ_CASE(poincare_function_evaluate) { Complex akd[1] = {Complex::Cartesian(0.5, 0.86602540378443864676)}; assert_parsed_expression_evaluates_to("root(-1,3)", akd); } + +QUIZ_CASE(poincare_function_simplify) { + assert_parsed_expression_simplify_to("abs(P)", "P"); + assert_parsed_expression_simplify_to("abs(-P)", "P"); + assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); + assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); + assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); + assert_parsed_expression_simplify_to("conj(1/2)", "1/2"); + assert_parsed_expression_simplify_to("quo(19,3)", "6"); + assert_parsed_expression_simplify_to("quo(19,0)", "undef"); + assert_parsed_expression_simplify_to("quo(-19,3)", "-7"); + assert_parsed_expression_simplify_to("rem(19,3)", "1"); + assert_parsed_expression_simplify_to("rem(-19,3)", "2"); + assert_parsed_expression_simplify_to("rem(19,0)", "undef"); + assert_parsed_expression_simplify_to("99!", "933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000"); + assert_parsed_expression_simplify_to("floor(-1.3)", "-2"); + assert_parsed_expression_simplify_to("frac(-1.3)", "7/10"); + assert_parsed_expression_simplify_to("gcd(123,278)", "1"); + assert_parsed_expression_simplify_to("gcd(11,121)", "11"); + assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); + assert_parsed_expression_simplify_to("lcm(11,121)", "121"); + assert_parsed_expression_simplify_to("R(4)", "2"); + assert_parsed_expression_simplify_to("root(4,3)", "root(4,3)"); + assert_parsed_expression_simplify_to("root(4,P)", "4^(1/P)"); + assert_parsed_expression_simplify_to("root(27,3)", "3"); + assert_parsed_expression_simplify_to("round(4.235,2)", "106/25"); + assert_parsed_expression_simplify_to("round(4.23,0)", "4"); + assert_parsed_expression_simplify_to("round(4.9,0)", "5"); + assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); + assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); + assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); + assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); + assert_parsed_expression_simplify_to("re(1/2)", "1/2"); +} diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index 8095a5c2a..103a68122 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -5,6 +5,11 @@ #include #include #include +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif using namespace Poincare; @@ -72,5 +77,24 @@ void assert_parsed_expression_evaluates_to(const char * expression, Complex * delete m; } +void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Expression::AngleUnit angleUnit) { + GlobalContext globalContext; + Expression * e = parse_expression(expression); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- Simplify: " << expression << "----" << endl; +#endif + Expression::Simplify(&e, globalContext, angleUnit); + char buffer[200]; + e->writeTextInBuffer(buffer, sizeof(buffer)); + translate_in_ASCII_chars(buffer); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + print_expression(e, 0); + cout << "---- serialize to: " << buffer << " ----" << endl; + cout << "----- compared to: " << simplifiedExpression << " ----\n" << endl; +#endif + assert(strcmp(buffer, simplifiedExpression) == 0); + delete e; +} + template void assert_parsed_expression_evaluates_to(char const*, Poincare::Complex*, int, int, Poincare::Expression::AngleUnit); template void assert_parsed_expression_evaluates_to(char const*, Poincare::Complex*, int, int, Poincare::Expression::AngleUnit); diff --git a/poincare/test/helper.h b/poincare/test/helper.h index 6f5627871..d0b62e71f 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -13,3 +13,4 @@ template void assert_parsed_expression_evaluates_to(const char * expression, Poincare::Complex * results, Poincare::Expression::AngleUnit angleUnit = Degree) { assert_parsed_expression_evaluates_to(expression, results, 0, 0, angleUnit); } +void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::Expression::AngleUnit angleUnit = Poincare::Expression::AngleUnit::Radian); diff --git a/poincare/test/logarithm.cpp b/poincare/test/logarithm.cpp new file mode 100644 index 000000000..3017142a3 --- /dev/null +++ b/poincare/test/logarithm.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +QUIZ_CASE(poincare_logarithm_simplify) { + assert_parsed_expression_simplify_to("log(12925)", "2*log(5)+log(11)+log(47)"); + assert_parsed_expression_simplify_to("ln(12925)", "2*ln(5)+ln(11)+ln(47)"); + assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "(-2*log(5,6))+log(7,6)+3*log(11,6)+log(17,6)-log(47,6)"); + assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); + assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "undef"); + assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); + assert_parsed_expression_simplify_to("ln(X^3)", "3"); + assert_parsed_expression_simplify_to("log(10)", "1"); + assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); + assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); + assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); + assert_parsed_expression_simplify_to("ln(X^(IP/7))", "(P*I)/7"); + assert_parsed_expression_simplify_to("log(10^24)", "24"); + assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); + assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); + assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); + assert_parsed_expression_simplify_to("log(26061622162116)", "2*log(2)+log(3)+log(2171801846843)"); + assert_parsed_expression_simplify_to("log(26061622162116/5)", "2*log(2)+log(3)-log(5)+log(2171801846843)"); +} diff --git a/poincare/test/matrix.cpp b/poincare/test/matrix.cpp index 4e28f8ac6..25e354060 100644 --- a/poincare/test/matrix.cpp +++ b/poincare/test/matrix.cpp @@ -14,3 +14,99 @@ QUIZ_CASE(poincare_matrix_evaluate) { assert_parsed_expression_evaluates_to("[[1,2,3][4,5,6]]", b, 2, 3); #endif } + +QUIZ_CASE(poincare_matrix_simplify) { +#if MATRICES_ARE_DEFINED +#if MATRIX_EXACT_REDUCING + // Addition Matrix + assert_parsed_expression_simplify_to("1+[[1,2,3][4,5,6]]", "[[2,3,4][5,6,7]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+1", "[[2,3,4][5,6,7]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]+[[1,2,3][4,5,6]]", "undef"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); + assert_parsed_expression_simplify_to("2+[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[4,6,8][10,12,14]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+cos(2)+[[1,2,3][4,5,6]]", "[[2+cos(2),4+cos(2),6+cos(2)][8+cos(2),10+cos(2),12+cos(2)]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+10+[[1,2,3][4,5,6]]+R(2)", "[[12+R(2),14+R(2),16+R(2)][18+R(2),20+R(2),22+R(2)]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)+3", "inverse([[1,2][3,4]])+3"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+3", "inverse([[37,54][81,118]])+3"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]", "inverse([[37,54][81,118]])+[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]+4+R(2)", "inverse([[37,54][81,118]])+[[5+R(2),6+R(2)][7+R(2),8+R(2)]]"); + + // Multiplication Matrix + assert_parsed_expression_simplify_to("2*[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*R(2)", "[[R(2),2R(2),3R(2)][4R(2),5R(2),6R(2)]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]*[[1,2,3][4,5,6]]", "[[9, 12, 15][19, 26, 33]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*[[1,2][2,3][5,6]]", "[[20, 26][44, 59]]"); + assert_parsed_expression_simplify_to("[[1,2,3,4][4,5,6,5]]*[[1,2][2,3][5,6]]", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2,3][3,4,5]]*[[1,2][3,2][4,5]]*4", "[[37,54][81,118]]^(-1)*[[76,84][140,156]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); + + // Power Matrix + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6][7,8,9]]^3", "[[468,576,684][1062,1305,1548][1656,2034,2412]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]^(-1)", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)", "[[1,2][3,4]]^(-1)"); // TODO: Implement matrix inverse for dim < 3 + + // Function on matrix + assert_parsed_expression_simplify_to("abs([[1,-2][3,4]])", "[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); + assert_parsed_expression_simplify_to("asin([[1/R(2),1/2][1,-1]])", "[[P/4,P/6][P/2,-P/2]]"); + assert_parsed_expression_simplify_to("atan([[R(3),1][1/R(3),-1]])", "[[P/3,P/4][P/6,-P/4]]"); + assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); + assert_parsed_expression_simplify_to("binomial([[1,-2][3,4]], 2)", "undef"); + assert_parsed_expression_simplify_to("ceil([[1/R(2),1/2][1,-1.3]])", "[[ceil(R(2)/2),1][1,-1]]"); + assert_parsed_expression_simplify_to("confidence(1/3, 25)", "[[2/15,8/15]]"); + assert_parsed_expression_simplify_to("confidence(45, 25)", "undef"); + assert_parsed_expression_simplify_to("confidence(1/3, -34)", "undef"); + assert_parsed_expression_simplify_to("conj([[1/R(2),1/2][1,-1]])", "[[conj(1/R(2)),1/2][1,-1]]"); + assert_parsed_expression_simplify_to("cos([[P/3,0][P/7,P/2]])", "[[1/2,1][cos(P/7),0]]"); + assert_parsed_expression_simplify_to("diff([[P/3,0][P/7,P/2]],3)", "undef"); + assert_parsed_expression_simplify_to("det([[1,2][3,4]])", "det([[1,2][3,4]])"); // TODO: implement determinant if dim < 3 + assert_parsed_expression_simplify_to("det([[2,2][3,4]])", "det([[2,2][3,4]])"); + assert_parsed_expression_simplify_to("det([[2,2][3,3]])", "det([[2,2][3,3]])"); + assert_parsed_expression_simplify_to("quo([[2,2][3,3]],2)", "undef"); + assert_parsed_expression_simplify_to("rem([[2,2][3,3]],2)", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]!", "[[1,2][6,24]]"); + assert_parsed_expression_simplify_to("floor([[1/R(2),1/2][1,-1.3]])", "[[floor(R(2)/2),0][1,-2]]"); + assert_parsed_expression_simplify_to("frac([[1/R(2),1/2][1,-1.3]])", "[[frac(R(2)/2),1/2][0,0.7]]"); + assert_parsed_expression_simplify_to("gcd([[1/R(2),1/2][1,-1.3]], [[1]])", "undef"); + assert_parsed_expression_simplify_to("asinh([[1/R(2),1/2][1,-1]])", "[[asinh(1/R(2)),asinh(1/2)][asinh(1),asinh(-1)]]"); + assert_parsed_expression_simplify_to("atanh([[R(3),1][1/R(3),-1]])", "[[atanh(R(3)),atanh(1)][atanh(1/R(3)),atanh(-1)]]"); + assert_parsed_expression_simplify_to("acosh([[1/R(2),1/2][1,-1]])", "[[acosh(1/R(2)),acosh(1/2)][acosh(1),acosh(-1)]]"); + assert_parsed_expression_simplify_to("sinh([[1/R(2),1/2][1,-1]])", "[[sinh(1/R(2)),sinh(1/2)][sinh(1),sinh(-1)]]"); + assert_parsed_expression_simplify_to("tanh([[R(3),1][1/R(3),-1]])", "[[tanh(R(3)),tanh(1)][tanh(1/R(3)),tanh(-1)]]"); + assert_parsed_expression_simplify_to("cosh([[1/R(2),1/2][1,-1]])", "[[cosh(1/R(2)),cosh(1/2)][cosh(1),cosh(-1)]]"); + assert_parsed_expression_simplify_to("im([[1/R(2),1/2][1,-1]])", "[[im(1/R(2)),0][0,0]]"); + assert_parsed_expression_simplify_to("int([[P/3,0][P/7,P/2]],3,2)", "undef"); + assert_parsed_expression_simplify_to("lcm(2, [[1]])", "undef"); + assert_parsed_expression_simplify_to("log([[R(2),1/2][1,3]])", "[[(1/2)*log(2),-log(2)][0,log(3)]]"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]],3)", "undef"); + assert_parsed_expression_simplify_to("ln([[R(2),1/2][1,3]])", "[[(1/2)*ln(2),-ln(2)][0,ln(3)]]"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); + assert_parsed_expression_simplify_to("dim([[1/R(2),1/2,3][2,1,-3]])", "[[2,3]]"); + assert_parsed_expression_simplify_to("inverse([[1/R(2),1/2,3][2,1,-3]])", "undef"); + assert_parsed_expression_simplify_to("inverse([[1,2][3,4]])", "inverse([[1,2][3,4]])"); // TODO: implement matrix inverse if dim < 3 + assert_parsed_expression_simplify_to("trace([[1/R(2),1/2,3][2,1,-3]])", "undef"); + assert_parsed_expression_simplify_to("trace([[R(2),2][4,3+log(3)]])", "R(2)+3+log(3)"); + assert_parsed_expression_simplify_to("trace(R(2)+log(3))", "R(2)+log(3)"); + assert_parsed_expression_simplify_to("transpose([[1/R(2),1/2,3][2,1,-3]])", "[[1/R(2),2][1/2, 1][3,-3]]"); + assert_parsed_expression_simplify_to("transpose(R(4))", "2"); + assert_parsed_expression_simplify_to("root([[R(4)]],2)", "undef"); + assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); + assert_parsed_expression_simplify_to("-[[1/R(2),1/2,3][2,1,-3]]", "[[-1/R(2),-1/2,-3][-2,-1,3]]"); + assert_parsed_expression_simplify_to("permute([[1,-2][3,4]], 2)", "undef"); + assert_parsed_expression_simplify_to("prediction95(1/3, 25)", "[[1/3-49R(2)/375,1/3+49R(2)/375]]"); + assert_parsed_expression_simplify_to("prediction95(45, 25)", "undef"); + assert_parsed_expression_simplify_to("prediction95(1/3, -34)", "undef"); + assert_parsed_expression_simplify_to("product([[1,2][3,4]], 1/3, -34)", "product([[1,2][3,4]], 1/3, -34)"); + assert_parsed_expression_simplify_to("sum([[1,2][3,4]], 1/3, -34)", "sum([[1,2][3,4]], 1/3, -34)"); + assert_parsed_expression_simplify_to("re([[1/R(2),1/2][1,-1]])", "[[re(1/R(2)),1/2][1,-1]]"); + assert_parsed_expression_simplify_to("round([[1/R(2),1/2][1,-1]],2)", "undef"); + assert_parsed_expression_simplify_to("sin([[P/3,0][P/7,P/2]])", "[[R(3)/2,0][sin(P/7),1]]"); + assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "[[2,R(2)][R(P/7),1]]"); + assert_parsed_expression_simplify_to("tan([[P/3,0][P/7,P/6]])", "[[R(3),0][tan(P/7),R(3)/3]]"); +#else + assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "R([[4,2][P/7,1]])"); +#endif +#endif +} diff --git a/poincare/test/multiplication.cpp b/poincare/test/multiplication.cpp new file mode 100644 index 000000000..4b706b855 --- /dev/null +++ b/poincare/test/multiplication.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +QUIZ_CASE(poincare_multiplication_evaluate) { + Complex a[1] = {Complex::Float(2.0f)}; + assert_parsed_expression_evaluates_to("1*2", a); + + Complex b[1] = {Complex::Cartesian(11.0, 7.0)}; + assert_parsed_expression_evaluates_to("(3+I)*(4+I)", b); + +#if MATRICES_ARE_DEFINED + Complex c[6] = {Complex::Float(2.0f), Complex::Float(4.0f), Complex::Float(6.0f), Complex::Float(8.0f), Complex::Float(10.0f), Complex::Float(12.0f)}; + assert_parsed_expression_evaluates_to("[[1,2][3,4][5,6]]*2", c, 3, 2); + + Complex d[6] = {Complex::Cartesian(3.0, 1.0), Complex::Cartesian(5.0, 5.0), Complex::Cartesian(9.0, 3.0), Complex::Cartesian(12.0, 4.0), Complex::Cartesian(15.0, 5.0), Complex::Cartesian(18.0, 6.0)}; + assert_parsed_expression_evaluates_to("[[1,2+I][3,4][5,6]]*(3+I)", d, 3, 2); + + assert_parsed_expression_evaluates_to("2*[[1,2][3,4][5,6]]", c, 3, 2); + + assert_parsed_expression_evaluates_to("(3+I)*[[1,2+I][3,4][5,6]]", d, 3, 2); + + Complex e[12] = {Complex::Float(11.0f), Complex::Float(14.0f), Complex::Float(17.0f), Complex::Float(20.0f), Complex::Float(23.0f), Complex::Float(30.0f), Complex::Float(37.0f), Complex::Float(44.0f), Complex::Float(35.0f), Complex::Float(46.0f), Complex::Float(57.0f), Complex::Float(68.0f)}; + assert_parsed_expression_evaluates_to("[[1,2][3,4][5,6]]*[[1,2,3,4][5,6,7,8]]", e, 3, 4); + + Complex f[12] = {Complex::Cartesian(11.0, 5.0), Complex::Cartesian(13.0, 9.0), Complex::Cartesian(17.0, 7.0), Complex::Cartesian(20.0, 8.0), Complex::Cartesian(23.0, 0.0), Complex::Cartesian(30.0, 7.0), Complex::Cartesian(37.0, 0.0), Complex::Cartesian(44.0, 0.0), Complex::Cartesian(35.0, 0.0), Complex::Cartesian(46.0, 11.0), Complex::Cartesian(57.0, 0.0), Complex::Cartesian(68.0, 0.0)}; + assert_parsed_expression_evaluates_to("[[1,2+I][3,4][5,6]]*[[1,2+I,3,4][5,6+I,7,8]]", f, 3, 4); +#endif +} + +QUIZ_CASE(poincare_multiplication_simplify) { + assert_parsed_expression_simplify_to("0*x+B", "B"); + assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); + assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "(72*A^4*B^(2+x)+36*A^6*B^(2+x))/5"); + assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "3*A*B+3*A*C+A*B*D+A*C*D"); + assert_parsed_expression_simplify_to("A/B", "A/B"); + assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); + assert_parsed_expression_simplify_to("(1/2)*A/B", "A/(2*B)"); + assert_parsed_expression_simplify_to("1+2+3+4+5+6", "21"); + assert_parsed_expression_simplify_to("1-2+3-4+5-6", "-3"); + assert_parsed_expression_simplify_to("987654321123456789*998877665544332211", "986545842648570754445552922919330479"); + assert_parsed_expression_simplify_to("2/3", "2/3"); + assert_parsed_expression_simplify_to("9/17+5/4", "121/68"); + assert_parsed_expression_simplify_to("1/2*3/4", "3/8"); + assert_parsed_expression_simplify_to("0*2/3", "0"); + assert_parsed_expression_simplify_to("1+(1/(1+1/(1+1/(1+1))))", "8/5"); + assert_parsed_expression_simplify_to("1+2/(3+4/(5+6/(7+8)))", "155/101"); + assert_parsed_expression_simplify_to("3/4*16/12", "1"); + assert_parsed_expression_simplify_to("3/4*(8+8)/12", "1"); + assert_parsed_expression_simplify_to("916791/794976477", "305597/264992159"); + assert_parsed_expression_simplify_to("321654987123456789/112233445566778899", "3249040273974311/1133671167341201"); + assert_parsed_expression_simplify_to("0.1+0.2", "3/10"); + assert_parsed_expression_simplify_to("2^3", "8"); + assert_parsed_expression_simplify_to("(-1)*(-1)", "1"); + assert_parsed_expression_simplify_to("(-2)^2", "4"); + assert_parsed_expression_simplify_to("(-3)^3", "-27"); + assert_parsed_expression_simplify_to("(1/2)^-1", "2"); + assert_parsed_expression_simplify_to("R(2)*R(3)", "R(6)"); + assert_parsed_expression_simplify_to("2*2^P", "2*2^P"); + assert_parsed_expression_simplify_to("A^3*B*A^(-3)", "B"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("(x+1)*(x+2)", "2+3*x+x^2"); + assert_parsed_expression_simplify_to("(x+1)*(x-1)", "(-1)+x^2"); + assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); + assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3*P)"); + assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3*P)"); + assert_parsed_expression_simplify_to("A^2*BA^(-2)*B^(-2)", "1/B"); + assert_parsed_expression_simplify_to("A^(-1)*B^(-1)", "1/(A*B)"); + assert_parsed_expression_simplify_to("x+x", "2*x"); + assert_parsed_expression_simplify_to("2*x+x", "3*x"); + assert_parsed_expression_simplify_to("x*2+x", "3*x"); + assert_parsed_expression_simplify_to("2*x+2*x", "4*x"); + assert_parsed_expression_simplify_to("x*2+2*n", "2*n+2*x"); + assert_parsed_expression_simplify_to("x+x+n+n", "2*n+2*x"); + assert_parsed_expression_simplify_to("x-x-n+n", "0"); + assert_parsed_expression_simplify_to("x+n-x-n", "0"); + assert_parsed_expression_simplify_to("x-x", "0"); +} diff --git a/poincare/test/power.cpp b/poincare/test/power.cpp index d8d875c8e..f502dc323 100644 --- a/poincare/test/power.cpp +++ b/poincare/test/power.cpp @@ -28,3 +28,52 @@ QUIZ_CASE(poincare_power_evaluate) { Complex f[1] = {Complex::Float(std::exp(-M_PI_2))}; assert_parsed_expression_evaluates_to("I^I", f); } + +QUIZ_CASE(poincare_power_simplify) { + assert_parsed_expression_simplify_to("3^4", "81"); + assert_parsed_expression_simplify_to("3^(-4)", "1/81"); + assert_parsed_expression_simplify_to("1256^(1/3)*x", "2*root(157,3)*x"); + assert_parsed_expression_simplify_to("1256^(-1/3)", "1/(2*root(157,3))"); + assert_parsed_expression_simplify_to("32^(-1/5)", "1/2"); + assert_parsed_expression_simplify_to("(2+3-4)^(x)", "1"); + assert_parsed_expression_simplify_to("1^x", "1"); + assert_parsed_expression_simplify_to("x^1", "x"); + assert_parsed_expression_simplify_to("0^3", "0"); + assert_parsed_expression_simplify_to("0^0", "undef"); + assert_parsed_expression_simplify_to("0^(-3)", "undef"); + assert_parsed_expression_simplify_to("4^0.5", "2"); + assert_parsed_expression_simplify_to("8^0.5", "2*R(2)"); + assert_parsed_expression_simplify_to("(12^4*3)^(0.5)", "144*R(3)"); + assert_parsed_expression_simplify_to("(2^A)^B", "2^(A*B)"); + assert_parsed_expression_simplify_to("(2*A)^B", "2^B*A^B"); + assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144*R(x)"); + assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); + assert_parsed_expression_simplify_to("R(3^2)", "3"); + assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); + assert_parsed_expression_simplify_to("R(5513219850886344455940081)", "2348024669991"); + assert_parsed_expression_simplify_to("R(154355776)", "12424"); + assert_parsed_expression_simplify_to("R(P)^2", "P"); + assert_parsed_expression_simplify_to("R(P^2)", "P"); + assert_parsed_expression_simplify_to("R((-P)^2)", "P"); + assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(P)*R(x)"); + assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); + assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); + assert_parsed_expression_simplify_to("x^(1/7)", "root(x,7)"); + assert_parsed_expression_simplify_to("x^(-1/7)", "1/root(x,7)"); + assert_parsed_expression_simplify_to("1/(3R(2))", "R(2)/6"); + assert_parsed_expression_simplify_to("X^ln(3)", "3"); + assert_parsed_expression_simplify_to("X^ln(R(3))", "R(3)"); + assert_parsed_expression_simplify_to("P^log(R(3),P)", "R(3)"); + assert_parsed_expression_simplify_to("10^log(P)", "P"); + assert_parsed_expression_simplify_to("X^ln(65)", "65"); + assert_parsed_expression_simplify_to("X^ln(PX)", "P*X"); + assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); + assert_parsed_expression_simplify_to("R(X^2)", "X"); + assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); + /* This does not reduce but should not as the integer is above + * k_maxNumberOfPrimeFactors and thus it prime decomposition might overflow + * 32 factors. */ + assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "root(1881676377434183981909562699940347954480361860897069,3)"); +} diff --git a/poincare/test/product.cpp b/poincare/test/product.cpp deleted file mode 100644 index 21ac7c038..000000000 --- a/poincare/test/product.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include -#include "helper.h" - -using namespace Poincare; - -QUIZ_CASE(poincare_product_evaluate) { - Complex a[1] = {Complex::Float(2.0f)}; - assert_parsed_expression_evaluates_to("1*2", a); - - Complex b[1] = {Complex::Cartesian(11.0, 7.0)}; - assert_parsed_expression_evaluates_to("(3+I)*(4+I)", b); - -#if MATRICES_ARE_DEFINED - Complex c[6] = {Complex::Float(2.0f), Complex::Float(4.0f), Complex::Float(6.0f), Complex::Float(8.0f), Complex::Float(10.0f), Complex::Float(12.0f)}; - assert_parsed_expression_evaluates_to("[[1,2][3,4][5,6]]*2", c, 3, 2); - - Complex d[6] = {Complex::Cartesian(3.0, 1.0), Complex::Cartesian(5.0, 5.0), Complex::Cartesian(9.0, 3.0), Complex::Cartesian(12.0, 4.0), Complex::Cartesian(15.0, 5.0), Complex::Cartesian(18.0, 6.0)}; - assert_parsed_expression_evaluates_to("[[1,2+I][3,4][5,6]]*(3+I)", d, 3, 2); - - assert_parsed_expression_evaluates_to("2*[[1,2][3,4][5,6]]", c, 3, 2); - - assert_parsed_expression_evaluates_to("(3+I)*[[1,2+I][3,4][5,6]]", d, 3, 2); - - Complex e[12] = {Complex::Float(11.0f), Complex::Float(14.0f), Complex::Float(17.0f), Complex::Float(20.0f), Complex::Float(23.0f), Complex::Float(30.0f), Complex::Float(37.0f), Complex::Float(44.0f), Complex::Float(35.0f), Complex::Float(46.0f), Complex::Float(57.0f), Complex::Float(68.0f)}; - assert_parsed_expression_evaluates_to("[[1,2][3,4][5,6]]*[[1,2,3,4][5,6,7,8]]", e, 3, 4); - - Complex f[12] = {Complex::Cartesian(11.0, 5.0), Complex::Cartesian(13.0, 9.0), Complex::Cartesian(17.0, 7.0), Complex::Cartesian(20.0, 8.0), Complex::Cartesian(23.0, 0.0), Complex::Cartesian(30.0, 7.0), Complex::Cartesian(37.0, 0.0), Complex::Cartesian(44.0, 0.0), Complex::Cartesian(35.0, 0.0), Complex::Cartesian(46.0, 11.0), Complex::Cartesian(57.0, 0.0), Complex::Cartesian(68.0, 0.0)}; - assert_parsed_expression_evaluates_to("[[1,2+I][3,4][5,6]]*[[1,2+I,3,4][5,6+I,7,8]]", f, 3, 4); -#endif -} diff --git a/poincare/test/rational.cpp b/poincare/test/rational.cpp new file mode 100644 index 000000000..6a0f9c4a6 --- /dev/null +++ b/poincare/test/rational.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +QUIZ_CASE(poincare_rational_sign) { + assert(Rational(-2).sign() == Expression::Sign::Negative); + assert(Rational(-2, 3).sign() == Expression::Sign::Negative); + assert(Rational(2, 3).sign() == Expression::Sign::Positive); +} + +QUIZ_CASE(poincare_rational_compare) { + assert(Rational::NaturalOrder(Rational(123, 234),Rational(456, 567)) < 0); + assert(Rational::NaturalOrder(Rational(-123, 234),Rational(456, 567)) < 0); + assert(Rational::NaturalOrder(Rational(123, 234),Rational(-456, 567)) > 0); + assert(Rational::NaturalOrder(Rational(123, 234),Rational("123456789123456789", "12345678912345678910")) > 0); +} + +QUIZ_CASE(poincare_rational_evaluate) { + Complex a[1] = {Complex::Float(0.333333333f)}; + assert_parsed_expression_evaluates_to("1/3", a); + Complex b[1] = {Complex::Float(0.099999)}; + assert_parsed_expression_evaluates_to("123456/1234567", b); +} + +QUIZ_CASE(poincare_rational_simplify) { + assert_parsed_expression_simplify_to("-1/3", "-1/3"); + assert_parsed_expression_simplify_to("22355/45325", "4471/9065"); + assert_parsed_expression_simplify_to("0000.000000", "0"); + assert_parsed_expression_simplify_to(".000000", "0"); + assert_parsed_expression_simplify_to("0000", "0"); + assert_parsed_expression_simplify_to("0.1234567", "1234567/10000000"); + assert_parsed_expression_simplify_to("123.4567", "1234567/10000"); + assert_parsed_expression_simplify_to("0.1234", "617/5000"); + assert_parsed_expression_simplify_to("0.1234000", "617/5000"); + assert_parsed_expression_simplify_to("001234000", "1234000"); + assert_parsed_expression_simplify_to("001.234000E3", "1234"); + assert_parsed_expression_simplify_to("001234000E-4", "617/5"); + assert_parsed_expression_simplify_to("3/4+5/4-12+1/567", "-5669/567"); + assert_parsed_expression_simplify_to("34/78+67^(-1)", "1178/2613"); + assert_parsed_expression_simplify_to("12348/34564", "3087/8641"); + assert_parsed_expression_simplify_to("1-0.3-0.7", "0"); + assert_parsed_expression_simplify_to("123456789123456789+112233445566778899", "235690234690235688"); + assert_parsed_expression_simplify_to("56^56", "79164324866862966607842406018063254671922245312646690223362402918484170424104310169552592050323456"); + assert_parsed_expression_simplify_to("999^999", "999^999"); + assert_parsed_expression_simplify_to("999^-999", "1/999^999"); + assert_parsed_expression_simplify_to("0^0", "undef"); + assert_parsed_expression_simplify_to("x^0", "1"); + assert_parsed_expression_simplify_to("P^0", "1"); + assert_parsed_expression_simplify_to("A^0", "1"); + assert_parsed_expression_simplify_to("(-3)^0", "1"); +} diff --git a/poincare/test/simplify_addition.cpp b/poincare/test/simplify_addition.cpp deleted file mode 100644 index 0c9b0eb4b..000000000 --- a/poincare/test/simplify_addition.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include "simplify_utils.h" - -using namespace Poincare; - -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("A*(0+0)", "0")); -} diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp deleted file mode 100644 index a7ec02c6d..000000000 --- a/poincare/test/simplify_easy.cpp +++ /dev/null @@ -1,543 +0,0 @@ -#include -#include -#include -#include -#include "helper.h" -#if POINCARE_TESTS_PRINT_EXPRESSIONS -#include "../src/expression_debug.h" -#include -using namespace std; -#endif - -using namespace Poincare; - -void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Expression::AngleUnit angleUnit = Expression::AngleUnit::Radian) { - GlobalContext globalContext; - Expression * e = parse_expression(expression); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - cout << "---- Simplify: " << expression << "----" << endl; -#endif - Expression::Simplify(&e, globalContext, angleUnit); - char buffer[200]; - e->writeTextInBuffer(buffer, sizeof(buffer)); - translate_in_ASCII_chars(buffer); -#if POINCARE_TESTS_PRINT_EXPRESSIONS - print_expression(e, 0); - cout << "---- serialize to: " << buffer << " ----" << endl; - cout << "----- compared to: " << simplifiedExpression << " ----\n" << endl; -#endif - assert(strcmp(buffer, simplifiedExpression) == 0); - delete e; -} - -QUIZ_CASE(poincare_simplify_easy) { - - // Rational - assert_parsed_expression_simplify_to("-1/3", "-1/3"); - assert_parsed_expression_simplify_to("22355/45325", "4471/9065"); - assert_parsed_expression_simplify_to("0000.000000", "0"); - assert_parsed_expression_simplify_to(".000000", "0"); - assert_parsed_expression_simplify_to("0000", "0"); - assert_parsed_expression_simplify_to("0.1234567", "1234567/10000000"); - assert_parsed_expression_simplify_to("123.4567", "1234567/10000"); - assert_parsed_expression_simplify_to("0.1234", "617/5000"); - assert_parsed_expression_simplify_to("0.1234000", "617/5000"); - assert_parsed_expression_simplify_to("001234000", "1234000"); - assert_parsed_expression_simplify_to("001.234000E3", "1234"); - assert_parsed_expression_simplify_to("001234000E-4", "617/5"); - assert_parsed_expression_simplify_to("3/4+5/4-12+1/567", "-5669/567"); - assert_parsed_expression_simplify_to("34/78+67^(-1)", "1178/2613"); - assert_parsed_expression_simplify_to("12348/34564", "3087/8641"); - assert_parsed_expression_simplify_to("1-0.3-0.7", "0"); - assert_parsed_expression_simplify_to("123456789123456789+112233445566778899", "235690234690235688"); - assert_parsed_expression_simplify_to("56^56", "79164324866862966607842406018063254671922245312646690223362402918484170424104310169552592050323456"); - assert_parsed_expression_simplify_to("999^999", "999^999"); - assert_parsed_expression_simplify_to("999^-999", "1/999^999"); - assert_parsed_expression_simplify_to("0^0", "undef"); - assert_parsed_expression_simplify_to("x^0", "1"); - assert_parsed_expression_simplify_to("P^0", "1"); - assert_parsed_expression_simplify_to("A^0", "1"); - assert_parsed_expression_simplify_to("(-3)^0", "1"); - - // Addition - assert_parsed_expression_simplify_to("2+0", "2"); - assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10*cos(2)"); - assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "(2347+882*ln(2))/882"); - assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); - assert_parsed_expression_simplify_to("-5P+3P", "-2*P"); - assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "(-7)-A"); - assert_parsed_expression_simplify_to("1+2", "3"); - assert_parsed_expression_simplify_to("A-A", "0"); - assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); - assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); - assert_parsed_expression_simplify_to("-A", "-A"); - assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+P+x)/(2+P+2*x+P*x)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(P*x^2)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+P*x)/(P*x^3)"); - assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); - assert_parsed_expression_simplify_to("A+B-A-B", "0"); - assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); - - // Multiplication - assert_parsed_expression_simplify_to("0*x+B", "B"); - assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); - assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "(72*A^4*B^(2+x)+36*A^6*B^(2+x))/5"); - assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "3*A*B+3*A*C+A*B*D+A*C*D"); - assert_parsed_expression_simplify_to("A/B", "A/B"); - assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); - assert_parsed_expression_simplify_to("(1/2)*A/B", "A/(2*B)"); - assert_parsed_expression_simplify_to("1+2+3+4+5+6", "21"); - assert_parsed_expression_simplify_to("1-2+3-4+5-6", "-3"); - assert_parsed_expression_simplify_to("987654321123456789*998877665544332211", "986545842648570754445552922919330479"); - assert_parsed_expression_simplify_to("2/3", "2/3"); - assert_parsed_expression_simplify_to("9/17+5/4", "121/68"); - assert_parsed_expression_simplify_to("1/2*3/4", "3/8"); - assert_parsed_expression_simplify_to("0*2/3", "0"); - assert_parsed_expression_simplify_to("1+(1/(1+1/(1+1/(1+1))))", "8/5"); - assert_parsed_expression_simplify_to("1+2/(3+4/(5+6/(7+8)))", "155/101"); - assert_parsed_expression_simplify_to("3/4*16/12", "1"); - assert_parsed_expression_simplify_to("3/4*(8+8)/12", "1"); - assert_parsed_expression_simplify_to("916791/794976477", "305597/264992159"); - assert_parsed_expression_simplify_to("321654987123456789/112233445566778899", "3249040273974311/1133671167341201"); - assert_parsed_expression_simplify_to("0.1+0.2", "3/10"); - assert_parsed_expression_simplify_to("2^3", "8"); - assert_parsed_expression_simplify_to("(-1)*(-1)", "1"); - assert_parsed_expression_simplify_to("(-2)^2", "4"); - assert_parsed_expression_simplify_to("(-3)^3", "-27"); - assert_parsed_expression_simplify_to("(1/2)^-1", "2"); - assert_parsed_expression_simplify_to("R(2)*R(3)", "R(6)"); - assert_parsed_expression_simplify_to("2*2^P", "2*2^P"); - assert_parsed_expression_simplify_to("A^3*B*A^(-3)", "B"); - assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); - assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); - assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); - assert_parsed_expression_simplify_to("(x+1)*(x+2)", "2+3*x+x^2"); - assert_parsed_expression_simplify_to("(x+1)*(x-1)", "(-1)+x^2"); - assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); - assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3*P)"); - assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3*P)"); - assert_parsed_expression_simplify_to("A^2*BA^(-2)*B^(-2)", "1/B"); - assert_parsed_expression_simplify_to("A^(-1)*B^(-1)", "1/(A*B)"); - - // Power - assert_parsed_expression_simplify_to("3^4", "81"); - assert_parsed_expression_simplify_to("3^(-4)", "1/81"); - assert_parsed_expression_simplify_to("1256^(1/3)*x", "2*root(157,3)*x"); - assert_parsed_expression_simplify_to("1256^(-1/3)", "1/(2*root(157,3))"); - assert_parsed_expression_simplify_to("32^(-1/5)", "1/2"); - assert_parsed_expression_simplify_to("(2+3-4)^(x)", "1"); - assert_parsed_expression_simplify_to("1^x", "1"); - assert_parsed_expression_simplify_to("x^1", "x"); - assert_parsed_expression_simplify_to("0^3", "0"); - assert_parsed_expression_simplify_to("0^0", "undef"); - assert_parsed_expression_simplify_to("0^(-3)", "undef"); - assert_parsed_expression_simplify_to("4^0.5", "2"); - assert_parsed_expression_simplify_to("8^0.5", "2*R(2)"); - assert_parsed_expression_simplify_to("(12^4*3)^(0.5)", "144*R(3)"); - assert_parsed_expression_simplify_to("(2^A)^B", "2^(A*B)"); - assert_parsed_expression_simplify_to("(2*A)^B", "2^B*A^B"); - assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144*R(x)"); - assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); - assert_parsed_expression_simplify_to("R(3^2)", "3"); - assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); - assert_parsed_expression_simplify_to("R(5513219850886344455940081)", "2348024669991"); - assert_parsed_expression_simplify_to("R(154355776)", "12424"); - assert_parsed_expression_simplify_to("R(P)^2", "P"); - assert_parsed_expression_simplify_to("R(P^2)", "P"); - assert_parsed_expression_simplify_to("R((-P)^2)", "P"); - assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(P)*R(x)"); - assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); - assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); - assert_parsed_expression_simplify_to("x^(1/7)", "root(x,7)"); - assert_parsed_expression_simplify_to("x^(-1/7)", "1/root(x,7)"); - assert_parsed_expression_simplify_to("1/(3R(2))", "R(2)/6"); - assert_parsed_expression_simplify_to("X^ln(3)", "3"); - assert_parsed_expression_simplify_to("X^ln(R(3))", "R(3)"); - assert_parsed_expression_simplify_to("P^log(R(3),P)", "R(3)"); - assert_parsed_expression_simplify_to("10^log(P)", "P"); - assert_parsed_expression_simplify_to("X^ln(65)", "65"); - assert_parsed_expression_simplify_to("X^ln(PX)", "P*X"); - assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); - assert_parsed_expression_simplify_to("R(X^2)", "X"); - assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); - /* This does not reduce but should not as the integer is above - * k_maxNumberOfPrimeFactors and thus it prime decomposition might overflow - * 32 factors. */ - assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "root(1881676377434183981909562699940347954480361860897069,3)"); - - // Complex - assert_parsed_expression_simplify_to("I", "I"); - assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); - assert_parsed_expression_simplify_to("I^(3/5)", "(R(2)*R(5-R(5))+I+R(5)*I)/4"); - assert_parsed_expression_simplify_to("IIII", "1"); - assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); - assert_parsed_expression_simplify_to("Acos(9)IIln(2)", "-cos(9)*ln(2)*A"); - assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2*(2*R(2)+2*R(2)*I)^2)"); - assert_parsed_expression_simplify_to("root(5^(-I)3^9,I)", "undef"); - assert_parsed_expression_simplify_to("I^I", "undef"); - - //Functions - assert_parsed_expression_simplify_to("abs(P)", "P"); - assert_parsed_expression_simplify_to("abs(-P)", "P"); - assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); - assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); - assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); - assert_parsed_expression_simplify_to("conj(1/2)", "1/2"); - assert_parsed_expression_simplify_to("quo(19,3)", "6"); - assert_parsed_expression_simplify_to("quo(19,0)", "undef"); - assert_parsed_expression_simplify_to("quo(-19,3)", "-7"); - assert_parsed_expression_simplify_to("rem(19,3)", "1"); - assert_parsed_expression_simplify_to("rem(-19,3)", "2"); - assert_parsed_expression_simplify_to("rem(19,0)", "undef"); - assert_parsed_expression_simplify_to("99!", "933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000"); - assert_parsed_expression_simplify_to("floor(-1.3)", "-2"); - assert_parsed_expression_simplify_to("frac(-1.3)", "7/10"); - assert_parsed_expression_simplify_to("gcd(123,278)", "1"); - assert_parsed_expression_simplify_to("gcd(11,121)", "11"); - assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); - assert_parsed_expression_simplify_to("lcm(11,121)", "121"); - assert_parsed_expression_simplify_to("R(4)", "2"); - assert_parsed_expression_simplify_to("root(4,3)", "root(4,3)"); - assert_parsed_expression_simplify_to("root(4,P)", "4^(1/P)"); - assert_parsed_expression_simplify_to("root(27,3)", "3"); - assert_parsed_expression_simplify_to("round(4.235,2)", "106/25"); - assert_parsed_expression_simplify_to("round(4.23,0)", "4"); - assert_parsed_expression_simplify_to("round(4.9,0)", "5"); - assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); - assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); - assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); - assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); - assert_parsed_expression_simplify_to("re(1/2)", "1/2"); - - // Trigonometry - // -- sin/cos -> tan - assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); - assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); - assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "tan(x)*P"); - assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); - assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); - assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); - assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos((5*P)/21)/sin((8*P)/17)"); - assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-(3*P)/tan(P/21)"); - assert_parsed_expression_simplify_to("cos(62P/21)/(P*3*sin(62P/21))", "-1/(3*tan(P/21)*P)"); - assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3*tan(P/21)*P"); - assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3*P)"); - assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)*P)"); - assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-(2*ln(3))/(tan(P/62)*P)"); - // -- cos - assert_parsed_expression_simplify_to("cos(0)", "1"); - assert_parsed_expression_simplify_to("cos(P)", "-1"); - assert_parsed_expression_simplify_to("cos(P*4/7)", "-cos((3*P)/7)"); - assert_parsed_expression_simplify_to("cos(P*35/29)", "-cos((6*P)/29)"); - assert_parsed_expression_simplify_to("cos(-P*35/29)", "-cos((6*P)/29)"); - assert_parsed_expression_simplify_to("cos(P*340000)", "1"); - assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); - assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(R(2)*P)"); - assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); - assert_parsed_expression_simplify_to("cos(P/12)", "(R(2)+R(6))/4"); - assert_parsed_expression_simplify_to("cos(-P/12)", "(R(2)+R(6))/4"); - assert_parsed_expression_simplify_to("cos(-P17/8)", "R(2+R(2))/2"); - assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); - assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); - assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); - assert_parsed_expression_simplify_to("cos(41P/5)", "(1+R(5))/4"); - assert_parsed_expression_simplify_to("cos(7P/10)", "-(R(2)*R(5-R(5)))/4"); - assert_parsed_expression_simplify_to("cos(0)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180)", "-1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(61200000)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(39330)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-765/2)", "R(2+R(2))/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-60)", "1/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/5)", "(1+R(5))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); - // -- sin - assert_parsed_expression_simplify_to("sin(0)", "0"); - assert_parsed_expression_simplify_to("sin(P)", "0"); - assert_parsed_expression_simplify_to("sin(P*35/29)", "-sin((6*P)/29)"); - assert_parsed_expression_simplify_to("sin(-P*35/29)", "sin((6*P)/29)"); - assert_parsed_expression_simplify_to("sin(P*340000)", "0"); - assert_parsed_expression_simplify_to("sin(P*340001)", "0"); - assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); - assert_parsed_expression_simplify_to("sin(P/12)", "((-R(2))+R(6))/4"); - assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); - assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(R(2)*P)"); - assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); - assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(2-R(2))/2"); - assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); - assert_parsed_expression_simplify_to("sin(-3P/10)", "((-1)-R(5))/4"); - assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); - assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); - assert_parsed_expression_simplify_to("sin(17P/5)", "-(R(2)*R(5+R(5)))/4"); - assert_parsed_expression_simplify_to("sin(P/5)", "(R(2)*R(5-R(5)))/4"); - assert_parsed_expression_simplify_to("sin(0)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200000)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(15)", "((-R(2))+R(6))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(39330)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-765/2)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(1230)", "1/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(612)", "-(R(2)*R(5+R(5)))/4", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(36)", "(R(2)*R(5-R(5)))/4", Expression::AngleUnit::Degree); - // -- tan - assert_parsed_expression_simplify_to("tan(0)", "0"); - assert_parsed_expression_simplify_to("tan(P)", "0"); - assert_parsed_expression_simplify_to("tan(P*35/29)", "tan((6*P)/29)"); - assert_parsed_expression_simplify_to("tan(-P*35/29)", "-tan((6*P)/29)"); - assert_parsed_expression_simplify_to("tan(P*340000)", "0"); - assert_parsed_expression_simplify_to("tan(P*340001)", "0"); - assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); - assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/12)", "(-2)+R(3)"); - assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(R(2)*P)"); - assert_parsed_expression_simplify_to("tan(1311P/6)", "undef"); - assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); - assert_parsed_expression_simplify_to("tan(41P/6)", "-R(3)/3"); - assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); - assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/10)", "-(R(5)*R(5-2*R(5)))/5"); - assert_parsed_expression_simplify_to("tan(0)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200000)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-61200180)", "0", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-15)", "(-2)+R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(39330)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(1230)", "-R(3)/3", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180045)", "1", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); - // -- acos - assert_parsed_expression_simplify_to("acos(-1/2)", "(2*P)/3"); - assert_parsed_expression_simplify_to("acos(-1.2)", "undef"); - assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3"); - assert_parsed_expression_simplify_to("acos(cos(3/2))", "3/2"); - assert_parsed_expression_simplify_to("cos(acos(3/2))", "undef"); - assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); - assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); - assert_parsed_expression_simplify_to("acos(cos(4P/7))", "(4*P)/7"); - assert_parsed_expression_simplify_to("acos(-cos(2))", "(-2)+P"); - assert_parsed_expression_simplify_to("acos(-1/2)", "120", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(-1.2)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(190))", "170", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(75))", "75", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(190))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(75))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(12))", "12", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Expression::AngleUnit::Degree); - // -- asin - assert_parsed_expression_simplify_to("asin(-1/2)", "-P/6"); - assert_parsed_expression_simplify_to("asin(-1.2)", "undef"); - assert_parsed_expression_simplify_to("asin(sin(2/3))", "2/3"); - assert_parsed_expression_simplify_to("sin(asin(2/3))", "2/3"); - assert_parsed_expression_simplify_to("sin(asin(3/2))", "undef"); - assert_parsed_expression_simplify_to("asin(sin(3/2))", "3/2"); - assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); - assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); - assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); - assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(-1.2)", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(75))", "75", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(75))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(190))", "undef", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(32))", "32", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(400))", "40", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Expression::AngleUnit::Degree); - // -- atan - assert_parsed_expression_simplify_to("atan(-1)", "-P/4"); - assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)"); - assert_parsed_expression_simplify_to("atan(tan(2/3))", "2/3"); - assert_parsed_expression_simplify_to("tan(atan(2/3))", "2/3"); - assert_parsed_expression_simplify_to("tan(atan(5/2))", "5/2"); - assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); - assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); - assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); - assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); - assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); - assert_parsed_expression_simplify_to("atan(-1)", "-45", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(120))", "120", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Expression::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(R(3))", "60", Expression::AngleUnit::Degree); - - // Logarithm - assert_parsed_expression_simplify_to("log(12925)", "2*log(5)+log(11)+log(47)"); - assert_parsed_expression_simplify_to("ln(12925)", "2*ln(5)+ln(11)+ln(47)"); - assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "(-2*log(5,6))+log(7,6)+3*log(11,6)+log(17,6)-log(47,6)"); - assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); - assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "undef"); - assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); - assert_parsed_expression_simplify_to("ln(X^3)", "3"); - assert_parsed_expression_simplify_to("log(10)", "1"); - assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); - assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); - assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); - assert_parsed_expression_simplify_to("ln(X^(IP/7))", "(P*I)/7"); - assert_parsed_expression_simplify_to("log(10^24)", "24"); - assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); - assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); - assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); - assert_parsed_expression_simplify_to("log(26061622162116)", "2*log(2)+log(3)+log(2171801846843)"); - assert_parsed_expression_simplify_to("log(26061622162116/5)", "2*log(2)+log(3)-log(5)+log(2171801846843)"); - - // Factorial - assert_parsed_expression_simplify_to("1/3!", "1/6"); - assert_parsed_expression_simplify_to("5!", "120"); - assert_parsed_expression_simplify_to("(1/3)!", "undef"); - assert_parsed_expression_simplify_to("P!", "undef"); - assert_parsed_expression_simplify_to("X!", "undef"); - - // Root at denominator - assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "(-R(2))+R(3)"); - assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); - assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); - assert_parsed_expression_simplify_to("1/(2R(2)-4)", "((-2)-R(2))/4"); - assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "(2+R(2))/4"); - assert_parsed_expression_simplify_to("45^2", "2025"); - assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2*ln(3))"); - assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); - - // Common operation mix - assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); - assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3*R(2)-2*R(3)+25*P)/25"); - assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); - assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "(-40*cos(2))+ln(3)+cos(3)*ln(5)+4*A*B-3*A*B*C"); - assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "4+3*cos(2)+6*ln(5)+7*A"); - assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "(887+600*cos(2)+1200*ln(5)+1460*A)/200"); - assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "(70*A^2+23*A*B+72*A*B*C)/10"); - assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "4*A^3+A^3*B^2"); - assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13*A^3"); - assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "4*A^3+A^5"); - assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "((-9)-5*cos(4)+30*A*B+5*A*B*C*D)/5"); - assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); - assert_parsed_expression_simplify_to("(2+R(6))^2", "(2+R(6))^2"); - assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)*ln(2)+P"); - - - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) - //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); // TODO: Newton binome - //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? - - // Matrix -#if MATRIX_EXACT_REDUCING - // Addition Matrix - assert_parsed_expression_simplify_to("1+[[1,2,3][4,5,6]]", "[[2,3,4][5,6,7]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+1", "[[2,3,4][5,6,7]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]+[[1,2,3][4,5,6]]", "undef"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); - assert_parsed_expression_simplify_to("2+[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[4,6,8][10,12,14]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+cos(2)+[[1,2,3][4,5,6]]", "[[2+cos(2),4+cos(2),6+cos(2)][8+cos(2),10+cos(2),12+cos(2)]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+10+[[1,2,3][4,5,6]]+R(2)", "[[12+R(2),14+R(2),16+R(2)][18+R(2),20+R(2),22+R(2)]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)+3", "inverse([[1,2][3,4]])+3"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+3", "inverse([[37,54][81,118]])+3"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]", "inverse([[37,54][81,118]])+[[1,2][3,4]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]+4+R(2)", "inverse([[37,54][81,118]])+[[5+R(2),6+R(2)][7+R(2),8+R(2)]]"); - - // Multiplication Matrix - assert_parsed_expression_simplify_to("2*[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*R(2)", "[[R(2),2R(2),3R(2)][4R(2),5R(2),6R(2)]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]*[[1,2,3][4,5,6]]", "[[9, 12, 15][19, 26, 33]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*[[1,2][2,3][5,6]]", "[[20, 26][44, 59]]"); - assert_parsed_expression_simplify_to("[[1,2,3,4][4,5,6,5]]*[[1,2][2,3][5,6]]", "undef"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2,3][3,4,5]]*[[1,2][3,2][4,5]]*4", "[[37,54][81,118]]^(-1)*[[76,84][140,156]]"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); - - // Power Matrix - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6][7,8,9]]^3", "[[468,576,684][1062,1305,1548][1656,2034,2412]]"); - assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]^(-1)", "undef"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)", "[[1,2][3,4]]^(-1)"); // TODO: Implement matrix inverse for dim < 3 - - // Function on matrix - assert_parsed_expression_simplify_to("abs([[1,-2][3,4]])", "[[1,2][3,4]]"); - assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); - assert_parsed_expression_simplify_to("asin([[1/R(2),1/2][1,-1]])", "[[P/4,P/6][P/2,-P/2]]"); - assert_parsed_expression_simplify_to("atan([[R(3),1][1/R(3),-1]])", "[[P/3,P/4][P/6,-P/4]]"); - assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); - assert_parsed_expression_simplify_to("binomial([[1,-2][3,4]], 2)", "undef"); - assert_parsed_expression_simplify_to("ceil([[1/R(2),1/2][1,-1.3]])", "[[ceil(R(2)/2),1][1,-1]]"); - assert_parsed_expression_simplify_to("confidence(1/3, 25)", "[[2/15,8/15]]"); - assert_parsed_expression_simplify_to("confidence(45, 25)", "undef"); - assert_parsed_expression_simplify_to("confidence(1/3, -34)", "undef"); - assert_parsed_expression_simplify_to("conj([[1/R(2),1/2][1,-1]])", "[[conj(1/R(2)),1/2][1,-1]]"); - assert_parsed_expression_simplify_to("cos([[P/3,0][P/7,P/2]])", "[[1/2,1][cos(P/7),0]]"); - assert_parsed_expression_simplify_to("diff([[P/3,0][P/7,P/2]],3)", "undef"); - assert_parsed_expression_simplify_to("det([[1,2][3,4]])", "det([[1,2][3,4]])"); // TODO: implement determinant if dim < 3 - assert_parsed_expression_simplify_to("det([[2,2][3,4]])", "det([[2,2][3,4]])"); - assert_parsed_expression_simplify_to("det([[2,2][3,3]])", "det([[2,2][3,3]])"); - assert_parsed_expression_simplify_to("quo([[2,2][3,3]],2)", "undef"); - assert_parsed_expression_simplify_to("rem([[2,2][3,3]],2)", "undef"); - assert_parsed_expression_simplify_to("[[1,2][3,4]]!", "[[1,2][6,24]]"); - assert_parsed_expression_simplify_to("floor([[1/R(2),1/2][1,-1.3]])", "[[floor(R(2)/2),0][1,-2]]"); - assert_parsed_expression_simplify_to("frac([[1/R(2),1/2][1,-1.3]])", "[[frac(R(2)/2),1/2][0,0.7]]"); - assert_parsed_expression_simplify_to("gcd([[1/R(2),1/2][1,-1.3]], [[1]])", "undef"); - assert_parsed_expression_simplify_to("asinh([[1/R(2),1/2][1,-1]])", "[[asinh(1/R(2)),asinh(1/2)][asinh(1),asinh(-1)]]"); - assert_parsed_expression_simplify_to("atanh([[R(3),1][1/R(3),-1]])", "[[atanh(R(3)),atanh(1)][atanh(1/R(3)),atanh(-1)]]"); - assert_parsed_expression_simplify_to("acosh([[1/R(2),1/2][1,-1]])", "[[acosh(1/R(2)),acosh(1/2)][acosh(1),acosh(-1)]]"); - assert_parsed_expression_simplify_to("sinh([[1/R(2),1/2][1,-1]])", "[[sinh(1/R(2)),sinh(1/2)][sinh(1),sinh(-1)]]"); - assert_parsed_expression_simplify_to("tanh([[R(3),1][1/R(3),-1]])", "[[tanh(R(3)),tanh(1)][tanh(1/R(3)),tanh(-1)]]"); - assert_parsed_expression_simplify_to("cosh([[1/R(2),1/2][1,-1]])", "[[cosh(1/R(2)),cosh(1/2)][cosh(1),cosh(-1)]]"); - assert_parsed_expression_simplify_to("im([[1/R(2),1/2][1,-1]])", "[[im(1/R(2)),0][0,0]]"); - assert_parsed_expression_simplify_to("int([[P/3,0][P/7,P/2]],3,2)", "undef"); - assert_parsed_expression_simplify_to("lcm(2, [[1]])", "undef"); - assert_parsed_expression_simplify_to("log([[R(2),1/2][1,3]])", "[[(1/2)*log(2),-log(2)][0,log(3)]]"); - assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); - assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]],3)", "undef"); - assert_parsed_expression_simplify_to("ln([[R(2),1/2][1,3]])", "[[(1/2)*ln(2),-ln(2)][0,ln(3)]]"); - assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); - assert_parsed_expression_simplify_to("dim([[1/R(2),1/2,3][2,1,-3]])", "[[2,3]]"); - assert_parsed_expression_simplify_to("inverse([[1/R(2),1/2,3][2,1,-3]])", "undef"); - assert_parsed_expression_simplify_to("inverse([[1,2][3,4]])", "inverse([[1,2][3,4]])"); // TODO: implement matrix inverse if dim < 3 - assert_parsed_expression_simplify_to("trace([[1/R(2),1/2,3][2,1,-3]])", "undef"); - assert_parsed_expression_simplify_to("trace([[R(2),2][4,3+log(3)]])", "R(2)+3+log(3)"); - assert_parsed_expression_simplify_to("trace(R(2)+log(3))", "R(2)+log(3)"); - assert_parsed_expression_simplify_to("transpose([[1/R(2),1/2,3][2,1,-3]])", "[[1/R(2),2][1/2, 1][3,-3]]"); - assert_parsed_expression_simplify_to("transpose(R(4))", "2"); - assert_parsed_expression_simplify_to("root([[R(4)]],2)", "undef"); - assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); - assert_parsed_expression_simplify_to("-[[1/R(2),1/2,3][2,1,-3]]", "[[-1/R(2),-1/2,-3][-2,-1,3]]"); - assert_parsed_expression_simplify_to("permute([[1,-2][3,4]], 2)", "undef"); - assert_parsed_expression_simplify_to("prediction95(1/3, 25)", "[[1/3-49R(2)/375,1/3+49R(2)/375]]"); - assert_parsed_expression_simplify_to("prediction95(45, 25)", "undef"); - assert_parsed_expression_simplify_to("prediction95(1/3, -34)", "undef"); - assert_parsed_expression_simplify_to("product([[1,2][3,4]], 1/3, -34)", "product([[1,2][3,4]], 1/3, -34)"); - assert_parsed_expression_simplify_to("sum([[1,2][3,4]], 1/3, -34)", "sum([[1,2][3,4]], 1/3, -34)"); - assert_parsed_expression_simplify_to("re([[1/R(2),1/2][1,-1]])", "[[re(1/R(2)),1/2][1,-1]]"); - assert_parsed_expression_simplify_to("round([[1/R(2),1/2][1,-1]],2)", "undef"); - assert_parsed_expression_simplify_to("sin([[P/3,0][P/7,P/2]])", "[[R(3)/2,0][sin(P/7),1]]"); - assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "[[2,R(2)][R(P/7),1]]"); - assert_parsed_expression_simplify_to("tan([[P/3,0][P/7,P/6]])", "[[R(3),0][tan(P/7),R(3)/3]]"); -#else - assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "R([[4,2][P/7,1]])"); -#endif - -} diff --git a/poincare/test/simplify_mix.cpp b/poincare/test/simplify_mix.cpp new file mode 100644 index 000000000..fb4e05453 --- /dev/null +++ b/poincare/test/simplify_mix.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include "helper.h" +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif + +using namespace Poincare; + +QUIZ_CASE(poincare_simplify_mix) { + + // Root at denominator + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "(-R(2))+R(3)"); + assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); + assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); + assert_parsed_expression_simplify_to("1/(2R(2)-4)", "((-2)-R(2))/4"); + assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "(2+R(2))/4"); + assert_parsed_expression_simplify_to("45^2", "2025"); + assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2*ln(3))"); + assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); + + // Common operation mix + assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); + assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3*R(2)-2*R(3)+25*P)/25"); + assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); + assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "(-40*cos(2))+ln(3)+cos(3)*ln(5)+4*A*B-3*A*B*C"); + assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "4+3*cos(2)+6*ln(5)+7*A"); + assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "(887+600*cos(2)+1200*ln(5)+1460*A)/200"); + assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "(70*A^2+23*A*B+72*A*B*C)/10"); + assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "4*A^3+A^3*B^2"); + assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13*A^3"); + assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "4*A^3+A^5"); + assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "((-9)-5*cos(4)+30*A*B+5*A*B*C*D)/5"); + assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); + assert_parsed_expression_simplify_to("(2+R(6))^2", "(2+R(6))^2"); + assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)*ln(2)+P"); + + + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); // TODO: Newton binome + //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? + +} diff --git a/poincare/test/simplify_product.cpp b/poincare/test/simplify_product.cpp deleted file mode 100644 index ce1207271..000000000 --- a/poincare/test/simplify_product.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include "simplify_utils.h" - -using namespace Poincare; - -QUIZ_CASE(poincare_simplify_product_by_zero) { - assert(simplifies_to("3*0", "0")); - assert(simplifies_to("A*0", "0")); - - assert(simplifies_to("0*3", "0")); - assert(simplifies_to("0*A", "0")); - - assert(simplifies_to("3*5", "15")); - assert(simplifies_to("8*6", "48")); - - assert(simplifies_to("3*(5+4)", "27")); -} - -QUIZ_CASE(poincare_simplify_distributive_reverse) { - assert(equivalent_to("x+x", "2*x")); - assert(equivalent_to("2*x+x", "3*x")); - assert(equivalent_to("x*2+x", "3*x")); - assert(equivalent_to("2*x+2*x", "4*x")); - assert(equivalent_to("x*2+2*t", "2*(x+t)")); - assert(equivalent_to("x+x+t+t", "2*x+2*t")); - assert(equivalent_to("2*x+2*t", "2*(x+t)")); - //assert(equivalent_to("x+x+t+t", "2*(x+t)")); - assert(equivalent_to("x-x-t+t", "0)")); - assert(equivalent_to("x+t-x-t", "0")); - assert(equivalent_to("x+x*t", "x*(t+1)")); - assert(equivalent_to("x-x", "0")); - assert(equivalent_to("x-x", "0")); -} diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp index 0ba08252e..ce6822008 100644 --- a/poincare/test/trigo.cpp +++ b/poincare/test/trigo.cpp @@ -47,3 +47,181 @@ QUIZ_CASE(poincare_trigo_evaluate) { Complex c3[1] = {Complex::Cartesian(-1.00027905623446556836909f, 0.000610240921376259f)}; assert_parsed_expression_evaluates_to("tanh(I-4)", c3, Radian); } + +QUIZ_CASE(poincare_trigo_simplify) { + // -- sin/cos -> tan + assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); + assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); + assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "tan(x)*P"); + assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); + assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); + assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); + assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos((5*P)/21)/sin((8*P)/17)"); + assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-(3*P)/tan(P/21)"); + assert_parsed_expression_simplify_to("cos(62P/21)/(P*3*sin(62P/21))", "-1/(3*tan(P/21)*P)"); + assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3*tan(P/21)*P"); + assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3*P)"); + assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)*P)"); + assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-(2*ln(3))/(tan(P/62)*P)"); + // -- cos + assert_parsed_expression_simplify_to("cos(0)", "1"); + assert_parsed_expression_simplify_to("cos(P)", "-1"); + assert_parsed_expression_simplify_to("cos(P*4/7)", "-cos((3*P)/7)"); + assert_parsed_expression_simplify_to("cos(P*35/29)", "-cos((6*P)/29)"); + assert_parsed_expression_simplify_to("cos(-P*35/29)", "-cos((6*P)/29)"); + assert_parsed_expression_simplify_to("cos(P*340000)", "1"); + assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); + assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(R(2)*P)"); + assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); + assert_parsed_expression_simplify_to("cos(P/12)", "(R(2)+R(6))/4"); + assert_parsed_expression_simplify_to("cos(-P/12)", "(R(2)+R(6))/4"); + assert_parsed_expression_simplify_to("cos(-P17/8)", "R(2+R(2))/2"); + assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); + assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); + assert_parsed_expression_simplify_to("cos(41P/5)", "(1+R(5))/4"); + assert_parsed_expression_simplify_to("cos(7P/10)", "-(R(2)*R(5-R(5)))/4"); + assert_parsed_expression_simplify_to("cos(0)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(61200000)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(39330)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-15)", "(R(2)+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-765/2)", "R(2+R(2))/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-60)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/5)", "(1+R(5))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); + // -- sin + assert_parsed_expression_simplify_to("sin(0)", "0"); + assert_parsed_expression_simplify_to("sin(P)", "0"); + assert_parsed_expression_simplify_to("sin(P*35/29)", "-sin((6*P)/29)"); + assert_parsed_expression_simplify_to("sin(-P*35/29)", "sin((6*P)/29)"); + assert_parsed_expression_simplify_to("sin(P*340000)", "0"); + assert_parsed_expression_simplify_to("sin(P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(P/12)", "((-R(2))+R(6))/4"); + assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); + assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(R(2)*P)"); + assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); + assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(2-R(2))/2"); + assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); + assert_parsed_expression_simplify_to("sin(-3P/10)", "((-1)-R(5))/4"); + assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); + assert_parsed_expression_simplify_to("sin(17P/5)", "-(R(2)*R(5+R(5)))/4"); + assert_parsed_expression_simplify_to("sin(P/5)", "(R(2)*R(5-R(5)))/4"); + assert_parsed_expression_simplify_to("sin(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(15)", "((-R(2))+R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(39330)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-765/2)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(1230)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(612)", "-(R(2)*R(5+R(5)))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(36)", "(R(2)*R(5-R(5)))/4", Expression::AngleUnit::Degree); + // -- tan + assert_parsed_expression_simplify_to("tan(0)", "0"); + assert_parsed_expression_simplify_to("tan(P)", "0"); + assert_parsed_expression_simplify_to("tan(P*35/29)", "tan((6*P)/29)"); + assert_parsed_expression_simplify_to("tan(-P*35/29)", "-tan((6*P)/29)"); + assert_parsed_expression_simplify_to("tan(P*340000)", "0"); + assert_parsed_expression_simplify_to("tan(P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/12)", "(-2)+R(3)"); + assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(R(2)*P)"); + assert_parsed_expression_simplify_to("tan(1311P/6)", "undef"); + assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); + assert_parsed_expression_simplify_to("tan(41P/6)", "-R(3)/3"); + assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); + assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/10)", "-(R(5)*R(5-2*R(5)))/5"); + assert_parsed_expression_simplify_to("tan(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-15)", "(-2)+R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(39330)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(1230)", "-R(3)/3", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180045)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); + // -- acos + assert_parsed_expression_simplify_to("acos(-1/2)", "(2*P)/3"); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef"); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(3/2))", "3/2"); + assert_parsed_expression_simplify_to("cos(acos(3/2))", "undef"); + assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); + assert_parsed_expression_simplify_to("acos(cos(4P/7))", "(4*P)/7"); + assert_parsed_expression_simplify_to("acos(-cos(2))", "(-2)+P"); + assert_parsed_expression_simplify_to("acos(-1/2)", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(190))", "170", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(190))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(12))", "12", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Expression::AngleUnit::Degree); + // -- asin + assert_parsed_expression_simplify_to("asin(-1/2)", "-P/6"); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef"); + assert_parsed_expression_simplify_to("asin(sin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(3/2))", "undef"); + assert_parsed_expression_simplify_to("asin(sin(3/2))", "3/2"); + assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); + assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); + assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(190))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(32))", "32", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(400))", "40", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Expression::AngleUnit::Degree); + // -- atan + assert_parsed_expression_simplify_to("atan(-1)", "-P/4"); + assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)"); + assert_parsed_expression_simplify_to("atan(tan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(5/2))", "5/2"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); + assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); + assert_parsed_expression_simplify_to("atan(-1)", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(120))", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(R(3))", "60", Expression::AngleUnit::Degree); +} From e77a66c7d5b9fc47b99b1f4c510627fe3ef525bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Nov 2017 16:53:16 +0100 Subject: [PATCH 58/77] [poincare] Fix bug in addMissingFactor (a shallow reduce missing) Change-Id: I7200f86f9baadd3f7e7dce384aa66d30aabe654f --- poincare/src/multiplication.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 964ebb1d5..cc9c91476 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -623,6 +623,7 @@ void Multiplication::addMissingFactors(Expression * factor, Context & context, A Reduce((Expression **)&sub, context, angleUnit); if (sub->sign() == Sign::Negative) { // index[0] < index[1] factor->replaceOperand(factor->editableOperand(1), new Opposite(sub, true), true); + factor->editableOperand(1)->shallowReduce(context, angleUnit); factorizeBase(editableOperand(i), factor, context, angleUnit); editableOperand(i)->shallowReduce(context, angleUnit); } else if (sub->sign() == Sign::Unknown) { From f962f7ef6de1844f20efe970c3c6ceee70806e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 29 Nov 2017 16:53:49 +0100 Subject: [PATCH 59/77] [poincare] Remove ambiguity on casting char in int (different behaviours for different compilers..) Change-Id: I51630e104311d2038b140a9877ede893dddfefeb --- poincare/src/symbol.cpp | 4 ++-- poincare/test/addition.cpp | 6 +++--- poincare/test/power.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 009ef353b..57db3583e 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -190,10 +190,10 @@ bool Symbol::isScalarSymbol() const { int Symbol::simplificationOrderSameType(const Expression * e) const { assert(e->type() == Expression::Type::Symbol); - if (m_name == ((Symbol *)e)->m_name) { + if ((uint8_t)m_name == ((uint8_t)static_cast(e)->name())) { return 0; } - if ((m_name > ((Symbol *)e)->m_name)) { + if ((uint8_t)m_name > ((uint8_t)static_cast(e)->name())) { return 1; } return -1; diff --git a/poincare/test/addition.cpp b/poincare/test/addition.cpp index ce0fad89b..402c28b5f 100644 --- a/poincare/test/addition.cpp +++ b/poincare/test/addition.cpp @@ -48,9 +48,9 @@ QUIZ_CASE(poincare_addition_simplify) { assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); assert_parsed_expression_simplify_to("-A", "-A"); - assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+P+x)/(2+P+2*x+P*x)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(P*x^2)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+P*x)/(P*x^3)"); + assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+x+P)/(2+2*x+P+x*P)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(x^2*P)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+x*P)/(x^3*P)"); assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); assert_parsed_expression_simplify_to("A+B-A-B", "0"); assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); diff --git a/poincare/test/power.cpp b/poincare/test/power.cpp index f502dc323..fd238d4ed 100644 --- a/poincare/test/power.cpp +++ b/poincare/test/power.cpp @@ -56,8 +56,8 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("R(P^2)", "P"); assert_parsed_expression_simplify_to("R((-P)^2)", "P"); assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(P)*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*R(x)*P"); + assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(x)*R(P)"); assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); assert_parsed_expression_simplify_to("x^(1/7)", "root(x,7)"); From 11ab2a871f752ef505ca0fe42410274143e3f2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 10:30:18 +0100 Subject: [PATCH 60/77] [poincare] Fix bug in casting Integer to float/double Change-Id: Ibfc0be39dd6e90e99e226b40f4171c01f54ddae1 --- poincare/src/integer.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index 3b673e6d3..c1ee0f379 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -507,12 +507,22 @@ T Integer::approximate() const { exponent += numberOfBitsInLastDigit; uint64_t mantissa = 0; + /* Shift the most significant int to the left of the mantissa. The most + * significant 1 will be ignore at the end when inserting the mantissa in + * the resulting uint64_t (as required by IEEE754). */ mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBitsInLastDigit)); int digitIndex = 2; - int numberOfBits = log2(lastDigit); + int numberOfBits = numberOfBitsInLastDigit; + /* Complete the mantissa by inserting, from left to right, every digit of the + * Integer from the most significant one to the last from. We break when + * the mantissa is complete to avoid undefined right shifting (when the shift + * width is wider than the length of the digit in bits). */ while (m_numberOfDigits >= digitIndex) { lastDigit = digit(m_numberOfDigits-digitIndex); numberOfBits += 32; + if (numberOfBits-totalNumberOfBits >= 64) { + break; + } if (totalNumberOfBits > numberOfBits) { mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBits)); } else { @@ -520,6 +530,11 @@ T Integer::approximate() const { } digitIndex++; } + // TODO: Here, we just cast the Integer in float(double). We should round it + // to the closest float(double). To do so, we should keep a additional bit + // at the end of the mantissa and add it to the mantissa; do not forget to + // shift the mantissa if the rounding increase the length in bits of the + // mantissa. if (isZero()) { /* This special case for 0 is needed, because the current algorithm assumes From 79b174337ae7fa72fe03e060454e543139f30346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 10:30:46 +0100 Subject: [PATCH 61/77] [poincare] Fix bug in complex convertFloatToText Change-Id: I04a8b88acef9426ab728df57c789af5a644b9c54 --- poincare/src/complex.cpp | 15 ++++----------- poincare/test/convert_expression_to_text.cpp | 1 + 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 390c858c0..071ecaea5 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -371,18 +371,11 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif } /* We update the exponent in base 10 (if 0.99999999 was rounded to 1 for * instance) */ - T truncatedMantissa = (int)(f * std::pow(10, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))); - if (std::isinf(truncatedMantissa) || std::isnan(truncatedMantissa)) { - truncatedMantissa = (int)(std::pow(10, std::log10(std::fabs(f))+(T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal))); - truncatedMantissa = std::copysign(truncatedMantissa, f); - } - if (mantissa != truncatedMantissa) { - T newLogBase10 = mantissa != 0 ? std::log10(std::fabs(mantissa/std::pow((T)10, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)))) : 0; - if (std::isnan(newLogBase10) || std::isinf(newLogBase10)) { - newLogBase10 = std::log10(std::fabs((T)mantissa)) - (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal); - } - exponentInBase10 = std::floor(newLogBase10); + T newLogBase10 = mantissa != 0 ? std::log10(std::fabs(mantissa/std::pow((T)10, (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)))) : 0; + if (std::isnan(newLogBase10) || std::isinf(newLogBase10)) { + newLogBase10 = std::log10(std::fabs((T)mantissa)) - (T)(availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal); } + exponentInBase10 = std::floor(newLogBase10); int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == Expression::FloatDisplayMode::Scientific ? 1 : exponentInBase10+1; diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index b90e27e7d..822858ff8 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -91,6 +91,7 @@ QUIZ_CASE(assert_float_prints_to) { assert_float_prints_to(123.421, "1.2E2", DecimalDisplay, 5, 6); assert_float_prints_to(9.999999f, "10", DecimalDisplay, 6); assert_float_prints_to(-9.99999904, "-10", DecimalDisplay, 6); + assert_float_prints_to(1E50, "1E50", DecimalDisplay, 9); } QUIZ_CASE(poincare_rational_to_text) { From 81016fa16881b4e9fb8a32729717e9c1eb27822c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 10:47:20 +0100 Subject: [PATCH 62/77] [poincare] Get 14 significant digits when writing complex to text Change-Id: I0bdcc932782dce91d5861acc698e5b2f7a15355c --- poincare/include/poincare/complex.h | 34 +++--- poincare/src/complex.cpp | 109 ++++++++++--------- poincare/test/convert_expression_to_text.cpp | 35 +++--- 3 files changed, 92 insertions(+), 86 deletions(-) diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index bdd5ad43d..021f679eb 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace Poincare { @@ -12,14 +13,29 @@ namespace PrintFloat { // The wors case is -1.234E-38 return numberOfSignificantDigits + 7; } - /* This function prints the int i in the buffer with a '.' at the position + /* This function prints the integer i in the buffer with a '.' at the position * specified by the decimalMarkerPosition. It starts printing at the end of the * buffer and print from right to left. The integer given should be of the right * length to be written in bufferLength chars. If the integer is to small, the * empty chars on the left side are completed with '0'. If the integer is too * big, the printing stops when no more empty chars are available without - * returning any warning. */ - void printBase10IntegerWithDecimalMarker(char * buffer, int bufferSize, int i, int decimalMarkerPosition); + * returning any warning. + * Warning: the buffer is not null terminated but is ensured to hold + * bufferLength chars. */ + void printBase10IntegerWithDecimalMarker(char * buffer, int bufferLength, Integer i, int decimalMarkerPosition); + + constexpr static int k_numberOfPrintedSignificantDigits = 7; + constexpr static int k_numberOfStoredSignificantDigits = 14; + + /* We here define the buffer size to write the lengthest float possible. + * At maximum, the number has 15 significant digits so, in the worst case it + * has the form -1.99999999999999e-308 (15+7+1 char) (the auto mode is always + * shorter. */ + constexpr static int k_maxFloatBufferLength = k_numberOfStoredSignificantDigits+7+1; + /* We here define the buffer size to write the lengthest complex possible. + * The worst case has the form + * -1.99999999999999e-308*e^(-1.99999999999999e-308*i) (14+14+7+1 char) */ + constexpr static int k_maxComplexBufferLength = k_maxFloatBufferLength-1+k_maxFloatBufferLength-1+7+1; } template @@ -65,23 +81,13 @@ public: static int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode mode = Expression::FloatDisplayMode::Default); private: Complex(T a, T b); - constexpr static int k_numberOfSignificantDigits = 7; ExpressionLayout * privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const override; Expression * privateApproximate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } Expression * privateApproximate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } template Complex * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const; - /* We here define the buffer size to write the lengthest float possible. - * At maximum, the number has 7 significant digits so, in the worst case it - * has the form -1.999999e-308 (7+7+1 char) (the auto mode is always - * shorter. */ - constexpr static int k_maxFloatBufferLength = 7+7+1; - /* We here define the buffer size to write the lengthest complex possible. - * The worst case has the form -1.999999E-308*e^(-1.999999E-308*i) (14+14+7+1 - * char) */ - constexpr static int k_maxComplexBufferLength = 14+14+7+1; /* convertComplexToText and convertFloatToTextPrivate return the string length * of the buffer (does not count the 0 last char)*/ - int convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, char multiplicationSign) const; + int convertComplexToText(char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, char multiplicationSign) const; static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Expression::FloatDisplayMode mode); ExpressionLayout * createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const; ExpressionLayout * createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const; diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 071ecaea5..e4df4f94c 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -13,31 +13,28 @@ extern "C" { namespace Poincare { -void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferSize, int i, int decimalMarkerPosition) { +void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLength, Integer i, int decimalMarkerPosition) { /* The decimal marker position is always preceded by a char, thus, it is never * in first position. When called by convertFloatToText, the buffer length is * always > 0 as we asserted a minimal number of available chars. */ - assert(bufferSize > 0 && decimalMarkerPosition != 0); - int endChar = bufferSize - 1, startChar = 0; - int dividend = i, digit = 0, quotien = 0; - if (i < 0) { - buffer[startChar++] = '-'; - dividend = -i; - decimalMarkerPosition += 1; - } - /* This loop acts correctly as we asserted the endChar >= 0 and - * decimalMarkerPosition != 0 */ - do { - if (endChar == decimalMarkerPosition) { - assert(endChar >= 0 && endChar < bufferSize); - buffer[endChar--] = '.'; + assert(bufferLength > 0 && decimalMarkerPosition != 0); + char tempBuffer[PrintFloat::k_maxFloatBufferLength]; + int intLength = i.writeTextInBuffer(tempBuffer, PrintFloat::k_maxFloatBufferLength); + int firstDigitChar = tempBuffer[0] == '-' ? 1 : 0; + for (int k = bufferLength-1; k >= firstDigitChar; k--) { + if (k == decimalMarkerPosition) { + buffer[k] = '.'; + continue; } - quotien = dividend/10; - digit = dividend - quotien*10; - assert(endChar >= 0 && endChar < bufferSize); - buffer[endChar--] = '0'+digit; - dividend = quotien; - } while (endChar >= startChar); + if (intLength > firstDigitChar) { + buffer[k] = tempBuffer[--intLength]; + continue; + } + buffer[k] = '0'; + } + if (firstDigitChar == 1) { + buffer[0] = tempBuffer[0]; + } } template @@ -200,7 +197,7 @@ T Complex::toScalar() const { template int Complex::writeTextInBuffer(char * buffer, int bufferSize) const { - return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat(), Ion::Charset::MultiplicationSign); + return convertComplexToText(buffer, bufferSize, PrintFloat::k_numberOfStoredSignificantDigits, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat(), Ion::Charset::MultiplicationSign); } template @@ -210,7 +207,7 @@ int Complex::convertFloatToText(T f, char * buffer, int bufferSize, if (mode == Expression::FloatDisplayMode::Default) { return convertFloatToText(f, buffer, bufferSize, numberOfSignificantDigits, Preferences::sharedPreferences()->displayMode()); } - char tempBuffer[k_maxFloatBufferLength]; + char tempBuffer[PrintFloat::k_maxFloatBufferLength]; int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode); /* if the required buffer size overflows the buffer size, we first force the * display mode to scientific and decrease the number of significant digits to @@ -253,15 +250,15 @@ Complex * Complex::templatedApproximate(Context& context, Expression::Angl } template -int Complex::convertComplexToText(char * buffer, int bufferSize, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat, char multiplicationSpecialChar) const { +int Complex::convertComplexToText(char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode displayMode, Expression::ComplexFormat complexFormat, char multiplicationSpecialChar) const { assert(displayMode != Expression::FloatDisplayMode::Default); int numberOfChars = 0; if (std::isnan(m_a) || std::isnan(m_b)) { - return convertFloatToText(NAN, buffer, bufferSize, k_numberOfSignificantDigits, displayMode); + return convertFloatToText(NAN, buffer, bufferSize, numberOfSignificantDigits, displayMode); } if (complexFormat == Expression::ComplexFormat::Polar) { if (r() != 1 || th() == 0) { - numberOfChars = convertFloatToText(r(), buffer, bufferSize, k_numberOfSignificantDigits, displayMode); + numberOfChars = convertFloatToText(r(), buffer, bufferSize, numberOfSignificantDigits, displayMode); if (r() != 0 && th() != 0 && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = multiplicationSpecialChar; // Ensure that the string is null terminated even if buffer size is to small @@ -276,7 +273,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: // Ensure that the string is null terminated even if buffer size is to small buffer[numberOfChars] = 0; } - numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); + numberOfChars += convertFloatToText(th(), buffer+numberOfChars, bufferSize-numberOfChars, numberOfSignificantDigits, displayMode); if (bufferSize > numberOfChars+3) { buffer[numberOfChars++] = multiplicationSpecialChar; buffer[numberOfChars++] = Ion::Charset::IComplex; @@ -288,7 +285,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } if (m_a != 0 || m_b == 0) { - numberOfChars = convertFloatToText(m_a, buffer, bufferSize, k_numberOfSignificantDigits, displayMode); + numberOfChars = convertFloatToText(m_a, buffer, bufferSize, numberOfSignificantDigits, displayMode); if (m_b > 0 && !std::isnan(m_b) && bufferSize > numberOfChars+1) { buffer[numberOfChars++] = '+'; // Ensure that the string is null terminated even if buffer size is to small @@ -296,7 +293,7 @@ int Complex::convertComplexToText(char * buffer, int bufferSize, Expression:: } } if (m_b != 1 && m_b != -1 && m_b != 0) { - numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, k_numberOfSignificantDigits, displayMode); + numberOfChars += convertFloatToText(m_b, buffer+numberOfChars, bufferSize-numberOfChars, numberOfSignificantDigits, displayMode); buffer[numberOfChars++] = multiplicationSpecialChar; } if (m_b == -1 && bufferSize > numberOfChars+1) { @@ -354,14 +351,17 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif int availableCharsForMantissaWithSign = f >= 0 ? availableCharsForMantissaWithoutSign : availableCharsForMantissaWithoutSign + 1; // Compute mantissa - /* The number of digits in an integer is capped because the maximal integer is - * 2^31 - 1. As our mantissa is an integer, we assert that we stay beyond this - * threshold during computation. */ - assert(availableCharsForMantissaWithoutSign - 1 < std::log10(std::pow(2.0f, 31.0f))); + /* The number of digits in an mantissa is capped because the maximal int64_t + * is 2^63 - 1. As our mantissa is an integer built from an int64_t, we assert + * that we stay beyond this threshold during computation. */ + assert(availableCharsForMantissaWithoutSign - 1 < std::log10(std::pow(2.0f, 63.0f))); int numberOfDigitBeforeDecimal = exponentInBase10 >= 0 || displayMode == Expression::FloatDisplayMode::Scientific ? exponentInBase10 + 1 : 1; - T mantissa = std::round(f * std::pow(10, (T)availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal)); + + T unroundedMantissa = f * std::pow(10, (T)availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal); + T mantissa = std::round(unroundedMantissa); + /* if availableCharsForMantissaWithoutSign - 1 - numberOfDigitBeforeDecimal * is too big (or too small), mantissa is now inf. We handle this case by * using logarithm function. */ @@ -378,10 +378,11 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif exponentInBase10 = std::floor(newLogBase10); int decimalMarkerPosition = exponentInBase10 < 0 || displayMode == Expression::FloatDisplayMode::Scientific ? 1 : exponentInBase10+1; + decimalMarkerPosition = f < 0 ? decimalMarkerPosition+1 : decimalMarkerPosition; // Correct the number of digits in mantissa after rounding int mantissaExponentInBase10 = exponentInBase10 > 0 || displayMode == Expression::FloatDisplayMode::Scientific ? availableCharsForMantissaWithoutSign - 1 : availableCharsForMantissaWithoutSign + exponentInBase10; - if ((int)(std::fabs(mantissa) * std::pow((T)10, - mantissaExponentInBase10)) > 0) { + if (std::floor(std::fabs((T)mantissa) * std::pow((T)10, - mantissaExponentInBase10)) > 0) { mantissa = mantissa/10; } @@ -392,18 +393,18 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif } // Supress the 0 on the right side of the mantissa - int dividend = std::fabs((T)mantissa); - int quotien = dividend/10; - int digit = dividend - quotien*10; + Integer dividend = Integer((int64_t)std::fabs(mantissa)); + Integer quotient = Integer::Division(dividend, Integer(10)).quotient; + Integer digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10))); int minimumNumberOfCharsInMantissa = 1; - while (digit == 0 && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa && + while (digit.isZero() && availableCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa && (availableCharsForMantissaWithoutSign > exponentInBase10+2 || displayMode == Expression::FloatDisplayMode::Scientific)) { mantissa = mantissa/10; availableCharsForMantissaWithoutSign--; availableCharsForMantissaWithSign--; - dividend = quotien; - quotien = dividend/10; - digit = dividend - quotien*10; + dividend = quotient; + quotient = Integer::Division(dividend, Integer(10)).quotient; + digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10))); } // Suppress the decimal marker if no fractional part @@ -413,34 +414,34 @@ int Complex::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif } // Print mantissa - assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength); - PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, mantissa, decimalMarkerPosition); + assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength); + PrintFloat::printBase10IntegerWithDecimalMarker(buffer, availableCharsForMantissaWithSign, Integer((int64_t)mantissa), decimalMarkerPosition); if (displayMode == Expression::FloatDisplayMode::Decimal || exponentInBase10 == 0) { buffer[availableCharsForMantissaWithSign] = 0; return availableCharsForMantissaWithSign; } // Print exponent - assert(availableCharsForMantissaWithSign < k_maxFloatBufferLength); + assert(availableCharsForMantissaWithSign < PrintFloat::k_maxFloatBufferLength); buffer[availableCharsForMantissaWithSign] = Ion::Charset::Exponent; - assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < k_maxFloatBufferLength); - PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, exponentInBase10, -1); + assert(numberOfCharExponent+availableCharsForMantissaWithSign+1 < PrintFloat::k_maxFloatBufferLength); + PrintFloat::printBase10IntegerWithDecimalMarker(buffer+availableCharsForMantissaWithSign+1, numberOfCharExponent, Integer(exponentInBase10), -1); buffer[availableCharsForMantissaWithSign+1+numberOfCharExponent] = 0; return (availableCharsForMantissaWithSign+1+numberOfCharExponent); } template ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode floatDisplayMode) const { - char bufferBase[k_maxFloatBufferLength+2]; + char bufferBase[PrintFloat::k_maxFloatBufferLength+2]; int numberOfCharInBase = 0; - char bufferSuperscript[k_maxFloatBufferLength+2]; + char bufferSuperscript[PrintFloat::k_maxFloatBufferLength+2]; int numberOfCharInSuperscript = 0; if (std::isnan(r()) || (std::isnan(th()) && r() != 0)) { - numberOfCharInBase = convertFloatToText(NAN, bufferBase, k_maxComplexBufferLength, k_numberOfSignificantDigits, floatDisplayMode); + numberOfCharInBase = convertFloatToText(NAN, bufferBase, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); return new StringLayout(bufferBase, numberOfCharInBase); } if (r() != 1 || th() == 0) { - numberOfCharInBase = convertFloatToText(r(), bufferBase, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); + numberOfCharInBase = convertFloatToText(r(), bufferBase, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); if (r() != 0 && th() != 0) { bufferBase[numberOfCharInBase++] = Ion::Charset::MiddleDot; } @@ -451,7 +452,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl } if (r() != 0 && th() != 0) { - numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, k_maxFloatBufferLength, k_numberOfSignificantDigits, floatDisplayMode); + numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::MiddleDot; bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex; bufferSuperscript[numberOfCharInSuperscript] = 0; @@ -464,8 +465,8 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl template ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const { - char buffer[k_maxComplexBufferLength]; - int numberOfChars = convertComplexToText(buffer, k_maxComplexBufferLength, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot); + char buffer[PrintFloat::k_maxComplexBufferLength]; + int numberOfChars = convertComplexToText(buffer, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot); return new StringLayout(buffer, numberOfChars); } diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index 822858ff8..84ee111df 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -91,6 +91,7 @@ QUIZ_CASE(assert_float_prints_to) { assert_float_prints_to(123.421, "1.2E2", DecimalDisplay, 5, 6); assert_float_prints_to(9.999999f, "10", DecimalDisplay, 6); assert_float_prints_to(-9.99999904, "-10", DecimalDisplay, 6); + assert_float_prints_to(-0.017452, "-0.01745", DecimalDisplay, 6); assert_float_prints_to(1E50, "1E50", DecimalDisplay, 9); } @@ -164,29 +165,27 @@ QUIZ_CASE(poincare_decimal_to_text) { QUIZ_CASE(poincare_complex_to_text) { Complex c0 = Complex::Cartesian(1.0, 2.0); assert_expression_prints_to(&c0, "1+2*I", DecimalDisplay, Cartesian); - Complex c1 = Complex::Cartesian(1.0f, 2.0f); - assert_expression_prints_to(&c1, "2.236068*X^(1.107149*I)", DecimalDisplay, Polar); - Complex c2 = Complex::Cartesian(-1.3f, 2.444f); + Complex c1 = Complex::Cartesian(1.0, 2.0); + assert_expression_prints_to(&c1, "2.2360679774998*X^(1.1071487177941*I)", DecimalDisplay, Polar); + Complex c2 = Complex::Cartesian(-1.3, 2.444); assert_expression_prints_to(&c2, "-1.3+2.444*I", DecimalDisplay, Cartesian); Complex c3 = Complex::Cartesian(-1.3, 2.444); - assert_expression_prints_to(&c3, "2.768237*X^(2.059649*I)", DecimalDisplay, Polar); - Complex c4 = Complex::Cartesian(-1.3f, -2.444f); + assert_expression_prints_to(&c3, "2.7682369840749*X^(2.0596486811226*I)", DecimalDisplay, Polar); + Complex c4 = Complex::Cartesian(-1.3, -2.444); assert_expression_prints_to(&c4, "-1.3-2.444*I", DecimalDisplay, Cartesian); Complex c5 = Complex::Cartesian(64078208.0, 119229408.0); - assert_expression_prints_to(&c5, "6.407821E7+1.192294E8*I", DecimalDisplay, Cartesian); - Complex c6 = Complex::Cartesian(64078208.0f, 119229408.0f); - assert_expression_prints_to(&c6, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar); - Complex c7 = Complex::Cartesian(64078208.0f, 119229408.0f); - assert_expression_prints_to(&c7, "1.353576E8*X^(1.07765*I)", DecimalDisplay, Polar); - Complex c8 = Complex::Cartesian(INFINITY, 119229408.0f); - assert_expression_prints_to(&c8, "undef", DecimalDisplay, Polar); - Complex c9 = Complex::Cartesian(0.0f, 0.0f); - assert_expression_prints_to(&c9, "0", DecimalDisplay, Polar); - Complex c10 = Complex::Cartesian(NAN, 0.0f); + assert_expression_prints_to(&c5, "64078208+119229408*I", DecimalDisplay, Cartesian); + Complex c6 = Complex::Cartesian(64078208.0, 119229408.0); + assert_expression_prints_to(&c6, "135357557.86997*X^(1.0776501182461*I)", DecimalDisplay, Polar); + Complex c7 = Complex::Cartesian(INFINITY, 119229408.0); + assert_expression_prints_to(&c7, "undef", DecimalDisplay, Polar); + Complex c8 = Complex::Cartesian(0.0f, 0.0f); + assert_expression_prints_to(&c8, "0", DecimalDisplay, Polar); + Complex c9 = Complex::Cartesian(NAN, 0.0f); + assert_expression_prints_to(&c9, "undef", DecimalDisplay, Polar); + Complex c10 = Complex::Cartesian(0.0f, NAN); assert_expression_prints_to(&c10, "undef", DecimalDisplay, Polar); - Complex c11 = Complex::Cartesian(0.0f, NAN); + Complex c11 = Complex::Cartesian(NAN, NAN); assert_expression_prints_to(&c11, "undef", DecimalDisplay, Polar); - Complex c12 = Complex::Cartesian(NAN, NAN); - assert_expression_prints_to(&c12, "undef", DecimalDisplay, Polar); } From da2829b969dff3595cf8b6f3355b62afa693786d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 10:48:38 +0100 Subject: [PATCH 63/77] [poincare] Clean Integer constructor Change-Id: I1ced5587246507d3cd813b0c23af64f3459c6d0c --- poincare/src/integer.cpp | 20 ++++++++++++-------- poincare/test/integer.cpp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index c1ee0f379..28dce696b 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -34,17 +34,22 @@ static inline int8_t sign(bool negative) { // Constructors +static_assert(sizeof(Integer::double_native_int_t) == 2*sizeof(Integer::native_int_t), "double_native_int_t type has not the right size compared to native_int_t"); +static_assert(sizeof(Integer::native_int_t) == sizeof(Integer::native_uint_t), "native_int_t type has not the right size compared to native_uint_t"); + Integer::Integer(double_native_int_t i) { double_native_uint_t j = i < 0 ? -i : i; - if (j <= 0xFFFFFFFF) { - m_digit = j; - m_numberOfDigits = 1; + native_uint_t * digits = (native_uint_t *)&j; + native_uint_t leastSignificantDigit = *digits; + native_uint_t mostSignificantDigit = *(digits+1); + m_numberOfDigits = (mostSignificantDigit == 0) ? 1 : 2; + if (m_numberOfDigits == 1) { + m_digit = leastSignificantDigit; } else { native_uint_t * digits = new native_uint_t [2]; - digits[0] = j & 0xFFFFFFFF; - digits[1] = (j >> 32) & 0xFFFFFFFF; + digits[0] = leastSignificantDigit; + digits[1] = mostSignificantDigit; m_digits = digits; - m_numberOfDigits = 2; } m_negative = i < 0; } @@ -597,8 +602,7 @@ int Integer::writeTextInBuffer(char * buffer, int bufferSize) const { buffer[size] = 0; // Flip the string - int startChar = isNegative() ? 1 : 0; - for (int i=startChar, j=size-1 ; i < j ; i++, j--) { + for (int i=m_negative, j=size-1 ; i < j ; i++, j--) { char c = buffer[i]; buffer[i] = buffer[j]; buffer[j] = c; diff --git a/poincare/test/integer.cpp b/poincare/test/integer.cpp index ee69159b4..6865d7c73 100644 --- a/poincare/test/integer.cpp +++ b/poincare/test/integer.cpp @@ -9,7 +9,7 @@ QUIZ_CASE(poincare_integer) { assert(Integer("123").isEqualTo(Integer(123))); assert(!Integer("-123").isEqualTo(Integer(123))); assert(Integer("-123").isEqualTo(Integer(-123))); - //assert(Integer("0123") == Integer(123)); + assert(Integer((int64_t)1234567891011121314).isEqualTo(Integer((int64_t)1234567891011121314))); //FIXME: assert(Integer("0x2BABE") == Integer(178878)); //FIXME: assert(Integer("0b1011") == Integer(11)); } From 3f0328f5b508797ac1d8ce60024f6eedd965114f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 13:55:11 +0100 Subject: [PATCH 64/77] Add assertions for dangerous bit shift that could trigger undefined behaviour Change-Id: I051ed229d407eafcae1ea4b5fc745176a751e036 --- ion/include/ion/keyboard.h | 5 +++++ ion/src/device/regs/register.h | 7 +++++++ liba/src/aeabi-rt/llsl.c | 7 +++++++ liba/src/aeabi-rt/llsr.c | 7 +++++++ poincare/src/integer.cpp | 7 +++++-- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ion/include/ion/keyboard.h b/ion/include/ion/keyboard.h index 47339cb06..9e09314e9 100644 --- a/ion/include/ion/keyboard.h +++ b/ion/include/ion/keyboard.h @@ -3,6 +3,7 @@ extern "C" { #include +#include } namespace Ion { @@ -37,10 +38,14 @@ public: constexpr State(uint64_t s = 0) : m_bitField(s) {} + /* "Shift behavior is undefined if the right operand is negative, or greater + * than or equal to the length in bits of the promoted left operand" according + * to C++ spec but k is always in [0:52]. */ explicit constexpr State(Key k) : m_bitField((uint64_t)1 << (uint8_t)k) {} inline bool keyDown(Key k) const { + assert((uint8_t)k < 64); return (m_bitField>>(uint8_t)k) & 1; } operator uint64_t() const { return m_bitField; } diff --git a/ion/src/device/regs/register.h b/ion/src/device/regs/register.h index da4acb44d..15637284e 100644 --- a/ion/src/device/regs/register.h +++ b/ion/src/device/regs/register.h @@ -2,6 +2,7 @@ #define REGS_REGISTER_H #include +#include template class Register { @@ -21,13 +22,19 @@ public: m_value = bit_range_set_value(high, low, m_value, value); } T getBitRange(uint8_t high, uint8_t low) volatile { + /* "Shift behavior is undefined if the right operand is negative, or greater + * than or equal to the length in bits of the promoted left operand" according + * to C++ spec. */ + assert(low < 8*sizeof(T)); return (m_value & bit_range_mask(high,low)) >> low; } protected: static constexpr T bit_range_mask(uint8_t high, uint8_t low) { + // Same comment as for getBitRange: we should assert (high-low+1) < 8*sizeof(T) return ((((T)1)<<(high-low+1))-1)< typedef unsigned int uint32_t; long long __aeabi_llsl(long long value, int shift) { uint32_t low = (uint32_t)value << shift; + /* "Shift behavior is undefined if the right operand is negative, or greater + * than or equal to the length in bits of the promoted left operand" according + * to C++ spec. However, arm compiler fill the vacated bits with 0 */ + assert(shift < 32 || low == 0); uint32_t high = ((uint32_t)(value >> 32) << shift); + // Same comment + assert(shift < 32 || high == 0); if (shift < 32) { high |= ((uint32_t)value >> (32 - shift)); } else { diff --git a/liba/src/aeabi-rt/llsr.c b/liba/src/aeabi-rt/llsr.c index 38c200cce..0dd3e2cee 100644 --- a/liba/src/aeabi-rt/llsr.c +++ b/liba/src/aeabi-rt/llsr.c @@ -1,14 +1,21 @@ /* See the "Run-time ABI for the ARM Architecture", Section 4.2 */ +#include typedef unsigned int uint32_t; long long __aeabi_llsr(long long value, int shift) { uint32_t low = ((uint32_t)value >> shift); + /* "Shift behavior is undefined if the right operand is negative, or greater + * than or equal to the length in bits of the promoted left operand" according + * to C++ spec. However, arm compiler fill the vacated bits with 0 */ + assert(shift < 32 || low == 0); if (shift < 32) { low |= ((uint32_t)(value >> 32) << (32 - shift)); } else { low |= ((uint32_t)(value >> 32) >> (shift - 32)); } uint32_t high = (uint32_t)(value >> 32) >> shift; + // Same comment + assert(shift < 32 || high == 0); return ((long long)high << 32) | low; } diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index 28dce696b..72622403e 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -515,13 +515,15 @@ T Integer::approximate() const { /* Shift the most significant int to the left of the mantissa. The most * significant 1 will be ignore at the end when inserting the mantissa in * the resulting uint64_t (as required by IEEE754). */ + assert(totalNumberOfBits-numberOfBitsInLastDigit > 0 && totalNumberOfBits-numberOfBitsInLastDigit < 64); // Shift operator behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBitsInLastDigit)); int digitIndex = 2; int numberOfBits = numberOfBitsInLastDigit; /* Complete the mantissa by inserting, from left to right, every digit of the * Integer from the most significant one to the last from. We break when - * the mantissa is complete to avoid undefined right shifting (when the shift - * width is wider than the length of the digit in bits). */ + * the mantissa is complete to avoid undefined right shifting (Shift operator + * behavior is undefined if the right operand is negative, or greater than or + * equal to the length in bits of the promoted left operand). */ while (m_numberOfDigits >= digitIndex) { lastDigit = digit(m_numberOfDigits-digitIndex); numberOfBits += 32; @@ -529,6 +531,7 @@ T Integer::approximate() const { break; } if (totalNumberOfBits > numberOfBits) { + assert(totalNumberOfBits-numberOfBits > 0 && totalNumberOfBits-numberOfBits < 64); mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBits)); } else { mantissa |= ((uint64_t)lastDigit >> (numberOfBits-totalNumberOfBits)); From 8cfeff5fbede26806a16cbe83459d0fe548ebd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 13:56:35 +0100 Subject: [PATCH 65/77] [kandinsky] Change Large police Change-Id: Ic3063610eaf2ed73aa7e0e6f7edcb5ad3946ac22 --- kandinsky/fonts/LargeSourcePixel.ttf | Bin 206460 -> 219468 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/kandinsky/fonts/LargeSourcePixel.ttf b/kandinsky/fonts/LargeSourcePixel.ttf index 28a552c9dc7b217b34d289e0f236810dd32a6261..9def301b4e5a05efc3c4589701a79058dc70cc81 100644 GIT binary patch literal 219468 zcmce<34C1Dc_=>T&PXGTMzim8XWutzG^>`;Vp*2uO?boDMmWY8;}FLXLx@TUC6o|K z38mB_l#o11N#jtL5UM7`B^ymCI^7`+c_}6FQ`wKEz=8>yzy3q?i zhu?QR27fD#Tz}06|NHhYJS3Cb;P*-Lnk#R(UgnSmVEtF&|Ld+f_8Uk1e{k)`;s3ER zW@+l^)mI)?of!RzjQRc?{QtsH_<{U`U2nnqi|~E$=m&3Hd~Nqb=ivX}CX-p-K6c$z zSN8mO;IxeS^IK#x+u09Zxp;l|3*G+--!H)LdyilF!K?q{i@)&=fajD<#$12>bvNAj zy+Z6MncQWQ$wpte{>0VS|MlN4{F6*>e;$7S$FeTjZumU~b5bUk^~%&Tt;{TwW%WKi zv0AgmqFfGN%C(Fy64_0j!YhiUpR)W7+SX&7z%d!Du))3(3;$Z-2LXQs{%1aiKgedRjNA^Z`QrKELLe9j?2(H z)g3#IUAJ9458}QLaA@}N|LT3_MRJsTkUYkD$$4h)a%s85oP=8v&X2#}EBwAbApUlV z|5ff~{#W65$Ax=MH^o)f0=UTs@>w&Ws}W(0qht>R)7ebc`n4G+HeU`bwS@LKlC?i^}XR)-DcQ}>GXk^QlYp&k9IULc|zz^po`Yb{Z zf3kXVzEt8KES1Pnk}Mx1NjwGjGMoYUi_#|IC+{=E@0xx2tbUO^wzR}8EHQgarShw# z62rppB99<$;6{iq@6#hGA{i6~(#VtZh}7BHTgLJ^Hg1o`l2;UmVF@mCXejGqv+0!G zF_79P;(36-mm=<}Cs`#4$QJ>`4rTq_3KHbV4C6ASX9XJ#glFz}N|6z><2!u zVOBm;{H@zZxTlRKHye&7lhKgdWi&-1lamqhpI`Xe=1rS6J^h7UJ9o096Nf)KR?=uR z{o~`maqaM!-3G!7_ZQEp0@)xlBD9n@0&+9Ypu(PurRAF@O3O=28=?WD-e~p*Hu4uA zj~`qj$>koU(~&FKfr#)-7q`&6b>r!}x&sls*jgT^vPWsjX2ZPjX^;&JD>p6`a*5~W@$QhxeDXl$h6+GL0FdF*Qt9mv zte=j?u6SZPGqhuA=|D2hpFNS-yAVsLfc`@c`?AZmS#tlAplVQ_@%S5L1ObQGt%yPt zgdPS6o)^Uu4gm2;J`(Wibn1SKi8bC@^mvQImkwu!LP2*Zl-tU>Si2{5Fzav|j811} zX#KXyb;WpoU|>vc8Z((#*6uf(G%9s(!5R!qZ-|5)&QLsHG*y_=`CJyh1e}{1zxbBmVva`+;xo-hFpzdYZE3DG?J5>)p8`a-^V zG(VCa86NcqO0nWtF`Y>V{i7rMuJ}0XOvGZY5xANyJMtNiFOe80kLmQjwBP2iIpWC- z_j1Xn(`)yMBV3Qoq8SjfYA^-ChzLD+lN^lpiz_?FUoAP6k0b~eHB!YVFD?e zWddKD1*uN?p;Yn@3}q)lpZT3^dT4xnWW-B`Qn6GfowG%91Gz*bYZGQ%2)G6 zx7*__6lb=M4m+l;;Yeo5W{JnL>3GyM=N=u~!noXp!p6B`(d7yRCV+F6^0BDZYBKsJ z`~f04zeN@U!mt7YDi_2XGN8zW8=)MM=Os7!makNTrAvn`gX*kW2IQkXxx1!Wwn@)6)3$70KIrocT_kL zw^hP!5{t@W2E`Io`VfZ2LjLlRM07Nso1Pxt3U}f62gb&>9RX!YxR1NZmHqMfy5&8| zq+S;(BrH~jcmYv!n~K98&&cT2#$IM=an$QZ`sVfhi&TdTvcigo&t}a?k7hH&E|(S; zi~0RWj!cF_z|`CVd91`t>>U^=x!r{sdWNl(<^!lJ2ucv3oxC7Y8c054%`-^vJTe9R zW*Ih@-?{TMzCB}8*_<{UOAXHqk9d9l;PCM5b(0^<<}9H^s+f+(9QN$ccHqB-59RVI zbxGOda29gDevQ{NGCVt19Cdq21#ZD&jhsmtO%_WmI^i7ADE&13*@Y*E~srfLmH#2KftyR0(j%|NccC*pcXGfFvdXb5Qh-7c zhk5c!2@dr#IqDA+1=>*y z`_H~T|HC6km>0{hlH?igmA8f8N1W(EzJc&Zv}ECfpy~7aKxw&OW3f2zy>l+6H@~E?4qtybQ$x*jj?R1s^6X)kWHbfY2`@sHS`M~~? zeTDlpBShq(b?Zu?&3smy-JU6x65;3=c`cbd1S=({Shl;zY|3-blk*2hEU;=tHo_sT z$d^H+=z4yH-4qmEqVMGgUU7T##o4)vXqqkNy>4#7W=X^{JBNmbGCP*OR4BOJL?WAj zi3)R6GNseIFB=$K;`hI(1D-lBGs>cHFd$4bx>dZ%1=d(N0+va|mh4sv!Wpi4_%jE` zMm^qaZbW~>Ix^&B9ga|ZU?5cpJ97DCGO^6OSSn47|Hj5#(d8z6L-AO2d@L3*7`2A% znYhgc#yma0u)hg!7A;`p>5Baaph%b)kt2G93|sOi6O%Lfg4;7OarAeGK9U}^`3nOX z*PuCMHrwrqWR9FKr%Bk;r*^xG`Hjb>CesCOkIfc8ldu}p8hb3B$ws5}A_V!w>jl)3 z6bXO^bD-w<2mx|0-1d(>dcQlG4MxmnkcK@fyS)&1xO7%Sm$_8x){@gEbziTlPovkI zaChnL8m&^CKY4B%uA(G+3^X(?&(q_Vlum2BMYO2VsYmK`_<{u!q!Q*=hH?*uUV(l zy4V4y!>-k8bklK-*5l9{dV1AHpQkkG@l8)h^tyhvPo#N}8E{p`EclaPlpyC)k{|du2dqN z865DIO5-0LFBE;g(o3m;&u9QCOeQ$*{BEZ+oh}g*Q{~0;vL5iq3Sd%EEAX}%mIgJJ zE`D|v-D~P(iF|t=?LaUAi&-mLfe~c@gyinI-KEm}^uZ%&W-a{ihv=67@Q32Lffhh> zf&qcTrkGVy^8h|rjAXxH9rc?A#zyAPo}Ks4dL57A7vvkBO(|1xe&h0T^zx+Z?6MHo z5a`l$@17wnVVIP_l5G_ws=uhg~k0cNe<-l%A+w0N7jDk@s*nqH28*#Pm`4 zeLmj9%1^;pee}FmC0e>PQe;#DwP%4I&z&lrvdu%pXny@S_ZU^4-iQ1B9{H(nr=PoU zJ6he#L^tBO!V~-OPNZpZ&3SQ6(ris~tL0YBtqqIL+pgD@;|+_7YtG5KAu@#mISY$6 z0hRRZJ{Ui8Fl}g9dG1Jj|G`4p)^hDBnGfu<3|Q0D@e)ZDO(dRBc|C)Mb_0gK9&(xt zcB8J}VAX27Y>e3gKhQgL293e0)5&d&#nAxJRG->v?CG_bLrlu;QTN()>K=D?d_3#ePOvCF7aNv#k)3w5z??TSR?UeG9D2R}BlY0O4_j@akkw|hh9Z`aqDck6rDt9G zvbzp25w24*17IVLTEb8)S}Orr{;J!8_J;8W!xp#M!~@qiup)3<)!S_;h1KfgA+(y_ zzY>I1exHLlvle_jnGr(QR%M`3jy!!raYAz=d!x4jA#;Zh>rR}w@O<6z!RF6g^n#sv zQpRF5S^;z&i1?H!pafF@=tH0mkf$K6BH?g^Jh7MA$^G{(?;GqJNi(pe%j+=cj5=q` z@j3n2;Qv3M>K{=4Sz>PLjW=+^fXfqc>vZrdeZ5xs*!Y+3xWg5>3qn2gY!|0#d`c

>?X3`_tx`=J1WXaxY<77HoaEU*9TU4jkqj_)hd%w-3L~J5e(@n zE*AikR@_g998P_EAiZYPPmWt{u^z8pzlRy}eYXLz#em;n?6;^i8q=WM!RRl1trh~< zL8P%0l!uLgX&FlF2uj#MGZFhk+hM~3dr)&gaX^!6*zttrfWfv?H#5^rjyKuM2-{fs ztr}(4$<2oC-{f~Xc(QS;Bbr0QbgP{ge%SI{w^3Q*qn?8ln>2o`YPr4YJxk z)}&VJjD5YJV6@tx)u?gUmD+w&uL^(gBfOu3>_Kk10dWs1y9^pdx2E6Dc+J+HZctHO z{rwKcXMP6mZRgGg4Ggd&AIL=>2Va3|U;xh8jw6Vhy-|alA6SVsYh zPQW;;*U{v7vW1#T>0Q^J)-T&Fj;EH#L^q-ZD$!0pC ze~Vxq3x0#O8?YN}Tim|N3F7+18f33(iphw{!W-I7X)GIPyKA;Rhj2S&y{6kQi zDIpg&{q?JZWPq5?KmPdZ+?xeUpKkQvag&9*>Ov-t*c)CvsPp@0>? zY*2<7Ur3#Te=5d6FyMjdUuyXQt};c4mze0|VCMx1BzH+qsdaYzp#RG`c+>4r>*vSaib`8S;sQ(hDbE< z@#4h=YG+{9Jue%9pDBUl_|3u8iQv}4w_yYT&0-A97{DNvd`rMhCx7KrCwwl5Ht5p> zd(S>HGcaJa>V9j({`vKxfVwZX#lyy)II=Gi(dhfR*OtjmCWFQ7@ux@P5vxVcoV3{^ zu~aDd7t3IidVpiN7fgob-d>w0Z?|*TmoT1)etnd|d_8=p0Q_L~0Z|kw)&?-D39L%w zhC)t-q!R2{^4Q(o>uh#|G3MSrH5Gzo)1sk7G%c}^%f>prw1FB?iWdE>J_$surdWUkz0*hx^nOSEhcK@{QoYs0C*RaF2T~RQ80}U-Lbww5qSB;V{fl zdx-KbkUDU`Aan2>E{s*zN*xf&RhbQ9g~uP2N#syP!YFLCaHmxIh}myQ3Dr9_vgA?- z4Zz;Nec5IDS*4zNs#4K|^ksp+aDNTnl(DiOY#qqU5CBxBAl^U_9zKZ$LYqE)a-LlM z$?H#_zW$Tb>(-IuBuo#spX{^|4b`c;cHMo~E?65*#3Y$Vz<-9Q2VzU`Z-Hth!5tBwI5s-+{?BZho7sDp_lDx|IP>wIY<3G|ZC#GS9aoKxUAFgg4=*r5 zru@*Qdp>pVC9oIG#nG6b7AOwvR=K1?i6|i2XTq>9lqx|Aj>h@8SE{v2;^$HC;yME7)U2~x;FpIb z>j3Ld&K|BlEIe=3m zXmRN-uPk$~SC?a6XW9G3Pa<9N_lfo=W*lq$5A;ZCF!I%!NSOl+{hw7=ma~#5nalOX z$|m9-C|kkOtCy`cJKR{2o&9}Zm8>llk6*8>3%^y+0HR4s|@@Q73jiWPk2P_6i@+zG~U&@vY7IKfP+!Iyu?=M`f(v_ekcy9GuM z!KG^S=5-0q#QVUQHPlqJk6250{O7j8YD@LJ^44MM7pee>JjBP4Fg_1bK`B@<(!I%3 zh6A=eG<0;3m2x*{IApmKqeeSltS$@Oz|(eRT_dau0*eMkr0c%9-M0CeX3HKj94XW; zE3V1Mh0yw@g@nlDq8Zy1^1r}15G(r0{f(Q+4`cL4C-sXzQM+~JJbVl&-^tB2JMGV! z2Y+lmmEh06HUY17)aP^^mLelAtUF+Nxkeny3!1~G$7@6)@ZUHHMwZ(75EE%(=dWBTOLCydws&4Fi)!vKJFJ)$ z;X~auT*Yn^d%q2gX9Y$1c;|5Px9|@gLW=r@$0h2Q>1O(6^-w?2poETh41b-Dx@Xnr zKpi&KsKZu&ierMJQ)&A99_bu@sUG}%htR_vfbEMl&}j;Lqc5{EoiL(^6BH2Bc@_BW zVO7Cmp(eDc(~9u2Pwwc|={$myZl$>mk&Z-~7womw<9(}O)Sc-V=Zs+UeW*ipS7;vX zF_0fh?pO3)U#TGN;t*YlDW+ zhl0nz`?%P_P}m0LImsjfo=k!VjtxWTek|LVyYIT| zF7~dwaIHsyDb9;NJm#LHT{Kv*V<5@JMegNA!GEu`)4*q!=w~JVyE0$xz#$eed(I0V z=6!eRXCJ4Z6@6~e5|@5nq}@BkH3YM-x(dR74_&8Y|HUfVp?$H9PCWYx&PxRxxqy$3 zd8h_kt$65viy*Z0{VM=a+Tia*;{Qu`fdU#4ndtjhKPld2E5BTVH8NlE%QdR|s?b$q zmS2~yTYJxfu9m_?#8~7_B4}b@rMe8r?dvak$owwDwz?S|!g48fQYS#!$!E(R|Jox5uN2QIDW~%8qb& z8iW#;d5*T!IqXK*17os$KOM$Ulo$c!Tk4dRj1Yez|Mxec=8$i(`+G?Em`uD~Nk060 z)qcBgVoJx_*C$$!bp7e(>(|8g;ld^BpQyyC`}%6vmzLWg>_W@O_X8r@@^A^Uv zNI%g{dS=QegiqidB7V{C@4_lvzWdNpZ_Lwf#MdQudWkOZ84-;%Q$~$&T#wh&lJzi> zF2yG72f!1Xgpm0Kzg44``Md%vN14@2I|4z8@dv73aeDdnh9xnIS>8zjtzDPD7yitE zSN_aaSeu7yahV4O%m_dt)|*lMKwm*N52Z3dev^z<%b0!2l1&{fg&jXv|I!gAk1;Q( zZ3c3hyTPcw#df>e_^0r3@lI;-TOZvN@Nr86K1hNhq|b;YT*6v=Y0LpMJ;ofYXwp@> zK~b?DI8u6I?upWoQ@MWB0PKLD9>4dukL1Q*OLnLC&YDYvQQsu zvOe5D*3+qJotG58dVxmX6xUQqPL9P7Qkc1qN{K?s6a7^j2%W87K)`gRyA|Ul6ahkq ztN2-r|G;O_rs)Fs_|N_fEE()Wnl$=K2{3D{DFG%Kaqks;qI;#_Ncf*}ShY*IyG{mxT`F`9 zpOMG`{<3%+eWzX;i1*Plv?kzgZ^d%x@4H{zr;*UAcOl4_dYJ2WsKR_t1A*~t z{aYPvfxJe!(2Mj@#RbhGR?g48OiN)z(%{}kIU(nJr>VVc#G6Ix>a>Fzt!eVH_FdGx zrTw9Ht<)kceBBJdf|A0}VK+Cm&VQ#pEL6O3U+)wa)){eS5ICc%sS zVmqj+_l0~oB-_*pCw6+9?Y#%`(e`IqfrGj4wLhOiphw>47?f>niFA$j0MBEfvn?Ic(b94UNJo?5S8V|iaeD~h5U84FjaI>B=S_PY z1Mp{Gdz3NHsqWX^1An;BwS}xo*J$_qXq8vR1EK;Qvb7!GbE8-ArS{;~MbEkA_CSmG zT9px%$gOBM^8IqHCQwv7#s6s!pQx4pr#)PZY#8#!UZ|N!Vl8Qb?}Djx?qNEwMgBM( zVQQ<_ki7G2yZDuJE_x=9+;d+L*t3VIhZL; z3|=yS$-(z;-WHF?WAmHef7_njQByDwlKQG%)-6vlrn%Vz2R3euz&{%|EnL27HXMc; zX4FrhGsW2+MfgdFd{b|ah$^kxi?4)&dAOpnQZpVCfk0fK=iI%Hovbn!r1B}uPcc(C(m1Ms=0Lm?Xh2^HST0*F0EpSLCC95Nra`$?#d81}RTIBThn^j=fLgHrVWmoXy5;2+}b~ zk($v+{5T||e$L_0`Ft=4p+B@~_PVcaCg)%vlA~@&5--C6NI&)ey)>&2gu}Ootrkk` zjs4xuAr)%qR|&8v%hBJa@S;z&CV0_7`kz&R%Xbd15?s`g6u+o@sK%!rt5~vA#F|fR zhg>bLrTuf8R&j{N&bn0|NIVbvI1{oxok$|tT2A2}UP@wLs2mdeUo){}=FYAzJ$ zg=!fXEBDE9s*?aNDlf6?1^XTZ3A8HccwyNwgo3bK1)x;-BtiCUWj+mk*qBQZYQ4=q zz5%103Gq|FWyi(^Zu!I z{r#cn^p{Gh#KPj@fuwLkOd2yM6UocQpp)C$!~Gq3baRenmjV9De(2NLWiZf3?AazO z?fFfif19|Q?<;n3x87MiC&nCoAjKhQUpuv6FPTSG!| zAmmo`U{L@pzLm}{2faewL{KzT#(cl&{?AEMTdgjj{EhlNmr!pBvCU3kX^?l#VYyl& z+FBx_{0*rX!#*_Z^Tikr(qS4X1L_i1q>6fI&Oo-z)D$^VfaKVe*CAB=UGcGB#1HVt zig#s%^c`uA69zNsE%>7RkC+mwzCp7Xo#IAQR zr%{aaBde6sp;2~jD?Z@-hfDhYX_$+ezK?8Lfk z%?AS-6zjTf1vzPzT?Rj?%bh6SozpHIBGDpxwlX%*9HVw8RH@Md6uv$qfam|xrA6O~x2xqOQ?03k&Qi=|Bc%$`GacfeW z<#^{;+At#*WR#E#eHZSvJ|nc|slPB0uVNak`Ch*%jSCam{)=xhLlBFH=?5^y5FIix zp&=;F2zVTHbQ0LKSnNdy=7^Jwn679nJ(NtjZC-yMH5Cj(>-yjx=AnV>b`NEIF4s_I zeAB>y>!A;)(-yzgYBoh|VS^zSn~DW}c1J(^73g#P5OEI z5_yZatx^AJLH`Mo(+Z_ZbY7-d$Mz;pE*7Nsc42@q%mozq5;Ekq9j9RG(;BS+HnATQ z<%w6l1QW~$>RZ(JGdrO)>(n$dJHR8XiP@>jwQ;qQYi%wF0Lc)RKROPb1o}>6R z`MRFkHS4qnT~k_kE3SE=CezNZ$J<*-P;i}$ znc|I;qG{5-d~1t&(c=1O%V8fL*mB^oHeH`)*11<3ci=6s=bG*yu8*|kf%Sz*ajP>_ zVy4eF*@icW%1cc*;&lYY6~>ETK1BQ4lvYr*&wwWz!CN8WU4Er0$f6Cy=eRMaRY#2t z@~qX(w9066|JDS~Rw?ZA8%?3C(jfXgNP|G*t!QKko~>xy64TP0q3P-(ePF&f1{i3_ z_O`mCCN-a2gPV&^ zwk97vdqw`n+sWs-Yd@YV7KcAZ9^ejLH!|Wh2K-S;?sD&Gt?<&=>cbK++k%XJ zrJq_W%&3PWG|wi`)SBTQII6jpy;gH{Vl}YupEoQl80L4a4*Uv@yjwWW9kv|UyBfF) z2TX^@R|hokuwY};8QPWE_I36{eg3k^s}oYosObw7^=4AVe!-kn1o(-M4OX(^t>lQb z`r-B_%u-+MP(IUArl9?FK%U{JhF~w$$j*Bjp~+6)?|h}==3ao zVS_tb-KWQFh8I&H^EvC~j@%8wd0ti?pz(TP?jE0UTX{ze&)?N*S0Pm0c5`uWq)XVl znyFlIZ!{Ty-g0X`3)yz-%DM55!MkambzBjSni-wbEdgrGD7FPo#0%Pl9nI70;KtgU zhi|kz!b-`(wub@S^1RH`GvJ!f2Cj%(Y2yN_uGSR(2Nj>Uudf{}|0NBISKz`}R5wlm z5?-EZP?f?0fLO3hX9-;g@i-EZSvp^q2Co~wU;y`NpYd+v_S@UG3a zNq3xkhB^6n8~-a2-HSNp_eP%xCp&;nWmepqdPXm_*_`*1{-(|5;@)U)fxHy`r8fJE zl_uA>gF$eizSaf`w1;^d@V*HcN9}x*HW0S*Q2we7#4THF%m3N-418R}OKC{xmw?2F z@X8R<3%rTMKQu%Mv&p*bBOMlbYqV=W8K+gM#1)&-jcXNA!oi0bQ4^~tj8YfuS^c&aSK z?W`3+N+H3RzO%uMO1-7_LTJ3 zA&r8Z>Z-hqMZ%C%gT`p-`&hK0Ncu(=A6pXXRItO9)ioxBj0ab|^zqTd9(V|aA z0@DG3K5!ri_cQ6DH4){HwT4lMA4%5yrjTdj-juE<6w$%EJQWxP8<_9JtDLc3uGPFK zM*KC3s(Gs_-h;I7rusS=MoMbdyPZY_DMV!jh6UlXpe^6CnR&Os+B)Dde5+JO#6p4q zgdd^t07mFh=Ne|TMsFM)EtRgn$Lsyi>u$U4T6i&vVc{*pD+bf2nUjkw_k1|KdpD>; zgm)U^*lvhpyHO&Ek?e3j?-M5fQ*IN)ghGf@`XB=QlVWkxY#v@7BhTauSEWJ`m%$TH zrB6&wy1AG3P7EYnPC0j^D?b;5p#bxnk1+S{*=;a*Eq;^f^25h-f$+9J~}=gPpnTk)i!Uz z_xPzVY@G4=l4Y24zdzx2U2@5jXLjyXd(~U$S?|4gl)M1=!(2aom(3!CB82cgtbVWe zg4gaC()agkdz|)YKbkY^i_9bCy&F7U1=H;NDS-qwIVRoW>X!b`v*Zt!=* zoG8;*-Q?k~y7y{zX0sNaqcNLxEZA1%yXar!Ql}Gt>U8R~5U&TH^yZ64g*qXOMnnBd zB`O4~H^O`uh=$LjcsN!MJW7b`;m-v)b>GaqOSNiDFp8iI$o8FkJ*JGrW1I|A5#( zL~F#dGcee45rz^2{+Nbw-T(zmdP2cOJ~iNWdxr}X6PY2_S&EE~j&BHs{od`H?=k&* zDg(CO?~IPZqRh|XJr(XRj0UaVl7o4;Hb*p`h8OnWX*brnZa5UQTDl!B?nhdkYY|>+ zG?^1V4M54m`<4hVwdRE4*Hytw21=(&?$yAnNPj!d{rTG9eG%;FBu1dX0)%3(EKV^- zB3~8AOn3T$`_hk=_OAx&T>8HI;*WA)UMt)o*%sM;uvJ7-5RwV_R3PP=$}C}Jpio>B z<$T%c^f)#8KCMz^^!95=N$zrwnl&1&L8&t9)O`v?e~Hl(`)W>o+FVT#)stPyH+W@g?Ef7Dyyh?Ca4Xnu5_+DO-&OB`Zl`*-8P4hM}o_Eqi}o^ zprZV6onm4eP*S6)B>ZtH5Dz4EWsUS_>W#MbIBM$lTUmmv#t;fz(}#VfdRAGVRKrn z@YXcw}K>DL7FCQS-Fg;|LhZGX`_p7=ju+%tY&vWNWZhYc|6F# zFkzF;-ILRIt>th(18+;G^Rs$GpGD`j^?hnh2Nln$f|}~6Y+iN+AB967hiVm3enB-? zMa1#pzBStH|Ku{7V)h=rO4p;5!wmd>w^?sCJG;Q#?dgG6tNQ45rK+EF_4U~pm-*2(VFi6oDl6f=V%|>yfBBtwD)mBhEuM>1b~0=7VroTbg~lx) zbIjn2ZRTZ;0l!5JH z`;XNrY@n5@tmA!6=tOO-Q(KdbUN83Eq4!xM0pE!yS}O?UG6Te0_fp$_3^hGER29WX zqdIgtWudA9`7odm4up)A&tx?~^vV|wz~AsM?FGad)^MJDV$*0&;)xVgeFbfL zb)UnYzRKqv+muS__r;HaImiU z@FuupdPWB0gu_skv>jwU=yAxny^AM(2qK8T|L&ZYc`N>0y8PpJ<;)nT63&dhZ13us zpI=`7#=CT4sHcAV9i5kvd4Y}%%uA3rfcXew?gC!=gJ?Al8HU4~mDwuQLM{@BRWI6l zSo%r+A)70OQUgIo;h3Ly!~3{QpfHraDxY)j-3t}^1Mz`)xR6ZPZSf(G+hBARm^qu` z_;L2&wPA;CV*I)-!(@leHgHiEHX5;PfAR__Ri^c)G>7ye%xg@6e?zkk)o?nU)kDLl zX3;6dclW3XO2Go?OC|rnPH=@=IL;vh!+WmY9M zbt^nv<&f$_a16FLK)jP?tMgdH3AihJ;v04x8!fG$xjbw)M)bi{AUL{fVtptQi?5sB zTrA$)ukom1=h#(~PIou?nx`LD<}~<>z<>2|O?*Fq z_P>Z#?#<|m+$o8W+};+h-={$le*Wm|D*^}b$Yrx29}dcspbgykY7m`3%UH9KJoK_e z@VTXGLJRU-RXwIhz?uCpr1qO}hH*O-OgJ$xbmhpX*PG3a#}cnP97999_wK#q(n5j> z!US9HC!xbHlm|9PEw<^Y#Z6;lZjXL8R~$V&K7N3hwz>fpzGm}9$PdTp95gGW^6Han z77+$23i7-UT3^RWz6I~Ty9?I$6#P(}gG>gS8jI zB=4}#J2W(y@drY|{YU0XenGS#wVZwMw z1g8QJtom9NSpL!}6#hM(LH6Rqn5X5QT?K5Y1EOeKh_fs@$JzI=0i&CcLE3Hed(!A_tP?-gxip8i(_6_~%< zI|ZpDfJAsO@0p>mxKbMs^#C;wAq3kw&WIXb<#5-8C~oQu+TEduBoM)Uq!UzvZMGe9 zLQ2nat=Fn>0H-UZc?wuo`rF zgA+2EyDTQJc4KkG=lA)Fxph~LjxgLoH(_=Tq!V5+DnFqZnsGP^js-#YTjI6w|jXT0y{rCliT%Gn2Q~OxK~=C*&e{!$tqR+V$O=k zFM1D>>djT)_jWN$#SHmg?+AG2>(cxb#ND`1`y*6z(Tk8XoW;omoz=Z`ev;0LT_T+w zam6Z^_jPdGimc}IS4qe%(l|*(sB!N6T<}`YVZ%Xe+k*64hcc2g9oKoGX=~oVJ<bg{K5dxJ5EZB&>m4w) zrd&Oa-{A9SM$Dyses#D$U(KBLVJ2;6)p(~d<|xG5g*VuwvWHhz(wrGTL6kP1cP66R zhLiudkl5wE-O-h`jvp^!qIaPql0*JLnM-|tv;(KsUU$QF8P%4C(&aK2cVJz?t z)Z<_c8@0*M%2uw*`XP5(ZZ}MIY68gz*u9qBEO%cA>;iZ`Nc}sE*RF%vkkt^a{%-n4Mm|757RLyE!Ju8a0EPO;eSA6T5R-Yc>6>kO8V0XjF{_Ik|6jI z+@CAD7}C|P({?MZ)<`^Mi!U9t^mJ$QTMz2Gx|lw-&TBuh+w0Gde)w3SFs)a{)oP19 zFyuDrRJ|V0)VL$0grY-lXmlzOg1*ik2b;OisH2gl^80iqkI${^RY3``Avq8ZnoJB+ zvKWksnLq#vDKsWapPDef(B}DYG#;Iqjw_X9-&jDeS9EnL40`{l#bV|*YBhGh&d}4- zqcj+N9*arcr`1uro5vygEHkowvJ-$q^jV5oh~EAB(0G9lc|kbuJwYTNC@b%KPf=N+ zN3_c?ff|pG0e-9TE0Eq8tNpc7=ND`2dVGJ6`^9^N;@VVG`N{VX)gs@c4ZclwNcK^{ zGi7Y-?1ia0z|+50;ynO1yg>y|+k-v-(tCtnX%Mq~*L#RuiA+Enyoz6tib`IfTDx&{ z^si4iKuM`M;m7)??-72#V3oY6=IBzFZ~ArNSSi~8wemNB+!cDSeyzA27Q*{~_8#F? zND7p{_a35Clm{4}5$pi0wf}WW+HWp%@0ISt*Af5jJ;WZy-b5Q9B)bY~`hKrd5BAqd zsb6T`@S!r+G<=xeBQmacn!60u){ydp@8Rt*7vH9}I(L8t?uV$YSnEpdRvZ!seqlHg zp*d=7BP$etDfB{qcJM&4STdPiu9PP353{9G|A5?}I}9@?xrNlg`?yy~IOB5k_9%$K z3|*p-5PnVD58@xVNKNQ-k>ag;*F6g-`Jw&2ru63_yP&+Lzu@=N-T7_nJ%Ax>mY>g-xqcP!+Ck zkoyV`3hl`v-xcyWAWp=tH~!QwLmQhHR-)Z$ivsktU!VMdeg<%apWuJ{wdD-|yW($( z`2Q)bWq{R1n@P9>u~=aaJ|`Ada{RMq}KUAvz)`>3wR}MCYvUaR{+|_0B$Yz5!ZKO2=EUKRa+|-;SBX16IHqb zJj0vIkJ0m(;m#XQ#IIO?&YHu-O3Oqe5q34l_zuNe`Eybs2fOg+fa)Reajo!u17i!+ z{kILQ+z;oebrSzTxPO=*inxIsMHl>+=Z?n!w(}MGmaZ)HjOcB}baQGZKhkCzAvPxq> z&VT+YgWY)h z4aaFRj28Ew?qL5qIn{K37#32Yk*53CO=P381MY5`-W^n@p~P*4+GB@+p~a=1$JJl$sVCFJ+rON(jS+o`ds%VFcCF&Yn(GA-2(${l?#!{av{ZMO`nGK~Do+kn_~;1iumKLcleQ<>1{0p^5_O z0cWPr*%q&kt`CGwrjk}~a|K+i)utKLmP$h*-b(G)gyWmImq{|~boBJ}smy&@9ejI! ze^Jl#0lz2KiJ`9wwlSy!q{UWyVrkfPt1YP^z0#JHw&09d(-ej?3h}dc=M;OQx3=0? zv>e;-{TA?ih|U!3$lh*)jn2~iyIO%=way#ISA3<-SuRG&d7yK*47^H#&RgAC{Rs9q zTflRzL@Sc>E9_tX$Hr0Zyxf4hMSC4!s@Fp8?)a#Lk3P@>z*=*61po!w279|>9My^X zRycZ40_Ms%`f?+GUtAF20bXl!XBcy61w#)Q+AAEsd@nzv>f%cv%+uhbX(1CVGti10 zP@tew&6o#8ky24A)=K|`K0alOI4l}9DJ_ECPQK&y&ZeP70?MNmsbn<2D;5O}lD3#( zW<+dpajw7}BI)^9#OBFz50i6y2AuX};(gp%kSM6t8dfONEGNa40Z?^36MB4 zlsQ-vhh-B1zxjtCX2^sO{(|$%!9AA^4lDsdi11+UnZkn-*1#v2+rmQ};QQO!!$Ug_ z{&`jKU>=upvEU_j4APr?2KK)8PzhZJKUyVRA*jIK29(p0q*Qc>$AUDQQ|$tCSi zRWCiI5#FD5bX4$EcpAk#1io}I$XnJ~U=h@T)K6_gsgFW@WUf81G<@>qPM{L$6k+1| zW);vZEf+e1r6Wkh8ef)qU4kFr8tW=JXBpm`55gZ!d$>kbf2?~1{&0W08a$ybL2pj9 zCOW-2G9YBqo@@`AU|{@N2Y_KO0ADW~mra8wBaw1d$eI+-`dhx<{#zcm$D7MfOy+YQ zPa?SsLY-nbOXq}1Dsyu=H_ik~CC0`QNvGZJNDT~+CNd+#TecTR+-|pPw7C7s;z+<4 z2nQuWu61J`a!0-}u`ZKkp|vD8G`)T(>tb0dE5VM&-ZAvG@m+-uxuM>ICa<)`t#mp$ z`BHlXbDyeJtRlZ)oQ}d;Kdrn|fr``1#ro6pZNYAc^l_c3p^D7m>l*{252zILL>*$9 z+$R}>@<@AdCBsk3?U4*OF3Ls;oHa8~I)w9i$(WN)z^o28=?qK}PN4>q`hArc5SAf> zMX_q!KG0;0Ne95U7+%uZK?HmY^_NrF%ZxUnXc$%Eb7(hFyDd@x_lYmS?3Bd+rqeMc z?GM|!@KP8~(#fge%qVBTC*y6P37Hrx?({?r0;glENGf;4!6O}5NcG|eG$U_AV0@kL z`Kw-4k+*8Aa(`v3mwc`v27h^b&aMs7zt{+OBEMlxYlPdb}Jo@IJ^!Kbb}t$K=s|`tp!q88m@Q}b$$fBZ_Fs- zn1tH9?FiC4!K=8?F|>4g0H=5dFk_sD!ih(NST=5YF zNngntaeiFX@z7&(Pv_^T?=vZX@Ex29V^CXUmv`!0t?i~h*_o$xRp&TZ$>S=2ac%gZ z@}K8-xZ|~Pd_(Dd=MvyUl{`#e->RiS`D{%_5AEjJi?2hk+?z1XIVwmn+TaJtjB2pM zVg*r)5)AJZJpmF9@MD%ywn3tqz-w$%v1r6IVi_BwrhhaZ@eEr=M?KudM9Sev6MOEG zSd3cxgWPMZchKe5Xq@W|Gc(sxlV78AOc~a%KQ><^SF=vA@%K%T^*L^uTKRj{ao@1f zx*-J*FO50lyX7#{LXc)_ff}XR+pC3~ABpm-wZSi!se~Cf*eyN_^n)=ZQUB5ut;GET z-GRYvK)es;Qu9`nkXYi?N`cx{*>Hq>cAeW~vuJD%)^}4Req=BlGQ&TiQ2J=p6bk33 z0s)5m1rxg3%|<8vfq;K9%7SC1do>3y5p?|rdS)j5fo$%ItI`ldg}gfC7%@hL@3DYR#<;0c5~;(u z|12Q-!I@2w?wrXxH8Gkll|qwzyx&&P%DtW~+-v0gV%`ty-Gn()@Z$^^-^Y5Ny0sWF zKQ8DivZrp{y){AM+V#t?HH`!FXF$Ik26T#Exv6n)vR`n(>$YUXaDMG>0KbSIj4|VR z>Vme-aCMs`VC$*hNLmH*ox0tGeD6xQtJ}T-cX7$yhk}J6%({FT{PU#$Kl;c5Icg+Oe{d;@Q{PYJ9Ru3i$`~7Mu*)7HWtgB zP^p}@@oj@igCU#W`@nv6qBy*Bormwe`IN_|*Q(WOb0jjB@xY6~_JUQ_JLT-}?^CID zW)n<29ti}dr$c&!2I=W2g%$k|sG1ZbuxkPL=X^Bwq18YvG`W0ol`!-8MgAJA@|R&P zK*t3JJhK|81@0{WYE@tZHBh+aI^bO^SU(Nis}!<3r%$r&oihu;4Jle;~i7nXIF7YHA&3!S66XID6ftQaoH@M zV@)(xue4TkCr2a-Z8dlDz1om1)UO=}yKMmK0HF6?athH{6Z3#z9b@eSm~^7$hUG9c zc+Tq#UTajRXkZSJ(dcxxAnSKo^g6A|@6c-8-rP(y>b6V9a$>>V7m zIgAFA#qUo>q9Kpb>v0FiJoCvQ)IXTiTD`_<3HTg_DC@Ag2i>lLf&JyT7{_=b?y!YI zqajDo;ep}tHj_!!Wi{#aER4SN`g8`3#%6P<)E+n|+Cn8bCstmfb&2P}v2}dY8*R`c zKX7ji1larUPbMx4c)WhK&(1s>Pi`)sBhT6tQ&(h$paRIISHKDkc6-8`F<4^<_7APG zBbcp%lx`&kVg(FQ^Btb|36Q6-((cTyrR8O1x5b*A+@OxRqrS;NP@}n(A8PfBrKQsU zBu%z{jjf>T?lBp|(GhH6kevW|<_CMkMW@fKxD9mfG)S%!%{L{w8X-l_H`~7aD}|z~ zc1#??6BXb|VeQ#U$F4+x13#EP36f6;6ola?P00oxK^-?N|>uTEae~7 z@e$%=L4X6Q=2pgu5fIj5?}xycYkCb&^F0i5f$nhLuo7#RaV@+6<%+j(Gvr2p(VPmH zSMaryUpCtm=$zmS8uVOYkPjdgU~m?^D*&Yn!lgH%ccRv49zNM~EADspY%Q@zfTX8v ziib+D-$U%AgxHbJA$|*WD;OJt*;yDF!xR|s!%vcOiXnTw25I4w23P(e>dzwOknwP&otYG zmKXH2+C|*w4H{4P!am9f_Jy}T4tayOTF*AyOsE=QT5Koc4P#kQC)in)Kx= zuhaKPo(Jl0255a)N~zO#RbByF*8+UF3I5(O&#uIpQ<0`y-^{~6&)gf$K%nV;?&el| ziMYbOkj8+DTknGPx-Ir-y@Po7=i%=4SZ&i-=ihoq@|lLb19!0*2(_sOZeOeOin!(9 z!@*Ihbuv)Qe+%_K%GPv2Zg27A&8^JLBBv!^2!TH9*Lk6 zuS7H-&_pRl$n&YxU=ez(+=0O6LSiuIuDs$xYY!ZzVa36m(*?~|B+k9ECl+H}V`M+) z7@n0>8iWIE2wu|TbG1+<+YMMZ!c_Nho)=(iLBa}VQQh~gZ{qZuPe8k3f3Hf_+pp>U zbII4-zcjnwYy^6R~lU+?YZT1tv4+?X#D;HI&;Iyh-o3QaG;ylV|NF&XyQZj0S+5$sA@&9SaZZ)?Xpi7x04vIlhwxmBIv+;-i;B}!6)egBg8BTL+Zd=r+vKN~Wurz7?Qgd!E|F2pju_|k{ zxPN_hOlz6*wxk~BmL{+?R=~8he+bQyPcwJYm$|_KqHY}J6ung&a6msdCT*xWJ`Xh)3bqZ$kRIZ_jG`3MxrxNhqR{tKar#> zJK%)!Vr_zw$|4%mqwgt)K*ML{0eD-zL38bN?9$GkojW`KXjI~so}T)C7+O5fbI1R# zM#zAR15CG<(mV8fBE1`nSs*k>b@E$4*oJOowHAt4LU7Pfnl`JICXHK zL+Vz&vciVqewZgfdrk1GR@hP~Lz-V##m`$y=mRTTZ;b`X53H~) zbyb#D*c4FbHkO-b^4+f=$a=r}|)%(EsIS$pWvwPb?Lv~AK$TjW3lBQ)ZywL_a z;jSQ0;X~LgZRjLe2m4Rkf@gOztN&>q@!eJJrR3{^>bURzd%+AgiiN!nx>k;@tU~)p!ZCT(gdl zTcWmsv4X35ZZApKAyV@I>=qt<^?_>SQJA#@JlZRRyQr>>@~mdPyC4!RF7mWcPk`@x zs9{D-6ypWr`#;ihDy%KQ3M;G~z|tx{=LkQg^*l7NA#|6KmoxcwPbBw6hRFR< zn{D0H4fCV$Y>Cm}JG>-+^8BSK?v0QM9)`|mX093?!{igqxossJ%yEa9x+34I{ZEL* z04hPLR|oDu_g^}d!eKiOXVIiJXnO+Ar0zh8%n74+{+OIQm~%OC=+2Xy0)B;}+o1;2 z4EKduAaqs;fT3tH0)&h;nf-Bp)0rO%*^L)1ur5$U4b4+~?fH=C;`~`u=`xkJBFQugTd~=ehUwmEQ-mHT-{2WTwi`Z18v$ zOt(YDVl5BWr5y&EH3~f`us8MkVU__+&gJ!iP?Mk4pG5V*CQYARsW#iy{X-7NA}L6I zkg+=!gIb*#CUn50S+kCLsr=N93YEg%+ba)-x5D)3y6?Haa60jKolcz=Mvh=yYx<&v zS)ejf2{TkMHVtx;Mn9Y4>{bw)_oC=bH=qa&tLNZPxzH3Pzzf#{P82|Ye%fbO%L_^| zEMgV1mvCK1Nc&bY!S^x)#e8N%W+)ulvF&%@W$D}yyeu6olnU^&v_o<8j?F%AG&*|V zI5`=z+q}N1LLgZ4mNbqjDR9gD)6qG*eF4so{0VQ10ZcZGnZxX@Y9p+KMU{p|?mP}J zw)g-Bd_&BW9hwX!Q(;Cim`J4J;UXr8M@$0S?F$2eD=jNwv8hMkowEv<@ctS3{6HO;L^gBrEh;}Cq7mfpg-Y0nkezRq zizi=%CyU?}rHS`#H2DV7V;hP^pV#Xf8QEBbn>6?b2GZ+>Hf1vFCJIn-KV$J);9>8P zv8~&Np>NG>4qALh6Yvwv>$vy;)Tq4wejxM>v&Cc8wvXMKbk5o~5L!G*xIS=wP1K;D*L{*Z58imH^ zi-7QiT3H^xM*${>Bk3^pI5bl=uNT!nokjiz9w}NE@Twd=$I7Q#Kb}>As|W5GtC1_Z zp6VhL1wJVzF=ioD@#3RJ{Nl*f}9y8PTLI|h?h?#o~LU!T5Y_Y+^;m58T^S}j+xY<7LXPtLhqV{yml zp1$JpmtTU8`3#z*aNg~LEOeoTAlO+z7Jyr1q8c`bi8Uabp^TdRFq>O{%~**ckHEyO zh5cJKa04TAg}mG4;(mJBrG>nLyIGNg`t-?(52!3h@l@lb{>0ogJXqk z!VpWPMs^L+=O{ta52kH4joxMPYxVGQ>&BR(-|fjvg~NJ%)DnhU=jjY(59(Y8?JP!H zt!DqKM6ZFif

+3aHWQ14L+dB5Q$f(Oz%(%e>5^Ppx82lBl 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≪+K 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 LNdK`4lhpqL32ip9 literal 206460 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

~^1}yE(D6-0#G5z_ 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

7I`21rqGvB$Z0$$PJ;V7K)a6s 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 OXXT$6?>RA|Bl!Qc$#hl# From 164b6bb43e51d97de0f0b8d86e32c8dd40f9349d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 16:40:02 +0100 Subject: [PATCH 66/77] [poincare] Add a preference: numberOfSignificantDigits when layouting complex Change-Id: I2195c54c6cde43c42ccc0d180870fff0928aa270 --- poincare/include/poincare/preferences.h | 3 +++ poincare/src/complex.cpp | 8 ++++---- poincare/src/preferences.cpp | 24 ++++++++++++++---------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/poincare/include/poincare/preferences.h b/poincare/include/poincare/preferences.h index 4b0281d9b..40925dbd6 100644 --- a/poincare/include/poincare/preferences.h +++ b/poincare/include/poincare/preferences.h @@ -15,10 +15,13 @@ public: void setDisplayMode(Expression::FloatDisplayMode FloatDisplayMode); Expression::ComplexFormat complexFormat() const; void setComplexFormat(Expression::ComplexFormat complexFormat); + char numberOfSignificantDigits() const; + void setNumberOfSignificantDigits(char numberOfSignificantDigits); private: Expression::AngleUnit m_angleUnit; Expression::FloatDisplayMode m_displayMode; Expression::ComplexFormat m_complexFormat; + char m_numberOfSignificantDigits; }; } diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index e4df4f94c..34859247f 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -437,11 +437,11 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl int numberOfCharInSuperscript = 0; if (std::isnan(r()) || (std::isnan(th()) && r() != 0)) { - numberOfCharInBase = convertFloatToText(NAN, bufferBase, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); + numberOfCharInBase = convertFloatToText(NAN, bufferBase, PrintFloat::k_maxComplexBufferLength, Preferences::sharedPreferences()->numberOfSignificantDigits(), floatDisplayMode); return new StringLayout(bufferBase, numberOfCharInBase); } if (r() != 1 || th() == 0) { - numberOfCharInBase = convertFloatToText(r(), bufferBase, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); + numberOfCharInBase = convertFloatToText(r(), bufferBase, PrintFloat::k_maxFloatBufferLength, Preferences::sharedPreferences()->numberOfSignificantDigits(), floatDisplayMode); if (r() != 0 && th() != 0) { bufferBase[numberOfCharInBase++] = Ion::Charset::MiddleDot; } @@ -452,7 +452,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl } if (r() != 0 && th() != 0) { - numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, PrintFloat::k_maxFloatBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode); + numberOfCharInSuperscript = convertFloatToText(th(), bufferSuperscript, PrintFloat::k_maxFloatBufferLength, Preferences::sharedPreferences()->numberOfSignificantDigits(), floatDisplayMode); bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::MiddleDot; bufferSuperscript[numberOfCharInSuperscript++] = Ion::Charset::IComplex; bufferSuperscript[numberOfCharInSuperscript] = 0; @@ -466,7 +466,7 @@ ExpressionLayout * Complex::createPolarLayout(Expression::FloatDisplayMode fl template ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMode floatDisplayMode) const { char buffer[PrintFloat::k_maxComplexBufferLength]; - int numberOfChars = convertComplexToText(buffer, PrintFloat::k_maxComplexBufferLength, PrintFloat::k_numberOfPrintedSignificantDigits, floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot); + int numberOfChars = convertComplexToText(buffer, PrintFloat::k_maxComplexBufferLength, Preferences::sharedPreferences()->numberOfSignificantDigits(), floatDisplayMode, Expression::ComplexFormat::Cartesian, Ion::Charset::MiddleDot); return new StringLayout(buffer, numberOfChars); } diff --git a/poincare/src/preferences.cpp b/poincare/src/preferences.cpp index f23ad1cdf..c224419fd 100644 --- a/poincare/src/preferences.cpp +++ b/poincare/src/preferences.cpp @@ -1,4 +1,5 @@ #include +#include namespace Poincare { @@ -7,7 +8,8 @@ static Preferences s_preferences; Preferences::Preferences() : m_angleUnit(Expression::AngleUnit::Degree), m_displayMode(Expression::FloatDisplayMode::Decimal), - m_complexFormat(Expression::ComplexFormat::Cartesian) + m_complexFormat(Expression::ComplexFormat::Cartesian), + m_numberOfSignificantDigits(PrintFloat::k_numberOfPrintedSignificantDigits) { } @@ -20,9 +22,7 @@ Expression::AngleUnit Preferences::angleUnit() const { } void Preferences::setAngleUnit(Expression::AngleUnit angleUnit) { - if (angleUnit != m_angleUnit) { - m_angleUnit = angleUnit; - } + m_angleUnit = angleUnit; } Expression::FloatDisplayMode Preferences::displayMode() const { @@ -30,9 +30,7 @@ Expression::FloatDisplayMode Preferences::displayMode() const { } void Preferences::setDisplayMode(Expression::FloatDisplayMode FloatDisplayMode) { - if (FloatDisplayMode != m_displayMode) { - m_displayMode = FloatDisplayMode; - } + m_displayMode = FloatDisplayMode; } Expression::ComplexFormat Preferences::complexFormat() const { @@ -40,9 +38,15 @@ Expression::ComplexFormat Preferences::complexFormat() const { } void Preferences::setComplexFormat(Expression::ComplexFormat complexFormat) { - if (complexFormat != m_complexFormat) { - m_complexFormat = complexFormat; - } + m_complexFormat = complexFormat; +} + +char Preferences::numberOfSignificantDigits() const { + return m_numberOfSignificantDigits; +} + +void Preferences::setNumberOfSignificantDigits(char numberOfSignificantDigits) { + m_numberOfSignificantDigits = numberOfSignificantDigits; } } From 3dbe526a92a0a49fb288078f2cf79fc4ce5a2d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 16:41:29 +0100 Subject: [PATCH 67/77] [apps] In settings, add a row in display mode menu to choose the number of significant digits Change-Id: I5ff19740e0ee7258a6d77054df547f916d9b04c4 --- apps/settings/app.cpp | 2 +- apps/settings/app.h | 4 +- apps/settings/base.de.i18n | 1 + apps/settings/base.en.i18n | 1 + apps/settings/base.es.i18n | 1 + apps/settings/base.fr.i18n | 1 + apps/settings/base.pt.i18n | 1 + apps/settings/main_controller.cpp | 4 +- apps/settings/sub_controller.cpp | 127 ++++++++++++++++++++++++++---- apps/settings/sub_controller.h | 21 +++-- 10 files changed, 136 insertions(+), 27 deletions(-) diff --git a/apps/settings/app.cpp b/apps/settings/app.cpp index 137768557..b9013a437 100644 --- a/apps/settings/app.cpp +++ b/apps/settings/app.cpp @@ -26,7 +26,7 @@ App::Descriptor * App::Snapshot::descriptor() { } App::App(Container * container, Snapshot * snapshot) : - ::App(container, snapshot, &m_stackViewController, I18n::Message::Warning), + Shared::TextFieldDelegateApp(container, snapshot, &m_stackViewController), m_mainController(&m_stackViewController), m_stackViewController(&m_modalViewController, &m_mainController) { diff --git a/apps/settings/app.h b/apps/settings/app.h index ea9170848..a84c79b16 100644 --- a/apps/settings/app.h +++ b/apps/settings/app.h @@ -1,12 +1,12 @@ #ifndef SETTINGS_APP_H #define SETTINGS_APP_H -#include #include "main_controller.h" +#include "../shared/text_field_delegate_app.h" namespace Settings { -class App : public ::App { +class App : public Shared::TextFieldDelegateApp { public: class Descriptor : public ::App::Descriptor { public: diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 5c8afecaf..58505a7c2 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -17,6 +17,7 @@ Degres = "Grad " Radian = "Bogenmass " Auto = "Auto " Scientific = "Wissenschaftlich " +SignificantFigures = "Signifikante Stellen " Deg = "gra" Rad = "rad" Sci = "sci/" diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index 3880a44cb..975e2893e 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -17,6 +17,7 @@ Degres = "Degrees " Radian = "Radians " Auto = "Auto " Scientific = "Scientific " +SignificantFigures = "Significant figures " Deg = "deg" Rad = "rad" Sci = "sci/" diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index a50f4e084..d759afc75 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -17,6 +17,7 @@ Degres = "Grados " Radian = "Radianes " Auto = "Auto " Scientific = "Cientifico " +SignificantFigures = "Cifras significativas " Deg = "gra" Rad = "rad" Sci = "sci/" diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index 9cfbbda93..5a12de95a 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -17,6 +17,7 @@ Degres = "Degres " Radian = "Radians " Auto = "Auto " Scientific = "Scientifique " +SignificantFigures = "Chiffres significatifs " Deg = "deg" Rad = "rad" Sci = "sci/" diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index 11f86cc8f..cf03bb47a 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -17,6 +17,7 @@ Degres = "Graus " Radian = "Radianos " Auto = "Auto " Scientific = "Cientifico " +SignificantFigures = "Algarismo significativo " Deg = "gra" Rad = "rad" Sci = "sci/" diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index 5e4c91032..1f4aca5fd 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -11,7 +11,7 @@ using namespace Poincare; namespace Settings { const SettingsMessageTree angleChildren[2] = {SettingsMessageTree(I18n::Message::Degres), SettingsMessageTree(I18n::Message::Radian)}; -const SettingsMessageTree FloatDisplayModeChildren[2] = {SettingsMessageTree(I18n::Message::Auto), SettingsMessageTree(I18n::Message::Scientific)}; +const SettingsMessageTree FloatDisplayModeChildren[3] = {SettingsMessageTree(I18n::Message::Auto), SettingsMessageTree(I18n::Message::Scientific), SettingsMessageTree(I18n::Message::SignificantFigures)}; const SettingsMessageTree complexFormatChildren[2] = {SettingsMessageTree(I18n::Message::Default), SettingsMessageTree(I18n::Message::Default)}; const SettingsMessageTree examChildren[1] = {SettingsMessageTree(I18n::Message::ActivateExamMode)}; const SettingsMessageTree aboutChildren[3] = {SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId)}; @@ -21,7 +21,7 @@ const SettingsMessageTree menu[8] = #else const SettingsMessageTree menu[7] = #endif - {SettingsMessageTree(I18n::Message::AngleUnit, angleChildren, 2), SettingsMessageTree(I18n::Message::DisplayMode, FloatDisplayModeChildren, 2), SettingsMessageTree(I18n::Message::ComplexFormat, complexFormatChildren, 2), + {SettingsMessageTree(I18n::Message::AngleUnit, angleChildren, 2), SettingsMessageTree(I18n::Message::DisplayMode, FloatDisplayModeChildren, 3), SettingsMessageTree(I18n::Message::ComplexFormat, complexFormatChildren, 2), SettingsMessageTree(I18n::Message::Brightness), SettingsMessageTree(I18n::Message::Language), SettingsMessageTree(I18n::Message::ExamMode, examChildren, 1), #if OS_WITH_SOFTWARE_UPDATE_PROMPT SettingsMessageTree(I18n::Message::UpdatePopUp), diff --git a/apps/settings/sub_controller.cpp b/apps/settings/sub_controller.cpp index e89c03945..8fc44b8b3 100644 --- a/apps/settings/sub_controller.cpp +++ b/apps/settings/sub_controller.cpp @@ -4,6 +4,7 @@ #include "../../poincare/src/layout/baseline_relative_layout.h" #include "../../poincare/src/layout/string_layout.h" #include +#include using namespace Poincare; @@ -11,8 +12,9 @@ namespace Settings { SubController::SubController(Responder * parentResponder) : ViewController(parentResponder), + m_editableCell(&m_selectableTableView, this, m_draftTextBuffer), m_selectableTableView(this, this, 0, 1, k_topBottomMargin, Metric::CommonRightMargin, - k_topBottomMargin, Metric::CommonLeftMargin, this), + k_topBottomMargin, Metric::CommonLeftMargin, this, this), m_messageTreeModel(nullptr) { for (int i = 0; i < k_totalNumberOfCell; i++) { @@ -28,6 +30,8 @@ SubController::SubController(Responder * parentResponder) : for (int i = 0; i < 2; i++) { m_complexFormatCells[i].setExpression(m_complexFormatLayout[i]); } + m_editableCell.setMessage(I18n::Message::SignificantFigures); + m_editableCell.setMessageFontSize(KDText::FontSize::Large); } SubController::~SubController() { @@ -50,13 +54,8 @@ View * SubController::view() { return &m_selectableTableView; } -void SubController::didEnterResponderChain(Responder * previousResponder) { - if (previousResponder->commonAncestorWith(this) == parentResponder()) { - /* We want to select the prefered SettingMessageTree only when the previous page - * was the main setting page. We do not to change the selection when - * dismissing a pop-up for instance. */ - selectCellAtLocation(0, valueIndexForPreference(m_messageTreeModel->label())); - } +void SubController::didBecomeFirstResponder() { + selectCellAtLocation(0, valueIndexForPreference(m_messageTreeModel->label())); if (m_messageTreeModel->label() == I18n::Message::ExamMode) { m_selectableTableView.reloadData(); } @@ -94,6 +93,7 @@ bool SubController::handleEvent(Ion::Events::Event event) { return false; } /* Generic behaviour of preference menu*/ + assert(m_messageTreeModel->label() != I18n::Message::DisplayMode || selectedRow() != numberOfRows()-1); // In that case, events OK and EXE are handled by the cell setPreferenceWithValueIndex(m_messageTreeModel->label(), selectedRow()); AppsContainer * myContainer = (AppsContainer * )app()->container(); myContainer->refreshPreferences(); @@ -115,30 +115,70 @@ int SubController::numberOfRows() { return 0; } -HighlightCell * SubController::reusableCell(int index) { - assert(index >= 0); - assert(index < k_totalNumberOfCell); - if (m_messageTreeModel->label() == I18n::Message::ComplexFormat) { +HighlightCell * SubController::reusableCell(int index, int type) { + if (type == 2) { + assert(index == 0); + return &m_editableCell; + } else if (type == 1) { + assert(index >= 0 && index < 2); return &m_complexFormatCells[index]; } + assert(index >= 0 && index < k_totalNumberOfCell); return &m_cells[index]; } -int SubController::reusableCellCount() { - if (m_messageTreeModel->label() == I18n::Message::ComplexFormat) { - return 2; +int SubController::reusableCellCount(int type) { + switch (type) { + case 0: + return k_totalNumberOfCell; + case 1: + return 2; + case 2: + return 1; + default: + assert(false); + return 0; } - return k_totalNumberOfCell; } -KDCoordinate SubController::cellHeight() { +int SubController::typeAtLocation(int i, int j) { + if (m_messageTreeModel->label() == I18n::Message::ComplexFormat) { + return 1; + } + if (m_messageTreeModel->label() == I18n::Message::DisplayMode && j == numberOfRows()-1) { + return 2; + } + return 0; +} + +KDCoordinate SubController::rowHeight(int j) { return Metric::ParameterCellHeight; } +KDCoordinate SubController::cumulatedHeightFromIndex(int j) { + return rowHeight(0) * j; +} + +int SubController::indexFromCumulatedHeight(KDCoordinate offsetY) { + KDCoordinate height = rowHeight(0); + if (height == 0) { + return 0; + } + return (offsetY - 1) / height; +} + void SubController::willDisplayCellForIndex(HighlightCell * cell, int index) { if (m_messageTreeModel->label() == I18n::Message::ComplexFormat) { return; } + /* Number of significants figure row */ + if (m_messageTreeModel->label() == I18n::Message::DisplayMode && index == numberOfRows()-1) { + MessageTableCellWithEditableText * myCell = (MessageTableCellWithEditableText *)cell; + char buffer[3]; + Integer(Preferences::sharedPreferences()->numberOfSignificantDigits()).writeTextInBuffer(buffer, 3); + myCell->setAccessoryText(buffer); + return; + } MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)cell; myCell->setMessage(m_messageTreeModel->children(index)->label()); myCell->setMessageFontSize(KDText::FontSize::Large); @@ -179,6 +219,56 @@ void SubController::viewDidDisappear() { m_selectableTableView.deselectTable(); } +bool SubController::textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) { + return (event == Ion::Events::Up && selectedRow() > 0) + || TextFieldDelegate::textFieldShouldFinishEditing(textField, event); +} + +bool SubController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { + Context * globalContext = textFieldDelegateApp()->localContext(); + float floatBody = Expression::approximateToScalar(text, *globalContext); + if (std::isnan(floatBody) || std::isinf(floatBody)) { + floatBody = PrintFloat::k_numberOfPrintedSignificantDigits; + } + if (floatBody < 1) { + floatBody = 1; + } + if (floatBody > PrintFloat::k_numberOfStoredSignificantDigits) { + floatBody = PrintFloat::k_numberOfStoredSignificantDigits; + } + Preferences::sharedPreferences()->setNumberOfSignificantDigits((char)std::round(floatBody)); + m_selectableTableView.reloadCellAtLocation(0, selectedRow()); + if (event == Ion::Events::Up || event == Ion::Events::OK) { + m_selectableTableView.handleEvent(event); + } + return true; +} + +bool SubController::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { + if (event == Ion::Events::Backspace && !textField->isEditing()) { + textField->setEditing(true); + return true; + } + return TextFieldDelegate::textFieldDidReceiveEvent(textField, event); +} + +void SubController::tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) { + if (m_messageTreeModel->label() != I18n::Message::DisplayMode) { + return; + } + if (previousSelectedCellX == t->selectedColumn() && previousSelectedCellY == t->selectedRow()) { + return; + } + if (previousSelectedCellY == numberOfRows()-1) { + MessageTableCellWithEditableText * myCell = (MessageTableCellWithEditableText *)t->cellAtLocation(previousSelectedCellX, previousSelectedCellY); + myCell->setEditing(false); + } + if (t->selectedRow() == numberOfRows() -1) { + MessageTableCellWithEditableText * myNewCell = (MessageTableCellWithEditableText *)t->selectedCell(); + app()->setFirstResponder(myNewCell); + } +} + StackViewController * SubController::stackController() const { return (StackViewController *)parentResponder(); } @@ -208,5 +298,8 @@ int SubController::valueIndexForPreference(I18n::Message message) { return 0; } +Shared::TextFieldDelegateApp * SubController::textFieldDelegateApp() { + return (Shared::TextFieldDelegateApp *)app(); +} } diff --git a/apps/settings/sub_controller.h b/apps/settings/sub_controller.h index 5e4c81d67..42a78c329 100644 --- a/apps/settings/sub_controller.h +++ b/apps/settings/sub_controller.h @@ -4,10 +4,11 @@ #include #include "settings_message_tree.h" #include "../hardware_test/pop_up_controller.h" +#include "../shared/text_field_delegate.h" namespace Settings { -class SubController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { +class SubController : public ViewController, public ListViewDataSource, public SelectableTableViewDataSource, public SelectableTableViewDelegate, public Shared::TextFieldDelegate { public: SubController(Responder * parentResponder); ~SubController(); @@ -18,24 +19,34 @@ public: View * view() override; const char * title() override; bool handleEvent(Ion::Events::Event event) override; - void didEnterResponderChain(Responder * previousFirstResponder) override; + void didBecomeFirstResponder() override; int numberOfRows() override; - KDCoordinate cellHeight() override; - HighlightCell * reusableCell(int index) override; - int reusableCellCount() override; + KDCoordinate rowHeight(int j) override; + KDCoordinate cumulatedHeightFromIndex(int j) override; + int indexFromCumulatedHeight(KDCoordinate offsetY) override; + HighlightCell * reusableCell(int index, int type) override; + int reusableCellCount(int type) override; + int typeAtLocation(int i, int j) override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; void setMessageTreeModel(const MessageTree * messageTreeModel); void viewWillAppear() override; void viewDidDisappear() override; + bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; + bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; + bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; + void tableViewDidChangeSelection(SelectableTableView * t, int previousSelectedCellX, int previousSelectedCellY) override; private: StackViewController * stackController() const; void setPreferenceWithValueIndex(I18n::Message message, int valueIndex); int valueIndexForPreference(I18n::Message message); + Shared::TextFieldDelegateApp * textFieldDelegateApp() override; constexpr static int k_totalNumberOfCell = I18n::NumberOfLanguages; constexpr static KDCoordinate k_topBottomMargin = 13; MessageTableCellWithBuffer m_cells[k_totalNumberOfCell]; ExpressionTableCell m_complexFormatCells[2]; Poincare::ExpressionLayout * m_complexFormatLayout[2]; + MessageTableCellWithEditableText m_editableCell; + char m_draftTextBuffer[MessageTableCellWithEditableText::k_bufferLength]; SelectableTableView m_selectableTableView; MessageTree * m_messageTreeModel; HardwareTest::PopUpController m_hardwareTestPopUpController; From b5cd6c76ca7a2e7ab659a34452ac9db374b81172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 16:42:16 +0100 Subject: [PATCH 68/77] [poincare] Increase the maximal length of decimal number before switching to scientific printing Change-Id: I31c404cc7f89babd279689a48c1adcb33a46da7a --- poincare/include/poincare/decimal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poincare/include/poincare/decimal.h b/poincare/include/poincare/decimal.h index 823e1c98d..90ee18b1c 100644 --- a/poincare/include/poincare/decimal.h +++ b/poincare/include/poincare/decimal.h @@ -41,7 +41,7 @@ private: Expression * privateApproximate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } template Expression * templatedApproximate(Context& context, Expression::AngleUnit angleUnit) const; - constexpr static int k_maxLength = 10; + constexpr static int k_maxLength = 15; Integer m_mantissa; int m_exponent; }; From 95ce07dfc76107bb19a0259130c511150414da0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 17:39:04 +0100 Subject: [PATCH 69/77] [poincare] In Integer::approximate, exclude special case 0 first to avoid breaking assertions Change-Id: I7dea2e05a485a581251b43be1814f00615f17594 --- poincare/src/integer.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index 72622403e..1c17bf2c3 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -473,6 +473,15 @@ IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denomin template T Integer::approximate() const { + if (isZero()) { + /* This special case for 0 is needed, because the current algorithm assumes + * that the big integer is non zero, thus puts the exponent to 126 (integer + * area), the issue is that when the mantissa is 0, a "shadow bit" is + * assumed to be there, thus 126 0x000000 is equal to 0.5 and not zero. + */ + T result = m_negative ? -0.0 : 0.0; + return result; + } union { uint64_t uint_result; T float_result; @@ -544,16 +553,6 @@ T Integer::approximate() const { // shift the mantissa if the rounding increase the length in bits of the // mantissa. - if (isZero()) { - /* This special case for 0 is needed, because the current algorithm assumes - * that the big integer is non zero, thus puts the exponent to 126 (integer - * area), the issue is that when the mantissa is 0, a "shadow bit" is - * assumed to be there, thus 126 0x000000 is equal to 0.5 and not zero. - */ - T result = m_negative ? -0.0 : 0.0; - return result; - } - u.uint_result = 0; u.uint_result |= ((uint64_t)sign << (totalNumberOfBits-1)); u.uint_result |= ((uint64_t)exponent << mantissaNbBit); From abb1e04b13f85e6c4575b6e97344eef05915a389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 30 Nov 2017 17:39:52 +0100 Subject: [PATCH 70/77] [poincare] Fix bug: when looking for the common multiple, the factor can have the same base without being a power! Change-Id: I8a3af89165ed2117a39d19968138fb3a1e1169f0 --- poincare/src/multiplication.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index cc9c91476..eeac975b6 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -622,7 +622,11 @@ void Multiplication::addMissingFactors(Expression * factor, Context & context, A Expression * sub = new Subtraction(CreateExponent(editableOperand(i)), CreateExponent(factor), false); Reduce((Expression **)&sub, context, angleUnit); if (sub->sign() == Sign::Negative) { // index[0] < index[1] - factor->replaceOperand(factor->editableOperand(1), new Opposite(sub, true), true); + if (factor->type() == Type::Power) { + factor->replaceOperand(factor->editableOperand(1), new Opposite(sub, true), true); + } else { + factor = new Power(factor, new Opposite(sub, true), false); + } factor->editableOperand(1)->shallowReduce(context, angleUnit); factorizeBase(editableOperand(i), factor, context, angleUnit); editableOperand(i)->shallowReduce(context, angleUnit); From bd26c499e4b353cfcf99be425c529f983e20fe53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 11:25:25 +0100 Subject: [PATCH 71/77] [poincare] In Multiplication::addMissingFactors: fix bug due to creating a multiplication nested inside a multiplication without reducing it Change-Id: I2e8174a3714e90e6ecc4edbed0e17e434921e269 --- poincare/include/poincare/multiplication.h | 1 + poincare/src/multiplication.cpp | 32 ++++++++++++++-------- poincare/test/addition.cpp | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index 64376d5d0..6bc68b1ed 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -34,6 +34,7 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; Expression * privateShallowReduce(Context& context, AngleUnit angleUnit, bool expand); + void mergeMultiplicationOperands(); void factorizeBase(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); void factorizeExponent(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); Expression * distributeOnOperandAtIndex(int index, Context & context, AngleUnit angleUnit); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index eeac975b6..49048bad1 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -126,16 +126,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a } /* Step 1: Multiplication is associative, so let's start by merging children * which also are multiplications themselves. */ - int i = 0; - int initialNumberOfOperands = numberOfOperands(); - while (i < initialNumberOfOperands) { - Expression * o = editableOperand(i); - if (o->type() == Type::Multiplication) { - mergeOperands(static_cast(o)); // TODO: ensure that matrix operands are not swapped to implement MATRIX_EXACT_REDUCING - continue; - } - i++; - } + mergeMultiplicationOperands(); /* Step 2: If any of the operand is zero, the multiplication result is zero */ for (int i = 0; i < numberOfOperands(); i++) { @@ -225,7 +216,7 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a /* Step 4: Gather like terms. For example, turn pi^2*pi^3 into pi^5. Thanks to * the simplification order, such terms are guaranteed to be next to each * other. */ - i = 0; + int i = 0; while (i < numberOfOperands()-1) { Expression * oi = editableOperand(i); Expression * oi1 = editableOperand(i+1); @@ -328,6 +319,20 @@ Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit a return result; } +void Multiplication::mergeMultiplicationOperands() { + // Multiplication is associative: a*(b*c)->a*b*c + int i = 0; + int initialNumberOfOperands = numberOfOperands(); + while (i < initialNumberOfOperands) { + Expression * o = editableOperand(i); + if (o->type() == Type::Multiplication) { + mergeOperands(static_cast(o)); // TODO: ensure that matrix operands are not swapped to implement MATRIX_EXACT_REDUCING + continue; + } + i++; + } +} + void Multiplication::factorizeSineAndCosine(Expression * o1, Expression * o2, Context & context, AngleUnit angleUnit) { assert(o1->parent() == this && o2->parent() == this); /* This function turn sin(x)^p * cos(x)^q into either: @@ -632,8 +637,13 @@ void Multiplication::addMissingFactors(Expression * factor, Context & context, A editableOperand(i)->shallowReduce(context, angleUnit); } else if (sub->sign() == Sign::Unknown) { factorizeBase(editableOperand(i), factor, context, angleUnit); + editableOperand(i)->shallowReduce(context, angleUnit); } else {} delete sub; + /* Reducing the new operand i can lead to creating a new multiplication + * (ie 2^(1+2*3^(1/2)) -> 2*2^(2*3^(1/2)). We thus have to get rid of + * nested multiplication: */ + mergeMultiplicationOperands(); return; } } diff --git a/poincare/test/addition.cpp b/poincare/test/addition.cpp index 402c28b5f..85216991c 100644 --- a/poincare/test/addition.cpp +++ b/poincare/test/addition.cpp @@ -54,4 +54,5 @@ QUIZ_CASE(poincare_addition_simplify) { assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); assert_parsed_expression_simplify_to("A+B-A-B", "0"); assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); + assert_parsed_expression_simplify_to("3^(1/2)+2^(-2*3^(1/2)*X^P)/2", "(1+2*2^(2*R(3)*X^P)*R(3))/(2*2^(2*R(3)*X^P))"); } From a20e6ee2ed505b1f1f1d8c8e75cee1188aba0082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 14:09:10 +0100 Subject: [PATCH 72/77] [poincare] Enable interrupting some methods in Simplify that are called recursively Change-Id: I5b8c0de51617a3a36b3c8bc673de66dc60e74483 --- poincare/src/expression.cpp | 6 ++++++ poincare/src/power.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 64045703e..4e8e4f4e0 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -168,9 +168,15 @@ bool Expression::IsMatrix(const Expression * e) { int Expression::SimplificationOrder(const Expression * e1, const Expression * e2) { if (e1->type() > e2->type()) { return -(e2->simplificationOrderGreaterType(e1)); + if (shouldStopProcessing()) { + return 1; + } } else if (e1->type() == e2->type()) { return e1->simplificationOrderSameType(e2); } else { + if (shouldStopProcessing()) { + return -1; + } return e1->simplificationOrderGreaterType(e2); } } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index a81e2520a..155065413 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -34,6 +34,9 @@ Expression * Power::clone() const { } Expression::Sign Power::sign() const { + if (shouldStopProcessing()) { + return Sign::Unknown; + } if (operand(0)->sign() == Sign::Positive && operand(1)->sign() != Sign::Unknown) { return Sign::Positive; } From 15717e4349d5ecb58d3449bb3a21c1edc5f6ea60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 14:39:27 +0100 Subject: [PATCH 73/77] [poincare] Fix leak in multiplication::distributeOnOperandAtIndex Change-Id: Iba88f1c80a68b5b5c34d8448717e9b492f94c451 --- poincare/src/multiplication.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 49048bad1..69e2afa02 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -437,11 +437,12 @@ Expression * Multiplication::distributeOnOperandAtIndex(int i, Context & context // This function turns a*(b+c) into a*b + a*c // We avoid deleting and creating a new addition Addition * a = static_cast(editableOperand(i)); + removeOperand(a, false); for (int j = 0; j < a->numberOfOperands(); j++) { + Multiplication * m = static_cast(clone()); Expression * termJ = a->editableOperand(j); - replaceOperand(operand(i), termJ->clone(), false); - Expression * m = clone(); - a->replaceOperand(termJ, m, true); + a->replaceOperand(termJ, m, false); + m->addOperand(termJ); m->shallowReduce(context, angleUnit); // pi^(-1)*(pi + x) -> pi^(-1)*pi + pi^(-1)*x -> 1 + pi^(-1)*x } replaceWith(a, true); From 439c46d736b8aa9934a69593ba5e3e202411f2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 15:31:44 +0100 Subject: [PATCH 74/77] [poincare] Enable to interrupt Integral evaluation Change-Id: I4c4f03beb1801cb9a9c568067356dac3c6a95820 --- poincare/src/integral.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index a60268268..8fb5ad914 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -174,6 +174,9 @@ Integral::DetailedResult Integral::kronrodGaussQuadrature(T a, T b, VariableC template T Integral::adaptiveQuadrature(T a, T b, T eps, int numberOfIterations, VariableContext xContext, AngleUnit angleUnit) const { + if (shouldStopProcessing()) { + return NAN; + } DetailedResult quadKG = kronrodGaussQuadrature(a, b, xContext, angleUnit); T result = quadKG.integral; if (quadKG.absoluteError <= eps) { From 99ddde28d56cc199b90b94ac8b6813d4a1abf8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 15:55:03 +0100 Subject: [PATCH 75/77] [poincare] Fix leak in Power::computeOnMatrixAndComplex Change-Id: I009947c02053ceef967fb7d68524c7cf61db9c90 --- poincare/src/power.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 155065413..addb4f3f8 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -106,7 +106,9 @@ template Matrix * Power::computeOnMatrixAndComplex(const Matrix * m, delete result; return nullptr; } - result = Multiplication::computeOnMatrices(result, m); + Matrix * mult = Multiplication::computeOnMatrices(result, m); + delete result; + result = mult; } return result; } From 504bd416643859430e5d681b3534f7bc31f81ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 16:38:52 +0100 Subject: [PATCH 76/77] [apps] In calculation: Avoid to parse twice instead of once when checking the input string Change-Id: I653ae54b554c05735c7f9502a6ca44076aed5d7e --- apps/calculation/app.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 2a947cf8c..701ce5926 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -55,7 +55,7 @@ Context * App::localContext() { } bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event event) { - if (TextFieldDelegateApp::textFieldDidReceiveEvent(textField, event)) { + if ((event == Ion::Events::Var || event == Ion::Events::XNT) && TextFieldDelegateApp::textFieldDidReceiveEvent(textField, event)) { return true; } /* Here, we check that the expression entered by the user can be printed with @@ -63,7 +63,10 @@ bool App::textFieldDidReceiveEvent(::TextField * textField, Ion::Events::Event e * user from adding this expression to the calculation store. */ if (textField->isEditing() && textField->textFieldShouldFinishEditing(event)) { Expression * exp = Expression::parse(textField->text()); - assert(exp != nullptr); + if (exp == nullptr) { + textField->app()->displayWarning(I18n::Message::SyntaxError); + return true; + } char buffer[Calculation::k_printedExpressionSize]; int length = exp->writeTextInBuffer(buffer, sizeof(buffer)); delete exp; From b3f2f245b07b603bc1edc4d826bbeaccece6c993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 1 Dec 2017 16:50:02 +0100 Subject: [PATCH 77/77] [poincare] Fix leak when accepting ill-formed matrices-> do not accept ill-formed matrices Change-Id: I11a47c832287ac631838b9673174db5fff909f94 --- poincare/include/poincare/matrix_data.h | 1 - poincare/src/expression_parser.y | 2 +- poincare/src/matrix_data.cpp | 10 +++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/poincare/include/poincare/matrix_data.h b/poincare/include/poincare/matrix_data.h index f168aaf1b..53ef2fbe7 100644 --- a/poincare/include/poincare/matrix_data.h +++ b/poincare/include/poincare/matrix_data.h @@ -25,7 +25,6 @@ private: int m_numberOfRows; int m_numberOfColumns; const Expression ** m_operands; - static Complex * defaultExpression(); }; } diff --git a/poincare/src/expression_parser.y b/poincare/src/expression_parser.y index 4c989fa9c..8218558e7 100644 --- a/poincare/src/expression_parser.y +++ b/poincare/src/expression_parser.y @@ -144,7 +144,7 @@ lstData: /* MATRICES_ARE_DEFINED */ mtxData: LEFT_BRACKET lstData RIGHT_BRACKET { $$ = new Poincare::MatrixData($2, false); $2->detachOperands(); delete $2; } - | mtxData LEFT_BRACKET lstData RIGHT_BRACKET { $$ = $1; $$->pushListData($3, false); $3->detachOperands(); delete $3; } + | mtxData LEFT_BRACKET lstData RIGHT_BRACKET { if ($3->numberOfOperands() != $1->numberOfColumns()) { delete $1; delete $3; YYERROR; } ; $$ = $1; $$->pushListData($3, false); $3->detachOperands(); delete $3; } number: DIGITS { $$ = new Poincare::Rational(Poincare::Integer($1.address, false)); } diff --git a/poincare/src/matrix_data.cpp b/poincare/src/matrix_data.cpp index 8c1e31a3c..4ef93f708 100644 --- a/poincare/src/matrix_data.cpp +++ b/poincare/src/matrix_data.cpp @@ -23,10 +23,6 @@ MatrixData::MatrixData(ListData * listData, bool clone) : } } -Complex * MatrixData::defaultExpression() { - return new Complex(Complex::Float(0.0)); -} - MatrixData::~MatrixData() { if (m_operands != nullptr) { for (int i=0; inumberOfOperands() == m_numberOfColumns); for (int i = 0; i < m_numberOfRows*m_numberOfColumns; i++) { newOperands[i] = m_operands[i]; } for (int i = 0; i < m_numberOfColumns; i++) { - int max = listData->numberOfOperands(); if (clone) { - newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? listData->operand(i)->clone() : defaultExpression(); + newOperands[m_numberOfRows*m_numberOfColumns+i] = listData->operand(i)->clone(); } else { - newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? listData->operand(i) : defaultExpression(); + newOperands[m_numberOfRows*m_numberOfColumns+i] = listData->operand(i); } } delete[] m_operands;