diff --git a/day5/examples/polymorphic/1_virtual/CMakeLists.txt b/day5/examples/polymorphic/1_virtual/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7bd6febb57f29f1471e810ebc72e10c72960ff96 --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(polymorphic_w_virtual CXX) +set(CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set(Headers Point.hh Shape.hh Polygon.hh Triangle.hh Circle.hh) +add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc ${Headers}) + diff --git a/day5/examples/polymorphic/1_virtual/Circle.cc b/day5/examples/polymorphic/1_virtual/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..c3a14b772831b7ac34cdc5ccede99aa0840f56ec --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Circle.cc @@ -0,0 +1,31 @@ +#include "Circle.hh" + +constexpr double pi = 3.141592653589793; + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +std::string Circle::name() const +{ + return "Circle"; +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/day5/examples/polymorphic/1_virtual/Circle.hh b/day5/examples/polymorphic/1_virtual/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..4f1148bb24b6adfa1bfc04a7c118cb1a51a27d0c --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Circle.hh @@ -0,0 +1,27 @@ +#ifndef Circle_HH +#define Circle_HH +#include "Point.hh" +#include "Shape.hh" +#include <string> + +class Circle : public Shape { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + void rotate(double phi) override; + void translate(Point p) override; + double area() const override; + double perimeter() const override; + inline double circumference() const { return perimeter(); } + std::string name() const override; + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + +#endif diff --git a/day5/examples/polymorphic/1_virtual/Point.cc b/day5/examples/polymorphic/1_virtual/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Point.cc @@ -0,0 +1,53 @@ +#include "Point.hh" +#include <iostream> + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} diff --git a/day5/examples/polymorphic/1_virtual/Point.hh b/day5/examples/polymorphic/1_virtual/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Point.hh @@ -0,0 +1,23 @@ +#ifndef Point_HH +#define Point_HH +class ostream; +struct Point { + double X = 0, Y = 0; + Point() = default; + Point(const Point&) = default; + Point(Point&&) = default; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +Point operator*(double f, const Point& p); +ostream& operator<<(ostream& os, const Point& p); + +#endif diff --git a/day5/examples/polymorphic/1_virtual/Polygon.hh b/day5/examples/polymorphic/1_virtual/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..6e321bfe5f253e56377b59c16c1095612e6385f8 --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Polygon.hh @@ -0,0 +1,49 @@ +#ifndef Polygon_HH +#define Polygon_HH +#include <array> +#include <cmath> +#include "Shape.hh" + +template <unsigned int NV> +class Polygon : public Shape { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + std::string name() const override { return "Polygon<" +std::to_string(NV)+">"; } + + double perimeter() const override { + double ans = 0; + for (size_t i = 1; i < vertex.size(); ++i) { + ans += sqrt((vertex[i] - vertex[i - 1]) * (vertex[i] - vertex[i - 1])); + } + ans += sqrt((vertex.front() - vertex.back()) * (vertex.front() - vertex.back())); + return ans; + } + void translate(Point p) override { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) override{ + Point center; + for (auto pt : vertex) + center += pt; + center = (1.0 / NV) * center; + double ct = cos(phi), st = sin(phi); + for (auto& pt : vertex) { + auto rel = pt - center; + pt = center + Point(ct * rel.X + st * rel.Y, -st * rel.X + ct * rel.Y); + } + } + + +protected: + std::array<Point, NV> vertex; +}; + +#endif diff --git a/day5/examples/polymorphic/1_virtual/Shape.hh b/day5/examples/polymorphic/1_virtual/Shape.hh new file mode 100644 index 0000000000000000000000000000000000000000..0841248ce7cb103e15ad002f220e1b4c7dc2a45c --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Shape.hh @@ -0,0 +1,17 @@ +#ifndef Shape_HH +#define Shape_HH + +#include "Point.hh" +#include <string> + +class Shape { +public: + virtual ~Shape() = default; + virtual void rotate(double) = 0; + virtual void translate(Point) = 0; + virtual double area() const = 0; + virtual double perimeter() const = 0; + virtual std::string name() const = 0; +}; + +#endif diff --git a/day5/examples/polymorphic/1_virtual/Triangle.cc b/day5/examples/polymorphic/1_virtual/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..f9fd2e923b2d43de3e2d2c86780f73f4e2c27a1c --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Triangle.cc @@ -0,0 +1,23 @@ +#include "Triangle.hh" +#include <cmath> +// A "static" function in a source file (not a static member function of a class) +// is meant to be visible only in the same translation unit, most often just that +// source file. +static inline double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) + : Polygon<3>{} +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + double s = 0.5 * perimeter(); + double a = sqrt(sqr(vertex[0] - vertex[1])); + double b = sqrt(sqr(vertex[0] - vertex[2])); + double c = sqrt(sqr(vertex[1] - vertex[2])); + return sqrt(s * (s - a) * (s - b) * (s - c)); +} diff --git a/day5/examples/polymorphic/1_virtual/Triangle.hh b/day5/examples/polymorphic/1_virtual/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..4879bc3dc213f30338385e503055b138d43e2c1a --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/Triangle.hh @@ -0,0 +1,14 @@ +#include "Polygon.hh" +#include <string> + +class Triangle : public Polygon<3> { +public: + Triangle() = default; + Triangle(Point p1, Point p2, Point p3); + Triangle(const Triangle&) = default; + Triangle(Triangle&&) = default; + Triangle& operator=(const Triangle&) = default; + Triangle& operator=(Triangle&&) = default; + double area() const override; + std::string name() const override { return "Triangle"; } +}; diff --git a/day5/examples/polymorphic/1_virtual/main.cc b/day5/examples/polymorphic/1_virtual/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..3ed8c05b1c06f020bb1ae5f4b2022d38a82df140 --- /dev/null +++ b/day5/examples/polymorphic/1_virtual/main.cc @@ -0,0 +1,82 @@ +#include "Circle.hh" +#include "Triangle.hh" +#include <chrono> +#include <random> +#include <iostream> +#include <vector> +#include <memory> + +using element_type = std::unique_ptr<Shape>; +constexpr auto N = 100000ul; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +auto name(const std::vector<element_type>& v, size_t i) +{ + return v[i]->name(); +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + return v[i]->area(); +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(std::make_unique<Circle>(radius, centrepos)); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(std::make_unique<Triangle>(v1, v2, v3)); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +int main() +{ + std::vector<element_type> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +} diff --git a/day5/examples/polymorphic/2_any/CMakeLists.txt b/day5/examples/polymorphic/2_any/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc560625780ee2054e381df8ae532956e6a3de0b --- /dev/null +++ b/day5/examples/polymorphic/2_any/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(polymorphic_w_any CXX) +set(CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set(Headers Point.hh Polygon.hh Triangle.hh Circle.hh) +add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc ${Headers}) + diff --git a/day5/examples/polymorphic/2_any/Circle.cc b/day5/examples/polymorphic/2_any/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ae0fa5582a3385e0d4f1c1480d956797f994e46 --- /dev/null +++ b/day5/examples/polymorphic/2_any/Circle.cc @@ -0,0 +1,26 @@ +#include "Circle.hh" + +constexpr double pi = 3.141592653589793; + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/day5/examples/polymorphic/2_any/Circle.hh b/day5/examples/polymorphic/2_any/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..adee3d74ca3c027cd74ddd180782fb4aed86fef9 --- /dev/null +++ b/day5/examples/polymorphic/2_any/Circle.hh @@ -0,0 +1,26 @@ +#ifndef Circle_HH +#define Circle_HH +#include "Point.hh" +#include <string> + +class Circle { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + std::string name() const { return "Circle"; } + void rotate(double phi); + void translate(Point p); + double area() const; + double perimeter() const; + inline double circumference() const { return perimeter(); } + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + +#endif diff --git a/day5/examples/polymorphic/2_any/Point.cc b/day5/examples/polymorphic/2_any/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/day5/examples/polymorphic/2_any/Point.cc @@ -0,0 +1,53 @@ +#include "Point.hh" +#include <iostream> + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} diff --git a/day5/examples/polymorphic/2_any/Point.hh b/day5/examples/polymorphic/2_any/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/day5/examples/polymorphic/2_any/Point.hh @@ -0,0 +1,23 @@ +#ifndef Point_HH +#define Point_HH +class ostream; +struct Point { + double X = 0, Y = 0; + Point() = default; + Point(const Point&) = default; + Point(Point&&) = default; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +Point operator*(double f, const Point& p); +ostream& operator<<(ostream& os, const Point& p); + +#endif diff --git a/day5/examples/polymorphic/2_any/Polygon.hh b/day5/examples/polymorphic/2_any/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..8c71504ae0e64fe47441651b1a339997f1c2676c --- /dev/null +++ b/day5/examples/polymorphic/2_any/Polygon.hh @@ -0,0 +1,53 @@ +#ifndef Polygon_HH +#define Polygon_HH +#include "Point.hh" +#include <array> +#include <cmath> +#include <string> + +template <unsigned int NV> +class Polygon { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); + +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + std::string name() const { return "Polygon<" + std::to_string(NV) + ">"; } + + double perimeter() const + { + double ans = 0; + for (size_t i = 1; i < vertex.size(); ++i) { + ans += sqrt((vertex[i] - vertex[i - 1]) * (vertex[i] - vertex[i - 1])); + } + ans += sqrt((vertex.front() - vertex.back()) * (vertex.front() - vertex.back())); + return ans; + } + void translate(Point p) + { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) + { + Point center; + for (auto pt : vertex) + center += pt; + center = (1.0 / NV) * center; + double ct = cos(phi), st = sin(phi); + for (auto& pt : vertex) { + auto rel = pt - center; + pt = center + Point(ct * rel.X + st * rel.Y, -st * rel.X + ct * rel.Y); + } + } + +protected: + std::array<Point, NV> vertex; +}; + +#endif diff --git a/day5/examples/polymorphic/2_any/Triangle.cc b/day5/examples/polymorphic/2_any/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..b27b4ae583252f7ad306f65146f668e1f49adbba --- /dev/null +++ b/day5/examples/polymorphic/2_any/Triangle.cc @@ -0,0 +1,22 @@ +#include "Triangle.hh" +#include <cmath> +// A "static" function in a source file (not a static member function of a class) +// is meant to be visible only in the same translation unit, most often just that +// source file. +static inline double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + double s = 0.5 * perimeter(); + double a = sqrt(sqr(vertex[0] - vertex[1])); + double b = sqrt(sqr(vertex[0] - vertex[2])); + double c = sqrt(sqr(vertex[1] - vertex[2])); + return sqrt(s * (s - a) * (s - b) * (s - c)); +} diff --git a/day5/examples/polymorphic/2_any/Triangle.hh b/day5/examples/polymorphic/2_any/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..c360fc50f97a275711454e8fe1eda0c0680b9fbe --- /dev/null +++ b/day5/examples/polymorphic/2_any/Triangle.hh @@ -0,0 +1,9 @@ +#include "Polygon.hh" + +class Triangle : public Polygon<3> { +public: + using Polygon<3>::Polygon; + using Polygon<3>::operator=; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; diff --git a/day5/examples/polymorphic/2_any/main.cc b/day5/examples/polymorphic/2_any/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..7bbab1e58c41f6d88dbd02fef9b19bb6e0a86efc --- /dev/null +++ b/day5/examples/polymorphic/2_any/main.cc @@ -0,0 +1,91 @@ +#include "Circle.hh" +#include "Triangle.hh" +#include <chrono> +#include <random> +#include <iostream> +#include <vector> +#include <any> + +constexpr auto N = 100000ul; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +using element_type = std::any; + +auto name(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).name(); + } else { + return std::any_cast<Circle>(v[i]).name(); + } +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + if (v[i].type() == typeid(Triangle)) { + return std::any_cast<Triangle>(v[i]).area(); + } else { + return std::any_cast<Circle>(v[i]).area(); + } +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Circle>{}, radius, centrepos); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Triangle>{}, v1, v2, v3); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +int main() +{ + std::vector<element_type> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +} diff --git a/day5/examples/polymorphic/3_variant/CMakeLists.txt b/day5/examples/polymorphic/3_variant/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f48910dbc593a0d8cc549575877158bac95aa794 --- /dev/null +++ b/day5/examples/polymorphic/3_variant/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(polymorphic_w_variant CXX) +set(CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set(Headers Point.hh Polygon.hh Triangle.hh Circle.hh) +add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc ${Headers}) + diff --git a/day5/examples/polymorphic/3_variant/Circle.cc b/day5/examples/polymorphic/3_variant/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ae0fa5582a3385e0d4f1c1480d956797f994e46 --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Circle.cc @@ -0,0 +1,26 @@ +#include "Circle.hh" + +constexpr double pi = 3.141592653589793; + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/day5/examples/polymorphic/3_variant/Circle.hh b/day5/examples/polymorphic/3_variant/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..adee3d74ca3c027cd74ddd180782fb4aed86fef9 --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Circle.hh @@ -0,0 +1,26 @@ +#ifndef Circle_HH +#define Circle_HH +#include "Point.hh" +#include <string> + +class Circle { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + std::string name() const { return "Circle"; } + void rotate(double phi); + void translate(Point p); + double area() const; + double perimeter() const; + inline double circumference() const { return perimeter(); } + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + +#endif diff --git a/day5/examples/polymorphic/3_variant/Point.cc b/day5/examples/polymorphic/3_variant/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Point.cc @@ -0,0 +1,53 @@ +#include "Point.hh" +#include <iostream> + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} diff --git a/day5/examples/polymorphic/3_variant/Point.hh b/day5/examples/polymorphic/3_variant/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Point.hh @@ -0,0 +1,23 @@ +#ifndef Point_HH +#define Point_HH +class ostream; +struct Point { + double X = 0, Y = 0; + Point() = default; + Point(const Point&) = default; + Point(Point&&) = default; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +Point operator*(double f, const Point& p); +ostream& operator<<(ostream& os, const Point& p); + +#endif diff --git a/day5/examples/polymorphic/3_variant/Polygon.hh b/day5/examples/polymorphic/3_variant/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..8c71504ae0e64fe47441651b1a339997f1c2676c --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Polygon.hh @@ -0,0 +1,53 @@ +#ifndef Polygon_HH +#define Polygon_HH +#include "Point.hh" +#include <array> +#include <cmath> +#include <string> + +template <unsigned int NV> +class Polygon { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); + +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + std::string name() const { return "Polygon<" + std::to_string(NV) + ">"; } + + double perimeter() const + { + double ans = 0; + for (size_t i = 1; i < vertex.size(); ++i) { + ans += sqrt((vertex[i] - vertex[i - 1]) * (vertex[i] - vertex[i - 1])); + } + ans += sqrt((vertex.front() - vertex.back()) * (vertex.front() - vertex.back())); + return ans; + } + void translate(Point p) + { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) + { + Point center; + for (auto pt : vertex) + center += pt; + center = (1.0 / NV) * center; + double ct = cos(phi), st = sin(phi); + for (auto& pt : vertex) { + auto rel = pt - center; + pt = center + Point(ct * rel.X + st * rel.Y, -st * rel.X + ct * rel.Y); + } + } + +protected: + std::array<Point, NV> vertex; +}; + +#endif diff --git a/day5/examples/polymorphic/3_variant/Triangle.cc b/day5/examples/polymorphic/3_variant/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..b27b4ae583252f7ad306f65146f668e1f49adbba --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Triangle.cc @@ -0,0 +1,22 @@ +#include "Triangle.hh" +#include <cmath> +// A "static" function in a source file (not a static member function of a class) +// is meant to be visible only in the same translation unit, most often just that +// source file. +static inline double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + double s = 0.5 * perimeter(); + double a = sqrt(sqr(vertex[0] - vertex[1])); + double b = sqrt(sqr(vertex[0] - vertex[2])); + double c = sqrt(sqr(vertex[1] - vertex[2])); + return sqrt(s * (s - a) * (s - b) * (s - c)); +} diff --git a/day5/examples/polymorphic/3_variant/Triangle.hh b/day5/examples/polymorphic/3_variant/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..c360fc50f97a275711454e8fe1eda0c0680b9fbe --- /dev/null +++ b/day5/examples/polymorphic/3_variant/Triangle.hh @@ -0,0 +1,9 @@ +#include "Polygon.hh" + +class Triangle : public Polygon<3> { +public: + using Polygon<3>::Polygon; + using Polygon<3>::operator=; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; diff --git a/day5/examples/polymorphic/3_variant/main.cc b/day5/examples/polymorphic/3_variant/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..310db136ce7f2c30d85a4facc844d70318e124df --- /dev/null +++ b/day5/examples/polymorphic/3_variant/main.cc @@ -0,0 +1,89 @@ +#include "Circle.hh" +#include "Triangle.hh" +#include <chrono> +#include <iostream> +#include <random> +#include <variant> +#include <vector> + +constexpr auto N = 100000ul; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +using element_type = std::variant<Circle, Triangle>; + +auto name(const std::vector<element_type>& v, size_t i) +{ + return std::visit([](auto&& vr) { return vr.name(); }, v[i]); +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + if (auto cir = std::get_if<Circle>(&v[i])) { + return cir->area(); + } else if (auto tri = std::get_if<Triangle>(&v[i])) { + return tri->area(); + } + return 0.; + // return std::visit([](const auto& vr) { return vr.area(); }, v[i]); +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Circle>{}, radius, centrepos); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(std::in_place_type_t<Triangle>{}, v1, v2, v3); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +int main() +{ + std::vector<std::variant<Circle, Triangle>> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +} diff --git a/day5/examples/polymorphic/4_type_erasure/CMakeLists.txt b/day5/examples/polymorphic/4_type_erasure/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f58a3f2841c3143d981355590043e982204bba94 --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) + +project(polymorphic_w_te CXX) +set(CMAKE_CXX_EXTENSIONS OFF) +set (CMAKE_CXX_STANDARD 17) +set (CMAKE_CXX_STANDARD_REQUIRED ON) +set(Headers Point.hh Polygon.hh Triangle.hh Circle.hh) +add_executable(${PROJECT_NAME} Point.cc Triangle.cc Circle.cc main.cc ${Headers}) + diff --git a/day5/examples/polymorphic/4_type_erasure/Circle.cc b/day5/examples/polymorphic/4_type_erasure/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ae0fa5582a3385e0d4f1c1480d956797f994e46 --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Circle.cc @@ -0,0 +1,26 @@ +#include "Circle.hh" + +constexpr double pi = 3.141592653589793; + +Circle::Circle(double rad, const Point& p) + : r{ rad } + , c{ p } +{ +} + +double Circle::area() const +{ + return pi * r * r; +} + +double Circle::perimeter() const +{ + return 2 * pi * r; +} + +void Circle::rotate(double phi) { phi = 0; } + +void Circle::translate(Point p) +{ + c += p; +} diff --git a/day5/examples/polymorphic/4_type_erasure/Circle.hh b/day5/examples/polymorphic/4_type_erasure/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..adee3d74ca3c027cd74ddd180782fb4aed86fef9 --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Circle.hh @@ -0,0 +1,26 @@ +#ifndef Circle_HH +#define Circle_HH +#include "Point.hh" +#include <string> + +class Circle { +public: + Circle() = default; + Circle(double rad, const Point& p); + Circle(const Circle& cir) = default; + Circle(Circle&& cir) = default; + Circle& operator=(const Circle& cir) = default; + Circle& operator=(Circle&& cir) = default; + std::string name() const { return "Circle"; } + void rotate(double phi); + void translate(Point p); + double area() const; + double perimeter() const; + inline double circumference() const { return perimeter(); } + +private: + double r{ 1.0 }; + Point c{}; // Use default constructor of class Point to create c +}; + +#endif diff --git a/day5/examples/polymorphic/4_type_erasure/Point.cc b/day5/examples/polymorphic/4_type_erasure/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Point.cc @@ -0,0 +1,53 @@ +#include "Point.hh" +#include <iostream> + +Point::Point(double x, double y) + : X{ x } + , Y{ y } +{ +} + +Point& Point::operator+=(const Point& p) +{ + X += p.X; + Y += p.Y; + return *this; +} + +Point& Point::operator-=(const Point& p) +{ + X -= p.X; + Y -= p.Y; + return *this; +} + +Point Point::operator+(const Point& p) const +{ + return { X + p.X, Y + p.Y }; +} + +Point Point::operator-(const Point& p) const +{ + return { X - p.X, Y - p.Y }; +} + +double Point::operator*(const Point& p) const +{ + return (X * p.X + Y * p.Y); +} + +Point Point::operator*(double f) const +{ + return { f * X, f * Y }; +} + +Point operator*(double f, const Point& p) +{ + return { f * p.X, f * p.Y }; +} + +std::ostream& operator<<(std::ostream& os, const Point& p) +{ + os << "(" << p.X << ", " << p.Y << ")"; + return os; +} diff --git a/day5/examples/polymorphic/4_type_erasure/Point.hh b/day5/examples/polymorphic/4_type_erasure/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Point.hh @@ -0,0 +1,23 @@ +#ifndef Point_HH +#define Point_HH +class ostream; +struct Point { + double X = 0, Y = 0; + Point() = default; + Point(const Point&) = default; + Point(Point&&) = default; + Point& operator=(const Point& p) = default; + Point& operator=(Point&& p) = default; + Point(double x, double y); + Point& operator+=(const Point& p); + Point& operator-=(const Point& p); + Point operator+(const Point& p) const; + Point operator-(const Point& p) const; + double operator*(const Point& p) const; + Point operator*(double f) const; +}; + +Point operator*(double f, const Point& p); +ostream& operator<<(ostream& os, const Point& p); + +#endif diff --git a/day5/examples/polymorphic/4_type_erasure/Polygon.hh b/day5/examples/polymorphic/4_type_erasure/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..8c71504ae0e64fe47441651b1a339997f1c2676c --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Polygon.hh @@ -0,0 +1,53 @@ +#ifndef Polygon_HH +#define Polygon_HH +#include "Point.hh" +#include <array> +#include <cmath> +#include <string> + +template <unsigned int NV> +class Polygon { + static_assert(NV > 2, "Can't have polygon with less than 3 sides"); + +public: + Polygon() = default; + Polygon(const Polygon&) = default; + Polygon(Polygon&&) = default; + Polygon& operator=(const Polygon& pg) = default; + Polygon& operator=(Polygon&&) = default; + constexpr auto n_vertexes() const { return NV; } + std::string name() const { return "Polygon<" + std::to_string(NV) + ">"; } + + double perimeter() const + { + double ans = 0; + for (size_t i = 1; i < vertex.size(); ++i) { + ans += sqrt((vertex[i] - vertex[i - 1]) * (vertex[i] - vertex[i - 1])); + } + ans += sqrt((vertex.front() - vertex.back()) * (vertex.front() - vertex.back())); + return ans; + } + void translate(Point p) + { + for (auto& pt : vertex) + pt += p; + } + + void rotate(double phi) + { + Point center; + for (auto pt : vertex) + center += pt; + center = (1.0 / NV) * center; + double ct = cos(phi), st = sin(phi); + for (auto& pt : vertex) { + auto rel = pt - center; + pt = center + Point(ct * rel.X + st * rel.Y, -st * rel.X + ct * rel.Y); + } + } + +protected: + std::array<Point, NV> vertex; +}; + +#endif diff --git a/day5/examples/polymorphic/4_type_erasure/Triangle.cc b/day5/examples/polymorphic/4_type_erasure/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..b27b4ae583252f7ad306f65146f668e1f49adbba --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Triangle.cc @@ -0,0 +1,22 @@ +#include "Triangle.hh" +#include <cmath> +// A "static" function in a source file (not a static member function of a class) +// is meant to be visible only in the same translation unit, most often just that +// source file. +static inline double sqr(Point p) { return p * p; } + +Triangle::Triangle(Point p1, Point p2, Point p3) +{ + vertex[0] = p1; + vertex[1] = p2; + vertex[2] = p3; +} + +double Triangle::area() const +{ + double s = 0.5 * perimeter(); + double a = sqrt(sqr(vertex[0] - vertex[1])); + double b = sqrt(sqr(vertex[0] - vertex[2])); + double c = sqrt(sqr(vertex[1] - vertex[2])); + return sqrt(s * (s - a) * (s - b) * (s - c)); +} diff --git a/day5/examples/polymorphic/4_type_erasure/Triangle.hh b/day5/examples/polymorphic/4_type_erasure/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..c360fc50f97a275711454e8fe1eda0c0680b9fbe --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/Triangle.hh @@ -0,0 +1,9 @@ +#include "Polygon.hh" + +class Triangle : public Polygon<3> { +public: + using Polygon<3>::Polygon; + using Polygon<3>::operator=; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; diff --git a/day5/examples/polymorphic/4_type_erasure/main.cc b/day5/examples/polymorphic/4_type_erasure/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..9e15d0ee163a2c0da7403162c405843d67ccc811 --- /dev/null +++ b/day5/examples/polymorphic/4_type_erasure/main.cc @@ -0,0 +1,126 @@ +#include "Circle.hh" +#include "Triangle.hh" +#include <chrono> +#include <iostream> +#include <memory> +#include <random> +#include <vector> + +class Shape { +public: + template <typename T> + Shape(T&& t) + : ptr{ std::make_unique<Model<T>>(std::forward<T>(t)) } + { + } + double get_area() const { return ptr->get_area(); } + std::string get_name() const { return ptr->get_name(); } + +private: + struct Concept { + virtual ~Concept() {} + virtual double get_area() const = 0; + virtual std::string get_name() const = 0; + }; + template <typename T> + struct Model : public Concept { + T m_; + Model(const T& t) + : m_{ t } + { + } + double get_area() const override + { + return area(m_); + } + std::string get_name() const override + { + return name(m_); + } + }; + std::unique_ptr<Concept> ptr; +}; + +auto name(const Shape& s) { return s.get_name(); } +auto area(const Shape& s) { return s.get_area(); } + +constexpr auto N = 100000ul; +std::mt19937_64 engine; +std::discrete_distribution sel{ 0.5, 0.5 }; +std::exponential_distribution length{ 1.0 }; + +using element_type = Shape; + +std::string name(const Circle& c) { return c.name(); } +std::string name(const Triangle& t) { return t.name(); } +auto area(const Circle& c) { return c.area(); } +auto area(const Triangle& t) { return t.area(); } + +auto name(const std::vector<element_type>& v, size_t i) +{ + return name(v[i]); +} + +auto area(const std::vector<element_type>& v, size_t i) +{ + return area(v[i]); +} + +void construct_objects(std::vector<element_type>& v) +{ + for (auto i = 0ul; i < N; ++i) { + auto isel = sel(engine); + switch (isel) { + case 0: { + auto radius = length(engine); + auto centrepos = Point(length(engine), length(engine)); + v.emplace_back(Circle(radius, centrepos)); + break; + } + case 1: { + auto v1 = Point(length(engine), length(engine)); + auto v2 = Point(length(engine), length(engine)); + auto v3 = Point(length(engine), length(engine)); + v.emplace_back(Triangle(v1, v2, v3)); + break; + } + }; + } +} + +void calc_area_all(const std::vector<element_type>& v) +{ + auto max_loc = 0ul; + auto max_area = 0.; + for (size_t i = 0; i < v.size(); ++i) { + auto ar = area(v, i); + if (i < 5) { + std::cout << i << ": " << name(v, i) << " with area " + << ar << "\n"; + } + if (ar > max_area) { + max_loc = i; + } + } + + std::cout << "Largest object: \n"; + auto nm = name(v, max_loc); + auto ar = area(v, max_loc); + std::cout << "Name : " << nm << ", area = " << ar << "\n"; +} + +int main() +{ + std::vector<element_type> shapes; + shapes.reserve(N); + + auto t0 = std::chrono::steady_clock::now(); + construct_objects(shapes); + auto t1 = std::chrono::steady_clock::now(); + calc_area_all(shapes); + auto t2 = std::chrono::steady_clock::now(); + std::cout << "Object creation time for " << N << " objects, " + << std::chrono::duration<double>(t1 - t0).count() << "\n" + << "Area evaluation time for " << N << " objects, " + << std::chrono::duration<double>(t2 - t1).count() << "\n"; +}