Skip to content

Commit

Permalink
Implement complex number and ImaginaryUnit
Browse files Browse the repository at this point in the history
  • Loading branch information
fintarin committed Aug 25, 2023
1 parent 848e81d commit dac362f
Show file tree
Hide file tree
Showing 11 changed files with 942 additions and 1 deletion.
2 changes: 2 additions & 0 deletions include/fintamath/core/MathObjectTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum class MathObjectType : MathObjectTypeId {

Rational,
Real,
Complex,

IInteger = 8000,

Expand All @@ -69,6 +70,7 @@ enum class MathObjectType : MathObjectTypeId {
NegInf,
ComplexInf,
Undefined,
ImaginaryUnit,

IFunction = 11000,

Expand Down
22 changes: 22 additions & 0 deletions include/fintamath/literals/constants/ImaginaryUnit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include "fintamath/literals/constants/IConstant.hpp"
#include "fintamath/numbers/Complex.hpp"

namespace fintamath {

class ImaginaryUnit : public IConstantCRTP<Complex, ImaginaryUnit> {
public:
std::string toString() const override {
return "I";
}

static MathObjectTypeId getTypeIdStatic() {
return MathObjectTypeId(MathObjectType::ImaginaryUnit);
}

protected:
std::unique_ptr<IMathObject> call() const override;
};

}
71 changes: 71 additions & 0 deletions include/fintamath/numbers/Complex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"

namespace fintamath {

class Complex : public INumberCRTP<Complex> {
public:
Complex() = default;

Complex(const Complex &rhs);

Complex(Complex &&rhs) noexcept = default;

Complex &operator=(const Complex &rhs);

Complex &operator=(Complex &&rhs) noexcept = default;

explicit Complex(const std::string &str);

explicit Complex(const INumber &inReal, const INumber &inImag);

explicit Complex(int64_t inReal, int64_t inImag);

Complex(const Integer &rhs);

Complex(const Rational &rhs);

Complex(const Real &rhs);

Complex(int64_t rhs);

std::string toString() const override;

std::unique_ptr<IMathObject> toMinimalObject() const override;

Complex precise(uint8_t precision) const;

const INumber &real() const;

const INumber &imag() const;

static MathObjectTypeId getTypeIdStatic() {
return MathObjectTypeId(MathObjectType::Complex);
}

protected:
bool equals(const Complex &rhs) const override;

bool less(const Complex &rhs) const override;

bool more(const Complex &rhs) const override;

Complex &add(const Complex &rhs) override;

Complex &substract(const Complex &rhs) override;

Complex &multiply(const Complex &rhs) override;

Complex &divide(const Complex &rhs) override;

Complex &negate() override;

private:
std::unique_ptr<INumber> re = std::make_unique<Integer>(0);
std::unique_ptr<INumber> im = std::make_unique<Integer>(0);
};

}
14 changes: 14 additions & 0 deletions src/fintamath/config/ConverterConfig.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "fintamath/core/Converter.hpp"

#include "fintamath/numbers/Complex.hpp"
#include "fintamath/numbers/Integer.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"
Expand Down Expand Up @@ -39,6 +40,19 @@ struct ConverterConfig {
Converter::add<Real, Rational>([](const Real & /*type*/, const Rational &value) {
return std::make_unique<Real>(value);
});

Converter::add<Complex, Complex>([](const Complex & /*type*/, const Complex &value) {
return std::make_unique<Complex>(value);
});
Converter::add<Complex, Integer>([](const Complex & /*type*/, const Integer &value) {
return std::make_unique<Complex>(value);
});
Converter::add<Complex, Rational>([](const Complex & /*type*/, const Rational &value) {
return std::make_unique<Complex>(value);
});
Converter::add<Complex, Real>([](const Complex & /*type*/, const Real &value) {
return std::make_unique<Complex>(value);
});
}
};

Expand Down
3 changes: 2 additions & 1 deletion src/fintamath/config/ParserConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#include "fintamath/literals/constants/E.hpp"
#include "fintamath/literals/constants/False.hpp"
#include "fintamath/literals/constants/IConstant.hpp"
#include "fintamath/literals/constants/ImaginaryUnit.hpp"
#include "fintamath/literals/constants/Inf.hpp"
#include "fintamath/literals/constants/NegInf.hpp"
#include "fintamath/literals/constants/Pi.hpp"
Expand Down Expand Up @@ -157,7 +158,6 @@ struct ParserConfig {

INumber::registerType<IInteger>(&IInteger::parse);
INumber::registerType<Rational>();
INumber::registerType<Real>();

IInteger::registerType<Integer>();

Expand All @@ -169,6 +169,7 @@ struct ParserConfig {
IConstant::registerType<Pi>();
IConstant::registerType<True>();
IConstant::registerType<False>();
IConstant::registerType<ImaginaryUnit>();
IConstant::registerType<Inf>();
IConstant::registerType<NegInf>();
IConstant::registerType<ComplexInf>();
Expand Down
9 changes: 9 additions & 0 deletions src/fintamath/literals/constants/ImaginaryUnit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "fintamath/literals/constants/ImaginaryUnit.hpp"

namespace fintamath {

std::unique_ptr<IMathObject> ImaginaryUnit::call() const {
return std::make_unique<Complex>(Integer(0), Integer(1));
}

}
191 changes: 191 additions & 0 deletions src/fintamath/numbers/Complex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#include "fintamath/numbers/Complex.hpp"

#include <algorithm>
#include <stdexcept>

#include "fintamath/exceptions/InvalidInputException.hpp"
#include "fintamath/exceptions/UndefinedException.hpp"

namespace fintamath {

Complex::Complex(const Complex &rhs) : re(cast<INumber>(rhs.re->clone())), im(cast<INumber>(rhs.im->clone())) {
}

Complex &Complex::operator=(const Complex &rhs) {
if (this != &rhs) {
re = cast<INumber>(rhs.re->clone());
im = cast<INumber>(rhs.im->clone());
}

return *this;
}

Complex::Complex(const std::string &str) {
if (!str.empty() && str.back() == 'I') {
im = INumber::parse(str.substr(0, str.length() - 1));
}
else {
re = INumber::parse(str);
}

if (!re || !im) {
throw InvalidInputException(str);
}
}

Complex::Complex(const INumber &inReal, const INumber &inImag) {
if (is<Complex>(inReal) || is<Complex>(inImag)) {
throw InvalidInputException("Nested complex numbers are not allowed");
}

re = cast<INumber>(inReal.toMinimalObject());
im = cast<INumber>(inImag.toMinimalObject());
}

Complex::Complex(int64_t inReal, int64_t inImag) : re(std::make_unique<Integer>(inReal)),
im(std::make_unique<Integer>(inImag)) {
}

Complex::Complex(const Integer &rhs) : re(cast<INumber>(rhs.toMinimalObject())) {
}

Complex::Complex(const Rational &rhs) : re(cast<INumber>(rhs.toMinimalObject())) {
}

Complex::Complex(const Real &rhs) : re(cast<INumber>(rhs.toMinimalObject())) {
}

Complex::Complex(int64_t rhs) : re(std::make_unique<Integer>(rhs)) {
}

std::string Complex::toString() const {
std::string res;

if (*re != Integer(0)) {
res += re->toString();
}

if (*im != Integer(0)) {
std::string imStr = im->toString();
bool isImNeg = false;

if (imStr.front() == '-') {
imStr = imStr.substr(1);
isImNeg = true;
}

if (imStr == "1") {
imStr.clear();
}
else {
imStr += " ";
}

if (!res.empty()) {
res += isImNeg ? " - " : " + ";
}
else if (isImNeg) {
res += "-";
}

res += imStr + "I";
}

if (res.empty()) {
res = "0";
}

return res;
}

std::unique_ptr<IMathObject> Complex::toMinimalObject() const {
if (*im == Integer(0)) {
return re->toMinimalObject();
}

return clone();
}

Complex Complex::precise(uint8_t precision) const {
Complex res = *this;
res.re = std::make_unique<Real>(convert<Real>(*re)->precise(precision));
res.im = std::make_unique<Real>(convert<Real>(*im)->precise(precision));
return res;
}

const INumber &Complex::real() const {
return *re;
}

const INumber &Complex::imag() const {
return *im;
}

bool Complex::equals(const Complex &rhs) const {
return *re == *rhs.re && *im == *rhs.im;
}

bool Complex::less(const Complex &rhs) const {
if (*re == *rhs.re) {
return *im < *rhs.im;
}

return *re < *rhs.re;
}

bool Complex::more(const Complex &rhs) const {
if (*re == *rhs.re) {
return *im > *rhs.im;
}

return *re > *rhs.re;
}

Complex &Complex::add(const Complex &rhs) {
re = *re + *rhs.re;
im = *im + *rhs.im;

return *this;
}

Complex &Complex::substract(const Complex &rhs) {
re = *re - *rhs.re;
im = *im - *rhs.im;

return *this;
}

// https://en.wikipedia.org/wiki/Complex_number#Multiplication_and_square
Complex &Complex::multiply(const Complex &rhs) {
auto lhsRe = cast<INumber>(re->clone());
auto lhsIm = cast<INumber>(im->clone());

re = *(*lhsRe * *rhs.re) + *(*lhsIm * *rhs.im);
im = *(*lhsRe * *rhs.im) + *(*lhsIm * *rhs.re);

return *this;
}

// https://en.wikipedia.org/wiki/Complex_number#Reciprocal_and_division
Complex &Complex::divide(const Complex &rhs) {
auto lhsRe = cast<INumber>(re->clone());
auto lhsIm = cast<INumber>(im->clone());
auto divisor = *(*rhs.re * *rhs.re) + *(*rhs.im * *rhs.im);

if (is<Integer>(divisor)) {
divisor = convert<Rational>(*divisor);
}

re = *(*(*lhsRe * *rhs.re) + *(*lhsIm * *rhs.im)) / *divisor;
im = *(*(*lhsIm * *rhs.re) - *(*lhsRe * *rhs.im)) / *divisor;

return *this;
}

Complex &Complex::negate() {
re = -(*re);
im = -(*im);
return *this;
}

}
17 changes: 17 additions & 0 deletions tests/src/core/ConverterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "fintamath/core/Converter.hpp"

#include "fintamath/numbers/Complex.hpp"
#include "fintamath/numbers/Rational.hpp"
#include "fintamath/numbers/Real.hpp"

Expand All @@ -19,6 +20,14 @@ TEST(ConverterTests, convertTest) {
EXPECT_FALSE(convert(Integer(), Real()));
EXPECT_TRUE(is<Real>(convert(Real(), Rational())));
EXPECT_FALSE(convert(Rational(), Real()));

EXPECT_TRUE(is<Complex>(convert(Complex(), Complex())));
EXPECT_TRUE(is<Complex>(convert(Complex(), Integer())));
EXPECT_FALSE(convert(Integer(), Complex()));
EXPECT_TRUE(is<Complex>(convert(Complex(), Rational())));
EXPECT_FALSE(convert(Rational(), Complex()));
EXPECT_TRUE(is<Complex>(convert(Complex(), Real())));
EXPECT_FALSE(convert(Real(), Complex()));
}

TEST(ConverterTests, convertTemplateTest) {
Expand All @@ -33,4 +42,12 @@ TEST(ConverterTests, convertTemplateTest) {
EXPECT_FALSE(convert<Integer>(Real()));
EXPECT_TRUE(is<Real>(convert<Real>(Rational())));
EXPECT_FALSE(convert<Rational>(Real()));

EXPECT_TRUE(is<Complex>(convert<Complex>(Complex())));
EXPECT_TRUE(is<Complex>(convert<Complex>(Integer())));
EXPECT_FALSE(convert<Integer>(Complex()));
EXPECT_TRUE(is<Complex>(convert<Complex>(Rational())));
EXPECT_FALSE(convert<Rational>(Complex()));
EXPECT_TRUE(is<Complex>(convert<Complex>(Real())));
EXPECT_FALSE(convert<Real>(Complex()));
}
Loading

0 comments on commit dac362f

Please sign in to comment.