Example 2: Complex Number & Triangles

#include <iostream>
#include <string>
#include <sstream>
#include <cmath>

using namespace std;

class Triangle_Complex; // forward declaration

class Integer
{
protected:
    int m_int_value;

public:
    Integer(int arg_int_value = 0) : m_int_value(arg_int_value)
    {
    }
    Integer(const Integer &arg_int) : m_int_value(arg_int.m_int_value)
    {
    }
    operator int() const
    {
        return m_int_value;
    }
    Integer &operator=(const Integer &arg_int)
    {
        m_int_value = arg_int.m_int_value;
        return *this;
    }
    void set_int_value(int arg_int_value)
    {
        m_int_value = arg_int_value;
    }
    friend ostream &operator<<(ostream &arg_os, const Integer &arg_integer)
    {
        arg_os << arg_integer.m_int_value;
        return arg_os;
    }
    friend istream &operator>>(istream &arg_is, Integer &arg_integer)
    {
        arg_is >> arg_integer.m_int_value;
        return arg_is;
    }
    friend Integer operator+(const Integer &arg_integer1, const Integer &arg_integer2)
    {
        Integer temp_integer(arg_integer1.m_int_value + arg_integer2.m_int_value);
        return temp_integer;
    }
    friend Integer operator-(const Integer &arg_integer1, const Integer &arg_integer2)
    {
        Integer temp_integer(arg_integer1.m_int_value - arg_integer2.m_int_value);
        return temp_integer;
    }
    friend Integer operator*(const Integer &arg_integer1, const Integer &arg_integer2)
    {
        Integer temp_integer(arg_integer1.m_int_value * arg_integer2.m_int_value);
        return temp_integer;
    }
    friend Integer operator/(const Integer &arg_integer1, const Integer &arg_integer2)
    {
        Integer temp_integer(arg_integer1.m_int_value / arg_integer2.m_int_value);
        return temp_integer;
    }
};

// Real is a Integer
class Real : public Integer
{
protected:
    double m_after_decimal;

public:
    Real(double arg_value = 0.0) : Integer((int)arg_value),
                                   m_after_decimal(arg_value - (int)arg_value)
    {
    }
    Real(const Real &arg_real) : Integer(arg_real),
                                 m_after_decimal(arg_real.m_after_decimal)
    {
    }
    operator double() const
    {
        return m_after_decimal + (double)m_int_value;
    }
    Real &operator=(const Real &arg_real)
    {
        m_int_value = arg_real.m_int_value;
        m_after_decimal = arg_real.m_after_decimal;
        return *this;
    }
    void set_real_value(double arg_value)
    {
        m_int_value = (int)arg_value;
        m_after_decimal = arg_value - m_int_value;
    }
    friend ostream &operator<<(ostream &arg_os, const Real &arg_real)
    {
        arg_os << (double)arg_real;
        return arg_os;
    }
    friend istream &operator>>(istream &arg_is, Real &arg_real)
    {
        double temp_double;
        arg_is >> temp_double;
        arg_real.set_real_value(temp_double);
        return arg_is;
    }
    friend Real operator+(const Real &arg_real1, const Real &arg_real2)
    {
        // call the Integer operator+ to add the Integer part
        Integer temp_integer((Integer)arg_real1 + (Integer)arg_real2);
        Real temp_real(arg_real1.m_after_decimal + arg_real2.m_after_decimal);
        temp_real.set_int_value((int)temp_integer + (int)temp_real);
        return temp_real;
    }
    friend Real operator-(const Real &arg_real1, const Real &arg_real2)
    {
        Real temp_real((double)arg_real1 - (double)arg_real2);
        return temp_real;
    }
    friend Real operator*(const Real &arg_real1, const Real &arg_real2)
    {
        Real temp_real((double)arg_real1 * (double)arg_real2);
        return temp_real;
    }
    friend Real operator/(const Real &arg_real1, const Real &arg_real2)
    {
        Real temp_real((double)arg_real1 / (double)arg_real2);
        return temp_real;
    }
};

class Imaginary
{
protected:
    double m_imaginary_value;

public:
    Imaginary(double arg_imaginary_value = 0.0) : m_imaginary_value(arg_imaginary_value)
    {
    }
    Imaginary(const Imaginary &arg_imaginary) : m_imaginary_value(arg_imaginary.m_imaginary_value)
    {
    }
    Imaginary &operator=(const Imaginary &arg_imaginary)
    {
        m_imaginary_value = arg_imaginary.m_imaginary_value;
        return *this;
    }
    double get_imaginary_value() const
    {
        return m_imaginary_value;
    }
    void set_imaginary_value(double arg_imaginary_value)
    {
        m_imaginary_value = arg_imaginary_value;
    }
    friend ostream &operator<<(ostream &arg_os, const Imaginary &arg_imaginary)
    {
        arg_os << arg_imaginary.m_imaginary_value << "i";
        return arg_os;
    }
    friend Imaginary operator+(const Imaginary &arg_imaginary1, const Imaginary &arg_imaginary2)
    {
        Imaginary temp_imaginary(arg_imaginary1.m_imaginary_value + arg_imaginary2.m_imaginary_value);
        return temp_imaginary;
    }
    friend Imaginary operator-(const Imaginary &arg_imaginary1, const Imaginary &arg_imaginary2)
    {
        Imaginary temp_imaginary(arg_imaginary1.m_imaginary_value - arg_imaginary2.m_imaginary_value);
        return temp_imaginary;
    }
};

class Complex : public Real, public Imaginary
{
public:
    Complex(double arg_real_value = 0.0,
            double arg_imaginary_value = 0.0)
        : Real(arg_real_value),
          Imaginary(arg_imaginary_value)
    {
    }
    Complex(const Complex &arg_complex)
        : Real(arg_complex),
          Imaginary(arg_complex)
    {
    }
    Complex &operator=(const Complex &arg_complex)
    {
        Real::operator=(arg_complex);
        Imaginary::operator=(arg_complex);
        return *this;
    }
    Complex reciprocal() const
    {
        Complex temp_complex(*this);
        double temp_real_value = temp_complex;
        double temp_imaginary_value = temp_complex.get_imaginary_value();
        double temp_denominator = temp_real_value * temp_real_value + temp_imaginary_value * temp_imaginary_value;
        temp_complex.set_real_value(temp_real_value / temp_denominator);
        temp_complex.set_imaginary_value(-temp_imaginary_value / temp_denominator);
        return temp_complex;
    }
    double get_magnitude() const
    {
        Real temp_real(*this);
        return sqrt((double)(temp_real * temp_real) + get_imaginary_value() * get_imaginary_value());
    }
    Complex get_argument() const
    {
        double magnitude = get_magnitude();
        Complex temp_complex((double)(*this) / magnitude, get_imaginary_value() / magnitude);
        return temp_complex;
    }
    friend ostream &operator<<(ostream &arg_os, const Complex &arg_complex)
    {
        arg_os << (Real)arg_complex << " + " << (Imaginary)arg_complex;
        return arg_os;
    }
    friend istream &operator>>(istream &arg_is, Complex &arg_complex)
    {
        string temp_string;
        getline(arg_is, temp_string, '+');
        arg_complex.set_real_value(stod(temp_string));
        getline(arg_is, temp_string, 'i');
        arg_complex.set_imaginary_value(stod(temp_string));
        return arg_is;
    }
    friend Complex operator+(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        Complex temp_complex((double)arg_complex1 + (double)arg_complex2,
                             arg_complex1.get_imaginary_value() + arg_complex2.get_imaginary_value());
        return temp_complex;
    }
    friend Complex operator-(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        Complex temp_complex((double)arg_complex1 - (double)arg_complex2,
                             arg_complex1.get_imaginary_value() - arg_complex2.get_imaginary_value());
        return temp_complex;
    }
    friend Complex operator*(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        Complex temp_complex((double)arg_complex1 * (double)arg_complex2 -
                                 arg_complex1.get_imaginary_value() * arg_complex2.get_imaginary_value(),
                             (double)arg_complex1 * arg_complex2.get_imaginary_value() +
                                 arg_complex1.get_imaginary_value() * (double)arg_complex2);
        return temp_complex;
    }
    friend Complex operator/(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        Complex temp_complex(arg_complex1 * arg_complex2.reciprocal());
        return temp_complex;
    }
    friend bool operator==(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return ((double)arg_complex1 == (double)arg_complex2) &&
               (arg_complex1.get_imaginary_value() == arg_complex2.get_imaginary_value());
    }
    friend bool operator!=(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return !(arg_complex1 == arg_complex2);
    }
    friend bool operator<(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return ((double)arg_complex1 < (double)arg_complex2) ||
               ((double)arg_complex1 == (double)arg_complex2 &&
                arg_complex1.get_imaginary_value() < arg_complex2.get_imaginary_value());
    }
    friend bool operator>(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return arg_complex2 < arg_complex1;
    }
    friend bool operator<=(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return !(arg_complex1 > arg_complex2);
    }
    friend bool operator>=(const Complex &arg_complex1, const Complex &arg_complex2)
    {
        return !(arg_complex1 < arg_complex2);
    }
    friend class Triangle_Complex;
};

// Triangle_Complex class declaration
class Triangle_Complex
{
protected:
    // define three points of the Triangle_Complex
    // Triangle 'has-a' Complex, so it use composition
    Complex m_point[3];

    // check & correct the Triangle_Complex points to counterclockwise order
    void _check_points()
    {
        // if the area is negative, swap the points
        if (area() < 0.0)
        {
            Complex temp = m_point[0];
            m_point[0] = m_point[1];
            m_point[1] = temp;
        }
    }

public:
    // Constructor
    Triangle_Complex(const Complex &arg_point1,
                     const Complex &arg_point2,
                     const Complex &arg_point3)
        : m_point{arg_point1, arg_point2, arg_point3}
    {
        _check_points();
    }

    // Copy constructor
    Triangle_Complex(const Triangle_Complex &arg_triangle)
        : m_point{arg_triangle.m_point[0], arg_triangle.m_point[1], arg_triangle.m_point[2]}
    {
    }

    // Destructor
    ~Triangle_Complex() {} // do nothing

    // modify the three points of the Triangle_Complex
    virtual void set_points(const Complex &arg_point1,
                            const Complex &arg_point2,
                            const Complex &arg_point3)
    {
        m_point[0] = arg_point1;
        m_point[1] = arg_point2;
        m_point[2] = arg_point3;
        _check_points();
    }
    virtual void set_point1(const Complex &arg_point1)
    {
        m_point[0] = arg_point1;
        _check_points();
    }
    virtual void set_point2(const Complex &arg_point2)
    {
        m_point[1] = arg_point2;
        _check_points();
    }
    virtual void set_point3(const Complex &arg_point3)
    {
        m_point[2] = arg_point3;
        _check_points();
    }
    // get the three points of the Triangle_Complex
    Complex get_point1() const
    {
        return m_point[0];
    }
    Complex get_point2() const
    {
        return m_point[1];
    }
    Complex get_point3() const
    {
        return m_point[2];
    }

    // calculate the area of the Triangle_Complex
    double area()
    {
        double area = 0.0;
        for (int i = 0; i < 3; i++)
        {
            area += (double)m_point[i] * m_point[(i + 1) % 3].get_imaginary_value();
            area -= m_point[i].get_imaginary_value() * (double)m_point[(i + 1) % 3];
        }
        return area / 2.0;
    }

    // print the Triangle_Complex
    // format: (c1, c2, c3)
    friend std::ostream &operator<<(std::ostream &arg_os, const Triangle_Complex &arg_tri)
    {
        arg_os << "(";
        for (int i = 0; i < 3; i++)
        {
            arg_os << arg_tri.m_point[i];
            if (i < 2)
                arg_os << ", ";
        }
        arg_os << ")";
        return arg_os;
    }
    // read the coordinate of a point from the input,
    // format: (c1, c2, c3) and ignore space
    friend std::istream &operator>>(std::istream &arg_is, Triangle_Complex &arg_tri)
    {
        string temp;
        if (getline(arg_is, temp, '(')) // ignore the first '('
        {
            for (int i = 0; i < 3; i++)
            {
                arg_is >> arg_tri.m_point[i];
                if (i < 2)
                {
                    getline(arg_is, temp, ','); // ignore the ','
                }
            }
            getline(arg_is, temp, ')'); // ignore the last ')'
        }

        // check & correct the Triangle_Complex points to counterclockwise order
        arg_tri._check_points();

        return arg_is;
    }
};

class Equilateral_Triangle_Complex : public Triangle_Complex
// Equilateral Triangle `is-a' Triangle_Complex, so it use inheritance
// use function overloading to implement the different function
// of Equilateral_Triangle_Complex
// details will be discussed in polymorphism (next chapter)
{
public:
    Equilateral_Triangle_Complex(const Complex &arg_point1,
                                 const Complex &arg_point2)
        : Triangle_Complex(arg_point1, arg_point2, arg_point1)
    {
        Complex temp = arg_point2 - arg_point1;
        temp = temp * Complex(0.5, sqrt(3.0) / 2.0); // rotate 60 degree
        m_point[2] = arg_point1 + temp;              // calculate the third point
        _check_points();
    }

    void set_points(const Complex &arg_point1,
                    const Complex &arg_point2,
                    const Complex &arg_point3)
    {
        m_point[0] = arg_point1;
        m_point[1] = arg_point2;
        Complex temp = arg_point2 - arg_point1;
        temp = temp * Complex(0.5, sqrt(3.0) / 2.0); // rotate 60 degree
        m_point[2] = arg_point1 + temp;              // calculate the third point
        _check_points();
    }

    void set_point1(const Complex &arg_point1)
    {
        // invalid operation
    }
    void set_point2(const Complex &arg_point2)
    {
        // invalid operation
    }
    void set_point3(const Complex &arg_point3)
    {
        // invalid operation
    }
    // read the coordinate of a point from the input,
    // format: (c1, c2, c3) and ignore c3 and spaces
    friend std::istream &operator>>(std::istream &arg_is,
                                    Equilateral_Triangle_Complex &arg_tri)
    {
        string temp;
        if (getline(arg_is, temp, '(')) // ignore the first '('
        {
            for (int i = 0; i < 3; i++)
            {
                if (i < 2)
                {
                    arg_is >> arg_tri.m_point[i];
                    getline(arg_is, temp, ','); // ignore the ','
                }
            }
            getline(arg_is, temp, ')'); // ignore the c3 and last ')'
        }

        // check & correct the Triangle_Complex points to counterclockwise order
        arg_tri._check_points();

        return arg_is;
    }
};

// main function

int main()
{
    // create a Equilateral_Triangle_Complex
    Triangle_Complex *t = new Equilateral_Triangle_Complex(Complex(1, 1), Complex(2, 2));
    cout << *t << endl;
    cout << "Area: " << t->area() << endl;
    // change the points of the Equilateral_Triangle_Complex
    t->set_points(Complex(3, 3), Complex(2, 2), Complex(3, 0));
    cout << *t << endl;
    cout << "Area: " << t->area() << endl;
    delete t;

    // change the object to Triangle_Complex
    t = new Triangle_Complex(Complex(1, 1), Complex(2, 2), Complex(3, 0));
    cout << *t << endl;
    cout << "Area: " << t->area() << endl;
    // change the points of the Triangle_Complex
    t->set_points(Complex(3, 3), Complex(2, 2), Complex(3, 0));
    cout << *t << endl;
    cout << "Area: " << t->area() << endl;
    delete t;

    return 0;
}