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 18, 2023
1 parent 848e81d commit 7c75892
Show file tree
Hide file tree
Showing 10 changed files with 835 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(Integer inReal, Integer 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));
}

}
175 changes: 175 additions & 0 deletions src/fintamath/numbers/Complex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#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.clone());
im = cast<INumber>(inImag.clone());
}

Complex::Complex(Integer inReal, Integer inImag) {
re = cast<INumber>(std::move(inReal).clone());
im = cast<INumber>(std::move(inImag).clone());
}

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();
}

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

res += imStr + "I";
}

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

return res;
}

std::unique_ptr<IMathObject> Complex::toMinimalObject() const {
if (*im == Integer(0)) {
return re->clone();
}
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 *re;
}

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);
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;
}

}
31 changes: 31 additions & 0 deletions tests/src/literals/constants/ImaginaryUnitTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <gtest/gtest.h>

#include "fintamath/literals/constants/ImaginaryUnit.hpp"
#include "fintamath/literals/constants/Pi.hpp"

using namespace fintamath;

const ImaginaryUnit c;

TEST(ImaginaryUnitTests, toStringTest) {
EXPECT_EQ(c.toString(), "I");
}

TEST(ImaginaryUnitTests, callTest) {
EXPECT_EQ(c()->toString(), "I");
}

TEST(ImaginaryUnitTests, equalsTest) {
EXPECT_EQ(c, c);
EXPECT_EQ(c, ImaginaryUnit());
EXPECT_EQ(ImaginaryUnit(), c);
EXPECT_EQ(c, cast<IMathObject>(ImaginaryUnit()));
EXPECT_EQ(cast<IMathObject>(ImaginaryUnit()), c);
EXPECT_NE(c, Pi());
EXPECT_NE(Pi(), c);
}

TEST(ImaginaryUnitTests, getTypeIdTest) {
EXPECT_EQ(ImaginaryUnit::getTypeIdStatic(), MathObjectTypeId(MathObjectType::ImaginaryUnit));
EXPECT_EQ(ImaginaryUnit().getTypeId(), MathObjectTypeId(MathObjectType::ImaginaryUnit));
}
Loading

0 comments on commit 7c75892

Please sign in to comment.