#include <iostream>

class Complex
{
 private:
   double real;
   double imag;

 public:
   // A Constructor is a method that get envoked implicitly
   // when an instance of the class is created.
   // The name of the constructor method(s) is the same as the
   // class name.  
   // A constructor does not have a return type.

   // the default constructor takes no parameter
   Complex ()
     {
      real = 0.0;
      imag = 0.0;
     }

   // This is another constructor that takes two double parameters
   //  This constructor uses an initialization list to initialize
   //   (instead of assign) the values into the data members.
   Complex (double rl, double im)
     : real (rl), imag (im)
     {
      // real = rl;
      // imag = im;
     }

   // the copy constructor takes one parameter of the same type
   //  as the class itself
   // A default copy constructor is always created for a class
   //  if one is not written.  This default copy constructor
   //  does a member-by-member copy of the all data members
   // The parameter needs to be a pass-by-const-reference parameter
   //  for the copy constructor
   Complex (const Complex& cp)
     {
      real = cp.real;
      imag = cp.imag;
     }

   // The const before the parameter implies that the parameter
   //  rl will not be changed in the function/method
   inline void setReal (const double rl)
     {
      //  rl = 5.3;  //  This line would cause a compiler error
      real = rl;
     }

   void setImag (double im)
     {
      this->imag = im;
      // The above is the same as:
      // imag = im;
     }

   void setComplex (double rl, double im)
     {
      real = rl;
      imag = im;
     }

   // the const after the parameter list inplies that 
   //  this method will not change the data members
   //  of the class
   double getReal () const
     {
      // real = 7.2;   // This line would cause a compiler error
      return real;
     }

   double getImag ()
     {
      return imag;
     }

   //void printComplex (Complex *this)
   void printComplex ()
     {
       // if (this->imag == 0.0)
       if (imag == 0.0)
         cout << real;
      else
         cout << real << "+" << imag << "i";
     }

   // replace the complex number by the sum of the values given
   //  in the complex parameters  
   //   similar to c3 = c1 + c2;
   void sum (Complex lhs, Complex rhs)
     {
      real = lhs.real + rhs.real;
      imag = lhs.imag + rhs.imag;
     }

   // increment the complex number by the value given in
   //  the complex parameter
   //  similar to c3 += c1;  or c3 = c3 + c1;
   void inc (Complex rhs)
     {
      real = real + rhs.real;
      imag = imag + rhs.imag;
     }

   // assign the complex number given by the value in the parameter
   //  to the current instance of the complex class
   void assign (Complex rhs)
     {
      real = rhs.real;
      imag = rhs.imag;
     }

   // replace the complex number by the product of the values given
   //  in the complex parameters  
   //   similar to c3 = c1 * c2;
   void prod (Complex lhs, Complex rhs)
     {
      real = lhs.real * rhs.real - (lhs.imag * rhs.imag);
      imag = lhs.real * rhs.imag + lhs.imag * rhs.real;
     }

};  // end of Complex class

// functions that uses the Complex class

// A less than function.  Return true if the the
//  complex value in the first parameter is less 
//  the complex number in the second parameter.
bool lessThan (Complex cp1, Complex cp2)
{
 if (cp1.getReal() < cp2.getReal())
    return true;
 else if (cp1.getReal() > cp2.getReal())
    return false;
 else if (cp1.getImag() < cp2.getImag())
    return true;
 else
    return false;
}

int main()
{
 Complex c1, c2 (3.5, -7.8), c3(c2);
 Complex c4, c5, c6;

 int value1 (0);

 int value2;
 value2 = 0;

 // printint out the instances of class Complex prior to any
 //   assignment of values
 cout << "The following instances of class Complex have "
      << "not been assigned" << endl; 
 cout << "c1: ";
 c1.printComplex();
 cout << endl << "c2: ";
 c2.printComplex();
 cout << endl << "c3: ";
 c3.printComplex();
 // printComplex (c3);
 cout << endl;

 // cout << endl << "c2: " << c2 << endl;

 // the following lines cause a compiler (syntax) error because
 //  the data member "real" and "imag" are private elements of the
 //  class Complex
 //c1.real = 3.0;
 //c1.imag = 4.5;

 c1.setReal(4.0);
 // setReal(c1, 4.0);
 c1.setImag(5.5);

 c2.setComplex (-2.0, -1.5);

 // the following line causes a compiler (syntax) error because
 //  the data member "real" and "imag" are private elements of the
 //  class Complex
 //cout << "c1: " << c1.real << "+" << c1.imag << "i" << endl;
 cout << "c1: " << c1.getReal() << "+" << c1.getImag() << "i" << endl;

 cout << "c1: ";
 c1.printComplex();
 cout << endl << "c2: ";
 c2.printComplex();
 cout << endl;

 c3.sum (c1, c2);  // performs: c3 = c1 + c2;
 cout << "c3: ";
 c3.printComplex();
 cout << endl;

 c3.inc (c1);  // performs: c3 += c1;
 cout << "c3: ";
 c3.printComplex();
 cout << endl;

 c4.setComplex (3.5, 0.0);
 cout << "c4: ";       // with operator overloading we could write:
 c4.printComplex();    //  cout << "c4: " << c4 << endl;
 cout << endl;

 c5.prod (c3, c4);  //  performs: c5 = c3 * c4;
 cout << "c5: ";   
 c5.printComplex();
 cout << endl;

 c5.prod (c3, c2);  //  performs: c5 = c3 * c2;
 cout << "c5: ";   
 c5.printComplex();
 cout << endl;

 c6.assign(c5);     // performs: c6 = c5;
 cout << "c6: ";   
 c6.printComplex();
 cout << endl;

 cout << endl;
 cout << "Comparing c1 and c2. " << endl;
 cout << "c1: ";   
 c1.printComplex();
 cout << endl;
 cout << "c2: ";   
 c2.printComplex();
 cout << endl;
 if (lessThan (c1, c2))
    cout << "    c1 is less than c2" << endl;
 else
    cout << "    c1 is not less than c2" << endl;
}