diff --git a/source/simple/support/algorithm.hpp b/source/simple/support/algorithm.hpp index 4d3640cfad8cbcf7b8a96d5ab16618fffee34827..89831203807bdac35593d84176c7a5a3bcecb3e8 100644 --- a/source/simple/support/algorithm.hpp +++ b/source/simple/support/algorithm.hpp @@ -1,6 +1,8 @@ #include "algorithm/advance_vector.hpp" #include "algorithm/numeric.hpp" #include "algorithm/range_wrappers.hpp" +#include "algorithm/set_ops.hpp" +#include "algorithm/split.hpp" #include "algorithm/traits.hpp" #include "algorithm/utils.hpp" #include "algorithm/variance.hpp" diff --git a/source/simple/support/algorithm/range_wrappers.hpp b/source/simple/support/algorithm/range_wrappers.hpp index 15cb0b2671c5cc2d99bebfb7fc3317e72e970681..8f72213c9ba9d4b4b45db4b225d30a6aae7bed03 100644 --- a/source/simple/support/algorithm/range_wrappers.hpp +++ b/source/simple/support/algorithm/range_wrappers.hpp @@ -1,13 +1,24 @@ #ifndef SIMPLE_SUPPORT_ALGORITHM_RANGE_WRAPPERS_HPP #define SIMPLE_SUPPORT_ALGORITHM_RANGE_WRAPPERS_HPP #include <algorithm> +#include <string_view> namespace simple::support { // TODO: use the adl pattern for begin and end - unqualified call that defaults to std versions, noexcept being the tricky part of course - // rangey wrappers + + // TODO: check if string view has begin-end constructor(should since c++20) and replace this with alias + class string_view : public std::string_view + { + public: + using std::string_view::string_view; + string_view(const_iterator begin, const_iterator end) + : std::string_view(begin, end-begin) + {} + }; + template <typename Range> constexpr auto min_element(Range& range) diff --git a/source/simple/support/algorithm/set_ops.hpp b/source/simple/support/algorithm/set_ops.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e2bfc9fc670d597a00bf3879fcdbd15df5018cde --- /dev/null +++ b/source/simple/support/algorithm/set_ops.hpp @@ -0,0 +1,56 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_SET_OPS_HPP +#define SIMPLE_SUPPORT_ALGORITHM_SET_OPS_HPP +#include <functional> // all this for just std::less + +namespace simple::support +{ + + namespace detail + { + template <typename MinuendIt, typename SubtrahendIt, typename Less> + constexpr bool not_in_set + ( + SubtrahendIt& sub_begin, SubtrahendIt sub_end, + MinuendIt minuend, + Less&& less + ) + { + while(sub_begin != sub_end) + { + // TODO: I should use invoke, but it's not constexpr, and apply(forward_as_tuple) is unreadable + if(less(*minuend, *sub_begin)) + return true; + else if(less(*sub_begin, *minuend)) + ++sub_begin; + else + { + ++sub_begin; + return false; + } + } + return true; + } + } // namespace detail + + // cause standard version is not allowed to work in place for some reason + template <typename MinuendIt, typename SubtrahendIt, typename DifferenceIt, typename Less = std::less<>> + constexpr DifferenceIt set_difference + ( + MinuendIt begin, MinuendIt end, + SubtrahendIt sub_begin, SubtrahendIt sub_end, + DifferenceIt diff, + Less&& less = std::less<>{} + ) + { + while(begin != end) + { + if(detail::not_in_set(sub_begin, sub_end, begin, less)) + *diff++ = *begin; + ++begin; + } + return diff; + } + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/source/simple/support/algorithm/split.hpp b/source/simple/support/algorithm/split.hpp new file mode 100644 index 0000000000000000000000000000000000000000..97de62a14412c9dd6e1288652f04cec5b826b36b --- /dev/null +++ b/source/simple/support/algorithm/split.hpp @@ -0,0 +1,63 @@ +#ifndef SIMPLE_SUPPORT_ALGORITHM_SPLIT_HPP +#define SIMPLE_SUPPORT_ALGORITHM_SPLIT_HPP +#include "traits.hpp" +#include "../range.hpp" + +namespace simple::support +{ + + // cause we don't get a proper standard search until c++20, and even then it depends on super experimental ranges library + // (how does stuff like that even get in i wonder -_-) + template <typename It, typename NeedleIt> + simple::support::range<It> search(It begin, It end, NeedleIt nbegin, NeedleIt nend) + { + while(true) + { + It found = begin; + NeedleIt ncurrent = nbegin; + do + if(ncurrent == nend) + return {begin, found}; + else if(found == end) + return {end, end}; + while(*found++ == *ncurrent++); + ++begin; + } + return {end,end}; // unreachable + } + + template <typename It, typename SepIt, typename OutIt> + auto split(It begin, It end, SepIt s_begin, SepIt s_end, OutIt out) + { + assert(s_begin != s_end); // TODO: implement a version that skips consecutive separators, and will return character by character split in this case + auto prev = begin; + auto next = end; + do + { + auto found = support::search(prev, end, + s_begin, s_end); + next = found.begin(); + *out++ = {prev, next}; + prev = found.end(); + } + while(end != next); + + return out; + } + + template <typename Range, typename Separator, typename OutIt, + std::enable_if_t< + is_range_v<Range> && + is_range_v<Separator> + >* = nullptr + > + auto split(const Range& in, const Separator& seperator, OutIt out) + { + using std::begin; + using std::end; + return split(begin(in), end(in), begin(seperator), end(seperator), out); + } + +} // namespace simple::support + +#endif /* end of include guard */ diff --git a/unit_tests/algorithm.cpp b/unit_tests/algorithm.cpp index 2c4e6954a1c6c060b07a5c5750a860044c8efcff..ba779e2bd37dc641c5b13e4821ed0ff68b1cbc1c 100644 --- a/unit_tests/algorithm.cpp +++ b/unit_tests/algorithm.cpp @@ -1,10 +1,13 @@ +// TODO: need a test file per header and include the header first, to detect missing includes + #include <cassert> #include <vector> #include <numeric> #include "simple/support/algorithm.hpp" -using namespace simple::support; +using namespace simple; +using namespace support; void MultidimentionalIteration() { @@ -218,6 +221,126 @@ void TypeTraits() static_assert(is_range_v<adl_range_test::segment>); } +void Search() +{ + { + char x[] = "aaab"; + char y[] = "aab"; + assert(::search(std::begin(x), std::end(x), std::begin(y), std::end(y)).begin() == x + 1); + assert(::search(std::begin(x), std::end(x), std::begin(y), std::end(y)).end() == std::end(x)); + assert(*(std::end(x) - 1) == '\0'); + } + { + char x[] = "aaab"; + char y[] = "aba"; + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).begin() == std::end(x)); + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).end() == std::end(x)); + } + { + std::array<char,4> x = {'a','a','a','b'}; + char y[] = "aba"; + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).begin() == std::end(x)); + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).end() == std::end(x)); + } + { + std::array<char,4> x = {'a','a','a','b'}; + std::array<char,3> y = {'a','b','a'}; + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).begin() == std::end(x)); + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).end() == std::end(x)); + } + { + char x[] = "aaab"; + std::array<char,0> y{}; + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).begin() == std::begin(x)); + assert(search(std::begin(x), std::end(x), std::begin(y), std::end(y)).end() == std::begin(x)); + } +} + +auto split(std::string_view view, const std::string& separator) +{ + std::vector<string_view> result; + support::split(view, separator, std::back_inserter(result)); + return result; +} + +void Split() +{ + assert(( ::split("a--b--c", "--") == std::vector<string_view>{"a", "b", "c"} )); + assert(( ::split("a--b--c--", "--") == std::vector<string_view>{"a", "b", "c", ""} )); + assert(( ::split("--a--b--c", "--") == std::vector<string_view>{"", "a", "b", "c"} )); + assert(( ::split("--a--b--c--", "--") == std::vector<string_view>{"", "a", "b", "c", ""} )); + assert(( ::split("--""--""--""--""--a--b--c--", "--") == std::vector<string_view>{"", "", "", "", "", "a", "b", "c", ""} )); + assert(( ::split("--", "--") == std::vector<string_view>{"", ""} )); + assert(( ::split("", "--") == std::vector<string_view>{""} )); +} + +void SetDifference() +{ + { + std::array x {1,2,2,3,4,5}; + std::array y {2,4}; + std::array<int, 4> z; + support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(z)); + assert(( z == std::array{1,2,3,5} )); + } + + { + std::array x {5,4,3,2,2,1}; + std::array y {4,2}; + std::array<int, 4> z; + support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(z), std::greater<>{}); + assert(( z == std::array{5,3,2,1} )); + } + + { + std::array x {1,2,2,3,4,5}; + std::array y {2,4}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array diff{1,2,3,5}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } + + { + std::array x {1,2,2,3,4,5}; + std::array<int,0> y {}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array diff{1,2,2,3,4,5}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } + + { + std::array<int,0> x {}; + std::array y {1,2,3}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array<int,0> diff{}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } + + { + std::array x {1,2,3}; + std::array y {1,2,3}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array<int,0> diff{}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } + + { + std::array x {1,2,3}; + std::array y {1,2,3,4,5,6}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array<int,0> diff{}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } + + { + std::array x {12,12,12,12,12,12,12}; + std::array y {12,12,12,12,12,12}; + auto diff_end = support::set_difference(std::begin(x), std::end(x), std::begin(y), std::end(y), std::begin(x)); + std::array diff{12}; + assert( std::equal(std::begin(x), diff_end, std::begin(diff)) ); + } +} + int main() { MultidimentionalIteration(); @@ -225,6 +348,9 @@ int main() IteratorRange(); Variance(); TypeTraits(); + Search(); + Split(); + SetDifference(); static_assert(Constexprness()); return 0; }