diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e524d79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9382bed..e19a83e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,14 @@ -# Project: tsoutils ############################################################################### +# Project: cpputils ############################################################################### -Project ( tsoutils ) +Project ( cpputils ) Set ( CMAKE_CXX_STANDARD 14 ) Include ( GlobalCompilerFlags OPTIONAL ) File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -Add_Library ( tsoutils STATIC ${SOURCE_FILES} ) +Add_Library ( cpputils STATIC ${SOURCE_FILES} ) Target_Include_Directories ( - tsoutils + cpputils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) If ( __COTIRE_INCLUDED ) - Cotire ( tsoutils ) + Cotire ( cpputils ) EndIf ( ) \ No newline at end of file diff --git a/src/cpputils/Linq.h b/src/cpputils/Linq.h index b109c45..af0bab5 100644 --- a/src/cpputils/Linq.h +++ b/src/cpputils/Linq.h @@ -14,10 +14,10 @@ #ifdef LINQ_DEBUG #include "Misc.h" #define LINQ_TYPE_NAME() utl::TypeHelper::name() - #define LINQ_CTOR() do { std::cout << LINQ_TYPE_NAME() << "::ctor(this=" << this << ")" << std::endl; } while(0) - #define LINQ_COPY_CTOR() do { std::cout << LINQ_TYPE_NAME() << "::copy(this=" << this << ")" << std::endl; } while(0) - #define LINQ_MOVE_CTOR() do { std::cout << LINQ_TYPE_NAME() << "::move(this=" << this << ")" << std::endl; } while(0) - #define LINQ_DTOR() do { std::cout << LINQ_TYPE_NAME() << "::dtor(this=" << this << ")" << std::endl; } while(0) + #define LINQ_CTOR() do { std::cout << "CTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) + #define LINQ_COPY_CTOR() do { std::cout << "COPY(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) + #define LINQ_MOVE_CTOR() do { std::cout << "MOVE(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) + #define LINQ_DTOR() do { std::cout << "DTOR(this=" << this << ")" << LINQ_TYPE_NAME() << std::endl; } while(0) #else #define LINQ_TYPE_NAME() while(0) #define LINQ_CTOR() while(0) @@ -26,9 +26,6 @@ #define LINQ_DTOR() while(0) #endif -template -struct Fuu { }; - namespace linq { namespace __impl @@ -49,107 +46,59 @@ namespace linq }; template - struct __impl_mp_range_value_type - { - using clean_range_type = typename std::remove_reference::type; - using type = typename clean_range_type::value_type; - }; - - template - using mp_range_value_type = typename __impl_mp_range_value_type::type; + using mp_range_value_type = typename utl::mp_remove_ref::value_type; /* helper types **************************************************************************/ template - using mp_set_value_type = utl::mp_if< - std::is_reference, - utl::mp_remove_ref*, - T>; - - template - struct op_less + struct wrapper { - using predicate_type = TPredicate; - using set_value_type = mp_set_value_type; + using value_type = T; - predicate_type predicate; + value_type value; - template - inline utl::mp_enable_if_c::value, bool> - operator()(const set_value_type& l, const set_value_type& r) - { return predicate(l, r); } + inline value_type operator*() const + { return value; } - template - inline utl::mp_enable_if_c::value, bool> - operator()(const set_value_type& l, const set_value_type& r) - { return predicate(*l, *r); } + inline wrapper& operator=(const wrapper& other) + { + value = other.value; + return *this; + } - op_less(const predicate_type& lp) : - predicate(lp) - { } + inline wrapper& operator=(wrapper&& other) + { + value = std::move(other).value; + return *this; + } - op_less(const op_less& other) : - predicate(other.predicate) + template + inline wrapper(X&& v) : + value(std::forward(v)) { } - op_less(op_less&& other) : - predicate(std::move(other).predicate) - { } - }; - - template - struct ref_set : public std::set, op_less> - { - using less_predicate_type = TLessPredicate; - using value_type = T; - using this_type = ref_set; - using op_less_type = op_less; - using set_value_type = mp_set_value_type; - using base_type = std::set; - - template - inline utl::mp_enable_if_c::value, bool> - storeImpl(X x) - { return this->insert(&x).second; } - - template - inline utl::mp_enable_if_c::value, bool> - storeImpl(X x) - { return this->insert(x).second; } - - inline bool store(value_type value) - { return storeImpl(value); } - - ref_set(less_predicate_type&& lp) : - base_type::set(op_less_type(std::forward(lp))) + inline wrapper(const value_type& v) : + value(v) { } - ref_set(const this_type& other) : - base_type::set(other) + inline wrapper(const wrapper& other) : + value(other.value) { } - ref_set(this_type&& other) : - base_type::set(std::move(other)) + inline wrapper(wrapper&& other) : + value(std::move(other.value)) { } }; template - struct wrapper + struct wrapper { - T value; - - inline bool operator == (const wrapper& other) const { return value == other.value; } - inline bool operator != (const wrapper& other) const { return value != other.value; } - inline bool operator <= (const wrapper& other) const { return value <= other.value; } - inline bool operator >= (const wrapper& other) const { return value >= other.value; } - inline bool operator < (const wrapper& other) const { std::cout << value << " < " << other.value << " ? " << (value < other.value) << std::endl; return value < other.value; } - inline bool operator > (const wrapper& other) const { return value > other.value; } - - inline bool operator == (T&& t) const { return value == t; } - inline bool operator != (T&& t) const { return value != t; } - inline bool operator <= (T&& t) const { return value <= t; } - inline bool operator >= (T&& t) const { return value >= t; } - inline bool operator < (T&& t) const { return value < t; } - inline bool operator > (T&& t) const { return value > t; } + using value_type = T&; + using storage_type = T*; + + storage_type value; + + inline value_type operator*() const + { return *value; } inline wrapper& operator=(const wrapper& other) { @@ -159,24 +108,82 @@ namespace linq inline wrapper& operator=(wrapper&& other) { - value = std::move(other).value; + value = std::move(other.value); return *this; } - inline wrapper(T v) : - value(v) - { LINQ_CTOR(); } + inline wrapper(value_type v) : + value(&v) + { } inline wrapper(const wrapper& other) : value(other.value) - { LINQ_COPY_CTOR(); } + { } inline wrapper(wrapper&& other) : - value(std::move(other).value) - { LINQ_MOVE_CTOR(); } + value(std::move(other.value)) + { } + }; - inline ~wrapper() - { LINQ_DTOR(); } + template + struct op_wrapper_less + { + using predicate_type = TPredicate; + using value_type = wrapper; + + predicate_type predicate; + + inline bool operator()(const value_type& l, const value_type& r) + { return predicate(*l, *r); } + + inline op_wrapper_less(const predicate_type& lp) : + predicate(lp) + { } + + inline op_wrapper_less(const op_wrapper_less& other) : + predicate(other.predicate) + { } + + inline op_wrapper_less(op_wrapper_less&& other) : + predicate(std::move(other.predicate)) + { } + }; + + template + struct range_wrapper + { + using range_type = TRange; + using this_type = range_wrapper; + using value_type = mp_range_value_type; + + range_type range; + + inline value_type& front() + { return range.front(); } + + inline bool next() + { return range.next(); } + + template + inline auto operator >> (TBuilder&& builder) & + { return builder.build(range); } + + template + inline auto operator >> (TBuilder&& builder) && + { return builder.build(std::move(range)); } + + template + range_wrapper(Args&&... args) : + range(std::forward(args)...) + { } + + range_wrapper(const this_type& other) : + range(other.range) + { } + + range_wrapper(this_type&& other) : + range(std::move(other.range)) + { } }; template @@ -192,10 +199,23 @@ namespace linq using keys_value_type = std::pair; using keys_type = std::vector; using values_type = std::vector; + using range_indices_type = std::pair; + + private: + struct lookup_range; + struct lookup_key_value_range; + + using lookup_range_wrapper = range_wrapper; + using lookup_key_value_range_wrapper = range_wrapper; public: + using range_type = lookup_key_value_range_wrapper; + + private: struct lookup_range : public tag_range { + using value_type = lookup::value_type; + enum class State { Initialize, @@ -208,11 +228,11 @@ namespace linq size_t end; State state; - inline value_type front() + inline value_type& front() { assert(state == State::Iterating); assert(current < end); - return values[current].value; + return *values[current]; } inline bool next() @@ -238,30 +258,97 @@ namespace linq } } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - lookup_range(const values_type& v, size_t c, size_t e) : + inline lookup_range(const values_type& v, size_t c, size_t e) : values (v), current (c), end (e), state (State::Initialize) - { } + { LINQ_CTOR(); } - lookup_range(const lookup_range& other) : + inline lookup_range(const lookup_range& other) : values (other.values), current (other.current), end (other.end), state (other.state) - { } + { LINQ_COPY_CTOR(); } - lookup_range(lookup_range&& other) : - values (std::move(other).values), - current (std::move(other).current), - end (std::move(other).end), - state (std::move(other).state) - { } + inline lookup_range(lookup_range&& other) : + values (std::move(other.values)), + current (std::move(other.current)), + end (std::move(other.end)), + state (std::move(other.state)) + { LINQ_MOVE_CTOR(); } + + inline ~lookup_range() + { LINQ_DTOR(); } + }; + + struct lookup_key_value_range : public tag_range + { + using value_type = std::pair; + using iterator_type = typename keys_type::const_iterator; + + lookup container; + bool initialized; + range_indices_type range_indices; + iterator_type current; + iterator_type end; + + inline value_type front() + { + assert(initialized); + assert(current != end); + return value_type( + *current->first, + std::move(container.createRange(range_indices))); + } + + inline bool next() + { + if (!initialized) + { + initialized = true; + current = container._keys.begin(); + end = container._keys.end(); + if (current == end) + return false; + } + else + { + if (current == end) + return false; + ++current; + if (current == end) + return false; + } + range_indices = container.findKey(*current->first); + return true; + } + + template + lookup_key_value_range(C&& c) : + container (std::forward(c)), + initialized (false) + { LINQ_CTOR(); } + + lookup_key_value_range(const lookup_key_value_range& other) : + container (other.container), + initialized (other.initialized), + range_indices (other.range_indices), + current (other.current), + end (other.end) + { LINQ_COPY_CTOR(); } + + lookup_key_value_range(lookup_key_value_range&& other) : + container (std::move(other).container), + initialized (std::move(other).initialized), + range_indices (std::move(other).range_indices), + current (std::move(other).current), + end (std::move(other).end) + { LINQ_MOVE_CTOR(); } + + ~lookup_key_value_range() + { LINQ_DTOR(); } }; struct op_compare_keys @@ -269,18 +356,17 @@ namespace linq bool operator()( const keys_value_type& l, const keys_value_type& r) - { return l.first < r.first; } + { return *l.first < *r.first; } }; private: keys_type _keys; values_type _values; - public: - lookup_range operator[](const clean_key_type& key) + inline range_indices_type findKey(const clean_key_type& key) { if (_values.empty()) - return lookup_range(_values, 0, 0); + return std::make_pair(0, 0); auto it = std::lower_bound( _keys.begin(), @@ -288,38 +374,59 @@ namespace linq typename keys_type::value_type (const_cast(key), 0), op_compare_keys()); if ( it == _keys.end() - || it->first != const_cast(key)) - return lookup_range(_values, 0, 0); + || *it->first != const_cast(key)) + return std::make_pair(0, 0); auto next = it + 1; - return next == _keys.end() - ? lookup_range(_values, it->second, _values.size()) - : lookup_range(_values, it->second, next->second); + range_indices_type ret; + ret.first = it->second; + ret.second = next == _keys.end() + ? _values.size() + : next->second; + return ret; } + inline lookup_range_wrapper createRange(const range_indices_type& indices) + { return lookup_range_wrapper(_values, indices.first, indices.second); } + + public: + inline lookup_range_wrapper operator[](const clean_key_type& key) + { return createRange(findKey(key)); } + + template + inline auto operator >> (TBuilder&& builder) & + { return builder.build(std::move(lookup_key_value_range(*this))); } + + template + inline auto operator >> (TBuilder&& builder) && + { return builder.build(std::move(lookup_key_value_range(std::move(*this)))); } + private: - lookup(keys_type&& k, values_type&& v) : + inline lookup(keys_type&& k, values_type&& v) : _keys (k), _values (v) - { } + { LINQ_CTOR(); } public: - lookup() - { } + inline lookup() + { LINQ_CTOR(); } - lookup(const lookup& other) : + inline lookup(const lookup& other) : _keys (other._keys), _values (other._values) - { } + { LINQ_COPY_CTOR(); } - lookup(lookup&& other) : + inline lookup(lookup&& other) : _keys (std::move(other)._keys), _values (std::move(other)._values) - { } + { LINQ_MOVE_CTOR(); } + + inline ~lookup() + { LINQ_DTOR(); } public: template - static auto build(TRange& range, TKeyPredicate& kp, TValuePredicate& vp) + static inline auto build(TRange& range, TKeyPredicate& kp, TValuePredicate& vp) { keys_type k; values_type v; @@ -335,8 +442,6 @@ namespace linq return lookup(); std::sort(k.begin(), k.end(), op_compare_keys()); - for (auto& tmp : k) - std::cout << tmp.first.value << "-" << tmp.second << std::endl; keys_type keys; values_type values; @@ -360,7 +465,7 @@ namespace linq while (it != end) { values.push_back(v.at(it->second)); - if (prev->first.value < it->first.value) + if (*prev->first < *it->first) keys.push_back(std::make_pair(it->first, index)); prev = it; ++it; @@ -383,7 +488,7 @@ namespace linq iterator_type current; iterator_type end; - inline value_type front() + inline value_type& front() { assert(initialized); assert(current != end); @@ -399,32 +504,31 @@ namespace linq return (current != end); } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - iterator_range(iterator_type beg, iterator_type end) : + inline iterator_range(iterator_type beg, iterator_type end) : initialized (false), current (beg), end (end) { LINQ_CTOR(); } - iterator_range(const this_type& other) : + inline iterator_range(const this_type& other) : initialized (other.initialized), current (other.current), end (other.end) { LINQ_COPY_CTOR(); } - iterator_range(this_type&& other) : + inline iterator_range(this_type&& other) : initialized (std::move(other).initialized), current (std::move(other).current), end (std::move(other).end) { LINQ_MOVE_CTOR(); } - ~iterator_range() + inline ~iterator_range() { LINQ_DTOR(); } }; + template + using iterator_range_wrapper = range_wrapper>; + template struct container_range : public tag_range { @@ -437,7 +541,7 @@ namespace linq bool initialized; iterator_type current; - inline value_type front() + inline value_type& front() { assert(initialized); assert(current != std::end(container)); @@ -458,31 +562,30 @@ namespace linq return (current != std::end(container)); } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - container_range(container_type& c) noexcept : + inline container_range(container_type& c) noexcept : container (c), initialized (false) { LINQ_CTOR(); } - container_range(const this_type& other) noexcept : + inline container_range(const this_type& other) noexcept : container (other.container), initialized (other.initialized), current (other.current) { LINQ_COPY_CTOR(); } - container_range(this_type&& other) noexcept : + inline container_range(this_type&& other) noexcept : container (std::move(other).container), initialized (std::move(other).initialized), current (std::move(other).current) { LINQ_MOVE_CTOR(); } - ~container_range() + inline ~container_range() { LINQ_DTOR(); } }; + template + using container_range_wrapper = range_wrapper>; + template struct generator_range : public tag_range { @@ -494,7 +597,7 @@ namespace linq predicate_type predicate; predicate_value_type value; - inline value_type front() + inline value_type& front() { assert(static_cast(value)); return *value; @@ -506,28 +609,27 @@ namespace linq return static_cast(value); } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - generator_range(predicate_type p) : + inline generator_range(predicate_type p) : predicate (p) { LINQ_CTOR(); } - generator_range(const this_type& other) : + inline generator_range(const this_type& other) : predicate (other.predicate), value (other.value) { LINQ_COPY_CTOR(); } - generator_range(this_type&& other) : + inline generator_range(this_type&& other) : predicate (std::move(other).predicate), value (std::move(other).value) { LINQ_MOVE_CTOR(); } - ~generator_range() + inline ~generator_range() { LINQ_DTOR(); } }; + template + using generator_range_wrapper = range_wrapper>; + template struct where_range : public tag_range { @@ -539,7 +641,7 @@ namespace linq range_type range; predicate_type predicate; - inline value_type front() + inline value_type& front() { return range.front(); } inline bool next() @@ -552,29 +654,29 @@ namespace linq return false; } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - where_range(const range_type& r, const predicate_type& p) : - range (r), - predicate (p) + template + inline where_range(R&& r, P&& p) : + range (std::forward(r)), + predicate (std::forward

(p)) { LINQ_CTOR(); } - where_range(const this_type& other) : + inline where_range(const this_type& other) : range (other.range), predicate (other.predicate) { LINQ_COPY_CTOR(); } - where_range(this_type&& other) : + inline where_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate) { LINQ_MOVE_CTOR(); } - ~where_range() + inline ~where_range() { LINQ_DTOR(); } }; + template + using where_range_wrapper = range_wrapper>; + template struct select_range : public tag_range { @@ -589,7 +691,7 @@ namespace linq range_type range; cache_type cache; - inline value_type front() + inline value_type& front() { assert(static_cast(cache)); return *cache; @@ -606,29 +708,29 @@ namespace linq return false; } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - select_range(const range_type& r, const predicate_type& p) : - range (r), - predicate (p) + template + inline select_range(R&& r, P&& p) : + range (std::forward(r)), + predicate (std::forward

(p)) { LINQ_CTOR(); } - select_range(const this_type& other) : + inline select_range(const this_type& other) : range (other.range), predicate (other.predicate) { LINQ_COPY_CTOR(); } - select_range(this_type&& other) : + inline select_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate) { LINQ_MOVE_CTOR(); } - ~select_range() + inline ~select_range() { LINQ_DTOR(); } }; + template + using select_range_wrapper = range_wrapper>; + template struct select_many_range : public tag_range { @@ -668,7 +770,7 @@ namespace linq build_inner_range(T value) { inner_range = inner_range_type(std::begin(value), std::end(value)); } - inline value_type front() + inline value_type& front() { assert(inner_range); return inner_range->front(); @@ -689,31 +791,31 @@ namespace linq return false; } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - select_many_range(const range_type& r, const predicate_type& p) : - range (r), - predicate (p) + template + inline select_many_range(R&& r, P&& p) : + range (std::forward(r)), + predicate (std::forward

(p)) { LINQ_CTOR(); } - select_many_range(const this_type& other) : + inline select_many_range(const this_type& other) : range (other.range), predicate (other.predicate), inner_range (other.inner_range) { LINQ_COPY_CTOR(); } - select_many_range(this_type&& other) : + inline select_many_range(this_type&& other) : range (std::move(other).range), predicate (std::move(other).predicate), inner_range (std::move(other).inner_range) { LINQ_MOVE_CTOR(); } - ~select_many_range() + inline ~select_many_range() { LINQ_DTOR(); } }; + template + using select_many_range_wrapper = range_wrapper>; + template struct order_by_range : public tag_range { @@ -790,7 +892,7 @@ namespace linq }); } - inline value_type front() + inline value_type& front() { return front_impl(); } inline bool next() @@ -814,35 +916,35 @@ namespace linq return (current < static_cast(values.size())); } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - order_by_range(const range_type& r, const select_predicate_type& sp, const less_predicate_type& lp) : - range (r), - select_predicate(sp), - less_predicate (lp), + template + inline order_by_range(R&& r, SP&& sp, LP&& lp) : + range (std::forward(r)), + select_predicate(std::forward(sp)), + less_predicate (std::forward(lp)), current (-1) { LINQ_CTOR(); } - order_by_range(const this_type& other) : + inline order_by_range(const this_type& other) : range (other.range), select_predicate(other.select_predicate), less_predicate (other.less_predicate), current (other.current) { LINQ_COPY_CTOR(); } - order_by_range(this_type&& other) : + inline order_by_range(this_type&& other) : range (std::move(other).range), select_predicate(std::move(other).select_predicate), less_predicate (std::move(other).less_predicate), current (std::move(other).current) { LINQ_MOVE_CTOR(); } - ~order_by_range() + inline ~order_by_range() { LINQ_DTOR(); } }; + template + using order_by_range_wrapper = range_wrapper>; + template struct distinct_range : public tag_range { @@ -850,47 +952,49 @@ namespace linq using less_predicate_type = TLessPredicate; using this_type = distinct_range; using value_type = mp_range_value_type; - using set_type = ref_set; + using set_value_type = wrapper; + using set_less_type = op_wrapper_less; + using set_type = std::set; range_type range; set_type set; - inline value_type front() + inline value_type& front() { return range.front(); } inline bool next() { while(range.next()) { - if (set.store(range.front())) + if (set.emplace(range.front()).second) return true; } return false; } - template - inline auto operator >> (TBuilder&& builder) - { return builder.build(*this); } - - distinct_range(const range_type& r, less_predicate_type&& lp) : - range (r), - set (std::forward(lp)) + template + inline distinct_range(R&& r, LP&& lp) : + range (std::forward(r)), + set (std::forward(lp)) { LINQ_CTOR(); } - distinct_range(const this_type& other) : + inline distinct_range(const this_type& other) : range (other.range), set (other.set) { LINQ_COPY_CTOR(); } - distinct_range(this_type&& other) : + inline distinct_range(this_type&& other) : range (std::move(other).range), set (std::move(other).set) { LINQ_MOVE_CTOR(); } - ~distinct_range() + inline ~distinct_range() { LINQ_DTOR(); } }; + template + using distinct_range_wrapper = range_wrapper>; + /* builder *******************************************************************************/ template class TOuterRange> struct builder : public tag_builder @@ -904,18 +1008,18 @@ namespace linq { // CAUTION: we want no reference to a range here, because the passed range may be destroyed before used in outer_range_type using range_type = utl::mp_remove_ref; - return outer_range_type(std::forward(range)); + return outer_range_type(std::forward(range)); } - builder() + inline builder() { LINQ_CTOR(); } - builder(this_type&& other) + inline builder(this_type&& other) { LINQ_MOVE_CTOR(); } - builder(const this_type&) = delete; + inline builder(const this_type&) = delete; - ~builder() + inline ~builder() { LINQ_DTOR(); } }; @@ -934,20 +1038,20 @@ namespace linq { // CAUTION: we want no reference to a range here, because the passed range may be destroyed before used in outer_range_type using range_type = utl::mp_remove_ref; - return outer_range_type(std::forward(range), std::move(predicate)); + return outer_range_type(std::forward(range), std::move(predicate)); } - predicate_builder(const predicate_type& p) : + inline predicate_builder(const predicate_type& p) : predicate(p) { LINQ_CTOR(); } - predicate_builder(this_type&& other) : + inline predicate_builder(this_type&& other) : predicate(std::move(other).predicate) { LINQ_MOVE_CTOR(); } - predicate_builder(const this_type&) = delete; + inline predicate_builder(const this_type&) = delete; - ~predicate_builder() + inline ~predicate_builder() { LINQ_DTOR(); } }; @@ -971,26 +1075,26 @@ namespace linq return outer_range_type(std::forward(range), std::move(predicate0), std::move(predicate1)); } - dual_predicate_builder(const predicate_0_type& p0, const predicate_1_type& p1) : + inline dual_predicate_builder(const predicate_0_type& p0, const predicate_1_type& p1) : predicate0(p0), predicate1(p1) { LINQ_CTOR(); } - dual_predicate_builder(this_type&& other) : + inline dual_predicate_builder(this_type&& other) : predicate0(std::move(other).predicate0), predicate1(std::move(other).predicate1) { LINQ_MOVE_CTOR(); } - dual_predicate_builder(const this_type&) = delete; + inline dual_predicate_builder(const this_type&) = delete; - ~dual_predicate_builder() + inline ~dual_predicate_builder() { LINQ_DTOR(); } }; struct count_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { size_t ret = 0; while (range.next()) @@ -1002,7 +1106,7 @@ namespace linq struct sum_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; @@ -1017,7 +1121,7 @@ namespace linq struct min_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; @@ -1035,7 +1139,7 @@ namespace linq struct max_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using value_type = mp_range_value_type; using return_type = utl::mp_clean_type; @@ -1053,7 +1157,7 @@ namespace linq struct any_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { return range.next(); } }; @@ -1068,7 +1172,7 @@ namespace linq predicate_type predicate; template - auto build(TRange&& range) + inline auto build(TRange&& range) { while(range.next()) { @@ -1078,7 +1182,7 @@ namespace linq return false; } - contains_builder(comperator_type&& c, predicate_type&& p) : + inline contains_builder(comperator_type&& c, predicate_type&& p) : comperator(c), predicate (p) { } @@ -1087,11 +1191,11 @@ namespace linq struct single_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { if (!range.next()) throw utl::Exception("range is empty"); - auto ret = range.front(); + auto ret = std::move(range.front()); if (range.next()) throw utl::Exception("range contains more than one value"); return ret; @@ -1101,13 +1205,13 @@ namespace linq struct single_or_default_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; if (!range.next()) return value_type(); - auto ret = range.front(); + auto ret = std::move(range.front()); if (range.next()) return value_type(); return ret; @@ -1117,59 +1221,99 @@ namespace linq struct first_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { if (!range.next()) throw utl::Exception("range is empty"); - return range.front(); + return std::move(range.front()); } }; struct first_or_default_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; if (!range.next()) return value_type(); - return range.front(); + return std::move(range.front()); } }; - struct last_builder : public tag_builder + struct last_builder_base + { + template + struct cache + { + std::unique_ptr value; + + inline cache& operator=(T& t) + { value.reset(new T(std::move(t))); return *this; } + + inline T& operator*() + { return *value; } + + inline operator bool() + { return static_cast(value); } + + inline cache() + { } + }; + + template + struct cache + { + T* value; + + inline cache& operator=(T& t) + { value = &t; return *this; } + + inline T& operator*() + { return *value; } + + inline operator bool() + { return static_cast(value); } + + inline cache() : + value(nullptr) + { } + }; + }; + + struct last_builder : public tag_builder, public last_builder_base { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using value_type = mp_range_value_type; - using cache_type = utl::Nullable; + using cache_type = cache; cache_type cache; while(range.next()) cache = range.front(); if (!static_cast(cache)) throw utl::Exception("range is empty"); - return *cache; + return std::move(*cache); } }; - struct last_or_default_builder : public tag_builder + struct last_or_default_builder : public tag_builder, public last_builder_base { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; - using cache_type = utl::Nullable; + using cache_type = cache; cache_type cache; while(range.next()) cache = range.front(); if (!static_cast(cache)) return value_type(); - return static_cast(*cache); + return std::move(*cache); } }; @@ -1178,7 +1322,7 @@ namespace linq size_t capacity; template - auto build(TRange&& range) + inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; @@ -1187,11 +1331,11 @@ namespace linq vector_type ret; ret.reserve(capacity); while (range.next()) - ret.emplace_back(range.front()); + ret.emplace_back(std::move(range.front())); return ret; } - to_vector_builder(size_t cap = 16) : + inline to_vector_builder(size_t cap = 16) : capacity(cap) { } }; @@ -1199,7 +1343,7 @@ namespace linq struct to_list_builder : public tag_builder { template - auto build(TRange&& range) + inline auto build(TRange&& range) { using range_value_type = mp_range_value_type; using value_type = utl::mp_remove_ref; @@ -1207,7 +1351,7 @@ namespace linq list_type ret; while (range.next()) - ret.emplace_back(range.front()); + ret.emplace_back(std::move(range.front())); return ret; } }; @@ -1227,8 +1371,8 @@ namespace linq { using range_type = TRange; using range_value_type = mp_range_value_type; - using key_type = decltype(std::declval()(std::declval())); - using value_type = decltype(std::declval()(std::declval())); + using key_type = decltype(std::declval()(std::declval())); + using value_type = decltype(std::declval()(std::declval())); using map_type = std::map, value_type>; map_type map; @@ -1243,7 +1387,7 @@ namespace linq return map; } - to_map_builder(const key_predicate_type& kp, const value_predicate_type& vp) : + inline to_map_builder(const key_predicate_type& kp, const value_predicate_type& vp) : key_predicate (kp), value_predicate (vp) { LINQ_CTOR(); } @@ -1264,7 +1408,7 @@ namespace linq predicate(range.front()); } - for_each_builder(const predicate_type& p) : + inline for_each_builder(const predicate_type& p) : predicate(p) { } }; @@ -1290,7 +1434,7 @@ namespace linq return lookup_type::build(range, key_predicate, value_predicate); } - to_lookup_builder(const key_predicate_type& kp, const value_predicate_type& vp) : + inline to_lookup_builder(const key_predicate_type& kp, const value_predicate_type& vp) : key_predicate (kp), value_predicate (vp) { LINQ_CTOR(); } @@ -1345,22 +1489,22 @@ namespace linq { return std::get<1>(t); } template - inline auto operator()(T& t) - { return t; } + inline auto operator()(T&& t) + { return std::forward(t); } }; /* constructors ******************************************************************************/ template inline auto from_iterator(TIterator&& beg, TIterator&& end) - { return __impl::iterator_range(std::forward(beg), std::forward(end)); } + { return __impl::iterator_range_wrapper(std::forward(beg), std::forward(end)); } template inline auto from_container(TContainer&& container) - { return __impl::container_range(std::forward(container)); } + { return __impl::container_range_wrapper(std::forward(container)); } template inline auto from_generator(TPredicate&& predicate) - { return __impl::generator_range(std::forward(predicate)); } + { return __impl::generator_range_wrapper(std::forward(predicate)); } template inline auto from_array(TArray&& array) @@ -1373,19 +1517,19 @@ namespace linq /* filter ************************************************************************************/ template inline auto where(TPredicate&& predicate) - { return __impl::predicate_builder(std::forward(predicate)); } + { return __impl::predicate_builder(std::forward(predicate)); } template inline auto select(TPredicate&& predicate) - { return __impl::predicate_builder(std::forward(predicate)); } + { return __impl::predicate_builder(std::forward(predicate)); } template inline auto select_many(TPredicate&& predicate) - { return __impl::predicate_builder(std::forward(predicate)); } + { return __impl::predicate_builder(std::forward(predicate)); } template inline auto order_by(TSelectPredicate&& sp, TLessPredicate&& lp) - { return __impl::dual_predicate_builder(std::forward(sp), std::forward(lp)); } + { return __impl::dual_predicate_builder(std::forward(sp), std::forward(lp)); } template inline auto order_by(TSelectPredicate&& sp) @@ -1396,7 +1540,7 @@ namespace linq template inline auto distinct(TLessPredicate&& lp) - { return __impl::predicate_builder(std::forward(lp)); } + { return __impl::predicate_builder(std::forward(lp)); } inline auto distinct() { return distinct(op_less_default()); } diff --git a/src/cpputils/old_linq.h b/src/cpputils/old_linq.h deleted file mode 100644 index bb2b6d9..0000000 --- a/src/cpputils/old_linq.h +++ /dev/null @@ -1,1834 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "Nullable.h" -#include "Exceptions.h" -#include "MetaProgramming.h" - -#define ISEF_LINQ_HAS_LAMBDA - -namespace isef -{ - - namespace linq - { - /* meta programming */ - template - struct mp_iterator_impl - { - static TContainer& get_container(); - using type = decltype(std::begin(get_container())); - }; - - template - using mp_iterator = typename mp_iterator_impl::type; - - - - template - struct mp_buildup_type_impl - { - static TBuilder get_builder(); - static TRange get_range(); - using type = decltype(get_builder().build(get_range())); - }; - - template - using mp_buildup_type = typename mp_buildup_type_impl::type; - - - - template - struct mp_range_value_type_impl - { - static TRange get_range(); - using type = decltype(get_range().front()); - }; - - template - using mp_range_value_type = typename mp_range_value_type_impl::type; - - - - template - struct mp_predicate_value_type_impl - { - static TPredicate get_predicate(); - static TParameter get_parameter(); - using type = decltype(get_predicate()(get_parameter())); - }; - - template - using mp_predicate_value_type = typename mp_predicate_value_type_impl::type; - - - - template - struct mp_array_properties; - - template - struct mp_array_properties - { - using size = std::integral_constant; - using value_type = T; - using iterator_type = T*; - }; - - - template - using mp_cleanup_type = typename std::remove_const< - typename std::remove_reference::type - >::type; - - - - template - struct mp_is_pair : public isef::mp_false { }; - - template - struct mp_is_pair> : public isef::mp_true { }; - - - - template - struct mp_is_tuple_of_two : public isef::mp_false { }; - - template - struct mp_is_tuple_of_two> : public isef::mp_true { }; - - - - /* default operations */ - struct op_select_default - { - template - inline T operator ()(T t) const - { return t; } - }; - - struct op_select_pointer - { - template - inline T* operator ()(T& t) const - { return &t; } - }; - - struct op_compare_default - { - template - inline bool operator()(const T& l, const T& r) const - { return (l == r); } - }; - - struct op_less_default - { - template - inline bool operator()(const T& l, const T& r) const - { return (l < r); } - }; - - struct op_auto_select_key - { - template - inline TKey operator()(const std::pair& p) const - { return p.first; } - - template - inline TKey operator()(const std::tuple& t) const - { return std::get<0>(t); } - - template - inline typename std::enable_if::value>, - mp_bool::value> - >::value, T>::type - operator()(const T& t) const - { return t; } - }; - - struct op_auto_select_value - { - template - inline TValue operator()(const std::pair& p) const - { return p.second; } - - template - inline TValue operator()(const std::tuple& t) const - { return std::get<1>(t); } - - template - inline typename std::enable_if::value>, - mp_bool::value> - >::value, T>::type - operator()(const T& t) const - { return t; } - }; - - /* types */ - struct range_tag - { }; - - struct builder_tag - { }; - - template - struct lookup - { - public: - using this_type = lookup; - - using key_type = TKey; - using value_type = TValue; - using return_type = const value_type&; - - using keys_type = std::vector>; - using values_type = std::vector; - - struct lookup_range : public range_tag - { - using this_type = lookup_range; - - enum class State - { - Initialize, - Iterating, - Finished - }; - - const values_type* values; - size_t it; - size_t end; - State state; - - inline return_type front() const - { - assert(state == State::Iterating); - assert(it < end); - return (*values)[it]; - } - - inline bool next() - { - switch (state) - { - case State::Iterating: - ++it; - case State::Initialize: - { - auto has_elements = (it < end); - state = has_elements ? State::Iterating : State::Finished; - return has_elements; - } - break; - - case State::Finished: - default: - return false; - } - } - - template - inline mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - lookup_range() : - values (nullptr), - it (0), - end (0), - state (State::Finished) - { } - - lookup_range( - const values_type* v, - size_t i, - size_t e) : - values (v), - it (i), - end (e), - state (State::Initialize) - { assert(values); } - - lookup_range(const lookup_range& other) : - values (other.values), - it (other.it), - end (other.end), - state (other.state) - { } - - lookup_range(lookup_range&& other) : - values (std::move(other.values)), - it (std::move(other.it)), - end (std::move(other.end)), - state (std::move(other.state)) - { } - }; - - struct lookup_key_value_range : public range_tag - { - using this_type = lookup_key_value_range; - using iterator_type = typename keys_type::const_iterator; - using value_type = std::pair; - using storage_type = nullable; - using return_type = value_type&; - - const lookup& container; - const keys_type& keys; - bool initialized; - storage_type storage; - iterator_type current; - iterator_type end; - - inline return_type front() - { - assert(storage); - assert(current != end); - return *storage; - } - - inline bool next() - { - if (!initialized) - { - initialized = true; - current = keys.begin(); - end = keys.end(); - if (current == end) - return false; - } - else - { - if (current == end) - return false; - ++current; - if (current == end) - return false; - } - storage = value_type(current->first, container[current->first]); - return true; - } - - lookup_key_value_range(const lookup& l, const keys_type& k) : - container (l), - keys (k), - initialized (false) - { } - - lookup_key_value_range(const lookup_key_value_range& other) : - container (other.container), - keys (other.keys), - current (other.current), - end (other.end), - initialized (other.initialized) - { } - - lookup_key_value_range(lookup_key_value_range&& other) : - container (std::move(other.container)), - keys (std::move(other.keys)), - current (std::move(other.current)), - end (std::move(other.end)), - initialized (std::move(other.initialized)) - { } - }; - - struct op_compare_keys - { - bool operator()( - const typename keys_type::value_type& l, - const typename keys_type::value_type& r) - { return l.first < r.first; } - }; - - private: - keys_type _keys; - values_type _values; - - public: - inline void swap(lookup& other) - { - _keys.swap (other._keys); - _values.swap(other._values); - } - - lookup& operator= (const lookup& other) - { - if (std::addressof(other) == this) - return *this; - lookup tmp(other); - swap(tmp); - return *this; - } - - lookup& operator= (lookup&& other) - { - if (std::addressof(other) == this) - return *this; - swap(other); - return *this; - } - - lookup_range operator[] (const key_type& key) const - { - if (_values.empty()) - return lookup_range(std::addressof(_values), 0, 0); - - auto it = std::lower_bound( - _keys.begin(), - _keys.end(), - typename keys_type::value_type (key, 0), - op_compare_keys()); - if ( it == _keys.end() - || it->first != key) - return lookup_range(std::addressof(_values), 0, 0); - - auto next = it + 1; - if (next == _keys.end()) - return lookup_range(std::addressof(_values), it->second, _values.size()); - - return lookup_range(std::addressof(_values), it->second, next->second); - } - - template - inline mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(lookup_key_value_range(*this, _keys)); } - - template - lookup( - size_t capacity, - TRange& range, - TKeyPredicate keyPredicate, - TValuePredicate valuePredicate) - { - keys_type k; - values_type v; - k.reserve(capacity); - v.reserve(capacity); - - size_t index = 0; - while (range.next()) - { - auto& tmp = range.front(); - k.emplace_back(std::move(keyPredicate (tmp)), index); - v.emplace_back(std::move(valuePredicate(tmp))); - ++index; - } - - if (v.empty()) - return; - - std::sort(k.begin(), k.end(), op_compare_keys()); - - _keys.reserve (k.size()); - _values.reserve(k.size()); - - auto it = k.begin(); - auto end = k.end(); - index = 0; - - if (it != end) - { - _keys.push_back (std::make_pair(it->first, index)); - _values.push_back(std::move(v.at(it->second))); - } - - auto prev = it; - ++it; - ++index; - - while (it != end) - { - _values.push_back(v.at(it->second)); - if (prev->first < it->first) - _keys.push_back(std::make_pair(it->first, index)); - prev = it; - ++it; - ++index; - } - } - - lookup() - { } - - lookup(const lookup& other) : - _keys (other._keys), - _values (other._values) - { } - - lookup(lookup&& other) : - _keys (std::move(other._keys)), - _values (std::move(other._values)) - { } - }; - - template - struct container_iterator - { - static TRange get_range(); - - using iterator_category = std::forward_iterator_tag; - using value_type = mp_cleanup_type; - using return_type = decltype(get_range().front()); - - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - - using this_type = container_iterator; - using range_type = TRange; - - nullable range; - bool hasValue; - - inline return_type operator* () - { - assert(hasValue); - assert(range); - return range->front(); - } - - inline const value_type* operator-> () - { return &range->front(); } - - inline this_type& operator++() - { - if (hasValue && range) - hasValue = range->next(); - return *this; - } - - inline bool operator== (const this_type& other) const - { - if (!hasValue && !other.hasValue) - return true; - else if (hasValue && other.hasValue && range.ptr() == other.range.ptr()) - return true; - else - return false; - } - - inline bool operator!= (const this_type& other) const - { return !(*this == other); } - - container_iterator() : - hasValue(false) - { } - - container_iterator(range_type r) : - range(std::move(r)) - { hasValue = range && range->next(); } - - container_iterator(const container_iterator& other) : - hasValue(other.hasValue), - range (other.range) - { } - - container_iterator(container_iterator&& other) : - hasValue(std::move(other.hasValue)), - range (std::move(other.range)) - { } - }; - - template - struct container - { - TRange range; - - container_iterator begin() - { return container_iterator(range); } - - container_iterator end() - { return container_iterator(); } - - explicit container(TRange r) : - range(std::move(r)) - { } - - container(const container& other) : - range(other.range) - { } - - container(container&& other) : - range(std::move(other.range)) - { } - }; - - /* ranges */ - template - struct from_range : public range_tag - { - static TIterator get_iterator(); - - using this_type = from_range; - using iterator_type = TIterator; - using value_type = decltype(*get_iterator()); - using return_type = value_type&; - - bool initialized; - iterator_type current; - iterator_type end; - - inline return_type front() const - { - assert(current != end); - return *current; - } - - inline bool next() - { - if (!initialized) - initialized = true; - else if (current != end) - ++current; - return (current != end); - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - from_range(iterator_type b, iterator_type e) : - initialized (false), - current (b), - end (e) - { } - - from_range(const from_range& other) : - initialized (other.initialized), - current (other.current), - end (other.end) - { } - - from_range(from_range&& other) : - initialized (std::move(other.initialized)), - current (std::move(other.current)), - end (std::move(other.end)) - { } - }; - - template - struct container_range : public range_tag - { - static TContainer& get_container(); - - using this_type = container_range; - using container_type = TContainer; - using iterator_type = decltype(get_container().begin()); - using value_type = decltype(*get_container().begin()); - using return_type = value_type&; - - TContainer& container; - bool initialized; - iterator_type current; - iterator_type end; - - inline return_type front() const - { - assert(initialized); - assert(current != end); - return *current; - } - - inline bool next() - { - if (!initialized) - { - initialized = true; - current = container.begin(); - end = container.end(); - } - else if (current != end) - ++current; - return (current != end); - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - container_range(TContainer& c) : - initialized(false), - container (c) - { } - - container_range(const container_range& other) : - initialized (other.initialized), - container (other.container), - current (other.current), - end (other.end) - { } - - container_range(container_range&& other) : - initialized (std::move(other.initialized)), - container ( other.container), - current (std::move(other.current)), - end (std::move(other.end)) - { } - }; - - template - struct generate_range : public range_tag - { - static TPredicate get_gnerator(); - - using this_type = generate_range; - using raw_nullable_value_type = decltype(get_gnerator()()); - using nullable_value_type = mp_cleanup_type; - using raw_value_type = decltype(*(get_gnerator()())); - using value_type = mp_cleanup_type; - using predicate_type = TPredicate; - using return_type = const value_type&; - - predicate_type predicate; - nullable_value_type value; - - inline return_type front() const - { - assert(value); - return *value; - } - - inline bool next() - { - value = predicate(); - return value.hasValue(); - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - generate_range(predicate_type p) : - predicate(p) - { } - - generate_range(const generate_range& other) : - value (other.value), - predicate (other.predicate) - { } - - generate_range(generate_range&& other) : - value (std::move(other.value)), - predicate (std::move(other.predicate)) - { } - }; - - template - struct where_range : public range_tag - { - static TRange get_range(); - - using this_type = where_range; - using range_type = TRange; - using predicate_type = TPredicate; - using value_type = decltype(get_range().front()); - using return_type = value_type&; - - range_type range; - predicate_type predicate; - - inline return_type front() - { return range.front(); } - - inline bool next() - { - while (range.next()) - { - if (predicate(range.front())) - return true; - } - return false; - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - where_range(range_type r, predicate_type p) : - predicate (p), - range (r) - { } - - where_range(const where_range& other) : - predicate (other.predicate), - range (other.range) - { } - - where_range(where_range&& other) : - predicate (std::move(other.predicate)), - range (std::move(other.range)) - { } - }; - - template - struct select_range : public range_tag - { - static TRange get_range(); - - using this_type = select_range; - using range_type = TRange; - using predicate_type = TPredicate; - using range_value_type = decltype(get_range().front()); - - static predicate_type get_predicate(); - static range_value_type get_range_value(); - using value_type = decltype(get_predicate()(get_range_value())); - using return_type = value_type&; - - predicate_type predicate; - range_type range; - nullable cache; - - inline return_type front() - { - assert(cache); - return *cache; - } - - inline bool next() - { - if (range.next()) - { - cache = predicate(range.front()); - return true; - } - cache.clear(); - return false; - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - select_range(range_type r, predicate_type p) : - predicate (p), - range (r) - { } - - select_range(const select_range& other) : - predicate (other.predicate), - range (other.range) - { } - - select_range(select_range&& other) : - predicate (std::move(other.predicate)), - range (std::move(other.range)) - { } - }; - - template - struct select_many_range : public range_tag - { - template - struct mp_make_inner_range_impl - { - static T get_T(); - using iterator_type = decltype(std::begin(get_T())); - using type = from_range; - }; - - template - using mp_make_inner_range = typename mp_make_inner_range_impl::type; - - - static TRange get_range(); - using this_type = select_many_range; - using range_type = TRange; - using predicate_type = TPredicate; - using range_value_type = decltype(get_range().front()); - - static predicate_type get_predicate(); - static range_value_type get_range_value(); - using op_return_type = decltype(get_predicate()(get_range_value())); - - using inner_range_type = isef::mp_eval_if< - std::is_base_of, - op_return_type, - mp_make_inner_range, op_return_type - >; - - static inner_range_type get_inner_range(); - using return_type = decltype(get_inner_range().front()); - - predicate_type predicate; - range_type range; - nullable cache; - nullable inner_range; - - template - inline typename std::enable_if::value>::type - build_inner_range(T value) - { inner_range = value; } - - template - inline typename std::enable_if::value>::type - build_inner_range(const T& value) - { inner_range = inner_range_type(std::begin(value), std::end(value)); } - - inline return_type front() - { - assert(cache); - assert(inner_range); - return inner_range->front(); - } - - inline bool next() - { - if (cache && inner_range && inner_range->next()) - return true; - - while (range.next()) - { - inner_range.clear(); - cache = predicate(range.front()); - if (cache) - build_inner_range(*cache); - if (cache && inner_range && inner_range->next()) - return true; - } - cache.clear(); - inner_range.clear(); - return false; - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - select_many_range(range_type r, predicate_type p) : - predicate (p), - range (r) - { } - - select_many_range(const select_many_range& other) : - predicate (other.predicate), - range (other.range) - { } - - select_many_range(select_many_range&& other) : - predicate (std::move(other.predicate)), - range (std::move(other.range)) - { } - }; - - template - struct order_by_range : public range_tag - { - using this_type = order_by_range; - using range_type = TRange; - using select_predicate_type = TSelectPredicate; - using less_predicate_type = TLessPredicate; - using return_type = mp_range_value_type; - using value_type = mp_if< - std::is_reference, - typename std::remove_reference::type*, - return_type>; - - range_type range; - select_predicate_type select_predicate; - less_predicate_type less_predicate; - ssize_t current; - std::vector values; - - template - inline mp_enable_if::value, X> - front_impl() - { - assert(current >= 0); - assert(current < static_cast(values.size())); - return *values.at(current); - } - - template - inline mp_enable_if::value, X> - front_impl() - { - assert(current >= 0); - assert(current < static_cast(values.size())); - return values.at(current); - } - - template - inline mp_enable_if::value> - storeValue(X x) - { values.push_back(&x); } - - template - inline mp_enable_if::value> - storeValue(X x) - { values.emplace(x); } - - template - inline mp_enable_if::value> - sortValues() - { - std::sort( - values.begin(), - values.end(), - [this](const value_type& l, const value_type& r) { - return this->less_predicate(this->select_predicate(*l), this->select_predicate(*r)); - }); - } - - template - inline mp_enable_if::value> - sortValues() - { - std::sort( - values.begin(), - values.end(), - [this](const value_type& l, const value_type& r) { - return this->less_predicate(this->select_predicate(l), this->select_predicate(r)); - }); - } - - inline return_type front() - { return front_impl(); } - - inline bool next() - { - if (current < 0) - { - values.clear(); - while (range.next()) - storeValue(range.front()); - - if (values.empty()) - return false; - - sortValues(); - - current = 0; - return true; - } - - if (current < static_cast(values.size())) - ++current; - return (current < static_cast(values.size())); - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - order_by_range(range_type r, select_predicate_type sp, less_predicate_type lp) : - range (r), - select_predicate(sp), - less_predicate (lp), - current (-1) - { } - - order_by_range(const this_type& other) : - range (other.range), - select_predicate(other.select_predicate), - less_predicate (other.less_predicate), - current (other.current) - { } - - order_by_range(this_type&& other) : - range (std::move(other.range)), - select_predicate(std::move(other.select_predicate)), - less_predicate (std::move(other.less_predicate)), - current (std::move(other.current)) - { } - }; - - template - struct distinct_range : public range_tag - { - using this_type = distinct_range; - using range_type = TRange; - using return_type = mp_range_value_type; - using value_type = mp_cleanup_type; - using set_type = std::set; - using cache_type = nullable; - - range_type range; - set_type set; - cache_type cache; - - inline return_type front() - { - assert(static_cast(cache)); - return *cache; - } - - inline bool next() - { - while (range.next()) - { - auto ret = set.insert(range.front()); - if (ret.second) - { - cache = range.front(); - return true; - } - } - return false; - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - distinct_range(range_type r) : - range(r) - { } - - distinct_range(const this_type& other) : - range(other.range), - set (other.set), - cache(other.cache) - { } - - distinct_range(this_type&& other) : - range(std::move(other.range)), - set (std::move(other.set)), - cache(std::move(other.cache)) - { } - }; - - template - struct default_if_empty_range : public range_tag - { - using this_type = default_if_empty_range; - using range_type = TRange; - using value_type = T; - using return_type = const T&; - - enum class State - { - Init, - Iterate, - Empty, - Finish - }; - - range_type range; - value_type value; - State state; - - inline return_type front() - { - assert(state != State::Init && state != State::Finish); - if (state == State::Empty) - return value; - else - return range.front(); - } - - inline bool next() - { - bool ret = false; - switch(state) - { - case State::Init: - ret = range.next(); - if (ret) - { - state = State::Iterate; - } - else - { - state = State::Empty; - ret = true; - } - break; - - case State::Iterate: - ret = range.next(); - if (!ret) - state = State::Finish; - break; - - case State::Empty: - ret = false; - state = State::Finish; - break; - - default: - break; - } - return ret; - } - - template - mp_buildup_type - operator >> (TBuilder builder) const - { return builder.build(*this); } - - default_if_empty_range(range_type r, T t) : - range(r), - value(t), - state(State::Init) - { } - - default_if_empty_range(const default_if_empty_range& other) : - range(other.range), - value(other.value), - state(other.state) - { } - - default_if_empty_range(default_if_empty_range&& other) : - range(std::move(other.range)), - value(std::move(other.value)), - state(std::move(other.state)) - { } - }; - - /* range builder */ - template - struct where_builder : public builder_tag - { - using this_type = where_builder; - using predicate_type = TPredicate; - - predicate_type predicate; - - template - where_range - build(TRange range) const - { return where_range(range, predicate); } - - where_builder(predicate_type p) : - predicate(p) - { } - - where_builder(const this_type& other) : - predicate(other.predicate) - { } - - where_builder(const this_type&& other) : - predicate(std::move(other.predicate)) - { } - }; - - template - struct select_builder : public builder_tag - { - using this_type = select_builder; - using predicate_type = TPredicate; - - predicate_type predicate; - - template - select_range - build(TRange range) const - { return select_range(range, predicate); } - - select_builder(predicate_type p) : - predicate(p) - { } - - select_builder(const this_type& other) : - predicate(other.predicate) - { } - - select_builder(this_type&& other) : - predicate(std::move(other.predicate)) - { } - }; - - template - struct select_many_builder : public builder_tag - { - using this_type = select_many_builder; - using predicate_type = TPredicate; - - predicate_type predicate; - - template - select_many_range - build(TRange range) const - { return select_many_range(range, predicate); } - - select_many_builder(predicate_type p) : - predicate(p) - { } - - select_many_builder(const this_type& other) : - predicate(other.predicate) - { } - - select_many_builder(this_type&& other) : - predicate(std::move(other.predicate)) - { } - }; - - template - struct order_by_builder : public builder_tag - { - using this_type = order_by_builder; - using select_predicate_type = TSelectPredicate; - using less_predicate_type = TLessPredicate; - - select_predicate_type select_predicate; - less_predicate_type less_predicate; - - template - order_by_range - build(TRange range) const - { return order_by_range(std::move(range), select_predicate, less_predicate); } - - order_by_builder(select_predicate_type sp, less_predicate_type lp) : - select_predicate(sp), - less_predicate (lp) - { } - - order_by_builder(const this_type& other) : - select_predicate(other.select_predicate), - less_predicate (other.less_predicate) - { } - - order_by_builder(this_type&& other) : - select_predicate(std::move(other.select_predicate)), - less_predicate (std::move(other.less_predicate)) - { } - }; - - struct distinct_builder : public builder_tag - { - using this_type = distinct_builder; - - template - inline distinct_range - build(TRange range) const - { return distinct_range(range); } - - distinct_builder() - { } - - distinct_builder(const this_type& other) - { } - - distinct_builder(this_type&& other) - { } - }; - - template - struct default_if_empty_builder : public builder_tag - { - using this_type = default_if_empty_builder; - - T value; - - template - inline default_if_empty_range - build(TRange range) const - { return default_if_empty_range(range, value); } - - default_if_empty_builder(T t) : - value(t) - { } - - default_if_empty_builder(const this_type& other) : - value(other.value) - { } - - default_if_empty_builder(this_type&& other) : - value(std::move(other.value)) - { } - }; - - /* result builder */ - struct sum_builder : public builder_tag - { - template - mp_cleanup_type> - build(TRange range) const - { - auto sum = mp_cleanup_type>(); - while (range.next()) - sum += range.front(); - return sum; - } - }; - - template - struct contains_builder : public builder_tag - { - using this_type = contains_builder; - using comparator_type = T; - using predicate_type = TPredicate; - - comparator_type comparator; - predicate_type predicate; - - template - bool build(TRange range) const - { - while (range.next()) - { - if (predicate(comparator, range.front())) - return true; - } - return false; - } - - contains_builder(comparator_type t, predicate_type p) : - comparator (t), - predicate (p) - { } - - contains_builder(const this_type& other) : - comparator (other.comparator), - predicate (other.predicate) - { } - - contains_builder(this_type&& other) : - comparator (std::move(other.comparator)), - predicate (std::move(other.predicate)) - { } - }; - - struct single_or_default_builder : public builder_tag - { - template - inline mp_cleanup_type> - build(TRange range) const - { - using return_type = mp_cleanup_type>; - - if (!range.next()) - return return_type(); - auto ret = range.front(); - if (range.next()) - return return_type(); - return ret; - } - }; - - struct first_builder : public builder_tag - { - template - inline mp_range_value_type - build(TRange range) const - { - if (!range.next()) - throw isef::Exception("range is empty"); - return range.front(); - } - }; - - struct first_or_default_builder : public builder_tag - { - template - inline mp_cleanup_type> - build(TRange range) const - { - using return_type = mp_cleanup_type>; - - if (!range.next()) - return return_type(); - return range.front(); - } - }; - - struct last_builder : public builder_tag - { - template - inline mp_range_value_type - build(TRange range) const - { - using return_type = mp_range_value_type; - - nullable cache; - while (range.next()) - cache = range.front(); - if (!static_cast(cache)) - throw isef::Exception("range is empty"); - return *cache; - } - }; - - struct last_or_default_builder : public builder_tag - { - template - inline mp_cleanup_type> - build(TRange range) const - { - using return_type = mp_range_value_type; - using value_type = mp_cleanup_type; - - nullable cache; - while (range.next()) - cache = range.front(); - - return static_cast(cache) ? *cache : value_type(); - } - }; - - struct any_builder : public builder_tag - { - template - inline bool build(TRange range) const - { return range.next(); } - }; - - struct min_builder : public builder_tag - { - template - inline mp_cleanup_type> - build(TRange range) const - { - using return_type = mp_cleanup_type>; - return_type ret = std::numeric_limits::max(); - while (range.next()) - { - if (ret > range.front()) - ret = range.front(); - } - return ret; - } - }; - - struct max_builder : public builder_tag - { - template - inline mp_cleanup_type> - build(TRange range) const - { - using return_type = mp_cleanup_type>; - return_type ret = std::numeric_limits::min(); - while (range.next()) - { - if (ret < range.front()) - ret = range.front(); - } - return ret; - } - }; - - struct to_vector_builder : public builder_tag - { - size_t capacity; - - template - std::vector>> - build(TRange range) const - { - std::vector>> ret; - ret.reserve(capacity); - while (range.next()) - ret.emplace_back(range.front()); - return ret; - } - - to_vector_builder(size_t cap = 16) : - capacity(cap) - { } - }; - - struct to_list_builder : public builder_tag - { - template - std::list>> - build(TRange range) const - { - std::list>> ret; - while (range.next()) - ret.push_back(range.front()); - return ret; - } - }; - - template - struct to_map_builder : public builder_tag - { - using this_type = to_map_builder; - using key_predicate_type = TKeyPredicate; - using value_predicate_type = TValuePredicate; - - key_predicate_type keyPredicate; - value_predicate_type valuePredicate; - - template - std::map< - mp_predicate_value_type>, - mp_predicate_value_type>> - build(TRange range) const - { - using map_type = std::map< - mp_predicate_value_type>, - mp_predicate_value_type>>; - map_type map; - while (range.next()) - { - auto& tmp = range.front(); - auto ret = map.emplace( - std::move(keyPredicate (tmp)), - std::move(valuePredicate(tmp))); - if (!ret.second) - throw isef::Exception("duplicate key"); - } - return map; - } - - to_map_builder(key_predicate_type k, value_predicate_type v) : - keyPredicate (k), - valuePredicate (v) - { } - - to_map_builder(const this_type& other) : - keyPredicate (other.keyPredicate), - valuePredicate (other.valuePredicate) - { } - - to_map_builder(this_type&& other) : - keyPredicate (std::move(other.keyPredicate)), - valuePredicate (std::move(other.valuePredicate)) - { } - }; - - template - struct to_lookup_builder : public builder_tag - { - using this_type = to_lookup_builder; - using key_predicate_type = TKeyPredicate; - using value_predicate_type = TValuePredicate; - - key_predicate_type keyPredicate; - value_predicate_type valuePredicate; - - template - lookup< - mp_predicate_value_type>, - mp_predicate_value_type>> - build(TRange range) const - { - using lookup_type = lookup< - mp_predicate_value_type>, - mp_predicate_value_type>>; - return lookup_type(16, range, keyPredicate, valuePredicate); - } - - to_lookup_builder(key_predicate_type k, value_predicate_type v) : - keyPredicate (k), - valuePredicate (v) - { } - - to_lookup_builder(const this_type& other) : - keyPredicate (other.keyPredicate), - valuePredicate (other.valuePredicate) - { } - - to_lookup_builder(this_type&& other) : - keyPredicate (std::move(other.keyPredicate)), - valuePredicate (std::move(other.valuePredicate)) - { } - }; - - template - struct for_each_builder : public builder_tag - { - using this_type = for_each_builder; - using predicate_type = TPredicate; - - predicate_type predicate; - - template - void build(TRange range) const - { - while(range.next()) - predicate(range.front()); - } - - for_each_builder(predicate_type p) : - predicate(p) - { } - - for_each_builder(const this_type& other) : - predicate(other.predicate) - { } - - for_each_builder(this_type&& other) : - predicate(std::move(other.predicate)) - { } - }; - - struct container_builder - { - template - inline container - build(TRange range) const - { return container(range); } - }; - }; - -/* constructors */ - template - inline linq::from_range - from_iterators(TIterator beg, TIterator end) - { return linq::from_range(beg, end); } - - template - inline linq::container_range - from_container(TContainer& c) - { return linq::container_range(c); } - - template - inline linq::from_range::iterator_type> - from_array(TArray& array) - { - using properties = typename linq::mp_array_properties; - using iterator_type = typename properties::iterator_type; - using range_type = typename linq::from_range; - - iterator_type beg = array; - iterator_type end = array + properties::size::value; - - return range_type(std::move(beg), std::move(end)); - } - - template - inline linq::generate_range - from_generator(TPredicate p) - { return linq::generate_range(p); } - -/* operators */ - template - inline linq::where_builder - where(TPredicate p) - { return linq::where_builder(p); } - - template - inline linq::select_builder - select(TPredicate p) - { return linq::select_builder(p); } - - template - inline linq::select_many_builder - select_many(TPredicate p) - { return linq::select_many_builder(p); } - - template - inline linq::order_by_builder - order_by(TSelectPredicate sp, TLessPredicate lp) - { return linq::order_by_builder(std::move(sp), std::move(lp)); } - - template - inline linq::order_by_builder - order_by(TSelectPredicate sp) - { return linq::order_by_builder(std::move(sp), linq::op_less_default()); } - - inline linq::order_by_builder - order_by() - { return linq::order_by_builder(linq::op_select_default(), linq::op_less_default()); } - - inline linq::distinct_builder - distinct() - { return linq::distinct_builder(); } - - template - inline linq::default_if_empty_builder - default_if_empty(T t) - { return linq::default_if_empty_builder(t); } - -/* result generators */ - inline linq::sum_builder - sum() - { return linq::sum_builder(); } - - inline linq::any_builder - any() - { return linq::any_builder(); } - - inline linq::min_builder - min() - { return linq::min_builder(); } - - inline linq::max_builder - max() - { return linq::max_builder(); } - - template - inline linq::contains_builder - contains(T t) - { return linq::contains_builder(t, linq::op_compare_default()); } - - template - inline linq::contains_builder - contains(T t, TPredicate p) - { return linq::contains_builder(t, p); } - - inline linq::single_or_default_builder - single_or_default() - { return linq::single_or_default_builder(); } - - inline linq::first_builder - first() - { return linq::first_builder(); } - - inline linq::first_or_default_builder - first_or_default() - { return linq::first_or_default_builder(); } - - inline linq::last_builder - last() - { return linq::last_builder(); } - - inline linq::last_or_default_builder - last_or_default() - { return linq::last_or_default_builder(); } - - inline linq::to_vector_builder - to_vector(size_t capacity = 16) - { return linq::to_vector_builder(capacity); } - - inline linq::to_list_builder - to_list() - { return linq::to_list_builder(); } - - template - inline linq::to_map_builder - to_map(TKeyPredicate k, TValuePredicate v) - { return linq::to_map_builder(k, v); } - - template - inline linq::to_map_builder - to_map(TKeyPredicate k) - { return linq::to_map_builder(k, linq::op_select_default()); } - - inline linq::to_map_builder - to_map() - { return linq::to_map_builder(linq::op_auto_select_key(), linq::op_auto_select_value()); } - - inline linq::container_builder - to_container() - { return linq::container_builder(); } - - template - inline linq::to_lookup_builder - to_lookup(TKeyPredicate k, TValuePredicate v) - { return linq::to_lookup_builder(k, v); } - - template - inline linq::to_lookup_builder - to_lookup(TKeyPredicate k) - { return linq::to_lookup_builder(k, linq::op_select_default()); } - - inline linq::to_lookup_builder - to_lookup() - { return linq::to_lookup_builder(linq::op_auto_select_key(), linq::op_auto_select_value()); } - - template - inline linq::for_each_builder - for_each(TPredicate p) - { return linq::for_each_builder(p); } - -} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af01619..ce2a8c1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,24 +1,24 @@ -# Project: test_tsoutils ########################################################################## +# Project: test_cpputils ########################################################################## -Project ( test_tsoutils ) +Project ( test_cpputils ) Set ( CMAKE_CXX_STANDARD 14 ) Include ( GlobalCompilerFlags OPTIONAL ) File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -Add_Executable ( test_tsoutils EXCLUDE_FROM_ALL ${SOURCE_FILES} ) +Add_Executable ( test_cpputils EXCLUDE_FROM_ALL ${SOURCE_FILES} ) If ( __COTIRE_INCLUDED ) - Cotire ( test_tsoutils ) + Cotire ( test_cpputils ) EndIf ( ) Target_Link_Libraries ( - test_tsoutils + test_cpputils gtest gmock gmock_main pthread ) -Add_Custom_Target ( run_test_tsoutils DEPENDS test_tsoutils COMMAND ./test_tsoutils ) +Add_Custom_Target ( run_test_cpputils DEPENDS test_cpputils COMMAND ./test_cpputils ) If ( NOT TARGET tests) Add_Custom_Target ( tests ) EndIf ( ) -Add_Dependencies ( tests run_test_tsoutils ) \ No newline at end of file +Add_Dependencies ( tests run_test_cpputils ) \ No newline at end of file diff --git a/test/LinqTests.cpp b/test/LinqTests.cpp index 58434d3..aa3836d 100644 --- a/test/LinqTests.cpp +++ b/test/LinqTests.cpp @@ -9,6 +9,10 @@ namespace linq_tests { int value; + TestData() : + value(0) + { } + TestData(int v) : value(v) { } @@ -22,6 +26,28 @@ namespace linq_tests values(v) { } }; + + struct MoveOnlyData + { + int value; + + MoveOnlyData() : + value(0) + { LINQ_CTOR(); } + + MoveOnlyData(int v) : + value(v) + { LINQ_CTOR(); } + + MoveOnlyData(MoveOnlyData&& o) : + value(0) + { std::swap(value, o.value); LINQ_MOVE_CTOR(); } + + MoveOnlyData(const MoveOnlyData&) = delete; + + ~MoveOnlyData() + { LINQ_DTOR(); } + }; } using namespace ::linq; @@ -461,4 +487,204 @@ TEST(LinqTest, to_lookup) ASSERT_EQ (&data.at(5).second, &range2.front()); ASSERT_EQ (std::string("Str2-1"), range2.front()); ASSERT_FALSE(range2.next()); + + using lookup_type = decltype(lookup); + auto map = lookup + >> to_map([](lookup_type::range_type::value_type v){ + return v.first; + }, [](lookup_type::range_type::value_type v){ + return v.second >> to_vector(); + }); + + using map_type = decltype(map); + map_type expected({ + { 0, { "Str0-0", "Str0-1", "Str0-2" } }, + { 1, { "Str1-0", "Str1-1", "Str1-2" } }, + { 2, { "Str2-0", "Str2-1" } } + }); + EXPECT_EQ(expected, map); +} + +TEST(LinqTest, moveable_objects) +{ + std::vector data; + data.emplace_back(1); + + { + data.at(0).value = 1; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> first(); + EXPECT_EQ(1, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 2; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> first_or_default(); + EXPECT_EQ(2, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 3; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> last(); + EXPECT_EQ(3, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 4; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> last_or_default(); + EXPECT_EQ(4, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 5; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> single(); + EXPECT_EQ(5, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 6; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> single_or_default(); + EXPECT_EQ(6, v.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 7; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> to_vector(); + ASSERT_EQ(1, v.size()); + EXPECT_EQ(7, v.at(0).value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 8; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> to_map([](MoveOnlyData& d){ + return 5; + }, [](MoveOnlyData& d){ + return std::move(d); + }); + auto it = v.find(5); + ASSERT_NE(it, v.end()); + EXPECT_EQ(8, it->second.value); + EXPECT_EQ(0, data.at(0).value); + } + + { + data.at(0).value = 9; + auto v = from_container(data) + >> select([](MoveOnlyData& d) { + return std::move(d); + }) + >> to_list(); + auto it = v.begin(); + ASSERT_NE(it, v.end()); + EXPECT_EQ(9, it->value); + EXPECT_EQ(0, data.at(0).value); + } +} + +TEST(LinqTest, const_objects) +{ + const TestData data[] = { TestData(1) }; + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> first(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> first_or_default(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return std::move(d); + }) + >> last(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> last_or_default(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> single(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> single_or_default(); + EXPECT_EQ(1, v.value); + } + + { + auto v = from_array(data) + >> select([](const TestData& d) { + return d; + }) + >> to_list(); + auto it = v.begin(); + ASSERT_NE(it, v.end()); + EXPECT_EQ(1, it->value); + } } \ No newline at end of file