diff --git a/poincare/include/poincare/approximation_engine.h b/poincare/include/poincare/approximation_engine.h index 33f40765d..4224ebfff 100644 --- a/poincare/include/poincare/approximation_engine.h +++ b/poincare/include/poincare/approximation_engine.h @@ -9,6 +9,8 @@ namespace Poincare { class ApproximationEngine { public: + template static std::complex truncateRealOrImaginaryPartAccordingToArgument(std::complex c); + template using ComplexCompute = std::complex(*)(const std::complex, Expression::AngleUnit angleUnit); template static Evaluation * map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute); diff --git a/poincare/include/poincare/arc_cosine.h b/poincare/include/poincare/arc_cosine.h index 87de7eae9..b1c03031b 100644 --- a/poincare/include/poincare/arc_cosine.h +++ b/poincare/include/poincare/arc_cosine.h @@ -27,9 +27,7 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return Trigonometry::computeInverseOnComplex(c, angleUnit, std::acos); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit); Evaluation * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } diff --git a/poincare/include/poincare/arc_sine.h b/poincare/include/poincare/arc_sine.h index 704742e71..208d959d7 100644 --- a/poincare/include/poincare/arc_sine.h +++ b/poincare/include/poincare/arc_sine.h @@ -25,9 +25,7 @@ private: /* Simplification */ Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; /* Evaluation */ - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return Trigonometry::computeInverseOnComplex(c, angleUnit, std::asin); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit); Evaluation * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return ApproximationEngine::map(this, context, angleUnit, computeOnComplex); } diff --git a/poincare/include/poincare/arc_tangent.h b/poincare/include/poincare/arc_tangent.h index a2ce8d00a..1dab03ba7 100644 --- a/poincare/include/poincare/arc_tangent.h +++ b/poincare/include/poincare/arc_tangent.h @@ -25,9 +25,7 @@ private: /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return Trigonometry::computeInverseOnComplex(c, angleUnit, std::atan); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit); Evaluation * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h index 6f21f1798..54b4fd7d4 100644 --- a/poincare/include/poincare/cosine.h +++ b/poincare/include/poincare/cosine.h @@ -16,9 +16,7 @@ public: Type type() const override; Expression * clone() const override; float characteristicXRange(Context & context, AngleUnit angleUnit) const override; - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian) { - return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex::cos); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian); private: /* Layout */ ExpressionLayout * createLayout(PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const override { diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h index a67600e28..79acdba5f 100644 --- a/poincare/include/poincare/sine.h +++ b/poincare/include/poincare/sine.h @@ -15,9 +15,7 @@ class Sine : public StaticHierarchy<1> { public: Type type() const override; Expression * clone() const override; - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian) { - return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex::sin); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian); private: /* Layout */ ExpressionLayout * createLayout(PrintFloat::Mode floatDisplayMode, int numberOfSignificantDigits) const override { diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h index da09b3050..064e24e84 100644 --- a/poincare/include/poincare/tangent.h +++ b/poincare/include/poincare/tangent.h @@ -26,9 +26,7 @@ private: /* Simplication */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; /* Evaluation */ - template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian) { - return Trigonometry::computeDirectOnComplex(c, angleUnit, Complex::tan); - } + template static std::complex computeOnComplex(const std::complex c, AngleUnit angleUnit = AngleUnit::Radian); Evaluation * privateApproximate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return ApproximationEngine::map(this, context, angleUnit,computeOnComplex); } diff --git a/poincare/include/poincare/trigonometry.h b/poincare/include/poincare/trigonometry.h index d9d530bb5..d49511178 100644 --- a/poincare/include/poincare/trigonometry.h +++ b/poincare/include/poincare/trigonometry.h @@ -18,8 +18,11 @@ public: static bool ExpressionIsEquivalentToTangent(const Expression * e); constexpr static int k_numberOfEntries = 37; static Expression * table(const Expression * e, Expression::Type type, Context & context, Expression::AngleUnit angleUnit); // , Function f, bool inverse - template static std::complex computeDirectOnComplex(const std::complex c, Expression::AngleUnit angleUnit, ComplexFunction approximate); - template static std::complex computeInverseOnComplex(const std::complex c, Expression::AngleUnit angleUnit, ComplexFunction approximate); + template static std::complex ConvertToRadian(const std::complex c, Expression::AngleUnit angleUnit); + template static std::complex ConvertRadianToAngleUnit(const std::complex c, Expression::AngleUnit angleUnit); + template static std::complex RoundToMeaningfulDigits(const std::complex c); +private: + template static T RoundToMeaningfulDigits(T f); }; } diff --git a/poincare/src/approximation_engine.cpp b/poincare/src/approximation_engine.cpp index 92321ea6b..c637c49ad 100644 --- a/poincare/src/approximation_engine.cpp +++ b/poincare/src/approximation_engine.cpp @@ -8,6 +8,23 @@ extern "C" { namespace Poincare { +template T absMod(T a, T b) { + T result = std::fmod(std::fabs(a), b); + return result > b/2 ? b-result : result; +} + +template std::complex ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(std::complex c) { + T arg = std::arg(c); + T precision = 10*Expression::epsilon(); + if (absMod(arg, (T)M_PI) <= precision) { + c.imag(0); + } + if (absMod(arg-(T)M_PI/2.0, (T)M_PI) <= precision) { + c.real(0); + } + return c; +} + template Evaluation * ApproximationEngine::map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute) { assert(expression->numberOfOperands() == 1); Evaluation * input = expression->operand(0)->privateApproximate(T(), context, angleUnit); @@ -93,6 +110,8 @@ template MatrixComplex ApproximationEngine::elementWiseOnComplexM return result; } +template std::complex Poincare::ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(std::complex); +template std::complex Poincare::ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(std::complex); template Poincare::Evaluation * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute compute); template Poincare::Evaluation * Poincare::ApproximationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexCompute compute); template Poincare::Evaluation * Poincare::ApproximationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::ApproximationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationEngine::MatrixAndMatrixReduction computeOnMatrices); diff --git a/poincare/src/arc_cosine.cpp b/poincare/src/arc_cosine.cpp index d032aeb39..914566918 100644 --- a/poincare/src/arc_cosine.cpp +++ b/poincare/src/arc_cosine.cpp @@ -30,4 +30,19 @@ Expression * ArcCosine::shallowReduce(Context& context, AngleUnit angleUnit) { return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } +template +std::complex ArcCosine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex result = std::acos(c); + /* acos has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on + * this cut. We followed the convention chosen by the lib c++ of llvm on + * ]-inf+0i, -1+0i[ (warning: acos takes the other side of the cut values on + * ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with + * acos(-x) = Pi - acos(x) and tan(arccos(x)) = sqrt(1-x^2)/x. */ + if (c.imag() == 0 && c.real() > 1) { + result.imag(-result.imag()); // other side of the cut + } + result = Trigonometry::RoundToMeaningfulDigits(result); + return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit); +} + } diff --git a/poincare/src/arc_sine.cpp b/poincare/src/arc_sine.cpp index 641118c73..9be40472f 100644 --- a/poincare/src/arc_sine.cpp +++ b/poincare/src/arc_sine.cpp @@ -30,4 +30,19 @@ Expression * ArcSine::shallowReduce(Context& context, AngleUnit angleUnit) { return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } +template +std::complex ArcSine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex result = std::asin(c); + /* asin has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on + * this cut. We followed the convention chosen by the lib c++ of llvm on + * ]-inf+0i, -1+0i[ (warning: asin takes the other side of the cut values on + * ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with + * asin(-x) = -asin(x) and tan(arcsin(x)) = x/sqrt(1-x^2). */ + if (c.imag() == 0 && c.real() > 1) { + result.imag(-result.imag()); // other side of the cut + } + result = Trigonometry::RoundToMeaningfulDigits(result); + return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit); +} + } diff --git a/poincare/src/arc_tangent.cpp b/poincare/src/arc_tangent.cpp index e1b3eadc7..d420b6837 100644 --- a/poincare/src/arc_tangent.cpp +++ b/poincare/src/arc_tangent.cpp @@ -30,4 +30,19 @@ Expression * ArcTangent::shallowReduce(Context& context, AngleUnit angleUnit) { return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } +template +std::complex ArcTangent::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex result = std::atan(c); + /* atan has a branch cut on ]-inf*i, -i[U]i, +inf*i[: it is then multivalued + * on this cut. We followed the convention chosen by the lib c++ of llvm on + * ]-i+0, -i*inf+0[ (warning: atan takes the other side of the cut values on + * ]-i+0, -i*inf+0[) and choose the values on ]-inf*i, -i[ to comply with + * atan(-x) = -atan(x) and sin(arctan(x)) = x/sqrt(1+x^2). */ + if (c.real() == 0 && c.imag() < -1) { + result.real(-result.real()); // other side of the cut + } + result = Trigonometry::RoundToMeaningfulDigits(result); + return Trigonometry::ConvertRadianToAngleUnit(result, angleUnit); +} + } diff --git a/poincare/src/cosine.cpp b/poincare/src/cosine.cpp index 67d4cb099..c9fb03d8a 100644 --- a/poincare/src/cosine.cpp +++ b/poincare/src/cosine.cpp @@ -25,6 +25,13 @@ float Cosine::characteristicXRange(Context & context, AngleUnit angleUnit) const return Trigonometry::characteristicXRange(this, context, angleUnit); } +template +std::complex Cosine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); + std::complex res = std::cos(angleInput); + return Trigonometry::RoundToMeaningfulDigits(res); +} + Expression * Cosine::shallowReduce(Context& context, AngleUnit angleUnit) { Expression * e = Expression::shallowReduce(context, angleUnit); if (e != this) { diff --git a/poincare/src/evaluation.cpp b/poincare/src/evaluation.cpp index 9ce1534c3..da74af9e7 100644 --- a/poincare/src/evaluation.cpp +++ b/poincare/src/evaluation.cpp @@ -109,7 +109,6 @@ Expression * Complex::complexToExpression(Expression::ComplexFormat complexFo } } - template Complex * Complex::createInverse() const { return new Complex(Division::compute(std::complex(1.0), *this)); diff --git a/poincare/src/hyperbolic_arc_cosine.cpp b/poincare/src/hyperbolic_arc_cosine.cpp index 90af51886..72b309764 100644 --- a/poincare/src/hyperbolic_arc_cosine.cpp +++ b/poincare/src/hyperbolic_arc_cosine.cpp @@ -1,5 +1,6 @@ #include #include +#include extern "C" { #include } @@ -32,7 +33,12 @@ Expression * HyperbolicArcCosine::shallowReduce(Context& context, AngleUnit angl template std::complex HyperbolicArcCosine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::acosh(c); + std::complex result = std::acosh(c); + /* asinh has a branch cut on ]-inf, 1]: it is then multivalued + * on this cut. We followed the convention chosen by the lib c++ of llvm on + * ]-inf+0i, 1+0i] (warning: atanh takes the other side of the cut values on + * ]-inf-0i, 1-0i[).*/ + return Trigonometry::RoundToMeaningfulDigits(result); } } diff --git a/poincare/src/hyperbolic_arc_sine.cpp b/poincare/src/hyperbolic_arc_sine.cpp index 476374da2..f15b89cff 100644 --- a/poincare/src/hyperbolic_arc_sine.cpp +++ b/poincare/src/hyperbolic_arc_sine.cpp @@ -1,5 +1,6 @@ #include #include +#include extern "C" { #include } @@ -32,7 +33,16 @@ Expression * HyperbolicArcSine::shallowReduce(Context& context, AngleUnit angleU template std::complex HyperbolicArcSine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::asinh(c); + std::complex result = std::asinh(c); + /* asinh has a branch cut on ]-inf*i, -i[U]i, +inf*i[: it is then multivalued + * on this cut. We followed the convention chosen by the lib c++ of llvm on + * ]+i+0, +i*inf+0[ (warning: atanh takes the other side of the cut values on + * ]+i-0, +i*inf+0[) and choose the values on ]-inf*i, -i[ to comply with + * asinh(-x) = -asinh(x). */ + if (c.real() == 0 && c.imag() < 1) { + result.real(-result.real()); // other side of the cut + } + return Trigonometry::RoundToMeaningfulDigits(result); } } diff --git a/poincare/src/hyperbolic_arc_tangent.cpp b/poincare/src/hyperbolic_arc_tangent.cpp index b7e6bb74b..4648a5f7f 100644 --- a/poincare/src/hyperbolic_arc_tangent.cpp +++ b/poincare/src/hyperbolic_arc_tangent.cpp @@ -1,5 +1,6 @@ #include #include +#include extern "C" { #include } @@ -32,7 +33,16 @@ Expression * HyperbolicArcTangent::shallowReduce(Context& context, AngleUnit ang template std::complex HyperbolicArcTangent::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::atanh(c); + std::complex result = std::atanh(c); + /* atanh has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on + * this cut. We followed the convention chosen by the lib c++ of llvm on + * ]-inf+0i, -1+0i[ (warning: atanh takes the other side of the cut values on + * ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with + * atanh(-x) = -atanh(x) and sin(artanh(x)) = x/sqrt(1-x^2). */ + if (c.imag() == 0 && c.real() > 1) { + result.imag(-result.imag()); // other side of the cut + } + return Trigonometry::RoundToMeaningfulDigits(result); } } diff --git a/poincare/src/hyperbolic_cosine.cpp b/poincare/src/hyperbolic_cosine.cpp index dbccbd7c7..d59987e40 100644 --- a/poincare/src/hyperbolic_cosine.cpp +++ b/poincare/src/hyperbolic_cosine.cpp @@ -4,6 +4,7 @@ #include #include #include +#include extern "C" { #include } @@ -36,7 +37,7 @@ Expression * HyperbolicCosine::shallowReduce(Context& context, AngleUnit angleUn template std::complex HyperbolicCosine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::cosh(c); + return Trigonometry::RoundToMeaningfulDigits(std::cosh(c)); } } diff --git a/poincare/src/hyperbolic_sine.cpp b/poincare/src/hyperbolic_sine.cpp index 588b084eb..4507740ea 100644 --- a/poincare/src/hyperbolic_sine.cpp +++ b/poincare/src/hyperbolic_sine.cpp @@ -4,6 +4,7 @@ #include #include #include +#include extern "C" { #include } @@ -36,7 +37,7 @@ Expression * HyperbolicSine::shallowReduce(Context& context, AngleUnit angleUnit template std::complex HyperbolicSine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::sinh(c); + return Trigonometry::RoundToMeaningfulDigits(std::sinh(c)); } } diff --git a/poincare/src/hyperbolic_tangent.cpp b/poincare/src/hyperbolic_tangent.cpp index 1df271e57..a35b4820a 100644 --- a/poincare/src/hyperbolic_tangent.cpp +++ b/poincare/src/hyperbolic_tangent.cpp @@ -3,6 +3,7 @@ #include #include #include +#include extern "C" { #include } @@ -35,7 +36,7 @@ Expression * HyperbolicTangent::shallowReduce(Context& context, AngleUnit angleU template std::complex HyperbolicTangent::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return std::tanh(c); + return Trigonometry::RoundToMeaningfulDigits(std::tanh(c)); } } diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index 9d1f1ba20..bfb96f72f 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -31,6 +31,9 @@ Expression * Logarithm::clone() const { template std::complex Logarithm::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + /* log has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We + * followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i] + * (warning: log takes the other side of the cut values on ]-inf-0i, 0-0i]). */ return std::log10(c); } diff --git a/poincare/src/naperian_logarithm.cpp b/poincare/src/naperian_logarithm.cpp index 4278c6465..5326d98f9 100644 --- a/poincare/src/naperian_logarithm.cpp +++ b/poincare/src/naperian_logarithm.cpp @@ -38,6 +38,9 @@ Expression * NaperianLogarithm::shallowReduce(Context& context, AngleUnit angleU template std::complex NaperianLogarithm::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + /* ln has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We + * followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i] + * (warning: ln takes the other side of the cut values on ]-inf-0i, 0-0i]). */ return std::log(c); } diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index 5cbd03341..b4fa7e88f 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -49,7 +49,7 @@ Evaluation * NthRoot::templatedApproximate(Context& context, AngleUnit angleU if (base->type() == Evaluation::Type::Complex && index->type() == Evaluation::Type::Complex) { Complex * basec = static_cast *>(base); Complex * indexc = static_cast *>(index); - result = Complex::pow(*basec, std::complex(1)/(*indexc)); + result = Power::compute(*basec, std::complex(1)/(*indexc)); } delete base; delete index; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 81e60fbd6..ff7c1502e 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -119,7 +119,14 @@ Expression * Power::setSign(Sign s, Context & context, AngleUnit angleUnit) { template std::complex Power::compute(const std::complex c, const std::complex d) { - return Complex::pow(c, d); + /* Openbsd trigonometric functions are numerical implementation and thus are + * approximative. + * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to + * avoid weird results as e(i*pi) = -1+6E-17*i, we compute the argument of + * the result of c^d and if arg ~ 0 [Pi], we discard the residual imaginary + * part and if arg ~ Pi/2 [Pi], we discard the residual real part. */ + std::complex result = std::pow(c, d); + return ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(result); } template MatrixComplex Power::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n) { diff --git a/poincare/src/sine.cpp b/poincare/src/sine.cpp index ac325cf54..d4dfbc08d 100644 --- a/poincare/src/sine.cpp +++ b/poincare/src/sine.cpp @@ -25,6 +25,13 @@ float Sine::characteristicXRange(Context & context, AngleUnit angleUnit) const { return Trigonometry::characteristicXRange(this, context, angleUnit); } +template +std::complex Sine::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); + std::complex res = std::sin(angleInput); + return Trigonometry::RoundToMeaningfulDigits(res); +} + Expression * Sine::shallowReduce(Context& context, AngleUnit angleUnit) { Expression * e = Expression::shallowReduce(context, angleUnit); if (e != this) { diff --git a/poincare/src/square_root.cpp b/poincare/src/square_root.cpp index 274d8ae7f..4712ba1f9 100644 --- a/poincare/src/square_root.cpp +++ b/poincare/src/square_root.cpp @@ -26,7 +26,14 @@ int SquareRoot::writeTextInBuffer(char * buffer, int bufferSize, PrintFloat::Mod template std::complex SquareRoot::computeOnComplex(const std::complex c, AngleUnit angleUnit) { - return Complex::sqrt(c); + std::complex result = std::sqrt(c); + /* Openbsd trigonometric functions are numerical implementation and thus are + * approximative. + * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to + * avoid weird results as sqrt(-1) = 6E-16+i, we compute the argument of + * the result of sqrt(c) and if arg ~ 0 [Pi], we discard the residual imaginary + * part and if arg ~ Pi/2 [Pi], we discard the residual real part. */ + return ApproximationEngine::truncateRealOrImaginaryPartAccordingToArgument(result); } Expression * SquareRoot::shallowReduce(Context& context, AngleUnit angleUnit) { diff --git a/poincare/src/tangent.cpp b/poincare/src/tangent.cpp index 99c627c4b..bffcefe0f 100644 --- a/poincare/src/tangent.cpp +++ b/poincare/src/tangent.cpp @@ -26,6 +26,13 @@ float Tangent::characteristicXRange(Context & context, AngleUnit angleUnit) cons return Trigonometry::characteristicXRange(this, context, angleUnit); } +template +std::complex Tangent::computeOnComplex(const std::complex c, AngleUnit angleUnit) { + std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); + std::complex res = std::tan(angleInput); + return Trigonometry::RoundToMeaningfulDigits(res); +} + Expression * Tangent::shallowReduce(Context& context, AngleUnit angleUnit) { Expression * e = Expression::shallowReduce(context, angleUnit); if (e != this) { diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index ded1810da..733fed6ff 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -229,27 +229,46 @@ Expression * Trigonometry::table(const Expression * e, Expression::Type type, Co return nullptr; } + template -std::complex Trigonometry::computeDirectOnComplex(const std::complex c, Expression::AngleUnit angleUnit, ComplexFunction approximate) { - std::complex input(c); +std::complex Trigonometry::ConvertToRadian(const std::complex c, Expression::AngleUnit angleUnit) { if (angleUnit == Expression::AngleUnit::Degree) { - input = input*std::complex(M_PI/180.0); + return c*std::complex(M_PI/180.0); } - return approximate(input); + return c; +} + +template +std::complex Trigonometry::ConvertRadianToAngleUnit(const std::complex c, Expression::AngleUnit angleUnit) { + if (angleUnit == Expression::AngleUnit::Degree) { + return c*std::complex(180/M_PI); + } + return c; } template -std::complex Trigonometry::computeInverseOnComplex(const std::complex c, Expression::AngleUnit angleUnit, ComplexFunction approximate) { - std::complex result = approximate(c); - if (angleUnit == Expression::AngleUnit::Degree) { - result *= 180/M_PI; - } - return result; +T Trigonometry::RoundToMeaningfulDigits(T f) { + /* Cheat: openbsd trigonometric functions are numerical implementation and + * thus are approximative. + * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to + * avoid weird results as acos(1) = 6E-17 or cos(Pi/2) = 4E-17, we keep only + * 15 (or 7) decimals. + * We can't do that for all evaluation as the user can operate on values as + * small as 1E-308 (in double) and most results still be correct. */ + T precision = 10*Expression::epsilon(); + return std::round(f/precision)*precision; } -template std::complex Trigonometry::computeDirectOnComplex(const std::complex, Expression::AngleUnit, ComplexFunction); -template std::complex Trigonometry::computeDirectOnComplex(const std::complex, Expression::AngleUnit, ComplexFunction); -template std::complex Trigonometry::computeInverseOnComplex(const std::complex, Expression::AngleUnit, ComplexFunction); -template std::complex Trigonometry::computeInverseOnComplex(const std::complex, Expression::AngleUnit, ComplexFunction); +template +std::complex Trigonometry::RoundToMeaningfulDigits(const std::complex c) { + return std::complex(RoundToMeaningfulDigits(c.real()), RoundToMeaningfulDigits(c.imag())); +} + +template std::complex Trigonometry::ConvertToRadian(std::complex, Expression::AngleUnit); +template std::complex Trigonometry::ConvertToRadian(std::complex, Expression::AngleUnit); +template std::complex Trigonometry::ConvertRadianToAngleUnit(std::complex, Expression::AngleUnit); +template std::complex Trigonometry::ConvertRadianToAngleUnit(std::complex, Expression::AngleUnit); +template std::complex Trigonometry::RoundToMeaningfulDigits(std::complex); +template std::complex Trigonometry::RoundToMeaningfulDigits(std::complex); } diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp index 933b9f74d..b321fcca3 100644 --- a/poincare/test/trigo.cpp +++ b/poincare/test/trigo.cpp @@ -22,63 +22,308 @@ QUIZ_CASE(poincare_parse_trigo) { } QUIZ_CASE(poincare_trigo_evaluate) { - // TODO: check with Léo how to handle cos/acos on complex in Degree... + /* cos: R -> R (oscillator) + * Ri -> R (even) + */ + // On R assert_parsed_expression_evaluates_to("cos(2)", "-4.1614683654714E-1", Radian); assert_parsed_expression_evaluates_to("cos(2)", "0.9993908270191", Degree); - assert_parsed_expression_evaluates_to("cos(I-4)", "(-1.008625)-0.8893952*I", Radian); - assert_parsed_expression_evaluates_to("cos(I-4)", "0.997716+0.00121754*I", Degree, Cartesian, 6); // TODO: should we? - assert_parsed_expression_evaluates_to("sin(2)", "9.0929742682568E-1", Radian); - assert_parsed_expression_evaluates_to("sin(2)", "3.4899496702501E-2", Degree); - assert_parsed_expression_evaluates_to("sin(I-4)", "1.16781-0.768163*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("sin(I-4)", "(-0.0697671)+0.0174117*I", Degree, Cartesian, 6); - assert_parsed_expression_evaluates_to("tan(2)", "-2.1850398632615", Radian); - assert_parsed_expression_evaluates_to("tan(2)", "3.4920769491748E-2", Degree); - assert_parsed_expression_evaluates_to("tan(I-4)", "(-0.2735531)+1.00281*I", Radian); - assert_parsed_expression_evaluates_to("tan(I-4)", "(-0.0699054)+0.0175368*I", Degree, Cartesian, 6); - assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Radian); - assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Degree); - assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Degree, Cartesian, 6); - assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Radian); - assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Degree); - assert_parsed_expression_evaluates_to("sinh(I-4)", "(-14.7448)+22.9791*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("sinh(I-4)", "(-14.7448)+22.9791*I", Degree, Cartesian, 6); - assert_parsed_expression_evaluates_to("tanh(2)", "9.6402758007582E-1", Radian); - assert_parsed_expression_evaluates_to("tanh(2)", "9.6402758007582E-1", Degree); - assert_parsed_expression_evaluates_to("tanh(I-4)", "(-1.000279)+0.0006102409*I", Radian); - assert_parsed_expression_evaluates_to("tanh(I-4)", "(-1.000279)+0.0006102409*I", Degree); + // Oscillator + assert_parsed_expression_evaluates_to("cos(P/2)", "0", Radian); + assert_parsed_expression_evaluates_to("cos(3*P/2)", "0", Radian); + assert_parsed_expression_evaluates_to("cos(3*P)", "-1", Radian); + assert_parsed_expression_evaluates_to("cos(-540)", "-1", Degree); + // On R*i + assert_parsed_expression_evaluates_to("cos(-2*I)", "3.7621956910836", Radian); + assert_parsed_expression_evaluates_to("cos(-2*I)", "1.0006092967033", Degree); + // Symmetry: even + assert_parsed_expression_evaluates_to("cos(2*I)", "3.7621956910836", Radian); + assert_parsed_expression_evaluates_to("cos(2*I)", "1.0006092967033", Degree); + // On C + assert_parsed_expression_evaluates_to("cos(I-4)", "(-1.008625)-0.889395*I", Radian); + assert_parsed_expression_evaluates_to("cos(I-4)", "0.997716+0.001218*I", Degree, Cartesian, 6); + /* sin: R -> R (oscillator) + * Ri -> Ri (odd) + */ + // On R + assert_parsed_expression_evaluates_to("sin(2)", "9.0929742682568E-1", Radian); + assert_parsed_expression_evaluates_to("sin(2)", "0.0348994967025", Degree); + // Oscillator + assert_parsed_expression_evaluates_to("sin(P/2)", "1", Radian); + assert_parsed_expression_evaluates_to("sin(3*P/2)", "-1", Radian); + assert_parsed_expression_evaluates_to("sin(3*P)", "0", Radian); + assert_parsed_expression_evaluates_to("sin(-540)", "0", Degree); + // On R*i + assert_parsed_expression_evaluates_to("sin(3*I)", "10.01787492741*I", Radian); + assert_parsed_expression_evaluates_to("sin(3*I)", "0.052384*I", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("sin(-3*I)", "-10.01787492741*I", Radian); + assert_parsed_expression_evaluates_to("sin(-3*I)", "-0.052384*I", Degree); + // On: C + assert_parsed_expression_evaluates_to("sin(I-4)", "1.16781-0.768163*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("sin(I-4)", "(-0.069767)+0.017412*I", Degree, Cartesian, 6); + + /* tan: R -> R (tangent-style) + * Ri -> Ri (odd) + */ + // On R + assert_parsed_expression_evaluates_to("tan(2)", "-2.1850398632615", Radian); + assert_parsed_expression_evaluates_to("tan(2)", "3.492076949175E-2", Degree); + // Tangent-style + assert_parsed_expression_evaluates_to("tan(P/2)", "undef", Radian); + assert_parsed_expression_evaluates_to("tan(3*P/2)", "undef", Radian); + assert_parsed_expression_evaluates_to("tan(3*P)", "0", Radian); + assert_parsed_expression_evaluates_to("tan(-540)", "0", Degree); + // On R*i + assert_parsed_expression_evaluates_to("tan(-2*I)", "-9.6402758007582E-1*I", Radian); + assert_parsed_expression_evaluates_to("tan(-2*I)", "-0.034892*I", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("tan(2*I)", "9.6402758007582E-1*I", Radian); + assert_parsed_expression_evaluates_to("tan(2*I)", "0.034892*I", Degree); + // On C + assert_parsed_expression_evaluates_to("tan(I-4)", "(-0.273553)+1.002811*I", Radian); + assert_parsed_expression_evaluates_to("tan(I-4)", "(-0.069905)+0.017537*I", Degree, Cartesian); + + /* acos: [-1,1] -> R + * ]-inf,-1[ -> Pi+R*i (odd imaginary) + * ]1, inf[ -> R*i (odd imaginary) + * R*i -> Pi/2+R*i (odd imaginary) + */ + // On [-1, 1] assert_parsed_expression_evaluates_to("acos(0.5)", "1.0471975511966", Radian); assert_parsed_expression_evaluates_to("acos(0.5)", "60", Degree); + // On [1, inf[ + assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", Degree); + // Symmetry: odd on imaginary + assert_parsed_expression_evaluates_to("acos(-2)", "3.1415926535898-1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("acos(-2)", "180-75.456129290217*I", Degree); + // On ]-inf, -1[ + assert_parsed_expression_evaluates_to("acos(-32)", "3.1415926535898-4.1586388532792*I", Radian); + assert_parsed_expression_evaluates_to("acos(-32)", "180-238.2725*I", Degree); + // On R*i + //assert_parsed_expression_evaluates_to("acos(3*I)", "1.5707963-1.818446*I", Radian); + //assert_parsed_expression_evaluates_to("acos(3*I)", "90-104.1892*I", Degree); + // Symmetry: odd on imaginary + //assert_parsed_expression_evaluates_to("acos(-3*I)", "1.5707963+1.818446*I", Radian); + //assert_parsed_expression_evaluates_to("acos(-3*I)", "90+104.1892*I", Degree); + // On C assert_parsed_expression_evaluates_to("acos(I-4)", "2.8894-2.0966*I", Radian, Cartesian, 5); assert_parsed_expression_evaluates_to("acos(I-4)", "165.551-120.126*I", Degree, Cartesian, 6); + // Key values + assert_parsed_expression_evaluates_to("acos(0)", "90", Degree); + assert_parsed_expression_evaluates_to("acos(-1)", "180", Degree); + assert_parsed_expression_evaluates_to("acos(1)", "0", Degree); + + /* asin: [-1,1] -> R + * ]-inf,-1[ -> -Pi/2+R*i (odd) + * ]1, inf[ -> Pi/2+R*i (odd) + * R*i -> R*i (odd) + */ + // On [-1, 1] assert_parsed_expression_evaluates_to("asin(0.5)", "0.5235987755983", Radian); assert_parsed_expression_evaluates_to("asin(0.5)", "30", Degree); + // On [1, inf[ + assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("asin(-2)", "(-1.5707963267949)+1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("asin(-2)", "(-90)+75.456129290217*I", Degree); + // On ]-inf, -1[ + assert_parsed_expression_evaluates_to("asin(-32)", "(-1.571)+4.159*I", Radian, Cartesian, 4); + assert_parsed_expression_evaluates_to("asin(-32)", "(-90)+238*I", Degree, Cartesian, 3); + // On R*i + assert_parsed_expression_evaluates_to("asin(3*I)", "1.8184464592321*I", Radian); + // Symmetry: odd + assert_parsed_expression_evaluates_to("asin(-3*I)", "-1.8184464592321*I", Radian); + // On C assert_parsed_expression_evaluates_to("asin(I-4)", "(-1.3186)+2.0966*I", Radian, Cartesian, 5); assert_parsed_expression_evaluates_to("asin(I-4)", "(-75.551)+120.13*I", Degree, Cartesian, 5); + // Key values + assert_parsed_expression_evaluates_to("asin(0)", "0", Degree); + assert_parsed_expression_evaluates_to("asin(-1)", "-90", Degree); + assert_parsed_expression_evaluates_to("asin(1)", "90", Degree); + + /* atan: R -> R (odd) + * [-i,i] -> R*i (odd) + * ]-inf*i,-i[ -> -Pi/2+R*i (odd) + * ]i, inf*i[ -> Pi/2+R*i (odd) + */ + // On R assert_parsed_expression_evaluates_to("atan(2)", "1.1071487177941", Radian); assert_parsed_expression_evaluates_to("atan(2)", "63.434948822922", Degree); - assert_parsed_expression_evaluates_to("atan(I-4)", "(-1.338973)+0.05578589*I", Radian); - assert_parsed_expression_evaluates_to("atan(I-4)", "(-76.71748)+3.196296*I", Degree); + assert_parsed_expression_evaluates_to("atan(0.5)", "0.463648", Radian); + // Symmetry: odd + assert_parsed_expression_evaluates_to("atan(-2)", "-1.1071487177941", Radian); + // On [-i, i] + assert_parsed_expression_evaluates_to("atan(0.2*I)", "0.202733*I", Radian, Cartesian, 6); + // Symmetry: odd + assert_parsed_expression_evaluates_to("atan(-0.2*I)", "-0.202733*I", Radian, Cartesian, 6); + // On [i, inf*i[ + assert_parsed_expression_evaluates_to("atan(26*I)", "1.5707963267949+3.848052056806E-2*I", Radian); + assert_parsed_expression_evaluates_to("atan(26*I)", "90+2.2047714220162*I", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("atan(-26*I)", "(-1.5707963267949)-3.848052056806E-2*I", Radian); + // On ]-inf*i, -i[ + assert_parsed_expression_evaluates_to("atan(-3.4*I)", "(-1.570796)-0.303068*I", Radian); + assert_parsed_expression_evaluates_to("atan(-3.4*I)", "(-90)-17.3645*I", Degree, Cartesian, 6); + // On C + assert_parsed_expression_evaluates_to("atan(I-4)", "(-1.338973)+0.055786*I", Radian); + assert_parsed_expression_evaluates_to("atan(I-4)", "(-76.7175)+3.1963*I", Degree, Cartesian, 6); + // Key values + assert_parsed_expression_evaluates_to("atan(0)", "0", Degree); + assert_parsed_expression_evaluates_to("atan(-I)", "undef", Radian); + assert_parsed_expression_evaluates_to("atan(I)", "undef", Radian); + + /* cosh: R -> R (even) + * R*i -> R (oscillator) + */ + // On R + assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Radian); + assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Degree); + // Symmetry: even + assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", Radian); + assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", Degree); + // On R*i + assert_parsed_expression_evaluates_to("cosh(43*I)", "5.5511330152063E-1", Radian); + // Oscillator + assert_parsed_expression_evaluates_to("cosh(P*I/2)", "0", Radian); + assert_parsed_expression_evaluates_to("cosh(5*P*I/2)", "0", Radian); + assert_parsed_expression_evaluates_to("cosh(8*P*I/2)", "1", Radian); + assert_parsed_expression_evaluates_to("cosh(9*P*I/2)", "0", Radian); + // On C + assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Degree, Cartesian, 6); + + /* sinh: R -> R (odd) + * R*i -> R*i (oscillator) + */ + // On R + assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Radian); + assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("sinh(-2)", "-3.626860407847", Radian); + // On R*i + assert_parsed_expression_evaluates_to("sinh(43*I)", "-0.8317747426286*I", Radian); + // Oscillator + assert_parsed_expression_evaluates_to("sinh(P*I/2)", "I", Radian); + assert_parsed_expression_evaluates_to("sinh(5*P*I/2)", "I", Radian); + assert_parsed_expression_evaluates_to("sinh(7*P*I/2)", "-I", Radian); + assert_parsed_expression_evaluates_to("sinh(8*P*I/2)", "0", Radian); + assert_parsed_expression_evaluates_to("sinh(9*P*I/2)", "I", Radian); + // On C + assert_parsed_expression_evaluates_to("sinh(I-4)", "(-14.7448)+22.9791*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("sinh(I-4)", "(-14.7448)+22.9791*I", Degree, Cartesian, 6); + + /* tanh: R -> R (odd) + * R*i -> R*i (tangent-style) + */ + // On R + assert_parsed_expression_evaluates_to("tanh(2)", "9.6402758007582E-1", Radian); + // Symmetry: odd + assert_parsed_expression_evaluates_to("tanh(-2)", "-9.6402758007582E-1", Degree); + // On R*i + assert_parsed_expression_evaluates_to("tanh(43*I)", "-1.4983873388552*I", Radian); + // Tangent-style + assert_parsed_expression_evaluates_to("tanh(P*I/2)", "undef", Radian); + assert_parsed_expression_evaluates_to("tanh(5*P*I/2)", "undef", Radian); + assert_parsed_expression_evaluates_to("tanh(7*P*I/2)", "undef", Radian); + assert_parsed_expression_evaluates_to("tanh(8*P*I/2)", "0", Radian); + assert_parsed_expression_evaluates_to("tanh(9*P*I/2)", "undef", Radian); + // On C + assert_parsed_expression_evaluates_to("tanh(I-4)", "(-1.000279)+0.00061*I", Radian); + assert_parsed_expression_evaluates_to("tanh(I-4)", "(-1.000279)+0.00061*I", Degree); + + /* acosh: [-1,1] -> R*i + * ]-inf,-1[ -> Pi*i+R (even on real) + * ]1, inf[ -> R (even on real) + * ]-inf*i, 0[ -> -Pi/2*i+R (even on real) + * ]0, inf*i[ -> Pi/2*i+R (even on real) + */ + // On [-1,1] assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", Radian); assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", Degree); + // On ]-inf, -1[ + assert_parsed_expression_evaluates_to("acosh(-4)", "2.0634370688956+3.1415926535898*I", Radian); + assert_parsed_expression_evaluates_to("acosh(-4)", "2.06344+3.14159*I", Radian, Cartesian, 6); + // On ]1,inf[: Symmetry: even on real + assert_parsed_expression_evaluates_to("acosh(4)", "2.0634370688956", Radian); + assert_parsed_expression_evaluates_to("acosh(4)", "2.063437", Radian); + // On ]-inf*i, 0[ + assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.4309584920805-1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.431-1.571*I", Radian, Cartesian, 4); + // On ]0, i*inf[: Symmetry: even on real + assert_parsed_expression_evaluates_to("acosh(42*I)", "4.4309584920805+1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("acosh(42*I)", "4.431+1.571*I", Radian, Cartesian, 4); + // On C assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", Radian, Cartesian, 5); assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", Degree, Cartesian, 5); + // Key values + //assert_parsed_expression_evaluates_to("acosh(-1)", "3.1415926535898*I", Radian); + assert_parsed_expression_evaluates_to("acosh(1)", "0", Radian); + assert_parsed_expression_evaluates_to("acosh(0)", "1.570796*I", Radian); + + /* acosh: R -> R (odd) + * [-i,i] -> R*i (odd) + * ]-inf*i,-i[ -> -Pi/2*i+R (odd) + * ]i, inf*I[ -> Pi/2*I+R (odd) + */ + // On R assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", Radian); assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", Radian); + assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", Degree); + // On [-i,i] + assert_parsed_expression_evaluates_to("asinh(0.2*I)", "2.0135792079033E-1*I", Radian); + assert_parsed_expression_evaluates_to("asinh(0.2*I)", "0.201358*I", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-2.0135792079033E-1*I", Radian); + assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-0.201358*I", Degree); + // On ]-inf*i, -i[ + assert_parsed_expression_evaluates_to("asinh(-22*I)", "(-3.7836727043295)-1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("asinh(-22*I)", "(-3.784)-1.571*I", Degree, Cartesian, 4); + // On ]i, inf*i[, Symmetry: odd + assert_parsed_expression_evaluates_to("asinh(22*I)", "3.7836727043295+1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("asinh(22*I)", "3.784+1.571*I", Degree, Cartesian, 4); + // On C assert_parsed_expression_evaluates_to("asinh(I-4)", "(-2.123)+0.2383*I", Radian, Cartesian, 4); assert_parsed_expression_evaluates_to("asinh(I-4)", "(-2.123)+0.2383*I", Degree, Cartesian, 4); + + /* acosh: [-1,1] -> R (odd) + * ]-inf,-1[ -> Pi/2*i+R (odd) + * ]1, inf[ -> -Pi/2*i+R (odd) + * R*i -> R*i (odd) + */ + // On [-1,1] assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", Radian); assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", Degree); + // Symmetry: odd + assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", Radian); + assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", Degree); + // On ]1, inf[ + assert_parsed_expression_evaluates_to("atanh(4)", "0.255412811883-1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("atanh(4)", "0.255413-1.570796*I", Degree); + // On ]-inf,-1[, Symmetry: odd + assert_parsed_expression_evaluates_to("atanh(-4)", "(-0.255412811883)+1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("atanh(-4)", "(-0.255413)+1.570796*I", Degree); + // On R*i + assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325817663668*I", Radian); + assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325818*I", Radian); + // Symmetry: odd + assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325817663668*I", Radian); + assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325818*I", Radian); + // On C assert_parsed_expression_evaluates_to("atanh(I-4)", "(-0.238878)+1.50862*I", Radian, Cartesian, 6); assert_parsed_expression_evaluates_to("atanh(I-4)", "(-0.238878)+1.50862*I", Degree, Cartesian, 6); // WARNING: evaluate on branch cut can be multivalued - assert_parsed_expression_evaluates_to("acos(2)", "-1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("acos(2)", "-75.456129290217*I", Degree); - assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949+1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("asin(2)", "90+75.456129290217*I", Degree); - assert_parsed_expression_evaluates_to("atanh(2)", "5.4930614433405E-1+1.5707963267949*I", Radian); + assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", Radian); + assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("atanh(2)", "5.4930614433405E-1-1.5707963267949*I", Radian); assert_parsed_expression_evaluates_to("atan(2I)", "1.5707963267949+5.4930614433405E-1*I", Radian); assert_parsed_expression_evaluates_to("atan(2I)", "90+31.472923730945*I", Degree); assert_parsed_expression_evaluates_to("asinh(2I)", "1.3169578969248+1.5707963267949*I", Radian);