Skip to content

Commit

Permalink
Implement logarithms simplify
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Jul 31, 2023
1 parent 514dcb8 commit f6fef5e
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 54 deletions.
11 changes: 11 additions & 0 deletions include/fintamath/functions/logarithms/Log.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

#include "fintamath/functions/IFunction.hpp"
#include "fintamath/numbers/INumber.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"

namespace fintamath {

Expand All @@ -19,6 +22,14 @@ class Log : public IFunctionCRTP<INumber, Log, INumber, INumber> {

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

static std::unique_ptr<IMathObject> multiLogSimpl(const INumber &lhs, const INumber &rhs);

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

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

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

}
30 changes: 11 additions & 19 deletions src/fintamath/expressions/binary/LogExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,43 +50,35 @@ ArgumentPtr LogExpression::preciseSimplify() const {

LogExpression::SimplifyFunctionsVector LogExpression::getFunctionsForPreSimplify() const {
static const LogExpression::SimplifyFunctionsVector simplifyFunctions = {
&LogExpression::numSimplify, //
&LogExpression::equalSimplify, //
&LogExpression::powSimplify, //
&LogExpression::callFuncSimplify, //
&LogExpression::equalSimplify, //
&LogExpression::powSimplify, //
};
return simplifyFunctions;
}

LogExpression::SimplifyFunctionsVector LogExpression::getFunctionsForPostSimplify() const {
static const LogExpression::SimplifyFunctionsVector simplifyFunctions = {
&LogExpression::constSimplify, //
&LogExpression::powSimplify, //
&LogExpression::numSimplify, //
&LogExpression::equalSimplify, //
};
return simplifyFunctions;
}

ArgumentPtr LogExpression::numSimplify(const IFunction & /*func*/, const ArgumentPtr &lhs, const ArgumentPtr &rhs) {
if (*lhs == Integer(1)) {
throw UndefinedFunctionException(Log().toString(), {lhs->toString(), rhs->toString()});
ArgumentPtr LogExpression::callFuncSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs) {
return callFunction(func, {lhs, rhs});
}

ArgumentPtr LogExpression::constSimplify(const IFunction & /*func*/, const ArgumentPtr &lhs, const ArgumentPtr &rhs) {
if (*rhs == Integer(1)) {
return Integer(0).clone();
}

if (*lhs == E()) {
return callFunction(Ln(), {rhs});
}

if (Log().doArgsMatch({*lhs, *rhs})) {
if (*lhs == Integer(2)) {
return callFunction(Lb(), {rhs});
}

if (*lhs == Integer(10)) {
return callFunction(Lg(), {rhs});
}

return callFunction(Log(), {lhs, rhs});
}

return {};
}

Expand Down
4 changes: 3 additions & 1 deletion src/fintamath/expressions/binary/LogExpression.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class LogExpression : public IBinaryExpressionCRTP<LogExpression> {
SimplifyFunctionsVector getFunctionsForPostSimplify() const override;

private:
static ArgumentPtr numSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs);
static ArgumentPtr callFuncSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs);

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

static ArgumentPtr equalSimplify(const IFunction &func, const ArgumentPtr &lhs, const ArgumentPtr &rhs);

Expand Down
123 changes: 122 additions & 1 deletion src/fintamath/functions/logarithms/Log.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,132 @@
#include "fintamath/functions/logarithms/Log.hpp"

#include "fintamath/exceptions/UndefinedException.hpp"
#include "fintamath/functions/arithmetic/Neg.hpp"
#include "fintamath/literals/constants/ComplexInf.hpp"
#include "fintamath/literals/constants/Inf.hpp"
#include "fintamath/literals/constants/NegInf.hpp"
#include "fintamath/literals/constants/Undefined.hpp"
#include "fintamath/numbers/RealFunctions.hpp"

namespace fintamath {

std::unique_ptr<IMathObject> Log::call(const ArgumentsRefVector &argsVect) const {
return log(convert<Real>(argsVect.front().get()), convert<Real>(argsVect.back().get())).toMinimalObject();
const auto &lhs = cast<INumber>(argsVect.front().get());
const auto &rhs = cast<INumber>(argsVect.back().get());

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

return ComplexInf().clone();
}

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

if (lhs == Integer(0)) {
if (rhs == Integer(0)) {
return Undefined().clone();
}

return Integer(0).clone();
}

if (rhs == Integer(0)) {
if (lhs > Integer(1)) {
return NegInf().clone();
}

return Inf().clone();
}

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

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

if (lhs < Integer(1)) {
auto newLhs = Rational(1) / lhs;

if (rhs < Integer(1)) {
auto newRhs = Rational(1) / rhs;
return multiLogSimpl(*newLhs, *newRhs);
}

return Neg()(*multiLogSimpl(*newLhs, rhs));
}

if (rhs < Integer(1)) {
auto newRhs = Rational(1) / rhs;
return Neg()(*multiLogSimpl(lhs, *newRhs));
}

return multiLogSimpl(lhs, rhs);
}

std::unique_ptr<IMathObject> Log::multiLogSimpl(const INumber &lhs, const INumber &rhs) {
static const auto multiLog = [] {
static MultiMethod<std::unique_ptr<IMathObject>(const INumber &, const INumber &)> outMultiPow;

outMultiPow.add<Integer, Integer>([](const Integer &inLhs, const Integer &inRhs) {
return logSimpl(inLhs, inRhs);
});

outMultiPow.add<Rational, Rational>([](const Rational &inLhs, const Rational &inRhs) {
return logSimpl(inLhs, inRhs);
});

outMultiPow.add<Real, Real>([](const Real &inLhs, const Real &inRhs) {
return logSimpl(inLhs, inRhs);
});

return outMultiPow;
}();

if (auto rhsConv = cast<INumber>(convert(lhs, rhs))) {
return multiLog(lhs, *rhsConv);
}

auto lhsConv = cast<INumber>(convert(rhs, lhs));
return multiLog(*lhsConv, rhs);
}

std::unique_ptr<IMathObject> Log::logSimpl(const Integer &lhs, const Integer &rhs) {
return logSimpl(Rational(lhs), Rational(rhs));
}

std::unique_ptr<IMathObject> Log::logSimpl(const Rational &lhs, const Rational &rhs) {
const bool isResInverted = lhs > rhs;
const Rational divider = isResInverted ? rhs : lhs;
Rational remainder = isResInverted ? lhs : rhs;
Rational res = 0;

while (remainder > divider) {
res += 1;
remainder /= divider;
}

if (remainder == divider) {
res += 1;

if (isResInverted) {
return (Rational(1) / res).toMinimalObject();
}

return res.toMinimalObject();
}

return logSimpl(Real(lhs), Real(rhs));
}

std::unique_ptr<IMathObject> Log::logSimpl(const Real &lhs, const Real &rhs) {
return log(lhs, rhs).toMinimalObject();
}

}
6 changes: 3 additions & 3 deletions tests/src/expressions/ExpressionFunctionsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ TEST(ExpressionFunctionsTests, expTest) {
}

TEST(ExpressionFunctionsTests, logTest) {
EXPECT_EQ(log(Expression("a+b"), Expression("1")).toString(), "log(a + b, 1)");
EXPECT_EQ(log(Expression("a+b"), Expression("1")).toString(), "0");
EXPECT_EQ(log(Expression("2*a"), Expression("a+b")).toString(), "log(2 a, a + b)");
EXPECT_EQ(log(Expression("a"), Expression("a^5")).toString(), "5");
}
Expand All @@ -134,8 +134,8 @@ TEST(ExpressionFunctionsTests, lnTest) {
}

TEST(ExpressionFunctionsTests, lbTest) {
EXPECT_EQ(lb(Expression("1024*a")).toString(), "log(2, 1024 a)"); // TODO logarithms
EXPECT_EQ(lb(Expression("2+a")).toString(), "log(2, a + 2)"); // TODO logarithms
EXPECT_EQ(lb(Expression("1024*a")).toString(), "log(2, 1024 a)");
EXPECT_EQ(lb(Expression("2+a")).toString(), "log(2, a + 2)");
}

TEST(ExpressionFunctionsTests, lgTest) {
Expand Down
63 changes: 37 additions & 26 deletions tests/src/expressions/ExpressionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ TEST(ExpressionTests, copyTest) {
}

TEST(ExpressionTests, stringConstructorTest) {
// TODO! split these tests

EXPECT_EQ(Expression("2").toString(), "2");
EXPECT_EQ(Expression("2 + 2").toString(), "4");
EXPECT_EQ(Expression(" 2 + 2 ").toString(), "4");
Expand Down Expand Up @@ -142,22 +144,6 @@ TEST(ExpressionTests, stringConstructorTest) {
EXPECT_EQ(Expression("exp100").toString(), "E^100");
EXPECT_EQ(Expression("E^101").toString(), "E^101");
EXPECT_EQ(Expression("E^(-101)").toString(), "1/(E^101)");
EXPECT_EQ(Expression("log(E,E)").toString(), "1");
EXPECT_EQ(Expression("log(Pi, Pi^10)").toString(), "10");
EXPECT_EQ(Expression("log(E,E^3)").toString(), "3");
EXPECT_EQ(Expression("log((Pi),(E)^((Pi)))").toString(), "Pi log(Pi, E)");
EXPECT_EQ(Expression("log(E^3, E)").toString(), "1/3");
EXPECT_EQ(Expression("log(E^Pi, E)").toString(), "1/Pi");
EXPECT_EQ(Expression("ln3").toString(), "ln(3)");
EXPECT_EQ(Expression("ln2").toString(), "ln(2)");
EXPECT_EQ(Expression("ln100").toString(), "ln(100)");
EXPECT_EQ(Expression("ln(E)").toString(), "1");
EXPECT_EQ(Expression("lg99").toString(), "log(10, 99)");
EXPECT_EQ(Expression("lg100").toString(), "2");
EXPECT_EQ(Expression("lb100").toString(), "log(2, 100)");
EXPECT_EQ(Expression("lb4").toString(), "2");
EXPECT_EQ(Expression("ln(ln(E))").toString(), "0");
EXPECT_EQ(Expression("ln(E)!").toString(), "1");
EXPECT_EQ(Expression("sin10").toString(), "sin(10)");
EXPECT_EQ(Expression("cos10").toString(), "cos(10)");
EXPECT_EQ(Expression("tan10").toString(), "tan(10)");
Expand Down Expand Up @@ -188,7 +174,6 @@ TEST(ExpressionTests, stringConstructorTest) {
EXPECT_EQ(Expression("sqrt((1-cos(2*(Pi/3)))/2)").toString(), "sqrt(-cos((2 Pi)/3)/2 + 1/2)");
EXPECT_EQ(Expression("2*sqrt((1-cos(2*(Pi/3)))/2)*cos(Pi/3)").toString(), "2 sqrt(-cos((2 Pi)/3)/2 + 1/2) cos(Pi/3)");
EXPECT_EQ(Expression("-sin(2)").toString(), "-sin(2)");
EXPECT_EQ(Expression("log(2, 256)").toString(), "8");
EXPECT_EQ(Expression("2^(3/2)").toString(), "2 sqrt(2)");
EXPECT_EQ(Expression("sqrt(sqrt5)").toString(), "root(5, 4)");
EXPECT_EQ(Expression("sqrt4!").toString(), "2");
Expand Down Expand Up @@ -377,6 +362,33 @@ TEST(ExpressionTests, stringConstructorTest) {
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)");

EXPECT_EQ(Expression("log(2, 2)").toString(), "1");
EXPECT_EQ(Expression("log(2, 256)").toString(), "8");
EXPECT_EQ(Expression("log(3, 515377520732011331036461129765621272702107522001)").toString(), "100");
EXPECT_EQ(Expression("log(515377520732011331036461129765621272702107522001, 3)").toString(), "1/100");
EXPECT_EQ(Expression("log(2/3, 4/9)").toString(), "2");
EXPECT_EQ(Expression("log(9/4, 3/2)").toString(), "1/2");
EXPECT_EQ(Expression("log(4/9, 2/3)").toString(), "1/2");
EXPECT_EQ(Expression("log(4/11, 2/3)").toString(), "log(4/11, 2/3)");
EXPECT_EQ(Expression("ln1").toString(), "0");
EXPECT_EQ(Expression("ln3").toString(), "ln(3)");
EXPECT_EQ(Expression("ln2").toString(), "ln(2)");
EXPECT_EQ(Expression("ln100").toString(), "ln(100)");
EXPECT_EQ(Expression("ln(E)").toString(), "1");
EXPECT_EQ(Expression("lg99").toString(), "log(10, 99)");
EXPECT_EQ(Expression("lg100").toString(), "2");
EXPECT_EQ(Expression("lb100").toString(), "log(2, 100)");
EXPECT_EQ(Expression("lb4").toString(), "2");
EXPECT_EQ(Expression("ln(ln(E))").toString(), "0");
EXPECT_EQ(Expression("ln(E)!").toString(), "1");
EXPECT_EQ(Expression("log(0, 1)").toString(), "0");
EXPECT_EQ(Expression("log(E,E)").toString(), "1");
EXPECT_EQ(Expression("log(Pi, Pi^10)").toString(), "10");
EXPECT_EQ(Expression("log(E,E^3)").toString(), "3");
EXPECT_EQ(Expression("log((Pi),(E)^((Pi)))").toString(), "Pi log(Pi, E)");
EXPECT_EQ(Expression("log(E^3, E)").toString(), "1/3");
EXPECT_EQ(Expression("log(E^Pi, E)").toString(), "1/Pi");

// TODO! implement this
// EXPECT_EQ(Expression("sqrt(x^2)").toString(), "abs(x)");
// EXPECT_EQ(Expression("(x^10)^(1/10))").toString(), "abs(x)");
Expand Down Expand Up @@ -722,6 +734,10 @@ TEST(ExpressionTests, stringConstructorTest) {
EXPECT_EQ(Expression("x = -Inf").toString(), "False");
EXPECT_EQ(Expression("Inf = Inf").toString(), "True");
EXPECT_EQ(Expression("Inf = -Inf").toString(), "False");
EXPECT_EQ(Expression("log(1, 0)").toString(), "ComplexInf");
EXPECT_EQ(Expression("log(1, 10)").toString(), "ComplexInf");
EXPECT_EQ(Expression("log(10, 0)").toString(), "-Inf");
EXPECT_EQ(Expression("log(1/10, 0)").toString(), "Inf");

EXPECT_EQ(Expression("0*Inf").toString(), "Undefined");
EXPECT_EQ(Expression("0*-Inf").toString(), "Undefined");
Expand Down Expand Up @@ -770,6 +786,8 @@ TEST(ExpressionTests, stringConstructorTest) {
EXPECT_EQ(Expression("ComplexInf = -Inf").toString(), "Undefined");
EXPECT_EQ(Expression("Inf = ComplexInf").toString(), "Undefined");
EXPECT_EQ(Expression("-Inf = ComplexInf").toString(), "Undefined");
EXPECT_EQ(Expression("log(1, 1)").toString(), "Undefined");
EXPECT_EQ(Expression("log(0, 0)").toString(), "Undefined");

EXPECT_EQ(Expression("Undefined").toString(), "Undefined");
EXPECT_EQ(Expression("-Undefined").toString(), "Undefined");
Expand Down Expand Up @@ -1051,21 +1069,14 @@ TEST(ExpressionTests, stringConstructorNegativeTest) {
EXPECT_THROW(Expression("(-1)!!"), UndefinedException);
EXPECT_THROW(Expression("(2/3)!!"), UndefinedException);
EXPECT_THROW(Expression("sqrt(-1)"), UndefinedException);
EXPECT_THROW(Expression("ln(0)"), UndefinedException);
EXPECT_THROW(Expression("ln(-1)"), UndefinedException);
EXPECT_THROW(Expression("log(-1, 1)"), UndefinedException);
EXPECT_THROW(Expression("log(0, 1)"), UndefinedException);
EXPECT_THROW(Expression("log(1, 0)"), UndefinedException);
EXPECT_THROW(Expression("lb(-1)"), UndefinedException);
EXPECT_THROW(Expression("lg(-1)"), UndefinedException);
EXPECT_THROW(Expression("sqrt(-1)"), UndefinedException);
EXPECT_THROW(Expression("(-1)^(2/3)"), UndefinedException);
EXPECT_THROW(Expression("ln(0)"), UndefinedException);
EXPECT_THROW(Expression("ln(-1)"), UndefinedException);
EXPECT_THROW(Expression("log(-1, 1)"), UndefinedException);
EXPECT_THROW(Expression("log(0, 1)"), UndefinedException);
EXPECT_THROW(Expression("log(1, 0)"), UndefinedException);
EXPECT_THROW(Expression("lb(-1)"), UndefinedException);
EXPECT_THROW(Expression("lg(-1)"), UndefinedException);
EXPECT_THROW(Expression("log(-1, 1)"), UndefinedException);
// TODO constants
// EXPECT_THROW(Expression("E!"), UndefinedException);
// EXPECT_THROW(Expression("tan(Pi/2)"), UndefinedException);
Expand Down
Loading

0 comments on commit f6fef5e

Please sign in to comment.