diff --git a/src/fintamath/expressions/ExpressionUtils.cpp b/src/fintamath/expressions/ExpressionUtils.cpp index 3eaadc095..0b3b95d5c 100644 --- a/src/fintamath/expressions/ExpressionUtils.cpp +++ b/src/fintamath/expressions/ExpressionUtils.cpp @@ -84,7 +84,7 @@ std::string prefixUnaryOperatorToString(const IOperator &oper, const ArgumentPtr std::string result = oper.toString(); if (const auto child = cast(rhs)) { - if (is(child->getFunction())) { + if (is(child->getOutputFunction())) { return result + putInBrackets(rhs->toString()); } } @@ -96,7 +96,7 @@ std::string postfixUnaryOperatorToString(const IOperator &oper, const ArgumentPt std::string result = rhs->toString(); if (const auto child = cast(rhs)) { - if (is(child->getFunction())) { + if (is(child->getOutputFunction())) { return putInBrackets(result) + oper.toString(); } } diff --git a/src/fintamath/expressions/binary/CompExpression.cpp b/src/fintamath/expressions/binary/CompExpression.cpp index c9899cec5..2bead4acd 100644 --- a/src/fintamath/expressions/binary/CompExpression.cpp +++ b/src/fintamath/expressions/binary/CompExpression.cpp @@ -119,7 +119,7 @@ ArgumentPtr CompExpression::coeffSimplify(const IFunction &func, const ArgumentP } } - if (dividerNum) { + if (dividerNum && hasVariables(lhsExpr)) { for (auto &child : dividendPolynom) { child = makeExpr(Div(), child, dividerNum); } @@ -133,6 +133,7 @@ ArgumentPtr CompExpression::coeffSimplify(const IFunction &func, const ArgumentP return makeExpr(func, newLhs, rhs); } } + return {}; } diff --git a/src/fintamath/expressions/binary/DivExpression.cpp b/src/fintamath/expressions/binary/DivExpression.cpp index b1a5df1cb..1a44dda3e 100644 --- a/src/fintamath/expressions/binary/DivExpression.cpp +++ b/src/fintamath/expressions/binary/DivExpression.cpp @@ -17,6 +17,15 @@ DivExpression::DivExpression(const ArgumentPtr &inLhsChild, const ArgumentPtr &i : IBinaryExpressionCRTP(Div(), inLhsChild, inRhsChild) { } +std::string DivExpression::toString() const { + if (auto lhsChildExpr = cast(lhsChild); lhsChildExpr && is(lhsChildExpr->getFunction())) { + ArgumentPtr innerDiv = makeExpr(Div(), lhsChildExpr->getChildren().front(), rhsChild); + return makeExpr(Neg(), innerDiv)->toString(); + } + + return IBinaryExpression::toString(); +} + DivExpression::SimplifyFunctionsVector DivExpression::getFunctionsForPreSimplify() const { static const DivExpression::SimplifyFunctionsVector simplifyFunctions = { &DivExpression::zeroSimplify, // diff --git a/src/fintamath/expressions/binary/DivExpression.hpp b/src/fintamath/expressions/binary/DivExpression.hpp index 1638de2fc..5d3f35c95 100644 --- a/src/fintamath/expressions/binary/DivExpression.hpp +++ b/src/fintamath/expressions/binary/DivExpression.hpp @@ -9,6 +9,8 @@ class DivExpression : public IBinaryExpressionCRTP { public: explicit DivExpression(const ArgumentPtr &inLhsChild, const ArgumentPtr &inRhsChild); + std::string toString() const override; + static MathObjectTypeId getTypeIdStatic() { return MathObjectTypeId(MathObjectType::DivExpression); } diff --git a/src/fintamath/expressions/binary/LogExpression.cpp b/src/fintamath/expressions/binary/LogExpression.cpp index 2c97c02df..e1e4e803e 100644 --- a/src/fintamath/expressions/binary/LogExpression.cpp +++ b/src/fintamath/expressions/binary/LogExpression.cpp @@ -52,6 +52,7 @@ LogExpression::SimplifyFunctionsVector LogExpression::getFunctionsForPreSimplify static const LogExpression::SimplifyFunctionsVector simplifyFunctions = { &LogExpression::numSimplify, // &LogExpression::equalSimplify, // + &LogExpression::powSimplify, // }; return simplifyFunctions; } diff --git a/src/fintamath/expressions/binary/PowExpression.cpp b/src/fintamath/expressions/binary/PowExpression.cpp index be6a9ef4d..894c6f2fa 100644 --- a/src/fintamath/expressions/binary/PowExpression.cpp +++ b/src/fintamath/expressions/binary/PowExpression.cpp @@ -217,17 +217,17 @@ ArgumentPtr PowExpression::numSimplify(const IFunction & /*func*/, const Argumen ArgumentPtr res = makeExpr(Div(), std::make_shared(1), lhs); return res; } - - if (*rhsInt < 0) { - ArgumentPtr res = makeExpr(Div(), std::make_shared(1), makeExpr(Pow(), lhs, makeExpr(Neg(), rhsInt))); - return res; - } } if (lhsInt && *lhsInt == 0) { return lhs; } + if (const auto rhsComp = cast(rhs); rhsComp && *rhsComp < Integer(0)) { + ArgumentPtr res = makeExpr(Div(), std::make_shared(1), makeExpr(Pow(), lhs, makeExpr(Neg(), rhs))); + return res; + } + return {}; } diff --git a/src/fintamath/expressions/interfaces/IPolynomExpression.cpp b/src/fintamath/expressions/interfaces/IPolynomExpression.cpp index efd30efcb..2b4d26054 100644 --- a/src/fintamath/expressions/interfaces/IPolynomExpression.cpp +++ b/src/fintamath/expressions/interfaces/IPolynomExpression.cpp @@ -318,29 +318,66 @@ int IPolynomExpression::comparatorPolynomAndNonPolynom(const std::shared_ptr &lhs, const ArgumentPtr &rhs) const { - if (auto oper = cast(lhs->getFunction()); - oper && oper->getOperatorPriority() <= IOperator::Priority::PrefixUnary) { - - if (int res = comparator(lhs->getChildren().front(), rhs); res != 0) { - return res; - } - - return 1; + if (!is(rhs)) { + return !isTermsOrderInversed() ? -1 : 1; } if (auto res = comparatorVariables(lhs, rhs, isTermsOrderInversed()); res != 0) { return res; } + if (auto lhsOper = cast(lhs->getFunction())) { + auto lhsOperPriority = lhsOper->getOperatorPriority(); + + switch (lhsOperPriority) { + case IOperator::Priority::PostfixUnary: + case IOperator::Priority::PrefixUnary: { + if (int res = comparator(lhs->getChildren().front(), rhs); res != 0) { + return res; + } + + return 1; + } + case IOperator::Priority::Exponentiation: + case IOperator::Priority::Multiplication: { + static const ArgumentPtr one = Integer(1).clone(); + ArgumentPtr rhsExpr = makeExpr(*lhsOper, rhs, one); + int comp = comparator(lhs, rhsExpr); + return isTermsOrderInversed() ? comp * -1 : comp; + } + default: + break; + } + } + return !isTermsOrderInversed() ? -1 : 1; } int IPolynomExpression::comparatorExpressions(const std::shared_ptr &lhs, const std::shared_ptr &rhs) const { + auto lhsOper = cast(lhs->getFunction()); + auto rhsOper = cast(rhs->getFunction()); + ArgumentsPtrVector lhsChildren = lhs->getChildren(); ArgumentsPtrVector rhsChildren = rhs->getChildren(); + if (lhsOper && lhsOper->getOperatorPriority() == IOperator::Priority::PrefixUnary && + (!rhsOper || rhsOper->getOperatorPriority() != IOperator::Priority::PrefixUnary)) { + + if (int res = comparator(lhsChildren.front(), rhs); res != 0) { + return res; + } + } + + if (rhsOper && rhsOper->getOperatorPriority() == IOperator::Priority::PrefixUnary && + (!lhsOper || lhsOper->getOperatorPriority() != IOperator::Priority::PrefixUnary)) { + + if (int res = comparator(lhs, rhsChildren.front()); res != 0) { + return res; + } + } + ChildrenComparatorResult childrenComp = comparatorChildren(lhs->getChildren(), rhs->getChildren()); if (childrenComp.prefixVariables != 0) { diff --git a/src/fintamath/expressions/polynomial/AddExpression.cpp b/src/fintamath/expressions/polynomial/AddExpression.cpp index 0312a1218..d65eec718 100644 --- a/src/fintamath/expressions/polynomial/AddExpression.cpp +++ b/src/fintamath/expressions/polynomial/AddExpression.cpp @@ -187,7 +187,7 @@ ArgumentPtr AddExpression::simplifyMulLogarithms(const IFunction & /*func*/, con mulExpr = lhsExpr; logExpr = rhsExpr; } - else if (is(rhsExpr->getFunction()) && is(lhsExpr->getFunction())) { + else if (is(lhsExpr->getFunction()) && is(rhsExpr->getFunction())) { mulExpr = rhsExpr; logExpr = lhsExpr; } diff --git a/tests/src/FintamathTests.cpp b/tests/src/FintamathTests.cpp index 1c6bcf9c5..cdc9a96a1 100644 --- a/tests/src/FintamathTests.cpp +++ b/tests/src/FintamathTests.cpp @@ -29,7 +29,7 @@ TEST(FintamathTests, fintamathTests) { EXPECT_EQ(expr.toString(), "x^3 y + 10 x"); expr = x + 10 * x / 10 + (sqrt(x) * 2); - EXPECT_EQ(expr.toString(), "2 sqrt(x) + 2 x"); + EXPECT_EQ(expr.toString(), "2 x + 2 sqrt(x)"); expr = eqv(x * x + y * y * y, x * y); EXPECT_EQ(expr.toString(), "x^2 - x y + y^3 = 0"); diff --git a/tests/src/expressions/ExpressionFunctionsTests.cpp b/tests/src/expressions/ExpressionFunctionsTests.cpp index 58a5a1f4d..db8359a06 100644 --- a/tests/src/expressions/ExpressionFunctionsTests.cpp +++ b/tests/src/expressions/ExpressionFunctionsTests.cpp @@ -271,6 +271,5 @@ TEST(ExpressionFunctionsTests, solveTest) { EXPECT_EQ(solve(Expression("E >= Ey")).toString(), "E y - E <= 0"); EXPECT_EQ(solve(Expression("x >= x sqrt(x)")).toString(), "x^(3/2) - x <= 0"); - EXPECT_EQ(solve(Expression("x >= x^(1/100)")).toString(), "root(x, 100) - x <= 0"); - EXPECT_EQ(solve(Expression("E^Pi > Pi^E")).toString(), "E^Pi - Pi^E > 0"); + EXPECT_EQ(solve(Expression("x >= x^(1/100)")).toString(), "x - root(x, 100) >= 0"); } diff --git a/tests/src/expressions/ExpressionTests.cpp b/tests/src/expressions/ExpressionTests.cpp index c10779292..e87ff7a2b 100644 --- a/tests/src/expressions/ExpressionTests.cpp +++ b/tests/src/expressions/ExpressionTests.cpp @@ -352,16 +352,23 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("(x/y)^2").toString(), "(x^2)/(y^2)"); EXPECT_EQ(Expression("(x/y)^(1/2)").toString(), "sqrt(x)/sqrt(y)"); EXPECT_EQ(Expression("(x/y)^(1/3)").toString(), "root(x, 3)/root(y, 3)"); + EXPECT_EQ(Expression("(x/y)^(-1/2)").toString(), "sqrt(y)/sqrt(x)"); + EXPECT_EQ(Expression("(x/y)^(-1/3)").toString(), "root(y, 3)/root(x, 3)"); EXPECT_EQ(Expression("(x/y)^x").toString(), "(x^x)/(y^x)"); EXPECT_EQ(Expression("(x/y)^(1/x)").toString(), "root(x, x)/root(y, x)"); - - // TODO! fix - // EXPECT_EQ(Expression("x/(2 sqrt(x) - x)").toString(), "TODO"); - // EXPECT_EQ(Expression("(x-1)/(sqrt(x) - x)").toString(), "TODO"); - // EXPECT_EQ(Expression("(x-1)/(2 sqrt(x) - x)").toString(), "TODO"); - // EXPECT_EQ(Expression("(x-1)/(sqrt(x)/2 - x)").toString(), "TODO"); - // EXPECT_EQ(Expression("(x-1)/(2 root(x, 3) - x)").toString(), "TODO"); - // EXPECT_EQ(Expression("(x-1)/(2 x^(4/3) - x)").toString(), "TODO"); + + EXPECT_EQ(Expression("sqrt(x) + x").toString(), "x + sqrt(x)"); + EXPECT_EQ(Expression("sqrt(x) - x").toString(), "-x + sqrt(x)"); + EXPECT_EQ(Expression("x/(sqrt(x) - x)").toString(), "-1 - (sqrt(x)/(x - sqrt(x)))"); + EXPECT_EQ(Expression("x/(2 sqrt(x) - x)").toString(), "-1 + (-2 sqrt(x))/(x - 2 sqrt(x))"); + EXPECT_EQ(Expression("(x-1)/(sqrt(x) - x)").toString(), "-1 - 1/sqrt(x)"); + EXPECT_EQ(Expression("(x-1)/(2 sqrt(x) - x)").toString(), "-1 + (-2 sqrt(x) + 1)/(x - 2 sqrt(x))"); + EXPECT_EQ(Expression("(x-1)/(sqrt(x)/2 - x)").toString(), "-1 + (sqrt(x) - 2)/(-2 x + sqrt(x))"); + EXPECT_EQ(Expression("(x-1)/(root(x, 3) - x)").toString(), "-1 + (-root(x, 3) + 1)/(x - root(x, 3))"); + EXPECT_EQ(Expression("(x-1)/(2 root(x, 3) - x)").toString(), "-1 + (-2 root(x, 3) + 1)/(x - 2 root(x, 3))"); + EXPECT_EQ(Expression("(x-1)/(root(x, 3)/2 - x)").toString(), "-1 + (root(x, 3) - 2)/(-2 x + root(x, 3))"); + EXPECT_EQ(Expression("(x-1)/(x^(4/3) - x)").toString(), "(x - 1)/(x^(4/3) - x)"); + EXPECT_EQ(Expression("(x-1)/(2 x^(4/3) - x)").toString(), "(x - 1)/(2 x^(4/3) - x)"); // TODO! implement this // EXPECT_EQ(Expression("sqrt(x^2)").toString(), "abs(x)"); @@ -445,6 +452,9 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("log(E,5)>ln(5)").toString(), "False"); EXPECT_EQ(Expression("log(E,5)<=ln(5)").toString(), "True"); EXPECT_EQ(Expression("log(E,5)>=ln(5)").toString(), "True"); + EXPECT_EQ(Expression("log(Deg, Deg^Deg) = Deg").toString(), "True"); + EXPECT_EQ(Expression("E^Pi > Pi^E").toString(), "-Pi^E + E^Pi > 0"); // TODO: True + EXPECT_EQ(Expression("Pi^E < E^Pi").toString(), "Pi^E - E^Pi < 0"); // TODO: True EXPECT_EQ(Expression("derivative(a, a)").toString(), "1"); EXPECT_EQ(Expression("derivative(a+a, a)").toString(), "derivative(2 a, a)"); @@ -484,6 +494,7 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("((1=2) -> (1=2) & ~(1=1)) !<-> ((1=1) <-> ~((1=2) | (1=1)))").toString(), "True"); EXPECT_EQ(Expression("False|1=1").toString(), "True"); EXPECT_EQ(Expression("1=1|False").toString(), "True"); + EXPECT_EQ(Expression("a>b|a").toString(), "a - b > 0 | a"); EXPECT_EQ(Expression("~(x = 1)").toString(), "x - 1 != 0"); EXPECT_EQ(Expression("~(x != 1)").toString(), "x - 1 = 0"); @@ -619,16 +630,17 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("ln((E)/(20000.1EE)) + ln(20000.1E)").toString(), "0"); EXPECT_EQ(Expression("ln((10 E)^2) - ln(10 E 10 E)").toString(), "0"); EXPECT_EQ(Expression("ln((10 E)^2) - 2 ln(10 E)").toString(), "0"); - EXPECT_EQ(Expression("3 ln((10 E)^2) - 2 ln(10 E)").toString(), "ln(10000 E^4)"); - EXPECT_EQ(Expression("ln((10 E)^(2ab)) - 2 a ln(10 E) b").toString(), - "ln(10^(2 a b) E^(2 a b) 10^(-2 a b) E^(-2 a b))"); // TODO! 0 - EXPECT_EQ(Expression("ln((10 E)^(2 ln(2))) - 2 ln(2) ln(10 E)").toString(), - "ln(10^(2 ln(2)) E^(2 ln(2)) 10^(-2 ln(2)) E^(-2 ln(2)))"); // TODO! 0 + EXPECT_EQ(Expression("3 ln((10 E)^2) - 2 ln(10 E)").toString(), "4 ln(10 E)"); + EXPECT_EQ(Expression("ln((10 E)^(2ab)) - 2 a ln(10 E) b").toString(), "0"); + EXPECT_EQ(Expression("ln((10 E)^(2 ln(2))) - 2 ln(2) ln(10 E)").toString(), "0"); EXPECT_EQ(Expression("log(2.3,(E)/(20000.1EE)) + log(2.3,20000.1E)").toString(), "0"); EXPECT_EQ(Expression("log(2, 3) + log(3, 4)").toString(), "log(3, 4) + log(2, 3)"); EXPECT_EQ(Expression("x log(2, 3) + log(2, 5)").toString(), "log(2, 5 3^x)"); + EXPECT_EQ(Expression("x log(2, 3) + log(2, 5a)").toString(), "log(2, 5 a 3^x)"); EXPECT_EQ(Expression("log(2, 3) + 3log(3, 4)").toString(), "3 log(3, 4) + log(2, 3)"); EXPECT_EQ(Expression("3log(2x, 3) + log(3, 4)").toString(), "3 log(2 x, 3) + log(3, 4)"); + EXPECT_EQ(Expression("3log(2x, 3) + 4log(3, 4)").toString(), "3 log(2 x, 3) + 4 log(3, 4)"); + EXPECT_EQ(Expression("3log(2x, 3) + 5log(2x, 4)").toString(), "log(2 x, 27648)"); EXPECT_EQ(Expression("sin(asin(x))").toString(), "x"); EXPECT_EQ(Expression("cos(acos(x))").toString(), "x");