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";
+}