From e7e6e2f8f3a2f91a22e7624130130fb733f224ce Mon Sep 17 00:00:00 2001 From: Boris Skert Date: Sun, 12 Jun 2022 21:11:02 +0200 Subject: [PATCH] Translate 'A Simple Music Decoder' kata --- include/a_simple_music_decoder/uncompress.h | 14 ++ src/a_simple_music_decoder/uncompress.cpp | 205 ++++++++++++++++++ .../uncompress_test.cpp | 39 ++++ 3 files changed, 258 insertions(+) create mode 100644 include/a_simple_music_decoder/uncompress.h create mode 100644 src/a_simple_music_decoder/uncompress.cpp create mode 100644 test/a_simple_music_decoder/uncompress_test.cpp diff --git a/include/a_simple_music_decoder/uncompress.h b/include/a_simple_music_decoder/uncompress.h new file mode 100644 index 0000000..0ff824c --- /dev/null +++ b/include/a_simple_music_decoder/uncompress.h @@ -0,0 +1,14 @@ +#ifndef CPP_KATAS_UNCOMPRESS_H +#define CPP_KATAS_UNCOMPRESS_H + +#include +#include + +/** + * https://www.codewars.com/kata/58de42bab4b74c214d0000e2 + * @param music a string containing the compressed music + * @return a vector of the uncompressed int values + */ +std::vector uncompress(std::string music); + +#endif //CPP_KATAS_UNCOMPRESS_H diff --git a/src/a_simple_music_decoder/uncompress.cpp b/src/a_simple_music_decoder/uncompress.cpp new file mode 100644 index 0000000..6f2217f --- /dev/null +++ b/src/a_simple_music_decoder/uncompress.cpp @@ -0,0 +1,205 @@ +#include +#include +#include + +std::vector split(const std::string &s, const std::string &delimiter); + +class Inflatable { +private: + static Inflatable *create_from(const std::string &s); + +public: + [[nodiscard]] virtual std::vector inflate() const = 0; + + static std::vector map_from(const std::vector &strings) { + std::vector created; + + for (const std::string &string: strings) { + created.push_back(Inflatable::create_from(string)); + } + + return created; + } +}; + +std::vector inflate_all(std::vector &inflatables); + +std::vector uncompress(std::string music) { + const std::vector &compressed_values = split(music, ","); + std::vector inflatables = Inflatable::map_from(compressed_values); + + return inflate_all(inflatables); +} + +std::vector inflate_all(std::vector &inflatables) { + std::vector all_inflated; + + for (Inflatable *i: inflatables) { + const std::vector &inflated_items = i->inflate(); + + for (const int inflated_item: inflated_items) { + all_inflated.push_back(inflated_item); + } + } + + return all_inflated; +} + +template +int signum(T val) { + if (T(0) > val) { + return -1; + } + + if (T(0) < val) { + return 1; + } + + return 0; +} + +class SameInterval : public Inflatable { +private: + static std::regex interval_regex; + + const int _start; + const int _end; + const int _step; + + SameInterval(int start, int end, int step) : _start(start), _end(end), _step(step) {} + +public: + [[nodiscard]] std::vector inflate() const override { + std::vector inflated; + int i = _start; + + for (; i != _end; i += _step) { + inflated.push_back(i); + } + + inflated.push_back(i); + + return inflated; + } + + static Inflatable *create_from(const std::string &s) { + std::smatch match; + + if (!std::regex_search(s, match, interval_regex)) { + throw std::exception(); + } + + const int start = std::stoi(match[1]); + const int end = std::stoi(match[2]); + const std::string parsed_step = match[4]; + + const int sign = signum(end - start); + + if (parsed_step.length() > 0) { + int step = std::stoi(parsed_step) * sign; + return new SameInterval(start, end, step); + } else { + return new SameInterval(start, end, sign); + } + } + + static bool accept(const std::string &s) { + return std::regex_match(s, interval_regex); + } +}; + +std::regex SameInterval::interval_regex = std::regex(R"((-?\d+)-(-?\d+)(/(\d+))?)"); + +class Identical : public Inflatable { +private: + static std::regex identical_regex; + + const int _value; + const size_t _count; + + Identical(const int value, const int count) : _value(value), _count(count) {} + +public: + [[nodiscard]] std::vector inflate() const override { + std::vector inflated; + + for (size_t i = 0; i < _count; i++) { + inflated.push_back(_value); + } + + return inflated; + } + + static Inflatable *create_from(const std::string &s) { + std::vector value_count = split(s, "*"); + std::smatch match; + + if (!std::regex_search(s, match, identical_regex)) { + throw std::exception(); + } + + const int value = std::stoi(match[1]); + const int count = std::stoi(match[2]); + + return new Identical(value, count); + } + + static bool accept(const std::string &s) { + return std::regex_match(s, identical_regex); + } +}; + +std::regex Identical::identical_regex = std::regex(R"((-?\d+)[*](\d+))"); + +class Simple : public Inflatable { +private: + const int _value; + + explicit Simple(const int value) : _value(value) {} + +public: + [[nodiscard]] std::vector inflate() const override { + return std::vector{_value}; + } + + static Inflatable *create_from(const std::string &s) { + const int value = std::stoi(s); + return new Simple(value); + } +}; + +Inflatable *Inflatable::create_from(const std::string &s) { + if (SameInterval::accept(s)) { + return SameInterval::create_from(s); + } + + if (Identical::accept(s)) { + return Identical::create_from(s); + } + + return Simple::create_from(s); +} + +std::vector split(const std::string &s, const std::string &delimiter) { + std::vector substrings; + + size_t start = 0; + size_t end; + std::string substring; + + while ((end = s.find(delimiter, start)) != std::string::npos) { + size_t count = end - start; + + substring = s.substr(start, count); + substrings.push_back(substring); + + start = end + delimiter.length(); + } + + end = std::min(end, s.length()); + + substring = s.substr(start, end); + substrings.push_back(substring); + + return substrings; +} diff --git a/test/a_simple_music_decoder/uncompress_test.cpp b/test/a_simple_music_decoder/uncompress_test.cpp new file mode 100644 index 0000000..64c9e91 --- /dev/null +++ b/test/a_simple_music_decoder/uncompress_test.cpp @@ -0,0 +1,39 @@ +#include +#include "a_simple_music_decoder/uncompress.h" + +using namespace igloo; + +Describe(A_Simple_Music_Decoder_Tests) { + It(Sample_tests) { + Assert::That(uncompress("1,2*2,3"), Equals(std::vector{1, 2, 2, 3})); + Assert::That(uncompress("1,3-5,7"), Equals(std::vector{1, 3, 4, 5, 7})); + Assert::That(uncompress("1,5-3,7"), Equals(std::vector{1, 5, 4, 3, 7})); + Assert::That(uncompress("1,10-6/2,7"), Equals(std::vector{1, 10, 8, 6, 7})); + } + + It(Fixed_tests) { + Assert::That(uncompress("1,2*5,3*3"), Equals(std::vector{1, 2, 2, 2, 2, 2, 3, 3, 3})); + Assert::That(uncompress("1-3,3-7,9-12"), Equals(std::vector{1, 2, 3, 3, 4, 5, 6, 7, 9, 10, 11, 12})); + Assert::That(uncompress("10-100/10"), Equals(std::vector{10, 20, 30, 40, 50, 60, 70, 80, 90, 100})); + Assert::That(uncompress("-3--1,-3*5,-15--5/5"), Equals(std::vector{-3, -2, -1, -3, -3, -3, -3, -3, -15, -10, -5})); + } + + It(Fixed_Negative_tests) { + Assert::That(uncompress("-3--1"), Equals(std::vector{-3, -2, -1})); + Assert::That(uncompress("-3*5"), Equals(std::vector{-3, -3, -3, -3, -3})); + Assert::That(uncompress("-15--5/5"), Equals(std::vector{-15, -10, -5})); + Assert::That(uncompress("-3--1,-3*5,-15--5/5"), + Equals(std::vector{-3, -2, -1, -3, -3, -3, -3, -3, -15, -10, -5})); + } + + It(Further_tests) { + Assert::That(uncompress("25,170*5,149*2,161,2-6/2"), + Equals(std::vector{25, 170, 170, 170, 170, 170, 149, 149, 161, 2, 4, 6})); + Assert::That(uncompress("74,108,5*2,164-161,53,6-18/3,72-64/2"), + Equals(std::vector{74, 108, 5, 5, 164, 163, 162, 161, 53, 6, 9, 12, 15, 18, 72, 70, 68, 66, + 64})); + Assert::That(uncompress("-2*2"), Equals(std::vector{-2, -2})); + Assert::That(uncompress("1*2,2-5,7,9"), Equals(std::vector{1, 1, 2, 3, 4, 5, 7, 9})); + Assert::That(uncompress("1--5/2"), Equals(std::vector{1, -1, -3, -5})); + } +};