#pragma once #include <Geode/DefaultInclude.hpp> #include <algorithm> #include <string> #include <concepts> #include <optional> #include <iterator> #ifndef __cpp_lib_concepts namespace std { // <concepts> isn't working for me lmao template <class From, class To> concept convertible_to = std::is_convertible_v<From, To> && requires { static_cast<To>(std::declval<From>()); }; } #endif #undef min #undef max namespace geode::utils::ranges { template <class C> concept ValidConstContainer = requires(C const& c) { c.begin(); c.end(); { c.size() } -> std::convertible_to<size_t>; typename C::value_type; typename C::iterator; typename C::const_iterator; }; template <class C> concept ValidMutContainer = requires(C& c) { c.begin(); c.end(); { c.size() } -> std::convertible_to<size_t>; typename C::value_type; typename C::iterator; typename C::const_iterator; }; template <class C> concept ValidContainer = ValidConstContainer<C> && ValidMutContainer<C>; template <class P, class C> concept ValidCUnaryPredicate = requires(P p, typename C::value_type const& t) { { p(t) } -> std::convertible_to<bool>; }; template <class P, class From, class Into> concept ValidIntoConverter = requires(P p, From const& t) { { p(t) } -> std::convertible_to<Into>; }; template <ValidConstContainer C> bool contains(C const& cont, typename C::value_type const& elem) { return std::find(cont.begin(), cont.end(), elem) != cont.end(); } template <ValidConstContainer C, ValidCUnaryPredicate<C> Predicate> bool contains(C const& cont, Predicate fun) { return std::find_if(cont.begin(), cont.end(), fun) != cont.end(); } template <ValidConstContainer C, ValidCUnaryPredicate<C> Predicate> std::optional<typename C::value_type> find(C const& cont, Predicate fun) { auto it = std::find_if(cont.begin(), cont.end(), fun); if (it != cont.end()) { return std::optional(*it); } return std::nullopt; } template <ValidConstContainer C> std::optional<size_t> indexOf(C const& cont, typename C::value_type const& elem) { auto it = std::find(cont.begin(), cont.end(), elem); if (it != cont.end()) { return std::optional(std::distance(cont.begin(), it)); } return std::nullopt; } template <ValidConstContainer C, ValidCUnaryPredicate<C> Predicate> std::optional<size_t> indexOf(C const& cont, Predicate fun) { auto it = std::find_if(cont.begin(), cont.end(), fun); if (it != cont.end()) { return std::optional(std::distance(cont.begin(), it)); } return std::nullopt; } template <ValidMutContainer C> bool move(C& cont, typename C::value_type const& elem, size_t where) { if (where > cont.size() - 1) { return false; } auto ix = indexOf(cont, elem); if (ix) { if (ix.value() > where) { std::rotate( cont.rend() - ix.value() - 1, cont.rend() - ix.value(), cont.rend() - where ); } else { std::rotate( cont.begin() + ix.value(), cont.begin() + ix.value() + 1, cont.begin() + where + 1 ); } return true; } return false; } template <ValidConstContainer C, class Output> requires std::is_default_constructible_v<Output> && std::is_convertible_v<Output, typename C::value_type> Output join(C const& cont, Output const& separator) { auto res = Output(); bool first = true; for (auto& p : cont) { if (!first) { res += separator; } else { first = false; } res += p; } return res; } template <ValidConstContainer C> std::string join(C const& cont, std::string const& separator) { auto res = std::string(); bool first = true; for (auto& p : cont) { if (!first) { res += separator; } else { first = false; } res += p; } return res; } template < ValidConstContainer C, class Output, ValidIntoConverter<typename C::value_type, Output> Conv> requires std::is_default_constructible_v<Output> Output join(C const& cont, Output const& separator, Conv converter) { auto res = Output(); bool first = true; for (auto& p : cont) { if (!first) { res += separator; } else { first = false; } res += converter(p); } return res; } template <ValidContainer C> C& push(C& container, C const& toAdd) { container.insert(container.end(), toAdd.begin(), toAdd.end()); return container; } template <ValidContainer C> C concat(C const& cont, typename C::value_type const& value) { auto copy = cont; copy.push_back(value); return copy; } template <ValidContainer C> C concat(C const& cont1, C const& cont2) { auto copy = cont1; ranges::push(copy, cont2); return copy; } template <ValidMutContainer C> C& remove(C& container, typename C::value_type const& value) { container.erase(std::remove(container.begin(), container.end(), value), container.end()); return container; } template <ValidMutContainer C, ValidCUnaryPredicate<C> Predicate> C& remove(C& container, Predicate fun) { container.erase( std::remove_if(container.begin(), container.end(), fun), container.end() ); return container; } template <ValidContainer C, ValidCUnaryPredicate<C> Predicate> C filter(C const& container, Predicate filterFun) { auto res = C(); std::copy_if(container.begin(), container.end(), res.end(), filterFun); return res; } template <class R, ValidConstContainer C, class Reducer> requires requires(Reducer r, R& acc, typename C::value_type t) { r(acc, t); } R reduce(C const& container, Reducer reducer) { auto res = R(); for (auto& item : container) { reducer(res, item); } return res; } template < ValidContainer Into, ValidConstContainer From, ValidIntoConverter<typename From::value_type, typename Into::value_type> Mapper> Into map(From const& from, Mapper mapper) { auto res = Into(); std::transform(from.begin(), from.end(), std::back_inserter(res), mapper); return res; } template <ValidConstContainer C> typename C::value_type min(C const& container) { auto it = std::min_element(container.begin(), container.end()); if (it == container.end()) { return C::value_type(); } return *it; } template <class T, ValidConstContainer C, ValidIntoConverter<typename C::value_type, T> Member> requires requires(T a, T b) { a < b; } T min(C const& container, Member member) { auto it = std::min_element( container.begin(), container.end(), [member](auto const& a, auto const& b) -> bool { return member(a) < member(b); } ); if (it == container.end()) { return T(); } return member(*it); } template <ValidConstContainer C> typename C::value_type max(C const& container) { auto it = std::max_element(container.begin(), container.end()); if (it == container.end()) { return C::value_type(); } return *it; } template <class T, ValidConstContainer C, ValidIntoConverter<typename C::value_type, T> Member> requires requires(T a, T b) { a < b; T(); } T max(C const& container, Member member) { auto it = std::max_element( container.begin(), container.end(), [member](auto const& a, auto const& b) -> bool { return member(a) < member(b); } ); if (it == container.end()) { return T(); } return member(*it); } template <class C> struct ReverseWrapper { C iter; decltype(auto) begin() { return std::rbegin(iter); } decltype(auto) end() { return std::rend(iter); } }; template <class C> auto reverse(C&& iter) { return ReverseWrapper<C>{std::forward<C>(iter)}; } }