diff --git a/include/fintamath/expressions/Expression.hpp b/include/fintamath/expressions/Expression.hpp index 1acbe99cf..402d6f46c 100644 --- a/include/fintamath/expressions/Expression.hpp +++ b/include/fintamath/expressions/Expression.hpp @@ -53,7 +53,7 @@ class Expression : public IExpressionCRTP { void setVariable(const Variable &var, const Expression &val); - static void registerTermsMaker(Parser::Function, const Token &> &&maker) { + static void registerTermMaker(Parser::Function, const Token &> &&maker) { Parser::add(getTermMakers(), std::move(maker)); } diff --git a/include/fintamath/expressions/ExpressionComparator.hpp b/include/fintamath/expressions/ExpressionComparator.hpp new file mode 100644 index 000000000..7fca85d48 --- /dev/null +++ b/include/fintamath/expressions/ExpressionComparator.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "fintamath/functions/FunctionArguments.hpp" + +namespace fintamath { + +struct ComparatorOptions { + bool constantOrderInversed = false; + bool comparableOrderInversed = false; +}; + +std::strong_ordering compare(ArgumentPtr lhs, ArgumentPtr rhs, ComparatorOptions options = {}); + +} diff --git a/include/fintamath/expressions/ExpressionUtils.hpp b/include/fintamath/expressions/ExpressionUtils.hpp index ffe46b085..76988029b 100644 --- a/include/fintamath/expressions/ExpressionUtils.hpp +++ b/include/fintamath/expressions/ExpressionUtils.hpp @@ -64,6 +64,8 @@ std::pair splitPowExpr(const ArgumentPtr &rhs); std::pair splitRational(const ArgumentPtr &arg); +ArgumentPtr makePolynom(const IFunction &func, ArgumentPtrVector &&args); + ArgumentPtr makePolynom(const IFunction &func, const ArgumentPtrVector &args); ArgumentPtrVector getPolynomChildren(const IFunction &func, const ArgumentPtr &arg); diff --git a/include/fintamath/expressions/interfaces/IPolynomExpression.hpp b/include/fintamath/expressions/interfaces/IPolynomExpression.hpp index 5eb1b4471..f6d59f7f8 100644 --- a/include/fintamath/expressions/interfaces/IPolynomExpression.hpp +++ b/include/fintamath/expressions/interfaces/IPolynomExpression.hpp @@ -28,39 +28,19 @@ class IPolynomExpression : public IExpression { using SimplifyFunctionVector = std::vector; - using ExpressionTreePathStack = std::stack, size_t>>; - - struct ChildrenComparatorResult { - int postfix = 0; - int postfixUnary = 0; - int prefixFirst = 0; - int prefixLast = 0; - int prefixVariables = 0; - int size = 0; - }; - virtual SimplifyFunctionVector getFunctionsForPreSimplify() const; virtual SimplifyFunctionVector getFunctionsForPostSimplify() const; virtual std::string childToString(const IOperator &oper, const ArgumentPtr &inChild, const ArgumentPtr &prevChild) const; - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - virtual int comparator(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const; + virtual std::strong_ordering compare(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const; ArgumentPtr preSimplify() const override; ArgumentPtr postSimplify() const override; - virtual bool isTermsOrderInversed() const; + virtual bool isConstantOrderInversed() const; virtual bool isComparableOrderInversed() const; @@ -73,88 +53,6 @@ class IPolynomExpression : public IExpression { void sort(); - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - int comparatorNonExpressions(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const; - - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - int comparatorPolynoms(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) const; - - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - int comparatorPolynomAndNonPolynom(const std::shared_ptr &lhs, - const ArgumentPtr &rhs) const; - - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - int comparatorExpressionAndNonExpression(const std::shared_ptr &lhs, - const ArgumentPtr &rhs) const; - - /** - * @brief - * - * @param lhs - * @param rhs - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - int comparatorExpressions(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) const; - - ChildrenComparatorResult comparatorChildren(const ArgumentPtrVector &lhsChildren, - const ArgumentPtrVector &rhsChildren) const; - - /** - * @brief - * - * @param lhsExpr - * @param rhsExpr - * @return -1 if we should swap the arguments - * @return 1 if we should not swap the arguments - * @return 0 if this comparator fails - */ - static int comparatorFunctions(const std::shared_ptr &lhs, - const std::shared_ptr &rhs); - - int comparatorVariables(const ArgumentPtr &lhs, const ArgumentPtr &rhs, bool isTermsOrderInversed) const; - - static std::shared_ptr getNextVariable(ExpressionTreePathStack &stack); - - static size_t getPositionOfFirstChildWithVariable(const ArgumentPtrVector &children); - - static bool unwrapUnary(ArgumentPtr &lhs); - protected: std::shared_ptr func; diff --git a/include/fintamath/functions/IFunction.hpp b/include/fintamath/functions/IFunction.hpp index 46c8d5b13..d07f21348 100644 --- a/include/fintamath/functions/IFunction.hpp +++ b/include/fintamath/functions/IFunction.hpp @@ -35,6 +35,8 @@ class IFunction : public IMathObject { virtual ArgumentTypeVector getArgTypes() const = 0; + virtual size_t getFunctionOrder() const = 0; + virtual bool doArgsMatch(const ArgumentRefVector &argsVect) const = 0; virtual bool isEvaluatable() const = 0; @@ -51,6 +53,9 @@ class IFunction : public IMathObject { template T> static void registerType() { Parser::registerType(getParser()); + + getFunctionOrderMutableMap()[T().toString()] = maxFunctionOrder; + maxFunctionOrder++; } static std::unique_ptr parse(const std::string &parsedStr, IFunction::Type type = IFunction::Type::Any) { @@ -67,8 +72,16 @@ class IFunction : public IMathObject { protected: virtual std::unique_ptr callAbstract(const ArgumentRefVector &argsVect) const = 0; + static const std::unordered_map &getFunctionOrderMap() { + return getFunctionOrderMutableMap(); + } + private: + static std::unordered_map &getFunctionOrderMutableMap(); + static Parser::Map> &getParser(); + + static inline size_t maxFunctionOrder = 0; }; template diff --git a/include/fintamath/functions/IFunctionCRTP.hpp b/include/fintamath/functions/IFunctionCRTP.hpp index 8667093df..3b4cbeda1 100644 --- a/include/fintamath/functions/IFunctionCRTP.hpp +++ b/include/fintamath/functions/IFunctionCRTP.hpp @@ -28,6 +28,11 @@ class IFunctionCRTP_ : public IFunction { return argTypes; } + size_t getFunctionOrder() const final { + static const std::string funcStr = Derived().toString(); + return getFunctionOrderMap().at(funcStr); + } + bool doArgsMatch(const ArgumentRefVector &argsVect) const override { if (argsVect.empty()) { // TODO: support None type functions return false; diff --git a/src/fintamath/config/ExpressionConfig.cpp b/src/fintamath/config/ExpressionConfig.cpp index 5a56bceb3..406e69291 100644 --- a/src/fintamath/config/ExpressionConfig.cpp +++ b/src/fintamath/config/ExpressionConfig.cpp @@ -113,7 +113,7 @@ struct ExpressionConfig { } static void registerTermsMakers() { - Expression::registerTermsMaker([](const Token &token) { + Expression::registerTermMaker([](const Token &token) { if (auto arg = IFunction::parse(token, IFunction::Type::Binary)) { return std::make_unique(token, std::move(arg)); } @@ -125,7 +125,7 @@ struct ExpressionConfig { return std::unique_ptr(); }); - Expression::registerTermsMaker([](const Token &token) { + Expression::registerTermMaker([](const Token &token) { if (auto arg = ILiteral::parse(token)) { return std::make_unique(token, std::move(arg)); } @@ -133,7 +133,7 @@ struct ExpressionConfig { return std::unique_ptr(); }); - Expression::registerTermsMaker([](const Token &token) { + Expression::registerTermMaker([](const Token &token) { if (auto arg = INumber::parse(token)) { return std::make_unique(token, std::move(arg)); } diff --git a/src/fintamath/config/ParserConfig.cpp b/src/fintamath/config/ParserConfig.cpp index b68c97ded..ca44ccdf3 100644 --- a/src/fintamath/config/ParserConfig.cpp +++ b/src/fintamath/config/ParserConfig.cpp @@ -135,6 +135,11 @@ Parser::Map> &IConstant::getParser() { return parser; } +std::unordered_map &IFunction::getFunctionOrderMutableMap() { + static std::unordered_map orderMap; + return orderMap; +} + Parser::Map> &IFunction::getParser() { static Parser::Map> parser; return parser; diff --git a/src/fintamath/expressions/ExpressionComparator.cpp b/src/fintamath/expressions/ExpressionComparator.cpp new file mode 100644 index 000000000..153c6932e --- /dev/null +++ b/src/fintamath/expressions/ExpressionComparator.cpp @@ -0,0 +1,463 @@ +#include "fintamath/expressions/ExpressionComparator.hpp" + +#include "fintamath/expressions/ExpressionUtils.hpp" +#include "fintamath/expressions/interfaces/IPolynomExpression.hpp" + +namespace fintamath { + +using ExpressionTreePathStack = std::stack, size_t>>; + +using Ordering = std::strong_ordering; + +struct ChildrenComparatorResult { + Ordering postfix = Ordering::equal; + Ordering postfixUnary = Ordering::equal; + Ordering prefixFirst = Ordering::equal; + Ordering prefixLast = Ordering::equal; + Ordering prefixVariables = Ordering::equal; + Ordering size = Ordering::equal; +}; + +Ordering compareNonExpressions(const ArgumentPtr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options); + +Ordering comparePolynoms(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions &options); + +Ordering compareExpressions(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions &options); + +Ordering comparePolynomAndNonPolynom(const std::shared_ptr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options); + +Ordering compareExpressionAndNonExpression(const std::shared_ptr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options); + +Ordering compareFunctions(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions &options); + +Ordering compareVariables(const ArgumentPtr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options); + +ChildrenComparatorResult compareChildren(const ArgumentPtrVector &lhsChildren, + const ArgumentPtrVector &rhsChildren, + const ComparatorOptions &options); + +std::shared_ptr popNextVariable(ExpressionTreePathStack &stack); + +size_t getPositionOfFirstChildWithVariable(const ArgumentPtrVector &children); + +bool unwrapUnaryExpression(ArgumentPtr &arg); + +bool unwrapEmptyExpression(ArgumentPtr &arg); + +Ordering reverse(Ordering ordering); + +Ordering compare(ArgumentPtr lhs, + ArgumentPtr rhs, + ComparatorOptions options) { + + unwrapEmptyExpression(lhs); + unwrapEmptyExpression(rhs); + + auto lhsExpr = cast(lhs); + auto rhsExpr = cast(rhs); + + if (!lhsExpr && !rhsExpr) { + return compareNonExpressions(lhs, rhs, options); + } + + auto lhsPolynom = cast(lhsExpr); + auto rhsPolynom = cast(rhsExpr); + + if (lhsPolynom && rhsPolynom) { + return comparePolynoms(lhsPolynom, rhsPolynom, options); + } + + if (lhsExpr && !rhsExpr) { + return compareExpressionAndNonExpression(lhsExpr, rhs, options); + } + + if (!lhsExpr && rhsExpr) { + return reverse(compareExpressionAndNonExpression(rhsExpr, lhs, options)); + } + + if (lhsPolynom && !rhsPolynom) { + return comparePolynomAndNonPolynom(lhsPolynom, rhs, options); + } + + if (!lhsPolynom && rhsPolynom) { + return reverse(comparePolynomAndNonPolynom(rhsPolynom, lhs, options)); + } + + return compareExpressions(lhsExpr, rhsExpr, options); +} + +Ordering compareNonExpressions(const ArgumentPtr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options) { + + if (is(lhs) && !is(rhs)) { + return !options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + if (!is(lhs) && is(rhs)) { + return options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + + if (is(lhs) && !is(rhs)) { + return !options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + if (!is(lhs) && is(rhs)) { + return options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + + if (*lhs == *rhs) { + return Ordering::equal; + } + + if (auto lhsComp = cast(lhs)) { + if (auto rhsComp = cast(rhs)) { + if (options.comparableOrderInversed) { + return *lhsComp < *rhsComp ? Ordering::greater : Ordering::less; + } + + return *lhsComp > *rhsComp ? Ordering::greater : Ordering::less; + } + } + + return lhs->toString() < rhs->toString() ? Ordering::greater : Ordering::less; +} + +Ordering comparePolynoms(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions &options) { + + ChildrenComparatorResult childrenComp = compareChildren(lhs->getChildren(), rhs->getChildren(), options); + + if (childrenComp.postfix != Ordering::equal) { + return childrenComp.postfix; + } + if (childrenComp.postfixUnary != Ordering::equal) { + return childrenComp.postfixUnary; + } + if (childrenComp.size != Ordering::equal) { + return childrenComp.size; + } + if (childrenComp.prefixFirst != Ordering::equal) { + return childrenComp.prefixFirst; + } + + return compareFunctions(lhs->getFunction(), rhs->getFunction(), options); +} + +Ordering compareExpressions(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions &options) { + + auto lhsOper = cast(lhs->getFunction()); + auto rhsOper = cast(rhs->getFunction()); + + ChildrenComparatorResult childrenComp = compareChildren(lhs->getChildren(), rhs->getChildren(), options); + + if (childrenComp.prefixVariables != Ordering::equal) { + return childrenComp.prefixVariables; + } + if (childrenComp.size != Ordering::equal) { + return childrenComp.size; + } + if (childrenComp.postfix != Ordering::equal) { + return childrenComp.postfix; + } + + if (is(lhs->getFunction()) && is(rhs->getFunction())) { + if (childrenComp.prefixLast != Ordering::equal) { + return childrenComp.prefixLast; + } + } + else { + if (childrenComp.prefixFirst != Ordering::equal) { + return childrenComp.prefixFirst; + } + } + + return compareFunctions(lhs->getFunction(), rhs->getFunction(), options); +} + +Ordering comparePolynomAndNonPolynom(const std::shared_ptr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options) { + + ChildrenComparatorResult childrenComp = compareChildren(lhs->getChildren(), {rhs}, options); + + if (childrenComp.postfix != Ordering::equal) { + return childrenComp.postfix; + } + + return childrenComp.prefixFirst; +} + +Ordering compareExpressionAndNonExpression(const std::shared_ptr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options) { + + if (!is(rhs)) { + return !options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + + if (auto res = compareVariables(lhs, rhs, options); res != Ordering::equal) { + return res; + } + + if (auto lhsOper = cast(lhs->getFunction())) { + auto lhsOperPriority = lhsOper->getOperatorPriority(); + + switch (lhsOperPriority) { + case IOperator::Priority::PostfixUnary: + case IOperator::Priority::PrefixUnary: { + if (Ordering res = compare(lhs->getChildren().front(), rhs); res != Ordering::equal) { + return res; + } + + return Ordering::less; + } + case IOperator::Priority::Exponentiation: + case IOperator::Priority::Multiplication: { + ArgumentPtr rhsExpr = makeExpr(*lhsOper, rhs, Integer(1).clone()); + Ordering res = compare(lhs, rhsExpr); + return options.constantOrderInversed ? reverse(res) : res; + } + default: { + break; + } + } + } + + return !options.constantOrderInversed ? Ordering::greater : Ordering::less; +} + +Ordering compareFunctions(const std::shared_ptr &lhs, + const std::shared_ptr &rhs, + const ComparatorOptions & /*options*/) { + + if (is(lhs) && !is(rhs)) { + return Ordering::greater; + } + if (!is(lhs) && is(rhs)) { + return Ordering::less; + } + + if (*lhs == *rhs) { + return Ordering::equal; + } + + if (lhs->getFunctionOrder() != rhs->getFunctionOrder()) { + return lhs->getFunctionOrder() < rhs->getFunctionOrder() ? Ordering::greater : Ordering::less; + } + + return lhs->toString() < rhs->toString() ? Ordering::greater : Ordering::less; +} + +Ordering compareVariables(const ArgumentPtr &lhs, + const ArgumentPtr &rhs, + const ComparatorOptions &options) { + + ExpressionTreePathStack lhsPath; + ExpressionTreePathStack rhsPath; + + std::shared_ptr lhsVar; + std::shared_ptr rhsVar; + + if (const auto &expr = cast(lhs)) { + lhsPath.emplace(expr, -1); + lhsVar = popNextVariable(lhsPath); + } + else if (const auto &var = cast(lhs)) { + lhsVar = var; + } + + if (const auto &expr = cast(rhs)) { + rhsPath.emplace(expr, -1); + rhsVar = popNextVariable(rhsPath); + } + else if (const auto &var = cast(rhs)) { + rhsVar = var; + } + + if (lhsVar && !rhsVar) { + return !options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + + if (!lhsVar && rhsVar) { + return options.constantOrderInversed ? Ordering::greater : Ordering::less; + } + + while (lhsVar && rhsVar) { + if (Ordering res = compareNonExpressions(lhsVar, rhsVar, options); res != Ordering::equal) { + return res; + } + + lhsVar = popNextVariable(lhsPath); + rhsVar = popNextVariable(rhsPath); + } + + return Ordering::equal; +} + +ChildrenComparatorResult compareChildren(const ArgumentPtrVector &lhsChildren, + const ArgumentPtrVector &rhsChildren, + const ComparatorOptions &options) { + + ChildrenComparatorResult result = {}; + + size_t lhsStart = getPositionOfFirstChildWithVariable(lhsChildren); + size_t rhsStart = getPositionOfFirstChildWithVariable(rhsChildren); + + for (size_t i = lhsStart, j = rhsStart; i < lhsChildren.size() && j < rhsChildren.size(); i++, j++) { + ArgumentPtr compLhs = lhsChildren[i]; + ArgumentPtr compRhs = rhsChildren[j]; + + bool isLhsUnary = unwrapUnaryExpression(compLhs); + bool isRhsUnary = unwrapUnaryExpression(compRhs); + + if (isLhsUnary && isRhsUnary) { + compLhs = lhsChildren[i]; + compRhs = rhsChildren[j]; + } + + if (result.postfixUnary == Ordering::equal && isLhsUnary != isRhsUnary) { + result.postfixUnary = !isLhsUnary ? Ordering::greater : Ordering::less; + } + + if (result.postfix == Ordering::equal) { + result.postfix = compare(compLhs, compRhs, options); + } + + if (result.postfix != Ordering::equal) { + break; + } + } + + if (result.postfix == Ordering::equal) { + size_t lhsPostfixSize = lhsChildren.size() - lhsStart; + size_t rhsPostfixSize = rhsChildren.size() - rhsStart; + + if (lhsPostfixSize != rhsPostfixSize) { + result.postfix = lhsPostfixSize > rhsPostfixSize ? Ordering::greater : Ordering::less; + } + } + + if (lhsChildren.size() != rhsChildren.size()) { + result.postfixUnary = Ordering::equal; + } + + for (auto end = std::min(std::max(lhsStart, rhsStart), std::min(lhsChildren.size(), rhsChildren.size())); + auto i : std::views::iota(0U, end)) { + + Ordering childrenComp = compare(lhsChildren[i], rhsChildren[i], options); + + if (childrenComp != Ordering::equal) { + result.prefixLast = childrenComp; + } + + if (result.prefixFirst == Ordering::equal) { + result.prefixFirst = childrenComp; + } + + if (result.prefixVariables == Ordering::equal) { + result.prefixVariables = compareVariables(lhsChildren[i], rhsChildren[i], {}); + } + + if (result.prefixVariables != Ordering::equal && result.prefixLast != Ordering::equal) { + break; + } + } + + if (lhsChildren.size() != rhsChildren.size()) { + result.size = lhsChildren.size() > rhsChildren.size() ? Ordering::greater : Ordering::less; + } + + return result; +} + +std::shared_ptr popNextVariable(ExpressionTreePathStack &stack) { + while (!stack.empty()) { + const ArgumentPtrVector &children = stack.top().first->getChildren(); + + // TODO: looks weird + size_t &exprIndex = stack.top().second; + exprIndex++; + + bool hasExprChild = false; + + for (; exprIndex < children.size(); exprIndex++) { + if (const auto &exprChild = cast(children[exprIndex]); exprChild && containsVariable(exprChild)) { + stack.emplace(exprChild, -1); + hasExprChild = true; + break; + } + + if (const auto &varChild = cast(children[exprIndex])) { + return varChild; + } + } + + if (hasExprChild) { + continue; + } + + stack.pop(); + } + + return {}; +} + +size_t getPositionOfFirstChildWithVariable(const ArgumentPtrVector &children) { + for (auto i : std::views::iota(0U, children.size())) { + auto lhsChildExpr = cast(children[i]); + + if (is(children[i]) || + (lhsChildExpr && containsVariable(lhsChildExpr))) { + + return i; + } + } + + return children.size(); +} + +bool unwrapUnaryExpression(ArgumentPtr &arg) { + if (const auto expr = cast(arg); + expr && + expr->getFunction()->getFunctionType() == IFunction::Type::Unary) { + + arg = expr->getChildren().front(); + return true; + } + + return false; +} + +bool unwrapEmptyExpression(ArgumentPtr &arg) { + if (const auto expr = cast(arg); + expr && + !expr->getFunction()) { + + arg = expr->getChildren().front(); + return true; + } + + return false; +} + +Ordering reverse(Ordering ordering) { + return 0 <=> ordering; +} + +} diff --git a/src/fintamath/expressions/ExpressionUtils.cpp b/src/fintamath/expressions/ExpressionUtils.cpp index 2e8adb5d4..73a33bf06 100644 --- a/src/fintamath/expressions/ExpressionUtils.cpp +++ b/src/fintamath/expressions/ExpressionUtils.cpp @@ -208,7 +208,7 @@ std::pair splitRational(const ArgumentPtr &arg) { return {arg, Integer(1).clone()}; } -ArgumentPtr makePolynom(const IFunction &func, const ArgumentPtrVector &args) { +ArgumentPtr makePolynom(const IFunction &func, ArgumentPtrVector &&args) { if (args.empty()) { return {}; } @@ -217,7 +217,11 @@ ArgumentPtr makePolynom(const IFunction &func, const ArgumentPtrVector &args) { return args.front(); } - return makeExpr(func, args); + return makeExpr(func, std::move(args)); +} + +ArgumentPtr makePolynom(const IFunction &func, const ArgumentPtrVector &args) { + return makePolynom(func, ArgumentPtrVector(args)); } ArgumentPtrVector getPolynomChildren(const IFunction &func, const ArgumentPtr &arg) { diff --git a/src/fintamath/expressions/binary/CompExpression.cpp b/src/fintamath/expressions/binary/CompExpression.cpp index 05f7aa33d..671c329fc 100644 --- a/src/fintamath/expressions/binary/CompExpression.cpp +++ b/src/fintamath/expressions/binary/CompExpression.cpp @@ -173,34 +173,34 @@ ArgumentPtr CompExpression::rateSimplify(const IFunction &func, const ArgumentPt return {}; } - ArgumentPtrVector dividendPolynom; - std::shared_ptr polynomFirstChildExpr; + ArgumentPtrVector children; + std::shared_ptr firstChildMulExpr; if (is(lhsExpr->getFunction())) { - polynomFirstChildExpr = cast(lhsExpr->getChildren().front()); - dividendPolynom = lhsExpr->getChildren(); + children = lhsExpr->getChildren(); + firstChildMulExpr = cast(lhsExpr->getChildren().front()); } else { - polynomFirstChildExpr = lhsExpr; - dividendPolynom.emplace_back(lhsExpr); + children.emplace_back(lhsExpr); + firstChildMulExpr = lhsExpr; } - auto [rate, value] = splitMulExpr(polynomFirstChildExpr); + auto [rate, value] = splitMulExpr(firstChildMulExpr); if (*rate == Integer(1) || containsInfinity(rate)) { return {}; } - for (auto &child : std::views::drop(dividendPolynom, 1)) { + for (auto &child : std::views::drop(children, 1)) { child = divExpr(child, rate); } { - ArgumentPtrVector newChildren = polynomFirstChildExpr->getChildren(); + ArgumentPtrVector newChildren = firstChildMulExpr->getChildren(); newChildren.erase(newChildren.begin()); - dividendPolynom.front() = makePolynom(Mul(), newChildren); + children.front() = makePolynom(Mul(), std::move(newChildren)); } - ArgumentPtr newLhs = dividendPolynom.size() > 1 ? addExpr(std::move(dividendPolynom)) : dividendPolynom.front(); + ArgumentPtr newLhs = makePolynom(Add(), std::move(children)); simplifyChild(newLhs); approximateSimplifyChild(rate); diff --git a/src/fintamath/expressions/binary/DivExpression.cpp b/src/fintamath/expressions/binary/DivExpression.cpp index 3a2c85281..63982223b 100644 --- a/src/fintamath/expressions/binary/DivExpression.cpp +++ b/src/fintamath/expressions/binary/DivExpression.cpp @@ -2,6 +2,7 @@ #include +#include "fintamath/expressions/ExpressionComparator.hpp" #include "fintamath/expressions/ExpressionUtils.hpp" #include "fintamath/functions/arithmetic/Add.hpp" #include "fintamath/functions/arithmetic/Div.hpp" @@ -142,9 +143,10 @@ ArgumentPtr DivExpression::divSimplify(const IFunction & /*func*/, const Argumen return {}; } - ArgumentPtr numerator = makePolynom(Mul(), numeratorChildren); - ArgumentPtr denominator = makePolynom(Mul(), denominatorChildren); + ArgumentPtr numerator = makePolynom(Mul(), std::move(numeratorChildren)); + ArgumentPtr denominator = makePolynom(Mul(), std::move(denominatorChildren)); ArgumentPtr res = divExpr(numerator, denominator); + return res; } @@ -207,25 +209,13 @@ ArgumentPtr DivExpression::mulSimplify(const SimplifyFunctionVector &simplFuncs, } } - ArgumentPtr numerator; - if (lhsChildren.size() > 1) { - numerator = mulExpr(lhsChildren); - } - else { - numerator = lhsChildren.front(); - } + ArgumentPtr numerator = makePolynom(Mul(), lhsChildren); if (rhsChildren.empty()) { return numerator; } - ArgumentPtr denominator; - if (rhsChildren.size() > 1) { - denominator = mulExpr(rhsChildren); - } - else { - denominator = rhsChildren.front(); - } + ArgumentPtr denominator = makePolynom(Mul(), rhsChildren); if (lhsChildren.size() != lhsChildrenSizeInitial || rhsChildren.size() != rhsChildrenSizeInitial) { ArgumentPtr res = divExpr(numerator, denominator); @@ -304,7 +294,7 @@ std::pair DivExpression::sumSumSimplify(const Argument return {}; } - ArgumentPtr result = resultVect.size() > 1 ? addExpr(std::move(resultVect)) : resultVect.front(); + ArgumentPtr result = makePolynom(Add(), std::move(resultVect)); ArgumentPtr remainderAdd = addExpr(std::move(remainderVect)); ArgumentPtr remainder = divExpr(remainderAdd, rhs); simplifyChild(remainder); @@ -351,11 +341,11 @@ std::pair DivExpression::sumMulSimplify(const Argument return {}; } - ArgumentPtr result = resultChildren.size() > 1 ? addExpr(std::move(resultChildren)) : resultChildren.front(); + ArgumentPtr result = makePolynom(Add(), std::move(resultChildren)); ArgumentPtr remainder; if (!remainderChildren.empty()) { - ArgumentPtr remainderAdd = remainderChildren.size() > 1 ? addExpr(std::move(remainderChildren)) : remainderChildren.front(); + ArgumentPtr remainderAdd = makePolynom(Add(), std::move(remainderChildren)); remainder = divExpr(remainderAdd, rhs); simplifyChild(remainder); } @@ -397,10 +387,15 @@ std::pair DivExpression::mulSumSimplify(const Argument multiplicators.emplace_back(mulExpr(rhsChildren[i], result)); } - ArgumentPtr remainderAdd = multiplicators.size() > 1 ? addExpr(std::move(multiplicators)) : multiplicators.front(); - ArgumentPtr remainderNegAdd = negExpr(remainderAdd); - simplifyChild(remainderNegAdd); - ArgumentPtr remainder = divExpr(remainderNegAdd, rhs); + ArgumentPtr remainderAdd = negExpr(makePolynom(Add(), std::move(multiplicators))); + simplifyChild(remainderAdd); + + ArgumentPtr remainderAddFirstChild = getPolynomChildren(Add(), remainderAdd).front(); + if (compare(lhs, remainderAddFirstChild) != std::strong_ordering::greater) { + return {}; + } + + ArgumentPtr remainder = divExpr(remainderAdd, rhs); return {result, remainder}; } @@ -519,8 +514,8 @@ ArgumentPtr DivExpression::nestedNumeratorRationalSimplify(const ArgumentPtrVect if (!denominatorChildren.empty()) { denominatorChildren.emplace_back(rhs); - ArgumentPtr numerator = makePolynom(Mul(), numeratorChildren); - ArgumentPtr denominator = makePolynom(Mul(), denominatorChildren); + ArgumentPtr numerator = makePolynom(Mul(), std::move(numeratorChildren)); + ArgumentPtr denominator = makePolynom(Mul(), std::move(denominatorChildren)); return divExpr(numerator, denominator); } diff --git a/src/fintamath/expressions/binary/DivExpression.hpp b/src/fintamath/expressions/binary/DivExpression.hpp index 123b8458c..e3b5b0644 100644 --- a/src/fintamath/expressions/binary/DivExpression.hpp +++ b/src/fintamath/expressions/binary/DivExpression.hpp @@ -60,6 +60,8 @@ class DivExpression : public IBinaryExpressionCRTP { static std::pair mulSumSimplify(const ArgumentPtr &lhs, const ArgumentPtr &rhs); + static std::pair mulSumSimplifyImpl(const ArgumentPtr &lhs, const ArgumentPtr &rhs); + static ArgumentPtr nestedNumeratorRationalSimplify(const ArgumentPtrVector &lhsChildren, const ArgumentPtr &rhs); static Integer getGcd(const ArgumentPtrVector &children); diff --git a/src/fintamath/expressions/interfaces/IPolynomExpression.cpp b/src/fintamath/expressions/interfaces/IPolynomExpression.cpp index 38a9ea6d7..b1b757e72 100644 --- a/src/fintamath/expressions/interfaces/IPolynomExpression.cpp +++ b/src/fintamath/expressions/interfaces/IPolynomExpression.cpp @@ -1,6 +1,7 @@ #include "fintamath/expressions/interfaces/IPolynomExpression.hpp" #include "fintamath/core/IComparable.hpp" +#include "fintamath/expressions/ExpressionComparator.hpp" #include "fintamath/expressions/ExpressionUtils.hpp" #include "fintamath/expressions/binary/CompExpression.hpp" #include "fintamath/functions/FunctionArguments.hpp" @@ -190,7 +191,15 @@ std::string IPolynomExpression::childToString(const IOperator &oper, const Argum return prevChild ? (putInSpaces(func->toString()) + childStr) : childStr; } -bool IPolynomExpression::isTermsOrderInversed() const { +std::strong_ordering IPolynomExpression::compare(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const { + ComparatorOptions options = { + .constantOrderInversed = isConstantOrderInversed(), + .comparableOrderInversed = isComparableOrderInversed(), + }; + return fintamath::compare(lhs, rhs, options); +} + +bool IPolynomExpression::isConstantOrderInversed() const { return false; } @@ -208,373 +217,8 @@ void IPolynomExpression::setChildren(const ArgumentPtrVector &childVect) { void IPolynomExpression::sort() { std::ranges::stable_sort(children, [this](const ArgumentPtr &lhs, const ArgumentPtr &rhs) { - return comparator(lhs, rhs) < 0; + return compare(lhs, rhs) == std::strong_ordering::greater; }); } -int IPolynomExpression::comparator(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const { - auto lhsExpr = cast(lhs); - auto rhsExpr = cast(rhs); - - if (!lhsExpr && !rhsExpr) { - return comparatorNonExpressions(lhs, rhs); - } - - auto lhsPolynom = cast(lhsExpr); - auto rhsPolynom = cast(rhsExpr); - - if (lhsPolynom && rhsPolynom) { - return comparatorPolynoms(lhsPolynom, rhsPolynom); - } - - if (lhsExpr && !rhsExpr) { - return comparatorExpressionAndNonExpression(lhsExpr, rhs); - } - - if (!lhsExpr && rhsExpr) { - return comparatorExpressionAndNonExpression(rhsExpr, lhs) * -1; - } - - if (lhsPolynom && !rhsPolynom) { - return comparatorPolynomAndNonPolynom(lhsPolynom, rhs); - } - - if (!lhsPolynom && rhsPolynom) { - return comparatorPolynomAndNonPolynom(rhsPolynom, lhs) * -1; - } - - return comparatorExpressions(lhsExpr, rhsExpr); -} - -int IPolynomExpression::comparatorNonExpressions(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const { - if (is(lhs) && !is(rhs)) { - return !isTermsOrderInversed() ? -1 : 1; - } - if (!is(lhs) && is(rhs)) { - return isTermsOrderInversed() ? -1 : 1; - } - - if (is(lhs) && !is(rhs)) { - return !isTermsOrderInversed() ? -1 : 1; - } - if (!is(lhs) && is(rhs)) { - return isTermsOrderInversed() ? -1 : 1; - } - - if (*lhs == *rhs) { - return 0; - } - - if (auto lhsComp = cast(lhs)) { - if (auto rhsComp = cast(rhs)) { - if (isComparableOrderInversed()) { - return *lhsComp < *rhsComp ? -1 : 1; - } - - return *lhsComp > *rhsComp ? -1 : 1; - } - } - - return lhs->toString() < rhs->toString() ? -1 : 1; -} - -int IPolynomExpression::comparatorPolynoms(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) const { - - ChildrenComparatorResult childrenComp = comparatorChildren(lhs->getChildren(), rhs->getChildren()); - - if (childrenComp.postfix != 0) { - return childrenComp.postfix; - } - if (childrenComp.postfixUnary != 0) { - return childrenComp.postfixUnary; - } - if (childrenComp.size != 0) { - return childrenComp.size; - } - if (childrenComp.prefixFirst != 0) { - return childrenComp.prefixFirst; - } - - return comparatorFunctions(lhs->getFunction(), rhs->getFunction()); -} - -int IPolynomExpression::comparatorPolynomAndNonPolynom(const std::shared_ptr &lhs, - const ArgumentPtr &rhs) const { - - ChildrenComparatorResult childrenComp = comparatorChildren(lhs->getChildren(), {rhs}); - int res = -1; - - if (childrenComp.postfix != 0) { - res = childrenComp.postfix; - } - else if (childrenComp.prefixFirst != 0) { - res = childrenComp.prefixFirst; - } - - return res * (isTermsOrderInversed() ? -1 : 1); -} - -int IPolynomExpression::comparatorExpressionAndNonExpression(const std::shared_ptr &lhs, - const ArgumentPtr &rhs) const { - - 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: { - ArgumentPtr rhsExpr = makeExpr(*lhsOper, rhs, Integer(1).clone()); - return comparator(lhs, rhsExpr) * (isTermsOrderInversed() ? -1 : 1); - } - 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()); - - ChildrenComparatorResult childrenComp = comparatorChildren(lhs->getChildren(), rhs->getChildren()); - - if (childrenComp.prefixVariables != 0) { - return childrenComp.prefixVariables; - } - if (childrenComp.size != 0) { - return childrenComp.size; - } - if (childrenComp.postfix != 0) { - return childrenComp.postfix; - } - - if (is(lhs->getFunction()) && is(rhs->getFunction())) { - if (childrenComp.prefixLast != 0) { - return childrenComp.prefixLast; - } - } - else { - if (childrenComp.prefixFirst != 0) { - return childrenComp.prefixFirst; - } - } - - return comparatorFunctions(lhs->getFunction(), rhs->getFunction()); -} - -IPolynomExpression::ChildrenComparatorResult -IPolynomExpression::comparatorChildren(const ArgumentPtrVector &lhsChildren, - const ArgumentPtrVector &rhsChildren) const { - - ChildrenComparatorResult result = {}; - - size_t lhsStart = getPositionOfFirstChildWithVariable(lhsChildren); - size_t rhsStart = getPositionOfFirstChildWithVariable(rhsChildren); - - for (size_t i = lhsStart, j = rhsStart; i < lhsChildren.size() && j < rhsChildren.size(); i++, j++) { - ArgumentPtr compLhs = lhsChildren[i]; - ArgumentPtr compRhs = rhsChildren[j]; - - bool isLhsUnary = unwrapUnary(compLhs); - bool isRhsUnary = unwrapUnary(compRhs); - - if (isLhsUnary && isRhsUnary) { - compLhs = lhsChildren[i]; - compRhs = rhsChildren[j]; - } - - if (result.postfixUnary == 0 && isLhsUnary != isRhsUnary) { - result.postfixUnary = !isLhsUnary ? -1 : 1; - } - - if (result.postfix == 0) { - result.postfix = comparator(compLhs, compRhs); - } - - if (result.postfix != 0) { - break; - } - } - - if (result.postfix == 0) { - size_t lhsPostfixSize = lhsChildren.size() - lhsStart; - size_t rhsPostfixSize = rhsChildren.size() - rhsStart; - - if (lhsPostfixSize != rhsPostfixSize) { - result.postfix = lhsPostfixSize > rhsPostfixSize ? -1 : 1; - } - } - - if (lhsChildren.size() != rhsChildren.size()) { - result.postfixUnary = 0; - } - - for (auto end = std::min(std::max(lhsStart, rhsStart), std::min(lhsChildren.size(), rhsChildren.size())); - auto i : std::views::iota(0U, end)) { - - int childrenComp = comparator(lhsChildren[i], rhsChildren[i]); - - if (childrenComp != 0) { - result.prefixLast = childrenComp; - } - - if (result.prefixFirst == 0) { - result.prefixFirst = childrenComp; - } - - if (result.prefixVariables == 0) { - result.prefixVariables = comparatorVariables(lhsChildren[i], rhsChildren[i], false); - } - - if (result.prefixVariables != 0 && result.prefixLast != 0) { - break; - } - } - - if (lhsChildren.size() != rhsChildren.size()) { - result.size = lhsChildren.size() > rhsChildren.size() ? -1 : 1; - } - - return result; -} - -int IPolynomExpression::comparatorFunctions(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) { - - if (is(lhs) && !is(rhs)) { - return -1; - } - if (!is(lhs) && is(rhs)) { - return 1; - } - - if (*lhs == *rhs) { - return 0; - } - - return lhs->toString() < rhs->toString() ? -1 : 1; -} - -int IPolynomExpression::comparatorVariables(const ArgumentPtr &lhs, const ArgumentPtr &rhs, bool isTermsOrderInversed) const { - ExpressionTreePathStack lhsPath; - ExpressionTreePathStack rhsPath; - - std::shared_ptr lhsVar; - std::shared_ptr rhsVar; - - if (const auto &expr = cast(lhs)) { - lhsPath.emplace(expr, -1); - lhsVar = getNextVariable(lhsPath); - } - else if (const auto &var = cast(lhs)) { - lhsVar = var; - } - - if (const auto &expr = cast(rhs)) { - rhsPath.emplace(expr, -1); - rhsVar = getNextVariable(rhsPath); - } - else if (const auto &var = cast(rhs)) { - rhsVar = var; - } - - if (lhsVar && !rhsVar) { - return !isTermsOrderInversed ? -1 : 1; - } - - if (!lhsVar && rhsVar) { - return isTermsOrderInversed ? -1 : 1; - } - - while (lhsVar && rhsVar) { - if (int res = comparatorNonExpressions(lhsVar, rhsVar); res != 0) { - return res; - } - - lhsVar = getNextVariable(lhsPath); - rhsVar = getNextVariable(rhsPath); - } - - return 0; -} - -std::shared_ptr IPolynomExpression::getNextVariable(ExpressionTreePathStack &stack) { - while (!stack.empty()) { - const ArgumentPtrVector &children = stack.top().first->getChildren(); - - // TODO: looks weird - size_t &exprIndex = stack.top().second; - exprIndex++; - - bool hasExprChild = false; - - for (; exprIndex < children.size(); exprIndex++) { - if (const auto &exprChild = cast(children[exprIndex]); exprChild && containsVariable(exprChild)) { - stack.emplace(exprChild, -1); - hasExprChild = true; - break; - } - - if (const auto &varChild = cast(children[exprIndex])) { - return varChild; - } - } - - if (hasExprChild) { - continue; - } - - stack.pop(); - } - - return {}; -} - -size_t IPolynomExpression::getPositionOfFirstChildWithVariable(const ArgumentPtrVector &children) { - for (auto i : std::views::iota(0U, children.size())) { - auto lhsChildExpr = cast(children[i]); - - if (is(children[i]) || - (lhsChildExpr && containsVariable(lhsChildExpr))) { - - return i; - } - } - - return children.size(); -} - -bool IPolynomExpression::unwrapUnary(ArgumentPtr &lhs) { - if (const auto lhsExpr = cast(lhs); - lhsExpr && lhsExpr->getFunction()->getFunctionType() == IFunction::Type::Unary) { - - lhs = lhsExpr->getChildren().front(); - return true; - } - - return false; -} - } diff --git a/src/fintamath/expressions/polynomial/AddExpression.cpp b/src/fintamath/expressions/polynomial/AddExpression.cpp index e7bb90a5c..4feadb75b 100644 --- a/src/fintamath/expressions/polynomial/AddExpression.cpp +++ b/src/fintamath/expressions/polynomial/AddExpression.cpp @@ -53,23 +53,25 @@ std::string AddExpression::childToString(const IOperator &oper, const ArgumentPt return result; } -int AddExpression::comparator(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const { +std::strong_ordering AddExpression::compare(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const { auto lhsExpr = cast(lhs); auto rhsExpr = cast(rhs); if (lhsExpr && is
(lhsExpr->getFunction()) && (!rhsExpr || !is
(rhsExpr->getFunction()))) { - return 1; + + return std::strong_ordering::less; } if (rhsExpr && is
(rhsExpr->getFunction()) && (!lhsExpr || !is
(lhsExpr->getFunction()))) { - return -1; + + return std::strong_ordering::greater; } - return IPolynomExpression::comparator(lhs, rhs); + return IPolynomExpression::compare(lhs, rhs); } AddExpression::SimplifyFunctionVector AddExpression::getFunctionsForPreSimplify() const { diff --git a/src/fintamath/expressions/polynomial/AddExpression.hpp b/src/fintamath/expressions/polynomial/AddExpression.hpp index 73a2c1630..c233559c9 100644 --- a/src/fintamath/expressions/polynomial/AddExpression.hpp +++ b/src/fintamath/expressions/polynomial/AddExpression.hpp @@ -28,7 +28,7 @@ class AddExpression : public IPolynomExpressionCRTP { * @return 1 if we should not swap the arguments * @return 0 if this comparator fails */ - int comparator(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const override; + std::strong_ordering compare(const ArgumentPtr &lhs, const ArgumentPtr &rhs) const override; private: static ArgumentPtr constSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs); diff --git a/src/fintamath/expressions/polynomial/MulExpression.cpp b/src/fintamath/expressions/polynomial/MulExpression.cpp index 32a7b59db..54957eca5 100644 --- a/src/fintamath/expressions/polynomial/MulExpression.cpp +++ b/src/fintamath/expressions/polynomial/MulExpression.cpp @@ -41,7 +41,7 @@ std::string MulExpression::toString() const { numeratorChildren.front() = childNumerator; } - ArgumentPtr numerator = makePolynom(Mul(), numeratorChildren); + ArgumentPtr numerator = makePolynom(Mul(), std::move(numeratorChildren)); ArgumentPtr denominator = childDenominator; ArgumentPtr res = divExpr(numerator, denominator); @@ -95,7 +95,7 @@ MulExpression::SimplifyFunctionVector MulExpression::getFunctionsForPostSimplify return simplifyFunctions; } -bool MulExpression::isTermsOrderInversed() const { +bool MulExpression::isConstantOrderInversed() const { return true; } @@ -279,8 +279,8 @@ ArgumentPtr MulExpression::trigDoubleAngleSimplify(const IFunction & /*func*/, c if (!lhsExpr || !rhsExpr || - !is(lhsExpr->getFunction()) || - !is(rhsExpr->getFunction())) { + !is(lhsExpr->getFunction()) || + !is(rhsExpr->getFunction())) { return {}; } diff --git a/src/fintamath/expressions/polynomial/MulExpression.hpp b/src/fintamath/expressions/polynomial/MulExpression.hpp index a64a4f78e..7756dde0f 100644 --- a/src/fintamath/expressions/polynomial/MulExpression.hpp +++ b/src/fintamath/expressions/polynomial/MulExpression.hpp @@ -20,7 +20,7 @@ class MulExpression : public IPolynomExpressionCRTP { SimplifyFunctionVector getFunctionsForPostSimplify() const override; - bool isTermsOrderInversed() const override; + bool isConstantOrderInversed() const override; private: static ArgumentPtr constSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs); diff --git a/tests/src/expressions/ExpressionComparatorTests.cpp b/tests/src/expressions/ExpressionComparatorTests.cpp new file mode 100644 index 000000000..cb3be245a --- /dev/null +++ b/tests/src/expressions/ExpressionComparatorTests.cpp @@ -0,0 +1,18 @@ +#include + +#include "fintamath/expressions/Expression.hpp" +#include "fintamath/expressions/ExpressionComparator.hpp" + +using namespace fintamath; + +TEST(ExpressionComparatorTests, comparatorTest) { + EXPECT_EQ(compare(Variable("a").clone(), Expression("a").clone()), std::strong_ordering::equal); + EXPECT_EQ(compare(Expression("a").clone(), Expression("a").clone()), std::strong_ordering::equal); + EXPECT_EQ(compare(Integer(2).clone(), Expression("2 a").clone()), std::strong_ordering::less); + EXPECT_EQ(compare(Integer(2).clone(), Expression("2 a").clone()), std::strong_ordering::less); + EXPECT_EQ(compare(Variable("a").clone(), Expression("a b").clone()), std::strong_ordering::less); + EXPECT_EQ(compare(Variable("a").clone(), Expression("a + b").clone()), std::strong_ordering::less); + EXPECT_EQ(compare(Expression("a^b").clone(), Expression("a b c").clone()), std::strong_ordering::greater); + + // TODO: add more tests +} diff --git a/tests/src/expressions/ExpressionTests.cpp b/tests/src/expressions/ExpressionTests.cpp index 24296b767..bb0d0608a 100644 --- a/tests/src/expressions/ExpressionTests.cpp +++ b/tests/src/expressions/ExpressionTests.cpp @@ -159,6 +159,7 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("E").toString(), "E"); EXPECT_EQ(Expression("Pi").toString(), "Pi"); EXPECT_EQ(Expression("E^101-E^101").toString(), "0"); + // EXPECT_EQ(Expression("E E Pi E").toString(), "E^2 Pi^2"); // TODO! fix EXPECT_EQ(Expression("ln(E^E) / ln(E^E) - 1").toString(), "0"); EXPECT_EQ(Expression("8E").toString(), "8 E"); EXPECT_EQ(Expression("8Pi").toString(), "8 Pi"); @@ -427,6 +428,8 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("x Pi^4 ln(5) + x E^2 sin(1) sinh(2)").toString(), "(E^2 sinh(2) sin(1) + ln(5) Pi^4) x"); EXPECT_EQ(Expression("(a+b) (-sqrt2 + sqrt3 - sqrt5)").toString(), "(sqrt(3) - sqrt(5) - sqrt(2)) a + (sqrt(3) - sqrt(5) - sqrt(2)) b"); EXPECT_EQ(Expression("(sqrt(2) x + sqrt(3) x + Pi^4 x + 1) / (sqrt(2) + sqrt(3) + Pi^4)").toString(), "x + 1/(Pi^4 + sqrt(3) + sqrt(2))"); + EXPECT_EQ(Expression("sqrt(1/(7 + x^2)) + sqrt(x)/(sqrt(x) + 1)").toString(), "(sqrt(x) sqrt(x^2 + 7) + sqrt(x) + 1)/(sqrt(x^2 + 7) + sqrt(x) sqrt(x^2 + 7))"); + EXPECT_EQ(Expression("root(1/(7 + x^2), 3) + sqrt(x)/(sqrt(x) + 1)").toString(), "(sqrt(x) root(x^2 + 7, 3) + sqrt(x) + 1)/(root(x^2 + 7, 3) + sqrt(x) root(x^2 + 7, 3))"); EXPECT_EQ(Expression("log(2, 2)").toString(), "1"); EXPECT_EQ(Expression("log(2, 256)").toString(), "8"); @@ -830,6 +833,7 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("csc(x)*tan(x)").toString(), "sec(x)"); EXPECT_EQ(Expression("sin(x)cos(x)").toString(), "sin(2 x)/2"); + EXPECT_EQ(Expression("sin(x)cos(x)sign(x)").toString(), "(sin(2 x) sign(x))/2"); EXPECT_EQ(Expression("sin(x)^2").toString(), "-cos(2 x)/2 + 1/2"); EXPECT_EQ(Expression("cos(x)^2").toString(), "cos(2 x)/2 + 1/2"); @@ -1443,10 +1447,10 @@ TEST(ExpressionTests, stringConstructorTest) { EXPECT_EQ(Expression("tan(3/2*Pi)").toString(), "ComplexInf"); EXPECT_EQ(Expression("cot(0)").toString(), "ComplexInf"); EXPECT_EQ(Expression("cot(2*Pi)").toString(), "ComplexInf"); - EXPECT_EQ(Expression("sin(Inf)^2 + cos(Inf)^2").toString(), "cos(Inf)^2 + sin(Inf)^2"); - EXPECT_EQ(Expression("sin(Inf) cos(Inf)").toString(), "cos(Inf) sin(Inf)"); + EXPECT_EQ(Expression("sin(Inf)^2 + cos(Inf)^2").toString(), "sin(Inf)^2 + cos(Inf)^2"); + EXPECT_EQ(Expression("sin(Inf) cos(Inf)").toString(), "sin(Inf) cos(Inf)"); EXPECT_EQ(Expression("tan(Inf)").toString(), "tan(Inf)"); - EXPECT_EQ(Expression("tan(Inf) * cot(Inf)").toString(), "cot(Inf) tan(Inf)"); + EXPECT_EQ(Expression("tan(Inf) * cot(Inf)").toString(), "tan(Inf) cot(Inf)"); EXPECT_EQ(Expression("sign(Inf)").toString(), "1"); EXPECT_EQ(Expression("sign(-Inf)").toString(), "-1"); EXPECT_EQ(Expression("abs(Inf)").toString(), "Inf");