diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 2fd71b5a0..b23db2450 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -8,6 +8,7 @@ app_regression_test_src += $(addprefix apps/regression/,\ ) app_regression_test_src += $(addprefix apps/regression/model/,\ + affine_model.cpp \ cubic_model.cpp \ exponential_model.cpp \ linear_model.cpp \ diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n index 2e06f3bba..25ea97908 100644 --- a/apps/regression/base.de.i18n +++ b/apps/regression/base.de.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht" NumberOfDots = "Punktanzahl" Covariance = "Kovarianz" Linear = "Lineare" +Affine = "Affine" Quadratic = "Quadratische" Cubic = "Kubische" Quartic = "Biquadratische" diff --git a/apps/regression/base.en.i18n b/apps/regression/base.en.i18n index 891c30eb3..58cac5578 100644 --- a/apps/regression/base.en.i18n +++ b/apps/regression/base.en.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Value not reached in this window" NumberOfDots = "Number of points" Covariance = "Covariance" Linear = "Linear" +Affine = "Affine" Quadratic = "Quadratic" Cubic = "Cubic" Quartic = "Quartic" diff --git a/apps/regression/base.es.i18n b/apps/regression/base.es.i18n index 15b0fff73..d1c5ec86b 100644 --- a/apps/regression/base.es.i18n +++ b/apps/regression/base.es.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor no alcanzado en esta ventana" NumberOfDots = "Número de puntos" Covariance = "Covarianza" Linear = "Lineal" +Affine = "Affine" Quadratic = "Cuadrática" Cubic = "Cúbica" Quartic = "Cuártica" diff --git a/apps/regression/base.fr.i18n b/apps/regression/base.fr.i18n index 0d24b1bd8..be93f4aa8 100644 --- a/apps/regression/base.fr.i18n +++ b/apps/regression/base.fr.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valeur non atteinte dans cette fenêtre" NumberOfDots = "Nombre de points" Covariance = "Covariance" Linear = "Linéaire" +Affine = "Affine" Quadratic = "Quadratique" Cubic = "Cubique" Quartic = "Quartique" diff --git a/apps/regression/base.pt.i18n b/apps/regression/base.pt.i18n index 7ec8ab7b5..9590cb914 100644 --- a/apps/regression/base.pt.i18n +++ b/apps/regression/base.pt.i18n @@ -10,6 +10,7 @@ ValueNotReachedByRegression = "Valor não alcançado nesta janela" NumberOfDots = "Número de pontos" Covariance = "Covariancia" Linear = "Linear" +Affine = "Affine" Quadratic = "Quadrática" Cubic = "Cúbica" Quartic = "Quarto grau" diff --git a/apps/regression/base.universal.i18n b/apps/regression/base.universal.i18n index bb896dc36..a1bf08724 100644 --- a/apps/regression/base.universal.i18n +++ b/apps/regression/base.universal.i18n @@ -1,3 +1,4 @@ +AffineRegressionFormula = " y=a·x " QuadraticRegressionFormula = " y=a·x^2+b·x+c " CubicRegressionFormula = " y=a·x^3+b·x^2+c·x+d " QuarticRegressionFormula = " y=a·x^4+b·x^3+c·x^2+d·x+e " diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index f582d315e..d2e6cdef2 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -187,14 +187,15 @@ void GraphController::reloadBannerView() { coefficientName++; } - if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear) { + if (m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Linear || m_store->seriesRegressionType(*m_selectedSeriesIndex) == Model::Type::Affine) { + int index = model->numberOfCoefficients(); // Set "r=..." numberOfChar = 0; legend = " r="; double r = m_store->correlationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); numberOfChar += PoincareHelpers::ConvertFloatToText(r, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits); - m_bannerView.subTextAtIndex(2)->setText(buffer); + m_bannerView.subTextAtIndex(0+index)->setText(buffer); // Set "r2=..." numberOfChar = 0; @@ -202,11 +203,11 @@ void GraphController::reloadBannerView() { double r2 = m_store->squaredCorrelationCoefficient(*m_selectedSeriesIndex); numberOfChar += strlcpy(buffer, legend, bufferSize); numberOfChar += PoincareHelpers::ConvertFloatToText(r2, buffer + numberOfChar, bufferSize - numberOfChar, Preferences::LargeNumberOfSignificantDigits); - m_bannerView.subTextAtIndex(3)->setText(buffer); + m_bannerView.subTextAtIndex(1+index)->setText(buffer); // Clean the last subview buffer[0] = 0; - m_bannerView.subTextAtIndex(4)->setText(buffer); + m_bannerView.subTextAtIndex(2+index)->setText(buffer); } else { // Empty all non used subviews diff --git a/apps/regression/model/affine_model.cpp b/apps/regression/model/affine_model.cpp new file mode 100644 index 000000000..0786bafe5 --- /dev/null +++ b/apps/regression/model/affine_model.cpp @@ -0,0 +1,51 @@ +#include "affine_model.h" +#include "../store.h" +#include +#include +#include + +using namespace Poincare; + +namespace Regression { + +Layout AffineModel::layout() { + if (m_layout.isUninitialized()) { + const char * s = "a·X"; + m_layout = LayoutHelper::String(s, strlen(s), k_layoutFont); + } + return m_layout; +} + +double AffineModel::evaluate(double * modelCoefficients, double x) const { + double a = modelCoefficients[0]; + return a*x; +} + +double AffineModel::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) { + double a = modelCoefficients[0]; + double b = modelCoefficients[1]; + if (a == 0) { + return NAN; + } + return y-b; +} + +void AffineModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { + modelCoefficients[0] = store->slope(series); + modelCoefficients[1] = store->yIntercept(series); +} + +double AffineModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { + if (derivateCoefficientIndex == 0) { + // Derivate: x + return x; + } + if (derivateCoefficientIndex == 1) { + // Derivate: 1; + return 1; + } + assert(false); + return 0.0; +} + +} diff --git a/apps/regression/model/affine_model.h b/apps/regression/model/affine_model.h new file mode 100644 index 000000000..42dbf7a7e --- /dev/null +++ b/apps/regression/model/affine_model.h @@ -0,0 +1,24 @@ +#ifndef REGRESSION_AFFINE_MODEL_H +#define REGRESSION_AFFINE_MODEL_H + +#include "model.h" + +namespace Regression { + +class AffineModel : public Model { +public: + using Model::Model; + Poincare::Layout layout() override; + I18n::Message formulaMessage() const override { return I18n::Message::AffineRegressionFormula; } + double evaluate(double * modelCoefficients, double x) const override; + double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; + void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; + double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; + int numberOfCoefficients() const override { return 1; } + int bannerLinesCount() const override { return 2; } +}; + +} + + +#endif diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h index c018a3528..e38cebb01 100644 --- a/apps/regression/model/model.h +++ b/apps/regression/model/model.h @@ -15,16 +15,17 @@ class Model { public: enum class Type : uint8_t { Linear = 0, - Quadratic = 1, - Cubic = 2, - Quartic = 3, - Logarithmic = 4, - Exponential = 5, - Power = 6, - Trigonometric = 7, - Logistic = 8 + Affine = 1, + Quadratic = 2, + Cubic = 3, + Quartic = 4, + Logarithmic = 5, + Exponential = 6, + Power = 7, + Trigonometric = 8, + Logistic = 9 }; - static constexpr int k_numberOfModels = 9; + static constexpr int k_numberOfModels = 10; static constexpr int k_maxNumberOfCoefficients = 5; // This has to verify: k_maxNumberOfCoefficients < Matrix::k_maxNumberOfCoefficients virtual ~Model() = default; virtual Poincare::Layout layout() = 0; diff --git a/apps/regression/regression_controller.cpp b/apps/regression/regression_controller.cpp index 9cc70181f..fb348cd56 100644 --- a/apps/regression/regression_controller.cpp +++ b/apps/regression/regression_controller.cpp @@ -82,7 +82,7 @@ HighlightCell * RegressionController::reusableCell(int index, int type) { void RegressionController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { assert(i == 0); assert(j >= 0 && j < k_numberOfRows); - I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; + I18n::Message messages[k_numberOfRows] = {I18n::Message::Linear, I18n::Message::Affine, I18n::Message::Quadratic, I18n::Message::Cubic, I18n::Message::Quartic, I18n::Message::Logarithmic, I18n::Message::Exponential, I18n::Message::Power, I18n::Message::Trigonometrical, I18n::Message::Logistic}; MessageTableCellWithExpression * castedCell = static_cast(cell); castedCell->setMessage(messages[j]); castedCell->setLayout(m_store->regressionModel((Model::Type) j)->layout()); diff --git a/apps/regression/regression_controller.h b/apps/regression/regression_controller.h index f0875cc28..bb32f5dca 100644 --- a/apps/regression/regression_controller.h +++ b/apps/regression/regression_controller.h @@ -31,7 +31,7 @@ public: int numberOfRows() const override { return k_numberOfRows; } void willDisplayCellAtLocation(HighlightCell * cell, int i, int j) override; private: - constexpr static int k_numberOfRows = 9; + constexpr static int k_numberOfRows = 10; constexpr static int k_numberOfCells = 6; // (240 - 70) / 35 MessageTableCellWithExpression m_regressionCells[k_numberOfCells]; SelectableTableView m_selectableTableView; diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index e7d827cb2..7a98b4f96 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -11,7 +11,7 @@ using namespace Shared; namespace Regression { -static_assert(Model::k_numberOfModels == 9, "Number of models changed, Regression::Store() needs to adapt"); +static_assert(Model::k_numberOfModels == 10, "Number of models changed, Regression::Store() needs to adapt"); static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regression::Store() needs to adapt (m_seriesChecksum)"); Store::Store() : @@ -285,7 +285,7 @@ double Store::squaredCorrelationCoefficient(int series) const { } Model * Store::regressionModel(int index) { - Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; + Model * models[Model::k_numberOfModels] = {&m_linearModel, &m_affineModel, &m_quadraticModel, &m_cubicModel, &m_quarticModel, &m_logarithmicModel, &m_exponentialModel, &m_powerModel, &m_trigonometricModel, &m_logisticModel}; return models[index]; } diff --git a/apps/regression/store.h b/apps/regression/store.h index 2cc6f600f..bb97b2774 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -11,6 +11,7 @@ #include "model/quadratic_model.h" #include "model/quartic_model.h" #include "model/trigonometric_model.h" +#include "model/affine_model.h" #include "../shared/interactive_curve_view_range.h" #include "../shared/double_pair_store.h" #include @@ -79,6 +80,7 @@ private: uint32_t m_seriesChecksum[k_numberOfSeries]; Model::Type m_regressionTypes[k_numberOfSeries]; LinearModel m_linearModel; + AffineModel m_affineModel; QuadraticModel m_quadraticModel; CubicModel m_cubicModel; QuarticModel m_quarticModel;