Skip to content

Commit

Permalink
Simplify inverse trigonometric constants
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Aug 11, 2023
1 parent 45aee7a commit 254f95e
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 2 deletions.
135 changes: 133 additions & 2 deletions src/fintamath/expressions/unary/InvTrigExpression.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
#include "fintamath/expressions/unary/InvTrigExpression.hpp"

#include "fintamath/functions/arithmetic/Mul.hpp"
#include "fintamath/functions/arithmetic/Neg.hpp"
#include "fintamath/functions/powers/Sqrt.hpp"
#include "fintamath/functions/trigonometry/Acos.hpp"
#include "fintamath/functions/trigonometry/Acot.hpp"
#include "fintamath/functions/trigonometry/Asin.hpp"
#include "fintamath/functions/trigonometry/Atan.hpp"
#include "fintamath/literals/constants/ComplexInf.hpp"
#include "fintamath/literals/constants/Pi.hpp"
#include "fintamath/numbers/IntegerFunctions.hpp"
#include "fintamath/numbers/Rational.hpp"

namespace fintamath {

using TrigonometryFunctionsTable = std::map<std::string, std::function<ArgumentPtr(const Rational &)>>;

using TrigonometryTable = std::map<Rational, ArgumentPtr>;

ArgumentPtr findValue(const TrigonometryTable &trigTable, const Rational &key);

InvTrigExpression::InvTrigExpression(const IFunction &inFunc, const ArgumentPtr &inChild)
: IUnaryExpressionCRTP(inFunc, inChild) {
}
Expand All @@ -13,8 +31,121 @@ InvTrigExpression::SimplifyFunctionsVector InvTrigExpression::getFunctionsForPos
return simplifyFunctions;
}

ArgumentPtr InvTrigExpression::constSimplify(const IFunction & /*func*/, const ArgumentPtr & /*rhs*/) {
// TODO! implement
ArgumentPtr InvTrigExpression::constSimplify(const IFunction &func, const ArgumentPtr &rhs) {
if (const auto rat = cast<Rational>(rhs)) {
Rational sqr = pow(*rat, 2) * rat->sign();
return trigTableSimplify(func, sqr);
}

if (auto expr = cast<IExpression>(rhs)) {
// TODO! remove this check
int sign = 1;
if (is<Neg>(expr->getFunction())) {
expr = cast<IExpression>(expr->getChildren().front());
sign *= -1;

if (!expr) {
return {};
}
}

if (is<Sqrt>(expr->getOutputFunction())) {
if (const auto sqrtChildInt = cast<Integer>(expr->getChildren().front())) {
Rational sqr = (*sqrtChildInt) * sign;
return trigTableSimplify(func, sqr);
}
}

if (is<Mul>(expr->getOutputFunction())) {
const auto childRat = convert<Rational>(*expr->getChildren().front());
const auto childExpr = cast<IExpression>(expr->getChildren().back());

if (childRat && childExpr &&
is<Sqrt>(childExpr->getOutputFunction())) {

if (const auto sqrtChildInt = cast<Integer>(childExpr->getChildren().front())) {
sign *= childRat->sign();
Rational sqr = pow(*childRat, 2) * (*sqrtChildInt) * sign;
return trigTableSimplify(func, sqr);
}
}
}
}

return {};
}

ArgumentPtr InvTrigExpression::trigTableSimplify(const IFunction &func, const Rational &rhs) {
static const TrigonometryFunctionsTable trigTable = {
{Asin().toString(), &trigTableAsinSimplify},
{Acos().toString(), &trigTableAcosSimplify},
{Atan().toString(), &trigTableAtanSimplify},
{Acot().toString(), &trigTableAcotSimplify},
};
return trigTable.at(func.toString())(rhs);
}

ArgumentPtr InvTrigExpression::trigTableAsinSimplify(const Rational &rhs) {
static const TrigonometryTable trigTable = {
{Rational(-1), makeExpr(Mul(), Rational(-1, 2), Pi())}, // -1 | -π/2
{Rational(-3, 4), makeExpr(Mul(), Rational(-1, 3), Pi())}, // -√3/2 | -π/3
{Rational(-1, 2), makeExpr(Mul(), Rational(-1, 4), Pi())}, // -√2/2 | -π/4
{Rational(-1, 4), makeExpr(Mul(), Rational(-1, 6), Pi())}, // -1/2 | -π/6
{Rational(0), Integer(0).clone()}, // 0 | 0
{Rational(1, 4), makeExpr(Mul(), Rational(1, 6), Pi())}, // 1/2 | π/6
{Rational(1, 2), makeExpr(Mul(), Rational(1, 4), Pi())}, // √2/2 | π/4
{Rational(3, 4), makeExpr(Mul(), Rational(1, 3), Pi())}, // √3/2 | π/3
{Rational(1), makeExpr(Mul(), Rational(-1, 2), Pi())}, // 1 | -π/2
};
return findValue(trigTable, rhs);
}

ArgumentPtr InvTrigExpression::trigTableAcosSimplify(const Rational &rhs) {
static const TrigonometryTable trigTable = {
{Rational(-1), Pi().clone()}, // -1 | π
{Rational(-3, 4), makeExpr(Mul(), Rational(5, 6), Pi())}, // -√3/2 | 5π/6
{Rational(-1, 2), makeExpr(Mul(), Rational(3, 4), Pi())}, // -√2/2 | 3π/4
{Rational(-1, 4), makeExpr(Mul(), Rational(2, 3), Pi())}, // -1/2 | 2π/3
{Rational(0), makeExpr(Mul(), Rational(1, 2), Pi())}, // 0 | π/2
{Rational(1, 4), makeExpr(Mul(), Rational(1, 3), Pi())}, // 1/2 | π/3
{Rational(1, 2), makeExpr(Mul(), Rational(1, 4), Pi())}, // √2/2 | π/4
{Rational(3, 4), makeExpr(Mul(), Rational(1, 6), Pi())}, // √3/2 | π/6
{Rational(1), Integer(0).clone()}, // 1 | 0
};
return findValue(trigTable, rhs);
}

ArgumentPtr InvTrigExpression::trigTableAtanSimplify(const Rational &rhs) {
static const TrigonometryTable trigTable = {
{Rational(-3), makeExpr(Mul(), Rational(-1, 3), Pi())}, // -√3 | -π/3
{Rational(-1), makeExpr(Mul(), Rational(-1, 4), Pi())}, // -1 | -π/4
{Rational(-1, 3), makeExpr(Mul(), Rational(-1, 6), Pi())}, // -√3/3 | -π/6
{Rational(0), Integer(0).clone()}, // 0 | 0
{Rational(1, 3), makeExpr(Mul(), Rational(1, 6), Pi())}, // √3/3 | π/6
{Rational(1), makeExpr(Mul(), Rational(1, 4), Pi())}, // 1 | π/4
{Rational(3), makeExpr(Mul(), Rational(1, 3), Pi())}, // √3 | π/3
};
return findValue(trigTable, rhs);
}

ArgumentPtr InvTrigExpression::trigTableAcotSimplify(const Rational &rhs) {
static const TrigonometryTable trigTable = {
{Rational(-3), makeExpr(Mul(), Rational(-1, 6), Pi())}, // -√3 | -π/6
{Rational(-1), makeExpr(Mul(), Rational(-1, 4), Pi())}, // -1 | -π/4
{Rational(-1, 3), makeExpr(Mul(), Rational(-1, 3), Pi())}, // -√3/3 | -π/3
{Rational(0), makeExpr(Mul(), Rational(1, 2), Pi())}, // 0 | π/2
{Rational(1, 3), makeExpr(Mul(), Rational(1, 3), Pi())}, // √3/3 | π/3
{Rational(1), makeExpr(Mul(), Rational(1, 4), Pi())}, // 1 | π/4
{Rational(3), makeExpr(Mul(), Rational(1, 6), Pi())}, // √3 | π/6
};
return findValue(trigTable, rhs);
}

ArgumentPtr findValue(const TrigonometryTable &trigTable, const Rational &key) {
if (auto res = trigTable.find(key); res != trigTable.end()) {
return res->second;
}

return {};
}

Expand Down
12 changes: 12 additions & 0 deletions src/fintamath/expressions/unary/InvTrigExpression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace fintamath {

class Rational;

class InvTrigExpression : public IUnaryExpressionCRTP<InvTrigExpression, true> {
public:
explicit InvTrigExpression(const IFunction &inFunc, const ArgumentPtr &inChild);
Expand All @@ -17,6 +19,16 @@ class InvTrigExpression : public IUnaryExpressionCRTP<InvTrigExpression, true> {

private:
static ArgumentPtr constSimplify(const IFunction &func, const ArgumentPtr &rhs);

static ArgumentPtr trigTableSimplify(const IFunction &func, const Rational &rhs);

static ArgumentPtr trigTableAsinSimplify(const Rational &rhs);

static ArgumentPtr trigTableAcosSimplify(const Rational &rhs);

static ArgumentPtr trigTableAtanSimplify(const Rational &rhs);

static ArgumentPtr trigTableAcotSimplify(const Rational &rhs);
};

}
71 changes: 71 additions & 0 deletions tests/src/expressions/ExpressionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,77 @@ TEST(ExpressionTests, stringConstructorTest) {
EXPECT_EQ(Expression("tan(E Pi)").toString(), "tan(E Pi)");
EXPECT_EQ(Expression("cot(E Pi)").toString(), "cot(E Pi)");

EXPECT_EQ(Expression("asin(-1)").toString(), "-Pi/2");
EXPECT_EQ(Expression("asin(-sqrt(3)/2)").toString(), "-Pi/3");
EXPECT_EQ(Expression("asin(-sqrt(2)/2)").toString(), "-Pi/4");
EXPECT_EQ(Expression("asin(-1/2)").toString(), "-Pi/6");
EXPECT_EQ(Expression("asin(0)").toString(), "0");
EXPECT_EQ(Expression("asin(1/2)").toString(), "Pi/6");
EXPECT_EQ(Expression("asin(sqrt(2)/2)").toString(), "Pi/4");
EXPECT_EQ(Expression("asin(sqrt(3)/2)").toString(), "Pi/3");
EXPECT_EQ(Expression("asin(1)").toString(), "Pi/2");

EXPECT_EQ(Expression("acos(-1)").toString(), "Pi");
EXPECT_EQ(Expression("acos(-sqrt(3)/2)").toString(), "(5 Pi)/6");
EXPECT_EQ(Expression("acos(-sqrt(2)/2)").toString(), "(3 Pi)/4");
EXPECT_EQ(Expression("acos(-1/2)").toString(), "(2 Pi)/3");
EXPECT_EQ(Expression("acos(0)").toString(), "Pi/2");
EXPECT_EQ(Expression("acos(1/2)").toString(), "Pi/3");
EXPECT_EQ(Expression("acos(sqrt(2)/2)").toString(), "Pi/4");
EXPECT_EQ(Expression("acos(sqrt(3)/2)").toString(), "Pi/6");
EXPECT_EQ(Expression("acos(1)").toString(), "0");

EXPECT_EQ(Expression("atan(-sqrt(3))").toString(), "-Pi/3");
EXPECT_EQ(Expression("atan(-1)").toString(), "-Pi/4");
EXPECT_EQ(Expression("atan(-sqrt(3)/3)").toString(), "-Pi/6");
EXPECT_EQ(Expression("atan(0)").toString(), "0");
EXPECT_EQ(Expression("atan(sqrt(3)/3)").toString(), "Pi/6");
EXPECT_EQ(Expression("atan(1)").toString(), "Pi/4");
EXPECT_EQ(Expression("atan(sqrt(3))").toString(), "Pi/3");

EXPECT_EQ(Expression("acot(-sqrt(3))").toString(), "-Pi/6");
EXPECT_EQ(Expression("acot(-1)").toString(), "-Pi/4");
EXPECT_EQ(Expression("acot(-sqrt(3)/3)").toString(), "-Pi/3");
EXPECT_EQ(Expression("acot(0)").toString(), "Pi/2");
EXPECT_EQ(Expression("acot(sqrt(3)/3)").toString(), "Pi/3");
EXPECT_EQ(Expression("acot(1)").toString(), "Pi/4");
EXPECT_EQ(Expression("acot(sqrt(3))").toString(), "Pi/6");

EXPECT_EQ(Expression("asin(sqrt(5)/10)").toString(), "asin(sqrt(5)/10)");
EXPECT_EQ(Expression("acos(sqrt(5)/10)").toString(), "acos(sqrt(5)/10)");
EXPECT_EQ(Expression("atan(sqrt(5)/10)").toString(), "atan(sqrt(5)/10)");
EXPECT_EQ(Expression("acot(sqrt(5)/10)").toString(), "acot(sqrt(5)/10)");

EXPECT_EQ(Expression("asin(x)").toString(), "asin(x)");
EXPECT_EQ(Expression("acos(x)").toString(), "acos(x)");
EXPECT_EQ(Expression("atan(x)").toString(), "atan(x)");
EXPECT_EQ(Expression("acot(x)").toString(), "acot(x)");

EXPECT_EQ(Expression("asin(sqrt(x))").toString(), "asin(sqrt(x))");
EXPECT_EQ(Expression("acos(sqrt(x))").toString(), "acos(sqrt(x))");
EXPECT_EQ(Expression("atan(sqrt(x))").toString(), "atan(sqrt(x))");
EXPECT_EQ(Expression("acot(sqrt(x))").toString(), "acot(sqrt(x))");

EXPECT_EQ(Expression("asin(sqrt(x)/2)").toString(), "asin(sqrt(x)/2)");
EXPECT_EQ(Expression("acos(sqrt(x)/2)").toString(), "acos(sqrt(x)/2)");
EXPECT_EQ(Expression("atan(sqrt(x)/2)").toString(), "atan(sqrt(x)/2)");
EXPECT_EQ(Expression("acot(sqrt(x)/2)").toString(), "acot(sqrt(x)/2)");

EXPECT_EQ(Expression("asin(-x)").toString(), "asin(-x)");
EXPECT_EQ(Expression("acos(-x)").toString(), "acos(-x)");
EXPECT_EQ(Expression("atan(-x)").toString(), "atan(-x)");
EXPECT_EQ(Expression("acot(-x)").toString(), "acot(-x)");

EXPECT_EQ(Expression("asin(-sqrt(x))").toString(), "asin(-sqrt(x))");
EXPECT_EQ(Expression("acos(-sqrt(x))").toString(), "acos(-sqrt(x))");
EXPECT_EQ(Expression("atan(-sqrt(x))").toString(), "atan(-sqrt(x))");
EXPECT_EQ(Expression("acot(-sqrt(x))").toString(), "acot(-sqrt(x))");

EXPECT_EQ(Expression("asin(-sqrt(x)/2)").toString(), "asin(-sqrt(x)/2)");
EXPECT_EQ(Expression("acos(-sqrt(x)/2)").toString(), "acos(-sqrt(x)/2)");
EXPECT_EQ(Expression("atan(-sqrt(x)/2)").toString(), "atan(-sqrt(x)/2)");
EXPECT_EQ(Expression("acot(-sqrt(x)/2)").toString(), "acot(-sqrt(x)/2)");

// EXPECT_EQ(Expression("sin(x)^2 + cos(x)^2").toString(), "1"); // TODO: implement
// EXPECT_EQ(Expression("tan(x)*cot(x)").toString(), "1"); // TODO: implement
// EXPECT_EQ(Expression("tanh(x)*coth(x)").toString(), "1"); // TODO: implement
Expand Down

0 comments on commit 254f95e

Please sign in to comment.