//! \addtogroup Containers //! \{ //! \file pideque.h //! \brief //! \~english Declares \a PIMap //! \~russian Объявление \a PIMap //! \~\authors //! \~english //! Ivan Pelipenko peri4ko@yandex.ru; //! Andrey Bychkov work.a.b@yandex.ru; //! \~russian //! Иван Пелипенко peri4ko@yandex.ru; //! Андрей Бычков work.a.b@yandex.ru; //! \~\} /* PIP - Platform Independent Primitives Associative array with custom types of key and value Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef PIMAP_H #define PIMAP_H #include "pivector.h" #include "pideque.h" #include "pipair.h" template class PIMapIterator; template class PIMapReverseIterator; //! \addtogroup Containers //! \{ //! \class PIMap //! \brief //! \~english Associative array. //! \~russian Словарь. //! \~\} //! \details //! \~english //! A collection of key/value pairs, from which you retrieve a value using its associated key. //! There is a finite number of keys in the map, and each key has exactly one value associated with it. //! \a value() returns value for key and leave map //! unchaged in any case. \a operator [] create entry in map if //! there is no entry for given key. You can retrieve all //! keys by method \a keys() and all values by methos \a values(). //! To iterate all entries use class PIMapIterator, or methods //! \a makeIterator() and \a makeReverseIterator(). //! A key in the Map may only occur once. //! \~russian //! Словари, в принципе, похожи на обычные, используемые в повседневной жизни. //! Они хранят элементы одного и того же типа, индексируемые ключевыми значениями. //! Достоинство словаря в том, что он позволяет быстро получать значение, //! ассоциированное с заданным ключом. //! Ключи должны быть уникальными. //! Элемент //! В контейнеры этого типа заносятся элементы вместе с ключами, //! по которым их можно найти, которыми могут выступать значения любого типа. //! \a operator [] позволяет получить доступ к элементу по ключу, //! и если такого эелемента небыло, то он будет создан. template class PIMap { template friend class PIMapIterator; template friend class PIMapReverseIterator; template friend PIBinaryStream

& operator <<(PIBinaryStream

& s, const PIMap & v); template friend PIBinaryStream

& operator >>(PIBinaryStream

& s, PIMap & v); public: typedef T mapped_type; typedef Key key_type; typedef PIPair value_type; //! \~english Constructs an empty map. //! \~russian Создает пустой словарь. PIMap() {} //! \~english Copy constructor. //! \~russian Копирующий конструктор. PIMap(const PIMap & other) {*this = other;} //! \~english Move constructor. //! \~russian Перемещающий конструктор. PIMap(PIMap && other) : pim_content(std::move(other.pim_content)), pim_index(std::move(other.pim_index)) {} //! \~english Contructs map from //! [C++11 initializer list](https://en.cppreference.com/w/cpp/utility/initializer_list). //! \~russian Создает словарь из //! [списка инициализации C++11](https://ru.cppreference.com/w/cpp/utility/initializer_list). //! \~\details //! \~\code //! PIMap m{{1, "a"}, {2, "b"}}; //! piCout << m; // {1, 2, 3} //! \endcode PIMap(std::initializer_list> init_list) { for (auto i: init_list) { insert(std::get<0>(i), std::get<1>(i)); } } //! \~english Assign operator. //! \~russian Оператор присваивания. PIMap & operator =(const PIMap & other) { if (this == &other) return *this; clear(); pim_content = other.pim_content; pim_index = other.pim_index; return *this; } //! \~english Assign move operator. //! \~russian Оператор перемещающего присваивания. PIMap & operator =(PIMap && other) { swap(other); return *this; } class iterator { friend class PIMap; private: iterator(const PIMap * v, ssize_t p): parent(v), pos(p) {} const PIMap * parent; ssize_t pos; public: iterator(): parent(nullptr), pos(0) {} const Key & key() const {return const_cast * >(parent)->_key(pos);} T & value() {return const_cast * >(parent)->_value(pos);} inline PIPair operator *() const { return PIPair(const_cast * >(parent)->_key(pos), const_cast * >(parent)->_value(pos)); } void operator ++() {++pos;} void operator ++(int) {++pos;} void operator --() {--pos;} void operator --(int) {--pos;} bool operator ==(const iterator & it) const {return (pos == it.pos);} bool operator !=(const iterator & it) const {return (pos != it.pos);} }; class reverse_iterator { friend class PIMap; private: reverse_iterator(const PIMap * v, ssize_t p): parent(v), pos(p) {} const PIMap * parent; ssize_t pos; public: reverse_iterator(): parent(nullptr), pos(0) {} const Key & key() const {return const_cast * >(parent)->_key(pos);} T & value() const {return const_cast * >(parent)->_value(pos);} inline PIPair operator *() const { return PIPair(const_cast * >(parent)->_key(pos), const_cast * >(parent)->_value(pos)); } void operator ++() {--pos;} void operator ++(int) {--pos;} void operator --() {++pos;} void operator --(int) {++pos;} bool operator ==(const reverse_iterator & it) const {return (pos == it.pos);} bool operator !=(const reverse_iterator & it) const {return (pos != it.pos);} }; class const_iterator { friend class PIMap; private: const_iterator(const PIMap * v, ssize_t p): parent(v), pos(p) {} const PIMap * parent; ssize_t pos; public: const_iterator(): parent(nullptr), pos(0) {} const value_type operator *() const {return parent->_pair(pos);} const Key & key() const {return const_cast * >(parent)->_key(pos);} const T & value() const {return const_cast * >(parent)->_value(pos);} void operator ++() {++pos;} void operator ++(int) {++pos;} void operator --() {--pos;} void operator --(int) {--pos;} bool operator ==(const const_iterator & it) const {return (pos == it.pos);} bool operator !=(const const_iterator & it) const {return (pos != it.pos);} }; class const_reverse_iterator { friend class PIMap; private: const_reverse_iterator(const PIMap * v, ssize_t p): parent(v), pos(p) {} const PIMap * parent; ssize_t pos; public: const_reverse_iterator(): parent(nullptr), pos(0) {} const value_type operator *() const {return parent->_pair(pos);} void operator ++() {--pos;} void operator ++(int) {--pos;} void operator --() {++pos;} void operator --(int) {++pos;} bool operator ==(const const_reverse_iterator & it) const {return (pos == it.pos);} bool operator !=(const const_reverse_iterator & it) const {return (pos != it.pos);} }; iterator begin() {return iterator(this, 0);} iterator end() {return iterator(this, size());} const_iterator begin() const {return const_iterator(this, 0);} const_iterator end() const {return const_iterator(this, size());} const_iterator constBegin() const {return const_iterator(this, 0);} const_iterator constEnd() const {return const_iterator(this, size());} reverse_iterator rbegin() {return reverse_iterator(this, size() - 1);} reverse_iterator rend() {return reverse_iterator(this, -1);} const_reverse_iterator rbegin() const {return const_reverse_iterator(this, size() - 1);} const_reverse_iterator rend() const {return const_reverse_iterator(this, -1);} const_reverse_iterator constRbegin() const {return const_reverse_iterator(this, size() - 1);} const_reverse_iterator constRend() const {return const_reverse_iterator(this, -1);} //! \relatesalso PIMapIterator PIMapIterator makeIterator() const {return PIMapIterator(*this);} //! \relatesalso PIMapReverseIterator PIMapReverseIterator makeReverseIterator() const {return PIMapReverseIterator(*this);} size_t size() const {return pim_content.size();} int size_s() const {return pim_content.size_s();} size_t length() const {return pim_content.size();} bool isEmpty() const {return (pim_content.size() == 0);} bool isNotEmpty() const {return (pim_content.size() > 0);} T & operator [](const Key & key) { bool f(false); ssize_t i = _find(key, f); if (f) return pim_content[pim_index[i].index]; pim_content.push_back(T()); pim_index.insert(i, MapIndex(key, pim_content.size() - 1)); return pim_content.back(); } T at(const Key & key) const {return value(key);} T take(const Key & key) const { bool f(false); ssize_t i = _find(key, f); if (!f) return T(); T ret(pim_content[pim_index[i].index]); _remove(i); return ret; } PIMap & operator <<(const PIMap & other) { #ifndef NDEBUG if (&other == this) { printf("error with PIMap<%s, %s>::<<\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T)); } #endif assert(&other != this); if (other.isEmpty()) return *this; if (other.size() == 1) { insert(other.pim_index[0].key, other.pim_content[0]); return *this; } if (other.size() == 2) { insert(other.pim_index[0].key, other.pim_content[0]); insert(other.pim_index[1].key, other.pim_content[1]); return *this; } for (int i = 0; i < other.pim_index.size_s(); ++i) { insert(other.pim_index[i].key, other.pim_content[other.pim_index[i].index]); } return *this; } bool operator ==(const PIMap & t) const { return (pim_content == t.pim_content && pim_index == t.pim_index); } bool operator !=(const PIMap & t) const { return (pim_content != t.pim_content || pim_index != t.pim_index); } bool contains(const Key & key) const { bool f(false); _find(key, f); return f; } bool containsValue(const T & value) const { return pim_content.contains(value); } PIMap & reserve(size_t new_size) { pim_content.reserve(new_size); pim_index.reserve(new_size); return *this; } PIMap & remove(const Key & key) { bool f(false); ssize_t i = _find(key, f); if (f) _remove(i); return *this; } PIMap & removeWhere(std::function test) { for (int i = 0; i < pim_index.size_s(); ++i) { if (pim_index[i].key, pim_content[pim_index[i].index]) { _remove(i); --i; } } } PIMap & erase(const Key & key) {return remove(key);} PIMap & clear() { pim_content.clear(); pim_index.clear(); return *this; } void swap(PIMap & other) { pim_content.swap(other.pim_content); pim_index.swap(other.pim_index); } PIMap & insert(const Key & key, const T & value) { bool f(false); ssize_t i = _find(key, f); if (f) { pim_content[pim_index[i].index] = value; } else { pim_content.push_back(value); pim_index.insert(i, MapIndex(key, pim_content.size() - 1)); } return *this; } PIMap & insert(const Key & key, T && value) { bool f(false); ssize_t i = _find(key, f); if (f) { pim_content[pim_index[i].index] = std::move(value); } else { pim_content.push_back(std::move(value)); pim_index.insert(i, MapIndex(key, pim_content.size() - 1)); } return *this; } PIMap & insert(const PIPair & pair) { bool f(false); ssize_t i = _find(pair.first, f); if (f) { pim_content[pim_index[i].index] = pair.second; } else { pim_content.push_back(pair.second); pim_index.insert(i, MapIndex(pair.first, pim_content.size() - 1)); } return *this; } PIMap & insert(PIPair && pair) { bool f(false); Key k(std::move(pair.first)); ssize_t i = _find(k, f); if (f) { pim_content[pim_index[i].index] = std::move(pair.second); } else { pim_content.push_back(std::move(pair.second)); pim_index.insert(i, MapIndex(k, pim_content.size() - 1)); } return *this; } T value(const Key & key, const T & default_ = T()) const { bool f(false); ssize_t i = _find(key, f); if (!f) return default_; return pim_content[pim_index[i].index]; } PIVector values() const {return pim_content;} Key key(const T & value_, const Key & default_ = Key()) const { for (int i = 0; i < pim_index.size_s(); ++i) { if (pim_content[pim_index[i].index] == value_) { return pim_index[i].key; } } return default_; } PIVector keys() const { PIVector ret; ret.reserve(pim_index.size()); for (int i = 0; i < pim_index.size_s(); ++i) { ret << pim_index[i].key; } return ret; } void forEach(std::function f) const { for (int i = 0; i < pim_index.size_s(); ++i) { f(pim_index[i].key, pim_content[pim_index[i].index]); } } template inline PIMap map(std::function(const Key & key, const T & value)> f) const { PIMap ret; ret.reserve(size()); for (int i = 0; i < pim_index.size_s(); ++i) { ret.insert(f(pim_index[i].key, pim_content[pim_index[i].index])); } return ret; } template inline PIVector map(std::function f) const { PIVector ret; ret.reserve(size()); for (int i = 0; i < pim_index.size_s(); ++i) { ret << f(pim_index[i].key, pim_content[pim_index[i].index]); } return ret; } private: struct MapIndex { MapIndex(const Key & k = Key(), size_t i = 0): key(k), index(i) {} MapIndex(Key && k, size_t i = 0): key(std::move(k)), index(i) {} Key key; size_t index; bool operator ==(const MapIndex & s) const {return key == s.key;} bool operator !=(const MapIndex & s) const {return key != s.key;} bool operator <(const MapIndex & s) const {return key < s.key;} bool operator >(const MapIndex & s) const {return key > s.key;} }; template friend PIBinaryStream

& operator >>(PIBinaryStream

& s, PIDeque::MapIndex> & v); template friend PIBinaryStream

& operator <<(PIBinaryStream

& s, const PIDeque::MapIndex> & v); ssize_t _binarySearch(ssize_t first, ssize_t last, const Key & key, bool & found) const { ssize_t mid; while (first <= last) { mid = (first + last) / 2; if (key > pim_index[mid].key) first = mid + 1; else if (key < pim_index[mid].key) last = mid - 1; else {found = true; return mid;} } found = false; return first; } ssize_t _find(const Key & k, bool & found) const { if (pim_index.isEmpty()) { found = false; return 0; } return _binarySearch(0, pim_index.size_s() - 1, k, found); } void _remove(ssize_t index) { size_t ci = pim_index[index].index, bi = pim_index.size() - 1; pim_index.remove(index); for (size_t i = 0; i < pim_index.size(); ++i) { if (pim_index[i].index == bi) { pim_index[i].index = ci; break; } } piSwap(pim_content[ci], pim_content.back()); pim_content.resize(pim_index.size()); } const value_type _pair(ssize_t index) const { if (index < 0 || index >= pim_index.size_s()) return value_type(); return value_type(pim_index[index].key, pim_content[pim_index[index].index]); } Key & _key(ssize_t index) {return pim_index[index].key;} const Key & _key(ssize_t index) const {return pim_index[index].key;} T & _value(ssize_t index) {return pim_content[pim_index[index].index];} const T & _value(ssize_t index) const {return pim_content[pim_index[index].index];} PIVector pim_content; PIDeque pim_index; }; //! \addtogroup Containers //! \{ //! \class PIMapIterator //! \brief //! \~english Java-style iterator for \a PIMap. //! \~russian Итератор Java стиля для \a PIMap. //! \~\} //! \details //! \~english //! This class used to easy serial access keys and values in PIMap. //! You can use constructor to create iterator, or use \a PIMap::makeIterator() //! \~russian //! Этот класс используется для удобного перебора ключей и значений всего словаря в обратном порядке. //! Ты можешь использовать конструктор, в который передать словарь, или функцию словаря \a PIMap::makeReverseIterator(). //! \~ //! \code //! PIMap m; //! m[1] = "one"; //! m[2] = "two"; //! m[4] = "four"; //! auto it = m.makeIterator(); //! while (it.next()) { //! piCout << it.key() << it.value(); //! // 1 one //! // 2 two //! // 4 four //! \endcode template class PIMapIterator { typedef PIMap MapType; public: PIMapIterator(const PIMap & map, bool reverse = false): m(map), pos(-1) {} //! \~english Returns current key. //! \~russian Возвращает ключ текущего элемента. //! \~\sa \a value(), \a valueRef() const Key & key() const { return m._key(pos); } //! \~english Returns current value. //! \~russian Возвращает значение текущего элемента. //! \~\sa \a key(), \a valueRef() const T & value() const { return m._value(pos); } //! \~english Returns current value reference. //! \~russian Возвращает изменяемую ссылку на значение текущего элемента. //! \~\sa \a key(), \a value() T & valueRef() { return const_cast(m)._value(pos); } //! \~english Returns true if iterator can jump to next entry //! \~russian Возвращает true если итератор может перейти к следующему элементу. inline bool hasNext() const { return pos < (m.size_s() - 1); } //! \~english Jump to next entry and return true if new position is valid. //! \~russian Переходит к следующему элементу и возвращает true если он существует. inline bool next() { ++pos; return pos < m.size_s(); } //! \~english Reset iterator to initial position. //! \~russian Переходит на начало. inline void reset() { pos = -1; } private: const MapType & m; ssize_t pos; }; //! \addtogroup Containers //! \{ //! \class PIMapReverseIterator //! \brief //! \~english Java-style reverse iterator for \a PIMap. //! \~russian Итератор Java стиля для \a PIMap в обратном порядке. //! \~\} //! \details //! \~english //! This class used to easy serial reverse access keys and values in PIMap. //! You can use constructor to create iterator, or use \a PIMap::makeReverseIterator(). //! \~russian //! Этот класс используется для удобного перебора ключей и значений всего словаря в обратном порядке. //! Ты можешь использовать конструктор, в который передать словарь, или функцию словаря \a PIMap::makeReverseIterator(). //! \~ //! \code //! PIMap m; //! m[1] = "one"; //! m[2] = "two"; //! m[4] = "four"; //! auto it = m.makeReverseIterator(); //! while (it.next()) { //! piCout << it.key() << it.value(); //! } //! // 4 four //! // 2 two //! // 1 one //! \endcode //! \~english Write access: //! \~russian Доступ на запись: //! \~ //! \code //! while (it.next()) { //! it.valueRef().append("_!"); //! piCout << it.key() << it.value(); //! } //! // 4 four_! //! // 2 two_! //! // 1 one_! //! \endcode template class PIMapReverseIterator { typedef PIMap MapType; public: PIMapReverseIterator(const PIMap & map): m(map), pos(m.size_s()) {} //! \~english Returns current key. //! \~russian Возвращает ключ текущего элемента. //! \~\sa \a value(), \a valueRef() const Key & key() const { return m._key(pos); } //! \~english Returns current value. //! \~russian Возвращает значение текущего элемента. //! \~\sa \a key(), \a valueRef() const T & value() const { return m._value(pos); } //! \~english Returns current value reference. //! \~russian Возвращает изменяемую ссылку на значение текущего элемента. //! \~\sa \a key(), \a value() T & valueRef() { return const_cast(m)._value(pos); } //! \~english Returns true if iterator can jump to next entry //! \~russian Возвращает true если итератор может перейти к следующему элементу. inline bool hasNext() const { return pos > 0; } //! \~english Jump to next entry and return true if new position is valid. //! \~russian Переходит к следующему элементу и возвращает true если он существует. inline bool next() { --pos; return pos >= 0; } //! \~english Reset iterator to initial position. //! \~russian Переходит на начало. inline void reset() { pos = m.size_s(); } private: const MapType & m; ssize_t pos; }; #ifdef PIP_STD_IOSTREAM //! \~english Output operator to [std::ostream](https://en.cppreference.com/w/cpp/io/basic_ostream). //! \~russian Оператор вывода в [std::ostream](https://ru.cppreference.com/w/cpp/io/basic_ostream). template inline std::ostream & operator <<(std::ostream & s, const PIMap & v) { s << "{"; bool first = true; for (typename PIMap::const_iterator i = v.begin(); i != v.end(); ++i) { if (!first) s << ", "; first = false; s << i.key() << ": " << i.value(); } s << "}"; return s; } #endif //! \relatesalso PICout //! \~english Output operator to \a PICout //! \~russian Оператор вывода в \a PICout template inline PICout operator <<(PICout s, const PIMap & v) { s.space(); s.setControl(0, true); s << "{"; bool first = true; for (typename PIMap::const_iterator i = v.begin(); i != v.end(); ++i) { if (!first) s << ", "; first = false; s << i.key() << ": " << i.value(); } s << "}"; s.restoreControl(); return s; } template inline void piSwap(PIMap & f, PIMap & s) {f.swap(s);} #endif // PIMAP_H