From 81308e53f2d3e36c7d0af75d6ff55a6bd61dffa2 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 10 May 2019 05:35:29 +0700 Subject: [PATCH] add flat_multimap and flat_multiset containers #4 --- headers/flat_hpp/flat_multimap.hpp | 436 +++++++++++++++++++++++++++ headers/flat_hpp/flat_multiset.hpp | 364 +++++++++++++++++++++++ untests/flat_multimap_tests.cpp | 463 +++++++++++++++++++++++++++++ untests/flat_multiset_tests.cpp | 428 ++++++++++++++++++++++++++ 4 files changed, 1691 insertions(+) create mode 100644 headers/flat_hpp/flat_multimap.hpp create mode 100644 headers/flat_hpp/flat_multiset.hpp create mode 100644 untests/flat_multimap_tests.cpp create mode 100644 untests/flat_multiset_tests.cpp diff --git a/headers/flat_hpp/flat_multimap.hpp b/headers/flat_hpp/flat_multimap.hpp new file mode 100644 index 0000000..c705cd9 --- /dev/null +++ b/headers/flat_hpp/flat_multimap.hpp @@ -0,0 +1,436 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + template < typename Key + , typename Value + , typename Compare = std::less + , typename Container = std::vector> > + class flat_multimap final { + class uber_comparer_type : public Compare { + public: + uber_comparer_type() = default; + uber_comparer_type(const Compare& c) : Compare(c) {} + + bool operator()(const Key& l, const Key& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Key& l, typename Container::const_reference r) const { + return Compare::operator()(l, r.first); + } + + bool operator()(typename Container::const_reference l, const Key& r) const { + return Compare::operator()(l.first, r); + } + + bool operator()(typename Container::const_reference l, typename Container::const_reference r) const { + return Compare::operator()(l.first, r.first); + } + }; + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + class value_compare { + public: + bool operator()(const value_type& l, const value_type& r) const { + return compare_(l.first, r.first); + } + protected: + friend class flat_multimap; + explicit value_compare(key_compare compare) + : compare_(std::move(compare)) {} + private: + key_compare compare_; + }; + public: + flat_multimap() {} + + explicit flat_multimap(const Compare& c) + : compare_(c) {} + + template < typename Allocator > + explicit flat_multimap(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_multimap(const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) {} + + template < typename InputIter > + flat_multimap(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_multimap(InputIter first, InputIter last, const Compare& c) + : compare_(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multimap(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter , typename Allocator > + flat_multimap(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) { + insert(first, last); + } + + flat_multimap(std::initializer_list ilist) { + insert(ilist); + } + + flat_multimap(std::initializer_list ilist, const Compare& c) + : compare_(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(std::initializer_list ilist, const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multimap(flat_multimap&& other, const Allocator& a) + : data_(std::move(other.data_), a) + , compare_(std::move(other.compare_)) {} + + template < typename Allocator > + flat_multimap(const flat_multimap& other, const Allocator& a) + : data_(other.data_, a) + , compare_(other.compare_) {} + + flat_multimap(flat_multimap&& other) = default; + flat_multimap(const flat_multimap& other) = default; + + flat_multimap& operator=(flat_multimap&& other) = default; + flat_multimap& operator=(const flat_multimap& other) = default; + + flat_multimap& operator=(std::initializer_list ilist) { + flat_multimap(ilist).swap(*this); + return *this; + } + + iterator begin() noexcept { return data_.begin(); } + const_iterator begin() const noexcept { return data_.begin(); } + const_iterator cbegin() const noexcept { return data_.cbegin(); } + + iterator end() noexcept { return data_.end(); } + const_iterator end() const noexcept { return data_.end(); } + const_iterator cend() const noexcept { return data_.cend(); } + + reverse_iterator rbegin() noexcept { return data_.rbegin(); } + const_reverse_iterator rbegin() const noexcept { return data_.rbegin(); } + const_reverse_iterator crbegin() const noexcept { return data_.crbegin(); } + + reverse_iterator rend() noexcept { return data_.rend(); } + const_reverse_iterator rend() const noexcept { return data_.rend(); } + const_reverse_iterator crend() const noexcept { return data_.crend(); } + + bool empty() const noexcept { + return data_.empty(); + } + + size_type size() const noexcept { + return data_.size(); + } + + size_type max_size() const noexcept { + return data_.max_size(); + } + + size_type capacity() const noexcept { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type())->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type())->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_multimap::at: key not found"); + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + if ( iter != end() ) { + return iter->second; + } + throw std::out_of_range("flat_multimap::at: key not found"); + } + + iterator insert(value_type&& value) { + const iterator iter = upper_bound(value.first); + return data_.insert(iter, std::move(value)); + } + + iterator insert(const value_type& value) { + const iterator iter = upper_bound(value.first); + return data_.insert(iter, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || !compare_(value, *(hint - 1))) + && (hint == end() || !compare_(*hint, value)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || !compare_(value, *(hint - 1))) + && (hint == end() || !compare_(*hint, value)) + ? data_.insert(hint, value) + : insert(value); + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + iterator emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() noexcept { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const auto p = equal_range(key); + size_type r = std::distance(p.first, p.second); + erase(p.first, p.second); + return r; + } + + void swap(flat_multimap& other) { + using std::swap; + swap(data_, other.data_); + swap(compare_, other.compare_); + } + + size_type count(const key_type& key) const { + const auto p = equal_range(key); + return std::distance(p.first, p.second); + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !compare_(key, iter->first) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !compare_(key, iter->first) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, compare_); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, compare_); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, compare_); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, compare_); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, compare_); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, compare_); + } + + key_compare key_comp() const { + return compare_; + } + + value_compare value_comp() const { + return value_compare(compare_); + } + private: + container_type data_; + uber_comparer_type compare_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Value + , typename Compare + , typename Container > + void swap( + flat_multimap& l, + flat_multimap& r) + { + l.swap(r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator==( + const flat_multimap& l, + const flat_multimap& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator!=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(l == r); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<( + const flat_multimap& l, + const flat_multimap& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>( + const flat_multimap& l, + const flat_multimap& r) + { + return r < l; + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator<=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(r < l); + } + + template < typename Key + , typename Value + , typename Compare + , typename Container > + bool operator>=( + const flat_multimap& l, + const flat_multimap& r) + { + return !(l < r); + } +} diff --git a/headers/flat_hpp/flat_multiset.hpp b/headers/flat_hpp/flat_multiset.hpp new file mode 100644 index 0000000..d4250f0 --- /dev/null +++ b/headers/flat_hpp/flat_multiset.hpp @@ -0,0 +1,364 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace flat_hpp +{ + template < typename Key + , typename Compare = std::less + , typename Container = std::vector > + class flat_multiset final { + public: + using key_type = Key; + using value_type = Key; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using value_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::const_iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::const_reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + public: + flat_multiset() {} + + explicit flat_multiset(const Compare& c) + : compare_(c) {} + + template < typename Allocator > + explicit flat_multiset(const Allocator& a) + : data_(a) {} + + template < typename Allocator > + flat_multiset(const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) {} + + template < typename InputIter > + flat_multiset(InputIter first, InputIter last) { + insert(first, last); + } + + template < typename InputIter > + flat_multiset(InputIter first, InputIter last, const Compare& c) + : compare_(c) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multiset(InputIter first, InputIter last, const Allocator& a) + : data_(a) { + insert(first, last); + } + + template < typename InputIter, typename Allocator > + flat_multiset(InputIter first, InputIter last, const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) { + insert(first, last); + } + + flat_multiset(std::initializer_list ilist) { + insert(ilist); + } + + flat_multiset(std::initializer_list ilist, const Compare& c) + : compare_(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(std::initializer_list ilist, const Allocator& a) + : data_(a) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(std::initializer_list ilist, const Compare& c, const Allocator& a) + : data_(a) + , compare_(c) { + insert(ilist); + } + + template < typename Allocator > + flat_multiset(flat_multiset&& other, const Allocator& a) + : data_(std::move(other.data_), a) + , compare_(std::move(other.compare_)) {} + + template < typename Allocator > + flat_multiset(const flat_multiset& other, const Allocator& a) + : data_(other.data_, a) + , compare_(other.compare_) {} + + flat_multiset(flat_multiset&& other) = default; + flat_multiset(const flat_multiset& other) = default; + + flat_multiset& operator=(flat_multiset&& other) = default; + flat_multiset& operator=(const flat_multiset& other) = default; + + flat_multiset& operator=(std::initializer_list ilist) { + flat_multiset(ilist).swap(*this); + return *this; + } + + iterator begin() noexcept { return data_.begin(); } + const_iterator begin() const noexcept { return data_.begin(); } + const_iterator cbegin() const noexcept { return data_.cbegin(); } + + iterator end() noexcept { return data_.end(); } + const_iterator end() const noexcept { return data_.end(); } + const_iterator cend() const noexcept { return data_.cend(); } + + reverse_iterator rbegin() noexcept { return data_.rbegin(); } + const_reverse_iterator rbegin() const noexcept { return data_.rbegin(); } + const_reverse_iterator crbegin() const noexcept { return data_.crbegin(); } + + reverse_iterator rend() noexcept { return data_.rend(); } + const_reverse_iterator rend() const noexcept { return data_.rend(); } + const_reverse_iterator crend() const noexcept { return data_.crend(); } + + bool empty() const noexcept { + return data_.empty(); + } + + size_type size() const noexcept { + return data_.size(); + } + + size_type max_size() const noexcept { + return data_.max_size(); + } + + size_type capacity() const noexcept { + return data_.capacity(); + } + + void reserve(size_type ncapacity) { + data_.reserve(ncapacity); + } + + void shrink_to_fit() { + data_.shrink_to_fit(); + } + + iterator insert(value_type&& value) { + const iterator iter = upper_bound(value); + return data_.insert(iter, std::move(value)); + } + + iterator insert(const value_type& value) { + const iterator iter = upper_bound(value); + return data_.insert(iter, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || !compare_(value, *(hint - 1))) + && (hint == end() || !compare_(*hint, value)) + ? data_.insert(hint, std::move(value)) + : insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || !compare_(value, *(hint - 1))) + && (hint == end() || !compare_(*hint, value)) + ? data_.insert(hint, value) + : insert(value); + } + + template < typename InputIter > + void insert(InputIter first, InputIter last) { + while ( first != last ) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template < typename... Args > + iterator emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template < typename... Args > + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + void clear() noexcept { + data_.clear(); + } + + iterator erase(const_iterator iter) { + return data_.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return data_.erase(first, last); + } + + size_type erase(const key_type& key) { + const auto p = equal_range(key); + size_type r = std::distance(p.first, p.second); + erase(p.first, p.second); + return r; + } + + void swap(flat_multiset& other) { + using std::swap; + swap(data_, other.data_); + swap(compare_, other.compare_); + } + + size_type count(const key_type& key) const { + const auto p = equal_range(key); + return std::distance(p.first, p.second); + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !compare_(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !compare_(key, *iter) + ? iter + : end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, compare_); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, compare_); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, compare_); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, compare_); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, compare_); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, compare_); + } + + key_compare key_comp() const { + return compare_; + } + + value_compare value_comp() const { + return value_compare(compare_); + } + private: + container_type data_; + Compare compare_; + }; +} + +namespace flat_hpp +{ + template < typename Key + , typename Compare + , typename Container > + void swap( + flat_multiset& l, + flat_multiset& r) + { + l.swap(r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator==( + const flat_multiset& l, + const flat_multiset& r) + { + return l.size() == r.size() + && std::equal(l.begin(), l.end(), r.begin()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator!=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(l == r); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<( + const flat_multiset& l, + const flat_multiset& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>( + const flat_multiset& l, + const flat_multiset& r) + { + return r < l; + } + + template < typename Key + , typename Compare + , typename Container > + bool operator<=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(r < l); + } + + template < typename Key + , typename Compare + , typename Container > + bool operator>=( + const flat_multiset& l, + const flat_multiset& r) + { + return !(l < r); + } +} diff --git a/untests/flat_multimap_tests.cpp b/untests/flat_multimap_tests.cpp new file mode 100644 index 0000000..f3e024a --- /dev/null +++ b/untests/flat_multimap_tests.cpp @@ -0,0 +1,463 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#define CATCH_CONFIG_FAST_COMPILE +#include + +#include + +#include +using namespace flat_hpp; + +namespace +{ + template < typename T > + class dummy_allocator { + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using value_type = T; + + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template < typename U > + struct rebind { using other = dummy_allocator; }; + + dummy_allocator() = default; + dummy_allocator(int i) : i(i) {} + + template < typename U > + dummy_allocator(const dummy_allocator& o) noexcept { + i = o.i; + } + + T* allocate(std::size_t n) noexcept { + return static_cast(std::malloc(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t n) noexcept { + (void)n; + std::free(p); + } + + template < typename U, typename... Args > + void construct(U* p, Args&&... args) { + ::new((void*)p) U(std::forward(args)...); + } + + void destroy(pointer p) { + p->~T(); + } + + int i = 0; + }; + + template < typename T, typename U > + bool operator==(const dummy_allocator&, const dummy_allocator&) noexcept { + return true; + } + + template < typename T, typename U > + bool operator!=(const dummy_allocator& l, const dummy_allocator& r) noexcept { + return !(l == r); + } + + template < typename T > + constexpr std::add_const_t& my_as_const(T& t) noexcept { + return t; + } +} + +TEST_CASE("flat_multimap") { + SECTION("types") { + using map_t = flat_multimap; + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same>::value, + "unit test static error"); + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + + static_assert( + std::is_same&>::value, + "unit test static error"); + static_assert( + std::is_same&>::value, + "unit test static error"); + + static_assert( + std::is_same*>::value, + "unit test static error"); + static_assert( + std::is_same*>::value, + "unit test static error"); + } + SECTION("ctors") { + using alloc_t = dummy_allocator< + std::pair>; + + using map_t = flat_multimap< + int, + unsigned, + std::less, + std::vector, alloc_t>>; + + using map2_t = flat_multimap< + int, + unsigned, + std::greater, + std::vector, alloc_t>>; + + using vec_t = std::vector< + std::pair, alloc_t>; + + { + auto s0 = map_t(); + auto s1 = map2_t(alloc_t()); + auto s2 = map_t(std::less()); + auto s3 = map2_t(std::greater(), alloc_t()); + } + + { + vec_t v{{1,30},{2,20},{3,10}}; + auto s0 = map_t(v.cbegin(), v.cend()); + auto s1 = map2_t(v.cbegin(), v.cend(), alloc_t()); + auto s2 = map_t(v.cbegin(), v.cend(), std::less()); + auto s3 = map2_t(v.cbegin(), v.cend(), std::greater(), alloc_t()); + + REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({{1,30},{2,20},{3,10}})); + REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({{3,10},{2,20},{1,30}})); + REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({{1,30},{2,20},{3,10}})); + REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({{3,10},{2,20},{1,30}})); + } + + { + auto s0 = map_t({{0,1}, {1,2}}); + auto s1 = map_t({{0,1}, {1,2}}, alloc_t()); + auto s2 = map_t({{0,1}, {1,2}}, std::less()); + auto s3 = map_t({{0,1}, {1,2}}, std::less(), alloc_t()); + + REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({{0,1},{1,2}})); + REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({{0,1},{1,2}})); + REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({{0,1},{1,2}})); + REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({{0,1},{1,2}})); + } + + { + auto s0 = map_t{{0,1}, {1,2}}; + auto s1 = s0; + REQUIRE(s0 == map_t{{0,1}, {1,2}}); + REQUIRE(s1 == map_t{{0,1}, {1,2}}); + auto s2 = std::move(s1); + REQUIRE(s1.empty()); + REQUIRE(s2 == map_t{{0,1}, {1,2}}); + auto s3 = map_t(s2, alloc_t(42)); + REQUIRE(s2 == s3); + auto s4 = map_t(std::move(s3), alloc_t(21)); + REQUIRE(s3.empty()); + REQUIRE(s4 == map_t{{0,1}, {1,2}}); + } + + { + auto s0 = map_t{{0,1}, {1,2}}; + map_t s1; + s1 = s0; + REQUIRE(s0 == map_t{{0,1}, {1,2}}); + REQUIRE(s1 == map_t{{0,1}, {1,2}}); + map_t s2; + s2 = std::move(s1); + REQUIRE(s0 == map_t{{0,1}, {1,2}}); + REQUIRE(s1.empty()); + REQUIRE(s2 == map_t{{0,1}, {1,2}}); + map_t s3; + s3 = {{0,1}, {1,2}}; + REQUIRE(s3 == map_t{{0,1}, {1,2}}); + } + } + SECTION("capacity") { + using map_t = flat_multimap; + + { + map_t s0; + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + + s0.insert({2,42}); + + REQUIRE_FALSE(s0.empty()); + REQUIRE(s0.size() == 1u); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + + s0.insert({2,84}); + REQUIRE(s0.size() == 2u); + + s0.insert({3,84}); + REQUIRE(s0.size() == 3u); + + s0.clear(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator>().max_size()); + } + + { + map_t s0; + + REQUIRE(s0.capacity() == 0); + s0.reserve(42); + REQUIRE(s0.capacity() == 42); + s0.insert({{1,2},{2,3},{3,4}}); + REQUIRE(s0.capacity() == 42); + s0.shrink_to_fit(); + REQUIRE(s0.size() == 3); + REQUIRE(s0.capacity() == 3); + REQUIRE(s0 == map_t{{1,2},{2,3},{3,4}}); + + using alloc2_t = dummy_allocator< + std::pair>; + + using map2_t = flat_multimap< + int, + unsigned, + std::less, + std::deque, alloc2_t>>; + + map2_t s1; + s1.insert({{1,2},{2,3},{3,4}}); + REQUIRE(s1 == map2_t{{1,2},{2,3},{3,4}}); + } + } + SECTION("access") { + struct obj_t { + obj_t(int i) : i(i) {} + int i; + + bool operator<(const obj_t& o) const { + return i < o.i; + } + + bool operator==(const obj_t& o) const { + return i == o.i; + } + }; + + using map_t = flat_multimap; + map_t s0; + + obj_t k1(1); + + s0[k1] = 42; + REQUIRE(s0[k1] == 42); + REQUIRE(s0 == map_t{{1,42}}); + + s0[1] = 84; + REQUIRE(s0[1] == 84); + REQUIRE(s0 == map_t{{1,84}}); + + s0[2] = 21; + REQUIRE(s0[2] == 21); + REQUIRE(s0 == map_t{{1,84},{2,21}}); + + REQUIRE(s0.at(1) == 84); + REQUIRE(my_as_const(s0).at(k1) == 84); + REQUIRE_THROWS_AS(s0.at(0), std::out_of_range); + REQUIRE_THROWS_AS(my_as_const(s0).at(0), std::out_of_range); + } + SECTION("inserts") { + struct obj_t { + obj_t(int i) : i(i) {} + int i; + + bool operator<(const obj_t& o) const { + return i < o.i; + } + + bool operator==(const obj_t& o) const { + return i == o.i; + } + }; + + using map_t = flat_multimap; + + { + map_t s0; + + auto k1_42 = std::make_pair(1, 42); + auto k3_84 = std::make_pair(3, 84); + + auto i0 = s0.insert(k1_42); + REQUIRE(s0 == map_t{{1,42}}); + REQUIRE(i0 == s0.begin()); + + auto i1 = s0.insert(std::make_pair(1, obj_t(21))); + REQUIRE(s0 == map_t{{1,42},{1,21}}); + REQUIRE(i1 == s0.begin() + 1); + + auto i2 = s0.insert(std::make_pair(2, obj_t(42))); + REQUIRE(s0 == map_t{{1,42},{1,21},{2,42}}); + REQUIRE(i2 == s0.begin() + 2); + + auto i3 = s0.insert(s0.cend(), k3_84); + REQUIRE(i3 == s0.begin() + 3); + + s0.insert(s0.cend(), std::make_pair(4, obj_t(84))); + auto i4 = s0.insert(s0.cend(), std::make_pair(0, obj_t(21))); + REQUIRE(i4 == s0.begin()); + + auto i5 = s0.emplace(5, 100500); + REQUIRE(i5 == s0.end() - 1); + REQUIRE(s0 == map_t{{0,21},{1,42},{1,21},{2,42},{3,84},{4,84},{5,100500}}); + + auto i6 = s0.emplace_hint(s0.cend(), 6, 100500); + REQUIRE(i6 == s0.end() - 1); + REQUIRE(s0 == map_t{{0,21},{1,42},{1,21},{2,42},{3,84},{4,84},{5,100500},{6,100500}}); + } + } + SECTION("erasers") { + using map_t = flat_multimap; + { + map_t s0{{1,2},{2,3},{3,4}}; + s0.clear(); + REQUIRE(s0.empty()); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + auto i = s0.erase(s0.find(2)); + REQUIRE(i == s0.begin() + 1); + REQUIRE(s0 == map_t{{1,2},{3,4}}); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + auto i = s0.erase(s0.begin() + 1, s0.end()); + REQUIRE(i == s0.end()); + REQUIRE(s0 == map_t{{1,2}}); + } + { + map_t s0{{1,2},{2,3},{2,1},{3,4}}; + REQUIRE(s0.erase(1) == 1); + REQUIRE(s0.erase(2) == 2); + REQUIRE(s0.erase(6) == 0); + REQUIRE(s0 == map_t{{3,4}}); + } + { + map_t s0{{1,2},{2,3},{3,4}}; + map_t s1{{2,3},{3,4},{5,6}}; + s0.swap(s1); + REQUIRE(s0 == map_t{{2,3},{3,4},{5,6}}); + REQUIRE(s1 == map_t{{1,2},{2,3},{3,4}}); + swap(s1, s0); + REQUIRE(s0 == map_t{{1,2},{2,3},{3,4}}); + REQUIRE(s1 == map_t{{2,3},{3,4},{5,6}}); + } + } + SECTION("lookup") { + using map_t = flat_multimap; + { + map_t s0{{1,2},{2,3},{2,1},{3,4},{4,5},{5,6}}; + REQUIRE(s0.count(3) == 1); + REQUIRE(s0.count(2) == 2); + REQUIRE_FALSE(s0.count(6)); + REQUIRE(my_as_const(s0).count(5)); + REQUIRE_FALSE(my_as_const(s0).count(0)); + } + { + map_t s0{{1,2},{2,3},{3,4},{4,5},{5,6}}; + REQUIRE(s0.find(2) == s0.begin() + 1); + REQUIRE(my_as_const(s0).find(3) == s0.cbegin() + 2); + REQUIRE(s0.find(6) == s0.end()); + REQUIRE(my_as_const(s0).find(0) == s0.cend()); + } + { + map_t s0{{1,2},{2,3},{2,1},{3,4},{4,5},{5,6}}; + REQUIRE(s0.equal_range(2) == std::make_pair(s0.begin() + 1, s0.begin() + 3)); + REQUIRE(s0.equal_range(6) == std::make_pair(s0.end(), s0.end())); + REQUIRE(my_as_const(s0).equal_range(2) == std::make_pair(s0.cbegin() + 1, s0.cbegin() + 3)); + REQUIRE(my_as_const(s0).equal_range(0) == std::make_pair(s0.cbegin(), s0.cbegin())); + } + { + map_t s0{{0,1},{0,0},{3,2},{6,3}}; + REQUIRE(s0.lower_bound(0) == s0.begin()); + REQUIRE(s0.lower_bound(1) == s0.begin() + 2); + REQUIRE(s0.lower_bound(10) == s0.end()); + REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); + REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 4); + } + } + SECTION("observers") { + struct my_less { + int i; + my_less(int i) : i(i) {} + bool operator()(int l, int r) const { + return l < r; + } + }; + using map_t = flat_multimap; + map_t s0(my_less(42)); + REQUIRE(my_as_const(s0).key_comp().i == 42); + REQUIRE(my_as_const(s0).value_comp()({2,50},{4,20})); + } + SECTION("operators") { + using map_t = flat_multimap; + + REQUIRE(map_t{{1,2},{3,4}} == map_t{{3,4},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{2,4},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{1,3},{1,2}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} == map_t{{3,4},{1,2},{0,0}}); + + REQUIRE_FALSE(map_t{{1,2},{3,4}} != map_t{{3,4},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{2,4},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{1,3},{1,2}}); + REQUIRE(map_t{{1,2},{3,4}} != map_t{{3,4},{1,2},{0,0}}); + + REQUIRE(map_t{{0,2},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,1},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} < map_t{{1,2},{3,4},{5,6}}); + + REQUIRE(map_t{{0,2},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,1},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} <= map_t{{1,2},{3,4},{5,6}}); + + REQUIRE(map_t{{1,2},{3,4}} > map_t{{0,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} > map_t{{1,1},{3,4}}); + REQUIRE(map_t{{1,2},{3,4},{5,6}} > map_t{{1,2},{3,4}}); + + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{0,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{1,1},{3,4}}); + REQUIRE(map_t{{1,2},{3,4},{5,6}} >= map_t{{1,2},{3,4}}); + + REQUIRE_FALSE(map_t{{1,2},{3,4}} < map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} <= map_t{{1,2},{3,4}}); + REQUIRE_FALSE(map_t{{1,2},{3,4}} > map_t{{1,2},{3,4}}); + REQUIRE(map_t{{1,2},{3,4}} >= map_t{{1,2},{3,4}}); + + const map_t s0; + REQUIRE(s0 == s0); + REQUIRE_FALSE(s0 != s0); + REQUIRE_FALSE(s0 < s0); + REQUIRE_FALSE(s0 > s0); + REQUIRE(s0 <= s0); + REQUIRE(s0 >= s0); + } +} diff --git a/untests/flat_multiset_tests.cpp b/untests/flat_multiset_tests.cpp new file mode 100644 index 0000000..d25a23d --- /dev/null +++ b/untests/flat_multiset_tests.cpp @@ -0,0 +1,428 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/flat.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#define CATCH_CONFIG_FAST_COMPILE +#include + +#include + +#include +using namespace flat_hpp; + +namespace +{ + template < typename T > + class dummy_allocator { + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using value_type = T; + + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template < typename U > + struct rebind { using other = dummy_allocator; }; + + dummy_allocator() = default; + dummy_allocator(int i) : i(i) {} + + template < typename U > + dummy_allocator(const dummy_allocator& o) noexcept { + i = o.i; + } + + T* allocate(std::size_t n) noexcept { + return static_cast(std::malloc(sizeof(T) * n)); + } + + void deallocate(T* p, std::size_t n) noexcept { + (void)n; + std::free(p); + } + + template < typename U, typename... Args > + void construct(U* p, Args&&... args) { + ::new((void*)p) U(std::forward(args)...); + } + + void destroy(pointer p) { + p->~T(); + } + + int i = 0; + }; + + template < typename T, typename U > + bool operator==(const dummy_allocator&, const dummy_allocator&) noexcept { + return true; + } + + template < typename T, typename U > + bool operator!=(const dummy_allocator& l, const dummy_allocator& r) noexcept { + return !(l == r); + } + + template < typename T > + constexpr std::add_const_t& my_as_const(T& t) noexcept { + return t; + } +} + +TEST_CASE("flat_multiset") { + SECTION("types") { + using set_t = flat_multiset; + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + + static_assert( + std::is_same::value, + "unit test static error"); + static_assert( + std::is_same::value, + "unit test static error"); + } + SECTION("ctors") { + using alloc_t = dummy_allocator; + using set_t = flat_multiset, std::vector>; + using set2_t = flat_multiset, std::vector>; + using vec_t = std::vector; + + { + auto s0 = set_t(); + auto s1 = set2_t(alloc_t()); + auto s2 = set_t(std::less()); + auto s3 = set2_t(std::greater(), alloc_t()); + } + + { + vec_t v{1,2,3}; + auto s0 = set_t(v.cbegin(), v.cend()); + auto s1 = set2_t(v.cbegin(), v.cend(), alloc_t()); + auto s2 = set_t(v.cbegin(), v.cend(), std::less()); + auto s3 = set2_t(v.cbegin(), v.cend(), std::greater(), alloc_t()); + + REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({1,2,3})); + REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({3,2,1})); + REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({1,2,3})); + REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({3,2,1})); + } + + { + auto s0 = set_t({0,1,2}); + auto s1 = set2_t({0,1,2}, alloc_t()); + auto s2 = set_t({0,1,2}, std::less()); + auto s3 = set2_t({0,1,2}, std::greater(), alloc_t()); + + REQUIRE(vec_t(s0.begin(), s0.end()) == vec_t({0,1,2})); + REQUIRE(vec_t(s1.begin(), s1.end()) == vec_t({2,1,0})); + REQUIRE(vec_t(s2.begin(), s2.end()) == vec_t({0,1,2})); + REQUIRE(vec_t(s3.begin(), s3.end()) == vec_t({2,1,0})); + } + + { + auto s0 = set_t{0,1,2}; + auto s1 = s0; + REQUIRE(s0 == set_t{0,1,2}); + REQUIRE(s1 == set_t{0,1,2}); + auto s2 = std::move(s1); + REQUIRE(s1.empty()); + REQUIRE(s2 == set_t{0,1,2}); + auto s3 = set_t(s2, alloc_t(42)); + REQUIRE(s2 == s3); + auto s4 = set_t(std::move(s3), alloc_t(21)); + REQUIRE(s3.empty()); + REQUIRE(s4 == set_t{0,1,2}); + } + + { + auto s0 = set_t{0,1,2}; + set_t s1; + s1 = s0; + REQUIRE(s0 == set_t{0,1,2}); + REQUIRE(s1 == set_t{0,1,2}); + set_t s2; + s2 = std::move(s1); + REQUIRE(s0 == set_t{0,1,2}); + REQUIRE(s1.empty()); + REQUIRE(s2 == set_t{0,1,2}); + set_t s3; + s3 = {1,2,3}; + REQUIRE(s3 == set_t{1,2,3}); + } + } + SECTION("capacity") { + using set_t = flat_multiset; + + { + set_t s0; + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator().max_size()); + + s0.insert(42); + + REQUIRE_FALSE(s0.empty()); + REQUIRE(s0.size() == 1u); + REQUIRE(s0.max_size() == std::allocator().max_size()); + + s0.insert(42); + REQUIRE(s0.size() == 2u); + + s0.insert(84); + REQUIRE(s0.size() == 3u); + + s0.clear(); + + REQUIRE(s0.empty()); + REQUIRE_FALSE(s0.size()); + REQUIRE(s0.max_size() == std::allocator().max_size()); + } + + { + set_t s0; + + REQUIRE(s0.capacity() == 0); + s0.reserve(42); + REQUIRE(s0.capacity() == 42); + s0.insert({1,2,3}); + REQUIRE(s0.capacity() == 42); + s0.shrink_to_fit(); + REQUIRE(s0.size() == 3); + REQUIRE(s0.capacity() == 3); + REQUIRE(s0 == set_t{1,2,3}); + + using alloc2_t = dummy_allocator; + + using set2_t = flat_multiset< + int, + std::less, + std::deque>; + + set2_t s1; + s1.insert({1,2,3}); + REQUIRE(s1 == set2_t{1,2,3}); + } + } + SECTION("inserts") { + struct obj_t { + obj_t(int i) : i(i) {} + int i; + + bool operator<(const obj_t& o) const { + return i < o.i; + } + + bool operator==(const obj_t& o) const { + return i == o.i; + } + }; + + using set_t = flat_multiset; + + { + set_t s0; + + auto i0 = s0.insert(1); // 1 + REQUIRE(s0 == set_t{1}); + REQUIRE(i0 == s0.begin()); + + auto i1 = s0.insert(obj_t(1)); // 1,1 + REQUIRE(s0 == set_t{1,1}); + REQUIRE(i1 == s0.begin() + 1); + + auto i2 = s0.insert(obj_t(2)); // 1,1,2 + REQUIRE(s0 == set_t{1,1,2}); + REQUIRE(i2 == s0.begin() + 2); + + auto o2 = obj_t(2); + auto i3 = s0.insert(o2); // 1,1,2,2 + REQUIRE(i3 == s0.begin() + 3); + + s0.insert(s0.cbegin(), 1); // 1,1,1,2,2 + s0.insert(s0.cbegin(), 2); // 1,1,1,2,2,2 + s0.insert(s0.cend(), 1); // 1,1,1,1,2,2,2 + s0.insert(s0.cend(), 2); // 1,1,1,1,2,2,2,2 + REQUIRE(s0 == set_t{1,1,1,1,2,2,2,2}); + + s0.insert(s0.cbegin(), 0); // 0,1,1,1,1,2,2,2,2 + REQUIRE(s0 == set_t{0,1,1,1,1,2,2,2,2}); + s0.insert(s0.cend(), 3); // 0,1,1,1,1,2,2,2,2,3 + REQUIRE(s0 == set_t{0,1,1,1,1,2,2,2,2,3}); + s0.insert(s0.cbegin(), 4); // 0,1,1,1,1,2,2,2,2,3,4 + s0.insert(s0.cend(), -1); // -1,0,1,1,1,1,2,2,2,2,3,4 + REQUIRE(s0 == set_t{-1,0,1,1,1,1,2,2,2,2,3,4}); + + s0.insert(s0.cbegin() + 2, obj_t(5)); // -1,0,1,1,1,1,2,2,2,2,3,4,5 + REQUIRE(s0 == set_t{-1,0,1,1,1,1,2,2,2,2,3,4,5}); + s0.insert(s0.cbegin(), obj_t(-2)); // -2,-1,0,1,1,1,1,2,2,2,2,3,4,5 + REQUIRE(s0 == set_t{-2,-1,0,1,1,1,1,2,2,2,2,3,4,5}); + } + + { + set_t s0; + + auto e0 = s0.emplace(3); // 3 + REQUIRE(s0 == set_t{3}); + REQUIRE(e0 == s0.begin()); + + auto e1 = s0.emplace(obj_t(3)); // 3,3 + REQUIRE(e1 == s0.begin() + 1); + + auto e2 = s0.emplace(4); // 3,3,4 + REQUIRE(s0 == set_t{3,3,4}); + REQUIRE(e2 == s0.begin() + 2); + + auto e3 = s0.emplace_hint(s0.cbegin(), 1); // 1,3,3,4 + REQUIRE(e3 == s0.begin()); + auto e4 = s0.emplace_hint(s0.cend(), 2); // 1,2,3,3,4 + REQUIRE(e4 == s0.begin() + 1); + s0.emplace_hint(s0.cbegin(), 5); // 1,2,3,3,4,5 + s0.emplace_hint(s0.cend(), 6); // 1,2,3,3,4,5,6 + REQUIRE(s0 == set_t{1,2,3,3,4,5,6}); + } + } + SECTION("erasers") { + using set_t = flat_multiset; + { + set_t s0{1,2,3,4,5}; + s0.clear(); + REQUIRE(s0.empty()); + } + { + set_t s0{1,2,3,4,5}; + auto i = s0.erase(s0.find(3)); + REQUIRE(i == s0.begin() + 2); + REQUIRE(s0 == set_t{1,2,4,5}); + } + { + set_t s0{1,2,3,4,5}; + auto i = s0.erase(s0.begin() + 2, s0.end()); + REQUIRE(i == s0.end()); + REQUIRE(s0 == set_t{1,2}); + } + { + set_t s0{1,2,3,3,4,5}; + REQUIRE(s0.erase(2) == 1); + REQUIRE(s0.erase(3) == 2); + REQUIRE(s0.erase(6) == 0); + REQUIRE(s0 == set_t{1,4,5}); + } + { + set_t s0{1,2,3}; + set_t s1{3,4,5}; + s0.swap(s1); + REQUIRE(s0 == set_t{3,4,5}); + REQUIRE(s1 == set_t{1,2,3}); + swap(s1, s0); + REQUIRE(s0 == set_t{1,2,3}); + REQUIRE(s1 == set_t{3,4,5}); + } + } + SECTION("lookup") { + using set_t = flat_multiset; + { + set_t s0{1,2,3,3,4,5}; + REQUIRE(s0.count(2) == 1); + REQUIRE(s0.count(3) == 2); + REQUIRE_FALSE(s0.count(6)); + REQUIRE(my_as_const(s0).count(5)); + REQUIRE_FALSE(my_as_const(s0).count(0)); + } + { + set_t s0{1,2,3,4,5}; + REQUIRE(s0.find(2) == s0.begin() + 1); + REQUIRE(my_as_const(s0).find(3) == s0.cbegin() + 2); + REQUIRE(s0.find(6) == s0.end()); + REQUIRE(my_as_const(s0).find(0) == s0.cend()); + } + { + set_t s0{1,2,3,3,4,5}; + REQUIRE(s0.equal_range(3) == std::make_pair(s0.begin() + 2, s0.begin() + 4)); + REQUIRE(s0.equal_range(6) == std::make_pair(s0.end(), s0.end())); + REQUIRE(my_as_const(s0).equal_range(3) == std::make_pair(s0.cbegin() + 2, s0.cbegin() + 4)); + REQUIRE(my_as_const(s0).equal_range(0) == std::make_pair(s0.cbegin(), s0.cbegin())); + } + { + set_t s0{0,0,3,6,9}; + REQUIRE(s0.lower_bound(0) == s0.begin()); + REQUIRE(s0.lower_bound(1) == s0.begin() + 2); + REQUIRE(s0.lower_bound(10) == s0.end()); + REQUIRE(my_as_const(s0).lower_bound(-1) == s0.cbegin()); + REQUIRE(my_as_const(s0).lower_bound(7) == s0.cbegin() + 4); + } + } + SECTION("observers") { + struct my_less { + int i; + my_less(int i) : i(i) {} + bool operator()(int l, int r) const { + return l < r; + } + }; + using set_t = flat_multiset; + set_t s0(my_less(42)); + REQUIRE(my_as_const(s0).key_comp().i == 42); + REQUIRE(my_as_const(s0).value_comp().i == 42); + } + SECTION("operators") { + using set_t = flat_multiset; + + REQUIRE(set_t{1,2,3} == set_t{3,2,1}); + REQUIRE_FALSE(set_t{1,2,3} == set_t{3,2,4}); + REQUIRE_FALSE(set_t{1,2,3} == set_t{1,2,3,4}); + + REQUIRE(set_t{1,2,3} != set_t{3,2,4}); + REQUIRE_FALSE(set_t{1,2,3} != set_t{3,2,1}); + + REQUIRE(set_t{2,3,4,6} < set_t{2,3,5}); + REQUIRE(set_t{2,3,4,6} <= set_t{2,3,5}); + REQUIRE_FALSE(set_t{2,3,5} < set_t{2,3,4,6}); + REQUIRE_FALSE(set_t{2,3,5} <= set_t{2,3,4,6}); + + REQUIRE_FALSE(set_t{2,3,4,6} > set_t{2,3,5}); + REQUIRE_FALSE(set_t{2,3,4,6} >= set_t{2,3,5}); + REQUIRE(set_t{2,3,5} > set_t{2,3,4,6}); + REQUIRE(set_t{2,3,5} >= set_t{2,3,4,6}); + + REQUIRE_FALSE(set_t{1,2,3} < set_t{1,2,3}); + REQUIRE(set_t{1,2,3} <= set_t{1,2,3}); + REQUIRE_FALSE(set_t{1,2,3} > set_t{1,2,3}); + REQUIRE(set_t{1,2,3} >= set_t{1,2,3}); + + const set_t s0; + REQUIRE(s0 == s0); + REQUIRE_FALSE(s0 != s0); + REQUIRE_FALSE(s0 < s0); + REQUIRE_FALSE(s0 > s0); + REQUIRE(s0 <= s0); + REQUIRE(s0 >= s0); + } +}