diff --git a/.gitignore b/.gitignore index 1eabd6d6b..6d58dfa02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /output/ build/device/**/*.pyc -epsilon.elf \ No newline at end of file +epsilon.elf +.vscode diff --git a/.gitmodules b/.gitmodules index 134e135f6..1e294d433 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "apps/atom"] path = apps/atom url = https://github.com/Omega-Numworks/Omega-Atom.git +[submodule "themes"] + path = themes + url = https://github.com/Omega-Numworks/Omega-Themes.git diff --git a/apps/regression/model/model.cpp b/apps/regression/model/model.cpp index ca91effd3..d949b1571 100644 --- a/apps/regression/model/model.cpp +++ b/apps/regression/model/model.cpp @@ -18,14 +18,14 @@ void Model::tidy() { Poincare::Expression Model::simplifiedExpression(double * modelCoefficients, Poincare::Context * context) { Expression e = expression(modelCoefficients); if (!e.isUninitialized()) { - PoincareHelpers::Simplify(&e, context); + PoincareHelpers::Simplify(&e, context, ExpressionNode::ReductionTarget::SystemForApproximation); } return e; } double Model::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { Expression yExpression = Number::DecimalNumber(y); - PoincareHelpers::Simplify(&yExpression, context); + PoincareHelpers::Simplify(&yExpression, context, ExpressionNode::ReductionTarget::SystemForApproximation); Expression modelExpression = simplifiedExpression(modelCoefficients, context); double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).x1(); return result; diff --git a/apps/sequence/values/values_controller.cpp b/apps/sequence/values/values_controller.cpp index c5ba80fd4..36ae99790 100644 --- a/apps/sequence/values/values_controller.cpp +++ b/apps/sequence/values/values_controller.cpp @@ -24,6 +24,8 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle StackViewController * stack = ((StackViewController *)valuesController->stackController()); IntervalParameterController * controller = valuesController->intervalParameterController(); controller->setInterval(valuesController->intervalAtColumn(valuesController->selectedColumn())); + /* No need to change Nstart/Nend messages because they are the only messages + * used and we set them in ValuesController::ValuesController(...) */ stack->push(controller); return true; }, this), k_font) @@ -32,6 +34,7 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle m_sequenceTitleCells[i].setOrientation(Shared::FunctionTitleCell::Orientation::HorizontalIndicator); } setupSelectableTableViewAndCells(inputEventHandlerDelegate); + setDefaultStartEndMessages(); } // TableViewDataSource @@ -64,7 +67,7 @@ I18n::Message ValuesController::emptyMessage() { } // ValuesController -void ValuesController::setStartEndMessages(Shared::IntervalParameterController * controller, int column) { +void ValuesController::setDefaultStartEndMessages() { m_intervalParameterController.setStartEndMessages(I18n::Message::NStart, I18n::Message::NEnd); } diff --git a/apps/sequence/values/values_controller.h b/apps/sequence/values/values_controller.h index 11ea61c53..e24558e4a 100644 --- a/apps/sequence/values/values_controller.h +++ b/apps/sequence/values/values_controller.h @@ -33,7 +33,11 @@ private: constexpr static int k_maxNumberOfDisplayableCells = k_maxNumberOfDisplayableSequences * k_maxNumberOfDisplayableRows; // ValuesController - void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override; + void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override { + setDefaultStartEndMessages(); + } + + void setDefaultStartEndMessages(); I18n::Message valuesParameterMessageAtColumn(int columnIndex) const override; int maxNumberOfCells() override { return k_maxNumberOfDisplayableCells; } int maxNumberOfFunctions() override { return k_maxNumberOfDisplayableSequences; } diff --git a/apps/shared/expression_model.cpp b/apps/shared/expression_model.cpp index 015526c11..dcdb5d347 100644 --- a/apps/shared/expression_model.cpp +++ b/apps/shared/expression_model.cpp @@ -47,7 +47,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po m_expression = Undefined::Builder(); } else { m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); - PoincareHelpers::Simplify(&m_expression, context); + PoincareHelpers::Simplify(&m_expression, context, ExpressionNode::ReductionTarget::SystemForApproximation); // simplify might return an uninitialized Expression if interrupted if (m_expression.isUninitialized()) { m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record)); diff --git a/apps/shared/poincare_helpers.h b/apps/shared/poincare_helpers.h index b8d73f340..0f2ba050e 100644 --- a/apps/shared/poincare_helpers.h +++ b/apps/shared/poincare_helpers.h @@ -62,10 +62,10 @@ inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Contex return Poincare::Expression::ParseAndSimplify(text, context, complexFormat, preferences->angleUnit(), symbolicComputation); } -inline void Simplify(Poincare::Expression * e, Poincare::Context * context, bool symbolicComputation = true) { +inline void Simplify(Poincare::Expression * e, Poincare::Context * context, Poincare::ExpressionNode::ReductionTarget target, bool symbolicComputation = true) { Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), *e, context); - *e = e->simplify(context, complexFormat, preferences->angleUnit(), symbolicComputation); + *e = e->simplify(context, complexFormat, preferences->angleUnit(), target, symbolicComputation); } inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context * context, bool symbolicComputation = true) { diff --git a/apps/shared/values_controller.cpp b/apps/shared/values_controller.cpp index ec0a2c4bd..8786a4a3d 100644 --- a/apps/shared/values_controller.cpp +++ b/apps/shared/values_controller.cpp @@ -243,10 +243,23 @@ void ValuesController::didChangeCell(int column, int row) { return; } + // Find the abscissa column corresponding to column + int abscissaColumn = 0; + int nbOfColumns = numberOfColumnsForAbscissaColumn(abscissaColumn); + while (column >= nbOfColumns) { + abscissaColumn = nbOfColumns; + nbOfColumns += numberOfColumnsForAbscissaColumn(abscissaColumn); + } + // Update the memoization of rows linked to the changed cell int nbOfMemoizedColumns = numberOfMemoizedColumn(); - for (int i = column+1; i < column+numberOfColumnsForAbscissaColumn(column); i++) { + int nbOfColumnsForAbscissa = numberOfColumnsForAbscissaColumn(abscissaColumn); + for (int i = abscissaColumn+1; i < abscissaColumn+nbOfColumnsForAbscissa; i++) { int memoizedI = valuesColumnForAbsoluteColumn(i) - m_firstMemoizedColumn; + if (memoizedI < 0 || memoizedI >= nbOfMemoizedColumns) { + // The changed column is out of the memoized table + continue; + } fillMemoizedBuffer(i, row, nbOfMemoizedColumns*memoizedRow+memoizedI); } } diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index d083bb533..ea3112b53 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -276,7 +276,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact assert(degree == 2); // Compute delta = b*b-4ac Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone())); - delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit()); + delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), ExpressionNode::ReductionTarget::SystemForApproximation); if (delta.isUninitialized()) { delta = Poincare::Undefined::Builder(); } diff --git a/apps/solver/test/equation_store.cpp b/apps/solver/test/equation_store.cpp index f98a56125..396c6a9ed 100644 --- a/apps/solver/test/equation_store.cpp +++ b/apps/solver/test/equation_store.cpp @@ -128,6 +128,11 @@ QUIZ_CASE(equation_solve) { const char * solutions11[] = {"\u0012\u0012√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "\u0012\u0012-√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "π^\u00122\u0013-2π+8√\u00125\u0013+9"}; // (√(π^2-2π+8√(5)+9)-π+1)/4, (-√(π^2-2π+8×√(5)+9)-π+1)/4, π^2-2π+8√(5)+9 assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 3); + // (x-3)^2 + const char * equations21[] = {"(x-3)^2=0", 0}; + const char * solutions21[] = {"3", "0"}; + assert_equation_system_exact_solve_to(equations21, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions21, 2); + // TODO // x^3 - 4x^2 + 6x - 24 = 0 //const char * equations10[] = {"2×x^2-4×x+4=3", 0}; @@ -205,6 +210,11 @@ QUIZ_CASE(equation_solve_complex_format) { const char * equations4[] = {"x+√(-1)×√(-1)=0", 0}; assert_equation_system_exact_solve_to(equations4, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0); + // root(-8,3)*x+3 = 0 --> 3/2 in R + const char * equations5[] = {"root(-8,3)*x+3=0", 0}; + const char * solutions5[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013"}; + assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5, 1); + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian); // x+𝐢 = 0 --> x = -𝐢 assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1); @@ -224,6 +234,9 @@ QUIZ_CASE(equation_solve_complex_format) { const char * solutions4[] = {"1"}; assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions4, 1); + const char * solutions5Cartesain[] = {"-\u0012\u00123\u0013/\u00124\u0013\u0013+\u0012\u00123√\u00123\u0013\u0013/\u00124\u0013\u0013𝐢"}; //-3/4+(3√3/4)*𝐢 + assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Cartesain, 1); + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar); // x+𝐢 = 0 --> x = e^(-π/2×i) const char * solutions0Polar[] = {"ℯ^\u0012-\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ℯ^(-(π/2)𝐢) @@ -240,6 +253,9 @@ QUIZ_CASE(equation_solve_complex_format) { const char * solutions3Polar[] = {"ℯ^\u0012-\u0012\u00123π\u0013/\u00124\u0013\u0013𝐢\u0013", "ℯ^\u0012\u0012\u0012π\u0013/\u00124\u0013\u0013𝐢\u0013", "4ℯ^\u0012\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ℯ^(-(3×π/4)𝐢)"‰, "ℯ^((π/4)𝐢)", "4ℯ^((π/2)𝐢) assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3Polar, 3); + const char * solutions5Polar[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013ℯ^\u0012\u0012\u00122π\u0013/\u00123\u0013\u0013𝐢\u0013"}; //3/2ℯ^\u0012\u00122π\u0012/3\u0013𝐢"}; + assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Polar, 1); + } QUIZ_CASE(equation_and_symbolic_computation) { diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 437d613d8..3abc930e4 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -12,7 +12,10 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex @echo " using an USB cable and press at the same time the 6 key and the RESET" @echo " button on the back of your device." $(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done - $(Q) $(PYTHON) build/device/dfu.py -u $(word 2,$^) - $(Q) sleep 2 + $(eval DFU_SLAVE := $(shell $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11")) + $(Q) if [[ "$(DFU_SLAVE)" == *"0483:df11"* ]]; \ + then \ + $(PYTHON) build/device/dfu.py -u $(word 2,$^); \ + sleep 2; \ + fi $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) - diff --git a/escher/include/escher/palette.h b/escher/include/escher/palette.h index 5fd89df4c..91a3b1366 100644 --- a/escher/include/escher/palette.h +++ b/escher/include/escher/palette.h @@ -50,12 +50,10 @@ public: constexpr static KDColor Halogen = KDColor::RGB24(0x00debd); constexpr static KDColor ReactiveNonmetal = KDColor::RGB24(0x00ee00); constexpr static KDColor NobleGas = KDColor::RGB24(0x8baaff); - constexpr static KDColor TableLines = KDColor::RGB24(0x323532); - constexpr static KDColor AtomColor[] = { - Unknown, AlkaliMetal, AlkaliEarthMetal, Lanthanide, Actinide, TransitionMetal, - PostTransitionMetal, Metalloid, Halogen, ReactiveNonmetal, NobleGas + Unknown, AlkaliMetal, AlkaliEarthMetal, Lanthanide, Actinide, TransitionMetal, + PostTransitionMetal, Metalloid, Halogen, ReactiveNonmetal, NobleGas }; }; diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 27d8a7326..cf0b19826 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -345,11 +345,14 @@ Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record re return notifyFullnessToDelegate(); } overrideSizeAtPosition(p, newRecordSize); - overrideBaseNameWithExtensionAtPosition(p+sizeof(record_size_t), baseName, extension); + char * fullNamePosition = p + sizeof(record_size_t); + overrideBaseNameWithExtensionAtPosition(fullNamePosition, baseName, extension); + // Recompute the CRC32 + record = Record(fullNamePosition); notifyChangeToDelegate(record); m_lastRecordRetrieved = record; m_lastRecordRetrievedPointer = p; - return Record::ErrorStatus::None; + return Record::ErrorStatus::None; } return Record::ErrorStatus::RecordDoesNotExist; } diff --git a/ion/src/shared/unicode/utf8_helper.cpp b/ion/src/shared/unicode/utf8_helper.cpp index f8c6345fc..d69016075 100644 --- a/ion/src/shared/unicode/utf8_helper.cpp +++ b/ion/src/shared/unicode/utf8_helper.cpp @@ -5,7 +5,6 @@ namespace UTF8Helper { -static inline int minInt(int x, int y) { return x < y ? x : y; } static inline size_t minSizeT(size_t x, size_t y) { return x < y ? x : y; } int CountOccurrences(const char * s, CodePoint c) { @@ -110,7 +109,11 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo // Remove CodePoint c while (codePoint != UCodePointNull && bufferIndex < dstSize) { if (codePoint != c) { - int copySize = minInt(nextPointer - currentPointer, dstSize - bufferIndex); + int copySize = nextPointer - currentPointer; + if (copySize > dstSize - 1 - bufferIndex) { + // Copying the current code point to the buffer would overflow the buffer + break; + } memcpy(dst + bufferIndex, currentPointer, copySize); bufferIndex+= copySize; } @@ -118,7 +121,7 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo codePoint = decoder.nextCodePoint(); nextPointer = decoder.stringPosition(); } - *(dst + minInt(bufferIndex, dstSize - 1)) = 0; + *(dst + bufferIndex) = 0; } void RemoveCodePoint(char * buffer, CodePoint c, const char * * pointerToUpdate, const char * stoppingPosition) { diff --git a/ion/test/utf8_helper.cpp b/ion/test/utf8_helper.cpp index 6398df9cd..cd1d1f36b 100644 --- a/ion/test/utf8_helper.cpp +++ b/ion/test/utf8_helper.cpp @@ -87,6 +87,21 @@ QUIZ_CASE(ion_utf8_copy_and_remove_code_point) { c = UCodePointLatinLetterSmallCapitalE; result = "124"; assert_copy_and_remove_code_point_gives(buffer, bufferSize, s, c, result); + + // The buffer size is to small to hold s + s = "1234ᴇ"; + c = '5'; + result = "1234"; // "1234ᴇ" size is 7 + assert_copy_and_remove_code_point_gives(buffer, 6, s, c, result); + assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result); + result = "1234ᴇ"; + assert_copy_and_remove_code_point_gives(buffer, 8, s, c, result); + + s = "1234ᴇ"; + c = '4'; + result = "123ᴇ"; + assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result); + } void assert_remove_code_point_gives(char * buffer, CodePoint c, const char * * indexToUpdate, const char * stoppingPosition, const char * indexToUpdateResult, const char * result) { diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 13cdd7aad..b1abfac91 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -221,11 +221,11 @@ public: * (For instance, in Polar mode, they return an expression of the form * r*e^(i*th) reduced and approximated.) */ static Expression ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true); - Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true); + Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation = true); static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true); void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true); - Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target = ExpressionNode::ReductionTarget::SystemForApproximation); Expression mapOnMatrixFirstChild(ExpressionNode::ReductionContext reductionContext); static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context * context); @@ -342,7 +342,7 @@ protected: * Warning: this must be called on reduced expressions */ Expression makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext reductionContext); - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return node()->denominator(context, complexFormat, angleUnit); } + Expression denominator(ExpressionNode::ReductionContext reductionContext) const { return node()->denominator(reductionContext); } Expression shallowReduce(ExpressionNode::ReductionContext reductionContext) { return node()->shallowReduce(reductionContext); } Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext) { return node()->shallowBeautify(reductionContext); } Expression deepBeautify(ExpressionNode::ReductionContext reductionContext); diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index 0dd39c7fd..96d8ebe96 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -110,7 +110,15 @@ public: /* Properties */ enum class ReductionTarget { - System = 0, + /* Minimal reduction: this at least reduces rationals operations as + * "1-0.3-0.7 --> 0" */ + SystemForApproximation = 0, + /* Expansion of Newton multinome to be able to identify polynoms */ + SystemForAnalysis, + /* Additional features as: + * - factorizing on a common denominator + * - turning complex expression into the form a+ib + * - identifying tangent in cos/sin polynoms ... */ User }; enum class Sign { @@ -196,7 +204,7 @@ public: /*!*/ virtual Expression shallowReduce(ReductionContext reductionContext); /*!*/ virtual Expression shallowBeautify(ReductionContext reductionContext); /* Return a clone of the denominator part of the expression */ - /*!*/ virtual Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + /*!*/ virtual Expression denominator(ExpressionNode::ReductionContext reductionContext) const; /* LayoutShape is used to check if the multiplication sign can be omitted between two expressions. It depends on the "layout syle" of the on the right of the left expression */ enum class LayoutShape { Decimal, diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index bd38e44c3..09d2e9d2c 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -47,7 +47,7 @@ private: // Simplification Expression shallowReduce(ReductionContext reductionContext) override; Expression shallowBeautify(ReductionContext reductionContext) override; - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; + Expression denominator(ExpressionNode::ReductionContext reductionContext) const override; // Approximation template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat) { @@ -82,7 +82,7 @@ public: Expression setSign(ExpressionNode::Sign s, ExpressionNode::ReductionContext reductionContext); Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext); - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Expression denominator(ExpressionNode::ReductionContext reductionContext) const; void sortChildrenInPlace(NAryExpressionNode::ExpressionOrder order, Context * context, bool canBeInterrupted) { NAryExpression::sortChildrenInPlace(order, context, false, canBeInterrupted); } @@ -94,8 +94,8 @@ private: void mergeInChildByFactorizingBase(int i, Expression e, ExpressionNode::ReductionContext reductionContext); void factorizeExponent(int i, int j, ExpressionNode::ReductionContext reductionContext); Expression distributeOnOperandAtIndex(int index, ExpressionNode::ReductionContext reductionContext); - void addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); - void factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + void addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext); + void factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext); static bool HaveSameNonNumeralFactors(const Expression & e1, const Expression & e2); static bool TermsHaveIdenticalBase(const Expression & e1, const Expression & e2); static bool TermsHaveIdenticalExponent(const Expression & e1, const Expression & e2); @@ -104,7 +104,7 @@ private: static const Expression CreateExponent(Expression e); /* Warning: mergeNegativePower doesnot always return a multiplication: * *(b^-1,c^-1) -> (bc)^-1 */ - Expression mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Expression mergeNegativePower(ExpressionNode::ReductionContext reductionContext); static inline const Expression Base(const Expression e); }; diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 76bcb6c71..06097d962 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -52,7 +52,7 @@ private: LayoutShape rightLayoutShape() const override { return LayoutShape::RightOfPower; } int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; + Expression denominator(ReductionContext reductionContext) const override; // Evaluation template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat); template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d, Preferences::ComplexFormat complexFormat); @@ -82,7 +82,7 @@ private: constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25; // Simplification - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Expression denominator(ExpressionNode::ReductionContext reductionContext) const; Expression simplifyPowerPower(ExpressionNode::ReductionContext reductionContext); Expression simplifyPowerMultiplication(ExpressionNode::ReductionContext reductionContext); diff --git a/poincare/include/poincare/rational.h b/poincare/include/poincare/rational.h index 4ec5e0b1a..84da9e017 100644 --- a/poincare/include/poincare/rational.h +++ b/poincare/include/poincare/rational.h @@ -57,7 +57,7 @@ private: Expression shallowBeautify(ReductionContext reductionContext) override; LayoutShape leftLayoutShape() const override { assert(!m_negative); return isInteger() ? LayoutShape::Integer : LayoutShape::Fraction; }; Expression setSign(Sign s, ReductionContext reductionContext) override; - Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; + Expression denominator(ReductionContext reductionContext) const override; bool m_negative; uint8_t m_numberOfDigitsNumerator; uint8_t m_numberOfDigitsDenominator; diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index 41b314ed2..182a06cca 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -354,7 +354,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte Multiplication commonDenominator = Multiplication::Builder(); for (int i = 0; i < numberOfChildren(); i++) { Expression childI = childAtIndex(i); - Expression currentDenominator = childI.denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + Expression currentDenominator = childI.denominator(reductionContext); if (!currentDenominator.isUninitialized()) { if (currentDenominator.recursivelyMatches(Expression::IsRandom, reductionContext.context(), true)) { // Remove "random" factors @@ -364,7 +364,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte continue; } // Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator); - commonDenominator.addMissingFactors(currentDenominator, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + commonDenominator.addMissingFactors(currentDenominator, reductionContext); } } if (commonDenominator.numberOfChildren() == 0) { diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 611aa6b86..c7a5dd767 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -46,7 +46,11 @@ Evaluation EqualNode::templatedApproximate(Context * context, Preferences::Co Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()); - return sub.reduce(context, complexFormat, angleUnit); + /* When reducing the equation, we specify the reduction target to be + * SystemForAnalysis. This enables to expand Newton multinom to be able to + * detect polynom correctly ("(x+2)^2" in this form won't be detected + * unless expanded). */ + return sub.reduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForAnalysis); } Expression Equal::shallowReduce() { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index cce1c46aa..5e3652b2a 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -519,7 +519,7 @@ Expression Expression::ParseAndSimplify(const char * text, Context * context, Pr if (exp.isUninitialized()) { return Undefined::Builder(); } - exp = exp.simplify(context, complexFormat, angleUnit, symbolicSimplification); + exp = exp.simplify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicSimplification); /* simplify might have been interrupted, in which case the resulting * expression is uninitialized, so we need to check that. */ if (exp.isUninitialized()) { @@ -547,9 +547,9 @@ void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression * } } -Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) { +Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation) { sSimplificationHasBeenInterrupted = false; - ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation); + ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, symbolicComputation); Expression e = deepReduce(c); if (!sSimplificationHasBeenInterrupted) { e = e.deepBeautify(c); @@ -618,7 +618,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre Expression e = clone().deepReduce(userReductionContext); if (sSimplificationHasBeenInterrupted) { sSimplificationHasBeenInterrupted = false; - ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation); + ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation); e = deepReduce(systemReductionContext); } *simplifiedExpression = Expression(); @@ -730,9 +730,9 @@ Expression Expression::angleUnitToRadian(Preferences::AngleUnit angleUnit) { return *this; } -Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { +Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { sSimplificationHasBeenInterrupted = false; - return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, true)); + return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, true)); } Expression Expression::deepReduce(ExpressionNode::ReductionContext reductionContext) { @@ -856,8 +856,13 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre Expression norm; Expression exp; if (!isOneRa || isZeroTb) { - assert(!isNegativeRa); // norm cannot be negative - norm = ra; + /* Norm cannot be negative but can be preceded by a negative sign (for + * instance "-log(0.3)") which would lead to isNegativeRa = True. */ + if (isNegativeRa) { + norm = Opposite::Builder(ra); + } else { + norm = ra; + } } if (!isZeroRa && !isZeroTb) { Expression arg; diff --git a/poincare/src/expression_node.cpp b/poincare/src/expression_node.cpp index 0b4a4219d..c69dfadd2 100644 --- a/poincare/src/expression_node.cpp +++ b/poincare/src/expression_node.cpp @@ -129,7 +129,7 @@ void ExpressionNode::setChildrenInPlace(Expression other) { Expression(this).defaultSetChildrenInPlace(other); } -Expression ExpressionNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Expression ExpressionNode::denominator(ReductionContext reductionContext) const { return Expression(); } diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 6a206b345..757981199 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -115,7 +115,7 @@ void Matrix::addChildrenAsRowInPlace(TreeHandle t, int i) { int Matrix::rank(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace) { Matrix m = inPlace ? *this : clone().convert(); - ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); + ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation); m = m.rowCanonize(systemReductionContext, nullptr); int rank = m.numberOfRows(); int i = rank-1; diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 8596dfcb4..b294a4d62 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -219,8 +219,8 @@ Expression MultiplicationNode::shallowBeautify(ReductionContext reductionContext return Multiplication(this).shallowBeautify(reductionContext); } -Expression MultiplicationNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return Multiplication(this).denominator(context, complexFormat, angleUnit); +Expression MultiplicationNode::denominator(ReductionContext reductionContext) const { + return Multiplication(this).denominator(reductionContext); } /* Multiplication */ @@ -306,7 +306,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu /* Step 2: Merge negative powers: a*b^(-1)*c^(-pi)*d = a*(b*c^pi)^(-1) * This also turns 2/3*a into 2*a*3^(-1) */ - Expression thisExp = mergeNegativePower(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + Expression thisExp = mergeNegativePower(reductionContext); if (thisExp.type() == ExpressionNode::Type::Power) { return thisExp.shallowBeautify(reductionContext); } @@ -339,13 +339,13 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu return thisExp; } -Expression Multiplication::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Expression Multiplication::denominator(ExpressionNode::ReductionContext reductionContext) const { // Merge negative power: a*b^-1*c^(-Pi)*d = a*(b*c^Pi)^-1 // WARNING: we do not want to change the expression but to create a new one. Multiplication thisClone = clone().convert(); - Expression e = thisClone.mergeNegativePower(context, complexFormat, angleUnit); + Expression e = thisClone.mergeNegativePower(reductionContext); if (e.type() == ExpressionNode::Type::Power) { - return e.denominator(context, complexFormat, angleUnit); + return e.denominator(reductionContext); } else { assert(e.type() == ExpressionNode::Type::Multiplication); for (int i = 0; i < e.numberOfChildren(); i++) { @@ -511,7 +511,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext for (int j = i+1; j < numberOfChildren(); j++) { Expression o2 = childAtIndex(j); if (Base(o2).type() == ExpressionNode::Type::Cosine && TermHasNumeralExponent(o2) && Base(o2).childAtIndex(0).isIdenticalTo(x)) { - factorizeSineAndCosine(i, j, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + factorizeSineAndCosine(i, j, reductionContext); break; } } @@ -738,10 +738,10 @@ Expression Multiplication::distributeOnOperandAtIndex(int i, ExpressionNode::Red return a.shallowReduce(reductionContext); // Order terms, put under a common denominator if needed } -void Multiplication::addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { +void Multiplication::addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext) { if (factor.type() == ExpressionNode::Type::Multiplication) { for (int j = 0; j < factor.numberOfChildren(); j++) { - addMissingFactors(factor.childAtIndex(j), context, complexFormat, angleUnit); + addMissingFactors(factor.childAtIndex(j), reductionContext); } return; } @@ -762,7 +762,6 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre if (factor.type() != ExpressionNode::Type::Rational) { /* If factor is not a rational, we merge it with the child of identical * base if any. Otherwise, we add it as an new child. */ - ExpressionNode::ReductionContext reductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); for (int i = 0; i < numberOfChildren(); i++) { if (TermsHaveIdenticalBase(childAtIndex(i), factor)) { Expression sub = Subtraction::Builder(CreateExponent(childAtIndex(i)), CreateExponent(factor)).deepReduce(reductionContext); @@ -783,10 +782,10 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre } } addChildAtIndexInPlace(factor.clone(), 0, numberOfChildren()); - sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true); } -void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { +void Multiplication::factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext) { /* This function turn sin(x)^p * cos(x)^q into either: * - tan(x)^p*cos(x)^(p+q) if |p|<|q| * - tan(x)^(-q)*sin(x)^(p+q) otherwise */ @@ -802,7 +801,6 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre Number absP = p.clone().convert().setSign(ExpressionNode::Sign::Positive); Number absQ = q.clone().convert().setSign(ExpressionNode::Sign::Positive); Expression tan = Tangent::Builder(x.clone()); - ExpressionNode::ReductionContext userReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); if (Number::NaturalOrder(absP, absQ) < 0) { // Replace sin(x) by tan(x) or sin(x)^p by tan(x)^p if (p.isRationalOne()) { @@ -810,19 +808,19 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre } else { replaceChildAtIndexInPlace(i, Power::Builder(tan, p)); } - childAtIndex(i).shallowReduce(userReductionContext); + childAtIndex(i).shallowReduce(reductionContext); // Replace cos(x)^q by cos(x)^(p+q) replaceChildAtIndexInPlace(j, Power::Builder(Base(childAtIndex(j)), sumPQ)); - childAtIndex(j).shallowReduce(userReductionContext); + childAtIndex(j).shallowReduce(reductionContext); } else { // Replace cos(x)^q by tan(x)^(-q) Expression newPower = Power::Builder(tan, Number::Multiplication(q, Rational::Builder(-1))); - newPower.childAtIndex(1).shallowReduce(userReductionContext); + newPower.childAtIndex(1).shallowReduce(reductionContext); replaceChildAtIndexInPlace(j, newPower); - newPower.shallowReduce(userReductionContext); + newPower.shallowReduce(reductionContext); // Replace sin(x)^p by sin(x)^(p+q) replaceChildAtIndexInPlace(i, Power::Builder(Base(childAtIndex(i)), sumPQ)); - childAtIndex(i).shallowReduce(userReductionContext); + childAtIndex(i).shallowReduce(reductionContext); } } @@ -876,7 +874,7 @@ bool Multiplication::TermHasNumeralExponent(const Expression & e) { return false; } -Expression Multiplication::mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { +Expression Multiplication::mergeNegativePower(ExpressionNode::ReductionContext reductionContext) { /* mergeNegativePower groups all factors that are power of form a^(-b) together * for instance, a^(-1)*b^(-c)*c = c*(a*b^c)^(-1) */ Multiplication m = Multiplication::Builder(); @@ -895,7 +893,7 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co while (i < numberOfChildren()) { if (childAtIndex(i).type() == ExpressionNode::Type::Power) { Expression p = childAtIndex(i); - Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor( ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User)); + Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext); if (!positivePIndex.isUninitialized()) { // Remove a^(-b) from the Multiplication removeChildAtIndexInPlace(i); @@ -913,10 +911,10 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co if (m.numberOfChildren() == 0) { return *this; } - m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true); + m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true); Power p = Power::Builder(m.squashUnaryHierarchyInPlace(), Rational::Builder(-1)); addChildAtIndexInPlace(p, 0, numberOfChildren()); - sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true); return squashUnaryHierarchyInPlace(); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 77bba4c05..d94699392 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -211,8 +211,8 @@ int PowerNode::simplificationOrderSameType(const ExpressionNode * e, bool ascend return SimplificationOrder(childAtIndex(1), e->childAtIndex(1), ascending, canBeInterrupted); } -Expression PowerNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return Power(this).denominator(context, complexFormat, angleUnit); +Expression PowerNode::denominator(ReductionContext reductionContext) const { + return Power(this).denominator(reductionContext); } // Evaluation @@ -743,8 +743,12 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex } /* Step 13: (a0+a1+...am)^n with n integer - * -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) */ + * -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) + * We don't apply this rule when the target is the SystemForApproximation. + * Indeed, developing the multinome is likely to increase the numbers of + * operations and lead to precision loss. */ if (!letPowerAtRoot + && reductionContext.target() != ExpressionNode::ReductionTarget::SystemForApproximation && indexType == ExpressionNode::Type::Rational && !static_cast(index).signedIntegerNumerator().isZero() && static_cast(index).isInteger() @@ -845,7 +849,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionContext) { // Step 1: X^-y -> 1/(X->shallowBeautify)^y - Expression p = denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + Expression p = denominator(reductionContext); // If the denominator is initialized, the index of the power is of form -y if (!p.isUninitialized()) { Division d = Division::Builder(Rational::Builder(1), p); @@ -867,12 +871,12 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont return result; } - /* Optional Step 3: if the ReductionTarget is the System, turn a^(p/q) into - * (root(a, q))^p + /* Optional Step 3: if the ReductionTarget is the SystemForApproximation or + * SystemForAnalysis, turn a^(p/q) into (root(a, q))^p * Indeed, root(a, q) can have a real root which is not the principale angle * but that we want to return in real complex format. This special case is * handled in NthRoot approximation but not in Power approximation. */ - if (reductionContext.target() == ExpressionNode::ReductionTarget::System && childAtIndex(1).type() == ExpressionNode::Type::Rational) { + if (reductionContext.target() != ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) { Integer p = childAtIndex(1).convert().signedIntegerNumerator(); Integer q = childAtIndex(1).convert().integerDenominator(); Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q)); @@ -887,11 +891,11 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont // Private // Simplification -Expression Power::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Expression Power::denominator(ExpressionNode::ReductionContext reductionContext) const { // Clone the power Expression clone = Power::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()); // If the power is of form x^(-y), denominator should be x^y - Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User)); + Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext); if (!positiveIndex.isUninitialized()) { // if y was -1, clone is now x^1, denominator is then only x // we cannot shallowReduce the clone as it is not attached to its parent yet diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp index cd9b99beb..b04ed48a9 100644 --- a/poincare/src/rational.cpp +++ b/poincare/src/rational.cpp @@ -145,7 +145,7 @@ Expression RationalNode::shallowBeautify(ReductionContext reductionContext) { return Rational(this).shallowBeautify(); } -Expression RationalNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { +Expression RationalNode::denominator(ReductionContext reductionContext) const { return Rational(this).denominator(); } diff --git a/poincare/test/expression_properties.cpp b/poincare/test/expression_properties.cpp index d138e8b6a..e2f7697e9 100644 --- a/poincare/test/expression_properties.cpp +++ b/poincare/test/expression_properties.cpp @@ -311,7 +311,7 @@ QUIZ_CASE(poincare_preperties_get_variables) { void assert_reduced_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Cartesian, Preferences::AngleUnit angleUnit = Radian) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression, false); - e = e.reduce(&globalContext, complexFormat, angleUnit); + e = e.reduce(&globalContext, complexFormat, angleUnit, SystemForAnalysis); Expression coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients]; int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, &globalContext, complexFormat, Radian); for (int i = 0; i <= d; i++) { diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index a35e01306..d79d8b27d 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -54,11 +54,11 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes return result; } -void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) { +void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, ExpressionNode::ReductionTarget target) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression, false); quiz_assert_print_if_failure(!e.isUninitialized(), expression); - e = e.simplify(&globalContext, complexFormat, angleUnit); + e = e.simplify(&globalContext, complexFormat, angleUnit, target); quiz_assert_print_if_failure(!(e.isUninitialized()), expression); } @@ -68,7 +68,7 @@ void assert_parsed_expression_simplify_to(const char * expression, const char * if (target == ExpressionNode::ReductionTarget::User) { copy.simplifyAndApproximate(©, nullptr, context, complexFormat, angleUnit, symbolicComputation); } else { - copy = copy.simplify(context, complexFormat, angleUnit, symbolicComputation); + copy = copy.simplify(context, complexFormat, angleUnit, target, symbolicComputation); } if (copy.isUninitialized()) { return e; @@ -81,7 +81,7 @@ template void assert_expression_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits; - assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::System, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) { + assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::SystemForApproximation, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) { return e.approximate(context, complexFormat, angleUnit); }, numberOfDigits); } diff --git a/poincare/test/helper.h b/poincare/test/helper.h index ea742d44b..7a110e016 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -5,7 +5,8 @@ const char * MaxIntegerString(); // (2^32)^k_maxNumberOfDigits-1 const char * OverflowedIntegerString(); // (2^32)^k_maxNumberOfDigits const char * BigOverflowedIntegerString(); // OverflowedIntegerString with a 2 on first digit -constexpr Poincare::ExpressionNode::ReductionTarget System = Poincare::ExpressionNode::ReductionTarget::System; +constexpr Poincare::ExpressionNode::ReductionTarget SystemForApproximation = Poincare::ExpressionNode::ReductionTarget::SystemForApproximation; +constexpr Poincare::ExpressionNode::ReductionTarget SystemForAnalysis = Poincare::ExpressionNode::ReductionTarget::SystemForAnalysis; constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionNode::ReductionTarget::User; constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree; constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian; @@ -30,7 +31,7 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes // Simplification -void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian); +void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::ExpressionNode::ReductionTarget target = User); void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = User, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, bool symbolicComputation = true); diff --git a/poincare/test/print_float.cpp b/poincare/test/print_float.cpp index 0403bacf8..b5ea82238 100644 --- a/poincare/test/print_float.cpp +++ b/poincare/test/print_float.cpp @@ -108,6 +108,9 @@ QUIZ_CASE(assert_print_floats) { assert_float_prints_to(10000000000000000000000000000.0, "1ᴇ28", DecimalMode, 14); assert_float_prints_to(10000000000000000000000000000.0, "10ᴇ27", EngineeringMode, 14); + // This used to crash on web platform + assert_float_prints_to(1000.0, "1000", DecimalMode, 7); + assert_float_prints_to(1000000.0f, "1ᴇ6", ScientificMode, 7); assert_float_prints_to(1000000.0f, "1000000", DecimalMode, 7); assert_float_prints_to(1000000.0f, "1ᴇ6", EngineeringMode, 7); diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 2699ddc57..86a546098 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -941,6 +941,7 @@ QUIZ_CASE(poincare_simplification_complex_format) { assert_parsed_expression_simplify_to("[[1,√(-1)]]", "[[1,ℯ^\u0012π/2×𝐢\u0013]]", User, Radian, Polar); assert_parsed_expression_simplify_to("atan(2)", "atan(2)", User, Radian, Polar); assert_parsed_expression_simplify_to("atan(-2)", "atan(2)×ℯ^\u0012π×𝐢\u0013", User, Radian, Polar); + assert_parsed_expression_simplify_to("cos(42π)", "-cos(42×π)×ℯ^\x12π×𝐢\x13", User, Degree, Polar); // User defined variable assert_parsed_expression_simplify_to("a", "a", User, Radian, Polar); @@ -959,36 +960,54 @@ QUIZ_CASE(poincare_simplification_complex_format) { } QUIZ_CASE(poincare_simplification_reduction_target) { - assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", System); + // Factorize on the same denominator only for ReductionTarget = User + assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForAnalysis); + assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForApproximation); assert_parsed_expression_simplify_to("1/π+1/x", "\u0012x+π\u0013/\u0012π×x\u0013", User); - assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", System); + // Display in the form a+ib only for ReductionTarget = User + assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForAnalysis); + assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForApproximation); assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/2-1/2×𝐢", User); - assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", System); + // Replace sin/cos-->tan for ReductionTarget = User + assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForAnalysis); + assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForApproximation); assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "tan(x)/cos(x)", User); - assert_parsed_expression_simplify_to("x^0", "x^0", System); + // Apply rule x^0 --> 1 for ReductionTarget = User (because this is not always true) + assert_parsed_expression_simplify_to("x^0", "x^0", SystemForAnalysis); + assert_parsed_expression_simplify_to("x^0", "x^0", SystemForApproximation); assert_parsed_expression_simplify_to("x^0", "1", User); + assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", SystemForApproximation); + assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User); - assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", System); + // Apply rule x^(2/3) --> root(x,3)^2 for ReductionTarget = System + assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForApproximation); + assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForAnalysis); assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", User); - assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System); - assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System); - assert_parsed_expression_simplify_to("x^2", "x^2", System); + assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForApproximation); + assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForAnalysis); + assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", User); + assert_parsed_expression_simplify_to("x^2", "x^2", SystemForApproximation); assert_parsed_expression_simplify_to("x^2", "x^2", User); - assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", System); + // Remove square root at denominator for ReductionTarget = User + assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", SystemForApproximation); assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "√(3)-√(2)", User); - assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", System); + // Always reduce sign for ReductionTarget = User + assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", SystemForApproximation); assert_parsed_expression_simplify_to("sign(abs(x))", "1", User); - assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", System); + // Apply rule atan(1/x)-> (π×sign(x)-2×atan(x))/2 for ReductionTarget = User (as it is not always true) + assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", SystemForApproximation); assert_parsed_expression_simplify_to("atan(1/x)", "\u0012π×sign(x)-2×atan(x)\u0013/2", User); - assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", System); - assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User); + // Expand multinome when ReductionTarget is not SystemForApproximation as it increases precision loss + assert_parsed_expression_simplify_to("(2+x)^2", "(x+2)^2", SystemForApproximation); + assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", SystemForAnalysis); + assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", User); } QUIZ_CASE(poincare_simplification_mix) { diff --git a/themes b/themes new file mode 160000 index 000000000..c5a10002c --- /dev/null +++ b/themes @@ -0,0 +1 @@ +Subproject commit c5a10002c094c2a981aed0b9132565ae3c70bfae