diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp index a9b14f194..e0a4aada5 100644 --- a/apps/solver/equation.cpp +++ b/apps/solver/equation.cpp @@ -19,58 +19,42 @@ bool Equation::containsIComplex(Context * context) const { return expressionClone().recursivelyMatches([](const Expression e, Context * context) { return e.type() == ExpressionNode::Type::Constant && static_cast(e).isIComplex(); }, context); } -Expression Equation::Model::standardForm(const Storage::Record * record, Context * context, bool replaceFunctionsButNotSymbols) const { - Expression * returnedExpression = replaceFunctionsButNotSymbols ? &m_standardFormWithReplacedFunctionsButNotSymbols : &m_standardFormWithReplacedFunctionsAndSymbols; - if (returnedExpression->isUninitialized()) { - Expression expressionInputWithoutFunctions = Expression::ExpressionWithoutSymbols(expressionClone(record), context, replaceFunctionsButNotSymbols); - if (expressionInputWithoutFunctions.isUninitialized()) { - // The expression is circularly-defined - expressionInputWithoutFunctions = Undefined::Builder(); - } - - EmptyContext emptyContext; - Context * contextToUse = replaceFunctionsButNotSymbols ? &emptyContext : context; - - // Reduce the expression - Expression expressionRed = expressionInputWithoutFunctions.clone(); - PoincareHelpers::Simplify(&expressionRed, contextToUse, ExpressionNode::ReductionTarget::SystemForApproximation); - // simplify might return an uninitialized Expression if interrupted - if (expressionRed.isUninitialized()) { - expressionRed = expressionInputWithoutFunctions; - } - - if (expressionRed.type() == ExpressionNode::Type::Unreal) { - *returnedExpression = Unreal::Builder(); - } else if (expressionRed.recursivelyMatches( - [](const Expression e, Context * context) { - return e.type() == ExpressionNode::Type::Undefined || e.type() == ExpressionNode::Type::Infinity || Expression::IsMatrix(e, context); - }, - contextToUse)) - { - *returnedExpression = Undefined::Builder(); - } else if (expressionRed.type() == ExpressionNode::Type::Equal) { - Preferences * preferences = Preferences::sharedPreferences(); - *returnedExpression = static_cast(expressionRed).standardEquation(contextToUse, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionInputWithoutFunctions, contextToUse), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat()); - } else { - assert(expressionRed.type() == ExpressionNode::Type::Rational && static_cast(expressionRed).isOne()); - // The equality was reduced which means the equality was always true. - *returnedExpression = Rational::Builder(0); - } - if (!m_standardFormWithReplacedFunctionsButNotSymbols.isUninitialized() && !m_standardFormWithReplacedFunctionsAndSymbols.isUninitialized()) { - // Do not keep two equal expressions - if (m_standardFormWithReplacedFunctionsButNotSymbols.isIdenticalTo(m_standardFormWithReplacedFunctionsAndSymbols)) { - m_standardFormWithReplacedFunctionsButNotSymbols = m_standardFormWithReplacedFunctionsAndSymbols; - } - } +Expression Equation::Model::standardForm(const Storage::Record * record, Context * context, bool replaceFunctionsButNotSymbols, ExpressionNode::ReductionTarget reductionTarget) const { + Expression returnedExpression = Expression(); + Expression expressionInputWithoutFunctions = Expression::ExpressionWithoutSymbols(expressionClone(record), context, replaceFunctionsButNotSymbols); + if (expressionInputWithoutFunctions.isUninitialized()) { + // The expression is circularly-defined + expressionInputWithoutFunctions = Undefined::Builder(); } - return *returnedExpression; -} + EmptyContext emptyContext; + Context * contextToUse = replaceFunctionsButNotSymbols ? &emptyContext : context; -void Equation::Model::tidy() const { - ExpressionModel::tidy(); - // Free the pool of the m_standardForm - m_standardFormWithReplacedFunctionsAndSymbols = Expression(); - m_standardFormWithReplacedFunctionsButNotSymbols = Expression(); + // Reduce the expression + Expression expressionRed = expressionInputWithoutFunctions.clone(); + PoincareHelpers::Simplify(&expressionRed, contextToUse, reductionTarget); + + // simplify might return an uninitialized Expression if interrupted + if (expressionRed.isUninitialized()) { + expressionRed = expressionInputWithoutFunctions; + } + if (expressionRed.type() == ExpressionNode::Type::Unreal) { + returnedExpression = Unreal::Builder(); + } else if (expressionRed.recursivelyMatches( + [](const Expression e, Context * context) { + return e.type() == ExpressionNode::Type::Undefined || e.type() == ExpressionNode::Type::Infinity || Expression::IsMatrix(e, context); + }, + contextToUse)) + { + returnedExpression = Undefined::Builder(); + } else if (expressionRed.type() == ExpressionNode::Type::Equal) { + Preferences * preferences = Preferences::sharedPreferences(); + returnedExpression = static_cast(expressionRed).standardEquation(contextToUse, Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), expressionInputWithoutFunctions, contextToUse), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat(), reductionTarget); + } else { + assert(expressionRed.type() == ExpressionNode::Type::Rational && static_cast(expressionRed).isOne()); + // The equality was reduced which means the equality was always true. + returnedExpression = Rational::Builder(0); + } + return returnedExpression; } void * Equation::Model::expressionAddress(const Ion::Storage::Record * record) const { diff --git a/apps/solver/equation.h b/apps/solver/equation.h index 549ab6998..a43db56c8 100644 --- a/apps/solver/equation.h +++ b/apps/solver/equation.h @@ -11,19 +11,16 @@ public: bool shouldBeClearedBeforeRemove() override { return false; } - Poincare::Expression standardForm(Poincare::Context * context, bool replaceFunctionsButNotSymbols) const { return m_model.standardForm(this, context, replaceFunctionsButNotSymbols); } + Poincare::Expression standardForm(Poincare::Context * context, bool replaceFunctionsButNotSymbols, Poincare::ExpressionNode::ReductionTarget reductionTarget) const { return m_model.standardForm(this, context, replaceFunctionsButNotSymbols, reductionTarget); } bool containsIComplex(Poincare::Context * context) const; private: class Model : public Shared::ExpressionModel { public: - Poincare::Expression standardForm(const Ion::Storage::Record * record, Poincare::Context * context, bool replaceFunctionsButNotSymbols) const; - void tidy() const override; + Poincare::Expression standardForm(const Ion::Storage::Record * record, Poincare::Context * context, bool replaceFunctionsButNotSymbols, Poincare::ExpressionNode::ReductionTarget reductionTarget) const; private: void * expressionAddress(const Ion::Storage::Record * record) const override; size_t expressionSize(const Ion::Storage::Record * record) const override; - mutable Poincare::Expression m_standardFormWithReplacedFunctionsAndSymbols; - mutable Poincare::Expression m_standardFormWithReplacedFunctionsButNotSymbols; }; size_t metaDataSize() const override { return 0; } const Shared::ExpressionModel * model() const override { return &m_model; } diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 8517bef6c..1523ff4f0 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -89,28 +89,23 @@ void EquationStore::setIntervalBound(int index, double value) { } } -double EquationStore::approximateSolutionAtIndex(int i) { - assert(m_type == Type::Monovariable && i >= 0 && i < m_numberOfSolutions); - return m_approximateSolutions[i]; -} - -bool EquationStore::haveMoreApproximationSolutions(Context * context, bool solveWithoutContext) { - if (m_numberOfSolutions < k_maxNumberOfEquations) { - return false; - } - double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; - return !std::isnan(PoincareHelpers::NextRoot(modelForRecord(definedRecordAtIndex(0))->standardForm(context, solveWithoutContext), m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], context)); -} - void EquationStore::approximateSolve(Poincare::Context * context, bool shouldReplaceFunctionsButNotSymbols) { + m_hasMoreThanMaxNumberOfApproximateSolution = false; + Expression undevelopedExpression = modelForRecord(definedRecordAtIndex(0))->standardForm(context, shouldReplaceFunctionsButNotSymbols, ExpressionNode::ReductionTarget::SystemForApproximation); m_userVariablesUsed = !shouldReplaceFunctionsButNotSymbols; assert(m_variables[0][0] != 0 && m_variables[1][0] == 0); assert(m_type == Type::Monovariable); m_numberOfSolutions = 0; double start = m_intervalApproximateSolutions[0]; double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; - for (int i = 0; i < k_maxNumberOfApproximateSolutions; i++) { - m_approximateSolutions[i] = PoincareHelpers::NextRoot(modelForRecord(definedRecordAtIndex(0))->standardForm(context, shouldReplaceFunctionsButNotSymbols), m_variables[0], start, step, m_intervalApproximateSolutions[1], context); + double root; + for (int i = 0; i <= k_maxNumberOfApproximateSolutions; i++) { + root = PoincareHelpers::NextRoot(undevelopedExpression, m_variables[0], start, step, m_intervalApproximateSolutions[1], context); + if (i == k_maxNumberOfApproximateSolutions) { + m_hasMoreThanMaxNumberOfApproximateSolution = !isnan(root); + break; + } + m_approximateSolutions[i] = root; if (std::isnan(m_approximateSolutions[i])) { break; } else { @@ -131,6 +126,20 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool return e; } +/* Equations are solved according to the following procedure : + * 1) We develop the equations using the reduction target "SystemForAnalysis". + * This expands structures like Newton multinoms and allows us to detect + * polynoms afterwards. ("(x+2)^2" in this form is not detected but is if + * expanded). + * 2) We look for classic forms of equations for which we have algorithms + * that output the exact answer. If one is recognized in the input equation, + * the exact answer is given to the user. + * 3) If no classic form has been found in the developped form, we need to use + * numerical approximation. Therefore, to prevent precision losses, we work + * with the undevelopped form of the equation. Therefore we set reductionTarget + * to SystemForApproximation. Solutions are then numericaly approximated + * between the bounds provided by the user. */ + EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * context, bool replaceFunctionsButNotSymbols) { tidySolution(); @@ -143,6 +152,7 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex // TODO we look twice for variables but not the same, is there a way to not do the same work twice? m_userVariables[0][0] = 0; m_numberOfUserVariables = 0; + Expression simplifiedExpressions[k_maxNumberOfEquations]; for (int i = 0; i < numberOfDefinedModels(); i++) { Shared::ExpiringPointer eq = modelForRecord(definedRecordAtIndex(i)); @@ -150,12 +160,21 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex /* Start by looking for user variables, so that if we escape afterwards, we * know if it might be due to a user variable. */ if (m_numberOfUserVariables < Expression::k_maxNumberOfVariables) { - const Expression eWithSymbols = eq->standardForm(context, true); + const Expression eWithSymbols = eq->standardForm(context, true, ExpressionNode::ReductionTarget::SystemForAnalysis); + /* if replaceFunctionsButNotSymbols is true we can memoize the expressions + * for the rest of the function. Otherwise, we will memoize them at the + * next call to standardForm*/ + if (replaceFunctionsButNotSymbols == true) { + simplifiedExpressions[i] = eWithSymbols; + } int varCount = eWithSymbols.getVariables(context, [](const char * symbol, Poincare::Context * context) { return context->expressionTypeForIdentifier(symbol, strlen(symbol)) == Poincare::Context::SymbolAbstractType::Symbol; }, (char *)m_userVariables, Poincare::SymbolAbstract::k_maxNameSize, m_numberOfUserVariables); m_numberOfUserVariables = varCount < 0 ? Expression::k_maxNumberOfVariables : varCount; } - - const Expression e = eq->standardForm(context, replaceFunctionsButNotSymbols); // The standard form is memoized so there is no double computation even if replaceFunctionsButNotSymbols is true. + if (simplifiedExpressions[i].isUninitialized()) { + // The expression was not memoized before. + simplifiedExpressions[i] = eq->standardForm(context, replaceFunctionsButNotSymbols, ExpressionNode::ReductionTarget::SystemForAnalysis); + } + const Expression e = simplifiedExpressions[i]; if (e.isUninitialized() || e.type() == ExpressionNode::Type::Undefined || e.recursivelyMatches(Expression::IsMatrix, context, replaceFunctionsButNotSymbols ? ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions : ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition)) { return Error::EquationUndefined; } @@ -180,7 +199,7 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex bool isLinear = true; // Invalid the linear system if one equation is non-linear Preferences * preferences = Preferences::sharedPreferences(); for (int i = 0; i < numberOfDefinedModels(); i++) { - isLinear = isLinear && modelForRecord(definedRecordAtIndex(i))->standardForm(context, replaceFunctionsButNotSymbols).getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], context, updatedComplexFormat(context), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat(), replaceFunctionsButNotSymbols ? ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions : ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition); + isLinear = isLinear && simplifiedExpressions[i].getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], context, updatedComplexFormat(context), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat(), replaceFunctionsButNotSymbols ? ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions : ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition); if (!isLinear) { // TODO: should we clean pool allocated memory if the system is not linear #if 0 @@ -211,7 +230,7 @@ EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * contex // Step 3. Polynomial & Monovariable? assert(numberOfVariables == 1 && numberOfDefinedModels() == 1); Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; - int degree = modelForRecord(definedRecordAtIndex(0))->standardForm(context, replaceFunctionsButNotSymbols) + int degree = simplifiedExpressions[0] .getPolynomialReducedCoefficients( m_variables[0], polynomialCoefficients, diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 5cc28c6b1..362392091 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -74,14 +74,18 @@ public: /* Approximate resolution */ double intervalBound(int index) const; void setIntervalBound(int index, double value); - double approximateSolutionAtIndex(int i); + double approximateSolutionAtIndex(int i) { + assert(m_type == Type::Monovariable && i >= 0 && i < m_numberOfSolutions); + return m_approximateSolutions[i]; + } void approximateSolve(Poincare::Context * context, bool shouldReplaceFuncionsButNotSymbols); - bool haveMoreApproximationSolutions(Poincare::Context * context, bool solveWithoutContext); + bool haveMoreApproximationSolutions() { return m_hasMoreThanMaxNumberOfApproximateSolution; } void tidy() override; static constexpr int k_maxNumberOfExactSolutions = Poincare::Expression::k_maxNumberOfVariables > Poincare::Expression::k_maxPolynomialDegree + 1? Poincare::Expression::k_maxNumberOfVariables : Poincare::Expression::k_maxPolynomialDegree + 1; static constexpr int k_maxNumberOfApproximateSolutions = 10; + bool m_hasMoreThanMaxNumberOfApproximateSolution; static constexpr int k_maxNumberOfSolutions = k_maxNumberOfExactSolutions > k_maxNumberOfApproximateSolutions ? k_maxNumberOfExactSolutions : k_maxNumberOfApproximateSolutions; private: static constexpr double k_precision = 0.01; diff --git a/apps/solver/interval_controller.cpp b/apps/solver/interval_controller.cpp index 01655199e..9086e4686 100644 --- a/apps/solver/interval_controller.cpp +++ b/apps/solver/interval_controller.cpp @@ -45,7 +45,8 @@ IntervalController::IntervalController(Responder * parentResponder, InputEventHa FloatParameterController(parentResponder), m_contentView(&m_selectableTableView), m_intervalCell{}, - m_equationStore(equationStore) + m_equationStore(equationStore), + m_shouldReplaceFunctionsButNotSymbols(false) { m_selectableTableView.setTopMargin(0); m_okButton.setMessage(I18n::Message::ResolveEquation); @@ -102,7 +103,7 @@ bool IntervalController::textFieldDidFinishEditing(TextField * textField, const void IntervalController::buttonAction() { StackViewController * stack = stackController(); - m_equationStore->approximateSolve(textFieldDelegateApp()->localContext(), App::app()->solutionsController()->shouldReplaceFuncionsButNotSymbols()); + m_equationStore->approximateSolve(textFieldDelegateApp()->localContext(), m_shouldReplaceFunctionsButNotSymbols); stack->push(App::app()->solutionsControllerStack(), KDColorWhite, Palette::SubTab, Palette::SubTab); } diff --git a/apps/solver/interval_controller.h b/apps/solver/interval_controller.h index 5ece5e361..6e273284d 100644 --- a/apps/solver/interval_controller.h +++ b/apps/solver/interval_controller.h @@ -15,6 +15,7 @@ public: TELEMETRY_ID("Interval"); int numberOfRows() const override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; + void setShouldReplaceFuncionsButNotSymbols(bool shouldReplaceFunctionsButNotSymbols) { m_shouldReplaceFunctionsButNotSymbols = shouldReplaceFunctionsButNotSymbols; } private: HighlightCell * reusableParameterCell(int index, int type) override; int reusableParameterCellCount(int type) override; @@ -39,6 +40,7 @@ private: constexpr static int k_maxNumberOfCells = 2; MessageTableCellWithEditableText m_intervalCell[k_maxNumberOfCells]; EquationStore * m_equationStore; + bool m_shouldReplaceFunctionsButNotSymbols; }; } diff --git a/apps/solver/list_controller.cpp b/apps/solver/list_controller.cpp index 774ac498c..0c84e5555 100644 --- a/apps/solver/list_controller.cpp +++ b/apps/solver/list_controller.cpp @@ -190,7 +190,7 @@ void ListController::resolveEquations() { return; case EquationStore::Error::RequireApproximateSolution: { - App::app()->solutionsController()->setShouldReplaceFuncionsButNotSymbols(resultWithoutUserDefinedSymbols); + reinterpret_cast(App::app()->intervalController())->setShouldReplaceFuncionsButNotSymbols(resultWithoutUserDefinedSymbols); stackController()->push(App::app()->intervalController(), KDColorWhite, Palette::PurpleBright, Palette::PurpleBright); return; } @@ -198,7 +198,7 @@ void ListController::resolveEquations() { { assert(e == EquationStore::Error::NoError); StackViewController * stack = stackController(); - App::app()->solutionsController()->setShouldReplaceFuncionsButNotSymbols(resultWithoutUserDefinedSymbols); + reinterpret_cast(App::app()->intervalController())->setShouldReplaceFuncionsButNotSymbols(resultWithoutUserDefinedSymbols); stack->push(App::app()->solutionsControllerStack(), KDColorWhite, Palette::PurpleBright, Palette::PurpleBright); } } diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index 22443438b..8e5e985dc 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -81,8 +81,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt m_equationStore(equationStore), m_deltaCell(0.5f, 0.5f), m_delta2Layout(), - m_contentView(this), - m_shouldReplaceFunctionsButNotSymbols(false) + m_contentView(this) { m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); const char * deltaB = "Δ=b"; @@ -112,7 +111,7 @@ void SolutionsController::viewWillAppear() { bool requireWarning = false; if (m_equationStore->type() == EquationStore::Type::Monovariable) { m_contentView.setWarningMessages(I18n::Message::OnlyFirstSolutionsDisplayed0, I18n::Message::OnlyFirstSolutionsDisplayed1); - requireWarning = m_equationStore->haveMoreApproximationSolutions(App::app()->localContext(), m_shouldReplaceFunctionsButNotSymbols); + requireWarning = m_equationStore->haveMoreApproximationSolutions(); } else if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && m_equationStore->numberOfSolutions() == 1) { assert(Preferences::sharedPreferences()->complexFormat() == Preferences::ComplexFormat::Real); m_contentView.setWarningMessages(I18n::Message::PolynomeHasNoRealSolution0, I18n::Message::PolynomeHasNoRealSolution1); diff --git a/apps/solver/solutions_controller.h b/apps/solver/solutions_controller.h index f63465d94..b78d8b960 100644 --- a/apps/solver/solutions_controller.h +++ b/apps/solver/solutions_controller.h @@ -11,8 +11,6 @@ namespace Solver { class SolutionsController : public ViewController, public AlternateEmptyViewDefaultDelegate, public SelectableTableViewDataSource, public TableViewDataSource, public SelectableTableViewDelegate { public: SolutionsController(Responder * parentResponder, EquationStore * equationStore); - void setShouldReplaceFuncionsButNotSymbols(bool shouldReplaceFuncionsButNotSymbols) { m_shouldReplaceFunctionsButNotSymbols = shouldReplaceFuncionsButNotSymbols; } - bool shouldReplaceFuncionsButNotSymbols() const { return m_shouldReplaceFunctionsButNotSymbols; } /* ViewController */ const char * title() override; View * view() override { return &m_contentView; } @@ -111,7 +109,6 @@ private: EvenOddBufferTextCell m_approximateValueCells[k_numberOfApproximateValueCells]; MessageCell m_messageCells[k_numberOfMessageCells]; ContentView m_contentView; - bool m_shouldReplaceFunctionsButNotSymbols; }; } diff --git a/apps/solver/test/equation_store.cpp b/apps/solver/test/equation_store.cpp index 5a5534a6f..2b7424133 100644 --- a/apps/solver/test/equation_store.cpp +++ b/apps/solver/test/equation_store.cpp @@ -89,8 +89,12 @@ QUIZ_CASE(equation_solve) { // conj(x)*x+1 = 0 assert_solves_to_error("conj(x)*x+1=0", RequireApproximateSolution); assert_solves_numerically_to("conj(x)*x+1=0", -100, 100, {}); + + assert_solves_to_error("(x-10)^7=0", RequireApproximateSolution); + assert_solves_numerically_to("(x-10)^7=0", -100, 100, {10}); } + QUIZ_CASE(equation_solve_complex_real) { set_complex_format(Real); assert_solves_to("x+𝐢=0", "x=-𝐢"); // We still want complex solutions if the input has some complex value diff --git a/poincare/include/poincare/equal.h b/poincare/include/poincare/equal.h index 5f39dc772..b81e0b0f3 100644 --- a/poincare/include/poincare/equal.h +++ b/poincare/include/poincare/equal.h @@ -39,7 +39,7 @@ public: static Equal Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder({child0, child1}); } // For the equation A = B, create the reduced expression A-B - Expression standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat) const; + Expression standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::ReductionTarget reductionTarget) const; // Expression Expression shallowReduce(); }; diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 6ae1f00cc..1d5091d88 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -44,13 +44,9 @@ Evaluation EqualNode::templatedApproximate(Context * context, Preferences::Co } -Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat) const { +Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::ReductionTarget reductionTarget) const { Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()); - /* 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(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, unitFormat, ExpressionNode::ReductionTarget::SystemForAnalysis)); + return sub.reduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, unitFormat, reductionTarget)); } Expression Equal::shallowReduce() {