1. Homepage of Dr. Zoltán Porkoláb
    1. Home
    2. Archive
  2. Teaching
    1. Timetable
    2. Bolyai College
    3. C++ (for mathematicians)
    4. Imperative programming (BSc)
    5. Multiparadigm programming (MSc)
    6. Programming (MSc Aut. Sys.)
    7. Programming languages (PhD)
    8. Software technology lab
    9. Theses proposals (BSc and MSc)
  3. Research
    1. Sustrainability
    2. CodeChecker
    3. CodeCompass
    4. Templight
    5. Projects
    6. Conferences
    7. Publications
    8. PhD students
  4. Affiliations
    1. Dept. of Programming Languages and Compilers
    2. Ericsson Hungary Ltd

Practice 22.

A sample test

Video: cpp-mat-probazh.mp4

In this practice we will solve a task which is very similar to the one you will solve as the final exam.

IMPORTANT NOTICE

As many of you noticed us, we make an error implementing the multiplication of the complex numbers. We fixed this error in the code below, and marked it with comments. However, we did not fixed the video, so the main function here is now different than the one in the video.

Sorry for the error and thank you for the warning! You are mathematicians!

Create a class for complex numbers

You have to create a (non-template) class which represents complex numbers. Therefore we have to write a complex.h and a complex.cpp file. However, the specification of the class is not given as a text, it is given by the main.cpp file which will use the class you write.

Inside the main.cpp we have several parts, each are separated by printing out the value of your_mark. The last printed value will be your mark for the exam.

You are not allowed to modify main.cpp (you can do this for your tests, but the submitted complex.h and complex.cpp should work with the original version of main.cpp.

main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include "complex.h"

int main()
{
  int your_mark = 1;

  Complex a(4.3, 3.1);
  Complex b(1.1);
  your_mark = a.getImag() - b.getReal();
  std::cout << "your mark is " << your_mark << std::endl;

  const Complex c = (a*b)-a;
  b.real() = 3;
  b.imag() = 3; 
  // we changed here the original main here to fix the error
  // this line removed: int i = static_cast<int>(c.imag());  
  if ( b.getReal() == 3 && b.getImag() == 3 )
    ++your_mark; // in the original version: your_mark -= i; 
  std::cout << "your mark is " << your_mark << std::endl;
  
  Complex conj = ~c;
  if ( conj.imag()== -c.imag() && conj.abs() == c.abs() )
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;

  /* since we fixed multiplication, this won't work anymore:
  if ( "(-4.300000)+(-1.780000)i" == c.to_string() &&
       "(-4.300000)+1.780000i" == conj.to_string() )  */
  // instead we put these lines here:
  if ( "0.430000+0.310000i" == c.to_string() &&
        "0.430000+(-0.310000)i" == conj.to_string() ) 
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;

  return 0;	
}

You are not allowed to modify main.cpp (you can do this for your tests, but the submitted complex.h and complex.cpp should work with the original version of main.cpp.

What you can do, you may put comments around those blocks of code which are not implemented yet. So, when targeting mark 3, you can do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include "complex.h"

int main()
{
  int your_mark = 1;

  Complex a(4.3, 3.1);
  Complex b(1.1);
  your_mark = a.getImag() - b.getReal();
  std::cout << "your mark is " << your_mark << std::endl;

  const Complex c = (a*b)-a;
  b.real() = 3;
  b.imag() = 3; 
  if ( b.getReal() == 3 && b.getImag() == 3 )
    ++your_mark; 
  std::cout << "your mark is " << your_mark << std::endl;
/*  
  Complex conj = ~c;
  if ( conj.imag()== -c.imag() && conj.abs() == c.abs() )
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;

 if ( "0.430000+0.310000i" == c.to_string() &&
        "0.430000+(-0.310000)i" == conj.to_string() ) 
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;
*/
  return 0;	
}

.. but not this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include "complex.h"

int main()
{
  int your_mark = 1;

  Complex a(4.3, 3.1);
  Complex b(1.1);
  your_mark = a.getImag() - b.getReal();
  std::cout << "your mark is " << your_mark << std::endl;

  const Complex c = (a*b)-a;
  b.real() = 3;
  b.imag() = 3; 
  if ( b.getReal() == 3 && b.getImag() == 3 )
    ++your_mark; 
  std::cout << "your mark is " << your_mark << std::endl;
  
  Complex conj = ~c;
//  if ( conj.imag()== -c.imag() && conj.abs() == c.abs() )  Cheating!!!
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;

/* if ( "0.430000+0.310000i" == c.to_string() &&
        "0.430000+(-0.310000)i" == conj.to_string() ) Cheating!!! */ 
    ++your_mark;
  std::cout << "your mark is " << your_mark << std::endl;

  return 0;	
}

So what we have to write:

  • mark 2: constructor which takes 2, 1 (or 0) parameters getters: getReal() and getImag().

  • mark 3: operators (+,-,*,/), and special accessor methods, which can stand both on the right and on the left side of assignment and whould work on constant when reading the value.

  • mark 4: the getters/accessors should work on constant objects too, and we should write an conjuction operator “~” and the abs() absolute value.

  • mark 5: a to_string() method to print a string representation of a complex number. You are allowed to remove/add zeros to the strings in main.cpp if necessary.

PLEASE, STOP HERE, AND SPEND 2 HOURS (MAX) TO SOLVE THE TASK

If you do not try yourself, it will not really help you! If you stuck, watch the video, then stop it and continue yourself.

The solution

complex.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef COMPLEX_H
#define COMPLEX_H

#include <string>

class Complex
{
public:
  // only one constructor, whith default parameters, 
  // so they could be called with 2, 1, or 0 parameter 
  Complex(double r = 0.0, double i = 0.0);
	
  // usual getters - const methods
  double getReal() const { return _real; }
  double getImag() const { return _imag; }

  // these accessors returning reference, so they
  // can appear on the left side of an assignment
  double       &real()       { return _real; }
  // but we need a const version too, because 
  // they should work on constants too (but just reading) 
  const double &real() const { return _real; }
  // same for the imaginarius part
  double       &imag()       { return _imag; }
  const double &imag() const { return _imag; }

  // the abs() function and the conjugate operator
  double abs() const;         // absolute value
  Complex operator~() const;  //conjugate

  // similar than the one for test1.
  std::string to_string() const;
private:
  double _real;
  double _imag;
};

// operators are global
Complex operator+(Complex a, Complex b);
Complex operator-(Complex a, Complex b);
Complex operator*(Complex a, Complex b);
Complex operator/(Complex a, Complex b);
#endif

There is nothing special in the implementation.

complex.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <string>
#include <cmath>
#include "complex.h"

Complex::Complex(double r, double i) : _real(r),_imag(i) { }

double Complex::abs() const 
{
  return std::sqrt( getReal() * getReal() +
                    getImag() * getImag() );	
}
Complex Complex::operator~() const
{
  return Complex( getReal(), -getImag());	
}

Complex operator+(Complex a, Complex b)
{
  return Complex(a.getReal() + b.getReal(),
  	         a.getImag() + b.getImag() );
}
Complex operator-(Complex a, Complex b)
{
  return Complex(a.getReal() - b.getReal(),
	         a.getImag() - b.getImag() );
}
Complex operator*(Complex a, Complex b)
{
  /* Original faulty implementation (still in the video) 
  double r = a.getReal() * b.getImag()
            -a.getImag() * b.getImag();
  double i = a.getReal() * b.getReal() 
           - b.getReal() * a.getImag(); */
 
  // correct implementation 
  double r = a.getReal() * b.getReal() 
           - a.getImag() * b.getImag();
  double i = a.getReal() * b.getImag() 
           + a.getImag() * b.getReal();
  return Complex{r,i};
}
Complex operator/(Compllex a, Complex b)
{
  // just hope, we did not make fault here :)
  double r = ( a.getReal() * b.getReal() 
             + a.getImag() * b.getImag() ) / 
	     ( b.getReal() * b.getReal() 
             + b.getImag() * b.getImag() );
  double i = ( a.getImag() * b.getReal() 
             + a.getReal() * b.getImag() ) / 
   	     ( b.getReal() * b.getReal() 
             + b.getImag() * b.getImag() );
  return Complex{r,i};
}
std::string Complex::to_string() const
{
  std::string res;
  if (getReal() < 0)
    res += "(";
  res += std::to_string(getReal());
  if (getReal() < 0)
    res += ")";
		
  res += "+";
  if (getImag() < 0)
    res += "(";
  res += std::to_string(getImag());
  if (getImag() < 0)
    res += ")";
  res += "i";

  return res;
}

The expected output:

$ g++ -Wall -Wextra main.cpp complex.cpp
$ ./a.out 
your mark is 2
your mark is 3
your mark is 4
your mark is 5

Congratulation! You just got excellent mark!

Do not hesitate to aske Anett or me if you have question(s).


Financed from the financial support ELTE won from the Higher Education Restructuring Fund of the Hungarian Government.