#ifndef _VTEST_VMAKE_HPP #define _VTEST_VMAKE_HPP #include #include "optional.hpp" namespace vmake { struct sequence_terminated_error : std::exception { virtual const char *what() const noexcept override final { return "iterating on a terminated sequence."; } }; namespace polyfill { template struct as_const_reference_t { using type = const typename std::remove_reference::type&; }; template inline constexpr auto as_const_reference(typename as_const_reference_t::type x) noexcept { return x; } template using optional = nonstd::optional; } // namespace polyfill namespace details { template struct iota_sequence { using result = T; T start; iota_sequence(const T& start) : start(start) {} bool is_terminated() const noexcept { return false; } auto operator()() { return start++; } }; template struct ranged_sequence { using result = T; T start, end; ranged_sequence(const T& start, const T& end) : start(start), end(end) {} bool is_terminated() const noexcept { return start == end; } auto operator()() { if (start == end) throw sequence_terminated_error(); return start++; } }; template struct ranged_step_sequence { using result = T; T start, end, step; ranged_step_sequence(const T& start, const T& end, const T& step) : start(start), end(end), step(step) {} bool is_terminated() const noexcept { return start == end; } auto operator()() { if (start == end) throw sequence_terminated_error(); T res = start; start += step; return res; } }; } // namespace details template inline auto iota(const Val &start) { return details::iota_sequence(start); } template inline auto range(const Val &start, const Val &end) { return details::ranged_sequence(start, end); } template inline auto range(const Val &start, const Val &end, const Val &step) { return details::ranged_step_sequence(start, end, step); } namespace details { template struct empty_sequence { using result = T; bool is_terminated() const noexcept { return true; } T operator()() { throw sequence_terminated_error(); } }; } // namespace details template inline auto nothing() { return details::empty_sequence(); } namespace details { template struct ranged_iterator_extractor { using result = typename std::remove_reference())>::type; ContIt cur, end; ranged_iterator_extractor(const ContIt &begin, const ContIt &end) : cur(begin), end(end) {} bool is_terminated() const noexcept { return cur == end; } auto operator()() { if (cur == end) throw sequence_terminated_error(); return *cur++; } }; template struct iterator_extractor { using result = typename std::remove_reference())>::type; ContIt it; bool added; iterator_extractor(const ContIt &begin) : it(begin), added(false) {}; constexpr bool is_terminated() const noexcept { return false; } auto operator()() { // WARN: use for strange iterators such as std::istream_iterator // do NOT try to optimize this into *it++ if (added) return *++it; added = true; return *it; } }; } // namespace details template inline auto extract(const ContIt &begin, const ContIt &end) { return details::ranged_iterator_extractor(begin, end); } template inline auto extract(const ContIt &begin) { return details::iterator_extractor(begin); } template inline auto extract_n(const ContIt &begin, size_t n) { return take(details::iterator_extractor(begin), n); } namespace details { template struct generator { using result = typename std::result_of::type; Func g; generator(Func &&g) : g(g) {} generator(const Func &g) : g(g) {} generator& operator=(generator &&y) = default; generator& operator=(const generator &y) = default; generator(generator &&y) = default; generator(const generator &y) = default; bool is_terminated() const noexcept { return false; } auto operator()() { return g(); } }; } // namespace details template inline auto make_generator(Args ...args) { return details::generator(std::move(Func(args...))); } template inline constexpr auto generate(Func &&f) { return details::generator(std::forward(f)); } namespace details { template struct limitor { using core = Gen; using result = typename Gen::result; Gen g; size_t lim; limitor(Gen &&g, size_t lim) : g(std::move(g)), lim(lim) {} limitor(limitor &&g) = default; limitor(const limitor &g) = default; bool is_terminated() const noexcept { return lim <= 0; } auto operator()() { if (lim-- > 0) return g(); else throw sequence_terminated_error(); } }; } // namespace details template inline auto take(Gen &&g, size_t lim) { return details::limitor::type>(std::move(g), lim); }; namespace details { template struct concator { using result = typename Gen1::result; using core = Gen1; using core2 = Gen2; Gen1 g1; Gen2 g2; concator(Gen1 &&g1, Gen2 &&g2) : g1(std::move(g1)), g2(std::move(g2)) {} concator(concator &&c) = default; concator(const concator &c) = default; // concator& operator=(concator &&c) = default; bool is_terminated() const noexcept { return g1.is_terminated() && g2.is_terminated(); } auto operator()() { if (g1.is_terminated()) return g2(); return g1(); } }; } // namespace details template inline constexpr auto concat(Gen1 &&x, Gen2 &&y) noexcept { return details::concator(std::move(x), std::move(y)); } namespace details { template struct transformer { using result = typename std::result_of::type; using core = Gen; Gen g; Func f; transformer(Gen &&g, Func &&gf) : g(std::move(g)), f(std::move(f)) {} transformer(transformer &&c) = default; transformer(const transformer &c) = default; // transformer& operator=(transformer &&c) = default; bool is_terminated() const noexcept { return g.is_terminated(); } auto operator()() { return f(g()); } }; } // namespace details template inline auto transform(Gen &&g, Func &&f) { return details::transformer(std::move(g), std::move(f)); } template struct filteror { using result = typename Gen::result; using core = Gen; filteror(filteror &&) = default; filteror(const filteror &) = default; mutable Gen g; Pred p; mutable polyfill::optional preview; filteror(Gen &&g, Pred &&p) : g(std::forward(g)), p(std::forward(p)), preview() { _find_next(); }; bool is_terminated() const noexcept { if (g.is_terminated() && !preview.has_value()) return true; if (!preview.has_value()) _find_next(); return !preview.has_value(); } auto operator()() { if (is_terminated()) throw sequence_terminated_error(); result res = std::move(*preview); preview.reset(); return res; } private: void _find_next() const { preview.reset(); do { if (g.is_terminated()) { preview.reset(); return; } preview.emplace(std::move(g())); } while (!p(polyfill::as_const_reference(*preview))); } }; template inline filteror::type, typename std::decay::type> filter(Gen &&g, Pred &&p) { return {std::forward(g), std::forward(p)}; } namespace rng { template inline auto common() { // NOTE: MinGW GCC older than 9.2 have a fixed random_device return make_generator(std::random_device{}()); } template inline auto common(Seed seed) { return make_generator(seed); } namespace details { struct cstyle_rng { using result = decltype(std::rand()); bool is_terminated() const noexcept { return false; } auto operator()() { return std::rand(); } }; } inline auto cstyle() { std::srand(time(0)); std::rand(); return details::cstyle_rng(); } inline auto cstyle(int seed) { std::srand(seed); std::rand(); return details::cstyle_rng(); } } template inline OutputIt copy(OutputIt it, Gen&& g) { while (!g.is_terminated()) { *it++ = g(); } return it; } template inline OutputIt copy_n(OutputIt it, size_t n, Gen&& g) { for (size_t i = 0; i < n; ++i) { *it++ = g(); } return it; } template inline auto output(OutputStream& out, const char *delim, Gen &&g) { return copy(std::ostream_iterator::type::result>(out, delim), std::move(g)); } template inline auto output(OutputStream& out, Gen &&g) { return copy(std::ostream_iterator::type::result>(out), std::move(g)); } template inline auto output_n(OutputStream& out, size_t n, const char *delim, Gen &&g) { return copy_n(std::ostream_iterator::type::result>(out, delim), n, std::move(g)); } template inline auto output_n(OutputStream& out, size_t n, Gen &&g) { return copy_n(std::ostream_iterator::type::result>(out), n, std::move(g)); } } #endif