//////////////////////////////////////////////////////////////////////////////// /** \file \brief \ru Мультимножество, реализующее основной функционал std::multimap. \en Multiset implementing the core functionality of std::multimap. \~ */ //////////////////////////////////////////////////////////////////////////////// #ifndef __TEMPL_MULTIMAP_H #define __TEMPL_MULTIMAP_H #include //------------------------------------------------------------------------------ /** \brief \ru Ассоциативное множество c дубликатами (мультимножество). \en Associative set with duplicates (multiset). \~ \details \ru Ассоциативное множество c дубликатами (мультимножество). \n Реализует основные функциии std::multimap. Требования к типам данных KeyType и ValType такие же, как в SArray. Мультимножество задает соответствие (ассоциации) объекта-ключа подмножеству объектов-значений. Для некоторого объекта типа KeyType задается соответствие подмножеству объектов ValType. \en Associative set with duplicates (multiset). \n Implements the core functions of is std::multimap. Requirements to data types KeyType and ValType are the same as in SArray. Multiset sets the mapping (associations) between a key-object and a subset of value-objects. For some object of the "KeyType" type there is set a mapping to a subset of objects of the "ValType" type. \~ \ingroup Base_Tools_Containers */ // --- template class MultiMap { public: struct Pair { const KeyType m_key; const ValType m_val; Pair( const KeyType & key, const ValType & val ) : m_key( key ), m_val( val ) {} bool operator < ( const Pair & p ) const { return m_key < p.m_key; } bool operator == ( const Pair & p ) const { return m_key == p.m_key && m_val == p.m_val; } Pair & operator = ( const Pair & ); // \ru Не реализовано. \en Not implemented. }; public: MultiMap(); virtual ~MultiMap() {} class Iterator; public: /// \ru Оператор доступа по ключу. \en Access by key operator. Iterator operator[] ( const KeyType & key ) const; /// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. Iterator First() const; /// \ru Добавить элемент с заданным ключом и значением. \en Add an element with specified key and value. void Associate( const KeyType & key, const ValType & val ); /// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. void Dissociate( const KeyType & key, const ValType & val ); /// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). void Dissociate( Iterator & it1, Iterator & it2 ); /// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. Iterator Dissociate( Iterator & it ); /// \ru Удалить все элементы из контейнера. \en Removes all elements from the container. void Flush() { m_Pairs.Flush(); } /// \ru Существует ли элемент с заданными ключом и значением. \en Is there an element with the specified key and value. bool IsAssociated( const KeyType & key, const ValType & val ) const; /// \ru Найти элемент с заданными ключом и значением. \en Find an element with the specified key and value. Iterator Find( const KeyType & key ) const; // \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. \en An iterator pointing to the first element not less than the given key. Iterator LowerBound( const KeyType & key ) const; // \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. \en An iterator pointing to the first element greater than the given key. Iterator UpperBound( const KeyType & key ) const; // \ru Диапазон, содержащий все элементы с данным ключом в контейнере. \en A range containing all elements with the given key in the container. std::pair EqualRange( const KeyType & key ) const; // \ru Количество элементов в контейнере. \en The number of elements in the container. size_t Count() const { return m_Pairs.Count(); } static size_t UpperBoundEx( const SArray & pairs, const KeyType & key ); static size_t LowerBoundEx( const SArray & pairs, const KeyType & key ); static std::pair EqualRangeEx( const SArray & pairs, const KeyType & key ); private: SArray m_Pairs; private: // \ru Специфицируем нуль для разных типов. \en Specify null for different types. template struct Null { // \ru Нуль тривиальных типов (int, float, double и т.д.). \en Null of trivial types (int, float, double etc.). static inline T val() { return 0; } }; template struct Null { // \ru Нуль указателей. \en Null of pointers. static inline T* val() { return nullptr; } }; // \ru LF_Linux: 25.03.11 g++ выдает ошибку на этот код - не использованы KeyType, ValType в полной специализации шаблона. // Однако непонятно, зачем нужна эта полная специализация - общая частичная специализация для тривиальных типов вполне подойдет. // \en LF_Linux: 25.03.11 g++ returns an error in this code - types KeyType and ValType are not used in the full specialization of template. // But it is not clear what for this specialization is required - the common partial specialization for the trivial types is sufficient. // template<> // \ru struct Null { // Нуль вещественных чисел \en struct Null { // Null of real numbers // static inline double val() { return 0.0; } // }; public: // \ru Итератор по элементам контейнера. \en Iterator for the container elements. class Iterator { private: Pair * m_Ptr; Pair * m_MaxPtr; public: Iterator() : m_Ptr( nullptr ), m_MaxPtr( nullptr ) {} Iterator( const Iterator & iter ) : m_Ptr( iter.m_Ptr ), m_MaxPtr( iter.m_MaxPtr ) {} Iterator( const SArray & m_Pairs, const Pair & pair ) : m_Ptr( nullptr ), m_MaxPtr( nullptr ) { const size_t count = m_Pairs.Count(); if ( count > 0 ) { size_t idx = MultiMap::LowerBoundEx( m_Pairs, pair.m_key ); size_t temp = idx; while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == pair.m_key ) { if ( m_Pairs[temp] == pair ) { idx = temp; break; } temp++; } m_MaxPtr = &m_Pairs[count-1]; if ( idx < count ) { m_Ptr = &m_Pairs[idx]; } } } Iterator( const SArray & m_Pairs, const Iterator & iter1, const Iterator & iter2 ) // range : m_Ptr( nullptr ), m_MaxPtr( nullptr ) { const size_t count = m_Pairs.Count(); if ( count > 0 && iter1.m_Ptr != nullptr ) { size_t idx1 = MultiMap::LowerBoundEx( m_Pairs, iter1.m_Ptr->m_key ); size_t temp = idx1; while ( temp < m_Pairs.Count() && m_Pairs[temp].m_key == iter1.m_Ptr->m_key ) { if ( m_Pairs[temp] == *iter1.m_Ptr ) { idx1 = temp; break; } temp++; } size_t idx2 = SYS_MAX_T; if ( iter2.m_Ptr != nullptr ) { idx2 = MultiMap::UpperBoundEx( m_Pairs, iter2.m_Ptr->m_key ); if ( idx2 < m_Pairs.Count() ) { if ( idx2 > 0) idx2--; else idx2 = SYS_MAX_T; } } if ( idx2 == SYS_MAX_T ) idx2 = count - 1; if ( idx1 < count && idx2 < count ) { m_Ptr = &m_Pairs[idx1]; m_MaxPtr = &m_Pairs[idx2]; } } } public: /// \ru Получить текущий элемент и сдвинуть итератор на следующий. \en Get the current element and move the iterator to the next. Iterator operator ++( int ) { Iterator iter(*this); if ( m_Ptr && m_Ptr < m_MaxPtr ) m_Ptr++; else m_Ptr = 0; return iter; } /// \ru Оператор равенства. \en An equality operator. bool operator == ( const Iterator & itr ) const { if ( Empty() && itr.Empty() ) return true; if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) return false; return m_Ptr == itr.m_Ptr && m_MaxPtr == itr.m_MaxPtr; } /// \ru Оператор "!=". \en An "!=" operator. bool operator != ( const Iterator & itr ) const { if ( Empty() && itr.Empty() ) return false; if ( ( Empty() && !itr.Empty() ) || ( !Empty() && itr.Empty() ) ) return true; return m_Ptr != itr.m_Ptr || m_MaxPtr != itr.m_MaxPtr; } /// \ru Оператор "меньше". \en Operator "less". bool operator < ( const Iterator& itr ) const { return m_Ptr < itr.m_Ptr && m_MaxPtr <= itr.m_MaxPtr; } /// \ru Пустой ли итератор. \en Is the iterator empty. bool Empty() const { return !( m_Ptr && m_Ptr <= m_MaxPtr ); } // \ru Получить текущий ключ элемента. \en Get the current key of the element. KeyType Key() const { return !Empty() ? m_Ptr->m_key : (KeyType)0; } // \ru Получить текущее значение элемента. \en Get the current value of the element. ValType Value() const { return !Empty() ? m_Ptr->m_val : (ValType)0; // SKIP_SA } // \ru Получить пару с текущим ключом и текущим кзначением элемента. \en Get the pair with the current key and the current value of the element. Pair* GetPair() const { return m_Ptr; } }; }; //------------------------------------------------------------------------------ // // --- template size_t MultiMap::LowerBoundEx( const SArray::Pair> & pairs, const KeyType & key ) { if ( pairs.Count() > 11 ) { size_t end = pairs.Count() - 1; size_t last = end; size_t start = 0; size_t firstNotLess = SYS_MAX_T; while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. size_t middle = ( start + end ) / 2; Pair& mdE = pairs[middle]; if ( mdE.m_key < key ) { start = middle; } else if ( key <= mdE.m_key ) { if ( middle < firstNotLess ) firstNotLess = middle; end = middle; } // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. // \en If we are here, then operators of identity check and comparison are not correct. else { PRECONDITION( 0 ); return SYS_MAX_T; } } // \ru Проверяем ключ между start и end. \en Check a key between start and end. if ( start + 1 < pairs.Count() - 1 ) { size_t middle = start + 1; Pair& mdE = pairs[middle]; if ( key <= mdE.m_key && middle < firstNotLess ) firstNotLess = middle; } if ( key <= pairs[0].m_key ) return 0; if ( firstNotLess <= last && key <= pairs[firstNotLess].m_key ) return firstNotLess; if ( key == pairs[last].m_key ) return last; } else { if ( pairs.Count() == 1 ) return key <= pairs[0].m_key ? 0 : SYS_MAX_T; else if ( pairs.Count() == 2 ) { if ( key <= pairs[0].m_key ) return 0; if ( key <= pairs[1].m_key ) return 1; } else { // 2 < count <= 11 for( size_t i = 0; i < pairs.Count(); ++i ) if ( key <= pairs[i].m_key ) return i; } } return SYS_MAX_T; } //------------------------------------------------------------------------------ // \ru Итератор, указывающий на первый элемент со значением ключа, не меньшим, чем заданный. // \en An iterator pointing to the first element not less than the given key. // --- template inline typename MultiMap::Iterator MultiMap::LowerBound( const KeyType & key ) const { size_t idx = LowerBoundEx ( m_Pairs, key ); return idx < m_Pairs.Count() ? Iterator ( m_Pairs, m_Pairs[idx] ) : Iterator(); } template size_t MultiMap::UpperBoundEx( const SArray::Pair> & pairs, const KeyType & key ) { if ( pairs.Count() > 11 ) { size_t end = pairs.Count() - 1; size_t last = end; size_t start = 0; size_t firstGreater = SYS_MAX_T; while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. size_t middle = ( start + end ) / 2; Pair& mdE = pairs[middle]; if ( mdE.m_key <= key ) { start = middle; } else if ( key < mdE.m_key ) { if ( middle < firstGreater ) firstGreater = middle; end = middle; } // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. // \en If we are here, then operators of identity check and comparison are not correct. else { PRECONDITION( 0 ); return SYS_MAX_T; } } if ( key < pairs[0].m_key ) return 0; if ( firstGreater <= end && key < pairs[firstGreater].m_key ) return firstGreater; if ( key < pairs[last].m_key ) return last; } else { if ( pairs.Count() == 1 ) return key < pairs[0].m_key ? 0 :SYS_MAX_T; else if ( pairs.Count() == 2 ) { if ( key < pairs[0].m_key ) return 0; if ( key < pairs[1].m_key ) return 1; } else { // 2 < count <= 11 for( size_t i = 0; i < pairs.Count(); ++i ) if ( key < pairs[i].m_key ) return i; } } return SYS_MAX_T; } //------------------------------------------------------------------------------ // \ru Итератор, указывающий на первый элемент со значением ключа, большим, чем заданный. // \en An iterator pointing to the first element greater than the given key. // --- template inline typename MultiMap::Iterator MultiMap::UpperBound( const KeyType & key ) const { size_t ind = UpperBoundEx ( m_Pairs, key ); return ind != SYS_MAX_T ? Iterator ( m_Pairs, m_Pairs[ind] ) : Iterator(); } //------------------------------------------------------------------------------ // // --- template std::pair MultiMap::EqualRangeEx( const SArray::Pair> & pairs, const KeyType & key ) { if ( pairs.Count() > 11 ) { size_t end = pairs.Count() - 1; size_t lastRight = end; size_t start = 0; size_t firstNotLess = SYS_MAX_T; size_t firstGreater = SYS_MAX_T; // \ru Проверяем последний элемент. \en Check the last element. if ( key > pairs[pairs.Count() - 1].m_key ) return std::pair (SYS_MAX_T, SYS_MAX_T); // \ru Проверяем первый элемент. \en Check the first element. if ( key < pairs[0].m_key ) return std::pair (0, 0); if ( key != pairs[0].m_key ) { while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. size_t middle = ( start + end ) / 2; Pair& mdE = pairs[middle]; if ( mdE.m_key < key ) { start = middle; } else if ( key <= mdE.m_key ) { if ( key < mdE.m_key && lastRight > middle ) lastRight = middle; if ( middle < firstNotLess ) firstNotLess = middle; end = middle; } // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. // \en If we are here, then operators of identity check and comparison are not correct. else { PRECONDITION( 0 ); return std::pair (SYS_MAX_T, SYS_MAX_T); } } } else firstNotLess = 0; // key == pairs[0].m_key // \ru Проверяем ключ между start и end. \en Check a key between start and end. if ( start + 1 == end ) { size_t middle = start + 1; Pair& mdE = pairs[middle]; if ( key <= mdE.m_key && middle < firstNotLess ) firstNotLess = middle; } // \ru Проверяем последний элемент. \en Check the last element. if ( firstNotLess == SYS_MAX_T && key == pairs[pairs.Count() - 1].m_key ) return std::pair (pairs.Count() - 1, SYS_MAX_T); if ( firstNotLess == SYS_MAX_T ) return std::pair (SYS_MAX_T, SYS_MAX_T); // \ru Теперь начинаем правый поиск. \en Now start right search. end = lastRight; start = firstNotLess; if ( start == end && pairs[firstNotLess].m_key != key ) return std::pair ( firstNotLess, firstNotLess ); while ( start + 1 < end ) { // \ru Ищем, пока не нашли. \en Seek until find. size_t middle = ( start + end ) / 2; Pair& mdE = pairs[middle]; if ( mdE.m_key <= key ) { start = middle; } else if ( key < mdE.m_key ) { if ( middle < firstGreater ) firstGreater = middle; end = middle; } // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. // \en If we are here, then operators of identity check and comparison are not correct. else { PRECONDITION( 0 ); return std::pair ( SYS_MAX_T, SYS_MAX_T ); } } if ( firstGreater == SYS_MAX_T ) { if ( pairs[firstNotLess].m_key != key ) firstGreater = firstNotLess; else if ( lastRight > 0 && pairs[lastRight - 1].m_key == key && pairs[lastRight].m_key != key ) firstGreater = lastRight; else if ( firstNotLess + 1 < pairs.Count() && pairs[firstNotLess + 1].m_key != key ) firstGreater = firstNotLess + 1; } return std::pair ( firstNotLess, firstGreater ); } else { if ( pairs.Count() == 0 ) return std::pair ( SYS_MAX_T, SYS_MAX_T ); if ( pairs.Count() == 1 ) return key == pairs[0].m_key ? std::pair ( 0, SYS_MAX_T ) : ( key < pairs[0].m_key ? std::pair ( 0, 0 ) : std::pair ( SYS_MAX_T, SYS_MAX_T ) ); if ( pairs.Count() == 2 ) { size_t first = SYS_MAX_T; if ( key < pairs[0].m_key ) return std::pair(0, 0); if ( key > pairs[1].m_key ) return std::pair(SYS_MAX_T, SYS_MAX_T); if ( key == pairs[0].m_key ) first = 0; else if ( key == pairs[1].m_key ) first = 1; else // ( key > pairs[0].m_key && key < pairs[1].m_key ) return std::pair ( 1, 1 ); if ( key == pairs[1].m_key ) return std::pair ( first, SYS_MAX_T ); return std::pair (first, 1); } else { // 2 < count <= 11 size_t first = SYS_MAX_T, second = SYS_MAX_T; if ( key < pairs[0].m_key ) return std::pair ( 0, 0 ); if ( key > pairs[pairs.Count() - 1].m_key ) return std::pair ( first, second ); for( size_t i = 0; i < pairs.Count(); ++i ) { if ( key == pairs[i].m_key ) { if ( first == SYS_MAX_T ) first = i; } else { if ( key < pairs[i].m_key ) { second = i; break; } } } if ( first == SYS_MAX_T ) first = second; return std::pair ( first, second ); } } } //------------------------------------------------------------------------------ // \ru Диапазон, содержащий все элементы с данным ключом в контейнере. // \en A range containing all elements with the given key in the container. // --- template inline std::pair::Iterator,typename MultiMap::Iterator> MultiMap::EqualRange ( const KeyType & key ) const { std::pair idx = EqualRangeEx ( m_Pairs, key ); Iterator first, second; if ( idx.first < m_Pairs.Count() ) first = Iterator ( m_Pairs, m_Pairs[idx.first]); if ( idx.second < m_Pairs.Count() ) second = Iterator ( m_Pairs, m_Pairs[idx.second]); return std::pair ( first, second ); } //------------------------------------------------------------------------------ /// \ru Найти элемент с заданными ключом и значением. // \en Find an element with the specified key and value. // --- template inline typename MultiMap::Iterator MultiMap::Find( const KeyType & key ) const { if ( m_Pairs.Count() > 11 ) { size_t mx = m_Pairs.Count() - 1; size_t mxc = mx; size_t mn = 0; while ( mn + 1 < mx ) { // \ru Ищем, пока не нашли. \en Seek until find. size_t md = ( mn + mx ) / 2; Pair& mdE = m_Pairs[md]; if ( mdE.m_key < key ) { mn = md; } else if ( key < mdE.m_key ) { mx = md; } else if ( mdE.m_key == key ) return Iterator ( m_Pairs, mdE ); // \ru Если попадаем сюда, значит некорректно написаны операторы "тождественно" и сравнения. // \en If we are here, then operators of identity check and comparison are not correct. else { PRECONDITION( 0 ); return Iterator(); } } if ( key == m_Pairs[0].m_key ) return Iterator ( m_Pairs, m_Pairs[0] ); if ( key == m_Pairs[mxc].m_key ) return Iterator ( m_Pairs, m_Pairs[mxc] ); } else { if ( m_Pairs.Count() == 1 ) return key == m_Pairs[0].m_key ? Iterator ( m_Pairs, m_Pairs[0] ) : Iterator(); else if ( m_Pairs.Count() == 2 ) { if ( key == m_Pairs[0].m_key ) return Iterator ( m_Pairs, m_Pairs[0] ); if ( key == m_Pairs[1].m_key ) return Iterator ( m_Pairs, m_Pairs[1] ); return Iterator(); } else { // 2 < count <= 11 for( size_t i = 0; i < m_Pairs.Count(); ++i ) if ( key == m_Pairs[i].m_key ) return Iterator ( m_Pairs, m_Pairs[i] ); } } return Iterator(); } //------------------------------------------------------------------------------- // \ru Конструктор. \en Contructor. // --- template MultiMap::MultiMap() : m_Pairs( 0, 1 ) {} //------------------------------------------------------------------------------- /// \ru Добавить элемент с заданным ключом и значением. // \en Add an element with specified key and value. // --- template inline void MultiMap::Associate( const KeyType & key, const ValType & val ) { Pair pair( key, val ); std::pair idx = EqualRangeEx ( m_Pairs, key ); if ( idx.second < m_Pairs.Count() ) m_Pairs.InsertInd ( idx.second, pair ); else m_Pairs.Add ( pair ); } //------------------------------------------------------------------------------- // \ru Существует ли элемент с заданными ключом и значением. // \en Is there an element with the specified key and value. // --- template inline bool MultiMap::IsAssociated( const KeyType & key, const ValType & val ) const { return m_Pairs.FindIt( Pair(key, val) ) < m_Pairs.Count(); } //------------------------------------------------------------------------------- /// \ru Удалить заданное значение с заданным ключом. \en Remove an element with specified key and value. // --- template inline void MultiMap::Dissociate( const KeyType & key, const ValType & val ) { Pair pair( key, val ); size_t idx = m_Pairs.FindIt( pair ); if ( idx < m_Pairs.Count() ) { m_Pairs.RemoveInd( idx ); } } //------------------------------------------------------------------------------- /// \ru Удалить элемент, указанный итератором. \en Remove an element specified by an iterator. // --- template inline typename MultiMap::Iterator MultiMap::Dissociate( typename MultiMap::Iterator & it ) { size_t idx = m_Pairs.FindIt( *it.GetPair() ); if ( idx < m_Pairs.Count() ) { m_Pairs.RemoveInd( idx ); if ( idx < m_Pairs.Count() ) return Iterator ( m_Pairs, m_Pairs[idx]); } return Iterator(); } //------------------------------------------------------------------------------- /// \ru Удалить элементы в диапазоне [it1, it2). \en Remove elements in the range [it1, it2). // --- template inline void MultiMap::Dissociate( typename MultiMap::Iterator & it1, typename MultiMap::Iterator & it2 ) { if ( m_Pairs.Count() == 0 || it1.Empty() ) return; if ( it2.Empty() ) { // \ru Удаляем с it1 до конца. \en Remove from it1 up to the end. it2 = Iterator ( m_Pairs, m_Pairs[m_Pairs.Count() - 1] ); if ( it1 == it2 ) Dissociate ( it1.Key(), it1.Value() ); // \ru Просто удаляем последний элемент. \en Just remove the last element. else if ( it1 < it2 ) { size_t idx = m_Pairs.FindIt( *it1.GetPair() ); if ( idx != SYS_MAX_T ) m_Pairs.RemoveInd ( idx, m_Pairs.Count () ); } } else if ( it1 < it2 ) m_Pairs.Remove ( it1.GetPair(), it2.GetPair() ); } //------------------------------------------------------------------------------- /// \ru Оператор доступа по ключу. \en Access by key operator. // --- template inline typename MultiMap::Iterator MultiMap::operator[] ( const KeyType & key ) const { return Iterator( m_Pairs, Pair(key, Null::val()) ); } //------------------------------------------------------------------------------- /// \ru Получить итератор, указывающий на первый элемент. \en Get an iterator pointing to the first element. // --- template inline typename MultiMap::Iterator MultiMap::First() const { if ( m_Pairs.Count() ) return Iterator ( m_Pairs, m_Pairs[0] ); return Iterator(); } #endif // __TEMPL_MULTIMAP_H