Skip to content

Commit

Permalink
Implement Factorial of Real numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Jul 31, 2023
1 parent 7a66a65 commit 75abc79
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 8 deletions.
17 changes: 17 additions & 0 deletions include/fintamath/functions/other/Factorial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@

namespace fintamath {

class Integer;
class Rational;
class Real;

class Factorial : public IOperatorCRTP<INumber, Factorial, INumber> {
public:
Factorial() : IOperatorCRTP(IOperator::Priority::PostfixUnary) {
}

Factorial(size_t inOrder) : Factorial() {
setOrder(inOrder);
}

std::string toString() const override {
return std::string(order, '!');
}
Expand All @@ -28,6 +36,15 @@ class Factorial : public IOperatorCRTP<INumber, Factorial, INumber> {
protected:
std::unique_ptr<IMathObject> call(const ArgumentsRefVector &argsVect) const override;

private:
static std::unique_ptr<IMathObject> multiFactorialSimpl(const INumber &lhs, size_t order);

static std::unique_ptr<IMathObject> factorialSimpl(const Integer &rhs, size_t order);

static std::unique_ptr<IMathObject> factorialSimpl(const Rational &rhs, size_t order);

static std::unique_ptr<IMathObject> factorialSimpl(const Real &rhs, size_t order);

private:
size_t order = 1;
};
Expand Down
4 changes: 4 additions & 0 deletions include/fintamath/numbers/Integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class Integer : public IIntegerCRTP<Integer> {

explicit Integer(std::string str);

template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
explicit Integer(T val) : backend(val) {
}

Integer(int64_t val);

std::string toString() const override;
Expand Down
2 changes: 2 additions & 0 deletions include/fintamath/numbers/RealFunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ Real getE();

Real getPi();

Real tgamma(const Real &rhs);

}
54 changes: 49 additions & 5 deletions src/fintamath/functions/other/Factorial.cpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,62 @@
#include "fintamath/functions/other/Factorial.hpp"

#include "fintamath/exceptions/UndefinedException.hpp"
#include "fintamath/literals/constants/ComplexInf.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> Factorial::call(const ArgumentsRefVector &argsVect) const {
const auto &rhs = argsVect.front().get();
const auto rhs = cast<INumber>(argsVect.front().get().toMinimalObject());
return multiFactorialSimpl(*rhs, order);
}

std::unique_ptr<IMathObject> Factorial::multiFactorialSimpl(const INumber &lhs, size_t order) {
static const auto multiFactorial = [] {
static MultiMethod<std::unique_ptr<IMathObject>(const INumber &, const Integer &)> outMultiAbs;

outMultiAbs.add<Integer, Integer>([](const Integer &inRhs, const Integer &inOrder) {
return factorialSimpl(inRhs, size_t(inOrder));
});

outMultiAbs.add<Rational, Integer>([](const Rational &inRhs, const Integer &inOrder) {
return factorialSimpl(inRhs, size_t(inOrder));
});

outMultiAbs.add<Real, Integer>([](const Real &inRhs, const Integer &inOrder) {
return factorialSimpl(inRhs, size_t(inOrder));
});

return outMultiAbs;
}();

return multiFactorial(lhs, Integer(order));
}

std::unique_ptr<IMathObject> Factorial::factorialSimpl(const Integer &rhs, size_t order) {
if (rhs < 0) {
return ComplexInf().clone();
}

return factorial(rhs, order).toMinimalObject();
}

std::unique_ptr<IMathObject> Factorial::factorialSimpl(const Rational &rhs, size_t order) {
if (order != 1) {
return makeExpr(Factorial(order), rhs);
}

return factorialSimpl(Real(rhs), order);
}

if (!is<Integer>(rhs)) {
throw UndefinedUnaryOperatorException(toString(), rhs.toString(), UndefinedUnaryOperatorException::Type::Postfix);
std::unique_ptr<IMathObject> Factorial::factorialSimpl(const Real &rhs, size_t order) {
if (order != 1) {
return makeExpr(Factorial(order), rhs);
}

return factorial(cast<Integer>(rhs), order).toMinimalObject();
return tgamma(rhs + 1).toMinimalObject();
}

}
9 changes: 9 additions & 0 deletions src/fintamath/numbers/RealFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,13 @@ Real getPi() {
return {cpp_dec_float_100(default_ops::get_constant_pi<cpp_dec_float_100::backend_type>())};
}

Real tgamma(const Real &rhs) {
try {
return boost::math::tgamma(rhs.getBackend());
}
catch (const std::domain_error &) {
throw UndefinedFunctionException("tgamma", {rhs.toString()});
}
}

}
9 changes: 6 additions & 3 deletions tests/src/functions/other/FactorialTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "fintamath/functions/arithmetic/UnaryPlus.hpp"
#include "fintamath/literals/Variable.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"

using namespace fintamath;

Expand All @@ -29,10 +30,12 @@ TEST(FactorialTests, callTest) {
EXPECT_EQ(f(Integer(1))->toString(), "1");
EXPECT_EQ(f(Integer(10))->toString(), "3628800");

EXPECT_EQ(f(Variable("a"))->toString(), "a!");
EXPECT_EQ(f(Integer(-10))->toString(), "ComplexInf");
EXPECT_EQ(f(Rational(-10))->toString(), "ComplexInf");
EXPECT_EQ(f(Real(-10))->toString(), "ComplexInf");
EXPECT_EQ(f(Rational(1, 10))->toString(), "");

EXPECT_THROW(f(Integer(-10)), UndefinedUnaryOperatorException);
EXPECT_THROW(f(Rational(1, 10)), UndefinedUnaryOperatorException);
EXPECT_EQ(f(Variable("a"))->toString(), "a!");

EXPECT_THROW(f(), InvalidInputFunctionException);
EXPECT_THROW(f(Integer(1), Integer(1), Integer(1)), InvalidInputFunctionException);
Expand Down
7 changes: 7 additions & 0 deletions tests/src/numbers/IntegerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ TEST(IntegerTests, stringConstructorTest) {
EXPECT_THROW(Integer("+"), InvalidInputException);
}

TEST(IntegerTests, templateConstructorTest) {
EXPECT_EQ(Integer(std::numeric_limits<uint64_t>::max()).toString(), "18446744073709551615");
EXPECT_EQ(Integer(std::numeric_limits<int64_t>::min()).toString(), "-9223372036854775808");
EXPECT_EQ(Integer(std::numeric_limits<uint8_t>::max()).toString(), "255");
EXPECT_EQ(Integer(std::numeric_limits<int8_t>::min()).toString(), "-128");
}

TEST(IntegerTests, intConstructorTest) {
EXPECT_EQ(Integer(10), 10);

Expand Down

0 comments on commit 75abc79

Please sign in to comment.