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 26, 2023
1 parent 61a0474 commit 13c0864
Show file tree
Hide file tree
Showing 15 changed files with 1,336 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;
};

}
73 changes: 73 additions & 0 deletions include/fintamath/numbers/Complex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#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;

bool isPrecise() const override;

bool isComplex() const override;

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

}
4 changes: 4 additions & 0 deletions include/fintamath/numbers/INumber.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class INumber : public IComparable {
return true;
}

virtual bool isComplex() const {
return false;
}

template <typename T, typename = std::enable_if_t<std::is_base_of_v<INumber, T>>>
static void registerType() {
Parser::registerType<T>(getParser());
Expand Down
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 Real(value).clone();
});

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

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

bool Complex::isPrecise() const {
return !is<Real>(re) && !is<Real>(im);
}

bool Complex::isComplex() const {
return *im != Integer(0);
}

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) {
Complex lhs = *this;

auto &x = *lhs.re;
auto &y = *lhs.im;
auto &u = *rhs.re;
auto &v = *rhs.im;

re = *(x * u) - *(y * v);
im = *(x * v) + *(y * u);

return *this;
}

// https://en.wikipedia.org/wiki/Complex_number#Reciprocal_and_division
Complex &Complex::divide(const Complex &rhs) {
Complex lhs = *this;

auto &x = *lhs.re;
auto &y = *lhs.im;
auto &u = *rhs.re;
auto &v = *rhs.im;

auto divisor = *(u * u) + *(v * v);

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

try {
re = *(*(x * u) + *(y * v)) / *divisor;
im = *(*(y * u) - *(x * v)) / *divisor;
}
catch (const UndefinedException &) {
throw UndefinedBinaryOperatorException("/", toString(), rhs.toString());
}

return *this;
}

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

}
Loading

0 comments on commit 13c0864

Please sign in to comment.