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.
     * Operator overload to be able to add two complex numbers.
     * @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;
    // Test for addition
    result = num1 + num2;
    expected = cnum1 + cnum2;
    assert(((void)"1 + 1i + 1 + 1i is equal to 2 + 2i but the addition doesn't "
                  "add up \n",
            (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;
}
Algerlogo

Β© Alger 2022

About us

We are a group of programmers helping each other build new things, whether it be writing complex encryption programs, or simple ciphers. Our goal is to work together to document and model beautiful, helpful and interesting algorithms using code. We are an open-source community - anyone can contribute. We check each other's work, communicate and collaborate to solve problems. We strive to be welcoming, respectful, yet make sure that our code follows the latest programming guidelines.