Skip to content

Commit

Permalink
Root simplification
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Jul 4, 2023
1 parent dce5a6e commit d7eb24e
Show file tree
Hide file tree
Showing 19 changed files with 547 additions and 178 deletions.
10 changes: 10 additions & 0 deletions include/fintamath/functions/powers/Pow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

namespace fintamath {

class Integer;
class Rational;
class Real;

class Pow : public IOperatorCRTP<INumber, Pow, INumber, INumber> {
public:
Pow() : IOperatorCRTP(IOperator::Priority::Exponentiation, false) {
Expand All @@ -20,6 +24,12 @@ class Pow : public IOperatorCRTP<INumber, Pow, INumber, INumber> {

protected:
std::unique_ptr<IMathObject> call(const ArgumentsRefVector &argsVect) const override;

static std::unique_ptr<IMathObject> powSimpl(const Integer &lhs, const Integer &rhs);

static std::unique_ptr<IMathObject> powSimpl(const Rational &lhs, const Rational &rhs);

static std::unique_ptr<IMathObject> powSimpl(const Real &lhs, const Real &rhs);
};

}
35 changes: 35 additions & 0 deletions include/fintamath/functions/powers/Root.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include "fintamath/functions/IFunction.hpp"
#include "fintamath/numbers/INumber.hpp"

namespace fintamath {

class Integer;
class Rational;
class Real;

class Root : public IFunctionCRTP<INumber, Root, INumber, INumber> {
public:
Root() = default;

std::string toString() const override {
return "root";
}

protected:
std::unique_ptr<IMathObject> call(const ArgumentsRefVector &argsVect) const override;

private:
static std::unique_ptr<IMathObject> rootSimpl(const Integer &lhs, const Integer &rhs);

static std::unique_ptr<IMathObject> rootSimpl(const Rational &lhs, const Integer &rhs);

static std::unique_ptr<IMathObject> rootSimpl(const Real &lhs, const Integer &rhs);

static std::map<Integer, Integer> roots(const Integer &lhs, const Integer &rhs);

static std::unique_ptr<IMathObject> perfectRoot(const Integer &lhs, const Integer &rhs);
};

}
2 changes: 2 additions & 0 deletions include/fintamath/numbers/IntegerFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Integer factorial(const Integer &rhs);

Integer factorial(const Integer &rhs, size_t order);

std::map<Integer, Integer> factors(Integer rhs, Integer limit = -1);

Integer combinations(const Integer &totalNumber, const Integer &choosedNumber);

Integer multinomialCoefficient(const Integer &totalNumber, const std::vector<Integer> &groupNumbers);
Expand Down
9 changes: 9 additions & 0 deletions src/fintamath/config/ExpressionConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "fintamath/functions/other/Percent.hpp"
#include "fintamath/functions/powers/Exp.hpp"
#include "fintamath/functions/powers/Pow.hpp"
#include "fintamath/functions/powers/Root.hpp"
#include "fintamath/functions/powers/Sqrt.hpp"
#include "fintamath/functions/trigonometry/Acos.hpp"
#include "fintamath/functions/trigonometry/Acot.hpp"
Expand Down Expand Up @@ -291,6 +292,14 @@ struct ExpressionConfig {
return makeExpr(Pow(), args.front(), std::make_shared<Rational>(1, 2));
});

Expression::registerFunctionExpressionMaker<Root>([](const ArgumentsPtrVector &args) {
if (const auto num = cast<INumber>(args.back())) {
return makeExpr(Pow(), args.front(), Integer(1) / (*num));
}

return makeExpr(Pow(), args.front(), makeExpr(Div(), std::make_shared<Integer>(1), args.back()));
});

Expression::registerFunctionExpressionMaker<Sin>([](const ArgumentsPtrVector &args) {
return std::make_unique<TrigonometryExpression>(Sin(), args.front());
});
Expand Down
2 changes: 2 additions & 0 deletions src/fintamath/config/ParserConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "fintamath/functions/other/Percent.hpp"
#include "fintamath/functions/powers/Exp.hpp"
#include "fintamath/functions/powers/Pow.hpp"
#include "fintamath/functions/powers/Root.hpp"
#include "fintamath/functions/powers/Sqrt.hpp"
#include "fintamath/functions/trigonometry/Acos.hpp"
#include "fintamath/functions/trigonometry/Acot.hpp"
Expand Down Expand Up @@ -171,6 +172,7 @@ struct ParserConfig {
IFunction::registerType<Lg>();
IFunction::registerType<Exp>();
IFunction::registerType<Sqrt>();
IFunction::registerType<Root>();
IFunction::registerType<Sin>();
IFunction::registerType<Cos>();
IFunction::registerType<Tan>();
Expand Down
13 changes: 7 additions & 6 deletions src/fintamath/expressions/binary/PowExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "fintamath/functions/arithmetic/Mul.hpp"
#include "fintamath/functions/arithmetic/Neg.hpp"
#include "fintamath/functions/powers/Pow.hpp"
#include "fintamath/functions/powers/Root.hpp"
#include "fintamath/functions/powers/Sqrt.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/IntegerFunctions.hpp"
Expand All @@ -23,13 +24,12 @@ std::string PowExpression::toString() const {
const Integer &numerator = val->numerator();
const Integer &denominator = val->denominator();

if (denominator == 2) {
if (numerator == 1) {
if (numerator == 1) {
if (denominator == 2) {
return functionToString(Sqrt(), {lhsChild});
}

PowExpression res(std::make_shared<PowExpression>(lhsChild, Rational(1, 2).clone()), numerator.clone());
return res.IBinaryExpression::toString();
return functionToString(Root(), {lhsChild, denominator.clone()});
}
}

Expand All @@ -45,7 +45,9 @@ std::shared_ptr<IFunction> PowExpression::getOutputFunction() const {
}

ArgumentPtr PowExpression::preciseSimplify() const {
if (*rhsChild == Rational(1, 2)) {
static const int64_t maxPreciseRoot = 9;

if (const auto ratRhsChild = cast<Rational>(rhsChild); ratRhsChild && ratRhsChild->denominator() <= maxPreciseRoot) {
auto preciseExpr = cast<PowExpression>(clone());
preciseSimplifyChild(preciseExpr->lhsChild);
return preciseExpr;
Expand Down Expand Up @@ -236,5 +238,4 @@ ArgumentPtr PowExpression::sumSimplify(const ArgumentPtr &lhs, const ArgumentPtr

return {};
}

}
4 changes: 1 addition & 3 deletions src/fintamath/functions/arithmetic/Abs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ std::unique_ptr<IMathObject> Abs::call(const ArgumentsRefVector &argsVect) const
return outMultiAbs;
}();

const auto &rhs = cast<INumber>(argsVect.front().get());

return multiAbs(rhs);
return multiAbs(cast<INumber>(argsVect.front().get()));
}

}
60 changes: 42 additions & 18 deletions src/fintamath/functions/powers/Pow.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "fintamath/functions/powers/Pow.hpp"

#include "fintamath/functions/powers/Root.hpp"
#include "fintamath/functions/powers/Sqrt.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/IntegerFunctions.hpp"
Expand All @@ -14,30 +15,15 @@ std::unique_ptr<IMathObject> Pow::call(const ArgumentsRefVector &argsVect) const
static MultiMethod<std::unique_ptr<IMathObject>(const INumber &, const INumber &)> outMultiPow;

outMultiPow.add<Integer, Integer>([](const Integer &lhs, const Integer &rhs) {
if (rhs < 0) {
return (Rational(1) / pow(lhs, -rhs)).toMinimalObject();
}

return pow(lhs, rhs).toMinimalObject();
return powSimpl(lhs, rhs);
});

outMultiPow.add<Rational, Rational>([](const Rational &lhs, const Rational &rhs) {
const Integer &numerator = rhs.numerator();
const Integer &denominator = rhs.denominator();

if (denominator == 1) {
return pow(lhs, numerator).toMinimalObject();
}

if (denominator == 2) { // TODO: implement nth root
return Pow()(*Sqrt()(lhs), numerator);
}

return pow(convert<Real>(lhs), convert<Real>(rhs)).toMinimalObject();
return powSimpl(lhs, rhs);
});

outMultiPow.add<Real, Real>([](const Real &lhs, const Real &rhs) {
return pow(lhs, rhs).toMinimalObject();
return powSimpl(lhs, rhs);
});

return outMultiPow;
Expand All @@ -46,6 +32,10 @@ std::unique_ptr<IMathObject> Pow::call(const ArgumentsRefVector &argsVect) const
const auto &lhs = cast<INumber>(argsVect.front().get());
const auto &rhs = cast<INumber>(argsVect.back().get());

if (rhs < Integer(0)) {
return Pow()(*(Rational(1) / lhs), *(-rhs));
}

if (auto rhsConv = cast<INumber>(convert(lhs, rhs))) {
return multiPow(lhs, *rhsConv);
}
Expand All @@ -54,4 +44,38 @@ std::unique_ptr<IMathObject> Pow::call(const ArgumentsRefVector &argsVect) const
return multiPow(*lhsConv, rhs);
}

std::unique_ptr<IMathObject> Pow::powSimpl(const Integer &lhs, const Integer &rhs) {
return pow(lhs, rhs).toMinimalObject();
}

std::unique_ptr<IMathObject> Pow::powSimpl(const Rational &lhs, const Rational &rhs) {
const Integer &lhsNumerator = lhs.numerator();
const Integer &lhsDenominator = lhs.denominator();

const Integer &rhsNumerator = rhs.numerator();
const Integer &rhsDenominator = rhs.denominator();

if (rhsDenominator == 1) {
if (lhsDenominator == 1) {
return pow(lhsNumerator, rhsNumerator).toMinimalObject();
}

return pow(lhs, rhsNumerator).toMinimalObject();
}

if (lhs < Integer(0)) {
throw UndefinedBinaryOperatorException(Pow().toString(), lhs.toString(), rhs.toString());
}

if (lhsDenominator == 1) {
return Root()(*Pow()(lhsNumerator, rhsNumerator), rhsDenominator);
}

return Root()(*Pow()(lhs, rhsNumerator), rhsDenominator);
}

std::unique_ptr<IMathObject> Pow::powSimpl(const Real &lhs, const Real &rhs) {
return pow(lhs, rhs).toMinimalObject();
}

}
145 changes: 145 additions & 0 deletions src/fintamath/functions/powers/Root.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include "fintamath/functions/powers/Root.hpp"

#include "fintamath/functions/arithmetic/Add.hpp"
#include "fintamath/functions/arithmetic/Div.hpp"
#include "fintamath/functions/arithmetic/Mul.hpp"
#include "fintamath/functions/powers/Pow.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/IntegerFunctions.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"
#include "fintamath/numbers/RealFunctions.hpp"

namespace fintamath {

std::unique_ptr<IMathObject> Root::call(const ArgumentsRefVector &argsVect) const {
static const auto multiRoot = [] {
static MultiMethod<std::unique_ptr<IMathObject>(const INumber &, const INumber &)> outMultiRoot;

outMultiRoot.add<Integer, Integer>([](const Integer &lhs, const Integer &rhs) {
return rootSimpl(lhs, rhs);
});

outMultiRoot.add<Rational, Integer>([](const Rational &lhs, const Integer &rhs) {
return rootSimpl(lhs, rhs);
});

outMultiRoot.add<Real, Integer>([](const Real &lhs, const Integer &rhs) {
return rootSimpl(lhs, rhs);
});

return outMultiRoot;
}();

const auto &lhs = cast<INumber>(argsVect.front().get());
const auto &rhs = cast<INumber>(argsVect.back().get());

if (lhs == Integer(1)) {
return lhs.clone();
}

if (const auto *rhsIntPtr = cast<Integer>(&rhs)) {
const auto &rhsInt = *rhsIntPtr;

if (rhsInt > Integer(1)) {
// TODO: cast to Complex, when it is implemented
if (lhs < Integer(0)) {
throw UndefinedFunctionException(toString(), {lhs.toString(), rhs.toString()});
}

return multiRoot(lhs, rhsInt);
}
}

return Pow()(lhs, *(Rational(1) / rhs));
}

std::unique_ptr<IMathObject> Root::rootSimpl(const Integer &lhs, const Integer &rhs) {
if (auto res = perfectRoot(lhs, rhs)) {
return res;
}

ArgumentsPtrVector mulChildren;

std::map<Integer, Integer> rootFactors = roots(lhs, rhs);
auto rootFactorIter = rootFactors.begin();

if (rootFactorIter->first == 1) {
if (rootFactorIter->second != 1) {
mulChildren.emplace_back(rootFactorIter->second.clone());
}

rootFactorIter++;
}

for (; rootFactorIter != rootFactors.end(); rootFactorIter++) {
Integer root = rootFactorIter->first;
Integer factor = rootFactorIter->second;

if (factor != 1) {
mulChildren.emplace_back(makeExpr(Root(), factor, root));
}
}

if (mulChildren.size() == 1) {
return mulChildren.front()->clone();
}

return makeExpr(Mul(), mulChildren);
}

std::map<Integer, Integer> Root::roots(const Integer &lhs, const Integer &rhs) {
static Integer factorLimit = pow(Integer(2), 15);

std::map<Integer, Integer> rootFactors{{1, 1}};
std::map<Integer, Integer> factorRates = factors(lhs, factorLimit);

for (const auto &factorRate : factorRates) {
Rational power(factorRate.second, rhs);
Integer factor = factorRate.first;

if (power.denominator() == 1) {
rootFactors[1] *= pow(factor, power.numerator());
continue;
}

if (power.numerator() > power.denominator()) {
rootFactors[1] *= pow(factor, power.numerator() / power.denominator());
}

factor = pow(factor, power.numerator() % power.denominator());

if (auto rootIter = rootFactors.find(power.denominator()); rootIter != rootFactors.end()) {
rootIter->second *= factor;
}
else {
rootFactors.insert({power.denominator(), factor});
}
}

return rootFactors;
}

std::unique_ptr<IMathObject> Root::perfectRoot(const Integer &lhs, const Integer &rhs) {
if (rhs == 2) { // TODO: implement perfect nth-roots minimization
Integer remainder;
Integer lhsSqrt = sqrt(lhs, remainder);

if (remainder == 0) {
return lhsSqrt.clone();
}
}

return {};
}

std::unique_ptr<IMathObject> Root::rootSimpl(const Rational &lhs, const Integer &rhs) {
return makeExpr(Div(), makeExpr(Root(), lhs.numerator(), rhs), makeExpr(Root(), lhs.denominator(), rhs))
->toMinimalObject();
}

std::unique_ptr<IMathObject> Root::rootSimpl(const Real &lhs, const Integer &rhs) {
return Pow()(lhs, 1 / Rational(rhs));
}

}
Loading

0 comments on commit d7eb24e

Please sign in to comment.