//! \addtogroup Containers
//! \{
//! \~\file pimap.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 <http://www.gnu.org/licenses/>.
*/

#ifndef PIMAP_H
#define PIMAP_H

#include "pideque.h"
#include "pipair.h"
#include "pivector.h"


template<typename Key, typename T>
class PIMapIteratorConst;
template<typename Key, typename T>
class PIMapIteratorConstReverse;
template<typename Key, typename T>
class PIMapIterator;
template<typename Key, typename T>
class PIMapIteratorReverse;


//! \addtogroup Containers
//! \{
//! \class PIMap
//! \~\brief
//! \~english Map of unique keys and associated values.
//! \~russian Словарь с уникальными ключами и связанными значениями.
//! \~\}
//! \~\details
//! \~english
//! Stores key/value pairs and keeps keys unique.
//! \a value() returns the value for a key or the provided default,
//! while \a operator[] creates a default value for a missing key.
//! Use \a keys() and \a values() to retrieve map contents,
//! and \a makeIterator() or \a makeReverseIterator() to traverse entries.
//! \~russian
//! Хранит пары ключ/значение и поддерживает уникальность ключей.
//! \a value() возвращает значение по ключу или значение по умолчанию,
//! а \a operator[] создает значение по умолчанию для отсутствующего ключа.
//! Для получения содержимого используйте \a keys() и \a values(),
//! а для обхода элементов - \a makeIterator() или \a makeReverseIterator().
template<typename Key, typename T>
class PIMap {
	template<typename Key1, typename T1>
	friend class PIMapIteratorConst;
	template<typename Key1, typename T1>
	friend class PIMapIteratorConstReverse;
	template<typename Key1, typename T1>
	friend class PIMapIterator;
	template<typename Key1, typename T1>
	friend class PIMapIteratorReverse;
	template<typename P, typename Key1, typename T1>
	friend PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIMap<Key1, T1> & v);
	template<typename P, typename Key1, typename T1>
	friend PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIMap<Key1, T1> & v);

public:
	typedef T mapped_type;
	typedef Key key_type;
	typedef PIPair<Key, T> value_type;

	//! \~english Constructs an empty map.
	//! \~russian Создает пустой словарь.
	inline PIMap() {}

	//! \~english Copy constructor.
	//! \~russian Копирующий конструктор.
	inline PIMap(const PIMap<Key, T> & other): pim_content(other.pim_content), pim_index(other.pim_index) {}

	//! \~english Move constructor.
	//! \~russian Перемещающий конструктор.
	inline PIMap(PIMap<Key, T> && 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 <int, PIString> m{{1, "a"}, {2, "b"}};
	//! piCout << m; // {1: a, 2: b}
	//! \endcode
	inline PIMap(std::initializer_list<std::pair<Key, T>> init_list) {
		for (auto i: init_list) {
			insert(std::get<0>(i), std::get<1>(i));
		}
	}

	//! \~english Assign operator.
	//! \~russian Оператор присваивания.
	inline PIMap<Key, T> & operator=(const PIMap<Key, T> & other) {
		if (this == &other) return *this;
		clear();
		pim_content = other.pim_content;
		pim_index   = other.pim_index;
		return *this;
	}

	//! \~english Assign move operator.
	//! \~russian Оператор перемещающего присваивания.
	inline PIMap<Key, T> & operator=(PIMap<Key, T> && other) {
		swap(other);
		return *this;
	}

	class iterator {
		friend class PIMap<Key, T>;

	private:
		iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;

	public:
		iterator(): parent(nullptr), pos(0) {}
		const Key & key() const { return const_cast<PIMap<Key, T> *>(parent)->_key(pos); }
		T & value() { return const_cast<PIMap<Key, T> *>(parent)->_value(pos); }
		inline PIPair<Key, T> operator*() const {
			return PIPair<Key, T>(const_cast<PIMap<Key, T> *>(parent)->_key(pos), const_cast<PIMap<Key, T> *>(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<Key, T>;

	private:
		reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * parent;
		ssize_t pos;

	public:
		reverse_iterator(): parent(nullptr), pos(0) {}
		const Key & key() const { return const_cast<PIMap<Key, T> *>(parent)->_key(pos); }
		T & value() const { return const_cast<PIMap<Key, T> *>(parent)->_value(pos); }
		inline PIPair<Key, T> operator*() const {
			return PIPair<Key, T>(const_cast<PIMap<Key, T> *>(parent)->_key(pos), const_cast<PIMap<Key, T> *>(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<Key, T>;

	private:
		const_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * 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<PIMap<Key, T> *>(parent)->_key(pos); }
		const T & value() const { return const_cast<PIMap<Key, T> *>(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<Key, T>;

	private:
		const_reverse_iterator(const PIMap<Key, T> * v, ssize_t p): parent(v), pos(p) {}
		const PIMap<Key, T> * 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); }
	};


	//! \~english Iterator to the first element.
	//! \~russian Итератор на первый элемент.
	inline iterator begin() { return iterator(this, 0); }

	//! \~english Iterator to the element following the last element.
	//! \~russian Итератор на элемент, следующий за последним элементом.
	inline iterator end() { return iterator(this, size()); }

	inline const_iterator begin() const { return const_iterator(this, 0); }
	inline const_iterator end() const { return const_iterator(this, size()); }

	//! \~english Returns a reverse iterator to the last map entry.
	//! \~russian Обратный итератор на первый элемент.
	inline reverse_iterator rbegin() { return reverse_iterator(this, size() - 1); }

	//! \~english Returns a reverse iterator to the position before the first map entry.
	//! \~russian Обратный итератор на элемент,
	//! следующий за последним элементом.
	inline reverse_iterator rend() { return reverse_iterator(this, -1); }

	inline const_reverse_iterator rbegin() const { return const_reverse_iterator(this, size() - 1); }
	inline const_reverse_iterator rend() const { return const_reverse_iterator(this, -1); }

	//! \relatesalso PIMapIteratorConst
	inline PIMapIteratorConst<Key, T> makeIterator() const { return PIMapIteratorConst<Key, T>(*this); }

	//! \relatesalso PIMapIterator
	inline PIMapIterator<Key, T> makeIterator() { return PIMapIterator<Key, T>(*this); }

	//! \relatesalso PIMapIteratorConstReverse
	inline PIMapIteratorConstReverse<Key, T> makeReverseIterator() const { return PIMapIteratorConstReverse<Key, T>(*this); }

	//! \relatesalso PIMapIteratorReverse
	inline PIMapIteratorReverse<Key, T> makeReverseIterator() { return PIMapIteratorReverse<Key, T>(*this); }

	//! \~english Number of entries in the map.
	//! \~russian Количество элементов в словаре.
	//! \~\sa \a size_s(), \a capacity(), \a isEmpty(), \a isNotEmpty(), \a resize(), \a reserve()
	inline size_t size() const { return pim_content.size(); }

	//! \~english Number of entries in the map as a signed value.
	//! \~russian Количество элементов в словаре в виде знакового числа.
	//! \~\sa \a size(), \a capacity(), \a isEmpty(), \a isNotEmpty(), \a resize(), \a reserve()
	inline int size_s() const { return pim_content.size_s(); }

	//! \~english Same as \a size().
	//! \~russian Синоним \a size().
	//! \~\sa \a size(), \a size_s(), \a capacity(), \a isEmpty(), \a isNotEmpty(), \a resize(), \a reserve()
	inline size_t length() const { return pim_content.size(); }

	//! \~english Checks whether the map is empty.
	//! \~russian Проверяет, пуст ли словарь.
	//! \~\sa \a size(), \a size_s(), \a isEmpty(), \a isNotEmpty(), \a resize(), \a reserve()
	inline bool isEmpty() const { return (pim_content.size() == 0); }

	//! \~english Checks whether the map contains entries.
	//! \~russian Проверяет, содержит ли словарь элементы.
	//! \~\sa \a size(), \a size_s(), \a isEmpty(), \a isNotEmpty(), \a resize(), \a reserve()
	inline bool isNotEmpty() const { return (pim_content.size() > 0); }


	//! \~english Full access to element key `key`.
	//! \~russian Полный доступ к элементу по ключу `key`.
	//! \~\details
	//! \~english If the map contains no item with key `key`,
	//! the function inserts a default-constructed value into the map with key `key`,
	//! and returns a reference to it.
	//! \~russian Если элемента с таким ключом `key` не существует,
	//! то он будет создан конструктором по умолчанию и добавлен в словарь
	//! по ключу `key`, а затем возвращена ссылка на этот новый элемент.
	//! \~\code
	//! PIMap <PIString, int> m;
	//! m["огурец"] = 500;
	//! piCout << m; // {огурец: 500}
	//! m["лук"] = 25;
	//! piCout << m; // {огурец: 500, лук: 25}
	//! m["огурец"] = 350;
	//! piCout << m; // {огурец: 350, лук: 25}
	//! \endcode
	//! \~\sa \a insert(), \a value(), \a key()
	inline T & operator[](const Key & key) {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (f) return _value(i);
		pim_content.push_back(T());
		pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		return pim_content.back();
	}

	//! \~english Read only access to element by `key`.
	//! \~russian Доступ исключительно на чтение к элементу по ключу `key`.
	//! \~\note
	//! \~english Element with key `key` must exists,
	//! otherwise it will lead to undefined program behavior and memory errors.
	//! \~russian Элемент по ключу `key` должен существовать,
	//! иначе это приведёт к неопределённому поведению программы и ошибкам памяти.
	//! \~\sa \a operator[](), \a value(), \a key()
	inline const T & at(const Key & key) const {
		bool f(false);
		const ssize_t i = _find(key, f);
		return _value(i);
	}

	//! \~english Removes entry with key `key` and returns its value.
	//! \~russian Удаляет элемент с ключом `key` и возвращает его значение.
	inline T take(const Key & key, const T & default_ = T()) {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (!f) return default_;
		const T ret = _value(i);
		_remove(i);
		return ret;
	}

	//! \~english Inserts all entries from `other`, overwriting existing keys.
	//! \~russian Добавляет все элементы из `other`, перезаписывая существующие ключи.
	inline PIMap<Key, T> & operator<<(const PIMap<Key, T> & other) {
#ifndef NDEBUG
		if (&other == this) {
			fprintf(stderr, "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[other.pim_index[0].index]);
			insert(other.pim_index[1].key, other.pim_content[other.pim_index[1].index]);
			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;
	}

	//! \~english Compares this map with `m`.
	//! \~russian Сравнивает этот словарь с `m`.
	inline bool operator==(const PIMap<Key, T> & m) const { return (pim_content == m.pim_content && pim_index == m.pim_index); }

	//! \~english Compares this map with `m`.
	//! \~russian Сравнивает этот словарь с `m`.
	inline bool operator!=(const PIMap<Key, T> & m) const { return (pim_content != m.pim_content || pim_index != m.pim_index); }

	//! \~english Checks whether the map contains key `key`.
	//! \~russian Проверяет, содержит ли словарь ключ `key`.
	inline bool contains(const Key & key) const {
		bool f(false);
		_find(key, f);
		return f;
	}

	//! \~english Checks whether the map contains value `value`.
	//! \~russian Проверяет, содержит ли словарь значение `value`.
	inline bool containsValue(const T & value) const { return pim_content.contains(value); }

	//! \~english Attempts to allocate memory for at least `new_size` elements.
	//! \~russian Резервируется память под как минимум `new_size` элементов.
	inline PIMap<Key, T> & reserve(size_t new_size) {
		pim_content.reserve(new_size);
		pim_index.reserve(new_size);
		return *this;
	}

	//! \~english Removes entry with key `key`.
	//! \~russian Удаляет элемент с ключом `key`.
	inline PIMap<Key, T> & remove(const Key & key) {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (f) _remove(i);
		return *this;
	}


	//! \~english Removes all entries
	//! passes the test implemented by the provided function `test`.
	//! \~russian Удаляет все элементы, удовлетворяющие условию,
	//! заданному в передаваемой функции `test`.
	inline PIMap<Key, T> & removeWhere(std::function<bool(const Key & key, const T & value)> test) {
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			if (test(mi.key, pim_content[mi.index])) {
				_remove(i);
				--i;
			}
		}
		return *this;
	}

	//! \~english Same as \a remove().
	//! \~russian Синоним функции \a remove().
	inline PIMap<Key, T> & erase(const Key & key) { return remove(key); }


	//! \~english Clears the map.
	//! \~russian Очищает словарь.
	//! \~\details
	//! \~\note
	//! \~english Reserved memory will not be released.
	//! \~russian Зарезервированная память не освободится.
	//! \~\sa \a resize()
	inline PIMap<Key, T> & clear() {
		pim_content.clear();
		pim_index.clear();
		return *this;
	}

	//! \~english Swaps this map with `other`.
	//! \~russian Меняет местами этот словарь и `other`.
	//! \~\details
	//! \~english This operation is very fast and never fails.
	//! \~russian Эта операция выполняется мгновенно без копирования памяти и никогда не дает сбоев.
	inline void swap(PIMap<Key, T> & other) {
		pim_content.swap(other.pim_content);
		pim_index.swap(other.pim_index);
	}

	//! \~english Inserts value `value` for key `key`.
	//! \~russian Вставляет значение `value` по ключу `key`.
	//! \~\details
	//! \~english If an element with the key `key` already exists, it will be overwritten with the value `value`.
	//! \~russian Если элемент с ключом `key` уже существует, то он будет перезаписан на значение `value`.
	inline PIMap<Key, T> & insert(const Key & key, const T & value) {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (f) {
			_value(i) = value;
		} else {
			pim_content.push_back(value);
			pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		}
		return *this;
	}

	inline PIMap<Key, T> & insert(const Key & key, T && value) {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (f) {
			_value(i) = std::move(value);
		} else {
			pim_content.push_back(std::move(value));
			pim_index.insert(i, MapIndex(key, pim_content.size() - 1));
		}
		return *this;
	}

	//! \~english Inserts entry `pair`.
	//! \~russian Вставляет элемент `pair`.
	//! \~\details
	//! \~english The first element of the pair is the key, and the second is the value.
	//! \~russian Первый элемент пары является ключом, а второй значением.
	inline PIMap<Key, T> & insert(const PIPair<Key, T> & pair) {
		bool f(false);
		const ssize_t i = _find(pair.first, f);
		if (f) {
			_value(i) = pair.second;
		} else {
			pim_content.push_back(pair.second);
			pim_index.insert(i, MapIndex(pair.first, pim_content.size() - 1));
		}
		return *this;
	}

	inline PIMap<Key, T> & insert(PIPair<Key, T> && pair) {
		bool f(false);
		Key k(std::move(pair.first));
		const ssize_t i = _find(k, f);
		if (f) {
			_value(i) = 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;
	}

	//! \~english Returns the value of the element by the key `key`
	//! or `default_` if there is no such element.
	//! \~russian Возвращает значение элемента по ключу `key`
	//!  или `default_` если такого элемента нет.
	inline T value(const Key & key, const T & default_ = T()) const {
		bool f(false);
		const ssize_t i = _find(key, f);
		if (!f) return default_;
		return _value(i);
	}

	//! \~english Returns values of all map entries.
	//! \~russian Возвращает значения всех элементов словаря.
	inline PIVector<T> values() const { return pim_content; }

	//! \~english Returns the key of the first element
	//! whose value matches `value` or `default_` if there is no such element.
	//! \~russian Возвращает ключ первого элемента, значение которого
	//! совпадает с `value` или `default_` если такого элемента нет.
	inline Key key(const T & value, const Key & default_ = Key()) const {
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			if (pim_content[mi.index] == value) {
				return mi.key;
			}
		}
		return default_;
	}

	//! \~english Returns keys of all map entries.
	//! \~russian Возвращает ключи всех элементов словаря.
	inline PIVector<Key> keys() const {
		PIVector<Key> ret;
		ret.reserve(pim_index.size());
		for (int i = 0; i < pim_index.size_s(); ++i) {
			ret << pim_index[i].key;
		}
		return ret;
	}

	//! \~english Calls `f` for every map entry.
	//! \~russian Вызывает `f` для каждого элемента словаря.
	inline void forEach(std::function<void(const Key & key, const T & value)> f) const {
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			f(mi.key, pim_content[mi.index]);
		}
	}

	//! \~english Creates a new \a PIMap from results of applying `f` to each map entry.
	//! \~russian Создает новый \a PIMap из результатов применения `f` к каждому элементу словаря.
	template<typename Key2, typename T2>
	inline PIMap<Key2, T2> map(std::function<PIPair<Key2, T2>(const Key & key, const T & value)> f) const {
		PIMap<Key2, T2> ret;
		ret.reserve(size());
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			ret.insert(f(mi.key, pim_content[mi.index]));
		}
		return ret;
	}

	//! \~english Creates a new \a PIVector from results of applying `f` to each map entry.
	//! \~russian Создает новый \a PIVector из результатов применения `f` к каждому элементу словаря.
	template<typename ST>
	inline PIVector<ST> map(std::function<ST(const Key & key, const T & value)> f) const {
		PIVector<ST> ret;
		ret.reserve(size());
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			ret << f(mi.key, pim_content[mi.index]);
		}
		return ret;
	}

	//! \~english Returns a new map with all entries
	//! that pass the test implemented by the provided function `bool test(const Key & key, const T & value)`.
	//! \~russian Возвращает новый словарь со всеми элементами,
	//! прошедшими проверку, задаваемую в передаваемой функции `bool test(const Key & key, const T & value)`.
	inline PIMap<Key, T> filter(std::function<bool(const Key & key, const T & value)> test) const {
		PIMap<Key, T> ret;
		for (int i = 0; i < pim_index.size_s(); ++i) {
			const auto & mi(pim_index[i]);
			const auto & v(pim_content[mi.index]);
			if (test(mi.key, v)) {
				ret.insert(mi.key, v);
			}
		}
		return ret;
	}

protected:
	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<typename P, typename Key1, typename T1>
	friend PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIDeque<typename PIMap<Key1, T1>::MapIndex> & v);
	template<typename P, typename Key1, typename T1>
	friend PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIDeque<typename PIMap<Key1, T1>::MapIndex> & v);

	PIVector<T> pim_content;
	PIDeque<MapIndex> pim_index;

private:
	inline ssize_t _binarySearch(ssize_t first, ssize_t last, const Key & key, bool & found) const {
		ssize_t mid = 0;
		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;
	}

	inline 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);
	}

	inline void _remove(ssize_t index) {
		const 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<T>(pim_content[ci], pim_content.back());
		pim_content.resize(pim_index.size());
	}

	inline const value_type _pair(ssize_t index) const {
		if (index < 0 || index >= pim_index.size_s()) return value_type();
		const auto & mi(pim_index[index]);
		return value_type(mi.key, pim_content[mi.index]);
	}

	inline Key & _key(ssize_t index) { return pim_index[index].key; }

	inline const Key & _key(ssize_t index) const { return pim_index[index].key; }

	inline T & _value(ssize_t index) { return pim_content[pim_index[index].index]; }

	inline const T & _value(ssize_t index) const { return pim_content[pim_index[index].index]; }
};


//! \addtogroup Containers
//! \{
//! \class PIMapIteratorConst
//! \~\brief
//! \~english Java-style iterator for \a PIMap.
//! \~russian Итератор Java стиля для \a PIMap.
//! \~\}
//! \details
//! \~english
//! Provides sequential read-only access to keys and values in \a PIMap.
//! Use the constructor directly or call \a PIMap::makeIterator().
//! \~russian
//! Используется для последовательного перебора ключей и значений \a PIMap только для чтения.
//! Можно использовать конструктор или вызвать \a PIMap::makeIterator().
//! \~
//! \code
//! PIMap<int, PIString> 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<typename Key, typename T>
class PIMapIteratorConst {
	typedef PIMap<Key, T> MapType;

public:
	inline PIMapIteratorConst(const PIMap<Key, T> & map): m(map), pos(-1) {}

	//! \~english Returns current key.
	//! \~russian Возвращает ключ текущего элемента.
	//! \~\sa \a value()
	inline const Key & key() const { return m._key(pos); }

	//! \~english Returns current value.
	//! \~russian Возвращает значение текущего элемента.
	//! \~\sa \a key()
	inline const T & value() const { return m._value(pos); }


	//! \~english Returns true if iterator can jump to next entry
	//! \~russian Возвращает true если итератор может перейти к следующему элементу.
	//! \~\sa \a next()
	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 если он существует.
	//! \~\sa \a hasNext(), \a reset()
	inline bool next() {
		++pos;
		return pos < m.size_s();
	}

	//! \~english Reset iterator to initial position.
	//! \~russian Переходит на начало.
	//! \~\sa \a next()
	inline void reset() { pos = -1; }

private:
	const MapType & m;
	ssize_t pos;
};


//! \addtogroup Containers
//! \{
//! \class PIMapIteratorConstReverse
//! \~\brief
//! \~english Java-style reverse iterator for \a PIMap.
//! \~russian Итератор Java стиля для \a PIMap в обратном порядке.
//! \~\}
//! \details
//! \~english
//! Provides sequential reverse read-only access to keys and values in \a PIMap.
//! Use the constructor directly or call \a PIMap::makeReverseIterator().
//! \~russian
//! Используется для последовательного обратного перебора ключей и значений \a PIMap только для чтения.
//! Можно использовать конструктор или вызвать \a PIMap::makeReverseIterator().
//! \~
//! \code
//! PIMap<int, PIString> 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
template<typename Key, typename T>
class PIMapIteratorConstReverse {
	typedef PIMap<Key, T> MapType;

public:
	inline PIMapIteratorConstReverse(const PIMap<Key, T> & map): m(map), pos(m.size_s()) {}

	//! \~english Returns current key.
	//! \~russian Возвращает ключ текущего элемента.
	//! \~\sa \a value()
	inline const Key & key() const { return m._key(pos); }

	//! \~english Returns current value.
	//! \~russian Возвращает значение текущего элемента.
	//! \~\sa \a key()
	inline const T & value() const { return m._value(pos); }

	//! \~english Returns true if iterator can jump to next entry
	//! \~russian Возвращает true если итератор может перейти к следующему элементу.
	//! \~\sa \a next()
	inline bool hasNext() const { return pos > 0; }

	//! \~english Jump to next entry and return true if new position is valid.
	//! \~russian Переходит к следующему элементу и возвращает true если он существует.
	//! \~\sa \a hasNext(), \a reset()
	inline bool next() {
		--pos;
		return pos >= 0;
	}

	//! \~english Reset iterator to initial position.
	//! \~russian Переходит на начало.
	//! \~\sa \a next()
	inline void reset() { pos = m.size_s(); }

private:
	const MapType & m;
	ssize_t pos;
};


//! \addtogroup Containers
//! \{
//! \class PIMapIterator
//! \~\brief
//! \~english Java-style iterator for \a PIMap.
//! \~russian Итератор Java стиля для \a PIMap.
//! \~\}
//! \details
//! \~english
//! Provides sequential access to keys and values in \a PIMap with write access to values.
//! Use the constructor directly or call \a PIMap::makeIterator().
//! \~russian
//! Используется для последовательного перебора ключей и значений \a PIMap с доступом на запись.
//! Можно использовать конструктор или вызвать \a PIMap::makeIterator().
//! \~
//! \code
//! PIMap<int, PIString> m;
//! m[1] = "one";
//! m[2] = "two";
//! m[4] = "four";
//! auto it = m.makeIterator();
//! while (it.next()) {
//!     it.value().append("_!");
//!     piCout << it.key() << it.value();
//! }
//! // 1 one_!
//! // 2 two_!
//! // 4 four_!
//! \endcode
template<typename Key, typename T>
class PIMapIterator {
	typedef PIMap<Key, T> MapType;

public:
	inline PIMapIterator(PIMap<Key, T> & map): m(map), pos(-1) {}

	//! \~english Returns current key.
	//! \~russian Возвращает ключ текущего элемента.
	//! \~\sa \a value()
	inline const Key & key() const { return m._key(pos); }

	//! \~english Returns current value.
	//! \~russian Возвращает значение текущего элемента.
	//! \~\sa \a key()
	inline T & value() { return m._value(pos); }

	//! \~english Returns true if iterator can jump to next entry
	//! \~russian Возвращает true если итератор может перейти к следующему элементу.
	//! \~\sa \a next()
	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 если он существует.
	//! \~\sa \a hasNext(), \a reset()
	inline bool next() {
		++pos;
		return pos < m.size_s();
	}

	//! \~english Reset iterator to initial position.
	//! \~russian Переходит на начало.
	//! \~\sa \a next()
	inline void reset() { pos = -1; }

private:
	MapType & m;
	ssize_t pos;
};


//! \addtogroup Containers
//! \{
//! \class PIMapIteratorReverse
//! \~\brief
//! \~english Java-style reverse iterator for \a PIMap.
//! \~russian Итератор Java стиля для \a PIMap в обратном порядке.
//! \~\}
//! \details
//! \~english
//! Provides sequential reverse access to keys and values in \a PIMap with write access to values.
//! Use the constructor directly or call \a PIMap::makeReverseIterator().
//! \~russian
//! Используется для последовательного обратного перебора ключей и значений \a PIMap с доступом на запись.
//! Можно использовать конструктор или вызвать \a PIMap::makeReverseIterator().
//! \~
//! \code
//! PIMap<int, PIString> m;
//! m[1] = "one";
//! m[2] = "two";
//! m[4] = "four";
//! auto it = m.makeReverseIterator();
//! while (it.next()) {
//!     it.value().append("_!");
//!     piCout << it.key() << it.value();
//! }
//! // 4 four_!
//! // 2 two_!
//! // 1 one_!
//! \endcode
template<typename Key, typename T>
class PIMapIteratorReverse {
	typedef PIMap<Key, T> MapType;

public:
	inline PIMapIteratorReverse(PIMap<Key, T> & map): m(map), pos(m.size_s()) {}

	//! \~english Returns current key.
	//! \~russian Возвращает ключ текущего элемента.
	//! \~\sa \a value()
	inline const Key & key() const { return m._key(pos); }

	//! \~english Returns current value.
	//! \~russian Возвращает значение текущего элемента.
	//! \~\sa \a key()
	inline T & value() { return m._value(pos); }

	//! \~english Returns true if iterator can jump to next entry
	//! \~russian Возвращает true если итератор может перейти к следующему элементу.
	//! \~\sa \a next()
	inline bool hasNext() const { return pos > 0; }

	//! \~english Jump to next entry and return true if new position is valid.
	//! \~russian Переходит к следующему элементу и возвращает true если он существует.
	//! \~\sa \a hasNext(), \a reset()
	inline bool next() {
		--pos;
		return pos >= 0;
	}

	//! \~english Reset iterator to initial position.
	//! \~russian Переходит на начало.
	//! \~\sa \a next()
	inline void reset() { pos = m.size_s(); }

private:
	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<typename Key, typename Type>
inline std::ostream & operator<<(std::ostream & s, const PIMap<Key, Type> & v) {
	s << "{";
	bool first = true;
	for (typename PIMap<Key, Type>::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<typename Key, typename Type>
inline PICout operator<<(PICout s, const PIMap<Key, Type> & v) {
	s.space();
	s.saveAndSetControls(0);
	s << "{";
	bool first = true;
	for (typename PIMap<Key, Type>::const_iterator i = v.begin(); i != v.end(); ++i) {
		if (!first) s << ", ";
		first = false;
		s << i.key() << ": " << i.value();
	}
	s << "}";
	s.restoreControls();
	return s;
}

template<typename Key, typename Type>
inline void piSwap(PIMap<Key, Type> & f, PIMap<Key, Type> & s) {
	f.swap(s);
}


#endif // PIMAP_H
