diff --git a/exercises_pdfs/exo-templates.pdf b/exercises_pdfs/exo-templates.pdf index 92c0a96..f877851 100644 Binary files a/exercises_pdfs/exo-templates.pdf and b/exercises_pdfs/exo-templates.pdf differ diff --git a/solutions/CMakeLists.txt b/solutions/CMakeLists.txt index 91cf5cc..f6018de 100644 --- a/solutions/CMakeLists.txt +++ b/solutions/CMakeLists.txt @@ -1,9 +1,10 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(chapter-01) add_subdirectory(chapter-02) add_subdirectory(chapter-03) add_subdirectory(chapter-04) add_subdirectory(chapter-05) add_subdirectory(chapter-06) add_subdirectory(chapter-07) +add_subdirectory(chapter-08) diff --git a/solutions/chapter-08/CMakeLists.txt b/solutions/chapter-08/CMakeLists.txt new file mode 100644 index 0000000..fb07bc2 --- /dev/null +++ b/solutions/chapter-08/CMakeLists.txt @@ -0,0 +1,12 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +# The next lines represent a list of all the executables in the folder (chapter) +# There is typically one executable per exercise. +# As you solve exercises, you need to add them here. +# The syntax is add_executable(executable_name source_file_name) + +add_executable(ch-08-ex-01-solution ch-08-ex-01-solution.cpp) +add_executable(ch-08-ex-02-solution ch-08-ex-02-solution.cpp) +add_executable(ch-08-ex-03-solution ch-08-ex-03-solution.cpp ComplexNumber.cpp) +add_executable(ch-08-ex-04-solution ch-08-ex-04-solution.cpp Point2d.cpp) +add_executable(exo-templates-solution exo-templates-solution.cpp) diff --git a/solutions/chapter-08/ComplexNumber.cpp b/solutions/chapter-08/ComplexNumber.cpp new file mode 100644 index 0000000..58dc752 --- /dev/null +++ b/solutions/chapter-08/ComplexNumber.cpp @@ -0,0 +1,134 @@ +#include "ComplexNumber.hpp" + +#include + +// Override default constructor +// Set real and imaginary parts to zero +ComplexNumber::ComplexNumber() { + mRealPart = 0.0; + mImaginaryPart = 0.0; +} + +// Constructor that sets complex number z=x+iy +ComplexNumber::ComplexNumber(double x, double y) { + mRealPart = x; + mImaginaryPart = y; +} + +// Copy constructor +ComplexNumber::ComplexNumber(const ComplexNumber &c) + : mRealPart(c.GetRealPart()), mImaginaryPart(c.GetImaginaryPart()) {} + +// Constructor for number with only real part +ComplexNumber::ComplexNumber(const double d) + : mRealPart(d), mImaginaryPart(0.0) {} + +// Get methods + +double ComplexNumber::GetRealPart() const { return mRealPart; } + +double ComplexNumber::GetImaginaryPart() const { return mImaginaryPart; } + +// Method for computing the modulus of a +// complex number +double ComplexNumber::CalculateModulus() const { + return sqrt(mRealPart * mRealPart + mImaginaryPart * mImaginaryPart); +} + +// Method for computing the argument of a +// complex number +double ComplexNumber::CalculateArgument() const { + return atan2(mImaginaryPart, mRealPart); +} + +// Method for raising complex number to the power n +// using De Moivre’s theorem - first complex +// number must be converted to polar form +ComplexNumber ComplexNumber::CalculatePower(double n) const { + double modulus = CalculateModulus(); + double argument = CalculateArgument(); + double mod_of_result = pow(modulus, n); + double arg_of_result = argument * n; + double real_part = mod_of_result * cos(arg_of_result); + double imag_part = mod_of_result * sin(arg_of_result); + ComplexNumber z(real_part, imag_part); + + return z; +} + +// Computes the complex conjugate +ComplexNumber ComplexNumber::CalculateConjugate() const { + return ComplexNumber(mRealPart, -mImaginaryPart); +} + +void ComplexNumber::SetConjugate() { mImaginaryPart = -mImaginaryPart; } + +// Overloading the = (assignment) operator +ComplexNumber &ComplexNumber::operator=(const ComplexNumber &z) { + mRealPart = z.mRealPart; + mImaginaryPart = z.mImaginaryPart; + return *this; +} + +// Overloading the unary - operator +ComplexNumber ComplexNumber::operator-() const { + ComplexNumber w; + w.mRealPart = -mRealPart; + w.mImaginaryPart = -mImaginaryPart; + return w; +} + +// Overloading the binary + operator +ComplexNumber ComplexNumber::operator+(const ComplexNumber &z) const { + ComplexNumber w; + w.mRealPart = mRealPart + z.mRealPart; + w.mImaginaryPart = mImaginaryPart + z.mImaginaryPart; + return w; +} + +// Overloading the binary - operator +ComplexNumber ComplexNumber::operator-(const ComplexNumber &z) const { + ComplexNumber w; + w.mRealPart = mRealPart - z.mRealPart; + w.mImaginaryPart = mImaginaryPart - z.mImaginaryPart; + return w; +} + +ComplexNumber ComplexNumber::operator*(const double d) const { + ComplexNumber t(mRealPart * d, mImaginaryPart * d); + + return t; +} + +ComplexNumber ComplexNumber::operator*(const ComplexNumber &z) const { + return ComplexNumber( + mRealPart * z.GetRealPart() - mImaginaryPart * z.GetImaginaryPart(), + mImaginaryPart * z.GetRealPart() + mRealPart * z.GetImaginaryPart()); +} + +// Overloading the insertion << operator +std::ostream &operator<<(std::ostream &output, const ComplexNumber &z) { + // Format as "(a + bi)" or as "(a - bi)" + output << "(" << z.mRealPart << " "; + if (z.mImaginaryPart >= 0.0) { + output << "+ " << z.mImaginaryPart << "i)"; + } else { + // z.mImaginaryPart < 0.0 + // Replace + with minus sign + output << "- " << -z.mImaginaryPart << "i)"; + } + + return output; +} + +double RealPart(const ComplexNumber &z) { return z.mRealPart; } + +double ImaginaryPart(const ComplexNumber &z) { return z.mImaginaryPart; } + +bool ComplexNumber::operator<(const ComplexNumber &z) const { + return (std::sqrt(mRealPart * mRealPart + mImaginaryPart * mImaginaryPart) < + std::sqrt(RealPart(z) * RealPart(z) + + ImaginaryPart(z) * ImaginaryPart(z)) + ? true + : false); +} diff --git a/solutions/chapter-08/ComplexNumber.hpp b/solutions/chapter-08/ComplexNumber.hpp new file mode 100644 index 0000000..864d27f --- /dev/null +++ b/solutions/chapter-08/ComplexNumber.hpp @@ -0,0 +1,44 @@ +#ifndef COMPLEXNUMBERHEADERDEF +#define COMPLEXNUMBERHEADERDEF + +#include + +class ComplexNumber { +private: + double mRealPart; + double mImaginaryPart; + +public: + // Constructors + ComplexNumber(); + ComplexNumber(double x, double y); + ComplexNumber(const ComplexNumber &c); + ComplexNumber(const double d); + + // Get methods + double GetRealPart() const; + double GetImaginaryPart() const; + + // Public methods + double CalculateModulus() const; + double CalculateArgument() const; + ComplexNumber CalculatePower(double n) const; + ComplexNumber CalculateConjugate() const; + void SetConjugate(); + + // Operators + ComplexNumber &operator=(const ComplexNumber &z); + ComplexNumber operator-() const; + ComplexNumber operator+(const ComplexNumber &z) const; + ComplexNumber operator-(const ComplexNumber &z) const; + ComplexNumber operator*(const double d) const; + ComplexNumber operator*(const ComplexNumber &z) const; + bool operator<(const ComplexNumber &z) const; + + // Friend functions + friend std::ostream &operator<<(std::ostream &output, const ComplexNumber &z); + friend double RealPart(const ComplexNumber &z); + friend double ImaginaryPart(const ComplexNumber &z); +}; + +#endif diff --git a/solutions/chapter-08/Point2d.cpp b/solutions/chapter-08/Point2d.cpp new file mode 100644 index 0000000..1dc315a --- /dev/null +++ b/solutions/chapter-08/Point2d.cpp @@ -0,0 +1,29 @@ +/* + * Point2d.hpp + * + * 2D Point class + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#include "Point2d.hpp" + +double Point2d::sTolerance = 1e-9; + +Point2d::Point2d(double a, double b) : x(a), y(b) {} + +bool Point2d::operator<(const Point2d &other) const { + if ((x - other.x) < -sTolerance) { + return true; + } else if ((x - other.x) > sTolerance) { + return false; + } else if ((y - other.y) < -sTolerance) { + // x == other.x + return true; + } else { + // x == other.x and + // y >= other.y + return false; + } +} diff --git a/solutions/chapter-08/Point2d.hpp b/solutions/chapter-08/Point2d.hpp new file mode 100644 index 0000000..9d4415f --- /dev/null +++ b/solutions/chapter-08/Point2d.hpp @@ -0,0 +1,23 @@ +/* + * Point2d.hpp + * + * 2D Point class + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#ifndef POINT2D_H +#define POINT2D_H + +class Point2d { +public: + double x, y; + static double sTolerance; + + Point2d(double a, double b); + + bool operator<(const Point2d &other) const; +}; + +#endif // POINT2D_H diff --git a/solutions/chapter-08/ch-08-ex-01-solution.cpp b/solutions/chapter-08/ch-08-ex-01-solution.cpp new file mode 100644 index 0000000..d5ee76a --- /dev/null +++ b/solutions/chapter-08/ch-08-ex-01-solution.cpp @@ -0,0 +1,66 @@ +/* + * chapter-08-exercise-01.cpp + * + * Chance of rain + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#include +#include + +template class DoubleVector { +public: + /* + * We use operator[] for assigning to the vector, also implementing + * bounds checking + */ + double &operator[](const int index) { + assert(index < DIM); + assert(index > -1); + return mData[index]; + } + + /* + * We use operator() for accessing elements of the vector + */ + double operator()(const int index) const { + assert(index < DIM); + assert(index > -1); + if (mData[index] >= 0.0 && mData[index] <= 1.0) { + return mData[index]; + } else if (mData[index] < 0.0 && mData[index] > -1e-6) { + return 0.0; + } else if (mData[index] >= 1.0 && mData[index] <= (1.0 + 1e-6)) { + return 1.0; + } else { + assert(0); + std::cerr << "Value out of range.\n"; + return -2.0; + } + } + +private: + double mData[DIM]; +}; + +int main(int argc, char *argv[]) { + // The number of days is a constant + const unsigned int N = 5; + DoubleVector prob; + + prob[0] = 0.1; + prob[1] = 1.0 + 5e-7; + prob[2] = 0 - 1.1e-6; + prob[3] = 1.0; + prob[4] = 0 - 1e-8; + + std::cout.precision(10); + for (unsigned int i = 0; i < N; ++i) { + std::cout << prob(i) << "\n"; + } + std::cout << std::endl; + + return 0; +} diff --git a/solutions/chapter-08/ch-08-ex-02-solution.cpp b/solutions/chapter-08/ch-08-ex-02-solution.cpp new file mode 100644 index 0000000..aa7ca3c --- /dev/null +++ b/solutions/chapter-08/ch-08-ex-02-solution.cpp @@ -0,0 +1,32 @@ +/* + * chapter-08-exercise-02.cpp + * + * Homebrew abs function + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#include + +template T absolute(const T x) { return (x >= 0) ? x : -1 * x; } + +// Same thing, but computation is done at compile time. Not possible for double +// type +template struct absolute2 { + const static int val = ((x >= 0) ? x : -1 * x); +}; + +int main(int argc, char *argv[]) { + const int x1 = -2; + const int x2 = -3; + const double x3 = -7.39; + std::cout << "The absolute value of " << x1 << " is " << absolute(x1) + << std::endl; + std::cout << "The absolute value of " << x2 << " is " << absolute2::val + << " (computed at compile-time)" << std::endl; + std::cout << "The absolute value of " << x3 << " is " << absolute(x3) + << std::endl; + + return 0; +} diff --git a/solutions/chapter-08/ch-08-ex-03-solution.cpp b/solutions/chapter-08/ch-08-ex-03-solution.cpp new file mode 100644 index 0000000..9b3046e --- /dev/null +++ b/solutions/chapter-08/ch-08-ex-03-solution.cpp @@ -0,0 +1,61 @@ +/* + * chapter-08-exercise-03.cpp + * + * STL vector of complex numbers + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#include +#include +#include +#include + +#include "ComplexNumber.hpp" + +int main(int argc, char *argv[]) { + std::vector numbers; + + numbers.reserve(6); + numbers.push_back(ComplexNumber(1.0, 2.0)); + numbers.push_back(ComplexNumber(2.0, -1.0)); + numbers.push_back(ComplexNumber(0.0, 0.0)); + + std::cout << "Length of vector is " << numbers.size() << "\n"; + std::cout << "Entries of vector are\n"; + for (int i = 0; i < 3; i++) { + std::cout << numbers[i] << "\n"; + } + + std::vector::const_iterator c; + for (c = numbers.begin(); c != numbers.end(); c++) { + std::cout << *c << "\n"; + } + + numbers.insert(numbers.begin(), ComplexNumber(5.0, 5.0)); + numbers.insert(numbers.begin() + 1, ComplexNumber(-1.1, -2.0)); + numbers.push_back(ComplexNumber(0.0, -2.0)); + + std::cout << "Length of vector is " << numbers.size() << "\n"; + std::cout << "Entries of vector are\n"; + for (c = numbers.begin(); c != numbers.end(); c++) { + std::cout << *c << "\n"; + } + + numbers.erase(numbers.begin() + 3, numbers.end()); + std::cout << "Length of vector is " << numbers.size() << "\n"; + std::cout << "Entries of vector are\n"; + for (c = numbers.begin(); c != numbers.end(); c++) { + std::cout << *c << "\n"; + } + + std::sort(numbers.begin(), numbers.end()); + std::cout << "Length of vector is " << numbers.size() << "\n"; + std::cout << "Entries of vector are\n"; + for (c = numbers.begin(); c != numbers.end(); c++) { + std::cout << *c << "\n"; + } + + return 0; +} diff --git a/solutions/chapter-08/ch-08-ex-04-solution.cpp b/solutions/chapter-08/ch-08-ex-04-solution.cpp new file mode 100644 index 0000000..8189766 --- /dev/null +++ b/solutions/chapter-08/ch-08-ex-04-solution.cpp @@ -0,0 +1,37 @@ +/* + * chapter-08-exercise-04.cpp + * + * STL and 2D Point class + * + * Created on: Nov 1, 2012 + * Author: Radu Popescu + */ + +#include +#include + +#include "Point2d.hpp" + +int main(int argc, char *argv[]) { + std::set points; + + // Set the tolerance for the comparison operator of Point2d + Point2d::sTolerance = 1e-6; + + Point2d origin(0.0, 0.0); + + points.insert(origin); + points.insert(Point2d(-2.0, 1.0)); + points.insert(Point2d(-2.0, -5.0)); + points.insert(Point2d(0.0, 0.0)); + + std::cout << "Number of points in set = " << points.size() << "\n"; + + std::set::const_iterator c; + std::cout.precision(3); + for (c = points.begin(); c != points.end(); c++) { + std::cout << c->x << " " << c->y << "\n"; + } + + return 0; +} diff --git a/solutions/chapter-08/exo-templates-solution.cpp b/solutions/chapter-08/exo-templates-solution.cpp new file mode 100644 index 0000000..fa32b76 --- /dev/null +++ b/solutions/chapter-08/exo-templates-solution.cpp @@ -0,0 +1,124 @@ +#include +#include + +// I have to declare in advance the daughter class +// This is called "forward" declaration +template class BoxTyped; + +// The mother class of any TypedBox +class Box { +public: + Box(){}; + virtual ~Box(){}; + // the type is not known to the mother: needs to be pure virtual + virtual const std::type_info &type() = 0; +}; + +template class BoxTyped : public Box { +public: + // Variadic template declaration of the construction arguments of typed box + // Allows to forward any number and any type for the constructor of 'T' + // This is generic code to the extreme. + template BoxTyped(Args... args) { + this->ptr = new T(args...); + }; + // do not forget to delete memory + ~BoxTyped() { delete ptr; }; + + // The operator '=' can receive a parameter of type T + // to assign the value to the one stored by the box + T &operator=(const T &a) { + assert(ptr != nullptr); + *ptr = a; + return *ptr; + }; + // can access the value pointed by the pointer: de-reference operation + T &operator*() { return *ptr; }; + // Can return type information thanks to the typeid function + // unlike for the mother class Box we know have access to T + const std::type_info &type() override { return typeid(T); } + +private: + // the raw pointer to store our boxed value + T *ptr; +}; + +// This is a generic object, which is not templated and which +// will benefit for the mechanisms of the Box. +class Any { +public: + // Conctructing an 'Any' object can be done with any + // other type: template constructor + template Any(const T &a) { + // allocate and construct a BoxTyped + BoxTyped *ptr = new BoxTyped(a); + // We have to dereference twice + // *ptr type is: BoxTyped& + // **ptr calls the operator '*' of BoxTyped => type is: T& + **ptr = a; + // save it as a non templated Box is the member of 'Any' + this->ptr = ptr; + } + // do not forget to clean memory + ~Any() { delete ptr; } + + // almost the same as the constuctor + template Any &operator=(const T &a) { + BoxTyped *ptr = new BoxTyped(); + ptr = a; + this->ptr = ptr; + return *this; + } + + // to attempt to cast an object to the stored type + template T &cast() { + // if the type 'T' is the the one of the stored object: error!! + return *(dynamic_cast &>(*this->ptr)); + } + // this is just a shortcut to the type info + const std::type_info &type() { return ptr->type(); } + +private: + // member storing the box + Box *ptr; +}; + +//////////////////////////////////////////////////////////////// +// dummy classes to test the possibilities of Box and Any +// in the main + +class A {}; +class B : public A { + +public: + B(int a){}; +}; + +//////////////////////////////////////////////////////////////// + +int main() { + + // Boxing an int + BoxTyped box; + *box = 2; + // manipulate as a Box to proove it works + Box &anonymous_box = box; + std::cout << anonymous_box.type().name() << std::endl; + + // Boxing an object of type B + BoxTyped box2(B{2}); + // manipulate as a Box to proove it works + Box &anonymous_box2 = box2; + std::cout << anonymous_box.type().name() << std::endl; + + // Use completely anonymous objects to store our values + Any i = 2; + Any a = A{}; + Any b = B{2}; + std::cerr << "i type: " << i.type().name() << std::endl; + std::cerr << "a type: " << a.type().name() << std::endl; + std::cerr << "b type: " << b.type().name() << std::endl; + std::cerr << "i cast: " << i.cast() << std::endl; + std::cerr << "a cast: " << &a.cast() << std::endl; + std::cerr << "b cast: " << &b.cast() << std::endl; // this one will fail +}