#ifndef ASSOCIATIVE_H
#define ASSOCIATIVE_H
#pragma once
#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <vector>
template<class iterator> inline auto structure_dereference_operator(const iterator& r) -> typename std::iterator_traits<iterator>::pointer {return r.operator->();}
template<class type> inline typename std::iterator_traits<type*>::pointer structure_dereference_operator(const type*& r) {return r;}
#pragma warning(push)
#pragma warning(disable : 4820) //warning C4820: 'associative<key_,mapped_>' : '3' bytes padding added after data member 'associative<key_,mapped_>::comp_'
//http://msdn.microsoft.com/en-us/library/xdayte4c.aspx
// carefully designed to also work on a linked list. Not that you should, because that would be silly.
template <class key_,
class mapped_,
class traits_ = std::less<key_>,
class undertype_ = std::vector<std::pair<key_,mapped_> >
>
class associative
{
public:
typedef traits_ key_compare;
typedef key_ key_type;
typedef mapped_ mapped_type;
typedef std::pair<const key_type, mapped_type> value_type;
typedef typename undertype_::allocator_type allocator_type;
typedef typename allocator_type::template rebind<value_type>::other value_allocator_type;
typedef typename undertype_::const_iterator const_iterator;
typedef typename value_allocator_type::const_pointer const_pointer;
typedef typename value_allocator_type::const_reference const_reference;
typedef typename undertype_::const_reverse_iterator const_reverse_iterator;
typedef typename value_allocator_type::difference_type difference_type;
typedef typename value_allocator_type::pointer pointer;
typedef typename value_allocator_type::reference reference;
typedef typename undertype_::size_type size_type;
typedef typename undertype_::iterator underiter;
typedef typename undertype_::const_iterator underciter;
typedef typename undertype_::reverse_iterator underriter;
typedef std::pair<key_type, mapped_type> undertype;
static_assert(std::is_same<typename undertype_::value_type, undertype >::value, "container::value_type must be std::pair<key_,mapped_>");
static_assert(std::is_same<typename undertype_::reference, typename allocator_type::reference>::value, "container::reference must be allocator_type::reference");
static_assert(std::is_same<typename undertype_::pointer, typename allocator_type::pointer>::value, "container::pointer must be allocator_type::pointer");
class value_compare {
key_compare pred_;
public:
inline value_compare(key_compare pred=key_compare()) : pred_(pred) {}
template <typename L, typename R>
inline bool operator()(const L& left, const R& right) const { return pred_(left.first,right.first); }
template <typename L>
inline bool operator()(const L& left, const key_type& right) const { return pred_(left.first,right); }
template <typename R>
inline bool operator()(const key_type& left, const R& right) const { return pred_(left,right.first); }
inline bool operator()(const key_type& left, const key_type& right) const { return pred_(left,right); }
inline key_compare key_comp( ) const {return pred_;}
};
class iterator {
public:
friend class associative<key_,mapped_,traits_,undertype_>;
typedef typename value_allocator_type::difference_type difference_type;
typedef typename value_allocator_type::value_type value_type;
typedef typename value_allocator_type::reference reference;
typedef typename value_allocator_type::pointer pointer;
typedef std::bidirectional_iterator_tag iterator_category;
inline iterator() : data() {}
inline iterator(const iterator& rhs) : data(rhs.data) {}
inline iterator& operator=(const iterator& rhs) {data=rhs.data; return *this;}
inline bool operator==(const iterator& rhs) const {return data==rhs.data;}
inline bool operator!=(const iterator& rhs) const {return data!=rhs.data;}
inline iterator& operator++() {++data; return *this;}
inline iterator operator++(int) {return iterator(data++);}
inline iterator& operator--() {--data; return *this;}
inline iterator operator--(int) {return iterator(data--);}
inline reference operator*() const { return reinterpret_cast<reference>(*data);} //cant const_cast half of a pair
inline pointer operator->() const {return reinterpret_cast<pointer>(structure_dereference_operator(data));} //cant const_cast half of a pair
inline operator const_iterator&() {return data;}
protected:
explicit inline iterator(const underiter& rhs) : data(rhs) {}
underiter data;
};
class reverse_iterator {
public:
friend class associative<key_,mapped_,traits_,undertype_>;
typedef typename value_allocator_type::difference_type difference_type;
typedef typename value_allocator_type::value_type value_type;
typedef typename value_allocator_type::reference reference;
typedef typename value_allocator_type::pointer pointer;
typedef std::bidirectional_iterator_tag iterator_category;
inline reverse_iterator() : data() {}
inline reverse_iterator(const reverse_iterator& rhs) : data(rhs.data) {}
inline reverse_iterator& operator=(const reverse_iterator& rhs) {data=rhs.data; return *this;}
inline bool operator==(const reverse_iterator& rhs) const {return data==rhs.data;}
inline bool operator!=(const reverse_iterator& rhs) const {return data!=rhs.data;}
inline reverse_iterator& operator++() {++data; return *this;}
inline reverse_iterator operator++(int) {return reverse_iterator(data++);}
inline reverse_iterator& operator--() {--data; return *this;}
inline reverse_iterator operator--(int) {return reverse_iterator(data--);}
inline reference operator*() const { return reinterpret_cast<reference>(*data);} //cant const_cast half of a pair
inline pointer operator->() const {return reinterpret_cast<pointer>(structure_dereference_operator(data));} //cant const_cast half of a pair
inline operator underriter&() {return data;}
protected:
explicit inline reverse_iterator(const underriter& rhs) : data(rhs) {}
underriter data;
};
inline associative() : internal_(), comp_() {}
explicit associative(const key_compare& traits) : internal_(), comp_(traits) {}
inline associative(const key_compare& traits, const allocator_type& allocator) :internal_(allocator), comp_(traits) {}
inline associative(const associative& rhs) : internal_(rhs.internal_), comp_(rhs.comp_) {}
inline associative(associative&& rhs) : internal_(std::move(rhs.internal_)), comp_(std::move(rhs.comp_)) {}
template<class input_iterator>
inline associative(input_iterator first, input_iterator last) : internal_(first, last), comp_()
{if (std::is_sorted(internal_.begin(), internal_.end())==false) std::sort(internal_.begin(), internal_.end(), comp_);}
template<class input_iterator>
inline associative(input_iterator first, input_iterator last, const key_compare& traits) : internal_(first, last, traits), comp_(traits)
{if (std::is_sorted(internal_.begin(), internal_.end())==false) std::sort(internal_.begin(), internal_.end(), comp_);}
template<class input_iterator>
inline associative(input_iterator first, input_iterator last, const key_compare& traits, const allocator_type& allocator) : internal_(first, last, allocator), comp_(traits)
{if (std::is_sorted(internal_.begin(), internal_.end())==false) std::sort(internal_.begin(), internal_.end(), comp_);}
inline ~associative() {}
//TODO assign from different underlying type
inline associative& operator=(const associative& right){internal_=right.internal_; comp_=right.comp_; return *this;}
inline associative& operator=(associative&& right){internal_=std::move(right.internal_); comp_=std::move(right.comp_); return *this;}
inline const_iterator begin() const {return internal_.begin();}
inline iterator begin() {return iterator(internal_.begin());}
inline const_iterator cbegin() const {return internal_.cbegin();}
inline const_iterator end() const {return internal_.end();}
inline iterator end() {return iterator(internal_.end());}
inline const_iterator cend() const {return internal_.cend();}
inline const_reverse_iterator rbegin() const {return internal_.rbegin();}
inline reverse_iterator rbegin() {return reverse_iterator(internal_.rbegin());}
inline const_reverse_iterator crbegin() const {return internal_.crbegin();}
inline const_reverse_iterator rend() const {return internal_.rend();}
inline reverse_iterator rend() {return reverse_iterator(internal_.rend());}
inline const_reverse_iterator crend() const {return internal_.crend();}
inline std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const {return std::equal_range(internal_.begin(), internal_.end(), key, comp_);}
inline std::pair<iterator, iterator> equal_range(const key_type& key) {
auto i = std::equal_range(internal_.begin(), internal_.end(), key, comp_);
return std::pair<iterator, iterator>(iterator(i.first), iterator(i.second));
}
inline iterator lower_bound(const key_type& key) {return iterator(std::lower_bound(internal_.begin(), internal_.end(), key, comp_));}
inline const_iterator lower_bound(const key_type& key) const {return std::lower_bound(internal_.begin(), internal_.end(), key, comp_);}
inline iterator upper_bound(const key_type& key) {return iterator(std::upper_bound(internal_.begin(), internal_.end(), key, comp_));}
inline const_iterator upper_bound(const key_type& key) const {return std::upper_bound(internal_.begin(), internal_.end(), key, comp_);}
inline mapped_type& at(const key_type& key) {
underiter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
if (i==internal_.end() || comp_(key,*i))
throw std::out_of_range("invalid associative<K, T> key");
return i->second;
}
inline const mapped_type& at(const key_type& key) const {
underciter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
if (i==internal_.end() || comp_(key,*i))
throw std::out_of_range("invalid associative<K, T> key");
return i->second;
}
inline size_type count(const key_type& key) const {
underciter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
return (i==internal_.end() || comp_(key,*i) ? 0u : 1u);
}
template<class rhs_type>
inline std::pair<iterator, bool> emplace(rhs_type&& rhs) {
underiter i = std::lower_bound(internal_.begin(), internal_.end(), rhs, comp_);
if (i==internal_.end() || comp_(rhs,*i))
return std::pair<iterator,bool>(iterator(internal_.emplace(i, std::move(rhs))), true);
else {
*i = std::move(rhs);
return std::pair<iterator,bool>(iterator(i), false);
}
}
template<class rhs_type>
std::pair<iterator, bool> emplace_hint(const_iterator where, rhs_type&& rhs) {
//TODO fix insert back
if (where==internal_.end()) { return std::pair<iterator,bool>(iterator(internal_.emplace(where, std::move(rhs))), true);}
if (!comp_(rhs,*where)) { return emplace(std::move(rhs));}
if (where == internal_.begin()) {return std::pair<iterator,bool>(iterator(internal_.emplace(where, std::move(rhs))), true);}
const_iterator n=where;
--n;
if(comp_(rhs,n)) { return emplace(std::move(rhs));}
return std::pair<iterator,bool>(iterator(internal_.emplace(where, std::move(rhs))), true);
}
inline iterator erase(iterator where) {return iterator(internal_.erase(where));}
inline iterator erase(iterator first, iterator last) {return iterator(internal_.erase(first, last));}
inline size_type erase(const key_type& key) {
underiter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
if (i==internal_.end() || comp_(key,*i))
return 0;
internal_.erase(i);
return 1;
}
template<class predicate>
inline iterator remove_if(predicate pred) {iterator(internal_.erase(std::remove_if(internal_.begin(),internal_.end(),pred), internal_.end()));}
inline iterator find(const key_type& key) {
underiter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
return iterator((i==internal_.end() || comp_(key,*i)) ? internal_.end() : i);
}
inline const_iterator find(const key_type& key) const {
underciter i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
return const_iterator((i==internal_.end() || comp_(key,*i)) ? internal_.end() : i);
}
inline std::pair<iterator, bool> insert(const value_type& rhs) {
underiter i = std::lower_bound(internal_.begin(), internal_.end(), rhs, comp_);
if (i==internal_.end() || comp_(rhs,*i))
return std::pair<iterator, bool>(iterator(internal_.emplace(i, std::move(rhs))), true);
else {
*i = std::move(rhs);
return std::pair<iterator, bool>(iterator(i), false);
}
}
iterator insert(const_iterator where, const value_type& rhs) {
//TODO fix insert back
if (where==internal_.end()) { return iterator(internal_.insert(where, rhs));}
if (!comp_(rhs,*where)) { return insert(rhs).first;}
if (where == internal_.begin()) {return iterator(internal_.insert(where, rhs));}
underciter n=where;
--n;
if(comp_(rhs,*n)) { return insert(rhs).first;}
return iterator(internal_.insert(where, rhs));
}
template<class input_iterator>
void insert(input_iterator first, input_iterator last) {
int s = std::distance(first, last);
while(first!=last)
internal_.insert(internal_.end(),*(first++));
iterator mid(internal_.end());
std::advance(mid, -s);
std::inplace_merge(internal_.begin(), mid, internal_.end());
}
template<class rhs_type>
inline std::pair<iterator, bool> insert(rhs_type&& rhs) {return emplace(std::move(rhs));}
template<class rhs_type>
inline iterator insert(const_iterator where, rhs_type&& rhs) {return emplace(where, std::move(rhs));}
inline mapped_type& operator[](const key_type& key) {return insert(value_type(key, mapped_type())).first->second;}
inline mapped_type& operator[](const key_type&& key) {return emplace(value_type(std::move(key), mapped_type())).first->second;}
inline const mapped_type& operator[](const key_type& key) const {return at(key);}
inline void clear() {internal_.clear();}
inline bool empty() const {return internal_.empty();}
inline size_type size( ) const {return internal_.size();}
inline void swap(associative& rhs) {internal_.swap(rhs.internal_); std::swap(comp_,rhs.comp_);}
inline friend void swap(associative& left, associative& right) {left.swap(right);}
inline allocator_type get_allocator() const {return internal_.get_allocator();}
inline key_compare key_comp() const{return comp_.key_comp();}
inline value_compare value_comp() const{return comp_;}
inline size_type max_size() const{return internal_.max_size();}
inline friend bool operator==(const associative& left, const associative& right) {return std::equal(left.internal_.begin(), left.internal_.end(), right.internal_.begin(), left.comp_);}
inline friend bool operator!=(const associative& left, const associative& right) {return !std::equal(left.internal_.begin(), left.internal_.end(), right.internal_.begin(), left.comp_);}
inline friend bool operator< (const associative& left, const associative& right) {return std::lexicographical_compare(left.internal_.begin(), left.internal_.end(), right.internal_.begin(), right.internal_.end(), left.comp_);}
inline friend bool operator<=(const associative& left, const associative& right) {return std::lexicographical_compare(right.internal_.begin(), right.internal_.end(), left.internal_.begin(), left.internal_.end(), left.comp_);}
inline friend bool operator> (const associative& left, const associative& right) {return !std::lexicographical_compare(right.internal_.begin(), right.internal_.end(), left.internal_.begin(), left.internal_.end(), left.comp_);}
inline friend bool operator>=(const associative& left, const associative& right) {return !std::lexicographical_compare(left.internal_.begin(), left.internal_.end(), right.internal_.begin(), right.internal_.end(), left.comp_);}
protected:
undertype_ internal_;
value_compare comp_;
};
#pragma warning(pop)
namespace std {
template<class key_, class mapped_, class traits_, class undertype_>
inline void swap(associative <key_, mapped_, traits_, undertype_>& left, associative <key_, mapped_, traits_, undertype_>& right)
{left.swap(right);}
};
#endif //ASSOCIATIVE_H
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <deque>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <string>
#include <vector>
#ifdef _DEBUG
#define WARMUP_TIME_MILLISECONDS 1
#define TEST_TIME_MILLISECONDS 9
#else
#define WARMUP_TIME_MILLISECONDS 100
#define TEST_TIME_MILLISECONDS 900
#endif
template<unsigned int size>
struct payload {
static unsigned int copycount;
int data[size/4];
payload() {data[0]=rand()*RAND_MAX+rand();}
#ifdef _DEBUG
payload(const payload& r) {
memcpy(data, r.data, sizeof(data));
++copycount;
}
payload& operator=(const payload& r) {
memcpy(data, r.data, sizeof(data));
++copycount;
return *this;
}
#endif
bool operator==(const payload& rhs) const {return data[0]==rhs.data[0];}
bool operator<(const payload& rhs) const {return data[0]<rhs.data[0];}
};
template<unsigned int size>
unsigned int payload<size>::copycount;
template<class payload, class container>
void test_internal(const std::vector<std::pair<payload, payload>>& data, const char* classname, const char* payloadname) {
clock_t beg,end,elapsed,freq;
unsigned int count=0;
bool timed=false;
freq = CLOCKS_PER_SEC;
std::vector<std::pair<payload, payload>> shuffled(data.begin(), data.end());
std::random_shuffle(shuffled.begin(), shuffled.end());
std::vector<std::pair<payload, payload>> sorted(data.begin(), data.end());
std::sort(sorted.begin(), sorted.end());
std::cout << payloadname << ' ' << std::setw(4) << data.size() << ' ' << classname << ' ';
timed = false;
elapsed = 0;
count = 0;
do {
beg = clock();
container a(data.begin(), data.end());
end = clock();
//assert(data.size()==a.size()); //meh, duplicates
elapsed += end-beg;
if (elapsed>WARMUP_TIME_MILLISECONDS && !timed) {
elapsed =0;
count=0;
timed = true;
}
++count;
} while(elapsed<TEST_TIME_MILLISECONDS);
std::cout << std::setw(9) << count << ' ';
timed = false;
elapsed = 0;
count = 0;
{
container a(data.begin(), data.end());
do {
beg = clock();
auto i=a.find(shuffled[count%data.size()].first);
end = clock();
assert(i != a.end());
assert(i->second == shuffled[count%data.size()].second);
elapsed += end-beg;
if (elapsed>WARMUP_TIME_MILLISECONDS && !timed) {
elapsed =0;
count=0;
timed = true;
}
++count;
} while(elapsed<TEST_TIME_MILLISECONDS);
}
std::cout << std::setw(9) << count << ' ';
timed = false;
elapsed =0;
count = 0;
{
do {
container a(data.begin(), data.end());
beg = clock();
for(unsigned int i=0; i<shuffled.size(); ++i)
a.erase(shuffled[i].first);
end = clock();
assert(a.size()==0);
elapsed += end-beg;
if (elapsed>WARMUP_TIME_MILLISECONDS && !timed) {
elapsed =0;
count=0;
timed = true;
}
++count;
} while(elapsed<TEST_TIME_MILLISECONDS);
}
std::cout << std::setw(9) << count << '\n';
}
template<class payload>
void test(const std::vector<std::pair<payload, payload>> &data, const char* payload_name) {
std::vector<std::pair<payload, payload>> use(data.begin(), data.begin()+8);
for( ; ; ) {
test_internal<payload, std::map <payload,payload>>(use, "std::map", payload_name);
test_internal<payload, associative<payload,payload,std::less<payload>,std::vector<std::pair<payload,payload>>>>(use, "astv/vec", payload_name);
if (use.size()<1024)
use.insert(use.end(), data.begin()+use.size()*1ull, data.begin()+use.size()*2ull);
else if (use.size() != data.size())
use.insert(use.end(), data.begin()+use.size(), data.end());
else break;
}
}
//template associative<payload< 4>,payload< 4>,std::less<payload< 4>>,std::list<std::pair<payload< 4>,payload< 4>>>>;
//template associative<payload< 4>,payload< 4>,std::less<payload< 4>>,std::vector<std::pair<payload< 4>,payload< 4>>>>;
//template associative<payload< 4>,payload< 4>,std::less<payload< 4>>,std::deque<std::pair<payload< 4>,payload< 4>>>>;
int main() {
std::vector<std::pair<std::string,std::string>> dynamic_data;
dynamic_data.reserve(2000);
{
std::ifstream i("F:/Code/Contests/About53/2000words.txt");
std::string line;
for(;dynamic_data.size()<2000 && i>>line;)
dynamic_data.push_back(std::make_pair(line, line));
std::random_shuffle(dynamic_data.begin(), dynamic_data.end());
}
std::cout << "bigger counts are best inserts lookups erases\n";
#ifndef _DEBUG
//test(dynamic_data, "std::string ");
#endif
test(std::vector<std::pair<payload< 4>,payload< 4>>>(2000), "payload< 4>");
#ifndef _DEBUG
test(std::vector<std::pair<payload< 8>,payload< 8>>>(2000), "payload< 8>");
test(std::vector<std::pair<payload< 16>,payload< 16>>>(2000), "payload< 16>");
test(std::vector<std::pair<payload< 32>,payload< 32>>>(2000), "payload< 32>");
test(std::vector<std::pair<payload< 64>,payload< 64>>>(2000), "payload< 64>");
test(std::vector<std::pair<payload<128>,payload<128>>>(2000), "payload<128>");
#endif
return 0;
}