-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement complex number and ImaginaryUnit
- Loading branch information
Showing
10 changed files
with
835 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ÷(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); | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} |
Oops, something went wrong.