diff --git a/include/fintamath/core/MathObjectTypes.hpp b/include/fintamath/core/MathObjectTypes.hpp index 947445a37..3bb56eac0 100644 --- a/include/fintamath/core/MathObjectTypes.hpp +++ b/include/fintamath/core/MathObjectTypes.hpp @@ -48,6 +48,7 @@ enum class MathObjectType : MathObjectTypeId { Rational, Real, + Complex, IInteger = 8000, @@ -68,6 +69,7 @@ enum class MathObjectType : MathObjectTypeId { NegInf, ComplexInf, Undefined, + ImaginaryUnit, IFunction = 11000, diff --git a/include/fintamath/literals/constants/ImaginaryUnit.hpp b/include/fintamath/literals/constants/ImaginaryUnit.hpp new file mode 100644 index 000000000..46ea3c398 --- /dev/null +++ b/include/fintamath/literals/constants/ImaginaryUnit.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "fintamath/literals/Boolean.hpp" +#include "fintamath/literals/constants/IConstant.hpp" +#include "fintamath/numbers/Complex.hpp" + +namespace fintamath { + +class ImaginaryUnit : public IConstantCRTP { +public: + std::string toString() const override { + return Complex(Integer(0), Integer(1)).toString(); + } + + static MathObjectTypeId getTypeIdStatic() { + return MathObjectTypeId(MathObjectType::ImaginaryUnit); + } + +protected: + std::unique_ptr call() const override; +}; + +} diff --git a/include/fintamath/numbers/Complex.hpp b/include/fintamath/numbers/Complex.hpp new file mode 100644 index 000000000..de1d7fb4a --- /dev/null +++ b/include/fintamath/numbers/Complex.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "fintamath/numbers/Integer.hpp" +#include "fintamath/numbers/Rational.hpp" +#include "fintamath/numbers/Real.hpp" + +namespace fintamath { + +class Complex : public INumberCRTP { +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); + + Complex(const Integer &rhs); + + Complex(const Rational &rhs); + + Complex(const Real &rhs); + + Complex(int64_t rhs); + + std::string toString() const override; + + std::unique_ptr toMinimalObject() const override; + + Complex precise(uint8_t precision) const; + + const Integer &real() const; + + const Integer &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 ÷(const Complex &rhs) override; + + Complex &negate() override; + +private: + std::unique_ptr re = std::make_unique(0); + std::unique_ptr im = std::make_unique(0); +}; + +} diff --git a/src/fintamath/config/ConverterConfig.cpp b/src/fintamath/config/ConverterConfig.cpp index 3b31aac17..a84119c2a 100644 --- a/src/fintamath/config/ConverterConfig.cpp +++ b/src/fintamath/config/ConverterConfig.cpp @@ -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" @@ -39,6 +40,19 @@ struct ConverterConfig { Converter::add([](const Real & /*type*/, const Rational &value) { return std::make_unique(value); }); + + Converter::add([](const Complex & /*type*/, const Complex &value) { + return std::make_unique(value); + }); + Converter::add([](const Complex & /*type*/, const Integer &value) { + return std::make_unique(value); + }); + Converter::add([](const Complex & /*type*/, const Rational &value) { + return std::make_unique(value); + }); + Converter::add([](const Complex & /*type*/, const Real &value) { + return std::make_unique(value); + }); } }; diff --git a/src/fintamath/config/ParserConfig.cpp b/src/fintamath/config/ParserConfig.cpp index 927b76a12..add93ce1e 100644 --- a/src/fintamath/config/ParserConfig.cpp +++ b/src/fintamath/config/ParserConfig.cpp @@ -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" @@ -157,7 +158,6 @@ struct ParserConfig { INumber::registerType(&IInteger::parse); INumber::registerType(); - INumber::registerType(); IInteger::registerType(); @@ -173,6 +173,7 @@ struct ParserConfig { IConstant::registerType(); IConstant::registerType(); IConstant::registerType(); + IConstant::registerType(); IFunction::registerType(); IFunction::registerType(); diff --git a/src/fintamath/literals/constants/ImaginaryUnit.cpp b/src/fintamath/literals/constants/ImaginaryUnit.cpp new file mode 100644 index 000000000..49ac76cf8 --- /dev/null +++ b/src/fintamath/literals/constants/ImaginaryUnit.cpp @@ -0,0 +1,9 @@ +#include "fintamath/literals/constants/ImaginaryUnit.hpp" + +namespace fintamath { + +std::unique_ptr ImaginaryUnit::call() const { + return std::make_unique(Integer(0), Integer(1)); +} + +} diff --git a/src/fintamath/numbers/Complex.cpp b/src/fintamath/numbers/Complex.cpp new file mode 100644 index 000000000..a151a9358 --- /dev/null +++ b/src/fintamath/numbers/Complex.cpp @@ -0,0 +1,135 @@ +#include "fintamath/numbers/Complex.hpp" + +#include +#include + +#include "fintamath/exceptions/InvalidInputException.hpp" +#include "fintamath/exceptions/UndefinedException.hpp" + +namespace fintamath { + +Complex::Complex(const Complex &rhs) : re(cast(rhs.re->clone())), im(cast(rhs.im->clone())) { +} + +Complex &Complex::operator=(const Complex &rhs) { + if (this != &rhs) { + re = cast(rhs.re->clone()); + im = cast(rhs.im->clone()); + } + + return *this; +} + +Complex::Complex(const std::string &str) { + re = INumber::parse(str); +} + +Complex::Complex(const INumber &inReal, const INumber &inImaginary) { + if (is(inReal) || is(inImaginary)) { + throw InvalidInputException("Nested complex numbers are not allowed"); + } + + re = cast(inReal.clone()); + im = cast(inImaginary.clone()); +} + +Complex::Complex(const Integer &rhs) : re(cast(rhs.toMinimalObject())) { +} + +Complex::Complex(const Rational &rhs) : re(cast(rhs.toMinimalObject())) { +} + +Complex::Complex(const Real &rhs) : re(cast(rhs.toMinimalObject())) { +} + +Complex::Complex(int64_t rhs) : re(std::make_unique(rhs)) { +} + +std::string Complex::toString() const { + std::string res; + + if (*re != Integer(0)) { + res += re->toString(); + } + + if (*im != Integer(0)) { + if (!res.empty()) { + res += " + "; + } + + res += im->toString() + "I"; + } + + if (res.empty()) { + res = "0"; + } + + return res; +} + +std::unique_ptr 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(convert(*re).precise(precision)); + res.im = std::make_unique(convert(*im).precise(precision)); + return res; +} + +bool Complex::equals(const Complex &rhs) const { + return re == rhs.re && im == rhs.im; +} + +bool Complex::less(const Complex &rhs) const { + // TODO!!! See symengine + throw InvalidInputBinaryOperatorException("<", toString(), rhs.toString()); +} + +bool Complex::more(const Complex &rhs) const { + // TODO!!! See symengine + throw InvalidInputBinaryOperatorException(">", toString(), rhs.toString()); +} + +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(re->clone()); + auto lhsIm = cast(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(re->clone()); + auto lhsIm = cast(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; +} + +}