diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile index 43f8f92c3..a75d97dd8 100644 --- a/apps/calculation/Makefile +++ b/apps/calculation/Makefile @@ -19,6 +19,7 @@ app_calculation_src = $(addprefix apps/calculation/,\ additional_outputs/list_controller.cpp \ additional_outputs/matrix_list_controller.cpp \ additional_outputs/rational_list_controller.cpp \ + additional_outputs/second_degree_list_controller.cpp \ additional_outputs/trigonometry_graph_cell.cpp \ additional_outputs/trigonometry_list_controller.cpp \ additional_outputs/trigonometry_model.cpp \ diff --git a/apps/calculation/additional_outputs/second_degree_list_controller.cpp b/apps/calculation/additional_outputs/second_degree_list_controller.cpp new file mode 100644 index 000000000..e4cb2a0c7 --- /dev/null +++ b/apps/calculation/additional_outputs/second_degree_list_controller.cpp @@ -0,0 +1,190 @@ +#include "../app.h" +#include +#include "../../shared/poincare_helpers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "second_degree_list_controller.h" + +using namespace Poincare; +using namespace Shared; + +namespace Calculation { + +void SecondDegreeListController::setExpression(Poincare::Expression e) { + ExpressionsListController::setExpression(e); + assert(!m_expression.isUninitialized()); + + Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; + + Context * context = App::app()->localContext(); + Preferences * preferences = Preferences::sharedPreferences(); + + PoincareHelpers::Reduce(&m_expression, context, ExpressionNode::ReductionTarget::SystemForAnalysis); + + int degree = m_expression.getPolynomialReducedCoefficients( + "x", + polynomialCoefficients, + context, + Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), m_expression, context), + preferences->angleUnit(), + GlobalPreferences::sharedGlobalPreferences()->unitFormat(), + ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition); + + assert(degree == 2); + + 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); + + Expression beta = Opposite::Builder(Division::Builder(delta.clone(), Multiplication::Builder(Rational::Builder(4), a.clone()))); + PoincareHelpers::Simplify(&beta, context, ExpressionNode::ReductionTarget::User); + + /* + * Because when can't apply reduce or simplify to keep the canonised + * 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 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)); + m_numberOfSolutions = 1; + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); + } + 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)); + m_numberOfSolutions = 2; + PoincareHelpers::Simplify(&x0, context, ExpressionNode::ReductionTarget::User); + PoincareHelpers::Simplify(&x1, context, ExpressionNode::ReductionTarget::User); + if (x0.type() == ExpressionNode::Type::Unreal) { + assert(x1.type() == ExpressionNode::Type::Unreal); + m_numberOfSolutions = 0; + } + } + + Expression factorized; + + if (m_numberOfSolutions == 2) { + 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()))); + } + else { + factorized = Multiplication::Builder(factorized.clone(), Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), x1.clone()))); + } + + if (aIsNotOne) { + factorized = Multiplication::Builder(a.clone(), factorized.clone()); + } + } + 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)); + } + else { + factorized = Power::Builder(Parenthesis::Builder(Subtraction::Builder(Symbol::Builder("x", strlen("x")), x0.clone())), Rational::Builder(2)); + } + + if (aIsNotOne) { + factorized = Multiplication::Builder(a.clone(), factorized.clone()); + } + } + + PoincareHelpers::Simplify(&delta, context, ExpressionNode::ReductionTarget::User); + + m_layouts[0] = PoincareHelpers::CreateLayout(canonised); + if (m_numberOfSolutions > 0) { + m_layouts[1] = PoincareHelpers::CreateLayout(factorized); + m_layouts[2] = PoincareHelpers::CreateLayout(delta); + m_layouts[3] = PoincareHelpers::CreateLayout(x0); + if (m_numberOfSolutions > 1) { + m_layouts[4] = PoincareHelpers::CreateLayout(x1); + } + } + else { + m_layouts[1] = PoincareHelpers::CreateLayout(delta); + } +} + +I18n::Message SecondDegreeListController::messageAtIndex(int index) { + if (m_numberOfSolutions > 0) { + if (index == 0) { + return I18n::Message::CanonicalForm; + } + if (index == 1) { + return I18n::Message::FactorizedForm; + } + if (index == 2) { + return I18n::Message::Discriminant; + } + if (index == 3) { + if (m_numberOfSolutions == 1) { + return I18n::Message::OnlyRoot; + } + else { + return I18n::Message::FirstRoot; + } + } + return I18n::Message::SecondRoot; + } + else { + switch (index) { + case 0: + return I18n::Message::CanonicalForm; + default: + return I18n::Message::Discriminant; + } + } +} + +} diff --git a/apps/calculation/additional_outputs/second_degree_list_controller.h b/apps/calculation/additional_outputs/second_degree_list_controller.h new file mode 100644 index 000000000..52300d972 --- /dev/null +++ b/apps/calculation/additional_outputs/second_degree_list_controller.h @@ -0,0 +1,25 @@ +#ifndef CALCULATION_ADDITIONAL_OUTPUTS_SECOND_DEGREE_CONTROLLER_H +#define CALCULATION_ADDITIONAL_OUTPUTS_SECOND_DEGREE_CONTROLLER_H + +#include "expressions_list_controller.h" + +namespace Calculation { + +class SecondDegreeListController : public ExpressionsListController { +public: + SecondDegreeListController(EditExpressionController * editExpressionController) : + ExpressionsListController(editExpressionController), + m_numberOfSolutions(0) {} + + void setExpression(Poincare::Expression e) override; + +private: + I18n::Message messageAtIndex(int index) override; + int m_numberOfSolutions; +}; + +} + +#endif + + diff --git a/apps/calculation/base.de.i18n b/apps/calculation/base.de.i18n index 406f27e84..5d12d9b22 100644 --- a/apps/calculation/base.de.i18n +++ b/apps/calculation/base.de.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Determinante" AdditionalInverse = "Inverse" AdditionalRowEchelonForm = "Stufenform" AdditionalReducedRowEchelonForm = "Reduzierte Stufenform" -AdditionalTrace = "Spur" \ No newline at end of file +AdditionalTrace = "Spur" +CanonicalForm = "Kanonische Form" +FactorizedForm = "Factorisierte Form" +Discriminant = "Diskriminante" +OnlyRoot = "Wurzel" +FirstRoot = "Erste Wurzel" +SecondRoot = "Zweite Wurzel" \ No newline at end of file diff --git a/apps/calculation/base.en.i18n b/apps/calculation/base.en.i18n index 967c09050..ada600b3c 100644 --- a/apps/calculation/base.en.i18n +++ b/apps/calculation/base.en.i18n @@ -12,3 +12,9 @@ AdditionalInverse = "Inverse" AdditionalRowEchelonForm = "Row echelon form" AdditionalReducedRowEchelonForm = "Reduced row echelon form" AdditionalTrace = "Trace" +CanonicalForm = "Canonical form" +FactorizedForm = "Factorized form" +Discriminant = "Discriminant" +OnlyRoot = "Root" +FirstRoot = "First root" +SecondRoot = "Second root" \ No newline at end of file diff --git a/apps/calculation/base.es.i18n b/apps/calculation/base.es.i18n index 057481a0d..1b9ffde00 100644 --- a/apps/calculation/base.es.i18n +++ b/apps/calculation/base.es.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Determinante" AdditionalInverse = "Inversa" AdditionalRowEchelonForm = "Matriz escalonada" AdditionalReducedRowEchelonForm = "Matriz escalonada reducida" -AdditionalTrace = "Traza" \ No newline at end of file +AdditionalTrace = "Traza" +CanonicalForm = "Forma canónica" +FactorizedForm = "Forma factorizada" +Discriminant = "Discriminante" +OnlyRoot = "Raíz" +FirstRoot = "Primera raíz" +SecondRoot = "Segunda raíz" \ No newline at end of file diff --git a/apps/calculation/base.fr.i18n b/apps/calculation/base.fr.i18n index 0e155e294..a8432eeb0 100644 --- a/apps/calculation/base.fr.i18n +++ b/apps/calculation/base.fr.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Déterminant" AdditionalInverse = "Inverse" AdditionalRowEchelonForm = "Forme échelonnée" AdditionalReducedRowEchelonForm = "Forme échelonnée réduite" -AdditionalTrace = "Trace" \ No newline at end of file +AdditionalTrace = "Trace" +CanonicalForm = "Forme canonique" +FactorizedForm = "Forme factorisée" +Discriminant = "Discriminant" +OnlyRoot = "Racine" +FirstRoot = "Première racine" +SecondRoot = "Seconde racine" \ No newline at end of file diff --git a/apps/calculation/base.hu.i18n b/apps/calculation/base.hu.i18n index 39397adee..c798ac817 100644 --- a/apps/calculation/base.hu.i18n +++ b/apps/calculation/base.hu.i18n @@ -12,3 +12,9 @@ AdditionalInverse = "inverz" AdditionalRowEchelonForm = "Sor echelon forma" AdditionalReducedRowEchelonForm = "Csökkentett sorú Echelon forma" AdditionalTrace = "Nyomkövetés" +CanonicalForm = "Kanonikus forma" +FactorizedForm = "Factorizált forma" +Discriminant = "Discriminant" +OnlyRoot = "Gyökér" +FirstRoot = "Első gyökér" +SecondRoot = "Második gyökér" \ No newline at end of file diff --git a/apps/calculation/base.it.i18n b/apps/calculation/base.it.i18n index ca4102893..c39614271 100644 --- a/apps/calculation/base.it.i18n +++ b/apps/calculation/base.it.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Determinante" AdditionalInverse = "Inversa" AdditionalRowEchelonForm = "Matrice a scalini" AdditionalReducedRowEchelonForm = "Matrice ridotta a scalini" -AdditionalTrace = "Traccia" \ No newline at end of file +AdditionalTrace = "Traccia" +CanonicalForm = "Forma canonica" +FactorizedForm = "Forma fattorizzata" +Discriminant = "Discriminante" +OnlyRoot = "Radice" +FirstRoot = "Prima radice" +SecondRoot = "Seconda radice" \ No newline at end of file diff --git a/apps/calculation/base.nl.i18n b/apps/calculation/base.nl.i18n index 51e412cb4..b156ddd98 100644 --- a/apps/calculation/base.nl.i18n +++ b/apps/calculation/base.nl.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Determinant" AdditionalInverse = "Inverse" AdditionalRowEchelonForm = "Echelonvorm" AdditionalReducedRowEchelonForm = "Gereduceerde echelonvorm" -AdditionalTrace = "Spoor" \ No newline at end of file +AdditionalTrace = "Spoor" +CanonicalForm = "Canonische vorm" +FactorizedForm = "Factorized vorm" +Discriminant = "Discriminant" +OnlyRoot = "Wortel" +FirstRoot = "Eerste wortel" +SecondRoot = "Tweede wortel" \ No newline at end of file diff --git a/apps/calculation/base.pt.i18n b/apps/calculation/base.pt.i18n index 941363a04..ae4c401ba 100644 --- a/apps/calculation/base.pt.i18n +++ b/apps/calculation/base.pt.i18n @@ -11,4 +11,10 @@ AdditionalDeterminant = "Determinante" AdditionalInverse = "Matriz inversa" AdditionalRowEchelonForm = "Matriz escalonada" AdditionalReducedRowEchelonForm = "Matriz escalonada reduzida" -AdditionalTrace = "Traço" \ No newline at end of file +AdditionalTrace = "Traço" +CanonicalForm = "Forma canónica" +FactorizedForm = "Factorized form" +Discriminant = "Discriminante" +OnlyRoot = "Raiz" +FirstRoot = "Primeira raiz" +SecondRoot = "Segunda raiz" \ No newline at end of file diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index c293cbbe8..fb02fea6f 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -272,6 +273,9 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co if (o.type() == ExpressionNode::Type::Matrix) { return AdditionalInformationType::Matrix; } + if (o.polynomialDegree(context, "x") == 2) { + return AdditionalInformationType::SecondDegree; + } return AdditionalInformationType::None; } diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index be7f87c9b..4c026cbc4 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -39,6 +39,7 @@ public: None = 0, Integer, Rational, + SecondDegree, Trigonometry, Unit, Matrix, diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 6d51e7862..9a8687309 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -16,6 +16,7 @@ HistoryController::HistoryController(EditExpressionController * editExpressionCo m_complexController(editExpressionController), m_integerController(editExpressionController), m_rationalController(editExpressionController), + m_secondDegreeController(editExpressionController), m_trigonometryController(editExpressionController), m_unitController(editExpressionController), m_matrixController(editExpressionController) @@ -100,6 +101,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) { Expression e = calculationAtIndex(focusRow)->exactOutput(); if (additionalInfoType == Calculation::AdditionalInformationType::Complex) { vc = &m_complexController; + } else if (additionalInfoType == Calculation::AdditionalInformationType::SecondDegree) { + vc = &m_secondDegreeController; } else if (additionalInfoType == Calculation::AdditionalInformationType::Trigonometry) { vc = &m_trigonometryController; // Find which of the input or output is the cosine/sine diff --git a/apps/calculation/history_controller.h b/apps/calculation/history_controller.h index e289eb5fc..b919e823c 100644 --- a/apps/calculation/history_controller.h +++ b/apps/calculation/history_controller.h @@ -8,6 +8,7 @@ #include "additional_outputs/complex_list_controller.h" #include "additional_outputs/integer_list_controller.h" #include "additional_outputs/rational_list_controller.h" +#include "additional_outputs/second_degree_list_controller.h" #include "additional_outputs/trigonometry_list_controller.h" #include "additional_outputs/unit_list_controller.h" #include "additional_outputs/matrix_list_controller.h" @@ -47,6 +48,7 @@ private: ComplexListController m_complexController; IntegerListController m_integerController; RationalListController m_rationalController; + SecondDegreeListController m_secondDegreeController; TrigonometryListController m_trigonometryController; UnitListController m_unitController; MatrixListController m_matrixController; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index b5f8de021..68d80943d 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -68,18 +69,33 @@ int PowerNode::polynomialDegree(Context * context, const char * symbolName) cons if (op0Deg < 0) { return -1; } + Integer i; + bool foundInteger = false; if (childAtIndex(1)->type() == ExpressionNode::Type::Rational) { RationalNode * r = static_cast(childAtIndex(1)); if (!r->isInteger() || Number(r).sign() == Sign::Negative) { return -1; } - Integer numeratorInt = r->signedNumerator(); - if (!numeratorInt.isExtractable()) { + foundInteger = true; + i = r->signedNumerator(); + } + else if(childAtIndex(1)->type() == ExpressionNode::Type::BasedInteger) { + BasedIntegerNode * b = static_cast(childAtIndex(1)); + if (Number(b).sign() == Sign::Negative) { return -1; } - op0Deg *= numeratorInt.extractedInt(); + foundInteger = true; + i = b->integer(); + } + + if (foundInteger) { + if (!i.isExtractable()) { + return -1; + } + op0Deg *= i.extractedInt(); return op0Deg; } + return -1; } @@ -356,10 +372,9 @@ int Power::getPolynomialCoefficients(Context * context, const char * symbolName, } /* Here we only consider the case x^4 as privateGetPolynomialCoefficients is * supposed to be called after reducing the expression. */ - if (childAtIndex(0).type() == ExpressionNode::Type::Symbol - && strcmp(childAtIndex(0).convert().name(), symbolName) == 0 - && childAtIndex(1).type() == ExpressionNode::Type::Rational) - { + int n; + bool foundInteger = false; + if (childAtIndex(1).type() == ExpressionNode::Type::Rational) { Rational r = childAtIndex(1).convert(); if (!r.isInteger() || r.sign() == ExpressionNode::Sign::Negative) { return -1; @@ -368,7 +383,26 @@ int Power::getPolynomialCoefficients(Context * context, const char * symbolName, if (!num.isExtractable()) { return -1; } - int n = num.extractedInt(); + foundInteger = true; + n = num.extractedInt(); + } + else if(childAtIndex(1).type() == ExpressionNode::Type::BasedInteger) { + BasedInteger b = childAtIndex(1).convert(); + if (Number(b).sign() == ExpressionNode::Sign::Negative) { + return -1; + } + foundInteger = true; + Integer i = b.integer(); + if (!i.isExtractable()) { + return -1; + } + n = i.extractedInt(); + } + + if (childAtIndex(0).type() == ExpressionNode::Type::Symbol + && strcmp(childAtIndex(0).convert().name(), symbolName) == 0 + && foundInteger) + { if (n <= k_maxPolynomialDegree) { for (int i = 0; i < n; i++) { coefficients[i] = Rational::Builder(0);