#### Complex Numbers

/**
* @author tjgurwara99
* @file
*
* \brief An implementation of Complex Number as Objects
* \details A basic implementation of Complex Number field as a class with
* operators overloaded to accommodate (mathematical) field operations.
*/

#include <cassert>
#include <cmath>
#include <complex>
#include <ctime>
#include <iostream>
#include <stdexcept>

/**
* \brief Class Complex to represent complex numbers as a field.
*/
class Complex {
// The real value of the complex number
double re;
// The imaginary value of the complex number
double im;

public:
/**
* \brief Complex Constructor which initialises our complex number.
* \details
* Complex Constructor which initialises the complex number which takes
* three arguments.
* @param x If the third parameter is 'true' then this x is the absolute
* value of the complex number, if the third parameter is 'false' then this
* x is the real value of the complex number (optional).
* @param y If the third parameter is 'true' then this y is the argument of
* the complex number, if the third parameter is 'false' then this y is the
* imaginary value of the complex number (optional).
* @param is_polar 'false' by default. If we want to initialise our complex
* number using polar form then set this to true, otherwise set it to false
* to use initialiser which initialises real and imaginary values using the
* first two parameters (optional).
*/
explicit Complex(double x = 0.f, double y = 0.f, bool is_polar = false) {
if (!is_polar) {
re = x;
im = y;
return;
}

re = x * std::cos(y);
im = x * std::sin(y);
}

/**
* \brief Copy Constructor
* @param other The other number to equate our number to.
*/
Complex(const Complex &other) : re(other.real()), im(other.imag()) {}

/**
* \brief Member function to get real value of our complex number.
* Member function (getter) to access the class' re value.
*/
double real() const { return this->re; }

/**
* \brief Member function to get imaginary value of our complex number.
* Member function (getter) to access the class' im value.
*/
double imag() const { return this->im; }

/**
* \brief Member function to give the modulus of our complex number.
* Member function to which gives the absolute value (modulus) of our
* complex number
* @return \f$\sqrt{z \bar{z}} \f$ where \f$z \f$ is our complex
* number.
*/
double abs() const {
return std::sqrt(this->re * this->re + this->im * this->im);
}

/**
* \brief Member function to give the argument of our complex number.
* @return Argument of our Complex number in radians.
*/
double arg() const { return std::atan2(this->im, this->re); }

/**
* \brief Operator overload of '+' on Complex class.
* @param other The other number that is added to the current number.
* @return result current number plus other number
*/
Complex operator+(const Complex &other) {
Complex result(this->re + other.re, this->im + other.im);
return result;
}

/**
* \brief Operator overload of '-' on Complex class.
* Operator overload to be able to subtract two complex numbers.
* @param other The other number being subtracted from the current number.
* @return result current number subtract other number
*/
Complex operator-(const Complex &other) {
Complex result(this->re - other.re, this->im - other.im);
return result;
}

/**
* \brief Operator overload of '*' on Complex class.
* Operator overload to be able to multiple two complex numbers.
* @param other The other number to multiply the current number to.
* @return result current number times other number.
*/
Complex operator*(const Complex &other) {
Complex result(this->re * other.re - this->im * other.im,
this->re * other.im + this->im * other.re);
return result;
}

/**
* \brief Operator overload of '~' on Complex class.
* Operator overload of the BITWISE NOT which gives us the conjugate of our
* complex number. NOTE: This is overloading the BITWISE operator but its
* not a BITWISE operation in this definition.
* @return result The conjugate of our complex number.
*/
Complex operator~() const {
Complex result(this->re, -(this->im));
return result;
}

/**
* \brief Operator overload of '/' on Complex class.
* Operator overload to be able to divide two complex numbers. This function
* would throw an exception if the other number is zero.
* @param other The other number we divide our number by.
* @return result Current number divided by other number.
*/
Complex operator/(const Complex &other) {
Complex result = *this * ~other;
double denominator =
other.real() * other.real() + other.imag() * other.imag();
if (denominator != 0) {
result = Complex(result.real() / denominator,
result.imag() / denominator);
return result;
} else {
throw std::invalid_argument("Undefined Value");
}
}

/**
* \brief Operator overload of '=' on Complex class.
* Operator overload to be able to copy RHS instance of Complex to LHS
* instance of Complex
*/
const Complex &operator=(const Complex &other) {
this->re = other.real();
this->im = other.imag();
return *this;
}
};

/**
* \brief Operator overload of '==' on Complex class.
* Logical Equal overload for our Complex class.
* @param a Left hand side of our expression
* @param b Right hand side of our expression
* @return 'True' If real and imaginary parts of a and b are same
* @return 'False' Otherwise.
*/
bool operator==(const Complex &a, const Complex &b) {
return a.real() == b.real() && a.imag() == b.imag();
}

/**
* \brief Operator overload of '<<' of ostream for Complex class.
* Overloaded insersion operator to accommodate the printing of our complex
* number in their standard form.
* @param os The console stream
* @param num The complex number.
*/
std::ostream &operator<<(std::ostream &os, const Complex &num) {
os << "(" << num.real();
if (num.imag() < 0) {
os << " - " << -num.imag();
} else {
os << " + " << num.imag();
}
os << "i)";
return os;
}

/**
* \brief Function to get random numbers to generate our complex numbers for
* test
*/
double get_rand() { return (std::rand() % 100 - 50) / 100.f; }

/**
* Tests Function
*/
void tests() {
std::srand(std::time(nullptr));
double x1 = get_rand(), y1 = get_rand(), x2 = get_rand(), y2 = get_rand();
Complex num1(x1, y1), num2(x2, y2);
std::complex<double> cnum1(x1, y1), cnum2(x2, y2);
Complex result;
std::complex<double> expected;
result = num1 + num2;
expected = cnum1 + cnum2;
assert(((void)"1 + 1i + 1 + 1i is equal to 2 + 2i but the addition doesn't "
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "First test passes." << std::endl;
// Test for subtraction
result = num1 - num2;
expected = cnum1 - cnum2;
assert(((void)"1 + 1i - 1 - 1i is equal to 0 but the program says "
"otherwise. \n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Second test passes." << std::endl;
// Test for multiplication
result = num1 * num2;
expected = cnum1 * cnum2;
assert(((void)"(1 + 1i) * (1 + 1i) is equal to 2i but the program says "
"otherwise. \n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Third test passes." << std::endl;
// Test for division
result = num1 / num2;
expected = cnum1 / cnum2;
assert(((void)"(1 + 1i) / (1 + 1i) is equal to 1 but the program says "
"otherwise.\n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Fourth test passes." << std::endl;
// Test for conjugates
result = ~num1;
expected = std::conj(cnum1);
assert(((void)"(1 + 1i) has a conjugate which is equal to (1 - 1i) but the "
"program says otherwise.\n",
(result.real() == expected.real() &&
result.imag() == expected.imag())));
std::cout << "Fifth test passes.\n";
// Test for Argument of our complex number
assert(((void)"(1 + 1i) has argument PI / 4 but the program differs from "
"the std::complex result.\n",
(num1.arg() == std::arg(cnum1))));
std::cout << "Sixth test passes.\n";
// Test for absolute value of our complex number
assert(((void)"(1 + 1i) has absolute value sqrt(2) but the program differs "
"from the std::complex result. \n",
(num1.abs() == std::abs(cnum1))));
std::cout << "Seventh test passes.\n";
}

/**
* Main function
*/
int main() {
tests();
return 0;
}  