From bc71d512a099539a7c0d034bbfe9ebe00cb0b902 Mon Sep 17 00:00:00 2001 From: Ondrej Hladuvka Date: Wed, 21 May 2025 21:10:59 +0300 Subject: [PATCH] rest of tasks --- CT1/booking/Makefile | 16 +++ CT1/booking/booking.cpp | 206 +++++++++++++++++++++++++++++++++++ CT2/Makefile | 11 ++ CT2/booking.cpp | 117 ++++++++++++++++++++ CT2/main.cpp | 160 +++++++++++++++++++++++++++ CT2/menugen.cpp | 138 +++++++++++++++++++++++ CT3/Makefile | 11 ++ CT3/booking.cpp | 152 ++++++++++++++++++++++++++ CT3/main.cpp | 159 +++++++++++++++++++++++++++ CT3/menugen.cpp | 138 +++++++++++++++++++++++ CT4/Makefile | 11 ++ CT4/booking.cpp | 166 ++++++++++++++++++++++++++++ CT4/main.cpp | 10 ++ CT4/menu.cpp | 235 ++++++++++++++++++++++++++++++++++++++++ CT4/menugen.cpp | 143 ++++++++++++++++++++++++ CT4/module.cpp | 114 +++++++++++++++++++ CT4/server.cpp | 67 ++++++++++++ 17 files changed, 1854 insertions(+) create mode 100644 CT1/booking/Makefile create mode 100644 CT1/booking/booking.cpp create mode 100644 CT2/Makefile create mode 100644 CT2/booking.cpp create mode 100644 CT2/main.cpp create mode 100644 CT2/menugen.cpp create mode 100644 CT3/Makefile create mode 100644 CT3/booking.cpp create mode 100644 CT3/main.cpp create mode 100644 CT3/menugen.cpp create mode 100644 CT4/Makefile create mode 100644 CT4/booking.cpp create mode 100644 CT4/main.cpp create mode 100644 CT4/menu.cpp create mode 100644 CT4/menugen.cpp create mode 100644 CT4/module.cpp create mode 100644 CT4/server.cpp diff --git a/CT1/booking/Makefile b/CT1/booking/Makefile new file mode 100644 index 0000000..990b568 --- /dev/null +++ b/CT1/booking/Makefile @@ -0,0 +1,16 @@ +CXX = g++ +CXXFLAGS = -std=c++20 -Wall -pedantic -O2 +LDFLAGS = + +SRCS = $(wildcard *.cpp) +EXECS = $(SRCS:.cpp=) + +all: $(EXECS) + +%: %.cpp + $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) + +clean: + rm -f $(EXECS) + +.PHONY: all clean \ No newline at end of file diff --git a/CT1/booking/booking.cpp b/CT1/booking/booking.cpp new file mode 100644 index 0000000..0dc9d0c --- /dev/null +++ b/CT1/booking/booking.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include + +#define RESET "\033[0m" +#define RED "❌ \033[31m" +#define GREEN "✅ \033[32m" +#define BOLDWHITE "\033[1m\033[37m" + +constexpr const char* MENU_MSG = "=== Ticket Booking System ===\n1. Configure>\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n"; +constexpr const char* REWRITE_WARNING_MSG = "WARNING: all reservations will be lost. Do you wish to continue? (0/1)\n"; +constexpr const char* CURSOR_MSG = "> "; + +using seat_num = uint64_t; +using uiBool = bool; + +class BookingException : public std::exception { + public: const char* what() const noexcept override { + return "Internal exception!"; + } +}; +class AlreadyReserved : public BookingException { + const char* what() const noexcept override { + return "Already reserved!"; + } +}; +class NotReserved : public BookingException { + const char* what() const noexcept override { + return "Not reserved!"; + } +}; +class InvalidSeat : public BookingException { + const char* what() const noexcept override { + return "Invalid seat number!"; + } +}; +class InvalidArgument : public BookingException { + const char* what() const noexcept override { + return "Invalid argument!"; + } +}; +class NotConfigured : public BookingException { + const char* what() const noexcept override { + return "Not configured!"; + } +}; + +enum class MenuState { + MENU, + SETUP = 1, + RESERVE = 2, + CANCEL = 3, + CHECK = 4, + EXIT = 5, +}; + +std::istream& operator>>(std::istream& is, MenuState& state) { + int a; + if (!(is >> a)) { + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + throw InvalidArgument(); + } + + state = static_cast(a); + + return is; +} + +template +T getInput(std::istream& is) { + T n; + if (!(is >> n) || n < 0) { + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + throw InvalidSeat(); + } + + return n; +} + +template <> +uiBool getInput(std::istream& is) { + uint64_t n; + if (!(is >> n) || (n != 1 && n != 0)) { + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + throw InvalidArgument(); + } + + return n == 1; +} + +class Seat { //TODO jmeno rezervace +private: + bool reserved; +public: + Seat() : reserved(false) {} + bool getState() { + return reserved; + } + bool setState(bool s) { + reserved = s; + return true; + } +}; + +class Booker { +public: + Booker() : inUse(false), configured(false) {}; + Booker(seat_num capacity) : seats(capacity), inUse(false), configured(true) {} + ~Booker() = default; + + bool getSeatState(seat_num seat) { + if (!configured) throw NotConfigured(); + if (seat >= seats.size()) throw InvalidSeat(); + return seats.at(seat).getState(); + } + bool reserveSeat(seat_num seat) { + if (!configured) throw NotConfigured(); + if (seat >= seats.size()) throw InvalidSeat(); + if (seats.at(seat).getState()) throw AlreadyReserved(); + inUse = true; + return seats.at(seat).setState(true); + } + bool cancelSeat(seat_num seat) { + if (!configured) throw NotConfigured(); + if (seat >= seats.size()) throw InvalidSeat(); + if (!seats.at(seat).getState()) throw NotReserved(); + return seats.at(seat).setState(false); + } + bool getInUse() { return inUse; } +private: + std::vector seats; + bool inUse; + bool configured; +}; + +int main(int ragc, char* argv[]) { + std::ostream& os = std::cout; + std::istream& is = std::cin; + + MenuState state = MenuState::MENU; + + Booker h; + for (;;) { + try { + seat_num seat; + + os << BOLDWHITE << MENU_MSG << RESET; + is >> state; + + switch (state) { + case MenuState::SETUP : + if (h.getInUse()) { + os << REWRITE_WARNING_MSG << CURSOR_MSG; + if (!getInput(is)) break; + } + os << "Enter a number of avaliable seats: \n" << CURSOR_MSG; + seat = getInput(is); + h = Booker(seat); + os << GREEN << "New avaliable seat count: " << seat << "\n" << RESET; + break; + + case MenuState::RESERVE : + os << "Seat to be reserved: "; + seat = getInput(is); + h.reserveSeat(seat); + os << GREEN << "Seat " << seat << " reserved\n" << RESET; + break; + + case MenuState::CANCEL : + os << "Seat to be cancelled: "; + seat = getInput(is); + h.cancelSeat(seat); + os << GREEN << "Seat " << seat << " cancelled\n" << RESET; + break; + + case MenuState::CHECK : + os << "Seat to be checked: "; + seat = getInput(is); + if (h.getSeatState(seat)) + os << RED << "Seat " << seat << " is reserved\n" << RESET; + else + os << GREEN << "Seat " << seat << " is avaliable\n" << RESET; + break; + + case MenuState::EXIT : + os << "Exiting...\n"; + return EXIT_SUCCESS; + break; + + case MenuState::MENU : + default: + throw InvalidArgument(); + } + } catch (BookingException& e) { + os << RED << e.what() << RESET; + // return EXIT_FAILURE; + } + state = MenuState::MENU; + os << "\n\n"; + } +} diff --git a/CT2/Makefile b/CT2/Makefile new file mode 100644 index 0000000..73c4f67 --- /dev/null +++ b/CT2/Makefile @@ -0,0 +1,11 @@ +CXX = g++ +CXXFLAGS = -std=c++20 -Wall -pedantic -O2 +LDFLAGS = + + +main: main.cpp booking.cpp menugen.cpp + $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) + +.PHONY clean: + rm -f main + diff --git a/CT2/booking.cpp b/CT2/booking.cpp new file mode 100644 index 0000000..a43b106 --- /dev/null +++ b/CT2/booking.cpp @@ -0,0 +1,117 @@ +#pragma once +#include +#include +#include +#include + +#include "menugen.cpp" + +namespace Booking { + using seat_num = uint64_t; + using seat_range = std::pair; + using uiBool = bool; + + constexpr size_t SEAT_CAPACITY = 20; + + class BookingException : public std::exception { + public: const char* what() const noexcept override { return "Internal exception!"; } + }; + + #define EXCEPTION(INTERNAL, EXTARNAL) \ + class INTERNAL : public BookingException { \ + const char* what() const noexcept override { return EXTARNAL; } \ + }; + + EXCEPTION(AlreadyReserved, "Already reserved!") + EXCEPTION(NotReserved, "Not reserved!") + EXCEPTION(InvalidSeat, "Invalid seat number!"); + EXCEPTION(InvalidArgument, "Invalid argument!") + + struct Seat { + enum class Status { + AVALIBLE, + RESERVED, + BROKEN + }; + + Status s = Status::AVALIBLE; + }; + + class Booker { + private: + Seat seats[SEAT_CAPACITY]; + + public: + Seat::Status getSeatState(seat_num seat) { + return seats[seat - 1].s; + } + + void reserveSeat(seat_range r) { + bool status = false; + for (auto i = r.first - 1; i < r.second; ++i) + status |= seats[i].s != Seat::Status::AVALIBLE; + if (status) throw AlreadyReserved(); + for (auto i = r.first - 1; i < r.second; ++i) + seats[i].s = Seat::Status::RESERVED; + } + + void cancelSeat(seat_range r) { + bool status = false; + for (auto i = r.first - 1; i < r.second; ++i) + status |= seats[i].s != Seat::Status::RESERVED; + if (status) throw NotReserved(); + for (auto i = r.first - 1; i < r.second; ++i) + seats[i].s = Seat::Status::AVALIBLE; + } + + void breakSeat(seat_range r, std::ostream&os, const char* prefix) { + for (auto i = r.first - 1; i < r.second; ++i) { + if (seats[i].s == Seat::Status::RESERVED) + os << prefix << "Reservation on seat " << i + 1 << " is canceled, seat is now broken\n"; + seats[i].s = Seat::Status::BROKEN; + } + } + + void filter(const std::function& f, std::ostream& os) { + for (seat_num i = 0; i <= SEAT_CAPACITY; ++i) { + if (f(seats[i])) + os << i + 1 << ' '; + } + os << '\n'; + } + + auto operator<=>(const Booker& oth) const = default; + + Booker operator+(const Booker& oth) { + Booker b; + for (seat_num i = 0; i < SEAT_CAPACITY; ++i) { + if (seats[i].s == Seat::Status::AVALIBLE || + oth.seats[i].s == Seat::Status::AVALIBLE) + b.seats[i].s = Seat::Status::AVALIBLE; + if (seats[i].s == Seat::Status::RESERVED || + oth.seats[i].s == Seat::Status::RESERVED) + b.seats[i].s = Seat::Status::RESERVED; + if (seats[i].s == Seat::Status::BROKEN || + oth.seats[i].s == Seat::Status::BROKEN) + b.seats[i].s = Seat::Status::BROKEN; + } + return b; + } + + void print(seat_range r, std::ostream& os) const { + for (seat_num i = r.first - 1; i < r.second; ++i) { + switch (seats[i].s) { + case Seat::Status::AVALIBLE: os << Menu::GREEN << "Seat " << i + 1 << " is avaliable\n" << Menu::RESET; break; + case Seat::Status::RESERVED: os << Menu::YELLOW << "Seat " << i + 1 << " is reserved\n" << Menu::RESET; break; + case Seat::Status::BROKEN: os << Menu::RED << "Seat " << i + 1 << " is broken\n" << Menu::RESET; break; + default: break; + } + } + } + }; +} + +std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) { + b.print({1, Booking::SEAT_CAPACITY}, os); + return os; +} \ No newline at end of file diff --git a/CT2/main.cpp b/CT2/main.cpp new file mode 100644 index 0000000..1c8aa4b --- /dev/null +++ b/CT2/main.cpp @@ -0,0 +1,160 @@ +#include +#include // streamsize::max +#include "booking.cpp" + +/* declaring X(option) generates: + - Ordered menu message entry + - Ordered Menu::State::option symbol +*/ +#define MENU_ENTRIES \ + X(RESERVE), \ + X(CANCEL), \ + X(CHECK), \ + X(BREAK), \ + X(FILTER_AVALIABLE), \ + X(FILTER_RESERVED), \ + X(FILTER_BROKEN), \ + X(EXIT) + +#include "menugen.cpp" + +namespace Menu { + // generate enum + #define X(entry) entry + enum class State : StateType{ + MENU = 0, // default state + MENU_ENTRIES, + COUNT // number of menu entries + }; + #undef X + + // generate menu message + constexpr auto generateMenuMessage() { + constexpr char menuMsg[] = "=== Ticket Booking System ==="; + constexpr char dot[] = ". "; + constexpr char nl[] = "\n"; + constexpr auto str = concat( nl, nl, menuMsg, nl, + #define X(entry) NumToString(State::entry)>::value, dot, #entry, nl + MENU_ENTRIES + #undef X + ); + return str; + } +} + +constexpr auto MENU_MSG = Menu::generateMenuMessage(); + +std::istream& operator>>(std::istream& is, Menu::State& state) { + if (!(is >> reinterpret_cast(state))) throw Booking::InvalidArgument(); + return is; +} + +std::ostream& operator<<(std::ostream& os, Booking::seat_range r) { + if (r.second == r.first) os << " seat " << r.first << " is "; + else os << " seats " << r.first << "-" << r.second << " are "; + return os; +} + +std::pair getRange(std::istream& is) { + auto isEnd = [](char c) { return c == '\n' || c == EOF; }; + try { + std::pair output; + if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY) + throw Booking::InvalidSeat(); + output.second = output.first; + while (!std::isdigit(is.peek())) { + char c = is.get(); + if (c == '-') break; + if (isEnd(c)) return output; + if (!std::iswspace(c)) throw Booking::InvalidSeat(); + } + if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY) + throw Booking::InvalidSeat(); + char c = is.peek(); + if (isEnd(c)) { + is.get(); // clear newline or eof + if (output.first > output.second) + std::swap(output.first, output.second); + return output; + } + while (std::iswspace(c - is.get())) if (isEnd(c)) return output; + throw Booking::InvalidSeat(); // unexpected character + } catch(...) { + throw Booking::InvalidSeat(); + } +} + +int main(int ragc, char* argv[]) { + std::ostream& os = std::cout; + std::istream& is = std::cin; + Menu::State state = Menu::State::MENU; + Booking::Booker h; + Booking::seat_range range; + + for (;;) try { + state = Menu::State::MENU; + os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR; is >> state; + + switch (state) { + + case Menu::State::RESERVE: + os << "Seat(s) to be reserved:\n" << Menu::CURSOR; + range = getRange(is); + // for (Booking::seat_num i = range.first; i <= range.second; ++i) h.reserveSeat(i); + h.reserveSeat(range); + os << Menu::GREEN << range << "reserved\n" << Menu::RESET; + break; + + case Menu::State::CANCEL: + os << "Seat(s) to be cancelled:\n" << Menu::CURSOR; + range = getRange(is); + h.cancelSeat(range); + os << Menu::GREEN << range << "cancelled\n" << Menu::RESET; + break; + + case Menu::State::BREAK: + os << "Seat(s) to be broken:\n" << Menu::CURSOR; + range = getRange(is); + h.breakSeat(range, os, Menu::RED); + os << Menu::RESET << range << "broken"; + break; + + case Menu::State::CHECK: + os << "Seat(s) to be checked:\n" << Menu::CURSOR; + range = getRange(is); + h.print(range, os); + break; + + + case Menu::State::FILTER_BROKEN: + os << "Avaliable seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::BROKEN; }, os); + break; + + case Menu::State::FILTER_AVALIABLE: + os << "Avaliable seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::AVALIBLE; }, os); + break; + + case Menu::State::FILTER_RESERVED: + os << "Reserved seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::RESERVED; }, os); + break; + + case Menu::State::EXIT: + os << "Exiting...\n"; + return EXIT_SUCCESS; + break; + + default: + throw Booking::InvalidArgument(); + } + } catch (Booking::BookingException& e) { + os << Menu::RED << e.what() << Menu::RESET << "\n"; + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + // return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/CT2/menugen.cpp b/CT2/menugen.cpp new file mode 100644 index 0000000..6b7b6bf --- /dev/null +++ b/CT2/menugen.cpp @@ -0,0 +1,138 @@ +#ifndef MENUGEN_0219820379823487123785 +#define MENUGEN_0219820379823487123785 + +// This is collection of utility constexpr tools for generating menu dynamically in compile time +// Menu::generateMenuMessage basically creates this: + +// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n"; +// enum class State : int { +// MENU = 0, +// SETUP = 1, +// RESERVE = 2, +// CANCEL = 3, +// CHECK = 4, +// EXIT = 5, +// }; + +// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message +// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there. +// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element +// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours, +// but they are a lot less versatile than std::array, so Ive got first prototype working with them. +// But STL containers are forbidden in CT2... so i wrote my own ConstString :) + +// With constexpr ready container, generating message is simple as +// 1. transforming number(enum member) to string +// 2. summing up all the submessage lengths +// 3. copying to single container + +// I was suprised cpp20 doesnt include way to transform int to string in constexpr. +// So i achived this with recursive template ExpandDecimal - name is self explanatory. +// Calculating total length is just sum of the sizes of expanded template pack +// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library + +// thats all, hope you find it interesting :) + +#include // index_sequence +namespace Menu{ + constexpr auto RESET = "\033[0m"; + constexpr auto RED = "❌ \033[31m"; + constexpr auto GREEN = "✅ \033[32m"; + constexpr auto YELLOW = "❌ \033[33m"; + constexpr auto BOLDWHITE = "\033[1m\033[37m"; + + constexpr auto CURSOR = "> "; + + using StateType = size_t; + + //################################################################################### + // helper char[] wrapper since it cannot be directly returned + template + struct ConstString { char data[size + 1];}; + + template + std::ostream& operator<<(std::ostream& os, const ConstString& r) { + for (char c : r.data) + os << c; + return os; + } + + //################################################################################### + // helpers to convert int (from enum) to string in ################################## + // shared array to store result + template + struct to_chars { static const char value[]; }; + + // convert single digit to char + template + // constexpr char to_chars::value[] = {('0' + digits)...}; + constexpr char to_chars::value[] = {('0' + digits)..., 0}; // null termination + + // when reminder is not zero - recursive expand for each decimal digit + template + struct ExpandDecimal : ExpandDecimal {}; + + // when reminder is not zero - call to_char for last digit + template + struct ExpandDecimal<0, digits...> : to_chars {}; + + // entry point - to by called directly + template + struct NumToString : ExpandDecimal {}; + //################################################################################### + + // helper templates to calculate total length as constexpr ########################## + template + constexpr std::size_t total_length(Strings&&... strings) { + return (0 + ... + std::size(strings)); + } + //################################################################################### + + // helper templates to concatonate as constexpr ##################################### + template // analog to copy_n from + static constexpr void copy_str(char* dest, std::size_t& idx, + Str str, std::index_sequence) { + ((dest[idx++] = str[I]), ...); + } + + template + constexpr auto concat(Strings&&... strings) { + constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1)); + + ConstString result; std::size_t idx = 0; + ((copy_str(result.data, idx, strings, // copy each string to result + std::make_index_sequence{}), ...)); + + result.data[total_len] = '\0'; + return result; + } + //################################################################################### + + // tests ############################################################################ + static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check"); + static_assert(NumToString<1>::value[0] == '1', "NumToString: 1"); + static_assert(NumToString<12345678900>::value[0] == '1' && + NumToString<12345678900>::value[1] == '2' && + NumToString<12345678900>::value[2] == '3' && + NumToString<12345678900>::value[3] == '4' && + NumToString<12345678900>::value[4] == '5' && + NumToString<12345678900>::value[5] == '6' && + NumToString<12345678900>::value[6] == '7' && + NumToString<12345678900>::value[7] == '8' && + NumToString<12345678900>::value[8] == '9' && + NumToString<12345678900>::value[9] == '0' && + NumToString<12345678900>::value[10] == '0' && + NumToString<12345678900>::value[11] == '\0'&& + sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum"); + + static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check"); + static_assert(concat("a", "bc", "def").data[0] == 'a' && + concat("a", "bc", "def").data[1] == 'b' && + concat("a", "bc", "def").data[2] == 'c' && + concat("a", "bc", "def").data[3] == 'd' && + concat("a", "bc", "def").data[4] == 'e' && + concat("a", "bc", "def").data[5] == 'f' && + concat("a", "bc", "def").data[6] == '\0', "concat: content"); +} + +#endif //MENUGEN_0219820379823487123785 \ No newline at end of file diff --git a/CT3/Makefile b/CT3/Makefile new file mode 100644 index 0000000..73c4f67 --- /dev/null +++ b/CT3/Makefile @@ -0,0 +1,11 @@ +CXX = g++ +CXXFLAGS = -std=c++20 -Wall -pedantic -O2 +LDFLAGS = + + +main: main.cpp booking.cpp menugen.cpp + $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) + +.PHONY clean: + rm -f main + diff --git a/CT3/booking.cpp b/CT3/booking.cpp new file mode 100644 index 0000000..fed59c2 --- /dev/null +++ b/CT3/booking.cpp @@ -0,0 +1,152 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "menugen.cpp" + +namespace Booking { + using seat_num = uint64_t; + using seat_range = std::pair; + using uiBool = bool; + + constexpr size_t SEAT_CAPACITY = 20; + + class BookingException : public std::exception { + public: const char* what() const noexcept override { return "Internal exception!"; } + }; + + #define EXCEPTION(INTERNAL, EXTARNAL) \ + class INTERNAL : public BookingException { \ + const char* what() const noexcept override { return EXTARNAL; } \ + }; + + EXCEPTION(AlreadyReserved, "Already reserved!") + EXCEPTION(NotReserved, "Not reserved!") + EXCEPTION(InvalidSeat, "Invalid seat number!"); + EXCEPTION(InvalidArgument, "Invalid argument!") + + class Seat : public std::enable_shared_from_this { + struct Private{ explicit Private() = default; }; + public: + enum class Type { + VIP, REGULAR, CHEAP + }; + enum class Status { + AVALIBLE, RESERVED, BROKEN + }; + Status s = Status::AVALIBLE; + Type c; + + Seat(Private, Type t) : c(t) {} + static std::shared_ptr create(Type t) { + return std::make_shared(Private(), t); + } + + std::shared_ptr getptr() { + return shared_from_this(); + } + }; + + class Booker { + private: + std::map< seat_num, std::shared_ptr< Seat > > seatmap; + + std::map< Seat::Type, std::queue< std::string > > queues = + {{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}}; + std::map< Seat::Type, std::queue< std::shared_ptr< Seat > > > avaliables = + {{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}}; + + public: + Booker(seat_num vip = 5, seat_num regular = 10, seat_num cheap = 10) { + for (seat_num i = 0; i < vip; ++i) { + auto s = Seat::create(Seat::Type::VIP); + seatmap.emplace(seatmap.size(), s->getptr() ); + avaliables[Seat::Type::VIP].emplace(s->getptr()); + } + for (seat_num i = 0; i < regular; ++i) { + auto s = Seat::create(Seat::Type::REGULAR); + seatmap.emplace(seatmap.size(), s->getptr() ); + avaliables[Seat::Type::REGULAR].emplace(s->getptr()); + } + for (seat_num i = 0; i < cheap; ++i) { + auto s = Seat::create(Seat::Type::CHEAP); + seatmap.emplace(seatmap.size(), s->getptr() ); + avaliables[Seat::Type::CHEAP].emplace(s->getptr()); + } + } + + Seat::Status getSeatState(seat_num seat) { + return seatmap.at(seat)->s; + } + + + void reserveSeat(Seat::Type t, seat_num n = 1) { + for (seat_num i = 0; i < n; ++i) { + if (avaliables[t].size()) { + auto s = avaliables[t].front(); + avaliables[t].pop(); + + seatmap[s] + } + } + } + + void reserveSeat(seat_range r) { + // bool status = false; + // for (auto i = r.first - 1; i < r.second; ++i) + // status |= seats[i].s != Seat::Status::AVALIBLE; + // if (status) throw AlreadyReserved(); + // for (auto i = r.first - 1; i < r.second; ++i) + // seats[i].s = Seat::Status::RESERVED; + } + + void cancelSeat(seat_range r) { + // bool status = false; + // for (auto i = r.first - 1; i < r.second; ++i) + // status |= seats[i].s != Seat::Status::RESERVED; + // if (status) throw NotReserved(); + // for (auto i = r.first - 1; i < r.second; ++i) + // seats[i].s = Seat::Status::AVALIBLE; + } + + void breakSeat(seat_range r, std::ostream&os, const char* prefix) { + // for (auto i = r.first - 1; i < r.second; ++i) { + // if (seats[i].s == Seat::Status::RESERVED) + // os << prefix << "Reservation on seat " << i + 1 << " is canceled, seat is now broken\n"; + // seats[i].s = Seat::Status::BROKEN; + // } + } + + void filter(const std::function& f, std::ostream& os) { + // for (seat_num i = 0; i <= SEAT_CAPACITY; ++i) { + // if (f(seats[i])) + // os << i + 1 << ' '; + // } + // os << '\n'; + } + + void print(seat_range r, std::ostream& os) const { + // for (seat_num i = r.first - 1; i < r.second; ++i) { + // switch (seats[i].s) { + // case Seat::Status::AVALIBLE: os << Menu::GREEN << "Seat " << i + 1 << " is avaliable\n" << Menu::RESET; break; + // case Seat::Status::RESERVED: os << Menu::YELLOW << "Seat " << i + 1 << " is reserved\n" << Menu::RESET; break; + // case Seat::Status::BROKEN: os << Menu::RED << "Seat " << i + 1 << " is broken\n" << Menu::RESET; break; + // default: break; + // } + // } + } + }; +} + +std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) { + b.print({1, Booking::SEAT_CAPACITY}, os); + return os; +} \ No newline at end of file diff --git a/CT3/main.cpp b/CT3/main.cpp new file mode 100644 index 0000000..8e484c7 --- /dev/null +++ b/CT3/main.cpp @@ -0,0 +1,159 @@ +#include +#include // streamsize::max +#include "booking.cpp" + +/* declaring X(option) generates: + - Ordered menu message entry + - Ordered Menu::State::option symbol +*/ +#define MENU_ENTRIES \ + X(RESERVE), \ + X(CANCEL), \ + X(CHECK), \ + X(BREAK), \ + X(FILTER_AVALIABLE), \ + X(FILTER_RESERVED), \ + X(FILTER_BROKEN), \ + X(EXIT) + +#include "menugen.cpp" + +namespace Menu { + // generate enum + #define X(entry) entry + enum class State : StateType{ + MENU = 0, // default state + MENU_ENTRIES, + COUNT // number of menu entries + }; + #undef X + + // generate menu message + constexpr auto generateMenuMessage() { + constexpr char menuMsg[] = "=== Ticket Booking System ==="; + constexpr char dot[] = ". "; + constexpr char nl[] = "\n"; + constexpr auto str = concat( nl, nl, menuMsg, nl, + #define X(entry) NumToString(State::entry)>::value, dot, #entry, nl + MENU_ENTRIES + #undef X + ); + return str; + } +} + +constexpr auto MENU_MSG = Menu::generateMenuMessage(); + +std::istream& operator>>(std::istream& is, Menu::State& state) { + if (!(is >> reinterpret_cast(state))) throw Booking::InvalidArgument(); + return is; +} + +std::ostream& operator<<(std::ostream& os, Booking::seat_range r) { + if (r.second == r.first) os << " seat " << r.first << " is "; + else os << " seats " << r.first << "-" << r.second << " are "; + return os; +} + +std::pair getRange(std::istream& is) { + auto isEnd = [](char c) { return c == '\n' || c == EOF; }; + try { + std::pair output; + if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY) + throw Booking::InvalidSeat(); + output.second = output.first; + while (!std::isdigit(is.peek())) { + char c = is.get(); + if (c == '-') break; + if (isEnd(c)) return output; + if (!std::iswspace(c)) throw Booking::InvalidSeat(); + } + if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY) + throw Booking::InvalidSeat(); + char c = is.peek(); + if (isEnd(c)) { + is.get(); // clear newline or eof + if (output.first > output.second) + std::swap(output.first, output.second); + return output; + } + while (std::iswspace(c - is.get())) if (isEnd(c)) return output; + throw Booking::InvalidSeat(); // unexpected character + } catch(...) { + throw Booking::InvalidSeat(); + } +} + +int main(int ragc, char* argv[]) { + std::ostream& os = std::cout; + std::istream& is = std::cin; + Menu::State state = Menu::State::MENU; + Booking::Booker h; + Booking::seat_range range; + + for (;;) try { + state = Menu::State::MENU; + os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR; is >> state; + + switch (state) { + + case Menu::State::RESERVE: + os << "Seat(s) to be reserved:\n" << Menu::CURSOR; + range = getRange(is); + h.reserveSeat(range); + os << Menu::GREEN << range << "reserved\n" << Menu::RESET; + break; + + case Menu::State::CANCEL: + os << "Seat(s) to be cancelled:\n" << Menu::CURSOR; + range = getRange(is); + h.cancelSeat(range); + os << Menu::GREEN << range << "cancelled\n" << Menu::RESET; + break; + + case Menu::State::BREAK: + os << "Seat(s) to be broken:\n" << Menu::CURSOR; + range = getRange(is); + h.breakSeat(range, os, Menu::RED); + os << Menu::RESET << range << "broken"; + break; + + case Menu::State::CHECK: + os << "Seat(s) to be checked:\n" << Menu::CURSOR; + range = getRange(is); + h.print(range, os); + break; + + + case Menu::State::FILTER_BROKEN: + os << "Avaliable seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::BROKEN; }, os); + break; + + case Menu::State::FILTER_AVALIABLE: + os << "Avaliable seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::AVALIBLE; }, os); + break; + + case Menu::State::FILTER_RESERVED: + os << "Reserved seats are: "; + h.filter([](const Booking::Seat& s) -> bool { return s.s == Booking::Seat::Status::RESERVED; }, os); + break; + + case Menu::State::EXIT: + os << "Exiting...\n"; + return EXIT_SUCCESS; + break; + + default: + throw Booking::InvalidArgument(); + } + } catch (Booking::BookingException& e) { + os << Menu::RED << e.what() << Menu::RESET << "\n"; + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + // return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/CT3/menugen.cpp b/CT3/menugen.cpp new file mode 100644 index 0000000..6b7b6bf --- /dev/null +++ b/CT3/menugen.cpp @@ -0,0 +1,138 @@ +#ifndef MENUGEN_0219820379823487123785 +#define MENUGEN_0219820379823487123785 + +// This is collection of utility constexpr tools for generating menu dynamically in compile time +// Menu::generateMenuMessage basically creates this: + +// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n"; +// enum class State : int { +// MENU = 0, +// SETUP = 1, +// RESERVE = 2, +// CANCEL = 3, +// CHECK = 4, +// EXIT = 5, +// }; + +// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message +// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there. +// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element +// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours, +// but they are a lot less versatile than std::array, so Ive got first prototype working with them. +// But STL containers are forbidden in CT2... so i wrote my own ConstString :) + +// With constexpr ready container, generating message is simple as +// 1. transforming number(enum member) to string +// 2. summing up all the submessage lengths +// 3. copying to single container + +// I was suprised cpp20 doesnt include way to transform int to string in constexpr. +// So i achived this with recursive template ExpandDecimal - name is self explanatory. +// Calculating total length is just sum of the sizes of expanded template pack +// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library + +// thats all, hope you find it interesting :) + +#include // index_sequence +namespace Menu{ + constexpr auto RESET = "\033[0m"; + constexpr auto RED = "❌ \033[31m"; + constexpr auto GREEN = "✅ \033[32m"; + constexpr auto YELLOW = "❌ \033[33m"; + constexpr auto BOLDWHITE = "\033[1m\033[37m"; + + constexpr auto CURSOR = "> "; + + using StateType = size_t; + + //################################################################################### + // helper char[] wrapper since it cannot be directly returned + template + struct ConstString { char data[size + 1];}; + + template + std::ostream& operator<<(std::ostream& os, const ConstString& r) { + for (char c : r.data) + os << c; + return os; + } + + //################################################################################### + // helpers to convert int (from enum) to string in ################################## + // shared array to store result + template + struct to_chars { static const char value[]; }; + + // convert single digit to char + template + // constexpr char to_chars::value[] = {('0' + digits)...}; + constexpr char to_chars::value[] = {('0' + digits)..., 0}; // null termination + + // when reminder is not zero - recursive expand for each decimal digit + template + struct ExpandDecimal : ExpandDecimal {}; + + // when reminder is not zero - call to_char for last digit + template + struct ExpandDecimal<0, digits...> : to_chars {}; + + // entry point - to by called directly + template + struct NumToString : ExpandDecimal {}; + //################################################################################### + + // helper templates to calculate total length as constexpr ########################## + template + constexpr std::size_t total_length(Strings&&... strings) { + return (0 + ... + std::size(strings)); + } + //################################################################################### + + // helper templates to concatonate as constexpr ##################################### + template // analog to copy_n from + static constexpr void copy_str(char* dest, std::size_t& idx, + Str str, std::index_sequence) { + ((dest[idx++] = str[I]), ...); + } + + template + constexpr auto concat(Strings&&... strings) { + constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1)); + + ConstString result; std::size_t idx = 0; + ((copy_str(result.data, idx, strings, // copy each string to result + std::make_index_sequence{}), ...)); + + result.data[total_len] = '\0'; + return result; + } + //################################################################################### + + // tests ############################################################################ + static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check"); + static_assert(NumToString<1>::value[0] == '1', "NumToString: 1"); + static_assert(NumToString<12345678900>::value[0] == '1' && + NumToString<12345678900>::value[1] == '2' && + NumToString<12345678900>::value[2] == '3' && + NumToString<12345678900>::value[3] == '4' && + NumToString<12345678900>::value[4] == '5' && + NumToString<12345678900>::value[5] == '6' && + NumToString<12345678900>::value[6] == '7' && + NumToString<12345678900>::value[7] == '8' && + NumToString<12345678900>::value[8] == '9' && + NumToString<12345678900>::value[9] == '0' && + NumToString<12345678900>::value[10] == '0' && + NumToString<12345678900>::value[11] == '\0'&& + sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum"); + + static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check"); + static_assert(concat("a", "bc", "def").data[0] == 'a' && + concat("a", "bc", "def").data[1] == 'b' && + concat("a", "bc", "def").data[2] == 'c' && + concat("a", "bc", "def").data[3] == 'd' && + concat("a", "bc", "def").data[4] == 'e' && + concat("a", "bc", "def").data[5] == 'f' && + concat("a", "bc", "def").data[6] == '\0', "concat: content"); +} + +#endif //MENUGEN_0219820379823487123785 \ No newline at end of file diff --git a/CT4/Makefile b/CT4/Makefile new file mode 100644 index 0000000..28efb1b --- /dev/null +++ b/CT4/Makefile @@ -0,0 +1,11 @@ +CXX = g++ +CXXFLAGS = -std=c++20 -Wall -pedantic -g +LDFLAGS = + + +main: main.cpp booking.cpp menugen.cpp menu.cpp server.cpp module.cpp + $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) + +.PHONY clean: + rm -f main + diff --git a/CT4/booking.cpp b/CT4/booking.cpp new file mode 100644 index 0000000..575fec9 --- /dev/null +++ b/CT4/booking.cpp @@ -0,0 +1,166 @@ +#pragma once +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "menugen.cpp" + +namespace Booking { + using seat_num = int64_t; + using seat_range = std::pair; + using uiBool = bool; + + class BookingException : public std::exception { + public: const char* what() const noexcept override { return "Internal exception!"; } + }; + + #define EXCEPTION(INTERNAL, EXTARNAL) \ + class INTERNAL : public BookingException { \ + const char* what() const noexcept override { return EXTARNAL; } \ + }; + + EXCEPTION(AlreadyReserved, "Already reserved!") + EXCEPTION(NotReserved, "Not reserved!") + EXCEPTION(InvalidSeat, "Invalid seat number!"); + EXCEPTION(InvalidArgument, "Invalid argument!") + + struct Seat { + enum class Status { + AVALIBLE, RESERVED, BROKEN + }; + + enum class Type { + VIP, REGULAR, CHEAP + }; + + Status s = Status::AVALIBLE; + Type t; + std::string reservation_name; + Seat(Type t) : t(t) {} + }; + + class Booker { + private: + std::map< seat_num, Seat > seats; + std::map< Seat::Type, std::stack< seat_num > > avaliables = + {{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}}; + std::map< Seat::Type, std::queue< std::string > > queues = + {{Seat::Type::VIP, {}}, {Seat::Type::REGULAR, {}}, {Seat::Type::CHEAP, {}}}; + + std::stack< std::pair< seat_num, std::string > > cancellations; + + mutable std::shared_mutex m; + public: + Booker() { + for (seat_num i = 0; i < 100; ++i) { + seats.emplace(seats.size() + 1, Seat::Type::REGULAR); + avaliables[Seat::Type::REGULAR].emplace(seats.size()); + } + + for (seat_num i = 0; i < 1; ++i) { + seats.emplace(seats.size() + 1, Seat::Type::VIP); + avaliables[Seat::Type::VIP].emplace(seats.size()); + } + + for (seat_num i = 0; i < 2; ++i) { + seats.emplace(seats.size() + 1, Seat::Type::CHEAP); + avaliables[Seat::Type::CHEAP].emplace(seats.size()); + } + } + + void reserveSeat(Seat::Type t, const std::string& name, std::ostream& os) { + m.lock(); + if (avaliables[t].size()) { + seat_num s = avaliables[t].top(); + avaliables[t].pop(); + + seats.at(s).s = Seat::Status::RESERVED; + seats.at(s).reservation_name = name; + os << "Seat " << s << " it now reserved" << std::endl; + } else { + queues[t].push(name); + os << "All seats of this type occupied, youre " << queues[t].size() << " in queue" << std::endl; + } + m.unlock(); + } + + void cancelSeat(std::ostream& os, std::string name) { + m.lock(); + bool c = false; + for (auto& i : seats) { + Seat& s = i.second; + if (s.reservation_name != name) + continue; + c = true; + cancellations.emplace(i.first, name); + // check for waiting + if (queues[s.t].empty()) { + s.s = Seat::Status::AVALIBLE; + s.reservation_name.clear(); + avaliables[s.t].emplace(i.first); + os << Menu::GREEN << "Reservation at " << i.first << " cancelled, noboady is waiting\n" << Menu::RESET << std::flush; + } else { + auto c = queues[s.t].front(); + queues[s.t].pop(); + + s.reservation_name = c; + os << Menu::GREEN << "Reservation at " << i.first << " cancelled" << Menu::RESET << ", " << c << " got the seat" << std::endl; + } + } + if (!c) + os << Menu::RED << "No reservation at this name found!\n" << Menu::RESET << std::flush; + m.unlock(); + } + + void print(std::ostream& os) const { + m.lock_shared(); + for (const auto& i : seats) { + const Seat& s = i.second; + seat_num n = i.first; + std::string type; + + switch (s.t) { + case Seat::Type::VIP: type = "VIP"; break; + case Seat::Type::REGULAR: type = "Regular"; break; + case Seat::Type::CHEAP: type = "Cheap"; break; + } + + switch (s.s) { + case Seat::Status::AVALIBLE: os << Menu::GREEN << type << " seat " << n << " is avaliable\n" << Menu::RESET << std::flush; break; + case Seat::Status::RESERVED: os << Menu::YELLOW << type << " seat " << n << " is reserved" << Menu::RESET << " by \"" << s.reservation_name << "\"\n" << std::flush; break; + case Seat::Status::BROKEN: os << Menu::RED << type << " seat " << n << " is broken\n" << Menu::RESET << std::flush; break; + default: break; + } + } + + std::stack tmp = cancellations; + os << "past cancellations:" << std::endl; + while (tmp.size()) { + auto i = tmp.top(); + tmp.pop(); + os << "seat " << i.first << " by " << i.second << std::endl; + } + m.unlock_shared(); + } + + std::pair calculateSeatStatistics() const; + std::vector getSortedClients() const; + std::pair getHighestSeatReservation() const; + void displaySeatsReversed(std::ostream& os) const; + size_t countBookingsByClient(const std::string& name) const; + size_t replaceClientName(const std::string& oldName, const std::string& newName); + std::vector getVipClients() const; + }; +} + +std::ostream& operator<<(std::ostream& os, const Booking::Booker& b) { + b.print(os); + return os; +} \ No newline at end of file diff --git a/CT4/main.cpp b/CT4/main.cpp new file mode 100644 index 0000000..36c67aa --- /dev/null +++ b/CT4/main.cpp @@ -0,0 +1,10 @@ +#include "menu.cpp" +#include "server.cpp" + +int main(int ragc, char* argv[]) { + void* ctx = menuInit(); + + serve(menu, ctx); + + menuCleanup(ctx); +} \ No newline at end of file diff --git a/CT4/menu.cpp b/CT4/menu.cpp new file mode 100644 index 0000000..cba2319 --- /dev/null +++ b/CT4/menu.cpp @@ -0,0 +1,235 @@ +#include +#include // streamsize::max +#include +#include "booking.cpp" + +/* declaring X(option) generates: + - Ordered menu message entry + - Ordered Menu::State::option symbol +*/ +#define MENU_ENTRIES \ + X(RESERVE), \ + X(CANCEL), \ + X(CHECK), \ + X(ACCUMULATE), \ + X(SORT), \ + X(MAX_ELEMENT), \ + X(REVERSE), \ + X(COUNT), \ + X(REPLACE), \ + X(COPY), \ + X(EXIT), \ + X(SHUTDOWN) + +#include "menugen.cpp" + +#include "module.cpp" + +namespace Menu { + // generate enum + #define X(entry) entry + enum class State : StateType{ + MENU = 0, // default state + MENU_ENTRIES, + COUNT_TAG // number of menu entries + }; + #undef X + + // generate menu message + constexpr auto generateMenuMessage() { + constexpr char menuMsg[] = "=== Ticket Booking System ==="; + constexpr char dot[] = ". "; + constexpr char nl[] = "\n"; + constexpr auto str = concat( nl, nl, menuMsg, nl, + #define X(entry) NumToString(State::entry)>::value, dot, #entry, nl + MENU_ENTRIES + #undef X + ); + return str; + } +} + +constexpr auto MENU_MSG = Menu::generateMenuMessage(); + +std::istream& operator>>(std::istream& is, Menu::State& state) { + if (!(is >> reinterpret_cast(state))) throw Booking::InvalidArgument(); + return is; +} + +std::ostream& operator<<(std::ostream& os, Booking::seat_range r) { + if (r.second == r.first) os << " seat " << r.first << " is " << std::flush; + else os << " seats " << r.first << "-" << r.second << " are " << std::flush; + return os; +} + +Booking::Seat::Type getType(std::istream& is, std::ostream& os) { + uint64_t n; + os << "Enter seat type: 0 - VIP, 1 - REGULAR, 2 - CHEAP\n" << std::flush; + if (!(is >> n) || (n != 0 && n != 1 && n != 2)) { + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + throw Booking::InvalidArgument(); + } + + return static_cast(n); +} + +std::string getName(std::istream& is, std::ostream& os) { + std::string name; + os << "Enter name: " << std::flush; + is >> std::ws; + + if (!std::getline(is, name) || name.empty()) { + is.clear(); + throw Booking::InvalidArgument(); + } + + return name; +} + +std::pair getRange(std::istream& is) { + auto isEnd = [](char c) { return c == '\n' || c == EOF; }; + try { + std::pair output; + // if (!(is >> output.first) || output.first < 1 || output.first > Booking::SEAT_CAPACITY) + // throw Booking::InvalidSeat(); + output.second = output.first; + while (!std::isdigit(is.peek())) { + char c = is.get(); + if (c == '-') break; + if (isEnd(c)) return output; + if (!std::iswspace(c)) throw Booking::InvalidSeat(); + } + // if (!(is >> output.second) || output.second < 1 || output.second > Booking::SEAT_CAPACITY) + // throw Booking::InvalidSeat(); + char c = is.peek(); + if (isEnd(c)) { + is.get(); // clear newline or eof + if (output.first > output.second) + std::swap(output.first, output.second); + return output; + } + while (std::iswspace(c - is.get())) if (isEnd(c)) return output; + throw Booking::InvalidSeat(); // unexpected character + } catch(...) { + throw Booking::InvalidSeat(); + } +} + +void* menuInit() { + return new Booking::Booker; + // return nullptr; +} + +void menuCleanup(void* ctx) { + Booking::Booker* h = reinterpret_cast(ctx); + delete h; +} + +bool menu(std::istream& is, std::ostream& os, void* ctx) { + Menu::State state = Menu::State::MENU; + Booking::Booker& h = *reinterpret_cast(ctx); + Booking::Seat::Type t; + std::string name, name_rep; + + //---------------- + std::pair stats; + std::vector sorted; + std::pair cli; + size_t cnt; + std::vector vips; + //---------------- + + Booking::seat_range range; + + for (;;) try { + state = Menu::State::MENU; + os << Menu::BOLDWHITE << MENU_MSG << Menu::RESET << Menu::CURSOR << std::flush; is >> state; + + switch (state) { + + case Menu::State::RESERVE: + os << "Seat(s) to be reserved:\n" << Menu::CURSOR << std::flush; + t = getType(is, os); + name = getName(is, os); + h.reserveSeat(t, name, os); + break; + + case Menu::State::CANCEL: + os << "Seat(s) to be cancelled:\n" << Menu::CURSOR << std::flush; + name = getName(is, os); + h.cancelSeat(os, name); + break; + + case Menu::State::CHECK: + h.print(os); + break; + + case Menu::State::EXIT: + os << "Client exiting..." << std::endl; + return EXIT_SUCCESS; + break; + + case Menu::State::SHUTDOWN: + os << "Sending shutdown signal..." << std::endl; + return 1; + + // ------------- NEW ---------------------------------------------- + case Menu::State::ACCUMULATE: + stats = h.calculateSeatStatistics(); + os << "Avaliable: " << stats.first << std::endl; + os << "Reserved: " << stats.second << std::endl; + break; + + case Menu::State::SORT: + sorted = h.getSortedClients(); + os << "Clients:\n"; + for (const auto& i : sorted) + os << i << "\n"; + os << std::flush; + break; + + case Menu::State::MAX_ELEMENT: + cli = h.getHighestSeatReservation(); + os << "Seat " << cli.first << " by " << cli.second << std::endl; + break; + + case Menu::State::REVERSE: + h.displaySeatsReversed(os); + break; + + case Menu::State::COUNT: + name = getName(is, os); + cnt = h.countBookingsByClient(name); + os << Menu::GREEN << "Count: " << cnt << Menu::RESET << std::endl; + break; + + case Menu::State::REPLACE: + name = getName(is, os); + os << "Replace with" << std::endl; + name_rep = getName(is, os); + os << Menu::GREEN << h.replaceClientName(name, name_rep) << " seats replaced" << Menu::RESET << std::endl; + break; + + case Menu::State::COPY: + vips = h.getVipClients(); + os << "VIP clients:\n"; + for (const auto& i : vips) + os << Menu::GREEN << i << "\n"; + os << Menu::RESET << std::flush; + break; + + // ---------------------------------------------------------------- + + default: + throw Booking::InvalidArgument(); + } + } catch (Booking::BookingException& e) { + os << Menu::RED << e.what() << Menu::RESET << std::endl; + is.clear(); + is.ignore(std::numeric_limits::max(), '\n'); + // return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/CT4/menugen.cpp b/CT4/menugen.cpp new file mode 100644 index 0000000..494e261 --- /dev/null +++ b/CT4/menugen.cpp @@ -0,0 +1,143 @@ +#ifndef MENUGEN_0219820379823487123785 +#define MENUGEN_0219820379823487123785 + +#if __cplusplus < 202002L +#error This code requires C++20 +#endif + + +// This is collection of utility constexpr tools for generating menu dynamically in compile time +// Menu::generateMenuMessage basically creates this: + +// constexpr const char* MENU_MSG = "\n\n=== Ticket Booking System ===\n1. Configure\n2. Reserve\n3. Cancel\n4. Check\n5. Exit\n"; +// enum class State : int { +// MENU = 0, +// SETUP = 1, +// RESERVE = 2, +// CANCEL = 3, +// CHECK = 4, +// EXIT = 5, +// }; + +// Its kinda long & ugly because we need to generate both symbols in enum and use their values in the message +// Menu entries are defined with generic macro X() which is defined where menu is expanded and formats each entry there. +// This is trivial for pure enum creation, but we also need to count them, this is achived with Menu::State::COUNT beeing last element +// Next step is creating message. To be constexpr we cant use string, I experimented with string_view for few hours, +// but they are a lot less versatile than std::array, so Ive got first prototype working with them. +// But STL containers are forbidden in CT2... so i wrote my own ConstString :) + +// With constexpr ready container, generating message is simple as +// 1. transforming number(enum member) to string +// 2. summing up all the submessage lengths +// 3. copying to single container + +// I was suprised cpp20 doesnt include way to transform int to string in constexpr. +// So i achived this with recursive template ExpandDecimal - name is self explanatory. +// Calculating total length is just sum of the sizes of expanded template pack +// And copying with std::array would be just std::copy_n, but same can be achived on my structure with index_sequence from utility library + +// thats all, hope you find it interesting :) + +#include // index_sequence +namespace Menu{ + constexpr auto RESET = "\033[0m"; + constexpr auto RED = "❌ \033[31m"; + constexpr auto GREEN = "✅ \033[32m"; + constexpr auto YELLOW = "❌ \033[33m"; + constexpr auto BOLDWHITE = "\033[1m\033[37m"; + + constexpr auto CURSOR = "> "; + + using StateType = size_t; + + //################################################################################### + // helper char[] wrapper since it cannot be directly returned + template + struct ConstString { char data[size + 1];}; + + template + std::ostream& operator<<(std::ostream& os, const ConstString& r) { + for (char c : r.data) + os << c; + return os; + } + + //################################################################################### + // helpers to convert int (from enum) to string in ################################## + // shared array to store result + template + struct to_chars { static const char value[]; }; + + // convert single digit to char + template + // constexpr char to_chars::value[] = {('0' + digits)...}; + constexpr char to_chars::value[] = {('0' + digits)..., 0}; // null termination + + // when reminder is not zero - recursive expand for each decimal digit + template + struct ExpandDecimal : ExpandDecimal {}; + + // when reminder is not zero - call to_char for last digit + template + struct ExpandDecimal<0, digits...> : to_chars {}; + + // entry point - to by called directly + template + struct NumToString : ExpandDecimal {}; + //################################################################################### + + // helper templates to calculate total length as constexpr ########################## + template + constexpr std::size_t total_length(Strings&&... strings) { + return (0 + ... + std::size(strings)); + } + //################################################################################### + + // helper templates to concatonate as constexpr ##################################### + template // analog to copy_n from + static constexpr void copy_str(char* dest, std::size_t& idx, + Str str, std::index_sequence) { + ((dest[idx++] = str[I]), ...); + } + + template + constexpr auto concat(Strings&&... strings) { + constexpr std::size_t total_len = (0 + ... + (std::size(strings) - 1)); + + ConstString result; std::size_t idx = 0; + ((copy_str(result.data, idx, strings, // copy each string to result + std::make_index_sequence{}), ...)); + + result.data[total_len] = '\0'; + return result; + } + //################################################################################### + + // tests ############################################################################ + static_assert(sizeof(NumToString<1>::value) == 2, "NumToString: size check"); + static_assert(NumToString<1>::value[0] == '1', "NumToString: 1"); + static_assert(NumToString<12345678900>::value[0] == '1' && + NumToString<12345678900>::value[1] == '2' && + NumToString<12345678900>::value[2] == '3' && + NumToString<12345678900>::value[3] == '4' && + NumToString<12345678900>::value[4] == '5' && + NumToString<12345678900>::value[5] == '6' && + NumToString<12345678900>::value[6] == '7' && + NumToString<12345678900>::value[7] == '8' && + NumToString<12345678900>::value[8] == '9' && + NumToString<12345678900>::value[9] == '0' && + NumToString<12345678900>::value[10] == '0' && + NumToString<12345678900>::value[11] == '\0'&& + sizeof(NumToString<12345678900>::value) == 12, "NumToString: longnum"); + + static_assert(sizeof(concat("a", "bc", "def")) == sizeof("abcdef") , "concat: size check"); + static_assert(concat("a", "bc", "def").data[0] == 'a' && + concat("a", "bc", "def").data[1] == 'b' && + concat("a", "bc", "def").data[2] == 'c' && + concat("a", "bc", "def").data[3] == 'd' && + concat("a", "bc", "def").data[4] == 'e' && + concat("a", "bc", "def").data[5] == 'f' && + concat("a", "bc", "def").data[6] == '\0', "concat: content"); +} + +#endif //MENUGEN_0219820379823487123785 \ No newline at end of file diff --git a/CT4/module.cpp b/CT4/module.cpp new file mode 100644 index 0000000..beabc1c --- /dev/null +++ b/CT4/module.cpp @@ -0,0 +1,114 @@ +// #include "booking.cpp" +#include +#include + +namespace Booking { +// 1. Calculate total free/booked seats - std::accumulate +std::pair Booker::calculateSeatStatistics() const { + m.lock_shared(); + auto ret = std::accumulate(seats.begin(), seats.end(), std::make_pair(0, 0), + [](std::pair acc, const std::pair& p) { + if (p.second.s == Seat::Status::AVALIBLE) ++acc.first; + if (p.second.s == Seat::Status::RESERVED) ++acc.second; + return acc; + } + ); + m.unlock_shared(); + + return ret; +} + +// 2. Sort clients by name using std::sort +std::vector Booker::getSortedClients() const { + m.lock_shared(); + std::vector clients; + for (const auto& kv : seats) { + if (kv.second.s == Seat::Status::RESERVED) + clients.push_back(kv.second.reservation_name); + } + m.unlock_shared(); + + std::sort(clients.begin(), clients.end()); + return clients; +} + +// 3. Find the client with the highest seat number using std::max_element +std::pair Booker::getHighestSeatReservation() const { + m.lock_shared(); + auto it = std::max_element(seats.begin(), seats.end(), + [](auto& a, auto& b) { + bool ra = (a.second.s == Seat::Status::RESERVED); + bool rb = (b.second.s == Seat::Status::RESERVED); + if (ra != rb) return !ra; + return a.first < b.first; + } + ); + auto end = seats.end(); + m.unlock_shared(); + + if (it == end || it->second.s != Seat::Status::RESERVED) + throw NotReserved(); + return { it->first, it->second.reservation_name }; +} + +// 4. Reverse display of seats using std::reverse and iteration +void Booker::displaySeatsReversed(std::ostream& os) const { + m.lock_shared(); + std::vector> vec(seats.begin(), seats.end()); + m.unlock_shared(); + + std::reverse(vec.begin(), vec.end()); + for (const auto& kv : vec) { + os << "Seat " << kv.first << ": "; + switch (kv.second.s) { + case Seat::Status::AVALIBLE: os << Menu::GREEN << "Available"; break; + case Seat::Status::RESERVED: os << Menu::YELLOW << "Reserved by " << kv.second.reservation_name; break; + case Seat::Status::BROKEN: os << "Broken"; break; + } + os << Menu::RESET << std::endl; + } +} + +// 5. Count bookings by a specific client using std::count_if +size_t Booker::countBookingsByClient(const std::string& name) const { + m.lock_shared(); + auto ret = std::count_if(seats.begin(), seats.end(), + [&](auto& kv){ return kv.second.s == Seat::Status::RESERVED && kv.second.reservation_name == name; } + ); + m.unlock_shared(); + + return ret; +} + +// 6. Replace a client name across all bookings using std::replace_if +size_t Booker::replaceClientName(const std::string& oldName, const std::string& newName) { + m.lock(); + size_t cnt = 0; + std::for_each(seats.begin(), seats.end(), + [&](auto& kv) { + if (kv.second.s == Seat::Status::RESERVED && kv.second.reservation_name == oldName) { + kv.second.reservation_name = newName; + ++cnt; + } + } + ); + m.unlock(); + return cnt; +} + +// 7. Copy all VIP clients to a separate list using std::copy_if +std::vector Booker::getVipClients() const { + m.lock_shared(); + std::vector> vip; + std::vector ret; + std::copy_if(seats.begin(), seats.end(), std::back_inserter(vip), + [](const auto& kv){ return kv.second.s == Seat::Status::RESERVED && kv.second.t == Seat::Type::VIP; } + ); + m.unlock_shared(); + + // extract names + for (const auto& i : vip) + ret.push_back(i.second.reservation_name); + return ret; +} +} diff --git a/CT4/server.cpp b/CT4/server.cpp new file mode 100644 index 0000000..61467ca --- /dev/null +++ b/CT4/server.cpp @@ -0,0 +1,67 @@ +#include // socket functions +#include // sockaddr_in +#include // exit() and EXIT_FAILURE +#include // cout +#include // read +#include + +#include // buffered stream + +#include + +using SOCKET_FD = int; +constexpr uint16_t LISTEN_PORT = 42069; +const int yes = 1; +SOCKET_FD listener = -1, connection = -1; + +void handle_client(SOCKET_FD client_fd, bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) { + using Filebuf = __gnu_cxx::stdio_filebuf; + + // setup input stream + FILE* fin = fdopen(client_fd, "r"); + Filebuf in_buf(fin, std::ios::in); + std::istream is(&in_buf); + + // setup output stream + FILE* fout = fdopen(client_fd, "w"); + Filebuf out_buf(fout, std::ios::out); + std::ostream os(&out_buf); + + signal(SIGPIPE, SIG_IGN); // ignore pipe signal + + if (handler(is, os, ctx)) { + shutdown(listener, SHUT_RD); + } + + fclose(fin); + fclose(fout); + close(client_fd); +} + +void serve(bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) { + sockaddr_in addr{ .sin_family = AF_INET, .sin_port = htons(LISTEN_PORT), .sin_addr = in_addr{ .s_addr = INADDR_ANY } }; + + if ((listener = socket(AF_INET, SOCK_STREAM, 0)) < 0 || + setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0 || + bind(listener, (const sockaddr*) &addr, sizeof(addr)) < 0 || + listen(listener, 10) < 0) { + goto cleanup; + } + + while ((connection = accept(listener, nullptr, nullptr)) > 0) { + std::thread(handle_client, connection, handler, ctx).detach(); + } + + cleanup: + std::cout << "Server shutting down..." << std::endl; + if (connection != -1) close(connection); + if (listener != -1) close(listener); +} + +bool h(std::istream& is, std::ostream& os, void* ctx) { + std::string s; + os << "hello from " << std::this_thread::get_id() <<"! are you there?" << std::endl; + is >> s; + os << s << " to you too!" << std::endl; + return true; +}