diff --git a/apps/calculation/additional_outputs/second_degree_list_controller.cpp b/apps/calculation/additional_outputs/second_degree_list_controller.cpp index e4cb2a0c7..26ad391ae 100644 --- a/apps/calculation/additional_outputs/second_degree_list_controller.cpp +++ b/apps/calculation/additional_outputs/second_degree_list_controller.cpp @@ -29,6 +29,12 @@ void SecondDegreeListController::setExpression(Poincare::Expression e) { Context * context = App::app()->localContext(); Preferences * preferences = Preferences::sharedPreferences(); + Poincare::ExpressionNode::ReductionContext reductionContext = Poincare::ExpressionNode::ReductionContext(context, + preferences->complexFormat(), preferences->angleUnit(), + GlobalPreferences::sharedGlobalPreferences()->unitFormat(), + ExpressionNode::ReductionTarget::SystemForApproximation, + ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, + Poincare::ExpressionNode::UnitConversion::Default); PoincareHelpers::Reduce(&m_expression, context, ExpressionNode::ReductionTarget::SystemForAnalysis); @@ -46,60 +52,108 @@ void SecondDegreeListController::setExpression(Poincare::Expression e) { Expression a = polynomialCoefficients[2]; Expression b = polynomialCoefficients[1]; Expression c = polynomialCoefficients[0]; - - bool aIsNotOne = !(a.type() == ExpressionNode::Type::Rational && static_cast(a).isOne()); - + Expression delta = Subtraction::Builder(Power::Builder(b.clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), a.clone(), c.clone())); PoincareHelpers::Simplify(&delta, context, ExpressionNode::ReductionTarget::SystemForApproximation); - Expression alpha = Opposite::Builder(Division::Builder(b.clone(), Multiplication::Builder(Rational::Builder(2), a.clone()))); - PoincareHelpers::Simplify(&alpha, context, ExpressionNode::ReductionTarget::User); + // Alpha is -b/2a, but because after we use -α, we immediately store -α=-(-b/2a)=b/2a. + Expression minusAlpha = Division::Builder(b.clone(), Multiplication::Builder(Rational::Builder(2), a.clone())); + PoincareHelpers::Reduce(&minusAlpha, context, ExpressionNode::ReductionTarget::SystemForApproximation); - Expression beta = Opposite::Builder(Division::Builder(delta.clone(), Multiplication::Builder(Rational::Builder(4), a.clone()))); - PoincareHelpers::Simplify(&beta, context, ExpressionNode::ReductionTarget::User); + // Same thing for β + Expression minusBeta = Division::Builder(delta.clone(), Multiplication::Builder(Rational::Builder(4), a.clone())); + PoincareHelpers::Reduce(&minusBeta, context, ExpressionNode::ReductionTarget::SystemForApproximation); + + enum MultiplicationTypeForA { + Nothing, + Minus, + Parenthesis, + Normal + }; + + MultiplicationTypeForA multiplicationTypeForA; + + if (a.type() == ExpressionNode::Type::Rational && static_cast(a).isOne()) { + multiplicationTypeForA = MultiplicationTypeForA::Nothing; + } + else if(a.type() == ExpressionNode::Type::Rational && static_cast(a).isMinusOne()){ + multiplicationTypeForA = MultiplicationTypeForA::Minus; + } + else if (a.type() == ExpressionNode::Type::Addition) { + multiplicationTypeForA = MultiplicationTypeForA::Parenthesis; + } + else { + multiplicationTypeForA = MultiplicationTypeForA::Normal; + } + + PoincareHelpers::Simplify(&a, context, ExpressionNode::ReductionTarget::User); /* - * Because when can't apply reduce or simplify to keep the canonised - * we must beautify the expression manually + * Because when can't apply reduce or simplify to keep the + * canonized form we must beautify the expression manually */ - Expression canonised; - if (alpha.type() == ExpressionNode::Type::Opposite) { - canonised = Addition::Builder(Symbol::Builder("x", strlen("x")), alpha.childAtIndex(0).clone()); - } - else { - canonised = Subtraction::Builder(Symbol::Builder("x", strlen("x")), alpha.clone()); - } - canonised = Power::Builder(Parenthesis::Builder(canonised.clone()), Rational::Builder(2)); - if (aIsNotOne) { - canonised = Multiplication::Builder(a.clone(), canonised.clone()); - } - if (beta.type() == ExpressionNode::Type::Opposite) { - canonised = Subtraction::Builder(canonised.clone(), beta.childAtIndex(0).clone()); - } - else { - canonised = Addition::Builder(canonised.clone(), beta.clone()); - } + Expression xMinusAlphaPowerTwo; + Expression alpha = getOppositeIfExists(minusAlpha, &reductionContext); + if (alpha.isUninitialized()) { + PoincareHelpers::Simplify(&minusAlpha, context, ExpressionNode::ReductionTarget::User); + xMinusAlphaPowerTwo = Power::Builder(Parenthesis::Builder(Addition::Builder(Symbol::Builder("x", strlen("x")), minusAlpha)), Rational::Builder(2)); + } + else { + PoincareHelpers::Simplify(&alpha, context, ExpressionNode::ReductionTarget::User); + xMinusAlphaPowerTwo = Power::Builder(Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), alpha)), Rational::Builder(2)); + } + + Expression xMinusAlphaPowerTwoWithFactor; + + switch (multiplicationTypeForA) + { + case MultiplicationTypeForA::Nothing: + xMinusAlphaPowerTwoWithFactor = xMinusAlphaPowerTwo; + break; + case MultiplicationTypeForA::Minus: + xMinusAlphaPowerTwoWithFactor = Multiplication::Builder(a.clone(), xMinusAlphaPowerTwo); + break; + case MultiplicationTypeForA::Parenthesis: + xMinusAlphaPowerTwoWithFactor = Multiplication::Builder(Parenthesis::Builder(a.clone()), xMinusAlphaPowerTwo); + break; + case MultiplicationTypeForA::Normal: + xMinusAlphaPowerTwoWithFactor = Multiplication::Builder(a.clone(), xMinusAlphaPowerTwo); + break; + default: + assert(false); + break; + } + + Expression canonized; + Expression beta = getOppositeIfExists(minusBeta, &reductionContext); + if (beta.isUninitialized()) { + PoincareHelpers::Simplify(&minusBeta, context, ExpressionNode::ReductionTarget::User); + canonized = Subtraction::Builder(xMinusAlphaPowerTwoWithFactor, minusBeta); + } + else { + PoincareHelpers::Simplify(&beta, context, ExpressionNode::ReductionTarget::User); + canonized = Addition::Builder(xMinusAlphaPowerTwoWithFactor, beta); + } Expression x0; Expression x1; - if (delta.nullStatus(context) == ExpressionNode::NullStatus::Null) { // x0 = x1 = -b/(2a) - x0 = Division::Builder(Opposite::Builder(b), Multiplication::Builder(Rational::Builder(2), a)); + x0 = Division::Builder(Opposite::Builder(b.clone()), Multiplication::Builder(Rational::Builder(2), a.clone())); m_numberOfSolutions = 1; - PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::SystemForApproximation); } else { // x0 = (-b-sqrt(delta))/(2a) x0 = Division::Builder(Subtraction::Builder(Opposite::Builder(b.clone()), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), a.clone())); // x1 = (-b+sqrt(delta))/(2a) - x1 = Division::Builder(Addition::Builder(Opposite::Builder(b), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), a)); + x1 = Division::Builder(Addition::Builder(Opposite::Builder(b.clone()), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), a.clone())); m_numberOfSolutions = 2; - PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); - PoincareHelpers::Simplify(&x1, context, ExpressionNode::ReductionTarget::User); + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::SystemForApproximation); + PoincareHelpers::Simplify(&x1, context, ExpressionNode::ReductionTarget::SystemForApproximation); if (x0.type() == ExpressionNode::Type::Unreal) { assert(x1.type() == ExpressionNode::Type::Unreal); m_numberOfSolutions = 0; @@ -109,40 +163,87 @@ void SecondDegreeListController::setExpression(Poincare::Expression e) { Expression factorized; if (m_numberOfSolutions == 2) { + Expression firstFactor; + Expression secondFactor; + + Expression x0Opposite = getOppositeIfExists(x0, &reductionContext); + if (x0Opposite.isUninitialized()) { + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); + firstFactor = Subtraction::Builder(Symbol::Builder("x", strlen("x")), x0); + } + else { + PoincareHelpers::Simplify(&x0Opposite, context, ExpressionNode::ReductionTarget::User); + firstFactor = Addition::Builder(Symbol::Builder("x", strlen("x")), x0Opposite); + } if (x0.type() == ExpressionNode::Type::Opposite) { factorized = Parenthesis::Builder(Addition::Builder(Symbol::Builder("x", strlen("x")), x0.childAtIndex(0).clone())); } - else { - factorized = Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), x0.clone())); - } - - if (x1.type() == ExpressionNode::Type::Opposite) { - factorized = Multiplication::Builder(factorized.clone(), Parenthesis::Builder(Addition::Builder(Symbol::Builder("x", strlen("x")), x1.childAtIndex(0).clone()))); + + Expression x1Opposite = getOppositeIfExists(x1, &reductionContext); + if (x1Opposite.isUninitialized()) { + PoincareHelpers::Simplify(&x1, context, ExpressionNode::ReductionTarget::User); + secondFactor = Subtraction::Builder(Symbol::Builder("x", strlen("x")), x1); } else { - factorized = Multiplication::Builder(factorized.clone(), Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), x1.clone()))); + PoincareHelpers::Simplify(&x1Opposite, context, ExpressionNode::ReductionTarget::User); + secondFactor = Addition::Builder(Symbol::Builder("x", strlen("x")), x1Opposite); } - if (aIsNotOne) { - factorized = Multiplication::Builder(a.clone(), factorized.clone()); + Expression solutionProduct = Multiplication::Builder(Parenthesis::Builder(firstFactor), Parenthesis::Builder(secondFactor)); + switch (multiplicationTypeForA) + { + case MultiplicationTypeForA::Nothing: + factorized = solutionProduct; + break; + case MultiplicationTypeForA::Minus: + factorized = Multiplication::Builder(a.clone(), solutionProduct); + break; + case MultiplicationTypeForA::Parenthesis: + factorized = Multiplication::Builder(Parenthesis::Builder(a.clone()), solutionProduct); + break; + case MultiplicationTypeForA::Normal: + factorized = Multiplication::Builder(a.clone(), solutionProduct); + break; + default: + assert(false); + break; } } else if (m_numberOfSolutions == 1) { - if (x0.type() == ExpressionNode::Type::Opposite) { - factorized = Power::Builder(Parenthesis::Builder(Addition::Builder(Symbol::Builder("x", strlen("x")), x0.childAtIndex(0).clone())), Rational::Builder(2)); + Expression x0Opposite = getOppositeIfExists(x0, &reductionContext); + Expression factor; + if (x0Opposite.isUninitialized()) { + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); + factor = Subtraction::Builder(Symbol::Builder("x", strlen("x")), x0); } else { - factorized = Power::Builder(Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), x0.clone())), Rational::Builder(2)); + PoincareHelpers::Simplify(&x0Opposite, context, ExpressionNode::ReductionTarget::User); + factor = Addition::Builder(Symbol::Builder("x", strlen("x")), x0Opposite); } - - if (aIsNotOne) { - factorized = Multiplication::Builder(a.clone(), factorized.clone()); + Expression solutionProduct = Power::Builder(Parenthesis::Builder(factor), Rational::Builder(2)); + switch (multiplicationTypeForA) + { + case MultiplicationTypeForA::Nothing: + factorized = solutionProduct; + break; + case MultiplicationTypeForA::Minus: + factorized = Multiplication::Builder(a.clone(), solutionProduct); + break; + case MultiplicationTypeForA::Parenthesis: + factorized = Multiplication::Builder(Parenthesis::Builder(a.clone()), solutionProduct); + break; + case MultiplicationTypeForA::Normal: + factorized = Multiplication::Builder(a.clone(), solutionProduct); + break; + default: + assert(false); + break; } } PoincareHelpers::Simplify(&delta, context, ExpressionNode::ReductionTarget::User); - m_layouts[0] = PoincareHelpers::CreateLayout(canonised); + m_layouts[0] = PoincareHelpers::CreateLayout(canonized); if (m_numberOfSolutions > 0) { m_layouts[1] = PoincareHelpers::CreateLayout(factorized); m_layouts[2] = PoincareHelpers::CreateLayout(delta); @@ -156,6 +257,27 @@ void SecondDegreeListController::setExpression(Poincare::Expression e) { } } +Expression SecondDegreeListController::getOppositeIfExists(Expression e, Poincare::ExpressionNode::ReductionContext * reductionContext) { + if (e.isNumber() && e.sign(reductionContext->context()) == ExpressionNode::Sign::Negative) { + Number n = static_cast(e); + return std::move(n.setSign(ExpressionNode::Sign::Positive)); + } + else if (e.type() == ExpressionNode::Type::Multiplication && e.numberOfChildren() > 0 && e.childAtIndex(0).isNumber() && e.childAtIndex(0).sign(reductionContext->context()) == ExpressionNode::Sign::Negative) { + Multiplication m = static_cast(e); + if (m.childAtIndex(0).type() == ExpressionNode::Type::Rational && static_cast(e).isMinusOne()) { + // The negative numeral factor is -1, we just remove it + m.removeChildAtIndexInPlace(0); + } else { + Expression firstChild = m.childAtIndex(0); + Number n = static_cast(firstChild); + m.childAtIndex(0).setChildrenInPlace(n.setSign(ExpressionNode::Sign::Positive)); + } + PoincareHelpers::Simplify(&m, reductionContext->context(), ExpressionNode::ReductionTarget::User); + return std::move(m); + } + return Expression(); +} + I18n::Message SecondDegreeListController::messageAtIndex(int index) { if (m_numberOfSolutions > 0) { if (index == 0) { diff --git a/apps/calculation/additional_outputs/second_degree_list_controller.h b/apps/calculation/additional_outputs/second_degree_list_controller.h index 52300d972..89812b710 100644 --- a/apps/calculation/additional_outputs/second_degree_list_controller.h +++ b/apps/calculation/additional_outputs/second_degree_list_controller.h @@ -14,6 +14,7 @@ public: void setExpression(Poincare::Expression e) override; private: + Poincare::Expression getOppositeIfExists(Poincare::Expression e, Poincare::ExpressionNode::ReductionContext * reductionContext); I18n::Message messageAtIndex(int index) override; int m_numberOfSolutions; };