[apps/solver] Display user variables in the solutions

This commit is contained in:
Léa Saviot
2020-01-27 17:58:26 +01:00
parent afbca672fd
commit db0e7f0d8b
15 changed files with 198 additions and 70 deletions

View File

@@ -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);

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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;

View File

@@ -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;
};
}

View File

@@ -8,6 +8,7 @@
#include <poincare/horizontal_layout.h>
#include <poincare/preferences.h>
#include <poincare/symbol_abstract.h>
#include <poincare/symbol.h>
#include <poincare/vertical_offset_layout.h>
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<MessageCell *>(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<EvenOddExpressionCell *>(cell);
deltaCell->setLayout(m_delta2Layout);
} else {
EvenOddBufferTextCell * symbolCell = static_cast<EvenOddBufferTextCell *>(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<EvenOddBufferTextCell *>(cell);
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char bufferValue[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision);
valueCell->setText(bufferValue);
} else {
ScrollableTwoExpressionsCell * valueCell = static_cast<ScrollableTwoExpressionsCell *>(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<EvenOddBufferTextCell *>(cell);
constexpr int precision = Preferences::LargeNumberOfSignificantDigits;
constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision);
char bufferValue[bufferSize];
PoincareHelpers::ConvertFloatToText<double>(m_equationStore->approximateSolutionAtIndex(j), bufferValue, bufferSize, precision);
valueCell->setText(bufferValue);
} else {
// Values of the solutions or discriminant
ScrollableTwoExpressionsCell * valueCell = static_cast<ScrollableTwoExpressionsCell *>(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<ScrollableTwoExpressionsCell *>(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<EvenOddCell *>(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();
}
}

View File

@@ -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;
};
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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