diff --git a/day5/ProgrammingInC++_Book.pdf b/day5/ProgrammingInC++_Book.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f05a8a649abc197cb2149ef26d87f1f73e32486f Binary files /dev/null and b/day5/ProgrammingInC++_Book.pdf differ diff --git a/day5/ProgrammingInCXX_d5.pdf b/day5/ProgrammingInCXX_d5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..865b560d569fc61e5b27cd3d2e5f184a5a914578 Binary files /dev/null and b/day5/ProgrammingInCXX_d5.pdf differ diff --git a/day5/examples/TE/PolyVal.cc b/day5/examples/TE/PolyVal.cc new file mode 100644 index 0000000000000000000000000000000000000000..3d338f6c2c4d6da02d265ecc93a7fef2845022d3 --- /dev/null +++ b/day5/examples/TE/PolyVal.cc @@ -0,0 +1,68 @@ +#include <iostream> +#include <memory> +#include <vector> +#include <type_traits> +using namespace std::string_literals; + +void func1(int x) { std::cout << "Integer = " << x << "\n"; } +void func1(double x) { std::cout << "Double = " << x << "\n"; } +void func1(std::string x) { std::cout << "std::string = \"" << x << "\"\n"; } + +class PolyVal { + struct Internal { + virtual ~Internal() noexcept = default; + virtual auto clone() const -> std::unique_ptr<Internal> = 0; + virtual void func1_() const = 0; + }; + template <class T> + struct Wrapped : public Internal { + Wrapped(T ex) : obj{ex} {} + ~Wrapped() noexcept override {} + auto clone() const -> std::unique_ptr<Internal> override + { + return std::make_unique<Wrapped>(obj); + } + void func1_() const override + { + func1(obj); + } + T obj; + }; + +public: + template <class T> + PolyVal(const T& var) : ptr{ std::make_unique<Wrapped<T>>(var) } {} + PolyVal(const PolyVal& other) : ptr { other.ptr->clone() } {} + +private: + friend void func1(const PolyVal& pv) { pv.ptr->func1_(); } + std::unique_ptr<Internal> ptr; +}; + +auto f(int i) -> PolyVal +{ + if (i % 2 == 0) + return { 9 }; + else + return { "Nine"s }; +} + +auto main() -> int +{ + std::vector<PolyVal> v; + v.push_back(1); + v.push_back(2.0); + v.push_back("Green"s); + + for (auto&& elem : v) { + func1(elem); + } + std::cout << "------------\n"; + for (int i = 0; i < 6; ++i) { + std::cout << "Calling function with i = " + << i << " and receiving PolyVal by value\n"; + PolyVal X = f(i); + func1(X); + } +} + diff --git a/day5/examples/addables.cc b/day5/examples/addables.cc new file mode 100644 index 0000000000000000000000000000000000000000..0227058f45b08e0dacf0560f2b70af99cd14c207 --- /dev/null +++ b/day5/examples/addables.cc @@ -0,0 +1,23 @@ +#include <iostream> +#include <string> +#include <concepts> + +template <class T> +concept Addable = requires(T a, T b) { + { a + b } -> std::convertible_to<T>; +}; + +auto aggregate(Addable auto... args) +{ + return (args + ...); +} + +auto main() -> int +{ + using namespace std::string_literals; + std::cout << "aggregate(1, 2, 3, 4, 5) = " + << aggregate(1, 2, 3, 4, 5) << "\n"; + std::cout << R"aw(aggregate("|-"s, "-|"s) = )aw" + << aggregate("|-"s, "-|"s) << "\n"; +} + diff --git a/day5/examples/birthday_problem.cc b/day5/examples/birthday_problem.cc new file mode 100644 index 0000000000000000000000000000000000000000..334e9c2dd9bd3999f2b826d0bac6b78517628cd0 --- /dev/null +++ b/day5/examples/birthday_problem.cc @@ -0,0 +1,69 @@ +#include <random> +#include <iostream> +#include <vector> +#include <algorithm> +#include <ranges> +#include <execution> +#include <limits> +#include <atomic> +#include "CountingIterator.hh" +#include <numeric> +// #include <tbb/scalable_allocator.h> + +/* + * Run it first as it is. Then, try replacing the vector type with the + * scalable allocator type from the Threading Building Blocks (TBB). + * Then do the modification suggested in the function + * probability_for_equal_birthdays(). Run the program in each variant + * and check how long it takes by using the time command: + * + * time executablename [OPTIONS] + * + */ +namespace sr = std::ranges; +namespace sv = std::views; + +template <class T> +// using VectorType = std::vector<T, tbb::scalable_allocator<T>>; +using VectorType = std::vector<T>; + +auto sample_group(size_t n) -> VectorType<int> +{ + VectorType<int> grp(n, 0); + static thread_local std::mt19937_64 eng{ std::random_device{}() }; + static thread_local std::uniform_int_distribution<> dist{ 0, 365 }; + auto birthdays = [&]{ return dist(eng); }; + std::generate(grp.begin(), grp.end(), birthdays); + return grp; +} + +auto probability_for_equal_birthdays(size_t group_size, size_t nexpt = 10'000'000UL) -> double +{ + // transform_reduce(start, end, init, accumulator_op, transform_op); + // That's the normal syntax of transform_reduce. But, there is another + // overload that takes one extra argument at the front: an execution policy. + // Add one extra argument to transform_reduce: std::execution::par which + // specifies a parallel execution policy, and see what happens! + auto nclashes = std::transform_reduce(algo_counter(0UL), algo_counter(nexpt), 0UL, + std::plus<size_t>{}, + [&]([[maybe_unused]] auto counter) { + auto group = sample_group(group_size); + sr::sort(group); + auto newend = std::unique(group.begin(), group.end()); + //group.erase(newend, group.end()); + //if (group.size() != group_size) ++nclashes; + if (newend != group.end()) return 1UL; + return 0UL; + }); + return static_cast<double>(nclashes) / nexpt; +} + +auto main(int argc, char* argv[]) -> int +{ + auto target_group_size = (argc == 1 ? 50UL : std::stoul(argv[1])); + std::cout << "Group size\tShared birthday probability\n\n"; + for (auto gs = 0UL; gs < target_group_size; ++gs) { + std::cout << gs << "\t\t" << probability_for_equal_birthdays(gs) << "\n"; + } +} + diff --git a/day5/examples/dist.cc b/day5/examples/dist.cc new file mode 100644 index 0000000000000000000000000000000000000000..aa3e93e0e8d8c052f5291cc8e9c5fce1b27d6925 --- /dev/null +++ b/day5/examples/dist.cc @@ -0,0 +1,87 @@ +#include <array> +#include <iostream> +#include <iomanip> +#include <cxx20format> +#include <ctre.hpp> + +class Distance { +public: + enum class unit : unsigned { metre = 0U, + kilometre = 1U, + centimetre = 2U }; + +private: + static constexpr auto index(unit u) -> unsigned + { + return static_cast<unsigned>(u); + } + // The conv array has the factors you need to divide + // by to convert a value in a given unit to the + // internal representation, which is always in metres + static constexpr std::array conv { 1.0, 1.0e-3, 100.0 }; + double val {}; // internal, always in metres +public: + Distance() = default; + Distance(const Distance&) = default; + Distance(Distance&&) noexcept = default; + auto operator=(const Distance&) -> Distance& = default; + auto operator=(Distance&&) noexcept -> Distance& = default; + Distance(double v, unit u) + : val { v / conv[index(u)] } + { + } + auto value(unit in_unit) const noexcept { return val * conv[index(in_unit)]; } + void value(double v, unit u) { val = v / conv[index(u)]; } + void str(std::string_view sv); +}; + +auto operator""_m(long double inp) -> Distance { return { static_cast<double>(inp), Distance::unit::metre }; } +auto operator""_km(long double inp) -> Distance { return { static_cast<double>(inp), Distance::unit::kilometre }; } +auto operator""_cm(long double inp) -> Distance { return { static_cast<double>(inp), Distance::unit::centimetre }; } + +auto operator<<(std::ostream& os, const Distance& d) -> std::ostream& +{ + os << d.value(Distance::unit::metre) << "_m"; + return os; +} + +auto operator>>(std::istream& is, Distance& d) -> std::istream& +{ + std::string val; + is >> std::quoted(val); + d.str(val); + return is; +} + +auto main() -> int +{ + Distance d { 4.0, Distance::unit::kilometre }; + std::cout << d << "\n"; + Distance c { 35.4_cm }; + std::cout << c << "\n"; + std::cout << "Enter distance: "; + std::cin >> d; + std::cout << "That's " << d << "\n"; +} + +void Distance::str(std::string_view diststr) +{ + static constexpr ctll::fixed_string trx { + R"(^(-?[0-9]*\.?[0-9]+)(_|[ ]*)(m|metre|metres|cm|centimetre|centimetres|km|kilometre|kilometres)$)" + }; + if (auto m = ctre::match<trx>(diststr); m) { + auto numstr = m.get<1>().to_string(); + auto num = std::stod(numstr); + auto ustr = m.get<3>().to_view(); + if (ustr == "m" or ustr == "metre" or ustr == "metres") { + value(num, unit::metre); + } else if (ustr == "cm" or ustr == "centimetre" or ustr == "centimetres") { + value(num, unit::centimetre); + } else if (ustr == "km" or ustr == "kilometre" or ustr == "kilometres") { + value(num, unit::kilometre); + } + } else { + throw std::runtime_error(format(R"(Bad value "{}" for distance!)", diststr)); + } +} + diff --git a/day5/examples/format1.cc b/day5/examples/format1.cc new file mode 100644 index 0000000000000000000000000000000000000000..ae817bd76a0182b0ae22e9b73d97eaae88515eb0 --- /dev/null +++ b/day5/examples/format1.cc @@ -0,0 +1,18 @@ +#include <cxx20format> +#include <iostream> +#include <string> +#include <cmath> +#include <tuple> + +auto main() -> int +{ + using namespace std; + using namespace std::string_literals; + + for (auto i = 0UL; i < 110UL; ++i) { + std::cout << format( + "i = {0:010d}, E_tot = {2:16.12f}, E_hb = {1:8.4f}\n", + i, exp(cos(1.0 * i)), exp(sin(1.0 * i)) + ); + } +} diff --git a/day5/examples/modules/2_any/with_header_files/CMakeLists.txt b/day5/examples/modules/2_any/with_header_files/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc560625780ee2054e381df8ae532956e6a3de0b --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Circle.cc b/day5/examples/modules/2_any/with_header_files/Circle.cc new file mode 100644 index 0000000000000000000000000000000000000000..6ae0fa5582a3385e0d4f1c1480d956797f994e46 --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Circle.hh b/day5/examples/modules/2_any/with_header_files/Circle.hh new file mode 100644 index 0000000000000000000000000000000000000000..adee3d74ca3c027cd74ddd180782fb4aed86fef9 --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Point.cc b/day5/examples/modules/2_any/with_header_files/Point.cc new file mode 100644 index 0000000000000000000000000000000000000000..74ddec6e7e995d547fd04da729d083a8c6ad1bfc --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Point.hh b/day5/examples/modules/2_any/with_header_files/Point.hh new file mode 100644 index 0000000000000000000000000000000000000000..009c99a6dcfddc155364e2465cc31da8ec785e0f --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Polygon.hh b/day5/examples/modules/2_any/with_header_files/Polygon.hh new file mode 100644 index 0000000000000000000000000000000000000000..8c71504ae0e64fe47441651b1a339997f1c2676c --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Triangle.cc b/day5/examples/modules/2_any/with_header_files/Triangle.cc new file mode 100644 index 0000000000000000000000000000000000000000..b27b4ae583252f7ad306f65146f668e1f49adbba --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/Triangle.hh b/day5/examples/modules/2_any/with_header_files/Triangle.hh new file mode 100644 index 0000000000000000000000000000000000000000..c360fc50f97a275711454e8fe1eda0c0680b9fbe --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_header_files/main.cc b/day5/examples/modules/2_any/with_header_files/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..7bbab1e58c41f6d88dbd02fef9b19bb6e0a86efc --- /dev/null +++ b/day5/examples/modules/2_any/with_header_files/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/modules/2_any/with_modules/CMakeLists.txt b/day5/examples/modules/2_any/with_modules/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..51ba65862b04cceee44ca404d069e41497c469a3 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.26) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP ON) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(polymorphic_with_any) + +add_executable(polyany) +target_sources(polyany + PUBLIC + main.cc +) +target_sources(polyany + PUBLIC + FILE_SET geometry_modules TYPE CXX_MODULES FILES + Point.ixx + Circle.ixx + Polygon.ixx + Triangle.ixx +) + diff --git a/day5/examples/modules/2_any/with_modules/Circle.ixx b/day5/examples/modules/2_any/with_modules/Circle.ixx new file mode 100644 index 0000000000000000000000000000000000000000..4793712c1f61e1ffb7d04a53ff039f4025120880 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/Circle.ixx @@ -0,0 +1,51 @@ +module; +#include <string> +#include <numbers> +export module Circle; +import Point; + +using std::numbers::pi; + +export 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 +}; + + +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/modules/2_any/with_modules/Point.ixx b/day5/examples/modules/2_any/with_modules/Point.ixx new file mode 100644 index 0000000000000000000000000000000000000000..436926852116750d9402f9e9fa808a121cdce786 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/Point.ixx @@ -0,0 +1,73 @@ +export module Point; + +export 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; +}; + +export auto operator*(double f, const Point& p) -> Point; +// export auto operator<<(std::ostream& os, const Point& p) -> std::ostream&; + +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/modules/2_any/with_modules/Polygon.ixx b/day5/examples/modules/2_any/with_modules/Polygon.ixx new file mode 100644 index 0000000000000000000000000000000000000000..43ca1acb177bb4ac5ddbe3c5d3de20a21bb7ee6c --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/Polygon.ixx @@ -0,0 +1,52 @@ +module; +#include <cmath> +#include <array> +#include <string> +export module Polygon; +import Point; + +export 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; } + inline 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; +}; + diff --git a/day5/examples/modules/2_any/with_modules/Triangle.ixx b/day5/examples/modules/2_any/with_modules/Triangle.ixx new file mode 100644 index 0000000000000000000000000000000000000000..94a4b22783e70a43eea42577f21de8d4414b4df0 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/Triangle.ixx @@ -0,0 +1,33 @@ +module; +#include <array> +#include <cmath> +export module Triangle; +import Polygon; +import Point; + +export class Triangle : public Polygon<3U> { +public: + using Polygon<3U>::Polygon; + using Polygon<3U>::operator=; + using Polygon<3U>::name; + Triangle(Point p1, Point p2, Point p3); + double area() const; +}; +// not exported +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/modules/2_any/with_modules/build_with_clang.sh b/day5/examples/modules/2_any/with_modules/build_with_clang.sh new file mode 100755 index 0000000000000000000000000000000000000000..0211cb980d80b3fba0a2c8e628e566ee746e9235 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/build_with_clang.sh @@ -0,0 +1,13 @@ +for m in Point Circle Polygon Triangle; do +echo "Generating CMI for $m" +clang++ -std=c++20 -stdlib=libc++ -fprebuilt-module-path=. --precompile -xc++-module $m.ixx +done +for m in Point Circle Polygon Triangle; do +echo "Generating object file for $m" +clang++ -std=c++20 -c $m.pcm +done +echo "Compiling main.cc -> a.clg" +clang++ -std=c++20 -stdlib=libc++ -fprebuilt-module-path=. -o a.clg main.cc Point.o Circle.o Polygon.o Triangle.o + + + diff --git a/day5/examples/modules/2_any/with_modules/build_with_gcc.sh b/day5/examples/modules/2_any/with_modules/build_with_gcc.sh new file mode 100755 index 0000000000000000000000000000000000000000..702232de8df2f6ab96924e58d9a82bddfd41af0b --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/build_with_gcc.sh @@ -0,0 +1,12 @@ +for m in Point Circle Polygon Triangle; do +echo "Generating CMI and object files for $m" +g++ -std=c++20 -fmodules-ts -xc++ -c $m.ixx +done +echo "Compiling main.cc -> a.gcc" +g++ -std=c++20 -fmodules-ts -o a.gcc main.cc Point.o Circle.o Polygon.o Triangle.o + +echo "GCC module implementation is not in a usable state as of May 2023." +echo "Check if the example builds and runs." + + + diff --git a/day5/examples/modules/2_any/with_modules/clean.sh b/day5/examples/modules/2_any/with_modules/clean.sh new file mode 100755 index 0000000000000000000000000000000000000000..aff1f9ecfd5fea4825d71d4e1af2f0c4ccfe57f6 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/clean.sh @@ -0,0 +1,4 @@ +rm -f *.o *.pcm *.gcm +rm -rf gcm.cache +rm -f a.clg a.gcc + diff --git a/day5/examples/modules/2_any/with_modules/main.cc b/day5/examples/modules/2_any/with_modules/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..c8860ee6e2fc807705193f05c8517e780ec51b99 --- /dev/null +++ b/day5/examples/modules/2_any/with_modules/main.cc @@ -0,0 +1,95 @@ +#include <chrono> +#include <random> +#include <iostream> +#include <vector> +#include <array> +#include <any> +import Point; +import Polygon; +import Circle; +import Triangle; + +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"; +} + +auto main() -> int +{ + 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/modules/advent.cc b/day5/examples/modules/advent.cc new file mode 100644 index 0000000000000000000000000000000000000000..64745f7899380f277d2b28c5c0d1a6ef180b4d1e --- /dev/null +++ b/day5/examples/modules/advent.cc @@ -0,0 +1,36 @@ +/* +Calculates the date of the last advent in a given +year. Usage: + +(i) advent # calculate for current year +(ii) advent 2049 # for 2049 +*/ + +import <chrono>; +import <iostream>; +auto current_year() -> std::chrono::year +{ + using namespace std::chrono; + year_month_day date { floor<days>(system_clock::now()) }; + return date.year(); +} + +auto main(int argc, char* argv[]) -> int +{ + using namespace std::chrono; + using namespace std::chrono_literals; + using Date = year_month_day; + + year Y { argc == 1 ? current_year() : year {std::stoi(argv[1]) }}; + + Date s4 { Y / December / Sunday[4] }; + Date s3 { Y / December / Sunday[3] }; + Date xmas { Y / December / 25d }; + Date lastadv { s4 >= xmas ? s3 : s4 }; + + std::cout << "The last advent for the year " + << static_cast<int>(Y) << " falls on " + << static_cast<unsigned>(lastadv.day()) + << "'th of December.\n"; +} + diff --git a/day5/examples/modules/darray_module/README.md b/day5/examples/modules/darray_module/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9af03921a8e4ad548f693ad50ad2f212bd8667c2 --- /dev/null +++ b/day5/examples/modules/darray_module/README.md @@ -0,0 +1,5 @@ +clang++ -std=c++20 -stdlib=libc++ -fmodules --precompile -xc++-module mymodule.ixx +clang++ -std=c++20 -fmodules -c mymodule.pcm +clang++ -std=c++20 -stdlib=libc++ -fmodules -fprebuilt-module-path=. mymodule.o main.cc + + diff --git a/day5/examples/modules/darray_module/main.cc b/day5/examples/modules/darray_module/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..29ffc819c827dcff1825e6d37d3445d3e8ddde6d --- /dev/null +++ b/day5/examples/modules/darray_module/main.cc @@ -0,0 +1,21 @@ +#include <string> +#include <iostream> +import mymodule; + +auto main() -> int +{ + darray d1 { 1, 2, 3, 4, 5 }; // CTAD works! + //darray<int> d1{1, 2, 3, 4, 5}; + darray<std::string> d2 { "a", "b", "c" }; + // Explicit type required above, since "a", "b" etc are C-strings, and we want C++ strings + auto d3 = d1; //Copy + darray<int> d4 { 10 }; // This and the following line have a subtle difference + darray<int> d5(10); // This is the only situation where constructor calls with {} and () mean different things. + darray<std::string> d6 { std::move(d2) }; // d2 should be empty and d6 must have all data after this. + std::cout << "d1 = " << d1 << "\n"; + std::cout << "d2 = " << d2 << "\n"; + std::cout << "d3 = " << d3 << "\n"; + std::cout << "d4 = " << d4 << "\n"; + std::cout << "d5 = " << d5 << "\n"; + std::cout << "d6 = " << d6 << "\n"; +} diff --git a/day5/examples/modules/darray_module/mymodule.ixx b/day5/examples/modules/darray_module/mymodule.ixx new file mode 100644 index 0000000000000000000000000000000000000000..8c9f3bbab6290aadf88158fbb5395393c1c2c8a5 --- /dev/null +++ b/day5/examples/modules/darray_module/mymodule.ixx @@ -0,0 +1,103 @@ +module; +#include <initializer_list> +#include <iostream> +#include <memory> +export module mymodule; + +export template <class T> +class darray { +private: + // swap function + void swap(darray& oth) + { + std::swap(arr, oth.arr); + std::swap(sz, oth.sz); + } + std::unique_ptr<T[]> arr; + size_t sz = 0; + +public: + // read-only access for array elements + auto operator[](size_t i) const -> T { return arr[i]; } + // read-write access for array elements + auto operator[](size_t i) -> T& { return arr[i]; } + + // This is needed if you want to use range based for loops on your class + auto begin() const -> T* { return arr; } + // This is needed if you want to use range based for loops on your class + auto end() const -> T* { return arr + size(); } + + // returns the size + auto size() const -> decltype(sz) { return sz; } + + // Sums up array and returns the result + auto sum() const -> T + { + T a {}; + for (auto el : (*this)) { + a += el; + } // Use the range based for loop. //Well, why not! + return a; + } + + // Default constructor, also defaulted. + darray() = default; + // Destructor. With storage managed by a unique_ptr, we don't need to delete it. + ~darray() = default; + + // Constructor with a given size + darray(size_t N) + { + if (N != 0) { + arr = std::make_unique<T[]>(N); + sz = N; + } + } + + // Copy constructor + darray(const darray<T>& oth) + { + if (oth.sz != 0) { + sz = oth.sz; + arr = std::make_unique<T[]>(sz); + } + for (size_t i = 0; i < sz; ++i) + arr[i] = oth.arr[i]; + } + darray(darray<T>&& oth) noexcept + { + swap(oth); + } + // Initialiser list constructor + darray(std::initializer_list<T> l) + { + arr = std::make_unique<T[]>(l.size()); + sz = l.size(); + size_t i = 0; + for (auto el : l) + arr[i++] = el; + } + // Assignment operator using the copy and swap idiom + auto operator=(darray d) -> darray& + { + swap(d); + return *this; + } +}; + +// Output operator. No need to make it a friend, since we can use only +// public functions of the class to do all the work! +export template <typename T> +auto operator<<(std::ostream& os, const darray<T>& d) -> std::ostream& +{ + os << '['; + for (size_t i = 0; i < d.size(); ++i) { + os << d[i]; + if (i != (d.size() - 1)) + os << ','; + } + os << ']'; + return os; // This function returns os so that you can write cout << d1 << " other things\n"; + // That is interpreted as (cout << d1) << " other things\n"; +} + diff --git a/day5/examples/modules/feb.cc b/day5/examples/modules/feb.cc new file mode 100644 index 0000000000000000000000000000000000000000..1d7641b6df132948a1fea1f96e35f287a8a12457 --- /dev/null +++ b/day5/examples/modules/feb.cc @@ -0,0 +1,42 @@ +/* +Calculates the years in a given interval, in which +the month of February had 5 Sundays. + +Usage: +(i) feb # Start: current year, End: 100 years from now +(ii) feb 1975 # Start: 1975, End: current year +(iii) feb 2075 # Start: current year, End: 2075 +(iv) feb 1800 2000 # Start 1800, End 2000 + +Build: +clang++ -std=c++20 -stdlib=libc++ feb.cc -o feb + +*/ + +import <chrono>; +import <iostream>; + +auto current_year() -> std::chrono::year +{ + using namespace std::chrono; + year_month_day date { floor<days>(system_clock::now()) }; + return date.year(); +} + +auto main(int argc, char* argv[]) -> int +{ + using namespace std::chrono; + using namespace std::chrono_literals; + auto Y0 { current_year() }; + auto Y1 = Y0 + years{100}; + if (argc > 1) Y1 = year{std::stoi(argv[1])}; + if (argc > 2) Y0 = year{std::stoi(argv[2])}; + if (Y1 < Y0) std::swap(Y1, Y0); + + for (auto y = Y0; y < Y1; ++y) { + auto d = y / February / Sunday[5]; + if (d.ok()) + std::cout << static_cast<int>(y) << "\n"; + } +} + diff --git a/day5/examples/modules/hello_m.cc b/day5/examples/modules/hello_m.cc new file mode 100644 index 0000000000000000000000000000000000000000..f743015337ce7b049038e5ba97b0fca1e08ae90d --- /dev/null +++ b/day5/examples/modules/hello_m.cc @@ -0,0 +1,7 @@ +import <iostream>; + +auto main() -> int +{ + std::cout << "Hello, world!\n"; +} + diff --git a/day5/examples/modules/saxpy/with_header_files/README.md b/day5/examples/modules/saxpy/with_header_files/README.md new file mode 100644 index 0000000000000000000000000000000000000000..373387081bbac91d2934735b2044dfa48e2760b3 --- /dev/null +++ b/day5/examples/modules/saxpy/with_header_files/README.md @@ -0,0 +1,9 @@ +To compile with clang: + +clang++ -std=c++20 -stdlib=libc++ usesaxpy.cc + +With gcc: + +g++ -std=c++20 usesaxpy.cc + + diff --git a/day5/examples/modules/saxpy/with_header_files/saxpy.hh b/day5/examples/modules/saxpy/with_header_files/saxpy.hh new file mode 100644 index 0000000000000000000000000000000000000000..b52279919867975fe7df82da673a55b6e071eae5 --- /dev/null +++ b/day5/examples/modules/saxpy/with_header_files/saxpy.hh @@ -0,0 +1,21 @@ +#ifndef SAXPY_HH +#define SAXPY_HH +#include <algorithm> +#include <span> + +template <class T> +concept Number = std::floating_point<T> + or std::integral<T>; +template <Number T> +auto saxpy(T a, std::span<const T> x, + std::span<const T> y, + std::span<T> z) +{ + std::transform(x.begin(), x.end(), + y.begin(), z.begin(), + [a](T X, T Y) { + return a * X + Y; + }); +} +#endif + diff --git a/day5/examples/modules/saxpy/with_header_files/usesaxpy.cc b/day5/examples/modules/saxpy/with_header_files/usesaxpy.cc new file mode 100644 index 0000000000000000000000000000000000000000..2a5386031e9b5790e757398d286489e9282fb5ed --- /dev/null +++ b/day5/examples/modules/saxpy/with_header_files/usesaxpy.cc @@ -0,0 +1,18 @@ +#include <iostream> +#include <array> +#include <vector> +#include <span> +#include "saxpy.hh" + +auto main() -> int +{ + using namespace std; + const array inp1 { 1., 2., 3., 4., 5. }; + const array inp2 { 9., 8., 7., 6., 5. }; + vector outp(inp1.size(), 0.); + + saxpy(10., {inp1}, {inp2}, {outp}); + for (auto x : outp) cout << x << "\n"; + cout << ":::::::::::::::::::::\n"; +} + diff --git a/day5/examples/modules/saxpy/with_modules/CMakeLists.txt b/day5/examples/modules/saxpy/with_modules/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a24ef40a14e489a3db17e6e75104f22245451f1 --- /dev/null +++ b/day5/examples/modules/saxpy/with_modules/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.26) + +set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a") +set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP ON) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(use_saxpy-example) + +add_executable(use_saxpy) +target_sources(use_saxpy + PUBLIC + usesaxpy.cc +) +target_sources(use_saxpy + PUBLIC + FILE_SET saxpy_module TYPE CXX_MODULES FILES + saxpy.ixx +) + diff --git a/day5/examples/modules/saxpy/with_modules/README.md b/day5/examples/modules/saxpy/with_modules/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ffa375852386669c02b67a68c64bd87d76507d06 --- /dev/null +++ b/day5/examples/modules/saxpy/with_modules/README.md @@ -0,0 +1,25 @@ +Compile with clang: + +clang++ -std=c++20 -stdlib=libc++ -xc++-module --precompile saxpy.ixx +clang++ -std=c++20 -c saxpy.pcm -o saxpy.o +clang++ -std=c++20 -stdlib=libc++ -fprebuilt-module-path=. usesaxpy.cc saxpy.o -o usesaxpy.clg + +If you want to compile with GCC, change imports involving standard +library header units to includes, and then do this: + +g++ -std=c++20 -fmodules-ts -xc++ -c saxpy.ixx +g++ -std=c++20 -fmodules-ts saxpy.o usesaxpy.cc -o usesaxpy.gcc + +It is also possible to build this program by treating it as +a CMake project, using the experimental support for modules. +As of 4 May 2023, this only works using clang 16.0, cmake 3.26.3 +and ninja 1.11. Observe the unusual lines in the CMakeLists.txt +file. Hopefully, they will not be required for long. The following +steps will produce an executable called use_saxpy inside +the build directory: + +mkdir build +cd build +CC=clang CXX=clang++ cmake -DCMAKE_GENERATOR=Ninja .. +ninja + diff --git a/day5/examples/modules/saxpy/with_modules/cleanup.sh b/day5/examples/modules/saxpy/with_modules/cleanup.sh new file mode 100755 index 0000000000000000000000000000000000000000..3e1d09bdf9aa1c4a81b56fee495ec67f8ef44b4b --- /dev/null +++ b/day5/examples/modules/saxpy/with_modules/cleanup.sh @@ -0,0 +1,3 @@ +rm -rf build gcm.cache saxpy.pcm *.o *.gcc *.clg + + diff --git a/day5/examples/modules/saxpy/with_modules/saxpy.ixx b/day5/examples/modules/saxpy/with_modules/saxpy.ixx new file mode 100644 index 0000000000000000000000000000000000000000..47a8a348ed4b296b74159d5b4474d57467e32bc3 --- /dev/null +++ b/day5/examples/modules/saxpy/with_modules/saxpy.ixx @@ -0,0 +1,29 @@ +/* We are using #includes rather than imports +to be able to compile with gcc. Clang works +fine with the imports of STL headers. GCC, +even after generating header units with +-xc++-system-header crashes with an internal +compiler error. +*/ + +module; +#include <algorithm> +#include <span> +export module saxpy; + +template <class T> +concept Number = std::floating_point<T> + or std::integral<T>; + +export template <Number T> +auto saxpy(T a, std::span<const T> x, + std::span<const T> y, + std::span<T> z) +{ + std::transform(x.begin(), x.end(), + y.begin(), z.begin(), + [a](T X, T Y) { + return a * X + Y; + }); +} + diff --git a/day5/examples/modules/saxpy/with_modules/usesaxpy.cc b/day5/examples/modules/saxpy/with_modules/usesaxpy.cc new file mode 100644 index 0000000000000000000000000000000000000000..54fd6eb5420189b492dac7b557d7441d14455073 --- /dev/null +++ b/day5/examples/modules/saxpy/with_modules/usesaxpy.cc @@ -0,0 +1,18 @@ +#include <iostream> +#include <array> +#include <vector> +#include <span> +import saxpy; + +auto main() -> int +{ + using namespace std; + const array inp1 { 1., 2., 3., 4., 5. }; + const array inp2 { 9., 8., 7., 6., 5. }; + vector outp(inp1.size(), 0.); + + saxpy(10., {inp1}, {inp2}, {outp}); + for (auto x : outp) cout << x << "\n"; + cout << ":::::::::::::::::::::\n"; +} + 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"; +} diff --git a/day5/examples/ranges/dangling0.cc b/day5/examples/ranges/dangling0.cc new file mode 100644 index 0000000000000000000000000000000000000000..acc26317f12b687312960a48d75ad58951664ff8 --- /dev/null +++ b/day5/examples/ranges/dangling0.cc @@ -0,0 +1,22 @@ +#include <string> +#include <cxx20ranges> +#include <algorithm> +#include <iostream> +#include <vector> + +//namespace sr = std::ranges; +//namespace sv = std::views; + +auto get_vec() +{ + std::vector v{ 2, 4, -1, 8, 0, 9 }; + return v; +} + +auto main() -> int +{ + auto vec = get_vec(); + auto iter = sr::min_element(get_vec()); + std::cout << "Minimum value is " << *iter << "\n"; +} + diff --git a/day5/examples/ranges/dangling1.cc b/day5/examples/ranges/dangling1.cc new file mode 100644 index 0000000000000000000000000000000000000000..cc44e1381b608b12999e0049c9155c53792a5326 --- /dev/null +++ b/day5/examples/ranges/dangling1.cc @@ -0,0 +1,19 @@ +#include <span> +#include <vector> +#include <iostream> +#include <cxx20ranges> +#include <algorithm> + +static std::vector u{2, 3, 4, -1, 9}; +static std::vector v{3, 1, 4, 1, 5}; +auto get_vec(int c) -> std::span<int> { + return {(c%2 ==0) ? u : v}; +} +auto main(int argc, char* argv[]) -> int { + + //namespace sr = std::ranges; + auto iter = sr::min_element(get_vec(argc)); + + std::cout << "Minimum " << *iter << "\n"; +} + diff --git a/day5/examples/ranges/gerund.cc b/day5/examples/ranges/gerund.cc new file mode 100644 index 0000000000000000000000000000000000000000..fa4f397e3283d1a056f0e4cb3b54dfa88d2a9c45 --- /dev/null +++ b/day5/examples/ranges/gerund.cc @@ -0,0 +1,23 @@ +#include "range_output.hh" +#include <fstream> +#include <iostream> +#include <iterator> +#include <cxx20ranges> +#include <string> + +// namespace sr = std::ranges; +// namespace sv = std::views; + +int main(int argc, char* argv[]) +{ + using namespace output; + if (argc < 2) { + std::cerr << "Usage:\n" + << argv[0] << " input_file_name\n"; + return 1; + } + auto gerund = [](std::string_view w) { return w.ends_with("ing"); }; + std::ifstream fin { argv[1] }; + auto in = sr::istream_view<std::string>(fin); + std::cout << (in | sv::filter(gerund)) << "\n"; +} diff --git a/day5/examples/ranges/ha b/day5/examples/ranges/ha new file mode 100644 index 0000000000000000000000000000000000000000..c28786308e237b04f8ffbb1798ffd95ad56e5353 --- /dev/null +++ b/day5/examples/ranges/ha @@ -0,0 +1,11 @@ +The ranges library is an extension and generalization of the algorithms and iterator libraries that makes them more powerful by making them composable and less error-prone. + +The library creates and manipulates range views, lightweight objects that indirectly represent iterable sequences (ranges). Ranges are an abstraction on top of + + [begin, end) iterator pairs, e.g. ranges made by implicit conversion from containers. All algorithms that take iterator pairs now have overloads that accept ranges (e.g ranges::sort) + [start, size) counted sequences, e.g. range returned by views::counted + [start, predicate) conditionally-terminated sequences, e.g. range returned by views::take_while + [start..) unbounded sequences, e.g. range returned by views::iota + +The ranges library includes range algorithms, which are applied to ranges eagerly, and range adaptors, which are applied to views lazily. Adaptors can be composed into pipelines, so that their actions take place as the view is iterated. + diff --git a/day5/examples/ranges/iota.cc b/day5/examples/ranges/iota.cc new file mode 100644 index 0000000000000000000000000000000000000000..c30566b9901be118afb5c708a182ceb580449d26 --- /dev/null +++ b/day5/examples/ranges/iota.cc @@ -0,0 +1,20 @@ +// examples/iota.cc +#include <iostream> +#include <cxx20ranges> +// Compatibility header. + +auto main() -> int +{ + // namespace sv = std::views; + // Uncomment the line above if not using + // the compatibility header. + for (auto i : sv::iota(1UL)) { + if ((i + 1) % 10000UL == 0UL) { + std::cout << i << ' '; + if ((i + 1) % 100000UL == 0UL) + std::cout << '\n'; + if (i >= 100000000UL) + break; + } + } +} diff --git a/day5/examples/ranges/ranges0.cc b/day5/examples/ranges/ranges0.cc new file mode 100644 index 0000000000000000000000000000000000000000..32f9835c1cdd68757d01547793d77555d20f1064 --- /dev/null +++ b/day5/examples/ranges/ranges0.cc @@ -0,0 +1,38 @@ +#include <iostream> +#include <span> +#include <vector> +#include <valarray> +#include <list> +#include <array> +#include <string> + +#include <cxx20ranges> +// Compatibility header in your include path +// Replace with <ranges> if you only work with +// g++ or if your version of clang++ already +// has ranges algorithms implemented. + +// namespace sr = std::ranges; +// namespace sv = std::views; +// Uncomment the above two lines if you are not using the +// compatibility header above. + +auto sum(sr::input_range auto&& seq) +{ + std::iter_value_t<decltype(seq)> ans{}; + for (auto x : seq) ans += x; + return ans; +} + +auto main() -> int +{ + using namespace std::string_literals; + std::cout << "vector : " << sum(std::vector({ 9,8,7,2 } )) << "\n"; + std::cout << "list : " << sum(std::list({ 9,8,7,2 } )) << "\n"; + std::cout << "valarray : " << sum(std::valarray({ 9,8,7,2 } )) << "\n"; + std::cout << "array : " << sum(std::array<int,4>({ 9,8,7,2 } )) << "\n"; + std::cout << "array : " << sum(std::array<std::string, 4>({ "9"s,"8"s,"7"s,"2"s } )) << "\n"; + int A[]{1,2,3}; + std::cout << "C-array wrapped by span : " << sum(std::span(A)) << "\n"; +} + diff --git a/day5/examples/ranges/seqops_range.cc b/day5/examples/ranges/seqops_range.cc new file mode 100644 index 0000000000000000000000000000000000000000..2d1c7beae7419c351b9be2b55417e3da73c449cc --- /dev/null +++ b/day5/examples/ranges/seqops_range.cc @@ -0,0 +1,50 @@ +#include "range_output.hh" +#include <algorithm> +#include <iostream> +#include <iterator> +#include <cxx20ranges> +#include <vector> + +int main() +{ + using namespace std; +// namespace sr = std::ranges; + using namespace output; + vector v { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + cout << "Vector v = (after initialization): " << v << "\n"; + if (sr::is_sorted(v)) + cout << "The sequence is sorted in the increasing order.\n"; + else + cout << "The sequence is not sorted in the increasing order.\n"; + + sr::reverse(v); + cout << "After std::ranges::reverse: " << v << "\n"; + + sr::rotate(v, v.begin() + 3); + cout << "After std::rotate by 3" << v << "\n"; + + sr::sort(v); + cout << "After std::ranges::sort" << v << "\n"; + + vector<int> w { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, x, y, z; + cout << "Vector w = " << w << "\n"; + + vector<int> m; + sr::merge(v, w, std::back_inserter(m)); + cout << "ranges::merge of v and w gives ... : " << m << "\n"; + + sr::set_union(v, w, back_inserter(x)); + cout << "Union of v and w : " << x << "\n"; + + sr::set_intersection(w, v, back_inserter(y)); + cout << "Intersection of w and v : " << y << "\n"; + + sr::set_symmetric_difference(v, w, back_inserter(z)); + cout << "Symmetric difference of v and w : " << z << "\n"; + + if (sr::is_permutation(z, v)) { + cout << "The above sequence is a permutation of the first sequence printed.\n"; + } else { + cout << "The above sequence is not a permutation of the first sequence printed.\n"; + } +} diff --git a/day5/examples/ranges/trig_views.cc b/day5/examples/ranges/trig_views.cc new file mode 100644 index 0000000000000000000000000000000000000000..f91a2dd3531e6d4e85feb7fdc136dc6d5cffe895 --- /dev/null +++ b/day5/examples/ranges/trig_views.cc @@ -0,0 +1,44 @@ +// examples/trig_views.cc +#include <iostream> +#include <algorithm> +#include <cmath> +#include <limits> + +#include <cxx20ranges> + +// Compatibility header in your include path +// Replace with <ranges> if you only work with +// g++ or if your version of clang++ already +// has ranges algorithms implemented. + +auto main() -> int +{ +// namespace sr = std::ranges; +// namespace sv = std::views; +// Uncomment the above two lines if you are not using the +// compatibility header above. + + const auto pi = std::acos(-1); + constexpr auto npoints = 10'000'00UL; + constexpr auto eps = 100 * std::numeric_limits<double>::epsilon(); + auto to_0_2pi = [=](size_t idx) -> double { + return std::lerp(0., 2*pi, idx * 1.0 / npoints); + }; + auto x_to_fx = [ ](double x) -> double { + return sin(x) * sin(x) + cos(x) * cos(x) - 1.0; + }; + auto is_bad = [=](double x){ return std::fabs(x) > eps; }; + auto res = sv::iota(0UL, npoints) | sv::transform(to_0_2pi) + | sv::transform(x_to_fx); + if (sr::any_of(res, is_bad) ) { + std::cerr << "There is at least one input for which the relation does not hold.\n" + << "They are...\n"; + for (auto bad_res : res | sv::filter(is_bad)) { + std::cerr << bad_res << "\n"; + } + } else { + std::cout << "The relation holds for all inputs\n"; + } +} + + diff --git a/day5/examples/ranges/trig_views2.cc b/day5/examples/ranges/trig_views2.cc new file mode 100644 index 0000000000000000000000000000000000000000..94f2e1e40ae39ff8b7eaae08f7c745b11df89204 --- /dev/null +++ b/day5/examples/ranges/trig_views2.cc @@ -0,0 +1,34 @@ +// examples/trig_views2.cc +#include <iostream> +#include <cxx20ranges> +#include <algorithm> +#include <cmath> +#include <limits> + +auto main() -> int +{ + //namespace sr = std::ranges; + //namespace sv = std::views; + const auto pi = std::acos(-1); + constexpr auto npoints = 100UL; + auto is_bad = [=](double x){ return x > 0; }; + auto res = sv::iota(0UL, npoints) + | sv::transform([=](auto idx){ + auto output = std::lerp(0., 2*pi, idx * 1.0 / npoints); + std::cout << "Mapping " << idx << " to " << output << "\n"; + return output; + } ) + | sv::transform([ ](auto x) { + auto output = sin(x) * sin(x) - 0.99; + std::cout << "Input = " << x << "\toutput = " << output << "\n"; + return output; + } ); + std::cout << "Evaluating any_of ...\n"; + if (sr::any_of(res, is_bad) ) { + std::cerr << "There is at least one input for which the relation does not hold.\n"; + } else { + std::cout << "The relation holds for all inputs\n"; + } +} + + diff --git a/day5/examples/ranges/views_and_span.cc b/day5/examples/ranges/views_and_span.cc new file mode 100644 index 0000000000000000000000000000000000000000..a3d3376a144e27eaadf14b488a3ed9c85b4817b4 --- /dev/null +++ b/day5/examples/ranges/views_and_span.cc @@ -0,0 +1,25 @@ +// examples/views_and_span.cc +#include <iostream> +#include <span> +#include <cxx20ranges> +#include <algorithm> +#include <string> +#include <iomanip> + +auto main(int argc, char * argv[]) -> int +{ + std::span args(argv, argc); + auto str = [](auto inp) -> std::string_view { return inp; }; + if (argc < 2) { + std::cout << "Usage:\n" + << argv[0] << " some strings in the command line\n"; + return 1; + } + //namespace sr = std::ranges; + //namespace sv = std::views; + auto [first, last] = sr::minmax( args | sv::drop(1) | sv::transform(str) ); + + std::cout << "Alphabetically first and last strings in your input are " + << std::quoted(first) << " and " << std::quoted(last) << "\n"; +} + diff --git a/day5/examples/spans/main.cc b/day5/examples/spans/main.cc new file mode 100644 index 0000000000000000000000000000000000000000..fc9c2744747168849a20c61d8bca05d171e95716 --- /dev/null +++ b/day5/examples/spans/main.cc @@ -0,0 +1,29 @@ +#include <vector> +#include <valarray> +#include <iostream> +#include "spanfunc.hh" + +using std::span; +void elsewhere(double* x, double* y, + unsigned N) +{ + // Use it when you only have C-style + // pointers and sizes + std::cout << "adapting C-style arrays: " + << compute(span(x, N), span(y, N)) << "\n"; +} + +auto main() -> int +{ + std::vector x(100UL, 2.3); + std::vector y(x.size(), 3.4); + // Use it as if it was written for vector! + std::cout << "direct: " << compute(x, y) << "\n"; + std::valarray z(2.3, x.size()); // weird constructor, remember! + // Use the same function also for valarrays! + std::cout << "two valarrays: " << compute(z, z) << "\n"; + // Even with a valarray and a vector! + std::cout << "valarray and vector : " << compute(x, z) << "\n"; + elsewhere(x.data(), y.data(), x.size()); +} + diff --git a/day5/examples/spans/spanfunc.cc b/day5/examples/spans/spanfunc.cc new file mode 100644 index 0000000000000000000000000000000000000000..8876cb442b23957916225b222d19772a5dd74def --- /dev/null +++ b/day5/examples/spans/spanfunc.cc @@ -0,0 +1,17 @@ +#include "spanfunc.hh" +#include <numeric> +#include <functional> + +using std::span; +using std::transform_reduce; +using std::plus; +using std::multiplies; +// write function in terms of spans +auto compute(span<const double> u, + span<const double> v) -> double +{ + return transform_reduce( + u.begin(), u.end(), + v.begin(), 0., plus<double>{}, + multiplies<double>{}); +} diff --git a/day5/examples/spans/spanfunc.hh b/day5/examples/spans/spanfunc.hh new file mode 100644 index 0000000000000000000000000000000000000000..6d8c8d2385666677556a073bee5ec746740573c0 --- /dev/null +++ b/day5/examples/spans/spanfunc.hh @@ -0,0 +1,7 @@ +#pragma once + +#include <span> + +// write function in terms of spans +auto compute(std::span<const double> u, std::span<const double> v) -> double; + diff --git a/day5/examples/word_count.cc b/day5/examples/word_count.cc new file mode 100644 index 0000000000000000000000000000000000000000..04435d6b72938548a5d67da1ae95fdfdfd41b008 --- /dev/null +++ b/day5/examples/word_count.cc @@ -0,0 +1,26 @@ +#include <fstream> +#include <cxx20format> +#include <cxx20ranges> +#include <iostream> +#include <map> +#include <string> + +auto main(int argc, char* argv[]) -> int +{ + std::ifstream fin(argv[1]); + std::map<std::string, unsigned> freq; + std::string s; + auto non_alphabetic = [](auto c) { return not isalpha(c); }; + + while (fin >> s) { + std::string s2; + sr::copy(s | sv::drop_while(non_alphabetic) + | sv::reverse + | sv::drop_while(non_alphabetic) + | sv::reverse, std::back_inserter(s2)); + freq[s2]++; + } + + for (auto&& [word, count] : freq) + std::cout << format("{:20}{:4}{:12}\n", word, ':', count); +}