diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp index 0539631cd..956da75d8 100644 --- a/apps/regression/regression_context.cpp +++ b/apps/regression/regression_context.cpp @@ -10,7 +10,7 @@ using namespace Shared; namespace Regression { const Expression RegressionContext::expressionForSymbolAbstract(const SymbolAbstract & symbol, bool clone) { - if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isRegressionSymbol(symbol.name())) { + if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isRegressionSymbol(symbol.name(), nullptr)) { const char * seriesName = symbol.name(); assert(strlen(seriesName) == 2); diff --git a/apps/solver/base.de.i18n b/apps/solver/base.de.i18n index 5350c81d2..493fff49e 100644 --- a/apps/solver/base.de.i18n +++ b/apps/solver/base.de.i18n @@ -23,3 +23,7 @@ OnlyFirstSolutionsDisplayed0 = "Es werden nur die ersten" OnlyFirstSolutionsDisplayed1 = "zehn Lösungen angezeigt." PolynomeHasNoRealSolution0 = "Das Polynom hat" PolynomeHasNoRealSolution1 = "keine reelle Nullstelle" +PredefinedVariablesUsedLeft = "Verwen" +PredefinedVariablesUsedRight = "dete vordefinierte Variablen" +PredefinedVariablesIgnoredLeft = "Ignorie" +PredefinedVariablesIgnoredRight = "rt vordefinierte Variablen" diff --git a/apps/solver/base.en.i18n b/apps/solver/base.en.i18n index ebbb853e6..d642f7f46 100644 --- a/apps/solver/base.en.i18n +++ b/apps/solver/base.en.i18n @@ -23,3 +23,7 @@ OnlyFirstSolutionsDisplayed0 = "Only the first 10 solutions" OnlyFirstSolutionsDisplayed1 = "are displayed" PolynomeHasNoRealSolution0 = "The polynomial has no" PolynomeHasNoRealSolution1 = "real root" +PredefinedVariablesUsedLeft = "Used " +PredefinedVariablesUsedRight = "predefined variables" +PredefinedVariablesIgnoredLeft = "Ignor" +PredefinedVariablesIgnoredRight = "ed predefined variables" diff --git a/apps/solver/base.es.i18n b/apps/solver/base.es.i18n index 7667ba3b5..0f47ec623 100644 --- a/apps/solver/base.es.i18n +++ b/apps/solver/base.es.i18n @@ -23,3 +23,7 @@ OnlyFirstSolutionsDisplayed0 = "Sólo se muestran las" OnlyFirstSolutionsDisplayed1 = "10 primeras soluciones" PolynomeHasNoRealSolution0 = "El polinomio no tiene" PolynomeHasNoRealSolution1 = "ninguna raíz real" +PredefinedVariablesUsedLeft = "Variable" +PredefinedVariablesUsedRight = "s predefinidas utilizadas" +PredefinedVariablesIgnoredLeft = "Variable" +PredefinedVariablesIgnoredRight = "s predefinidas ignoradas" diff --git a/apps/solver/base.fr.i18n b/apps/solver/base.fr.i18n index c23428889..c337a0525 100644 --- a/apps/solver/base.fr.i18n +++ b/apps/solver/base.fr.i18n @@ -23,3 +23,7 @@ OnlyFirstSolutionsDisplayed0 = "Seulement les 10 premières" OnlyFirstSolutionsDisplayed1 = "solutions sont affichées" PolynomeHasNoRealSolution0 = "Le polynôme n'admet pas" PolynomeHasNoRealSolution1 = "de racine réelle" +PredefinedVariablesUsedLeft = "Variable" +PredefinedVariablesUsedRight = "s prédéfinies utilisées" +PredefinedVariablesIgnoredLeft = "Variable" +PredefinedVariablesIgnoredRight = "s prédéfinies ignorées" diff --git a/apps/solver/base.pt.i18n b/apps/solver/base.pt.i18n index c1c28d11b..94b3df314 100644 --- a/apps/solver/base.pt.i18n +++ b/apps/solver/base.pt.i18n @@ -23,3 +23,7 @@ OnlyFirstSolutionsDisplayed0 = "Somente as 10 primeiras" OnlyFirstSolutionsDisplayed1 = "soluções são exibidas" PolynomeHasNoRealSolution0 = "O polinômio não tem" PolynomeHasNoRealSolution1 = "nenhuma raiz real" +PredefinedVariablesUsedLeft = "Variáveis " +PredefinedVariablesUsedRight = "pré-definidas utilizadas" +PredefinedVariablesIgnoredLeft = "Variáveis" +PredefinedVariablesIgnoredRight = " pré-definidas ignoradas" diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index bca1a825f..6fe84b003 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -27,7 +27,8 @@ EquationStore::EquationStore() : m_type(Type::LinearSystem), m_numberOfSolutions(0), m_exactSolutionExactLayouts{}, - m_exactSolutionApproximateLayouts{} + m_exactSolutionApproximateLayouts{}, + m_numberOfUserVariables(0) { } @@ -100,14 +101,15 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context, bool solve 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 shouldReplaceFuncionsButNotSymbols) { +void EquationStore::approximateSolve(Poincare::Context * context, bool shouldReplaceFunctionsButNotSymbols) { + 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, shouldReplaceFuncionsButNotSymbols), m_variables[0], start, step, m_intervalApproximateSolutions[1], context); + m_approximateSolutions[i] = PoincareHelpers::NextRoot(modelForRecord(definedRecordAtIndex(0))->standardForm(context, shouldReplaceFunctionsButNotSymbols), m_variables[0], start, step, m_intervalApproximateSolutions[1], context); if (std::isnan(m_approximateSolutions[i])) { break; } else { @@ -120,6 +122,8 @@ void EquationStore::approximateSolve(Poincare::Context * context, bool shouldRep EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool replaceFunctionsButNotSymbols) { tidySolution(); + m_userVariablesUsed = !replaceFunctionsButNotSymbols; + // Step 0. Get unknown variables m_variables[0][0] = 0; int numberOfVariables = 0; @@ -131,7 +135,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool if (e.type() == ExpressionNode::Type::Unreal) { return Error::EquationUnreal; } - numberOfVariables = e.getVariables(context, [](const char * symbol) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize); + numberOfVariables = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize); if (numberOfVariables == -1) { return Error::TooManyVariables; } @@ -140,7 +144,22 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool assert(numberOfVariables >= 0); } - // Step 1. Linear System? + // Step 1. Get user defined variables + // TODO used previously fetched variables? + m_userVariables[0][0] = 0; + m_numberOfUserVariables = 0; + for (int i = 0; i < numberOfDefinedModels(); i++) { + const Expression e = modelForRecord(definedRecordAtIndex(i))->standardForm(context, true); + assert(!e.isUninitialized() && e.type() != ExpressionNode::Type::Undefined && e.type() != ExpressionNode::Type::Unreal); + int varCount = e.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); + if (varCount < 0) { + m_numberOfUserVariables = Expression::k_maxNumberOfVariables; + break; + } + m_numberOfUserVariables = varCount; + } + + // Step 2. Linear System? /* Create matrix coefficients and vector constants as: * coefficients * (x y z ...) = constants */ @@ -177,7 +196,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool m_type = Type::LinearSystem; error = resolveLinearSystem(exactSolutions, exactSolutionsApproximations, coefficients, constants, context); } else { - // Step 2. Polynomial & Monovariable? + // Step 3. Polynomial & Monovariable? assert(numberOfVariables == 1 && numberOfDefinedModels() == 1); Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; int degree = modelForRecord(definedRecordAtIndex(0))->standardForm(context, replaceFunctionsButNotSymbols).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, context, updatedComplexFormat(context), preferences->angleUnit()); @@ -186,7 +205,7 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool m_type = Type::PolynomialMonovariable; error = oneDimensialPolynomialSolve(exactSolutions, exactSolutionsApproximations, polynomialCoefficients, degree, context); } else { - // Step 3. Monovariable non-polynomial or polynomial with degree > 2 + // Step 4. Monovariable non-polynomial or polynomial with degree > 2 m_type = Type::Monovariable; m_intervalApproximateSolutions[0] = -10; m_intervalApproximateSolutions[1] = 10; diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index 62156bf84..7f1d75afe 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -38,6 +38,16 @@ public: assert(i < Poincare::Expression::k_maxNumberOfVariables && m_variables[i][0] != 0); return m_variables[i]; } + const char * userVariableAtIndex(size_t i) { + assert(i < Poincare::Expression::k_maxNumberOfVariables && m_userVariables[i][0] != 0); + return m_userVariables[i]; + } + int numberOfUserVariables() const { + return m_numberOfUserVariables; + } + bool userVariablesUsed() const { + return m_userVariablesUsed; + } int numberOfSolutions() const { return m_numberOfSolutions; } @@ -93,6 +103,7 @@ private: mutable Equation m_equations[k_maxNumberOfEquations]; Type m_type; char m_variables[Poincare::Expression::k_maxNumberOfVariables][Poincare::SymbolAbstract::k_maxNameSize]; + char m_userVariables[Poincare::Expression::k_maxNumberOfVariables][Poincare::SymbolAbstract::k_maxNameSize]; int m_numberOfSolutions; Poincare::Layout m_exactSolutionExactLayouts[k_maxNumberOfApproximateSolutions]; Poincare::Layout m_exactSolutionApproximateLayouts[k_maxNumberOfExactSolutions]; @@ -100,6 +111,8 @@ private: bool m_exactSolutionEquality[k_maxNumberOfExactSolutions]; double m_intervalApproximateSolutions[2]; double m_approximateSolutions[k_maxNumberOfApproximateSolutions]; + int m_numberOfUserVariables; + bool m_userVariablesUsed; }; } diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index 628a45efe..d13c58847 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include using namespace Poincare; @@ -17,19 +18,21 @@ namespace Solver { static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { return x > y ? x : y; } +constexpr KDColor SolutionsController::ContentView::k_backgroundColor; + SolutionsController::ContentView::ContentView(SolutionsController * controller) : - m_warningMessageView0(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), - m_warningMessageView1(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), + m_warningMessageView0(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, k_backgroundColor), + m_warningMessageView1(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, k_backgroundColor), m_selectableTableView(controller), m_displayWarningMoreSolutions(false) { - m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); + m_selectableTableView.setBackgroundColor(k_backgroundColor); m_selectableTableView.setVerticalCellOverlap(0); } void SolutionsController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { if (m_displayWarningMoreSolutions) { - ctx->fillRect(KDRect(0, 0, bounds().width(), k_topMargin), Palette::WallScreenDark); + ctx->fillRect(KDRect(0, 0, bounds().width(), k_topMargin), k_backgroundColor); } } @@ -80,7 +83,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt m_deltaCell(0.5f, 0.5f), m_delta2Layout(), m_contentView(this), - m_shouldReplaceFuncionsButNotSymbols(false) + m_shouldReplaceFunctionsButNotSymbols(false) { m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CodePointLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); const char * deltaB = "Δ=b"; @@ -93,6 +96,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt } for (int i = 0; i < k_numberOfSymbolCells; i++) { m_symbolCells[i].setAlignment(0.5f, 0.5f); + m_symbolCells[i].setFont(KDFont::LargeFont); } } @@ -109,7 +113,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_shouldReplaceFuncionsButNotSymbols); + requireWarning = m_equationStore->haveMoreApproximationSolutions(App::app()->localContext(), m_shouldReplaceFunctionsButNotSymbols); } 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); @@ -158,52 +162,76 @@ Responder * SolutionsController::defaultController() { /* TableViewDataSource */ int SolutionsController::numberOfRows() const { - return m_equationStore->numberOfSolutions(); + return m_equationStore->numberOfSolutions() + (m_equationStore->numberOfUserVariables() > 0 ? 1 + m_equationStore->numberOfUserVariables() : 0); } void SolutionsController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { + const int rowOfUserVariablesMessage = userVariablesMessageRow(); + if (j == rowOfUserVariablesMessage) { + // Predefined varaible used/ignored message + assert(i >= 0); + MessageCell * messageCell = static_cast(cell); + messageCell->setHorizontalAlignment(i == 0 ? 1.0f : 0.0f); + if (usedUserVariables()) { + messageCell->setMessage(i == 0 ? I18n::Message::PredefinedVariablesUsedLeft : I18n::Message::PredefinedVariablesUsedRight); + } else { + messageCell->setMessage(i == 0 ? I18n::Message::PredefinedVariablesIgnoredLeft : I18n::Message::PredefinedVariablesIgnoredRight); + } + return; + } if (i == 0) { - // Name of the variable or discriminant if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) { - // Discriminant + // Formula of the discriminant EvenOddExpressionCell * deltaCell = static_cast(cell); deltaCell->setLayout(m_delta2Layout); } else { EvenOddBufferTextCell * symbolCell = static_cast(cell); - symbolCell->setFont(KDFont::LargeFont); - char bufferSymbol[Poincare::SymbolAbstract::k_maxNameSize+1]; // Hold at maximum Delta = b^2-4ac or the variable name + a digit - switch (m_equationStore->type()) { - case EquationStore::Type::LinearSystem: + char bufferSymbol[Poincare::SymbolAbstract::k_maxNameSize+1]; // Holds at maximum the variable name + a digit + if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) { + // Solution symbol name + if (m_equationStore->type() == EquationStore::Type::LinearSystem) { /* The system has more than one variable: the cell text is the * variable name */ strlcpy(bufferSymbol, m_equationStore->variableAtIndex(j), Poincare::SymbolAbstract::k_maxNameSize); - break; - default: + } else { /* The system has one variable but might have many solutions: the cell * text is variableX, with X the row index + 1 (e.g. x1, x2,...) */ int length = strlcpy(bufferSymbol, m_equationStore->variableAtIndex(0), Poincare::SymbolAbstract::k_maxNameSize); - bufferSymbol[length++] = j+'1'; + bufferSymbol[length++] = j+'1'; // TODO LEA change for 10 bufferSymbol[length] = 0; - break; + } + } else { + // User variable name + assert(rowOfUserVariablesMessage > 0); + strlcpy(bufferSymbol, m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1), Poincare::SymbolAbstract::k_maxNameSize); } symbolCell->setText(bufferSymbol); } } else { - // Value of the variable or discriminant - if (m_equationStore->type() == EquationStore::Type::Monovariable) { - EvenOddBufferTextCell * valueCell = static_cast(cell); - constexpr int precision = Preferences::LargeNumberOfSignificantDigits; - constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision); - char bufferValue[bufferSize]; - PoincareHelpers::ConvertFloatToText(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision); - valueCell->setText(bufferValue); - } else { - ScrollableTwoExpressionsCell * valueCell = static_cast(cell); - Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutsAtIndexAreIdentical(j) ? Poincare::Layout() : m_equationStore->exactSolutionLayoutAtIndex(j, true); - valueCell->setLayouts(exactLayout, m_equationStore->exactSolutionLayoutAtIndex(j, false)); - if (!exactLayout.isUninitialized()) { - valueCell->setEqualMessage(m_equationStore->exactSolutionLayoutsAtIndexAreEqual(j) ? I18n::Message::Equal : I18n::Message::AlmostEqual); + if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) { + if (m_equationStore->type() == EquationStore::Type::Monovariable) { + // Values of the solutions + EvenOddBufferTextCell * valueCell = static_cast(cell); + constexpr int precision = Preferences::LargeNumberOfSignificantDigits; + constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision); + char bufferValue[bufferSize]; + PoincareHelpers::ConvertFloatToText(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision); + valueCell->setText(bufferValue); + } else { + // Values of the solutions or discriminant + ScrollableTwoExpressionsCell * valueCell = static_cast(cell); + Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutsAtIndexAreIdentical(j) ? Poincare::Layout() : m_equationStore->exactSolutionLayoutAtIndex(j, true); + valueCell->setLayouts(exactLayout, m_equationStore->exactSolutionLayoutAtIndex(j, false)); + if (!exactLayout.isUninitialized()) { + valueCell->setEqualMessage(m_equationStore->exactSolutionLayoutsAtIndexAreEqual(j) ? I18n::Message::Equal : I18n::Message::AlmostEqual); + } } + } else { + // Values of the solutions or discriminant + ScrollableTwoExpressionsCell * valueCell = static_cast(cell); + const char * symbol = m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1); + Poincare::Layout layout = PoincareHelpers::CreateLayout(App::app()->localContext()->expressionForSymbolAbstract(Poincare::Symbol::Builder(symbol, strlen(symbol)), false)); + valueCell->setLayouts(Poincare::Layout(), layout); } } EvenOddCell * evenOddCell = static_cast(cell); @@ -218,12 +246,22 @@ KDCoordinate SolutionsController::rowHeight(int j) { if (m_equationStore->type() == EquationStore::Type::Monovariable) { return k_defaultCellHeight; } - Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutAtIndex(j, true); - Poincare::Layout approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false); - KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height(); - KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height(); - KDCoordinate layoutHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline()); - return layoutHeight + 2 * Metric::CommonSmallMargin; + const int rowOfUserVariablesMessage = userVariablesMessageRow(); + if (rowOfUserVariablesMessage < 0 || j < rowOfUserVariablesMessage) { + Poincare::Layout exactLayout = m_equationStore->exactSolutionLayoutAtIndex(j, true); + Poincare::Layout approximateLayout = m_equationStore->exactSolutionLayoutAtIndex(j, false); + KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height(); + KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height(); + KDCoordinate layoutHeight = maxCoordinate(exactLayout.baseline(), approximateLayout.baseline()) + maxCoordinate(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline()); + return layoutHeight + 2 * Metric::CommonSmallMargin; + } + if (j == rowOfUserVariablesMessage) { + return Metric::CommonTopMargin + k_defaultCellHeight + Metric::CommonBottomMargin; + } + // TODO: memoize user symbols if too slow + const char * symbol = m_equationStore->userVariableAtIndex(j - rowOfUserVariablesMessage - 1); + Poincare::Layout layout = PoincareHelpers::CreateLayout(App::app()->localContext()->expressionForSymbolAbstract(Poincare::Symbol::Builder(symbol, strlen(symbol)), false)); + return layout.layoutSize().height() + 2 * Metric::CommonSmallMargin; } KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) { @@ -232,11 +270,9 @@ KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) { return 0; case 1: return k_symbolCellWidth; - case 2: - return k_symbolCellWidth+k_valueCellWidth; default: - assert(false); - return 0; + assert(i == 2); + return k_symbolCellWidth+k_valueCellWidth; } } @@ -260,6 +296,8 @@ HighlightCell * SolutionsController::reusableCell(int index, int type) { return &m_deltaCell; case k_exactValueCellType: return &m_exactValueCells[index]; + case k_messageCellType: + return &m_messageCells[index]; default: assert(type == k_approximateValueCellType); return &m_approximateValueCells[index]; @@ -269,29 +307,43 @@ HighlightCell * SolutionsController::reusableCell(int index, int type) { int SolutionsController::reusableCellCount(int type) { switch (type) { case k_symbolCellType: - return EquationStore::k_maxNumberOfSolutions; + return k_numberOfSymbolCells; case k_deltaCellType: return 1; case k_exactValueCellType: - return EquationStore::k_maxNumberOfExactSolutions; + return k_numberOfExactValueCells; + case k_messageCellType: + return k_numberOfMessageCells; default: assert(type == k_approximateValueCellType); - return EquationStore::k_maxNumberOfApproximateSolutions; + return k_numberOfApproximateValueCells; } } int SolutionsController::typeAtLocation(int i, int j) { + const int rowOfUserVariableMessage = userVariablesMessageRow(); + if (j == rowOfUserVariableMessage) { + return k_messageCellType; + } if (i == 0) { if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) { return k_deltaCellType; } return k_symbolCellType; } - return m_equationStore->type() == EquationStore::Type::Monovariable ? k_approximateValueCellType : k_exactValueCellType; + if ((rowOfUserVariableMessage < 0 || j < rowOfUserVariableMessage) && m_equationStore->type() == EquationStore::Type::Monovariable) { + return k_approximateValueCellType; + } + return k_exactValueCellType; } void SolutionsController::didBecomeFirstResponder() { Container::activeApp()->setFirstResponder(m_contentView.selectableTableView()); } +int SolutionsController::userVariablesMessageRow() const { + assert(m_equationStore->numberOfUserVariables() >= 0); + return m_equationStore->numberOfUserVariables() == 0 ? -1 : m_equationStore->numberOfSolutions(); +} + } diff --git a/apps/solver/solutions_controller.h b/apps/solver/solutions_controller.h index f2b420f67..85d864522 100644 --- a/apps/solver/solutions_controller.h +++ b/apps/solver/solutions_controller.h @@ -11,8 +11,8 @@ namespace Solver { class SolutionsController : public ViewController, public AlternateEmptyViewDefaultDelegate, public SelectableTableViewDataSource, public TableViewDataSource { public: SolutionsController(Responder * parentResponder, EquationStore * equationStore); - void setShouldReplaceFuncionsButNotSymbols(bool shouldReplaceFuncionsButNotSymbols) { m_shouldReplaceFuncionsButNotSymbols = shouldReplaceFuncionsButNotSymbols; } - bool shouldReplaceFuncionsButNotSymbols() const { return m_shouldReplaceFuncionsButNotSymbols; } + void setShouldReplaceFuncionsButNotSymbols(bool shouldReplaceFuncionsButNotSymbols) { m_shouldReplaceFunctionsButNotSymbols = shouldReplaceFuncionsButNotSymbols; } + bool shouldReplaceFuncionsButNotSymbols() const { return m_shouldReplaceFunctionsButNotSymbols; } /* ViewController */ const char * title() override; View * view() override { return &m_contentView; } @@ -40,13 +40,12 @@ private: class ContentView : public View { public: constexpr static KDCoordinate k_topMargin = 50; + constexpr static KDColor k_backgroundColor = Palette::WallScreenDark; ContentView(SolutionsController * controller); void drawRect(KDContext * ctx, KDRect rect) const override; void setWarning(bool warning); void setWarningMessages(I18n::Message message0, I18n::Message message1); - SelectableTableView * selectableTableView() { - return &m_selectableTableView; - } + SelectableTableView * selectableTableView() { return &m_selectableTableView; } private: constexpr static KDCoordinate k_middleMargin = 50; int numberOfSubviews() const override; @@ -58,11 +57,26 @@ private: bool m_displayWarningMoreSolutions; }; + class MessageCell : public HighlightCell { + public: + MessageCell() : m_messageView(KDFont::SmallFont, (I18n::Message)0, 0.0f, k_verticalAlignment, KDColorBlack, SolutionsController::ContentView::k_backgroundColor) {} + void setBackgroundColor(KDColor color) { m_messageView.setBackgroundColor(color); } + void setHorizontalAlignment(float alignment) { m_messageView.setAlignment(alignment, k_verticalAlignment); } + void setMessage(I18n::Message message) { m_messageView.setMessage(message); } + private: + constexpr static float k_verticalAlignment = 0.8f; + int numberOfSubviews() const override { return 1; } + View * subviewAtIndex(int index) override { assert(index == 0); return &m_messageView; } + void layoutSubviews(bool force = false) override { m_messageView.setFrame(bounds(), force); } + MessageTextView m_messageView; + }; + // Cell types constexpr static int k_symbolCellType = 0; constexpr static int k_deltaCellType = 1; constexpr static int k_exactValueCellType = 2; constexpr static int k_approximateValueCellType = 3; + constexpr static int k_messageCellType = 4; // Heights and widths constexpr static KDCoordinate k_defaultCellHeight = 20; @@ -70,11 +84,19 @@ private: constexpr static int k_valueCellWidth = 190; // Number of cells - constexpr static int k_maxNumberOfVisibleCells = (Ion::Display::Height - 3 * Meric::TitleBarHeight - ContentView::k_topMargin) / k_defaultCellHeight + 1; + constexpr static int k_maxNumberOfVisibleCells = (Ion::Display::Height - 3 * Metric::TitleBarHeight - ContentView::k_topMargin) / k_defaultCellHeight + 1; static_assert(k_maxNumberOfVisibleCells <= EquationStore::k_maxNumberOfSolutions + Poincare::Expression::k_maxNumberOfVariables, "We can reduce the number of cells in Solver:SolutionsController."); - constexpr static int k_numberOfSymbolCells = k_maxNumberOfVisibleCells < EquationStore::k_maxNumberOfSolutions ? k_maxNumberOfVisibleCells : EquationStore::k_maxNumberOfSolutions; - constexpr static int k_numberOfExactValueCells = k_maxNumberOfVisibleCells < EquationStore::k_maxNumberOfExactSolutions ? k_maxNumberOfVisibleCells : EquationStore::k_maxNumberOfExactSolutions; + constexpr static int k_maxNumberOfSymbols = EquationStore::k_maxNumberOfSolutions + Poincare::Expression::k_maxNumberOfVariables; + constexpr static int k_numberOfSymbolCells = k_maxNumberOfVisibleCells < k_maxNumberOfSymbols ? k_maxNumberOfVisibleCells : k_maxNumberOfSymbols; + constexpr static int k_maxNumberOfExactValues = EquationStore::k_maxNumberOfExactSolutions + Poincare::Expression::k_maxNumberOfVariables; + constexpr static int k_numberOfExactValueCells = k_maxNumberOfVisibleCells < k_maxNumberOfExactValues ? k_maxNumberOfVisibleCells : k_maxNumberOfExactValues; constexpr static int k_numberOfApproximateValueCells = k_maxNumberOfVisibleCells < EquationStore::k_maxNumberOfApproximateSolutions ? k_maxNumberOfVisibleCells : EquationStore::k_maxNumberOfApproximateSolutions; + constexpr static int k_numberOfMessageCells = 2; + + bool usedUserVariables() const { + return m_equationStore->userVariablesUsed(); + } + int userVariablesMessageRow() const; EquationStore * m_equationStore; EvenOddBufferTextCell m_symbolCells[k_numberOfSymbolCells]; @@ -82,8 +104,9 @@ private: Poincare::Layout m_delta2Layout; Shared::ScrollableTwoExpressionsCell m_exactValueCells[k_numberOfExactValueCells]; EvenOddBufferTextCell m_approximateValueCells[k_numberOfApproximateValueCells]; + MessageCell m_messageCells[k_numberOfMessageCells]; ContentView m_contentView; - bool m_shouldReplaceFuncionsButNotSymbols; + bool m_shouldReplaceFunctionsButNotSymbols; }; } diff --git a/apps/statistics/statistics_context.cpp b/apps/statistics/statistics_context.cpp index deed4d3bf..96a891d43 100644 --- a/apps/statistics/statistics_context.cpp +++ b/apps/statistics/statistics_context.cpp @@ -10,7 +10,7 @@ using namespace Shared; namespace Statistics { const Expression StatisticsContext::expressionForSymbolAbstract(const SymbolAbstract & symbol, bool clone) { - if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isSeriesSymbol(symbol.name())) { + if (symbol.type() == ExpressionNode::Type::Symbol && Symbol::isSeriesSymbol(symbol.name(), nullptr)) { const char * seriesName = symbol.name(); assert(strlen(seriesName) == 2); diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index a02b7cff5..0e311f4b6 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -165,7 +165,7 @@ public: virtual int polynomialDegree(Context * context, const char * symbolName) const; /*!*/ virtual int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[]) const; /*!*/ virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly); - typedef bool (*isVariableTest)(const char * c); + typedef bool (*isVariableTest)(const char * c, Poincare::Context * context); virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const; virtual float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const; bool isOfType(Type * types, int length) const; diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index 47809d783..5a4fbc20c 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -61,8 +61,9 @@ public: // Symbol properties bool isSystemSymbol() const { return node()->isUnknown(); } const char * name() const { return node()->name(); } - static bool isSeriesSymbol(const char * c); - static bool isRegressionSymbol(const char * c); + // IsVariable tests + static bool isSeriesSymbol(const char * c, Poincare::Context * context); + static bool isRegressionSymbol(const char * c, Poincare::Context * context); // Expression Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index a0f9c8de9..6ad1e1fae 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -43,7 +43,7 @@ int SymbolNode::getVariables(Context * context, isVariableTest isVariable, char while(variables[variablesIndex] != 0) { variablesIndex+= maxSizeVariable; } - if (isVariable(m_name)) { + if (isVariable(m_name, context)) { int index = 0; while (variables[index] != 0) { if (strcmp(m_name, &variables[index]) == 0) { @@ -134,7 +134,7 @@ Symbol Symbol::Builder(CodePoint name) { return Symbol::Builder(buffer, codePointLength); } -bool Symbol::isSeriesSymbol(const char * c) { +bool Symbol::isSeriesSymbol(const char * c, Poincare::Context * context) { // [NV][1-3] if (c[2] == 0 && (c[0] == 'N' || c[0] == 'V') && c[1] >= '1' && c[1] <= '3') { return true; @@ -142,7 +142,7 @@ bool Symbol::isSeriesSymbol(const char * c) { return false; } -bool Symbol::isRegressionSymbol(const char * c) { +bool Symbol::isRegressionSymbol(const char * c, Poincare::Context * context) { // [XY][1-3] if (c[2] == 0 && (c[0] == 'X' || c[0] == 'Y') && c[1] >= '1' && c[1] <= '3') { return true; diff --git a/poincare/test/expression_properties.cpp b/poincare/test/expression_properties.cpp index af4a873f3..525352120 100644 --- a/poincare/test/expression_properties.cpp +++ b/poincare/test/expression_properties.cpp @@ -280,7 +280,7 @@ void assert_expression_has_variables(const char * expression, const char * varia Expression e = parse_expression(expression, &globalContext, false); constexpr static int k_maxVariableSize = Poincare::SymbolAbstract::k_maxNameSize; char variableBuffer[Expression::k_maxNumberOfVariables+1][k_maxVariableSize] = {{0}}; - int numberOfVariables = e.getVariables(&globalContext, [](const char * symbol) { return true; }, (char *)variableBuffer, k_maxVariableSize); + int numberOfVariables = e.getVariables(&globalContext, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)variableBuffer, k_maxVariableSize); quiz_assert_print_if_failure(trueNumberOfVariables == numberOfVariables, expression); if (numberOfVariables < 0) { // Too many variables