From 6319d08fc91319192f88830f8cbc619f4108dce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 13 Jan 2017 11:26:10 +0100 Subject: [PATCH 1/5] [poincare] Add a class exponential Change-Id: I74cad802e239088cd388f5293b8574f80a2d312e --- poincare/Makefile | 1 + poincare/include/poincare.h | 1 + poincare/include/poincare/exponential.h | 15 +++++++++++++ poincare/include/poincare/expression.h | 1 + poincare/src/exponential.cpp | 28 +++++++++++++++++++++++++ poincare/src/expression_lexer.l | 1 + 6 files changed, 47 insertions(+) create mode 100644 poincare/include/poincare/exponential.h create mode 100644 poincare/src/exponential.cpp diff --git a/poincare/Makefile b/poincare/Makefile index 62c6c05e0..20436b40b 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -8,6 +8,7 @@ objs += $(addprefix poincare/src/,\ binary_operation.o\ cosine.o\ derivative.o\ + exponential.o\ expression.o\ expression_lexer.o\ expression_parser.o\ diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index 1d05b1af7..8074a4bba 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/include/poincare/exponential.h b/poincare/include/poincare/exponential.h new file mode 100644 index 000000000..72de97838 --- /dev/null +++ b/poincare/include/poincare/exponential.h @@ -0,0 +1,15 @@ +#ifndef POINCARE_EXPONENTIAL_H +#define POINCARE_EXPONENTIAL_H + +#include + +class Exponential : public Function { +public: + Exponential(); + float approximate(Context & context) const override; + Type type() const override; + Expression * cloneWithDifferentOperands(Expression ** newOperands, + int numberOfOperands, bool cloneOperands = true) const override; +}; + +#endif diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 2d7fa2041..d2f5872c5 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -13,6 +13,7 @@ class Expression { Addition, Cosine, Derivative, + Exponential, Float, Integer, Logarithm, diff --git a/poincare/src/exponential.cpp b/poincare/src/exponential.cpp new file mode 100644 index 000000000..6f55144ab --- /dev/null +++ b/poincare/src/exponential.cpp @@ -0,0 +1,28 @@ +#include + +extern "C" { +#include +#include +} + +Exponential::Exponential() : + Function("exp") +{ +} + +Expression::Type Exponential::type() const { + return Type::Exponential; +} + +Expression * Exponential::cloneWithDifferentOperands(Expression** newOperands, + int numberOfOperands, bool cloneOperands) const { + assert(numberOfOperands == 1); + assert(newOperands != nullptr); + Exponential * e = new Exponential(); + e->setArgument(newOperands, numberOfOperands, cloneOperands); + return e; +} + +float Exponential::approximate(Context& context) const { + return expf(m_args[0]->approximate(context)); +} diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 80b736893..ba1d33f35 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -82,6 +82,7 @@ E { return EE; } abs { poincare_expression_yylval.expression = new AbsoluteValue(); return FUNCTION; } diff { poincare_expression_yylval.expression = new Derivative(); return FUNCTION; } ans { poincare_expression_yylval.character = Symbol::Ans; return SYMBOL; } +exp { poincare_expression_yylval.expression = new Exponential(); return FUNCTION; } sin { poincare_expression_yylval.expression = new Sine(); return FUNCTION; } cos { poincare_expression_yylval.expression = new Cosine(); return FUNCTION; } tan { poincare_expression_yylval.expression = new Tangent(); return FUNCTION; } From 3ccd7417bb7dd52173278853a5a7c2bfebaeda41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 13 Jan 2017 13:34:38 +0100 Subject: [PATCH 2/5] [poincare] Create a class opposite Change-Id: Ib903a31d7ba76cd76b95464f701ea2605ff7392d --- poincare/Makefile | 1 + poincare/include/poincare.h | 1 + poincare/include/poincare/expression.h | 1 + poincare/include/poincare/opposite.h | 26 +++++++++ poincare/src/expression_parser.y | 5 +- poincare/src/opposite.cpp | 80 ++++++++++++++++++++++++++ 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 poincare/include/poincare/opposite.h create mode 100644 poincare/src/opposite.cpp diff --git a/poincare/Makefile b/poincare/Makefile index 20436b40b..964bd4a95 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -23,6 +23,7 @@ objs += $(addprefix poincare/src/,\ matrix.o\ matrix_data.o\ nth_root.o\ + opposite.o\ parenthesis.o\ power.o\ product.o\ diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index 8074a4bba..64961d0d7 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index d2f5872c5..ae9043ad8 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -19,6 +19,7 @@ class Expression { Logarithm, Matrix, NthRoot, + Opposite, Fraction, Parenthesis, Power, diff --git a/poincare/include/poincare/opposite.h b/poincare/include/poincare/opposite.h new file mode 100644 index 000000000..e17796cec --- /dev/null +++ b/poincare/include/poincare/opposite.h @@ -0,0 +1,26 @@ +#ifndef POINCARE_OPPOSITE_H +#define POINCARE_OPPOSITE_H + +#include +#include +#include + +class Opposite : public Expression { + public: + Opposite(Expression * operand, bool cloneOperands = true); + ~Opposite(); + const Expression * operand(int i) const override; + int numberOfOperands() const override; + Expression * clone() const override; + Expression * evaluate(Context& context) const override; + ExpressionLayout * createLayout() const override; + float approximate(Context& context) const override; + Type type() const override; + Expression * cloneWithDifferentOperands(Expression** newOperands, + int numnerOfOperands, bool cloneOperands = true) const override; + protected: + Expression * m_operand; + Expression * evaluateOnMatrix(Matrix * m, Context& context) const; +}; + +#endif diff --git a/poincare/src/expression_parser.y b/poincare/src/expression_parser.y index d663cc4a9..26ac384fb 100644 --- a/poincare/src/expression_parser.y +++ b/poincare/src/expression_parser.y @@ -112,13 +112,9 @@ mtxData: number: DIGITS { $$ = new Integer($1.address, false); } - | MINUS DIGITS { $$ = new Integer($2.address, true); } | DIGITS DOT DIGITS { $$ = new Float($1.address, $1.length, false, $3.address, $3.length, nullptr, 0, false); } - | MINUS DIGITS DOT DIGITS { $$ = new Float($2.address, $2.length, true, $4.address, $4.length, nullptr, 0, false); } | DIGITS DOT DIGITS EE DIGITS { $$ = new Float($1.address, $1.length, false, $3.address, $3.length, $5.address, $5.length, false); } - | MINUS DIGITS DOT DIGITS EE DIGITS { $$ = new Float($2.address, $2.length, true, $4.address, $4.length, $6.address, $6.length, false); } | DIGITS DOT DIGITS EE MINUS DIGITS { $$ = new Float($1.address, $1.length, false, $3.address, $3.length, $6.address, $6.length, true); } - | MINUS DIGITS DOT DIGITS EE MINUS DIGITS { $$ = new Float($2.address, $2.length, true, $4.address, $4.length, $7.address, $7.length, true); } exp: number { $$ = $1; } @@ -128,6 +124,7 @@ exp: | exp MULTIPLY exp { Expression * terms[2] = {$1,$3}; $$ = new Product(terms, false); } | exp DIVIDE exp { Expression * terms[2] = {$1,$3}; $$ = new Fraction(terms, false); } | exp POW exp { Expression * terms[2] = {$1,$3}; $$ = new Power(terms, false); } + | MINUS exp { $$ = new Opposite($2, false); } | LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = new Parenthesis($2, false); } | LEFT_BRACKET mtxData RIGHT_BRACKET { $$ = new Matrix($2); } | FUNCTION LEFT_PARENTHESIS lstData RIGHT_PARENTHESIS { $$ = $1; $1->setArgument($3, false);} diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp new file mode 100644 index 000000000..2a090ebc1 --- /dev/null +++ b/poincare/src/opposite.cpp @@ -0,0 +1,80 @@ +#include +extern "C" { +#include +#include +} +#include "layout/horizontal_layout.h" +#include "layout/string_layout.h" + +Opposite::Opposite(Expression * operand, bool cloneOperands) { + assert(operand != nullptr); + if (cloneOperands) { + m_operand = operand->clone(); + } else { + m_operand = operand; + } +} + +Opposite::~Opposite() { + delete m_operand; +} + +const Expression * Opposite::operand(int i) const { + assert(i == 0); + return m_operand; +} + +int Opposite::numberOfOperands() const { + return 1; +} + +Expression * Opposite::clone() const { + return this->cloneWithDifferentOperands((Expression**)&m_operand, 1, true); +} + +Expression * Opposite::evaluate(Context& context) const { + Expression * operandEvalutation = m_operand->evaluate(context); + if (operandEvalutation == nullptr) { + return nullptr; + } + Expression * result = nullptr; + if (operandEvalutation->type() == Type::Float) { + result = new Float(this->approximate(context)); + } + if (operandEvalutation->type() == Type::Matrix) { + result = evaluateOnMatrix((Matrix *)operandEvalutation, context); + } + delete operandEvalutation; + return result; +} + +ExpressionLayout * Opposite::createLayout() const { + ExpressionLayout** children_layouts = (ExpressionLayout **)malloc(2*sizeof(ExpressionLayout *)); + char string[2] = {'-', '\0'}; + children_layouts[0] = new StringLayout(string, 1); + children_layouts[1] = m_operand->createLayout(); + return new HorizontalLayout(children_layouts, 2); +} + +float Opposite::approximate(Context& context) const { + return -m_operand->approximate(context); +} + +Expression::Type Opposite::type() const { + return Expression::Type::Opposite; +} + +Expression * Opposite::cloneWithDifferentOperands(Expression** newOperands, + int numberOfOperands, bool cloneOperands) const { + assert(newOperands != nullptr); + assert(numberOfOperands == 1); + return new Opposite(newOperands[0], cloneOperands); +} + +Expression * Opposite::evaluateOnMatrix(Matrix * m, Context& context) const { + Expression * operands[m->numberOfRows() * m->numberOfColumns()]; + for (int i = 0; i < m->numberOfRows() * m->numberOfColumns(); i++) { + operands[i] = new Float(- m->operand(i)->approximate(context)); + } + return new Matrix(operands, m->numberOfRows() * m->numberOfColumns(), m->numberOfColumns(), m->numberOfRows(), false); +} From d9877e61811f71665d3498525ab618437b95dc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 13 Jan 2017 14:02:27 +0100 Subject: [PATCH 3/5] [poincare] Create a first version of a class integral (approx method is to be improved with QUADPACK algo) Change-Id: I2afed19640c9be3072220b15b14ebff7a9671218 --- apps/toolbox_controller.cpp | 2 +- poincare/Makefile | 1 + poincare/include/poincare.h | 1 + poincare/include/poincare/expression.h | 1 + poincare/include/poincare/integral.h | 18 +++++++++ poincare/src/expression_lexer.l | 1 + poincare/src/integral.cpp | 51 ++++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 poincare/include/poincare/integral.h create mode 100644 poincare/src/integral.cpp diff --git a/apps/toolbox_controller.cpp b/apps/toolbox_controller.cpp index 8cd3d3b18..6f78f319d 100644 --- a/apps/toolbox_controller.cpp +++ b/apps/toolbox_controller.cpp @@ -6,7 +6,7 @@ * and the text which would be edited by clicking on the row. When the node is a * subtree, the edited text is set at nullptr. */ -const ToolboxNode calculChildren[4] = {ToolboxNode("diff(,)", "Nombre derive"), ToolboxNode("Int(,,)", "Integrale"), ToolboxNode("sum(,,)", "Somme"), ToolboxNode("product(,,)", "Produit")}; +const ToolboxNode calculChildren[4] = {ToolboxNode("diff(,)", "Nombre derive"), ToolboxNode("int(,,)", "Integrale"), ToolboxNode("sum(,,)", "Somme"), ToolboxNode("product(,,)", "Produit")}; const ToolboxNode complexChildren[5] = {ToolboxNode("abs()", "Module"), ToolboxNode("arg()", "Argument"), ToolboxNode("re()", "Partie reelle"), ToolboxNode("im()", "Partie imaginaire"), ToolboxNode("conj()", "Conjugue")}; const ToolboxNode probabilityChildren[4] = {ToolboxNode("binomial()", "Combinaison"), ToolboxNode("permute(,)", "Arrangement"), ToolboxNode("random(,)", "Nombre aleatoire"), ToolboxNode("gamma()", "Fonction gamma")}; const ToolboxNode arithmeticChildren[4] = {ToolboxNode("gcd()", "PGCD"), ToolboxNode("lcm()", "PPCM"), ToolboxNode("rem()", "Reste division euclidienne"), ToolboxNode("quo()","Quotien division euclidienne")}; diff --git a/poincare/Makefile b/poincare/Makefile index 964bd4a95..8bee4c7ef 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -17,6 +17,7 @@ objs += $(addprefix poincare/src/,\ function.o\ global_context.o\ integer.o\ + integral.o\ list_data.o\ leaf_expression.o\ logarithm.o\ diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index 64961d0d7..9d74b0107 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index ae9043ad8..76a4878cd 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -16,6 +16,7 @@ class Expression { Exponential, Float, Integer, + Integral, Logarithm, Matrix, NthRoot, diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h new file mode 100644 index 000000000..71b94ebf8 --- /dev/null +++ b/poincare/include/poincare/integral.h @@ -0,0 +1,18 @@ +#ifndef POINCARE_INTEGRAL_H +#define POINCARE_INTEGRAL_H + +#include +#include + +class Integral : public Function { +public: + Integral(); + float approximate(Context & context) const override; + Type type() const override; + Expression * cloneWithDifferentOperands(Expression ** newOperands, + int numberOfOperands, bool cloneOperands = true) const override; +private: + float functionValueAtAbscissa(float x, XContext xcontext) const; +}; + +#endif diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index ba1d33f35..b98123782 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -85,6 +85,7 @@ ans { poincare_expression_yylval.character = Symbol::Ans; return SYMBOL; } exp { poincare_expression_yylval.expression = new Exponential(); return FUNCTION; } sin { poincare_expression_yylval.expression = new Sine(); return FUNCTION; } cos { poincare_expression_yylval.expression = new Cosine(); return FUNCTION; } +int { poincare_expression_yylval.expression = new Integral(); return FUNCTION; } tan { poincare_expression_yylval.expression = new Tangent(); return FUNCTION; } log { poincare_expression_yylval.expression = new Logarithm(); return FUNCTION; } root { poincare_expression_yylval.expression = new NthRoot(); return FUNCTION; } diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp new file mode 100644 index 000000000..15fbb9e7e --- /dev/null +++ b/poincare/src/integral.cpp @@ -0,0 +1,51 @@ +#include +#include +#include +#include +extern "C" { +#include +#include +} + +Integral::Integral() : + Function("int") +{ +} + +Expression::Type Integral::type() const { + return Type::Integral; +} + +Expression * Integral::cloneWithDifferentOperands(Expression** newOperands, + int numberOfOperands, bool cloneOperands) const { + assert(numberOfOperands == 3); + assert(newOperands != nullptr); + Integral * i = new Integral(); + i->setArgument(newOperands, numberOfOperands, cloneOperands); + return i; +} + +float Integral::approximate(Context& context) const { + /* We here use Gauss-Legendre quadrature with n = 5 */ + XContext xContext = XContext(&context); + static float x[5]={0.1488743389f, 0.4333953941f, 0.6794095682f, 0.8650633666f, 0.9739065285f}; + static float w[5]={0.2955242247f, 0.2692667193f, 0.2190863625f, 0.1494513491f, 0.0666713443f}; + float a = m_args[1]->approximate(context); + float b = m_args[2]->approximate(context); + float xm = 0.5f*(a+b); + float xr = 0.5f*(b-a); + float result = 0.0f; + for (int j = 0; j < 5; j++) { + float dx = xr * x[j]; + result += w[j]*(functionValueAtAbscissa(xm+dx, xContext) + functionValueAtAbscissa(xm-dx, xContext)); + } + result *= xr; + return result; +} + +float Integral::functionValueAtAbscissa(float x, XContext xContext) const { + Float e = Float(x); + Symbol xSymbol = Symbol('x'); + xContext.setExpressionForSymbolName(&e, &xSymbol); + return m_args[0]->approximate(xContext); +} From 5d746ff861cd54cffe997fb27a9080c97352be6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 13 Jan 2017 15:13:12 +0100 Subject: [PATCH 4/5] [poincare] Add comments to integral approximation for future improvements Change-Id: I44131774674cb7db3c8c0450a29faa5a08120ae6 --- poincare/src/integral.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index 15fbb9e7e..404ca9902 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -26,10 +26,19 @@ Expression * Integral::cloneWithDifferentOperands(Expression** newOperands, } float Integral::approximate(Context& context) const { - /* We here use Gauss-Legendre quadrature with n = 5 */ + /* We here use Gauss-Legendre quadrature with n = 5 + * Gauss-Legendre abscissae and weights taken from + * http://www.holoborodko.com/pavel/numerical-methods/numerical-integration/*/ + /* TODO: implement an adaptive quadrature version + * Use Gauss-Konrad quadrature (the one used in GNU scientific library)? + * QAGS and QAGI from netlib? + * Add to this version: + * - assess error + * - higher degree if error > threshold + * - find a way to reuse x and w ? Or useless, store all values ? */ XContext xContext = XContext(&context); - static float x[5]={0.1488743389f, 0.4333953941f, 0.6794095682f, 0.8650633666f, 0.9739065285f}; - static float w[5]={0.2955242247f, 0.2692667193f, 0.2190863625f, 0.1494513491f, 0.0666713443f}; + static float x[5]={0.1488743389816312108848260f, 0.4333953941292471907992659f, 0.6794095682990244062343274f, 0.8650633666889845107320967f, 0.9739065285171717200779640f}; + static float w[5]={0.2955242247147528701738930f, 0.2692667193099963550912269f, 0.2190863625159820439955349f, 0.1494513491505805931457763f, 0.0666713443086881375935688f}; float a = m_args[1]->approximate(context); float b = m_args[2]->approximate(context); float xm = 0.5f*(a+b); From 479213668f0fba7d76b2f3f07d6e6cc2101b6177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Fri, 13 Jan 2017 15:18:37 +0100 Subject: [PATCH 5/5] [apps] Fix bug: display 'n' when clicking on XNT in sum and product function Change-Id: Ic3151274a6ee8d0229ec8fda29f911923d6c026c --- apps/expression_text_field_delegate.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/expression_text_field_delegate.cpp b/apps/expression_text_field_delegate.cpp index b6c8aa125..ad0bf4e8a 100644 --- a/apps/expression_text_field_delegate.cpp +++ b/apps/expression_text_field_delegate.cpp @@ -9,13 +9,16 @@ const char * ExpressionTextFieldDelegate::XNT() { bool ExpressionTextFieldDelegate::cursorInToken(TextField * textField, const char * token) { const char * text = textField->text(); - int cursorLocation = textField->cursorLocation(); + int location = textField->cursorLocation(); int tokenLength = strlen(token); - if (cursorLocation - tokenLength < 0) { + while (text[location] != '(') { + location --; + } + if (location - tokenLength < 0) { return false; } char previousToken[10]; - strlcpy(previousToken, text+cursorLocation-tokenLength, tokenLength+1); + strlcpy(previousToken, text+location-tokenLength, tokenLength+1); if (strcmp(previousToken, token) == 0) { return true; } @@ -61,7 +64,7 @@ bool ExpressionTextFieldDelegate::textFieldDidReceiveEvent(TextField * textField textField->setEditing(true); textField->setText(""); } - if (cursorInToken(textField, "sum(") || cursorInToken(textField, "product(")) { + if (cursorInToken(textField, "sum") || cursorInToken(textField, "product")) { textField->insertTextAtLocation("n", textField->cursorLocation()); textField->setCursorLocation(textField->cursorLocation()+strlen("n")); return true;