From 3292bdfd44b49a42c73b1bb11d7cfb4a92c24444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Thu, 13 Dec 2018 11:08:12 +0100 Subject: [PATCH] [poincare] Sketch of complex reduction as part of the reduce routine --- poincare/Makefile | 1 + poincare/include/poincare/absolute_value.h | 1 + poincare/include/poincare/arc_tangent.h | 1 + .../include/poincare/binomial_coefficient.h | 1 + poincare/include/poincare/ceiling.h | 1 + poincare/include/poincare/complex_argument.h | 1 + poincare/include/poincare/complex_cartesian.h | 25 +- poincare/include/poincare/constant.h | 7 + poincare/include/poincare/cosine.h | 1 + poincare/include/poincare/derivative.h | 1 + poincare/include/poincare/division_quotient.h | 2 + .../include/poincare/division_remainder.h | 2 + poincare/include/poincare/expression.h | 6 +- poincare/include/poincare/expression_node.h | 4 +- poincare/include/poincare/factorial.h | 2 + poincare/include/poincare/floor.h | 2 + poincare/include/poincare/frac_part.h | 2 + .../include/poincare/great_common_divisor.h | 2 + poincare/include/poincare/imaginary_part.h | 2 + poincare/include/poincare/integral.h | 2 + .../include/poincare/least_common_multiple.h | 2 + poincare/include/poincare/matrix_dimension.h | 1 + .../include/poincare/n_ary_expression_node.h | 8 + poincare/include/poincare/number.h | 1 + .../include/poincare/permute_coefficient.h | 2 + poincare/include/poincare/power.h | 1 + poincare/include/poincare/randint.h | 1 + poincare/include/poincare/random.h | 1 + poincare/include/poincare/real_part.h | 1 + poincare/include/poincare/round.h | 1 + poincare/include/poincare/sign_function.h | 1 + poincare/include/poincare/sine.h | 1 + poincare/include/poincare/tangent.h | 2 + poincare/src/addition.cpp | 37 ++- poincare/src/complex_cartesian.cpp | 259 ++++++++++++++++++ poincare/src/complex_helper.cpp | 5 +- poincare/src/constant.cpp | 17 ++ poincare/src/expression.cpp | 10 +- poincare/src/multiplication.cpp | 43 ++- poincare/src/n_ary_expression_node.cpp | 22 ++ poincare/src/power.cpp | 86 +++++- 41 files changed, 542 insertions(+), 26 deletions(-) create mode 100644 poincare/src/complex_cartesian.cpp diff --git a/poincare/Makefile b/poincare/Makefile index a30d17d52..17fede897 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -49,6 +49,7 @@ objs += $(addprefix poincare/src/,\ ceiling.o\ complex.o\ complex_argument.o\ + complex_cartesian.o\ complex_helper.o\ confidence_interval.o\ conjugate.o\ diff --git a/poincare/include/poincare/absolute_value.h b/poincare/include/poincare/absolute_value.h index 482508e9c..7e33f4660 100644 --- a/poincare/include/poincare/absolute_value.h +++ b/poincare/include/poincare/absolute_value.h @@ -26,6 +26,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Approximation template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { diff --git a/poincare/include/poincare/arc_tangent.h b/poincare/include/poincare/arc_tangent.h index d6ce0f626..5175dc1c0 100644 --- a/poincare/include/poincare/arc_tangent.h +++ b/poincare/include/poincare/arc_tangent.h @@ -25,6 +25,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); } private: // Layout diff --git a/poincare/include/poincare/binomial_coefficient.h b/poincare/include/poincare/binomial_coefficient.h index fe8395167..da1c17ecd 100644 --- a/poincare/include/poincare/binomial_coefficient.h +++ b/poincare/include/poincare/binomial_coefficient.h @@ -21,6 +21,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override{ return Type::BinomialCoefficient; } diff --git a/poincare/include/poincare/ceiling.h b/poincare/include/poincare/ceiling.h index 185d18dae..91da42199 100644 --- a/poincare/include/poincare/ceiling.h +++ b/poincare/include/poincare/ceiling.h @@ -21,6 +21,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override { return Type::Ceiling; } diff --git a/poincare/include/poincare/complex_argument.h b/poincare/include/poincare/complex_argument.h index f431b1152..891bd222b 100644 --- a/poincare/include/poincare/complex_argument.h +++ b/poincare/include/poincare/complex_argument.h @@ -20,6 +20,7 @@ public: #endif // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override { return Type::ComplexArgument; } diff --git a/poincare/include/poincare/complex_cartesian.h b/poincare/include/poincare/complex_cartesian.h index 3f996c3ef..a8f7a25c3 100644 --- a/poincare/include/poincare/complex_cartesian.h +++ b/poincare/include/poincare/complex_cartesian.h @@ -2,7 +2,7 @@ #define POINCARE_COMPLEX_CARTESIAN_H #include -#include +#include namespace Poincare { @@ -26,19 +26,42 @@ private: // Evaluation Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { assert(false); return Evaluation(); } Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { assert(false); return Evaluation(); } + // Simplification + Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; }; class ComplexCartesian final : public Expression { public: ComplexCartesian() : Expression() {} + ComplexCartesian(const ComplexCartesianNode * node) : Expression(node) {} + static ComplexCartesian Builder() { ComplexCartesianNode * node = TreePool::sharedPool()->createTreeNode(); return ComplexCartesian(node); } static ComplexCartesian Builder(Expression child0, Expression child1) { return ComplexCartesian(child0, child1); } + + // Getters Expression real() { return childAtIndex(0); } Expression imag() { return childAtIndex(1); } + + // Simplification + Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + + // Common operations (done in-place) + Expression squareNorm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression norm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression argument(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian inverse(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian squareRoot(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian powerInteger(int n, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian multiply(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian power(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: ComplexCartesian(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { replaceChildAtIndexInPlace(0, child0); replaceChildAtIndexInPlace(1, child1); } + static Multiplication squareRootHelper(Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static Expression powerHelper(Expression norm, Expression trigo, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; diff --git a/poincare/include/poincare/constant.h b/poincare/include/poincare/constant.h index 4d91e8ca9..7d7f28110 100644 --- a/poincare/include/poincare/constant.h +++ b/poincare/include/poincare/constant.h @@ -18,6 +18,7 @@ public: #endif // Complex + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override; ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override; // Expression Properties @@ -36,6 +37,9 @@ public: bool isPi() const { return isConstantChar(Ion::Charset::SmallPi); } bool isExponential() const { return isConstantChar(Ion::Charset::Exponential); } bool isIComplex() const { return isConstantChar(Ion::Charset::IComplex); } + + // Simplification + Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; private: char m_name[0]; // MUST be the last member variable @@ -54,6 +58,9 @@ public: bool isExponential() const { return node()->isExponential(); } bool isIComplex() const { return node()->isIComplex(); } + // Simplification + Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + private: ConstantNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h index 757ca2315..5da48dacc 100644 --- a/poincare/include/poincare/cosine.h +++ b/poincare/include/poincare/cosine.h @@ -22,6 +22,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); } // Properties Type type() const override { return Type::Cosine; } diff --git a/poincare/include/poincare/derivative.h b/poincare/include/poincare/derivative.h index 1ba1d339f..44c01b74f 100644 --- a/poincare/include/poincare/derivative.h +++ b/poincare/include/poincare/derivative.h @@ -22,6 +22,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override { return Type::Derivative; } diff --git a/poincare/include/poincare/division_quotient.h b/poincare/include/poincare/division_quotient.h index 166b3d2c1..43f1ea673 100644 --- a/poincare/include/poincare/division_quotient.h +++ b/poincare/include/poincare/division_quotient.h @@ -23,6 +23,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/division_remainder.h b/poincare/include/poincare/division_remainder.h index f7f93f467..ad6e98f88 100644 --- a/poincare/include/poincare/division_remainder.h +++ b/poincare/include/poincare/division_remainder.h @@ -24,6 +24,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 5f850c2d9..8620088b3 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -29,6 +29,7 @@ class Expression : public TreeHandle { template friend class ComplexNode; friend class ComplexArgument; + friend class ComplexCartesian; friend class ComplexHelper; friend class ConfidenceInterval; friend class Conjugate; @@ -166,6 +167,7 @@ public: Expression defaultReplaceUnknown(const Symbol & symbol); /* Complex */ + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const { return node()->isReal(context, angleUnit); } bool isPureReal(Context & context, Preferences::AngleUnit angleUnit) const; ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const; ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const; @@ -186,8 +188,8 @@ public: Expression simplify(Context & context, Preferences::AngleUnit angleUnit); Expression reduce(Context & context, Preferences::AngleUnit angleUnit); static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context & context); - Expression radianToDegree(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression degreeToRadian(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression radianToDegree(); + Expression degreeToRadian(); /* Approximation Helper */ template static U epsilon(); diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index 39ba11fd5..409d1e1d7 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -48,7 +48,6 @@ public: BinomialCoefficient, Ceiling, ComplexArgument, - ComplexCartesian, ComplexPolar, Conjugate, Derivative, @@ -88,6 +87,8 @@ public: Symbol, Constant, + ComplexCartesian, + Matrix, ConfidenceInterval, MatrixDimension, @@ -128,6 +129,7 @@ public: * ComplexCartesian::shallowBeautify. This would enable us to do only one * scan of the tree in ParseAndSimplifyForComplexFormat instead of Simplifying * and then extracting ComplexCartesian. */ + virtual bool isReal(Context & context, Preferences::AngleUnit angleUnit) const { return false; } virtual ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const; virtual ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const; diff --git a/poincare/include/poincare/factorial.h b/poincare/include/poincare/factorial.h index d7541205b..9c4d5a4eb 100644 --- a/poincare/include/poincare/factorial.h +++ b/poincare/include/poincare/factorial.h @@ -26,6 +26,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout bool childNeedsParenthesis(const TreeNode * child) const override; diff --git a/poincare/include/poincare/floor.h b/poincare/include/poincare/floor.h index 5779a0fea..48e85999d 100644 --- a/poincare/include/poincare/floor.h +++ b/poincare/include/poincare/floor.h @@ -25,6 +25,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/frac_part.h b/poincare/include/poincare/frac_part.h index 45163bedd..aca4ee91c 100644 --- a/poincare/include/poincare/frac_part.h +++ b/poincare/include/poincare/frac_part.h @@ -25,6 +25,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/great_common_divisor.h b/poincare/include/poincare/great_common_divisor.h index dd3e6036b..1e1a86a08 100644 --- a/poincare/include/poincare/great_common_divisor.h +++ b/poincare/include/poincare/great_common_divisor.h @@ -22,6 +22,8 @@ public: Type type() const override { return Type::GreatCommonDivisor; } // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/imaginary_part.h b/poincare/include/poincare/imaginary_part.h index b4d73573e..bbc8bde4a 100644 --- a/poincare/include/poincare/imaginary_part.h +++ b/poincare/include/poincare/imaginary_part.h @@ -24,6 +24,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 3f0f4fee1..57d82309c 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -26,6 +26,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/least_common_multiple.h b/poincare/include/poincare/least_common_multiple.h index e186a5faa..dcbc9a0db 100644 --- a/poincare/include/poincare/least_common_multiple.h +++ b/poincare/include/poincare/least_common_multiple.h @@ -22,6 +22,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/matrix_dimension.h b/poincare/include/poincare/matrix_dimension.h index d54170778..215e89d42 100644 --- a/poincare/include/poincare/matrix_dimension.h +++ b/poincare/include/poincare/matrix_dimension.h @@ -23,6 +23,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/n_ary_expression_node.h b/poincare/include/poincare/n_ary_expression_node.h index 99fd3219f..c537c0a6c 100644 --- a/poincare/include/poincare/n_ary_expression_node.h +++ b/poincare/include/poincare/n_ary_expression_node.h @@ -18,6 +18,9 @@ public: } void eraseNumberOfChildren() override { m_numberOfChildren = 0; } + // Complex + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override; + // Comparison typedef int (*ExpressionOrder)(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted); @@ -48,6 +51,11 @@ public: Expression squashUnaryHierarchyInPlace() { return node()->squashUnaryHierarchyInPlace(); } + /* allChildrenAreReal returns: + * - 1 if all children are real + * - 0 if all non real children are ComplexCartesian + * - -1 if some chidren are non-real and non ComplexCartesian */ + int allChildrenAreReal(Context & context, Preferences::AngleUnit angleUnit) const; protected: NAryExpressionNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/number.h b/poincare/include/poincare/number.h index a7242ea6f..b2fbb08a2 100644 --- a/poincare/include/poincare/number.h +++ b/poincare/include/poincare/number.h @@ -25,6 +25,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } }; class Number : public Expression { diff --git a/poincare/include/poincare/permute_coefficient.h b/poincare/include/poincare/permute_coefficient.h index a87a5b165..00bc4e579 100644 --- a/poincare/include/poincare/permute_coefficient.h +++ b/poincare/include/poincare/permute_coefficient.h @@ -27,6 +27,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index f5bd315fb..7b1503d1a 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -22,6 +22,7 @@ public: #endif // Complex + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override; ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override; ComplexPolar complexPolar(Context & context, Preferences::AngleUnit angleUnit) const override; diff --git a/poincare/include/poincare/randint.h b/poincare/include/poincare/randint.h index c42eb76cf..25e5b1d95 100644 --- a/poincare/include/poincare/randint.h +++ b/poincare/include/poincare/randint.h @@ -23,6 +23,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } private: // Layout diff --git a/poincare/include/poincare/random.h b/poincare/include/poincare/random.h index e5ff18916..a78507d6d 100644 --- a/poincare/include/poincare/random.h +++ b/poincare/include/poincare/random.h @@ -21,6 +21,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override { return Type::Random; } diff --git a/poincare/include/poincare/real_part.h b/poincare/include/poincare/real_part.h index a376d5efc..da5a13da5 100644 --- a/poincare/include/poincare/real_part.h +++ b/poincare/include/poincare/real_part.h @@ -24,6 +24,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } private: // Layout diff --git a/poincare/include/poincare/round.h b/poincare/include/poincare/round.h index 8d2572c93..a2ae66d7a 100644 --- a/poincare/include/poincare/round.h +++ b/poincare/include/poincare/round.h @@ -21,6 +21,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } // Properties Type type() const override { return Type::Round; } diff --git a/poincare/include/poincare/sign_function.h b/poincare/include/poincare/sign_function.h index 6c17d83a5..36f476252 100644 --- a/poincare/include/poincare/sign_function.h +++ b/poincare/include/poincare/sign_function.h @@ -26,6 +26,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianRealFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return true; } private: // Layout diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h index 044551f3d..d2561db0c 100644 --- a/poincare/include/poincare/sine.h +++ b/poincare/include/poincare/sine.h @@ -22,6 +22,7 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); } // Properties Type type() const override { return Type::Sine; } diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h index 901303708..b4c0aa747 100644 --- a/poincare/include/poincare/tangent.h +++ b/poincare/include/poincare/tangent.h @@ -25,6 +25,8 @@ public: // Complex ComplexCartesian complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const override { return ComplexHelper::complexCartesianComplexFunction(this, context, angleUnit); } + bool isReal(Context & context, Preferences::AngleUnit angleUnit) const override { return childAtIndex(0)->isReal(context, angleUnit); } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index 54ae2353a..9b7fbc659 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -266,8 +266,43 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang // Step 5: Let's remove the addition altogether if it has a single child Expression result = squashUnaryHierarchyInPlace(); + if (result != *this) { + return result; + } - /* Step 6: Let's put everything under a common denominator. + /* Step 6: Let's bubble up the complex operator if possible + * 3 cases: + * - All children are real, we do nothing (allChildrenAreReal == 1) + * - One of the child is non-real and not a ComplexCartesian: it means a + * complex expression could not be resolved as a ComplexCartesian, we cannot + * do anything about it now (allChildrenAreReal == -1) + * - All children are either real or ComplexCartesian (allChildrenAreReal == 0) + * We can bubble up ComplexCartesian nodes. */ + if (allChildrenAreReal(context, angleUnit) == 0) { + Addition imag; + Addition real = *this; + i = numberOfChildren() - 1; + while (i >= 0) { + Expression c = childAtIndex(i); + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + real.replaceChildAtIndexInPlace(i, c.childAtIndex(0)); + imag.addChildAtIndexInPlace(c.childAtIndex(1), imag.numberOfChildren(), imag.numberOfChildren()); + } else { + // the Addition is sorted so ComplexCartesian nodes are the last ones + break; + } + i--; + } + ComplexCartesian newComplexCartesian = ComplexCartesian::Builder(); + replaceWithInPlace(newComplexCartesian); + newComplexCartesian.replaceChildAtIndexInPlace(0, real); + newComplexCartesian.replaceChildAtIndexInPlace(1, imag); + real.shallowReduce(context, angleUnit, target); + imag.shallowReduce(context, angleUnit, target); + return newComplexCartesian.shallowReduce(context, angleUnit); + } + + /* Step 7: Let's put everything under a common denominator. * This step is done only for ReductionTarget::User if the parent expression * is not an addition. */ Expression p = result.parent(); diff --git a/poincare/src/complex_cartesian.cpp b/poincare/src/complex_cartesian.cpp new file mode 100644 index 000000000..5dafd7c4f --- /dev/null +++ b/poincare/src/complex_cartesian.cpp @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +Expression ComplexCartesianNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ComplexCartesian(this).shallowReduce(context, angleUnit); +} + +Expression ComplexCartesianNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { + return ComplexCartesian(this).shallowBeautify(context, angleUnit); +} + + +bool isZero(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isZero(); +} +bool isOne(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isOne(); +} +bool isMinusOne(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isMinusOne(); +} + +Expression ComplexCartesian::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + if (imag().isRationalZero()) { + Expression r = real(); + replaceWithInPlace(r); + return r; + } + return *this; +} + +Expression ComplexCartesian::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { + Expression a = real(); + Expression b = imag(); + Expression oppositeA = a.makePositiveAnyNegativeNumeralFactor(context, angleUnit); + Expression oppositeB = b.makePositiveAnyNegativeNumeralFactor(context, angleUnit); + a = oppositeA.isUninitialized() ? a : oppositeA; + b = oppositeB.isUninitialized() ? b : oppositeB; + Expression e = Expression::CreateComplexExpression(a, b, Preferences::ComplexFormat::Cartesian, + a.type() == ExpressionNode::Type::Undefined || b.type() == ExpressionNode::Type::Undefined, + isZero(a), isOne(a), isZero(b), isOne(b), isMinusOne(b), + !oppositeA.isUninitialized(), + !oppositeB.isUninitialized() + ); + replaceWithInPlace(e); + return e; +} + +Expression ComplexCartesian::squareNorm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + Expression a2 = Power(a, Rational(2)); + Expression b2 = Power(b, Rational(2)); + Addition add(a2, b2); + a2.shallowReduce(context, angleUnit, target); + b2.shallowReduce(context, angleUnit, target); + return add; +} + +Expression ComplexCartesian::norm(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression n2 = squareNorm(context, angleUnit, target); + Expression n = SquareRoot::Builder(n2); + n2.shallowReduce(context, angleUnit, target); + return n; +} + +Expression ComplexCartesian::argument(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + if (!b.isRationalZero()) { + // if b != 0, argument = sign(b) * Pi/2 - arctan(a/b) + // First, compute arctan(a/b) or (Pi/180)*arctan(a/b) + Expression divab = Division(a, b.clone()); + Expression arcTangent = ArcTangent::Builder(divab); + divab.shallowReduce(context, angleUnit, target); + if (angleUnit == Preferences::AngleUnit::Degree) { + Expression temp = arcTangent.degreeToRadian(); + arcTangent.shallowReduce(context, angleUnit, target); + arcTangent = temp; + } + // Then, compute sign(b) * Pi/2 - arctan(a/b) + Expression signb = SignFunction::Builder(b); + Expression signbPi2 = Multiplication(Rational(1,2), signb, Constant(Ion::Charset::SmallPi)); + signb.shallowReduce(context, angleUnit, target); + Expression sub = Subtraction(signbPi2, arcTangent); + signbPi2.shallowReduce(context, angleUnit, target); + arcTangent.shallowReduce(context, angleUnit, target); + return sub; + } else { + // if b == 0, argument = (1-sign(a))*Pi/2 + Expression signa = SignFunction::Builder(a).shallowReduce(context, angleUnit); + Subtraction sub(Rational(1), signa); + signa.shallowReduce(context, angleUnit, target); + Multiplication mul(Rational(1,2), Constant(Ion::Charset::SmallPi), sub); + sub.shallowReduce(context, angleUnit, target); + return mul; + } +} + +ComplexCartesian ComplexCartesian::inverse(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + // 1/(a+ib) = a/(a^2+b^2)+i*(-b/(a^2+b^2)) + Expression denominatorReal = clone().convert().squareNorm(context, angleUnit, target); + Expression denominatorImag = denominatorReal.clone(); + Expression denominatorRealInv = Power(denominatorReal, Rational(-1)); + denominatorReal.shallowReduce(context, angleUnit, target); + Expression denominatorImagInv = Power(denominatorImag, Rational(-1)); + denominatorImag.shallowReduce(context, angleUnit, target); + Multiplication A(a, denominatorRealInv); + denominatorRealInv.shallowReduce(context, angleUnit, target); + Expression numeratorImag = Multiplication(Rational(-1), b); + Multiplication B(numeratorImag, denominatorImagInv); + numeratorImag.shallowReduce(context, angleUnit, target); + denominatorImagInv.shallowReduce(context, angleUnit, target); + ComplexCartesian result(A,B); + A.shallowReduce(context, angleUnit, target); + B.shallowReduce(context, angleUnit, target); + return result; +} + +Multiplication ComplexCartesian::squareRootHelper(Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + //(1/2)*sqrt(2*e) + Multiplication doubleE(Rational(2), e); + e.shallowReduce(context, angleUnit, target); + Expression sqrt = SquareRoot::Builder(doubleE); + doubleE.shallowReduce(context, angleUnit, target); + Multiplication result(Rational(1,2), sqrt); + sqrt.shallowReduce(context, angleUnit, target); + return result; +} + +ComplexCartesian ComplexCartesian::squareRoot(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + // A: (1/2)*sqrt(2*(sqrt(a^2+b^2)+a)) + // B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b) + Expression normA = clone().convert().norm(context, angleUnit, target); + Expression normB = normA.clone(); + // A = (1/2)*sqrt(2*(sqrt(a^2+b^2)+a)) + Addition normAdda(normA, a.clone()); + normA.shallowReduce(context, angleUnit, target); + Multiplication A = squareRootHelper(normAdda, context, angleUnit, target); + // B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a)) + Subtraction normSuba(normB, a); + normB.shallowReduce(context, angleUnit, target); + Multiplication B = squareRootHelper(normSuba, context, angleUnit, target); + // B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b) + Expression signb = SignFunction::Builder(b); + B.addChildAtIndexInPlace(signb, B.numberOfChildren(), B.numberOfChildren()); + signb.shallowReduce(context, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(A, B); + A.shallowReduce(context, angleUnit, target); + B.shallowReduce(context, angleUnit, target); + return result; +} + + +ComplexCartesian ComplexCartesian::powerInteger(int n, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return ComplexCartesian(); +} + +ComplexCartesian ComplexCartesian::multiply(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + Expression c = other.real(); + Expression d = other.imag(); + // (a+ib) * (c+id) = (ac-bd)+i*(ad+bc) + // Compute ac-bd + Expression ac = Multiplication(a.clone(), c.clone()); + Expression bd = Multiplication(b.clone(), d.clone()); + Subtraction A(ac, bd); + ac.shallowReduce(context, angleUnit, target); + bd.shallowReduce(context, angleUnit, target); + // Compute ad+bc + Expression ad = Multiplication(a, d); + Expression bc = Multiplication(b, c); + Addition B(ad, bc); + ad.shallowReduce(context, angleUnit, target); + bc.shallowReduce(context, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(A, B); + A.shallowReduce(context, angleUnit, target); + B.shallowReduce(context, angleUnit, target); + return result; +} + +Expression ComplexCartesian::powerHelper(Expression norm, Expression trigo, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Multiplication m(norm, trigo); + norm.shallowReduce(context, angleUnit, target); + trigo.shallowReduce(context, angleUnit, target); + return m; +} + +ComplexCartesian ComplexCartesian::power(ComplexCartesian & other, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression r = clone().convert().norm(context, angleUnit, target); + Expression rclone = r.clone(); + Expression th = argument(context, angleUnit, target); + Expression thclone = th.clone(); + Expression c = other.real(); + Expression d = other.imag(); + // R = r^c*e^(-th*d) + Expression rpowc = Power(rclone, c); + rclone.shallowReduce(context, angleUnit, target); + Expression thmuld = Multiplication(Rational(-1), thclone, d.clone()); + thclone.shallowReduce(context, angleUnit, target); + Expression exp = Power(Constant(Ion::Charset::Exponential), thmuld); + thmuld.shallowReduce(context, angleUnit, target); + Multiplication norm(rpowc, exp); + rpowc.shallowReduce(context, angleUnit, target); + exp.shallowReduce(context, angleUnit, target); + + // TH = d*ln(r)+c*th + Expression lnr = NaperianLogarithm::Builder(r); + r.shallowReduce(context, angleUnit, target); + Multiplication dlnr(d, lnr); + lnr.shallowReduce(context, angleUnit, target); + Multiplication thc(th, c); + th.shallowReduce(context, angleUnit, target); + Expression argument = Addition(thc, dlnr); + thc.shallowReduce(context, angleUnit, target); + dlnr.shallowReduce(context, angleUnit, target); + + if (angleUnit == Preferences::AngleUnit::Degree) { + Expression temp = argument.radianToDegree(); + argument.shallowReduce(context, angleUnit, target); + argument = temp; + } + // Result = (norm*cos(argument), norm*sin(argument)) + Expression normClone = norm.clone(); + Expression argClone = argument.clone(); + Expression cos = Cosine::Builder(argClone); + argClone.shallowReduce(context, angleUnit, target); + Expression normcosarg = powerHelper(normClone, cos, context, angleUnit, target); + Expression sin = Sine::Builder(argument); + argument.shallowReduce(context, angleUnit, target); + Expression normsinarg = powerHelper(norm, sin, context, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(normcosarg, normsinarg); + normcosarg.shallowReduce(context, angleUnit, target); + normsinarg.shallowReduce(context, angleUnit, target); + return result; +} + +} diff --git a/poincare/src/complex_helper.cpp b/poincare/src/complex_helper.cpp index f7de5efc2..e8e0541ec 100644 --- a/poincare/src/complex_helper.cpp +++ b/poincare/src/complex_helper.cpp @@ -51,7 +51,7 @@ ComplexCartesian ComplexHelper::complexCartesianFromComplexPolar(const Expressio Expression r = polar.norm(); Expression th = polar.arg(); assert(!r.isUninitialized() && !th.isUninitialized()); - Expression argument = angleUnit == Preferences::AngleUnit::Radian ? th : th.radianToDegree(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation); + Expression argument = th; return ComplexCartesian::Builder( complexCartesianFromComplexPolarHelper(r.clone(), Cosine::Builder(argument.clone()), context, angleUnit), complexCartesianFromComplexPolarHelper(r, Sine::Builder(argument), context, angleUnit) @@ -93,9 +93,6 @@ ComplexPolar ComplexHelper::complexPolarFromComplexCartesian(const ExpressionNod // if b != 0, argument = sign(b) * Pi/2 - arctan(a/b) // First, compute arctan(a/b) or (Pi/180)*arctan(a/b) Expression arcTangent = ArcTangent::Builder(Division(a, b.clone()).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation)).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation); - if (angleUnit == Preferences::AngleUnit::Degree) { - arcTangent = arcTangent.degreeToRadian(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation); - } // Then, compute sign(b) * Pi/2 - arctan(a/b) argument = Subtraction( Multiplication( diff --git a/poincare/src/constant.cpp b/poincare/src/constant.cpp index 6a41326de..50ec2c2cc 100644 --- a/poincare/src/constant.cpp +++ b/poincare/src/constant.cpp @@ -16,6 +16,10 @@ ExpressionNode::Sign ConstantNode::sign(Context * context, Preferences::AngleUni return Sign::Unknown; } +bool ConstantNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const { + return !isIComplex(); +} + ComplexCartesian ConstantNode::complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const { if (isIComplex()) { return ComplexCartesian::Builder(Rational(0), Rational(1)); @@ -47,8 +51,21 @@ Evaluation ConstantNode::templatedApproximate(Context& context, Preferences:: return Complex(M_E); } +Expression ConstantNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Constant(this).shallowReduce(context, angleUnit); +} + Constant::Constant(char name) : SymbolAbstract(TreePool::sharedPool()->createTreeNode(SymbolAbstract::AlignedNodeSize(1, sizeof(ConstantNode)))) { node()->setName(&name, 1); } +Expression Constant::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + if (isIComplex()) { + ComplexCartesian c = ComplexCartesian::Builder(Rational(0), Rational(1)); + replaceWithInPlace(c); + return c; + } + return *this; +} + } diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 3b48ddb93..4719db2d6 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -420,12 +420,14 @@ Expression Expression::ExpressionWithoutSymbols(Expression e, Context & context) return e; } -Expression Expression::radianToDegree(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return Multiplication(*this, Division(Rational(180), Constant(Ion::Charset::SmallPi)).shallowReduce(context, angleUnit, target)).shallowReduce(context, angleUnit, target); +Expression Expression::radianToDegree() { + // e*180/Pi + return Multiplication(*this, Rational(180), Power(Constant(Ion::Charset::SmallPi), Rational(-1))); } -Expression Expression::degreeToRadian(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return Multiplication(*this, Division(Constant(Ion::Charset::SmallPi), Rational(180)).shallowReduce(context, angleUnit, target)).shallowReduce(context, angleUnit, target); +Expression Expression::degreeToRadian() { + // e*Pi/180 + return Multiplication(*this, Power(Rational(180), Rational(-1)), Constant(Ion::Charset::SmallPi)); } Expression Expression::reduce(Context & context, Preferences::AngleUnit angleUnit) { diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index aba35c0a9..bbc7ad08d 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -522,8 +522,49 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: } } - // Step 8: Let's remove the multiplication altogether if it has one child + // Step 7: Let's remove the multiplication altogether if it has one child Expression result = squashUnaryHierarchyInPlace(); + if (result != *this) { + return result; + } + + /* Step 8: Let's bubble up the complex operator if possible + * 3 cases: + * - All children are real, we do nothing (allChildrenAreReal == 1) + * - One of the child is non-real and not a ComplexCartesian: it means a + * complex expression could not be resolved as a ComplexCartesian, we cannot + * do anything about it now (allChildrenAreReal == -1) + * - All children are either real or ComplexCartesian (allChildrenAreReal == 0) + * We can bubble up ComplexCartesian nodes. */ + if (allChildrenAreReal(context, angleUnit) == 0) { + int nbChildren = numberOfChildren(); + int i = nbChildren-1; + assert(childAtIndex(i).type() == ExpressionNode::Type::ComplexCartesian); + ComplexCartesian child = childAtIndex(i).convert(); + removeChildAtIndexInPlace(i); + i--; + while (i >= 0) { + Expression e = childAtIndex(i); + if (e.type() != ExpressionNode::Type::ComplexCartesian) { + // the Multiplication is sorted so ComplexCartesian nodes are the last ones + break; + } + child = child.multiply(static_cast(e), context, angleUnit, target); + removeChildAtIndexInPlace(i); + i--; + } + Multiplication real = *this; + Multiplication imag = clone().convert(); + real.addChildAtIndexInPlace(child.real(), real.numberOfChildren(), real.numberOfChildren()); + imag.addChildAtIndexInPlace(child.imag(), real.numberOfChildren(), real.numberOfChildren()); + ComplexCartesian newComplexCartesian = ComplexCartesian::Builder(); + replaceWithInPlace(newComplexCartesian); + newComplexCartesian.replaceChildAtIndexInPlace(0, real); + newComplexCartesian.replaceChildAtIndexInPlace(1, imag); + real.shallowReduce(context, angleUnit, target); + imag.shallowReduce(context, angleUnit, target); + return newComplexCartesian.shallowReduce(context, angleUnit); + } return result; } diff --git a/poincare/src/n_ary_expression_node.cpp b/poincare/src/n_ary_expression_node.cpp index b6db830f5..d50e1ca61 100644 --- a/poincare/src/n_ary_expression_node.cpp +++ b/poincare/src/n_ary_expression_node.cpp @@ -28,6 +28,10 @@ void NAryExpressionNode::sortChildrenInPlace(ExpressionOrder order, bool canBeIn } } +bool NAryExpressionNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const { + return NAryExpression(this).allChildrenAreReal(context, angleUnit) == 1; +} + Expression NAryExpressionNode::squashUnaryHierarchyInPlace() { NAryExpression reference = NAryExpression(this); if (reference.numberOfChildren() == 1) { @@ -76,4 +80,22 @@ int NAryExpressionNode::simplificationOrderGreaterType(const ExpressionNode * e, return 0; } +int NAryExpression::allChildrenAreReal(Context & context, Preferences::AngleUnit angleUnit) const { + int i = 0; + /* The addition children are assumed to be sorted. ComplexCartesian children + * are supposed to be the last ones before matrices. We just test children + * to be real until we reach the first ComplexCartesian. */ + while (i < numberOfChildren()) { + Expression c = childAtIndex(i); + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + return 0; + } + if (!c.isReal(context, angleUnit)) { + return -1; + } + i++; + } + return 1; +} + } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 99e99ad3a..51a6eb1c7 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -79,7 +79,13 @@ int PowerNode::getPolynomialCoefficients(Context & context, const char * symbolN return Power(this).getPolynomialCoefficients(context, symbolName, coefficients); } -// Private +bool PowerNode::isReal(Context & context, Preferences::AngleUnit angleUnit) const { + ExpressionNode * base = childAtIndex(0); + if (base->isReal(context, angleUnit) && base->sign(&context, angleUnit) == Sign::Positive && childAtIndex(1)->isReal(context, angleUnit)) { + return true; + } + return false; +} ComplexCartesian PowerNode::complexCartesian(Context & context, Preferences::AngleUnit angleUnit) const { Power p(this); @@ -125,6 +131,8 @@ ComplexPolar PowerNode::complexPolar(Context & context, Preferences::AngleUnit a return ComplexPolar::Builder(norm, argument); } +// Private + template Complex PowerNode::compute(const std::complex c, const std::complex d) { std::complex result; @@ -359,22 +367,76 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU #endif #endif - /* Step 0: if both children are true complexes, the result is undefined. We - * can assert that evaluations are Complex, as matrix are not simplified */ - - Evaluation c0Approximated = childAtIndex(0).node()->approximate(1.0f, context, angleUnit); + // TODO: do we need this? + /*Evaluation c0Approximated = childAtIndex(0).node()->approximate(1.0f, context, angleUnit); Evaluation c1Approximated = childAtIndex(1).node()->approximate(1.0f, context, angleUnit); Complex c0 = static_cast&>(c0Approximated); Complex c1 = static_cast&>(c1Approximated); bool bothChildrenComplexes = c0.imag() != 0 && c1.imag() != 0 && !std::isnan(c0.imag()) && !std::isnan(c1.imag()); - bool nonComplexNegativeChild0 = c0.imag() == 0 && c0.real() < 0; bool nonNullChild0 = !std::isnan(c0.real()) && !std::isnan(c0.imag()) && (c0.real() > Expression::epsilon() || c0.imag() > Expression::epsilon()); if (bothChildrenComplexes) { return *this; + }*/ + + /* Step 0: if both children are true unresolved complexes, the result is not simplified. TODO? */ + + Expression power = *this; + Expression base = childAtIndex(0); + Expression index = childAtIndex(1); + /* Step 0: if both children are true unresolved complexes, the result is not simplified. TODO? */ + if (!base.isReal(context, angleUnit) && !index.isReal(context, angleUnit)) { + return *this; + } + + /* Step 1: we now bubble up ComplexCartesian, we handle different case */ + + ComplexCartesian complexBase; + ComplexCartesian complexIndex; + ComplexCartesian result; + // First, (x+iy)^q with q special values + // TODO: explain here why? + if (base.type() == ExpressionNode::Type::ComplexCartesian) { + complexBase = static_cast(base); + Integer ten(10); + if (index.type() == ExpressionNode::Type::Rational) { + Rational r = static_cast(index); + if (r.isMinusOne()) { + // (x+iy)^(-1) + result = complexBase.inverse(context, angleUnit, target); + } else if (r.isHalf()) { + // (x+iy)^(1/2) + result = complexBase.squareRoot(context, angleUnit, target); + } else if (r.isMinusHalf()) { + // (x+iy)^(-1/2) + result = complexBase.squareRoot(context, angleUnit, target).inverse(context, angleUnit, target); + } else if (r.integerDenominator().isOne() && r.unsignedIntegerNumerator().isLowerThan(ten)) { + if (r.sign() == ExpressionNode::Sign::Positive) { + // (x+iy)^n, n integer positive n < 10 + result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, angleUnit, target); + } else { + // (x+iy)^(-n), n integer positive n < 10 + result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, angleUnit, target).inverse(context, angleUnit, target); + } + } + if (!result.isUninitialized()) { + replaceWithInPlace(result); + return result.shallowReduce(context, angleUnit); + } + } + } + // All other cases where one child at least is a ComplexCartesian + if ((base.isReal(context, angleUnit) && index.type() == ExpressionNode::Type::ComplexCartesian) || + (base.type() == ExpressionNode::Type::ComplexCartesian && index.isReal(context, angleUnit)) || + (base.type() == ExpressionNode::Type::ComplexCartesian && index.type() == ExpressionNode::Type::ComplexCartesian)) { + complexBase = base.type() == ExpressionNode::Type::ComplexCartesian ? static_cast(base) : ComplexCartesian::Builder(base, Rational(0)); + complexIndex = index.type() == ExpressionNode::Type::ComplexCartesian ? static_cast(index) : ComplexCartesian::Builder(index, Rational(0)); + result = complexBase.power(complexIndex, context, angleUnit, target); + replaceWithInPlace(result); + return result.shallowReduce(context, angleUnit); } /* Step 1: We handle simple cases as x^0, x^1, 0^x and 1^x first for 2 reasons: - * - we can assert this step that there is no division by 0: + * - we can assert after this step that there is no division by 0: * for instance, 0^(-2)->undefined * - we save computational time by early escaping for these cases. */ if (childAtIndex(1).type() == ExpressionNode::Type::Rational) { @@ -388,7 +450,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU return result; } // x^0 - if (target == ExpressionNode::ReductionTarget::User || nonNullChild0) { + if (target == ExpressionNode::ReductionTarget::User || childAtIndex(0).isNumber()) { /* Warning: if the ReductionTarget is User, in all other cases but 0^0, * we replace x^0 by one. This is almost always true except when x = 0. * However, not substituting x^0 by one would prevent from simplifying @@ -437,7 +499,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU } } - if (target == ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) { + /*if (target == ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) { const Rational b = childAtIndex(1).convert(); // i^(p/q) if (childAtIndex(0).type() == ExpressionNode::Type::Constant && childAtIndex(0).convert().isIComplex()) { @@ -446,7 +508,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU replaceWithInPlace(result); return result.shallowReduce(context, angleUnit, target); } - } + }*/ // (±inf)^x if (childAtIndex(0).type() == ExpressionNode::Type::Infinity) { @@ -508,7 +570,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU } } // e^(i*Pi*r) with r rational - if (!letPowerAtRoot && isNthRootOfUnity()) { + /*if (!letPowerAtRoot && isNthRootOfUnity()) { Expression m = childAtIndex(1); Expression i = m.childAtIndex(m.numberOfChildren()-1); static_cast(m).removeChildAtIndexInPlace(m.numberOfChildren()-1); @@ -525,7 +587,7 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU complexPart.shallowReduce(context, angleUnit, target); replaceWithInPlace(a); return a.shallowReduce(context, angleUnit, target); - } + }*/ // x^log(y,x)->y if y > 0 if (childAtIndex(1).type() == ExpressionNode::Type::Logarithm) { if (childAtIndex(1).numberOfChildren() == 2 && childAtIndex(0).isIdenticalTo(childAtIndex(1).childAtIndex(1))) {