diff --git a/fnv_1.h b/fnv_1.h index 543386241..8626c3980 100644 --- a/fnv_1.h +++ b/fnv_1.h @@ -5,7 +5,6 @@ The MIT License(MIT) Embedded Template Library. https://github.com/ETLCPP/etl -https://github.com/ETLCPP/etl Copyright(c) 2014 jwellbelove diff --git a/imap.h b/imap.h index 410076720..f31f634c5 100644 --- a/imap.h +++ b/imap.h @@ -433,16 +433,6 @@ namespace etl typedef std::reverse_iterator const_reverse_iterator; - //************************************************************************* - /// Assignment operator. - //************************************************************************* - imap& operator = (const imap& rhs) - { - assign(rhs.cbegin(), rhs.cend()); - - return *this; - } - //************************************************************************* /// Gets the beginning of the map. //************************************************************************* @@ -779,7 +769,7 @@ namespace etl ///\param position The position that would precede the value to insert. ///\param value The value to insert. //********************************************************************* - iterator insert(iterator position, const value_type& value) + iterator insert(iterator, const value_type& value) { // Default to no inserted node Node* inserted_node = nullptr; @@ -790,7 +780,7 @@ namespace etl Data_Node& node = allocate_data_node(value); // Obtain the inserted node (might be nullptr if node was a duplicate) - inserted_node = insert_node(find_node(root_node, position.p_node), node); + inserted_node = insert_node(root_node, node); } else { @@ -811,7 +801,7 @@ namespace etl ///\param position The position that would precede the value to insert. ///\param value The value to insert. //********************************************************************* - iterator insert(const_iterator position, const value_type& value) + iterator insert(const_iterator, const value_type& value) { // Default to no inserted node Node* inserted_node = nullptr; @@ -822,7 +812,7 @@ namespace etl Data_Node& node = allocate_data_node(value); // Obtain the inserted node (might be nullptr if node was a duplicate) - inserted_node = insert_node(find_node(root_node, position.p_node), node); + inserted_node = insert_node(root_node, node); } else { @@ -1151,7 +1141,7 @@ namespace etl } } - // Return root node if nothing or duplicate was found + // Return root node if nothing was found return root_node; } @@ -1324,36 +1314,6 @@ namespace etl return lower_node; } - //************************************************************************* - /// Find the node whose key is not considered to go before the key provided - //************************************************************************* - const Node& find_lower_node(const Node* position, const key_value_parameter_t& key) const - { - // Something at this position? keep going - const Node* lower_node = position; - while (lower_node) - { - // Downcast lower node to Data_Node reference for key comparisons - const Data_Node& data_node = imap::data_cast(*lower_node); - // Compare the key value to the current lower node key value - if (node_comp(key, data_node)) - { - lower_node = lower_node->children[kLeft]; - } - else if (node_comp(data_node, key)) - { - lower_node = lower_node->children[kRight]; - } - else - { - break; - } - } - - // Return the lower_node position found - return lower_node; - } - //************************************************************************* /// Find the node whose key is considered to go after the key provided //************************************************************************* @@ -1392,44 +1352,6 @@ namespace etl return upper_node; } - //************************************************************************* - /// Find the node whose key is considered to go after the key provided - //************************************************************************* - const Node* find_upper_node(const Node* position, const key_value_parameter_t& key) const - { - // Keep track of parent of last upper node - const Node* upper_node = nullptr; - // Start with position provided - const Node* node = position; - while (node) - { - // Downcast position to Data_Node reference for key comparisons - const Data_Node& data_node = imap::data_cast(*node); - // Compare the key value to the current upper node key value - if (node_comp(key, data_node)) - { - upper_node = node; - node = node->children[kLeft]; - } - else if (node_comp(data_node, key)) - { - node = node->children[kRight]; - } - else if (node->children[kRight]) - { - upper_node = find_limit_node(node->children[kRight], kLeft); - break; - } - else - { - break; - } - } - - // Return the upper node position found (might be nullptr) - return upper_node; - } - //************************************************************************* /// Insert a node. //************************************************************************* diff --git a/imultimap.h b/imultimap.h new file mode 100644 index 000000000..9fad67ab9 --- /dev/null +++ b/imultimap.h @@ -0,0 +1,1830 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IMULTIMAP__ +#define __ETL_IMULTIMAP__ +#define __ETL_IN_IMULTIMAP_H__ + +#include +#include +#include +#include + +#include "nullptr.h" +#include "map_base.h" +#include "type_traits.h" +#include "parameter_type.h" +#include "pool.h" + +#if WIN32 +#undef min +#endif + +namespace etl +{ + //*************************************************************************** + /// A templated base for all etl::multimap types. + ///\ingroup map + //*************************************************************************** + template + class imultimap : public map_base + { + public: + + typedef std::pair value_type; + typedef const TKey key_type; + typedef TMapped mapped_type; + typedef TKeyCompare key_compare; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef size_t size_type; + + //************************************************************************* + /// How to compare two key elements. + //************************************************************************* + struct key_comp + { + bool operator ()(const key_type& key1, const key_type& key2) const + { + return key_compare()(key1, key2); + } + }; + + //************************************************************************* + /// How to compare two value elements. + //************************************************************************* + struct value_comp + { + bool operator ()(const value_type& value1, const value_type& value2) const + { + return key_compare()(value1.first, value2.first); + } + }; + + protected: + static const uint8_t kLeft = 0; + static const uint8_t kRight = 1; + static const uint8_t kNeither = 2; + + //************************************************************************* + /// The node element in the multimap. + //************************************************************************* + struct Node + { + //*********************************************************************** + /// Constructor + //*********************************************************************** + Node() : + weight(kNeither), + dir(kNeither) + { + } + + //*********************************************************************** + /// Marks the node as a leaf. + //*********************************************************************** + void mark_as_leaf() + { + weight = kNeither; + dir = kNeither; + parent = nullptr; + children[0] = nullptr; + children[1] = nullptr; + } + + Node* parent; + Node* children[2]; + uint8_t weight; + uint8_t dir; + }; + + //************************************************************************* + /// The data node element in the multimap. + //************************************************************************* + struct Data_Node : public Node + { + explicit Data_Node(value_type value) + : value(value) + { + } + + value_type value; + }; + + /// Defines the key value parameter type + typedef typename parameter_type::type key_value_parameter_t; + + //************************************************************************* + /// How to compare node elements. + //************************************************************************* + bool node_comp(const Data_Node& node1, const Data_Node& node2) const + { + return key_compare()(node1.value.first, node2.value.first); + } + bool node_comp(const Data_Node& node, const key_value_parameter_t& key) const + { + return key_compare()(node.value.first, key); + } + bool node_comp(const key_value_parameter_t& key, const Data_Node& node) const + { + return key_compare()(key, node.value.first); + } + + private: + + /// The pool of data nodes used in the multimap. + ipool* p_node_pool; + + /// The node that acts as the multimap root. + Node* root_node; + + //************************************************************************* + /// Downcast a Node* to a Data_Node* + //************************************************************************* + static Data_Node* data_cast(Node* p_node) + { + return static_cast(p_node); + } + + //************************************************************************* + /// Downcast a Node& to a Data_Node& + //************************************************************************* + static Data_Node& data_cast(Node& node) + { + return static_cast(node); + } + + //************************************************************************* + /// Downcast a const Node* to a const Data_Node* + //************************************************************************* + static const Data_Node* data_cast(const Node* p_node) + { + return static_cast(p_node); + } + + //************************************************************************* + /// Downcast a const Node& to a const Data_Node& + //************************************************************************* + static const Data_Node& data_cast(const Node& node) + { + return static_cast(node); + } + + public: + //************************************************************************* + /// iterator. + //************************************************************************* + class iterator : public std::iterator + { + public: + + friend class imultimap; + + iterator() + : p_multimap(nullptr) + , p_node(nullptr) + { + } + + iterator(imultimap& multimap) + : p_multimap(&multimap) + , p_node(nullptr) + { + } + + iterator(imultimap& multimap, Node* node) + : p_multimap(&multimap) + , p_node(node) + { + } + + iterator(const iterator& other) + : p_multimap(other.p_multimap) + , p_node(other.p_node) + { + } + + ~iterator() + { + } + + iterator& operator ++() + { + p_multimap->next_node(p_node); + return *this; + } + + iterator operator ++(int) + { + iterator temp(*this); + p_multimap->next_node(p_node); + return temp; + } + + iterator& operator --() + { + p_multimap->prev_node(p_node); + return *this; + } + + iterator operator --(int) + { + iterator temp(*this); + p_multimap->prev_node(p_node); + return temp; + } + + iterator operator =(const iterator& other) + { + p_multimap = other.p_multimap; + p_node = other.p_node; + return *this; + } + + reference operator *() + { + return imultimap::data_cast(p_node)->value; + } + + const_reference operator *() const + { + return imultimap::data_cast(p_node)->value; + } + + pointer operator &() + { + return &(imultimap::data_cast(p_node)->value); + } + + const_pointer operator &() const + { + return &(imultimap::data_cast(p_node)->value); + } + + pointer operator ->() + { + return &(imultimap::data_cast(p_node)->value); + } + + const_pointer operator ->() const + { + return &(imultimap::data_cast(p_node)->value); + } + + friend bool operator == (const iterator& lhs, const iterator& rhs) + { + return lhs.p_multimap == rhs.p_multimap && lhs.p_node == rhs.p_node; + } + + friend bool operator != (const iterator& lhs, const iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + // Pointer to multimap associated with this iterator + imultimap* p_multimap; + + // Pointer to the current node for this iterator + Node* p_node; + }; + friend iterator; + + //************************************************************************* + /// const_iterator + //************************************************************************* + class const_iterator : public std::iterator + { + public: + + friend class imultimap; + + const_iterator() + : p_multimap(nullptr) + , p_node(nullptr) + { + } + + const_iterator(const imultimap& multimap) + : p_multimap(&multimap) + , p_node(nullptr) + { + } + + const_iterator(const imultimap& multimap, const Node* node) + : p_multimap(&multimap) + , p_node(node) + { + } + + const_iterator(const typename imultimap::iterator& other) + : p_multimap(other.p_multimap) + , p_node(other.p_node) + { + } + + const_iterator(const const_iterator& other) + : p_multimap(other.p_multimap) + , p_node(other.p_node) + { + } + + ~const_iterator() + { + } + + const_iterator& operator ++() + { + p_multimap->next_node(p_node); + return *this; + } + + const_iterator operator ++(int) + { + const_iterator temp(*this); + p_multimap->next_node(p_node); + return temp; + } + + const_iterator& operator --() + { + p_multimap->prev_node(p_node); + return *this; + } + + const_iterator operator --(int) + { + const_iterator temp(*this); + p_multimap->prev_node(p_node); + return temp; + } + + const_iterator operator =(const const_iterator& other) + { + p_multimap = other.p_multimap; + p_node = other.p_node; + return *this; + } + + const_reference operator *() const + { + return imultimap::data_cast(p_node)->value; + } + + const_pointer operator &() const + { + return imultimap::data_cast(p_node)->value; + } + + const_pointer operator ->() const + { + return &(imultimap::data_cast(p_node)->value); + } + + friend bool operator == (const const_iterator& lhs, const const_iterator& rhs) + { + return lhs.p_multimap == rhs.p_multimap && lhs.p_node == rhs.p_node; + } + + friend bool operator != (const const_iterator& lhs, const const_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + // Pointer to multimap associated with this iterator + const imultimap* p_multimap; + + // Pointer to the current node for this iterator + const Node* p_node; + }; + friend const_iterator; + + typedef typename std::iterator_traits::difference_type difference_type; + + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + + //************************************************************************* + /// Gets the beginning of the multimap. + //************************************************************************* + iterator begin() + { + return iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the beginning of the multimap. + //************************************************************************* + const_iterator begin() const + { + return const_iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the end of the multimap. + //************************************************************************* + iterator end() + { + return iterator(*this); + } + + //************************************************************************* + /// Gets the end of the multimap. + //************************************************************************* + const_iterator end() const + { + return const_iterator(*this); + } + + //************************************************************************* + /// Gets the beginning of the multimap. + //************************************************************************* + const_iterator cbegin() const + { + return const_iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the end of the multimap. + //************************************************************************* + const_iterator cend() const + { + return const_iterator(*this); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + reverse_iterator rbegin() + { + return reverse_iterator(iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(const_iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + reverse_iterator rend() + { + return reverse_iterator(iterator(*this, find_limit_node(root_node, kLeft))); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + const_reverse_iterator rend() const + { + return const_reverse_iterator(iterator(*this, find_limit_node(root_node, kLeft))); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(const_iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + const_reverse_iterator crend() const + { + return const_reverse_iterator(const_iterator(*this, find_limit_node(root_node, kLeft))); + } + + //********************************************************************* + /// Assigns values to the multimap. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_full if the multimap does not have enough free space. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_iterator if the iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { + initialise(); + insert(first, last); + } + + //************************************************************************* + /// Clears the multimap. + //************************************************************************* + void clear() + { + initialise(); + } + + //********************************************************************* + /// Counts the number of elements that contain the key specified. + ///\param key The key to search for. + ///\return 1 if element was found, 0 otherwise. + //********************************************************************* + size_type count(const key_value_parameter_t& key) const + { + return count_nodes(key); + } + + //************************************************************************* + /// Returns two iterators with bounding (lower bound, upper bound) the key + /// provided + //************************************************************************* + std::pair equal_range(const key_value_parameter_t& key) + { + return std::make_pair( + iterator(*this, find_lower_node(root_node, key)), + iterator(*this, find_upper_node(root_node, key))); + } + + //************************************************************************* + /// Returns two const iterators with bounding (lower bound, upper bound) + /// the key provided. + //************************************************************************* + std::pair equal_range(const key_value_parameter_t& key) const + { + return std::make_pair( + const_iterator(*this, find_lower_node(root_node, key)), + const_iterator(*this, find_upper_node(root_node, key))); + } + + //************************************************************************* + /// Erases the value at the specified position. + //************************************************************************* + void erase(iterator position) + { + // Remove the node by its node specified in iterator position + (void)erase(const_iterator(position)); + } + + //************************************************************************* + /// Erases the value at the specified position. + //************************************************************************* + iterator erase(const_iterator position) + { + // Cast const away from node to be removed. This is necessary because the + // STL definition of this method requires we provide the next node in the + // sequence as an iterator. + Node* node = const_cast(position.p_node); + iterator next(*this, node); + ++next; + + // Remove the non-const node provided + remove_node(node); + + return next; + } + + //************************************************************************* + // Erase the key specified. + //************************************************************************* + size_type erase(const key_value_parameter_t& key) + { + // Number of nodes removed + size_type count = 0; + const_iterator lower(*this, find_lower_node(root_node, key)); + const_iterator upper(*this, find_upper_node(root_node, key)); + while (lower != upper) + { + // Increment count for each node removed + ++count; + // Remove node using the other erase method + (void)erase(lower++); + } + + // Return the total count erased + return count; + } + + //************************************************************************* + /// Erases a range of elements. + //************************************************************************* + iterator erase(iterator first, iterator last) + { + iterator next; + while (first != last) + { + next = erase(const_iterator(first++)); + } + + return next; + } + + //************************************************************************* + /// Erases a range of elements. + //************************************************************************* + iterator erase(const_iterator first, const_iterator last) + { + iterator next; + while (first != last) + { + next = erase(first++); + } + + return next; + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator pointing to the element or end() if not found. + //********************************************************************* + iterator find(const key_value_parameter_t& key) + { + return iterator(*this, find_node(root_node, key)); + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator pointing to the element or end() if not found. + //********************************************************************* + const_iterator find(const key_value_parameter_t& key) const + { + return const_iterator(*this, find_node(root_node, key)); + } + + //********************************************************************* + /// Inserts a value to the multimap. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_full if the multimap is already full. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const value_type& value) + { + // Default to no inserted node + Node* inserted_node = nullptr; + + if (!full()) + { + // Get next available free node + Data_Node& node = allocate_data_node(value); + + // Obtain the inserted node (might be nullptr if node was a duplicate) + inserted_node = insert_node(root_node, node); + } + else + { +#ifdef ETL_THROW_EXCEPTIONS + throw map_full(); +#else + error_handler::error(map_full()); +#endif + } + + // Insert node into tree and return iterator to new node location in tree + return iterator(*this, inserted_node); + } + + //********************************************************************* + /// Inserts a value to the multimap starting at the position recommended. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_full if the multimap is already full. + ///\param position The position that would precede the value to insert. + ///\param value The value to insert. + //********************************************************************* + iterator insert(iterator position, const value_type& value) + { + // Ignore position provided and just do a normal insert + return insert(value); + } + + //********************************************************************* + /// Inserts a value to the multimap starting at the position recommended. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_full if the multimap is already full. + ///\param position The position that would precede the value to insert. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const_iterator position, const value_type& value) + { + // Ignore position provided and just do a normal insert + return insert(value); + } + + //********************************************************************* + /// Inserts a range of values to the multimap. + /// If ETL_THROW_EXCEPTIONS is defined, emits map_full if the multimap does not have enough free space. + ///\param position The position to insert at. + ///\param first The first element to add. + ///\param last The last + 1 element to add. + //********************************************************************* + template + void insert(TIterator first, TIterator last) + { + while (first != last) + { + insert(*first++); + } + } + + void print() const + { + print_tree(root_node); + } + + //********************************************************************* + /// Returns an iterator pointing to the first element in the container + /// whose key is not considered to go before the key provided or end() + /// if all keys are considered to go before the key provided. + ///\return An iterator pointing to the element not before key or end() + //********************************************************************* + iterator lower_bound(const key_value_parameter_t& key) + { + return iterator(*this, find_lower_node(root_node, key)); + } + + //********************************************************************* + /// Returns a const_iterator pointing to the first element in the + /// container whose key is not considered to go before the key provided + /// or end() if all keys are considered to go before the key provided. + ///\return An const_iterator pointing to the element not before key or end() + //********************************************************************* + const_iterator lower_bound(const key_value_parameter_t& key) const + { + return const_iterator(*this, find_lower_node(root_node, key)); + } + + //********************************************************************* + /// Returns an iterator pointing to the first element in the container + /// whose key is not considered to go after the key provided or end() + /// if all keys are considered to go after the key provided. + ///\return An iterator pointing to the element after key or end() + //********************************************************************* + iterator upper_bound(const key_value_parameter_t& key) + { + return iterator(*this, find_upper_node(root_node, key)); + } + + //********************************************************************* + /// Returns a const_iterator pointing to the first element in the + /// container whose key is not considered to go after the key provided + /// or end() if all keys are considered to go after the key provided. + ///\return An const_iterator pointing to the element after key or end() + //********************************************************************* + const_iterator upper_bound(const key_value_parameter_t& key) const + { + return const_iterator(*this, find_upper_node(root_node, key)); + } + + protected: + + //************************************************************************* + /// Constructor. + //************************************************************************* + imultimap(ipool& node_pool, size_t max_size_) + : map_base(max_size_) + , p_node_pool(&node_pool) + , root_node(nullptr) + { + initialise(); + } + + private: + + //************************************************************************* + /// Allocate a Data_Node. + //************************************************************************* + Data_Node& allocate_data_node(value_type value) const + { + return *(p_node_pool->allocate(Data_Node(value))); + } + + //************************************************************************* + /// Destroy a Data_Node. + //************************************************************************* + void destroy_data_node(Data_Node& node) const + { + p_node_pool->release(&node); + } + + //************************************************************************* + /// Initialise the multimap. + //************************************************************************* + void initialise() + { + if (!empty()) + { + p_node_pool->release_all(); + } + + current_size = 0; + root_node = nullptr; + } + + //************************************************************************* + /// Attach the provided node to the position provided + //************************************************************************* + void attach_node(Node* parent, Node*& position, Data_Node& node) + { + // Mark new node as leaf on attach to tree at position provided + node.mark_as_leaf(); + + // Keep track of this node's parent + node.parent = parent; + + // Add the node here + position = &node; + + // One more. + ++current_size; + } + + //************************************************************************* + /// Balance the critical node at the position provided as needed + //************************************************************************* + void balance_node(Node*& critical_node) + { + // Step 1: Update weights for all children of the critical node up to the + // newly inserted node. This step is costly (in terms of traversing nodes + // multiple times during insertion) but doesn't require as much recursion + Node* weight_node = critical_node->children[critical_node->dir]; + while (weight_node) + { + // Keep going until we reach a terminal node (dir == kNeither) + if (kNeither != weight_node->dir) + { + // Does this insert balance the previous weight factor value? + if (weight_node->weight == 1 - weight_node->dir) + { + weight_node->weight = kNeither; + } + else + { + weight_node->weight = weight_node->dir; + } + + // Update weight factor node to point to next node + weight_node = weight_node->children[weight_node->dir]; + } + else + { + // Stop loop, terminal node found + break; + } + } // while(weight_node) + + // Step 2: Update weight for critical_node or rotate tree to balance node + if (kNeither == critical_node->weight) + { + critical_node->weight = critical_node->dir; + } + // If direction is different than weight, then it will now be balanced + else if (critical_node->dir != critical_node->weight) + { + critical_node->weight = kNeither; + } + // Rotate is required to balance the tree at the critical node + else + { + // If critical node matches child node direction then perform a two + // node rotate in the direction of the critical node + if (critical_node->weight == critical_node->children[critical_node->dir]->dir) + { + rotate_2node(critical_node, critical_node->dir); + } + // Otherwise perform a three node rotation in the direction of the + // critical node + else + { + rotate_3node(critical_node, critical_node->dir, + critical_node->children[critical_node->dir]->children[1 - critical_node->dir]->dir); + } + } + } + + //************************************************************************* + /// Count the nodes that match the key provided + //************************************************************************* + size_type count_nodes(const key_value_parameter_t& key) const + { + // Number of nodes that match the key provided result + size_type result = 0; + + // Find lower and upper nodes for the key provided + const Node* lower = find_lower_node(root_node, key); + const Node* upper = find_upper_node(root_node, key); + + // Loop from lower node to upper node and find nodes that match + while (lower != upper) + { + // Downcast found to Data_Node class for comparison and other operations + const Data_Node& data_node = imultimap::data_cast(*lower); + + if (!node_comp(key, data_node) && !node_comp(data_node, key)) + { + // This node matches the key provided + ++result; + } + + // Move on to the next node + next_node(lower); + } + + // Return the number of nodes that match + return result; + } + + //************************************************************************* + /// Detach the node at the position provided + //************************************************************************* + void detach_node(Node*& position, Node*& replacement) + { + // Make temporary copy of actual nodes involved because we might lose + // their references in the process (e.g. position is the same as + // replacement or replacement is a child of position) + Node* detached = position; + Node* swap = replacement; + + // Update current position to point to swap (replacement) node first + position = swap; + + // Update replacement node to point to child in opposite direction + // otherwise we might lose the other child of the swap node + replacement = swap->children[1 - swap->dir]; + + // Point swap node to detached node's parent, children and weight + swap->parent = detached->parent; + swap->children[kLeft] = detached->children[kLeft]; + swap->children[kRight] = detached->children[kRight]; + if (swap->children[kLeft]) + { + swap->children[kLeft]->parent = swap; + } + if (swap->children[kRight]) + { + swap->children[kRight]->parent = swap; + } + swap->weight = detached->weight; + } + + //************************************************************************* + /// Find the value matching the node provided + //************************************************************************* + Node* find_node(Node* position, const key_value_parameter_t& key) const + { + Node* found = nullptr; + while (position) + { + // Downcast found to Data_Node class for comparison and other operations + Data_Node& data_node = imultimap::data_cast(*position); + // Compare the node value to the current position value + if (node_comp(key, data_node)) + { + // Keep searching for the node on the left + position = position->children[kLeft]; + } + else if (node_comp(data_node, key)) + { + // Keep searching for the node on the right + position = position->children[kRight]; + } + else + { + // We found one, keep looking for more on the left + found = position; + position = position->children[kLeft]; + } + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the value matching the node provided + //************************************************************************* + const Node* find_node(const Node* position, const key_value_parameter_t& key) const + { + const Node* found = nullptr; + while (position) + { + // Downcast found to Data_Node class for comparison and other operations + const Data_Node& data_node = imultimap::data_cast(*position); + // Compare the node value to the current position value + if (node_comp(key, data_node)) + { + // Keep searching for the node on the left + position = position->children[kLeft]; + } + else if (node_comp(data_node, key)) + { + // Keep searching for the node on the right + position = position->children[kRight]; + } + else + { + // We found one, keep looking for more on the left + found = position; + position = position->children[kLeft]; + } + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the node whose key would go before all the other keys from the + /// position provided + //************************************************************************* + Node* find_limit_node(Node* position, const int8_t dir) const + { + // Something at this position and in the direction specified? keep going + Node* limit_node = position; + while (limit_node && limit_node->children[dir]) + { + limit_node = limit_node->children[dir]; + } + + // Return the limit node position found + return limit_node; + } + + //************************************************************************* + /// Find the node whose key is not considered to go before the key provided + //************************************************************************* + Node* find_lower_node(Node* position, const key_value_parameter_t& key) const + { + // Something at this position? keep going + Node* lower_node = nullptr; + while (position) + { + // Downcast lower node to Data_Node reference for key comparisons + Data_Node& data_node = imultimap::data_cast(*position); + // Compare the key value to the current lower node key value + if (node_comp(key, data_node)) + { + lower_node = position; + if (position->children[kLeft]) + { + position = position->children[kLeft]; + } + else + { + // Found lowest node + break; + } + } + else if (node_comp(data_node, key)) + { + position = position->children[kRight]; + } + else + { + // Make note of current position, but keep looking to left for more + lower_node = position; + position = position->children[kLeft]; + } + } + + // Return the lower_node position found + return lower_node; + } + + //************************************************************************* + /// Find the node whose key is considered to go after the key provided + //************************************************************************* + Node* find_upper_node(Node* position, const key_value_parameter_t& key) const + { + // Keep track of parent of last upper node + Node* upper_node = nullptr; + // Has an equal node been found? start with no + bool found = false; + while (position) + { + // Downcast position to Data_Node reference for key comparisons + Data_Node& data_node = imultimap::data_cast(*position); + // Compare the key value to the current upper node key value + if (node_comp(data_node, key)) + { + position = position->children[kRight]; + } + else if (node_comp(key, data_node)) + { + upper_node = position; + // If a node equal to key hasn't been found go left + if (!found && position->children[kLeft]) + { + position = position->children[kLeft]; + } + else + { + break; + } + } + else + { + // We found an equal item, break on next bigger item + found = true; + next_node(position); + } + } + + // Return the upper node position found (might be nullptr) + return upper_node; + } + + //************************************************************************* + /// Insert a node. + //************************************************************************* + Node* insert_node(Node*& position, Data_Node& node) + { + // Find the location where the node belongs + Node* found = position; + + // Was position provided not empty? then find where the node belongs + if (position) + { + // Find the critical parent node (default to nullptr) + Node* critical_parent_node = nullptr; + Node* critical_node = root_node; + + while (found) + { + // Search for critical weight node (all nodes whose weight factor + // is set to kNeither (balanced) + if (kNeither != found->weight) + { + critical_node = found; + } + + // Downcast found to Data_Node class for comparison and other operations + Data_Node& found_data_node = imultimap::data_cast(*found); + + // Is the node provided to the left of the current position? + if (node_comp(node, found_data_node)) + { + // Update direction taken to insert new node in parent node + found->dir = kLeft; + } + // Is the node provided to the right of the current position? + else if (node_comp(found_data_node, node)) + { + // Update direction taken to insert new node in parent node + found->dir = kRight; + } + else + { + // Update direction taken to insert new node in parent (and + // duplicate) node to the right. + found->dir = kRight; + } + + // Is there a child of this parent node? + if (found->children[found->dir]) + { + // Will this node be the parent of the next critical node whose + // weight factor is set to kNeither (balanced)? + if (kNeither != found->children[found->dir]->weight) + { + critical_parent_node = found; + } + + // Keep looking for empty spot to insert new node + found = found->children[found->dir]; + } + else + { + // Attach node as a child of the parent node found + attach_node(found, found->children[found->dir], node); + + // Return newly added node + found = found->children[found->dir]; + + // Exit loop + break; + } + } + + // Was a critical node found that should be checked for balance? + if (critical_node) + { + if (critical_parent_node == nullptr && critical_node == root_node) + { + balance_node(root_node); + } + else if (critical_parent_node == nullptr && critical_node == position) + { + balance_node(position); + } + else + { + balance_node(critical_parent_node->children[critical_parent_node->dir]); + } + } + } + else + { + // Attatch node to current position (which is assumed to be root) + attach_node(nullptr, position, node); + + // Return newly added node at current position + found = position; + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the next node in sequence from the node provided + //************************************************************************* + void next_node(Node*& position) const + { + if (position) + { + // Is there a tree on the right? then find the minimum of that tree + if (position->children[kRight]) + { + // Return minimum node found + position = find_limit_node(position->children[kRight], kLeft); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; // find_parent_node(root_node, position); + // Repeat while previous position was on right side of parent tree + } while (parent && parent->children[kRight] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the next node in sequence from the node provided + //************************************************************************* + void next_node(const Node*& position) const + { + if (position) + { + // Is there a tree on the right? then find the minimum of that tree + if (position->children[kRight]) + { + // Return minimum node found + position = find_limit_node(position->children[kRight], kLeft); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + const Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on right side of parent tree + } while (parent && parent->children[kRight] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the previous node in sequence from the node provided + //************************************************************************* + void prev_node(Node*& position) const + { + // If starting at the terminal end, the previous node is the maximum node + // from the root + if (!position) + { + position = find_limit_node(root_node, kRight); + } + else + { + // Is there a tree on the left? then find the maximum of that tree + if (position->children[kLeft]) + { + // Return maximum node found + position = find_limit_node(position->children[kLeft], kRight); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on left side of parent tree + } while (parent && parent->children[kLeft] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the previous node in sequence from the node provided + //************************************************************************* + void prev_node(const Node*& position) const + { + // If starting at the terminal end, the previous node is the maximum node + // from the root + if (!position) + { + position = find_limit_node(root_node, kRight); + } + else + { + // Is there a tree on the left? then find the maximum of that tree + if (position->children[kLeft]) + { + // Return maximum node found + position = find_limit_node(position->children[kLeft], kRight); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + const Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on left side of parent tree + } while (parent && parent->children[kLeft] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Remove the node specified from somewhere starting at the position + /// provided + //************************************************************************* + void remove_node(Node* node) + { + // If valid found node was provided then proceed with steps 1 through 5 + if (node) + { + // Downcast found node provided to Data_Node class + Data_Node& data_node = imultimap::data_cast(*node); + + // Keep track of node as found node + Node* found = node; + + // Step 1: Mark path from node provided back to the root node using the + // internal temporary dir member value and using the parent pointer. This + // will allow us to avoid recursion in finding the node in a tree that + //might contain duplicate keys to be found. + while (node) + { + if (node->parent) + { + // Which direction does parent use to get to this node? + node->parent->dir = + node->parent->children[kLeft] == node ? kLeft : kRight; + + // Make this nodes parent the next node + node = node->parent; + } + else + { + // Root node found - break loop + break; + } + } + + // Step 2: Follow the path provided above until we reach the node + // provided and look for the balance node to start rebalancing the tree + // from (up to the replacement node that will be found in step 3) + Node* balance = root_node; + while (node) + { + // Did we reach the node provided originally (found) then go to step 3 + if (node == found) + { + // Update the direction towards a replacement node at the found node + node->dir = node->children[kLeft] ? kLeft : kRight; + + // Exit loop and proceed with step 3 + break; + } + else + { + // If this nodes weight is kNeither or we are taking the shorter path + // to the next node and our sibling (on longer path) is balanced then + // we need to update the balance node to this node but all our + // ancestors will not require rebalancing + if ((node->weight == kNeither) || + (node->weight == (1 - node->dir) && + node->children[1 - node->dir]->weight == kNeither)) + { + // Update balance node to this node + balance = node; + } + + // Keep searching for found in the direction provided in step 1 + node = node->children[node->dir]; + } + } + // The value for node should not be nullptr at this point otherwise + // step 1 failed to provide the correct path to found. Step 5 will fail + // (probably subtly) if node should be nullptr at this point + + // Step 3: Find the node (node should be equal to found at this point) + // to replace found with (might end up equal to found) while also + // continuing to update balance the same as in step 2 above. + while (node) + { + // Replacement node found if its missing a child in the replace->dir + // value set at the end of step 2 above + if (node->children[node->dir] == nullptr) + { + // Exit loop once node to replace found is determined + break; + } + + // If this nodes weight is kNeither or we are taking the shorter path + // to the next node and our sibling (on longer path) is balanced then + // we need to update the balance node to this node but all our + // ancestors will not require rebalancing + if ((node->weight == kNeither) || + (node->weight == (1 - node->dir) && + node->children[1 - node->dir]->weight == kNeither)) + { + // Update balance node to this node + balance = node; + } + + // Keep searching for replacement node in the direction specified above + node = node->children[node->dir]; + + // Downcast node to Data_Node class for comparison operations + Data_Node& replace_data_node = imultimap::data_cast(*node); + + // Compare the key provided to the replace data node key + if (node_comp(data_node, replace_data_node)) + { + // Update the direction to the replace node + node->dir = kLeft; + } + else if (node_comp(replace_data_node, data_node)) + { + // Update the direction to the replace node + node->dir = kRight; + } + else + { + // Update the direction to the replace node + node->dir = node->children[kLeft] ? kLeft : kRight; + } + } // while(node) + + // Step 4: Update weights from balance to parent of node determined + // in step 3 above rotating (2 or 3 node rotations) as needed. + while (balance) + { + // Break when balance node reaches the parent of replacement node + if (balance->children[balance->dir] == nullptr) + { + break; + } + + // If balance node is balanced already (kNeither) then just imbalance + // the node in the opposite direction of the node being removed + if (balance->weight == kNeither) + { + balance->weight = 1 - balance->dir; + } + // If balance node is imbalanced in the opposite direction of the + // node being removed then the node now becomes balanced + else if (balance->weight == balance->dir) + { + balance->weight = kNeither; + } + // Otherwise a rotation is required at this node + else + { + int weight = balance->children[1 - balance->dir]->weight; + // Perform a 3 node rotation if weight is same as balance->dir + if (weight == balance->dir) + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_3node(root_node, 1 - balance->dir, + balance->children[1 - balance->dir]->children[balance->dir]->weight); + } + else + { + rotate_3node(balance->parent->children[balance->parent->dir], 1 - balance->dir, + balance->children[1 - balance->dir]->children[balance->dir]->weight); + } + } + // Already balanced, rebalance and make it heavy in opposite + // direction of the node being removed + else if (weight == kNeither) + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_2node(root_node, 1 - balance->dir); + root_node->weight = balance->dir; + } + else + { + // Balance parent might change during rotate, keep local copy + // to old parent so its weight can be updated after the 2 node + // rotate is completed + Node* old_parent = balance->parent; + rotate_2node(balance->parent->children[balance->parent->dir], 1 - balance->dir); + old_parent->children[old_parent->dir]->weight = balance->dir; + } + // Update balance node weight in opposite direction of node removed + balance->weight = 1 - balance->dir; + } + // Rebalance and leave it balanced + else + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_2node(root_node, 1 - balance->dir); + } + else + { + rotate_2node(balance->parent->children[balance->parent->dir], 1 - balance->dir); + } + } + } + + // Next balance node to consider + balance = balance->children[balance->dir]; + } // while(balance) + + // Step 5: Swap found with node (replacement) + if (found->parent) + { + // Handle traditional case + detach_node(found->parent->children[found->parent->dir], + node->parent->children[node->parent->dir]); + } + // Handle root node removal + else + { + // Valid replacement node for root node being removed? + if (node->parent) + { + detach_node(root_node, node->parent->children[node->parent->dir]); + } + else + { + // Found node and replacement node are both root node + detach_node(root_node, root_node); + } + } + + // One less. + --current_size; + + // Destroy the node detached above + destroy_data_node(data_node); + } // if(found) + } + + //************************************************************************* + /// Rotate two nodes at the position provided the to balance the tree + //************************************************************************* + void rotate_2node(Node*& position, uint8_t dir) + { + // A C A B + // B C -> A E OR B C -> D A + // D E B D D E E C + // C (new position) becomes the root + // A (position) takes ownership of D as its children[kRight] child + // C (new position) takes ownership of A as its left child + // OR + // B (new position) becomes the root + // A (position) takes ownership of E as its left child + // B (new position) takes ownership of A as its right child + + // Capture new root (either B or C depending on dir) and its parent + Node* new_root = position->children[dir]; + + // Replace position's previous child with new root's other child + position->children[dir] = new_root->children[1 - dir]; + // Update new root's other child parent pointer + if (position->children[dir]) + { + position->children[dir]->parent = position; + } + + // New root's parent becomes current position's parent + new_root->parent = position->parent; + new_root->children[1 - dir] = position; + new_root->dir = 1 - dir; + + // Clear weight factor from current position + position->weight = kNeither; + // Position's parent becomes new_root + position->parent = new_root; + position = new_root; + // Clear weight factor from new root + position->weight = kNeither; + } + + //************************************************************************* + /// Rotate three nodes at the position provided the to balance the tree + //************************************************************************* + void rotate_3node(Node*& position, uint8_t dir, uint8_t third) + { + // __A__ __E__ __A__ __D__ + // _B_ C -> B A OR B _C_ -> A C + // D E D F G C D E B F G E + // F G F G + // E (new position) becomes the root + // B (position) takes ownership of F as its left child + // A takes ownership of G as its right child + // OR + // D (new position) becomes the root + // A (position) takes ownership of F as its right child + // C takes ownership of G as its left child + + // Capture new root (either E or D depending on dir) + Node* new_root = position->children[dir]->children[1 - dir]; + // Set weight factor for B or C based on F or G existing and being a different than dir + position->children[dir]->weight = third != kNeither && third != dir ? dir : kNeither; + + // Detach new root from its tree (replace with new roots child) + position->children[dir]->children[1 - dir] = new_root->children[dir]; + // Update new roots child parent pointer + if (new_root->children[dir]) + { + new_root->children[dir]->parent = position->children[dir]; + } + + // Attach current left tree to new root and update its parent + new_root->children[dir] = position->children[dir]; + position->children[dir]->parent = new_root; + + // Set weight factor for A based on F or G + position->weight = third != kNeither && third == dir ? 1 - dir : kNeither; + + // Move new root's right tree to current roots left tree + position->children[dir] = new_root->children[1 - dir]; + if (new_root->children[1 - dir]) + { + new_root->children[1 - dir]->parent = position; + } + + // Attach current root to new roots right tree and assume its parent + new_root->parent = position->parent; + new_root->children[1 - dir] = position; + new_root->dir = 1 - dir; + + // Update current position's parent and replace with new root + position->parent = new_root; + position = new_root; + // Clear weight factor for new current position + position->weight = kNeither; + } + + }; +} + +//*************************************************************************** +/// Equal operator. +///\param lhs Reference to the first lookup. +///\param rhs Reference to the second lookup. +///\return true if the arrays are equal, otherwise false +///\ingroup lookup +//*************************************************************************** +template +bool operator ==(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +//*************************************************************************** +/// Not equal operator. +///\param lhs Reference to the first lookup. +///\param rhs Reference to the second lookup. +///\return true if the arrays are not equal, otherwise false +///\ingroup lookup +//*************************************************************************** +template +bool operator !=(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return !(lhs == rhs); +} + +//************************************************************************* +/// Less than operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically less than the +/// second, otherwise false. +//************************************************************************* +template +bool operator <(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end()); +} + +//************************************************************************* +/// Greater than operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically greater than the +/// second, otherwise false. +//************************************************************************* +template +bool operator >(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + std::greater()); +} + +//************************************************************************* +/// Less than or equal operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically less than or equal +/// to the second, otherwise false. +//************************************************************************* +template +bool operator <=(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return !operator >(lhs, rhs); +} + +//************************************************************************* +/// Greater than or equal operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically greater than or +/// equal to the second, otherwise false. +//************************************************************************* +template +bool operator >=(const etl::imultimap& lhs, const etl::imultimap& rhs) +{ + return !operator <(lhs, rhs); +} + +#if WIN32 +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#undef __ETL_IN_IMULTIMAP_H__ + +#endif diff --git a/imultiset.h b/imultiset.h new file mode 100644 index 000000000..d843f508f --- /dev/null +++ b/imultiset.h @@ -0,0 +1,1813 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IMULTISET__ +#define __ETL_IMULTISET__ +#define __ETL_IN_IMULTISET_H__ + +#include +#include +#include +#include + +#include "nullptr.h" +#include "set_base.h" +#include "type_traits.h" +#include "parameter_type.h" +#include "pool.h" + +#if WIN32 +#undef min +#endif + +namespace etl +{ + //*************************************************************************** + /// A templated base for all etl::multiset types. + ///\ingroup set + //*************************************************************************** + template + class imultiset : public set_base + { + public: + + typedef const T key_type; + typedef const T value_type; + typedef TCompare key_compare; + typedef TCompare value_compare; + typedef value_type& const_reference; + typedef value_type* const_pointer; + typedef size_t size_type; + + //************************************************************************* + /// How to compare two key elements. + //************************************************************************* + struct key_comp + { + bool operator ()(key_type& key1, key_type& key2) const + { + return key_compare()(key1, key2); + } + }; + + //************************************************************************* + /// How to compare two value elements. + //************************************************************************* + struct value_comp + { + bool operator ()(value_type& value1, value_type& value2) const + { + return value_compare()(value1, value2); + } + }; + + protected: + static const uint8_t kLeft = 0; + static const uint8_t kRight = 1; + static const uint8_t kNeither = 2; + + //************************************************************************* + /// The node element in the multiset. + //************************************************************************* + struct Node + { + //*********************************************************************** + /// Constructor + //*********************************************************************** + Node() : + weight(kNeither), + dir(kNeither) + { + } + + //*********************************************************************** + /// Marks the node as a leaf. + //*********************************************************************** + void mark_as_leaf() + { + weight = kNeither; + dir = kNeither; + parent = nullptr; + children[0] = nullptr; + children[1] = nullptr; + } + + Node* parent; + Node* children[2]; + uint8_t weight; + uint8_t dir; + }; + + //************************************************************************* + /// The data node element in the multiset. + //************************************************************************* + struct Data_Node : public Node + { + explicit Data_Node(value_type value) + : value(value) + { + } + + value_type value; + }; + + /// Defines the key value parameter type + typedef typename parameter_type::type key_value_parameter_t; + + //************************************************************************* + /// How to compare node elements. + //************************************************************************* + bool node_comp(const Data_Node& node1, const Data_Node& node2) const + { + return key_compare()(node1.value, node2.value); + } + bool node_comp(const Data_Node& node, const key_value_parameter_t& key) const + { + return key_compare()(node.value, key); + } + bool node_comp(const key_value_parameter_t& key, const Data_Node& node) const + { + return key_compare()(key, node.value); + } + + private: + + /// The pool of data nodes used in the multiset. + ipool* p_node_pool; + + /// The node that acts as the multiset root. + Node* root_node; + + //************************************************************************* + /// Downcast a Node* to a Data_Node* + //************************************************************************* + static Data_Node* data_cast(Node* p_node) + { + return static_cast(p_node); + } + + //************************************************************************* + /// Downcast a Node& to a Data_Node& + //************************************************************************* + static Data_Node& data_cast(Node& node) + { + return static_cast(node); + } + + //************************************************************************* + /// Downcast a const Node* to a const Data_Node* + //************************************************************************* + static const Data_Node* data_cast(const Node* p_node) + { + return static_cast(p_node); + } + + //************************************************************************* + /// Downcast a const Node& to a const Data_Node& + //************************************************************************* + static const Data_Node& data_cast(const Node& node) + { + return static_cast(node); + } + + public: + //************************************************************************* + /// iterator. + //************************************************************************* + class iterator : public std::iterator + { + public: + + friend class imultiset; + + iterator() + : p_multiset(nullptr) + , p_node(nullptr) + { + } + + iterator(imultiset& multiset) + : p_multiset(&multiset) + , p_node(nullptr) + { + } + + iterator(imultiset& multiset, Node* node) + : p_multiset(&multiset) + , p_node(node) + { + } + + iterator(const iterator& other) + : p_multiset(other.p_multiset) + , p_node(other.p_node) + { + } + + ~iterator() + { + } + + iterator& operator ++() + { + p_multiset->next_node(p_node); + return *this; + } + + iterator operator ++(int) + { + iterator temp(*this); + p_multiset->next_node(p_node); + return temp; + } + + iterator& operator --() + { + p_multiset->prev_node(p_node); + return *this; + } + + iterator operator --(int) + { + iterator temp(*this); + p_multiset->prev_node(p_node); + return temp; + } + + iterator operator =(const iterator& other) + { + p_multiset = other.p_multiset; + p_node = other.p_node; + return *this; + } + + const_reference operator *() const + { + return imultiset::data_cast(p_node)->value; + } + + const_pointer operator &() const + { + return &(imultiset::data_cast(p_node)->value); + } + + const_pointer operator ->() const + { + return &(imultiset::data_cast(p_node)->value); + } + + friend bool operator == (const iterator& lhs, const iterator& rhs) + { + return lhs.p_multiset == rhs.p_multiset && lhs.p_node == rhs.p_node; + } + + friend bool operator != (const iterator& lhs, const iterator& rhs) + { + return !(lhs == rhs); + } + + private: + + // Pointer to multiset associated with this iterator + imultiset* p_multiset; + + // Pointer to the current node for this iterator + Node* p_node; + }; + friend iterator; + + //************************************************************************* + /// const_iterator + //************************************************************************* + class const_iterator : public std::iterator + { + public: + + friend class imultiset; + + const_iterator() + : p_multiset(nullptr) + , p_node(nullptr) + { + } + + const_iterator(const imultiset& multiset) + : p_multiset(&multiset) + , p_node(nullptr) + { + } + + const_iterator(const imultiset& multiset, const Node* node) + : p_multiset(&multiset) + , p_node(node) + { + } + + const_iterator(const typename imultiset::iterator& other) + : p_multiset(other.p_multiset) + , p_node(other.p_node) + { + } + + const_iterator(const const_iterator& other) + : p_multiset(other.p_multiset) + , p_node(other.p_node) + { + } + + ~const_iterator() + { + } + + const_iterator& operator ++() + { + p_multiset->next_node(p_node); + return *this; + } + + const_iterator operator ++(int) + { + const_iterator temp(*this); + p_multiset->next_node(p_node); + return temp; + } + + const_iterator& operator --() + { + p_multiset->prev_node(p_node); + return *this; + } + + const_iterator operator --(int) + { + const_iterator temp(*this); + p_multiset->prev_node(p_node); + return temp; + } + + const_iterator operator =(const const_iterator& other) + { + p_multiset = other.p_multiset; + p_node = other.p_node; + return *this; + } + + const_reference operator *() const + { + return imultiset::data_cast(p_node)->value; + } + + const_pointer operator &() const + { + return imultiset::data_cast(p_node)->value; + } + + const_pointer operator ->() const + { + return &(imultiset::data_cast(p_node)->value); + } + + friend bool operator == (const const_iterator& lhs, const const_iterator& rhs) + { + return lhs.p_multiset == rhs.p_multiset && lhs.p_node == rhs.p_node; + } + + friend bool operator != (const const_iterator& lhs, const const_iterator& rhs) + { + return !(lhs == rhs); + } + + private: + // Pointer to multiset associated with this iterator + const imultiset* p_multiset; + + // Pointer to the current node for this iterator + const Node* p_node; + }; + friend const_iterator; + + typedef typename std::iterator_traits::difference_type difference_type; + + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + + //************************************************************************* + /// Gets the beginning of the multiset. + //************************************************************************* + iterator begin() + { + return iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the beginning of the multiset. + //************************************************************************* + const_iterator begin() const + { + return const_iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the end of the multiset. + //************************************************************************* + iterator end() + { + return iterator(*this); + } + + //************************************************************************* + /// Gets the end of the multiset. + //************************************************************************* + const_iterator end() const + { + return const_iterator(*this); + } + + //************************************************************************* + /// Gets the beginning of the multiset. + //************************************************************************* + const_iterator cbegin() const + { + return const_iterator(*this, find_limit_node(root_node, kLeft)); + } + + //************************************************************************* + /// Gets the end of the multiset. + //************************************************************************* + const_iterator cend() const + { + return const_iterator(*this); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + reverse_iterator rbegin() + { + return reverse_iterator(iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(const_iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + reverse_iterator rend() + { + return reverse_iterator(iterator(*this, find_limit_node(root_node, kLeft))); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + const_reverse_iterator rend() const + { + return const_reverse_iterator(iterator(*this, find_limit_node(root_node, kLeft))); + } + + //************************************************************************* + /// Gets the reverse beginning of the list. + //************************************************************************* + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(const_iterator(*this)); + } + + //************************************************************************* + /// Gets the reverse end of the list. + //************************************************************************* + const_reverse_iterator crend() const + { + return const_reverse_iterator(const_iterator(*this, find_limit_node(root_node, kLeft))); + } + + //********************************************************************* + /// Assigns values to the multiset. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_full if the multiset does not have enough free space. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_iterator if the iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { + initialise(); + insert(first, last); + } + + //************************************************************************* + /// Clears the multiset. + //************************************************************************* + void clear() + { + initialise(); + } + + //********************************************************************* + /// Counts the number of elements that contain the key specified. + ///\param key The key to search for. + ///\return 1 if element was found, 0 otherwise. + //********************************************************************* + size_type count(const key_value_parameter_t& key) const + { + return count_nodes(key); + } + + //************************************************************************* + /// Returns two iterators with bounding (lower bound, upper bound) the key + /// provided + //************************************************************************* + std::pair equal_range(const value_type& key) + { + return std::make_pair( + iterator(*this, find_lower_node(root_node, key)), + iterator(*this, find_upper_node(root_node, key))); + } + + //************************************************************************* + /// Returns two const iterators with bounding (lower bound, upper bound) + /// the key provided. + //************************************************************************* + std::pair equal_range(const value_type& key) const + { + return std::make_pair( + const_iterator(*this, find_lower_node(root_node, key)), + const_iterator(*this, find_upper_node(root_node, key))); + } + + //************************************************************************* + /// Erases the value at the specified position. + //************************************************************************* + void erase(iterator position) + { + // Remove the node by its node specified in iterator position + (void)erase(const_iterator(position)); + } + + //************************************************************************* + /// Erases the value at the specified position. + //************************************************************************* + iterator erase(const_iterator position) + { + // Cast const away from node to be removed. This is necessary because the + // STL definition of this method requires we provide the next node in the + // sequence as an iterator. + Node* node = const_cast(position.p_node); + iterator next(*this, node); + ++next; + + // Remove the non-const node provided + remove_node(node); + + return next; + } + + //************************************************************************* + // Erase the key specified. + //************************************************************************* + size_type erase(const key_value_parameter_t& key_value) + { + // Number of nodes removed + size_type count = 0; + const_iterator lower(*this, find_lower_node(root_node, key_value)); + const_iterator upper(*this, find_upper_node(root_node, key_value)); + while (lower != upper) + { + // Increment count for each node removed + ++count; + // Remove node using the other erase method + (void)erase(lower++); + } + + // Return the total count erased + return count; + } + + //************************************************************************* + /// Erases a range of elements. + //************************************************************************* + iterator erase(iterator first, iterator last) + { + iterator next; + while (first != last) + { + next = erase(const_iterator(first++)); + } + + return next; + } + + //************************************************************************* + /// Erases a range of elements. + //************************************************************************* + iterator erase(const_iterator first, const_iterator last) + { + iterator next; + while (first != last) + { + next = erase(first++); + } + + return next; + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator pointing to the element or end() if not found. + //********************************************************************* + iterator find(const key_value_parameter_t& key_value) + { + return iterator(*this, find_node(root_node, key_value)); + } + + //********************************************************************* + /// Finds an element. + ///\param key The key to search for. + ///\return An iterator pointing to the element or end() if not found. + //********************************************************************* + const_iterator find(const key_value_parameter_t& key_value) const + { + return const_iterator(*this, find_node(root_node, key_value)); + } + + //********************************************************************* + /// Inserts a value to the multiset. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_full if the multiset is already full. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const value_type& value) + { + // Default to no inserted node + Node* inserted_node = nullptr; + + if (!full()) + { + // Get next available free node + Data_Node& node = allocate_data_node(value); + + // Obtain the inserted node (might be nullptr if node was a duplicate) + inserted_node = insert_node(root_node, node); + } + else + { +#ifdef ETL_THROW_EXCEPTIONS + throw set_full(); +#else + error_handler::error(set_full()); +#endif + } + + // Insert node into tree and return iterator to new node location in tree + return iterator(*this, inserted_node); + } + + //********************************************************************* + /// Inserts a value to the multiset starting at the position recommended. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_full if the multiset is already full. + ///\param position The position that would precede the value to insert. + ///\param value The value to insert. + //********************************************************************* + iterator insert(iterator position, const value_type& value) + { + // Ignore position provided and just do a normal insert + return insert(value); + } + + //********************************************************************* + /// Inserts a value to the multiset starting at the position recommended. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_full if the multiset is already full. + ///\param position The position that would precede the value to insert. + ///\param value The value to insert. + //********************************************************************* + iterator insert(const_iterator position, const value_type& value) + { + // Ignore position provided and just do a normal insert + return insert(value); + } + + //********************************************************************* + /// Inserts a range of values to the multiset. + /// If ETL_THROW_EXCEPTIONS is defined, emits set_full if the multiset does not have enough free space. + ///\param position The position to insert at. + ///\param first The first element to add. + ///\param last The last + 1 element to add. + //********************************************************************* + template + void insert(TIterator first, TIterator last) + { + while (first != last) + { + insert(*first++); + } + } + + void print() const + { + print_tree(root_node); + } + + //********************************************************************* + /// Returns an iterator pointing to the first element in the container + /// whose key is not considered to go before the key provided or end() + /// if all keys are considered to go before the key provided. + ///\return An iterator pointing to the element not before key or end() + //********************************************************************* + iterator lower_bound(const key_value_parameter_t& key) + { + return iterator(*this, find_lower_node(root_node, key)); + } + + //********************************************************************* + /// Returns a const_iterator pointing to the first element in the + /// container whose key is not considered to go before the key provided + /// or end() if all keys are considered to go before the key provided. + ///\return An const_iterator pointing to the element not before key or end() + //********************************************************************* + const_iterator lower_bound(const key_value_parameter_t& key) const + { + return const_iterator(*this, find_lower_node(root_node, key)); + } + + //********************************************************************* + /// Returns an iterator pointing to the first element in the container + /// whose key is not considered to go after the key provided or end() + /// if all keys are considered to go after the key provided. + ///\return An iterator pointing to the element after key or end() + //********************************************************************* + iterator upper_bound(const key_value_parameter_t& key) + { + return iterator(*this, find_upper_node(root_node, key)); + } + + //********************************************************************* + /// Returns a const_iterator pointing to the first element in the + /// container whose key is not considered to go after the key provided + /// or end() if all keys are considered to go after the key provided. + ///\return An const_iterator pointing to the element after key or end() + //********************************************************************* + const_iterator upper_bound(const key_value_parameter_t& key) const + { + return const_iterator(*this, find_upper_node(root_node, key)); + } + + protected: + + //************************************************************************* + /// Constructor. + //************************************************************************* + imultiset(ipool& node_pool, size_t max_size_) + : set_base(max_size_) + , p_node_pool(&node_pool) + , root_node(nullptr) + { + initialise(); + } + + private: + + //************************************************************************* + /// Allocate a Data_Node. + //************************************************************************* + Data_Node& allocate_data_node(value_type value) const + { + return *(p_node_pool->allocate(Data_Node(value))); + } + + //************************************************************************* + /// Destroy a Data_Node. + //************************************************************************* + void destroy_data_node(Data_Node& node) const + { + p_node_pool->release(&node); + } + + //************************************************************************* + /// Initialise the multiset. + //************************************************************************* + void initialise() + { + if (!empty()) + { + p_node_pool->release_all(); + } + + current_size = 0; + root_node = nullptr; + } + + //************************************************************************* + /// Attach the provided node to the position provided + //************************************************************************* + void attach_node(Node* parent, Node*& position, Data_Node& node) + { + // Mark new node as leaf on attach to tree at position provided + node.mark_as_leaf(); + + // Keep track of this node's parent + node.parent = parent; + + // Add the node here + position = &node; + + // One more. + ++current_size; + } + + //************************************************************************* + /// Balance the critical node at the position provided as needed + //************************************************************************* + void balance_node(Node*& critical_node) + { + // Step 1: Update weights for all children of the critical node up to the + // newly inserted node. This step is costly (in terms of traversing nodes + // multiple times during insertion) but doesn't require as much recursion + Node* weight_node = critical_node->children[critical_node->dir]; + while (weight_node) + { + // Keep going until we reach a terminal node (dir == kNeither) + if (kNeither != weight_node->dir) + { + // Does this insert balance the previous weight factor value? + if (weight_node->weight == 1 - weight_node->dir) + { + weight_node->weight = kNeither; + } + else + { + weight_node->weight = weight_node->dir; + } + + // Update weight factor node to point to next node + weight_node = weight_node->children[weight_node->dir]; + } + else + { + // Stop loop, terminal node found + break; + } + } // while(weight_node) + + // Step 2: Update weight for critical_node or rotate tree to balance node + if (kNeither == critical_node->weight) + { + critical_node->weight = critical_node->dir; + } + // If direction is different than weight, then it will now be balanced + else if (critical_node->dir != critical_node->weight) + { + critical_node->weight = kNeither; + } + // Rotate is required to balance the tree at the critical node + else + { + // If critical node matches child node direction then perform a two + // node rotate in the direction of the critical node + if (critical_node->weight == critical_node->children[critical_node->dir]->dir) + { + rotate_2node(critical_node, critical_node->dir); + } + // Otherwise perform a three node rotation in the direction of the + // critical node + else + { + rotate_3node(critical_node, critical_node->dir, + critical_node->children[critical_node->dir]->children[1 - critical_node->dir]->dir); + } + } + } + + //************************************************************************* + /// Count the nodes that match the key provided + //************************************************************************* + size_type count_nodes(const key_value_parameter_t& key) const + { + // Number of nodes that match the key provided result + size_type result = 0; + + // Find lower and upper nodes for the key provided + const Node* lower = find_lower_node(root_node, key); + const Node* upper = find_upper_node(root_node, key); + + // Loop from lower node to upper node and find nodes that match + while (lower != upper) + { + // Downcast found to Data_Node class for comparison and other operations + const Data_Node& data_node = imultiset::data_cast(*lower); + + if (!node_comp(key, data_node) && !node_comp(data_node, key)) + { + // This node matches the key provided + ++result; + } + + // Move on to the next node + next_node(lower); + } + + // Return the number of nodes that match + return result; + } + + //************************************************************************* + /// Detach the node at the position provided + //************************************************************************* + void detach_node(Node*& position, Node*& replacement) + { + // Make temporary copy of actual nodes involved because we might lose + // their references in the process (e.g. position is the same as + // replacement or replacement is a child of position) + Node* detached = position; + Node* swap = replacement; + + // Update current position to point to swap (replacement) node first + position = swap; + + // Update replacement node to point to child in opposite direction + // otherwise we might lose the other child of the swap node + replacement = swap->children[1 - swap->dir]; + + // Point swap node to detached node's parent, children and weight + swap->parent = detached->parent; + swap->children[kLeft] = detached->children[kLeft]; + swap->children[kRight] = detached->children[kRight]; + if (swap->children[kLeft]) + { + swap->children[kLeft]->parent = swap; + } + if (swap->children[kRight]) + { + swap->children[kRight]->parent = swap; + } + swap->weight = detached->weight; + } + + //************************************************************************* + /// Find the value matching the node provided + //************************************************************************* + Node* find_node(Node* position, const key_value_parameter_t& key) const + { + Node* found = nullptr; + while (position) + { + // Downcast found to Data_Node class for comparison and other operations + Data_Node& data_node = imultiset::data_cast(*position); + // Compare the node value to the current position value + if (node_comp(key, data_node)) + { + // Keep searching for the node on the left + position = position->children[kLeft]; + } + else if (node_comp(data_node, key)) + { + // Keep searching for the node on the right + position = position->children[kRight]; + } + else + { + // We found one, keep looking for more on the left + found = position; + position = position->children[kLeft]; + } + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the value matching the node provided + //************************************************************************* + const Node* find_node(const Node* position, const key_value_parameter_t& key) const + { + const Node* found = nullptr; + while (position) + { + // Downcast found to Data_Node class for comparison and other operations + const Data_Node& data_node = imultiset::data_cast(*position); + // Compare the node value to the current position value + if (node_comp(key, data_node)) + { + // Keep searching for the node on the left + position = position->children[kLeft]; + } + else if (node_comp(data_node, key)) + { + // Keep searching for the node on the right + position = position->children[kRight]; + } + else + { + // We found one, keep looking for more on the left + found = position; + position = position->children[kLeft]; + } + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the node whose key would go before all the other keys from the + /// position provided + //************************************************************************* + Node* find_limit_node(Node* position, const int8_t dir) const + { + // Something at this position and in the direction specified? keep going + Node* limit_node = position; + while (limit_node && limit_node->children[dir]) + { + limit_node = limit_node->children[dir]; + } + + // Return the limit node position found + return limit_node; + } + + //************************************************************************* + /// Find the node whose key is not considered to go before the key provided + //************************************************************************* + Node* find_lower_node(Node* position, const key_value_parameter_t& key) const + { + // Something at this position? keep going + Node* lower_node = nullptr; + while (position) + { + // Downcast lower node to Data_Node reference for key comparisons + Data_Node& data_node = imultiset::data_cast(*position); + // Compare the key value to the current lower node key value + if (node_comp(key, data_node)) + { + lower_node = position; + if (position->children[kLeft]) + { + position = position->children[kLeft]; + } + else + { + // Found lowest node + break; + } + } + else if (node_comp(data_node, key)) + { + position = position->children[kRight]; + } + else + { + // Make note of current position, but keep looking to left for more + lower_node = position; + position = position->children[kLeft]; + } + } + + // Return the lower_node position found + return lower_node; + } + + //************************************************************************* + /// Find the node whose key is considered to go after the key provided + //************************************************************************* + Node* find_upper_node(Node* position, const key_value_parameter_t& key) const + { + // Keep track of parent of last upper node + Node* upper_node = nullptr; + // Has an equal node been found? start with no + bool found = false; + while (position) + { + // Downcast position to Data_Node reference for key comparisons + Data_Node& data_node = imultiset::data_cast(*position); + // Compare the key value to the current upper node key value + if (node_comp(data_node, key)) + { + position = position->children[kRight]; + } + else if (node_comp(key, data_node)) + { + upper_node = position; + // If a node equal to key hasn't been found go left + if (!found && position->children[kLeft]) + { + position = position->children[kLeft]; + } + else + { + break; + } + } + else + { + // We found an equal item, break on next bigger item + found = true; + next_node(position); + } + } + + // Return the upper node position found (might be nullptr) + return upper_node; + } + + //************************************************************************* + /// Insert a node. + //************************************************************************* + Node* insert_node(Node*& position, Data_Node& node) + { + // Find the location where the node belongs + Node* found = position; + + // Was position provided not empty? then find where the node belongs + if (position) + { + // Find the critical parent node (default to nullptr) + Node* critical_parent_node = nullptr; + Node* critical_node = root_node; + + while (found) + { + // Search for critical weight node (all nodes whose weight factor + // is set to kNeither (balanced) + if (kNeither != found->weight) + { + critical_node = found; + } + + // Downcast found to Data_Node class for comparison and other operations + Data_Node& found_data_node = imultiset::data_cast(*found); + + // Is the node provided to the left of the current position? + if (node_comp(node, found_data_node)) + { + // Update direction taken to insert new node in parent node + found->dir = kLeft; + } + // Is the node provided to the right of the current position? + else if (node_comp(found_data_node, node)) + { + // Update direction taken to insert new node in parent node + found->dir = kRight; + } + else + { + // Update direction taken to insert new node in parent (and + // duplicate) node to the right. + found->dir = kRight; + } + + // Is there a child of this parent node? + if (found->children[found->dir]) + { + // Will this node be the parent of the next critical node whose + // weight factor is set to kNeither (balanced)? + if (kNeither != found->children[found->dir]->weight) + { + critical_parent_node = found; + } + + // Keep looking for empty spot to insert new node + found = found->children[found->dir]; + } + else + { + // Attach node as a child of the parent node found + attach_node(found, found->children[found->dir], node); + + // Return newly added node + found = found->children[found->dir]; + + // Exit loop + break; + } + } + + // Was a critical node found that should be checked for balance? + if (critical_node) + { + if (critical_parent_node == nullptr && critical_node == root_node) + { + balance_node(root_node); + } + else if (critical_parent_node == nullptr && critical_node == position) + { + balance_node(position); + } + else + { + balance_node(critical_parent_node->children[critical_parent_node->dir]); + } + } + } + else + { + // Attatch node to current position (which is assumed to be root) + attach_node(nullptr, position, node); + + // Return newly added node at current position + found = position; + } + + // Return the node found (might be nullptr) + return found; + } + + //************************************************************************* + /// Find the next node in sequence from the node provided + //************************************************************************* + void next_node(Node*& position) const + { + if (position) + { + // Is there a tree on the right? then find the minimum of that tree + if (position->children[kRight]) + { + // Return minimum node found + position = find_limit_node(position->children[kRight], kLeft); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; // find_parent_node(root_node, position); + // Repeat while previous position was on right side of parent tree + } while (parent && parent->children[kRight] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the next node in sequence from the node provided + //************************************************************************* + void next_node(const Node*& position) const + { + if (position) + { + // Is there a tree on the right? then find the minimum of that tree + if (position->children[kRight]) + { + // Return minimum node found + position = find_limit_node(position->children[kRight], kLeft); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + const Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on right side of parent tree + } while (parent && parent->children[kRight] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the previous node in sequence from the node provided + //************************************************************************* + void prev_node(Node*& position) const + { + // If starting at the terminal end, the previous node is the maximum node + // from the root + if (!position) + { + position = find_limit_node(root_node, kRight); + } + else + { + // Is there a tree on the left? then find the maximum of that tree + if (position->children[kLeft]) + { + // Return maximum node found + position = find_limit_node(position->children[kLeft], kRight); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on left side of parent tree + } while (parent && parent->children[kLeft] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Find the previous node in sequence from the node provided + //************************************************************************* + void prev_node(const Node*& position) const + { + // If starting at the terminal end, the previous node is the maximum node + // from the root + if (!position) + { + position = find_limit_node(root_node, kRight); + } + else + { + // Is there a tree on the left? then find the maximum of that tree + if (position->children[kLeft]) + { + // Return maximum node found + position = find_limit_node(position->children[kLeft], kRight); + } + // Otherwise find the parent of this node + else + { + // Start with current position as parent + const Node* parent = position; + do { + // Update current position as previous parent + position = parent; + // Find parent of current position + parent = position->parent; + // Repeat while previous position was on left side of parent tree + } while (parent && parent->children[kLeft] == position); + + // Set parent node as the next position + position = parent; + } + } + } + + //************************************************************************* + /// Remove the node specified from somewhere starting at the position + /// provided + //************************************************************************* + void remove_node(Node* node) + { + // If valid found node was provided then proceed with steps 1 through 5 + if (node) + { + // Downcast found node provided to Data_Node class + Data_Node& data_node = imultiset::data_cast(*node); + + // Keep track of node as found node + Node* found = node; + + // Step 1: Mark path from node provided back to the root node using the + // internal temporary dir member value and using the parent pointer. This + // will allow us to avoid recursion in finding the node in a tree that + //might contain duplicate keys to be found. + while (node) + { + if (node->parent) + { + // Which direction does parent use to get to this node? + node->parent->dir = + node->parent->children[kLeft] == node ? kLeft : kRight; + + // Make this nodes parent the next node + node = node->parent; + } + else + { + // Root node found - break loop + break; + } + } + + // Step 2: Follow the path provided above until we reach the node + // provided and look for the balance node to start rebalancing the tree + // from (up to the replacement node that will be found in step 3) + Node* balance = root_node; + while (node) + { + // Did we reach the node provided originally (found) then go to step 3 + if (node == found) + { + // Update the direction towards a replacement node at the found node + node->dir = node->children[kLeft] ? kLeft : kRight; + + // Exit loop and proceed with step 3 + break; + } + else + { + // If this nodes weight is kNeither or we are taking the shorter path + // to the next node and our sibling (on longer path) is balanced then + // we need to update the balance node to this node but all our + // ancestors will not require rebalancing + if ((node->weight == kNeither) || + (node->weight == (1 - node->dir) && + node->children[1 - node->dir]->weight == kNeither)) + { + // Update balance node to this node + balance = node; + } + + // Keep searching for found in the direction provided in step 1 + node = node->children[node->dir]; + } + } + // The value for node should not be nullptr at this point otherwise + // step 1 failed to provide the correct path to found. Step 5 will fail + // (probably subtly) if node should be nullptr at this point + + // Step 3: Find the node (node should be equal to found at this point) + // to replace found with (might end up equal to found) while also + // continuing to update balance the same as in step 2 above. + while (node) + { + // Replacement node found if its missing a child in the replace->dir + // value set at the end of step 2 above + if (node->children[node->dir] == nullptr) + { + // Exit loop once node to replace found is determined + break; + } + + // If this nodes weight is kNeither or we are taking the shorter path + // to the next node and our sibling (on longer path) is balanced then + // we need to update the balance node to this node but all our + // ancestors will not require rebalancing + if ((node->weight == kNeither) || + (node->weight == (1 - node->dir) && + node->children[1 - node->dir]->weight == kNeither)) + { + // Update balance node to this node + balance = node; + } + + // Keep searching for replacement node in the direction specified above + node = node->children[node->dir]; + + // Downcast node to Data_Node class for comparison operations + Data_Node& replace_data_node = imultiset::data_cast(*node); + + // Compare the key provided to the replace data node key + if (node_comp(data_node, replace_data_node)) + { + // Update the direction to the replace node + node->dir = kLeft; + } + else if (node_comp(replace_data_node, data_node)) + { + // Update the direction to the replace node + node->dir = kRight; + } + else + { + // Update the direction to the replace node + node->dir = node->children[kLeft] ? kLeft : kRight; + } + } // while(node) + + // Step 4: Update weights from balance to parent of node determined + // in step 3 above rotating (2 or 3 node rotations) as needed. + while (balance) + { + // Break when balance node reaches the parent of replacement node + if (balance->children[balance->dir] == nullptr) + { + break; + } + + // If balance node is balanced already (kNeither) then just imbalance + // the node in the opposite direction of the node being removed + if (balance->weight == kNeither) + { + balance->weight = 1 - balance->dir; + } + // If balance node is imbalanced in the opposite direction of the + // node being removed then the node now becomes balanced + else if (balance->weight == balance->dir) + { + balance->weight = kNeither; + } + // Otherwise a rotation is required at this node + else + { + int weight = balance->children[1 - balance->dir]->weight; + // Perform a 3 node rotation if weight is same as balance->dir + if (weight == balance->dir) + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_3node(root_node, 1 - balance->dir, + balance->children[1 - balance->dir]->children[balance->dir]->weight); + } + else + { + rotate_3node(balance->parent->children[balance->parent->dir], 1 - balance->dir, + balance->children[1 - balance->dir]->children[balance->dir]->weight); + } + } + // Already balanced, rebalance and make it heavy in opposite + // direction of the node being removed + else if (weight == kNeither) + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_2node(root_node, 1 - balance->dir); + root_node->weight = balance->dir; + } + else + { + // Balance parent might change during rotate, keep local copy + // to old parent so its weight can be updated after the 2 node + // rotate is completed + Node* old_parent = balance->parent; + rotate_2node(balance->parent->children[balance->parent->dir], 1 - balance->dir); + old_parent->children[old_parent->dir]->weight = balance->dir; + } + // Update balance node weight in opposite direction of node removed + balance->weight = 1 - balance->dir; + } + // Rebalance and leave it balanced + else + { + // Is the root node being rebalanced (no parent) + if (balance->parent == nullptr) + { + rotate_2node(root_node, 1 - balance->dir); + } + else + { + rotate_2node(balance->parent->children[balance->parent->dir], 1 - balance->dir); + } + } + } + + // Next balance node to consider + balance = balance->children[balance->dir]; + } // while(balance) + + // Step 5: Swap found with node (replacement) + if (found->parent) + { + // Handle traditional case + detach_node(found->parent->children[found->parent->dir], + node->parent->children[node->parent->dir]); + } + // Handle root node removal + else + { + // Valid replacement node for root node being removed? + if (node->parent) + { + detach_node(root_node, node->parent->children[node->parent->dir]); + } + else + { + // Found node and replacement node are both root node + detach_node(root_node, root_node); + } + } + + // One less. + --current_size; + + // Destroy the node detached above + destroy_data_node(data_node); + } // if(found) + } + + //************************************************************************* + /// Rotate two nodes at the position provided the to balance the tree + //************************************************************************* + void rotate_2node(Node*& position, uint8_t dir) + { + // A C A B + // B C -> A E OR B C -> D A + // D E B D D E E C + // C (new position) becomes the root + // A (position) takes ownership of D as its children[kRight] child + // C (new position) takes ownership of A as its left child + // OR + // B (new position) becomes the root + // A (position) takes ownership of E as its left child + // B (new position) takes ownership of A as its right child + + // Capture new root (either B or C depending on dir) and its parent + Node* new_root = position->children[dir]; + + // Replace position's previous child with new root's other child + position->children[dir] = new_root->children[1 - dir]; + // Update new root's other child parent pointer + if (position->children[dir]) + { + position->children[dir]->parent = position; + } + + // New root's parent becomes current position's parent + new_root->parent = position->parent; + new_root->children[1 - dir] = position; + new_root->dir = 1 - dir; + + // Clear weight factor from current position + position->weight = kNeither; + // Position's parent becomes new_root + position->parent = new_root; + position = new_root; + // Clear weight factor from new root + position->weight = kNeither; + } + + //************************************************************************* + /// Rotate three nodes at the position provided the to balance the tree + //************************************************************************* + void rotate_3node(Node*& position, uint8_t dir, uint8_t third) + { + // __A__ __E__ __A__ __D__ + // _B_ C -> B A OR B _C_ -> A C + // D E D F G C D E B F G E + // F G F G + // E (new position) becomes the root + // B (position) takes ownership of F as its left child + // A takes ownership of G as its right child + // OR + // D (new position) becomes the root + // A (position) takes ownership of F as its right child + // C takes ownership of G as its left child + + // Capture new root (either E or D depending on dir) + Node* new_root = position->children[dir]->children[1 - dir]; + // Set weight factor for B or C based on F or G existing and being a different than dir + position->children[dir]->weight = third != kNeither && third != dir ? dir : kNeither; + + // Detach new root from its tree (replace with new roots child) + position->children[dir]->children[1 - dir] = new_root->children[dir]; + // Update new roots child parent pointer + if (new_root->children[dir]) + { + new_root->children[dir]->parent = position->children[dir]; + } + + // Attach current left tree to new root and update its parent + new_root->children[dir] = position->children[dir]; + position->children[dir]->parent = new_root; + + // Set weight factor for A based on F or G + position->weight = third != kNeither && third == dir ? 1 - dir : kNeither; + + // Move new root's right tree to current roots left tree + position->children[dir] = new_root->children[1 - dir]; + if (new_root->children[1 - dir]) + { + new_root->children[1 - dir]->parent = position; + } + + // Attach current root to new roots right tree and assume its parent + new_root->parent = position->parent; + new_root->children[1 - dir] = position; + new_root->dir = 1 - dir; + + // Update current position's parent and replace with new root + position->parent = new_root; + position = new_root; + // Clear weight factor for new current position + position->weight = kNeither; + } + + }; +} + +//*************************************************************************** +/// Equal operator. +///\param lhs Reference to the first lookup. +///\param rhs Reference to the second lookup. +///\return true if the arrays are equal, otherwise false +///\ingroup lookup +//*************************************************************************** +template +bool operator ==(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +//*************************************************************************** +/// Not equal operator. +///\param lhs Reference to the first lookup. +///\param rhs Reference to the second lookup. +///\return true if the arrays are not equal, otherwise false +///\ingroup lookup +//*************************************************************************** +template +bool operator !=(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return !(lhs == rhs); +} + +//************************************************************************* +/// Less than operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically less than the +/// second, otherwise false. +//************************************************************************* +template +bool operator <(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end()); +} + +//************************************************************************* +/// Greater than operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically greater than the +/// second, otherwise false. +//************************************************************************* +template +bool operator >(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return std::lexicographical_compare(lhs.begin(), + lhs.end(), + rhs.begin(), + rhs.end(), + std::greater()); +} + +//************************************************************************* +/// Less than or equal operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically less than or equal +/// to the second, otherwise false. +//************************************************************************* +template +bool operator <=(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return !operator >(lhs, rhs); +} + +//************************************************************************* +/// Greater than or equal operator. +///\param lhs Reference to the first list. +///\param rhs Reference to the second list. +///\return true if the first list is lexicographically greater than or +/// equal to the second, otherwise false. +//************************************************************************* +template +bool operator >=(const etl::imultiset& lhs, const etl::imultiset& rhs) +{ + return !operator <(lhs, rhs); +} + +#if WIN32 +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#undef __ETL_IN_IMULTISET_H__ + +#endif diff --git a/ipriority_queue.h b/ipriority_queue.h new file mode 100644 index 000000000..fc7ef7fb5 --- /dev/null +++ b/ipriority_queue.h @@ -0,0 +1,220 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2015 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IPRIORITY_QUEUE__ +#define __ETL_IPRIORITY_QUEUE__ +#define __ETL_IN_IPRIORITY_QUEUE_H__ + +#include +#include + +#include "priority_queue_base.h" +#include "type_traits.h" +#include "parameter_type.h" + +#ifndef ETL_THROW_EXCEPTIONS +#include "error_handler.h" +#endif + +namespace etl +{ + //*************************************************************************** + ///\ingroup queue + ///\brief This is the base for all priority queues that contain a particular type. + ///\details Normally a reference to this type will be taken from a derived queue. + /// The TContainer specified must provide the front, push_back, pop_back, and + /// assign methods to work correctly with priority_queue. + ///\code + /// etl::priority_queue myPriorityQueue; + /// etl::ipriority_queue& iQueue = myPriorityQueue; + ///\endcode + /// \warning This priority queue cannot be used for concurrent access from + /// multiple threads. + /// \tparam T The type of value that the queue holds. + /// \tparam TContainer to hold the T queue values + /// \tparam TCompare to use in comparing T values + //*************************************************************************** + template + class ipriority_queue : public priority_queue_base + { + public: + + typedef T value_type; ///< The type stored in the queue. + typedef TContainer container_type; ///< The container type used for priority queue. + typedef T& reference; ///< A reference to the type used in the queue. + typedef const T& const_reference; ///< A const reference to the type used in the queue. + typedef priority_queue_base::size_type size_type; ///< The type used for determining the size of the queue. + typedef typename std::iterator_traits::difference_type difference_type; + + private: + + typedef typename parameter_type::type parameter_t; + + public: + + //************************************************************************* + /// Gets a reference to the highest priority value in the priority queue.
+ /// \return A reference to the highest priority value in the priority queue. + //************************************************************************* + reference top() + { + return container.front(); + } + + //************************************************************************* + /// Gets a const reference to the highest priority value in the priority queue.
+ /// \return A const reference to the highest priority value in the priority queue. + //************************************************************************* + const_reference top() const + { + return container.front(); + } + + //************************************************************************* + /// Adds a value to the queue. + /// If ETL_THROW_EXCEPTIONS is defined, throws an etl::priority_queue_full + /// is the priority queue is already full, otherwise does nothing if full. + ///\param value The value to push to the queue. + //************************************************************************* + void push(parameter_t value) + { + if (!full()) + { + // Put element at end + container.push_back(value); + // Pre-increment size + ++current_size; + // Make elements in container into heap + std::push_heap(container.begin(), container.end(), TCompare()); + } + else +#ifdef ETL_THROW_EXCEPTIONS + { + throw priority_queue_full(); + } +#else + { + error_handler::error(priority_queue_full()); + } +#endif + } + + //************************************************************************* + /// Clears the queue to the empty state. + //************************************************************************* + void clear() + { + container.clear(); + current_size = 0; + } + + //********************************************************************* + /// Assigns values to the priority queue. + /// If ETL_THROW_EXCEPTIONS is defined, emits priority_queue_full if + /// priority queue does not have enough free space. + /// If ETL_THROW_EXCEPTIONS is defined, emits priority_iterator if the + /// iterators are reversed. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //********************************************************************* + template + void assign(TIterator first, TIterator last) + { +#ifdef _DEBUG + difference_type count = std::distance(first, last); + + if (count < 0) + { +#ifdef ETL_THROW_EXCEPTIONS + throw priority_queue_iterator(); +#else + error_handler::error(priority_queue_iterator()); +#endif + } + + if (static_cast(count) > MAX_SIZE) + { +#ifdef ETL_THROW_EXCEPTIONS + throw priority_queue_full(); +#else + error_handler::error(priority_queue_full()); +#endif + } +#endif + + clear(); + container.assign(first, last); + std::make_heap(container.begin(), container.end(), TCompare()); + current_size = container.size(); + } + + //************************************************************************* + /// Removes the oldest value from the back of the priority queue. + /// Does nothing if the priority queue is already empty. + //************************************************************************* + void pop() + { + if (!empty()) + { + // Move largest element to end + std::pop_heap(container.begin(), container.end(), TCompare()); + // Actually remove largest element at end + container.pop_back(); + // Decrement size + --current_size; + } + } + + protected: + + //************************************************************************* + /// Make this a clone of the supplied priority queue + //************************************************************************* + void clone(const ipriority_queue& other) + { + assign(other.container.cbegin(), other.container.cend()); + } + + //************************************************************************* + /// The constructor that is called from derived classes. + //************************************************************************* + ipriority_queue(size_type max_size) + : priority_queue_base(max_size) + { + } + + private: + + /// The container specified at instantiation of the priority_queue + TContainer container; + }; +} + +#undef __ETL_IN_IPRIORITY_QUEUE_H__ +#endif diff --git a/iqueue.h b/iqueue.h index c516eb325..d38aabb65 100644 --- a/iqueue.h +++ b/iqueue.h @@ -63,7 +63,7 @@ namespace etl typedef T& reference; ///< A reference to the type used in the queue. typedef const T& const_reference; ///< A const reference to the type used in the queue. typedef T* pointer; ///< A pointer to the type used in the queue. - typedef const T* const_pointer; ///< A const pointer to the type used in the qu + typedef const T* const_pointer; ///< A const pointer to the type used in the queue. typedef queue_base::size_type size_type; ///< The type used for determining the size of the queue. private: diff --git a/iset.h b/iset.h index 56e777b93..6e584b34a 100644 --- a/iset.h +++ b/iset.h @@ -4,6 +4,7 @@ The MIT License(MIT) Embedded Template Library. +https://github.com/ETLCPP/etl Copyright(c) 2014 jwellbelove, rlindeman @@ -134,7 +135,7 @@ namespace etl value_type value; }; - /// Defines the key parameter type + /// Defines the key value parameter type typedef typename parameter_type::type key_value_parameter_t; //************************************************************************* @@ -299,9 +300,9 @@ namespace etl }; friend iterator; - ////************************************************************************* - ///// const_iterator - ////************************************************************************* + //************************************************************************* + /// const_iterator + //************************************************************************* class const_iterator : public std::iterator { public: @@ -695,7 +696,7 @@ namespace etl ///\param position The position that would precede the value to insert. ///\param value The value to insert. //********************************************************************* - iterator insert(iterator position, value_type& value) + iterator insert(iterator, value_type& value) { // Default to no inserted node Node* inserted_node = nullptr; @@ -706,7 +707,7 @@ namespace etl Data_Node& node = allocate_data_node(value); // Obtain the inserted node (might be nullptr if node was a duplicate) - inserted_node = insert_node(find_node(root_node, position.p_node), node); + inserted_node = insert_node(root_node, node); } else { @@ -727,7 +728,7 @@ namespace etl ///\param position The position that would precede the value to insert. ///\param value The value to insert. //********************************************************************* - iterator insert(const_iterator position, value_type& value) + iterator insert(const_iterator, value_type& value) { // Default to no inserted node Node* inserted_node = nullptr; @@ -738,7 +739,7 @@ namespace etl Data_Node& node = allocate_data_node(value); // Obtain the inserted node (might be nullptr if node was a duplicate) - inserted_node = insert_node(find_node(root_node, position.p_node), node); + inserted_node = insert_node(root_node, node); } else { @@ -1067,7 +1068,7 @@ namespace etl } } - // Return root node if nothing or duplicate was found + // Return root node if nothing was found return root_node; } @@ -1240,36 +1241,6 @@ namespace etl return lower_node; } - //************************************************************************* - /// Find the node whose key is not considered to go before the key provided - //************************************************************************* - const Node& find_lower_node(const Node* position, const key_value_parameter_t& key) const - { - // Something at this position? keep going - const Node* lower_node = position; - while (lower_node) - { - // Downcast lower node to Data_Node reference for key comparisons - const Data_Node& data_node = iset::data_cast(*lower_node); - // Compare the key value to the current lower node key value - if (node_comp(key, data_node)) - { - lower_node = lower_node->children[kLeft]; - } - else if (node_comp(data_node, key)) - { - lower_node = lower_node->children[kRight]; - } - else - { - break; - } - } - - // Return the lower_node position found - return lower_node; - } - //************************************************************************* /// Find the node whose key is considered to go after the key provided //************************************************************************* @@ -1308,44 +1279,6 @@ namespace etl return upper_node; } - //************************************************************************* - /// Find the node whose key is considered to go after the key provided - //************************************************************************* - const Node* find_upper_node(const Node* position, const key_value_parameter_t& key) const - { - // Keep track of parent of last upper node - const Node* upper_node = nullptr; - // Start with position provided - const Node* node = position; - while (node) - { - // Downcast position to Data_Node reference for key comparisons - const Data_Node& data_node = iset::data_cast(*node); - // Compare the key value to the current upper node key value - if (node_comp(key, data_node)) - { - upper_node = node; - node = node->children[kLeft]; - } - else if (node_comp(data_node, key)) - { - node = node->children[kRight]; - } - else if (node->children[kRight]) - { - upper_node = find_limit_node(node->children[kRight], kLeft); - break; - } - else - { - break; - } - } - - // Return the upper node position found (might be nullptr) - return upper_node; - } - //************************************************************************* /// Insert a node. //************************************************************************* diff --git a/map_base.h b/map_base.h index d83d054be..411d7d0ed 100644 --- a/map_base.h +++ b/map_base.h @@ -27,8 +27,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ -#ifndef __ETL_IN_IMAP_H__ -#error This header is a private element of etl::map & etl::imap +#if !defined(__ETL_IN_IMAP_H__) && !defined(__ETL_IN_IMULTIMAP_H__) +#error This header is a private element of etl::map, etl::multimap & etl::imap, etl::imultimap #endif #ifndef __ETL_MAP_BASE__ diff --git a/multimap.h b/multimap.h new file mode 100644 index 000000000..a76ecf5a3 --- /dev/null +++ b/multimap.h @@ -0,0 +1,110 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_MULTIMAP__ +#define __ETL_MULTIMAP__ + +#include +#include +#include + +#include "imultimap.h" +#include "container.h" +#include "pool.h" + +//***************************************************************************** +/// A multimap with the capacity defined at compile time. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //************************************************************************* + /// A templated multimap implementation that uses a fixed size buffer. + //************************************************************************* + template > + class multimap : public imultimap + { + public: + + static const size_t MAX_SIZE = MAX_SIZE_; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + multimap() + : imultimap(node_pool, MAX_SIZE) + { + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + explicit multimap(const multimap& other) + : imultimap(node_pool, MAX_SIZE) + { + imultimap::assign(other.cbegin(), other.cend()); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + multimap(TIterator first, TIterator last) + : imultimap(node_pool, MAX_SIZE) + { + imultimap::insert(first, last); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + multimap& operator = (const multimap& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + imultimap::assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + private: + + /// The pool of data nodes used for the multimap. + pool::Data_Node, MAX_SIZE> node_pool; + }; + +} + +#endif diff --git a/multiset.h b/multiset.h new file mode 100644 index 000000000..cddcda2a1 --- /dev/null +++ b/multiset.h @@ -0,0 +1,110 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_MULTISET__ +#define __ETL_MULTISET__ + +#include +#include +#include + +#include "imultiset.h" +#include "container.h" +#include "pool.h" + +//***************************************************************************** +/// A multiset with the capacity defined at compile time. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //************************************************************************* + /// A templated multiset implementation that uses a fixed size buffer. + //************************************************************************* + template > + class multiset : public imultiset + { + public: + + static const size_t MAX_SIZE = MAX_SIZE_; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + multiset() + : imultiset(node_pool, MAX_SIZE) + { + } + + //************************************************************************* + /// Copy constructor. + //************************************************************************* + explicit multiset(const multiset& other) + : imultiset(node_pool, MAX_SIZE) + { + imultiset::assign(other.cbegin(), other.cend()); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + multiset(TIterator first, TIterator last) + : imultiset(node_pool, MAX_SIZE) + { + imultiset::insert(first, last); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + multiset& operator = (const multiset& rhs) + { + // Skip if doing self assignment + if (this != &rhs) + { + imultiset::assign(rhs.cbegin(), rhs.cend()); + } + + return *this; + } + + private: + + /// The pool of data nodes used for the multiset. + pool::Data_Node, MAX_SIZE> node_pool; + }; + +} + +#endif diff --git a/priority_queue.h b/priority_queue.h new file mode 100644 index 000000000..79bee35f8 --- /dev/null +++ b/priority_queue.h @@ -0,0 +1,114 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2015 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_PRIORITY_QUEUE__ +#define __ETL_PRIORITY_QUEUE__ + +#include +#include + +#include "ipriority_queue.h" +#include "container.h" +#include "vector.h" + +//***************************************************************************** +///\defgroup queue queue +/// A priority queue with the capacity defined at compile time, +/// written in the STL style. +///\ingroup containers +//***************************************************************************** + +namespace etl +{ + //*************************************************************************** + ///\ingroup queue + /// A fixed capacity queue. + /// This queue does not support concurrent access by different threads. + /// \tparam T The type this queue should support. + /// \tparam SIZE The maximum capacity of the queue. + //*************************************************************************** + template , typename TCompare = std::less > + class priority_queue : public ipriority_queue + { + public: + + //************************************************************************* + /// Default constructor. + //************************************************************************* + priority_queue() + : ipriority_queue(SIZE) + { + } + + //************************************************************************* + /// Copy constructor + //************************************************************************* + priority_queue(const priority_queue& rhs) + : ipriority_queue(SIZE) + { + ipriority_queue::clone(rhs); + } + + //************************************************************************* + /// Constructor, from an iterator range. + ///\tparam TIterator The iterator type. + ///\param first The iterator to the first element. + ///\param last The iterator to the last element + 1. + //************************************************************************* + template + priority_queue(TIterator first, TIterator last) + : ipriority_queue(SIZE) + { + ipriority_queue::assign(first, last); + } + + //************************************************************************* + /// Destructor. + //************************************************************************* + ~priority_queue() + { + ipriority_queue::clear(); + } + + //************************************************************************* + /// Assignment operator. + //************************************************************************* + priority_queue& operator = (const priority_queue& rhs) + { + if (&rhs != this) + { + ipriority_queue::clone(rhs); + } + + return *this; + } + }; +} + +#endif diff --git a/priority_queue_base.h b/priority_queue_base.h new file mode 100644 index 000000000..ac441c5a0 --- /dev/null +++ b/priority_queue_base.h @@ -0,0 +1,154 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2015 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef __ETL_IN_IPRIORITY_QUEUE_H__ +#error This header is a private element of etl::priority_queue & etl::ipriority_queue +#endif + +#ifndef __ETL_PRIORITY_QUEUE_BASE__ +#define __ETL_PRIORITY_QUEUE_BASE__ + +#include + +#include "exception.h" + +namespace etl +{ + //*************************************************************************** + /// The base class for priority_queue exceptions. + ///\ingroup queue + //*************************************************************************** + class priority_queue_exception : public exception + { + public: + + priority_queue_exception(const char* what) + : exception(what) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the queue is full. + ///\ingroup queue + //*************************************************************************** + class priority_queue_full : public priority_queue_exception + { + public: + + priority_queue_full() + : priority_queue_exception("priority_queue: full") + { + } + }; + + //*************************************************************************** + /// The priority queue iterator exception on reversed iterators + ///\ingroup queue + //*************************************************************************** + class priority_queue_iterator : public priority_queue_exception + { + public: + + priority_queue_iterator() + : priority_queue_exception("priority_queue: iterator error") + { + } + }; + + //*************************************************************************** + /// The base class for all priority queues. + ///\ingroup queue + //*************************************************************************** + class priority_queue_base + { + public: + + typedef size_t size_type; ///< The type used for determining the size of queue. + + //************************************************************************* + /// Returns the current number of items in the priority queue. + //************************************************************************* + size_type size() const + { + return current_size; + } + + //************************************************************************* + /// Returns the maximum number of items that can be queued. + //************************************************************************* + size_type max_size() const + { + return MAX_SIZE; + } + + //************************************************************************* + /// Checks to see if the priority queue is empty. + /// \return true if the queue is empty, otherwise false + //************************************************************************* + bool empty() const + { + return current_size == 0; + } + + //************************************************************************* + /// Checks to see if the priority queue is full. + /// \return true if the priority queue is full, otherwise false + //************************************************************************* + bool full() const + { + return current_size == MAX_SIZE; + } + + //************************************************************************* + /// Returns the remaining capacity. + ///\return The remaining capacity. + //************************************************************************* + size_t available() const + { + return max_size() - size(); + } + + protected: + + //************************************************************************* + /// The constructor that is called from derived classes. + //************************************************************************* + priority_queue_base(size_type max_size) + : current_size(0), + MAX_SIZE(max_size) + { + } + + size_type current_size; ///< The number of items in the priority queue. + const size_type MAX_SIZE; ///< The maximum number of items in the priority queue. + }; +} + +#endif diff --git a/queue.h b/queue.h index 39baf2c6b..806ee7309 100644 --- a/queue.h +++ b/queue.h @@ -99,7 +99,7 @@ namespace etl private: - /// The unititialised buffer of T used in the stack. + /// The uninitialised buffer of T used in the stack. typename etl::aligned_storage::value>::type buffer[SIZE]; }; } diff --git a/set.h b/set.h index 597c72d4e..ea74819ee 100644 --- a/set.h +++ b/set.h @@ -4,6 +4,7 @@ The MIT License(MIT) Embedded Template Library. +https://github.com/ETLCPP/etl Copyright(c) 2014 jwellbelove, rlindeman diff --git a/set_base.h b/set_base.h index fe334f7a5..f33d3fe62 100644 --- a/set_base.h +++ b/set_base.h @@ -4,6 +4,7 @@ The MIT License(MIT) Embedded Template Library. +https://github.com/ETLCPP/etl Copyright(c) 2014 jwellbelove, rlindeman @@ -26,8 +27,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ -#ifndef __ETL_IN_ISET_H__ -#error This header is a private element of etl::set & etl::iset +#if !defined(__ETL_IN_ISET_H__) && !defined(__ETL_IN_IMULTISET_H__) +#error This header is a private element of etl::set, etl::multiset & etl::iset, etl::imultiset #endif #ifndef __ETL_SET_BASE__ diff --git a/test/test_map.cpp b/test/test_map.cpp index d0af7b2d0..b39b10ff4 100644 --- a/test/test_map.cpp +++ b/test/test_map.cpp @@ -35,8 +35,6 @@ SOFTWARE. #include #include -#include - #include "../map.h" static const size_t SIZE = 10; @@ -478,7 +476,6 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::map_full); } - //************************************************************************* TEST_FIXTURE(SetupFixture, test_equal_range) { diff --git a/test/test_multimap.cpp b/test/test_multimap.cpp new file mode 100644 index 000000000..5194384e4 --- /dev/null +++ b/test/test_multimap.cpp @@ -0,0 +1,949 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../multimap.h" + +static const size_t SIZE = 10; + +#define TEST_GREATER_THAN +#ifdef TEST_GREATER_THAN +typedef etl::multimap > Data; +typedef std::multimap > Compare_Data; +#else +typedef etl::multimap > Data; +typedef std::multimap > Compare_Data; +#endif + +typedef Data::iterator Data_iterator; +typedef Data::const_iterator Data_const_iterator; +typedef Compare_Data::iterator Compare_Data_iterator; +typedef Compare_Data::const_iterator Compare_Data_const_iterator; + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Data_iterator& it) +{ + os << (*it).first << " " << (*it).second; + + return os; +} + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Data_const_iterator& it) +{ + os << (*it).first << " " << (*it).second; + + return os; +} + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Compare_Data_iterator& it) +{ + os << (*it).first << " " << (*it).second; + + return os; +} + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Compare_Data_const_iterator& it) +{ + os << (*it).first << " " << (*it).second; + + return os; +} + +namespace +{ + SUITE(test_multimap) + { + //************************************************************************* + template + bool Check_Equal(T1 begin1, T1 end1, T2 begin2) + { + while (begin1 != end1) + { + if ((begin1->first != begin2->first) || (begin1->second != begin2->second)) + { + return false; + } + + ++begin1; + ++begin2; + } + + return true; + } + + //************************************************************************* + struct SetupFixture + { + // Multimaps of predefined data from which to constuct multimaps used in + // each test + std::multimap initial_data; + std::multimap excess_data; + std::multimap different_data; + std::multimap random_data; + + SetupFixture() + { + // Create a map of initial data + initial_data.insert(std::pair("0", 0)); + initial_data.insert(std::pair("0", 1)); + initial_data.insert(std::pair("1", 2)); + initial_data.insert(std::pair("1", 3)); + initial_data.insert(std::pair("1", 4)); + initial_data.insert(std::pair("2", 5)); + initial_data.insert(std::pair("2", 8)); + initial_data.insert(std::pair("2", 7)); + initial_data.insert(std::pair("3", 6)); + initial_data.insert(std::pair("4", 9)); + + // Create a multimap of excess data + excess_data.insert(std::pair("0", 0)); + excess_data.insert(std::pair("1", 1)); + excess_data.insert(std::pair("2", 2)); + excess_data.insert(std::pair("2", 3)); + excess_data.insert(std::pair("3", 6)); + excess_data.insert(std::pair("3", 5)); + excess_data.insert(std::pair("3", 4)); + excess_data.insert(std::pair("4", 7)); + excess_data.insert(std::pair("5", 8)); + excess_data.insert(std::pair("6", 9)); + excess_data.insert(std::pair("10", 10)); + + // Create a multimap of different data + different_data.insert(std::pair("10", 10)); + different_data.insert(std::pair("11", 11)); + different_data.insert(std::pair("12", 12)); + different_data.insert(std::pair("13", 13)); + different_data.insert(std::pair("14", 14)); + different_data.insert(std::pair("15", 15)); + different_data.insert(std::pair("16", 16)); + different_data.insert(std::pair("17", 17)); + different_data.insert(std::pair("18", 18)); + different_data.insert(std::pair("19", 19)); + + // Create a multimap of random data + random_data.insert(std::pair("3", 6)); + random_data.insert(std::pair("3", 5)); + random_data.insert(std::pair("0", 0)); + random_data.insert(std::pair("5", 8)); + random_data.insert(std::pair("6", 9)); + random_data.insert(std::pair("2", 2)); + random_data.insert(std::pair("0", 1)); + random_data.insert(std::pair("2", 3)); + random_data.insert(std::pair("4", 7)); + random_data.insert(std::pair("3", 4)); + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + Data data; + + CHECK_EQUAL(data.size(), size_t(0)); + CHECK(data.empty()); + CHECK_EQUAL(data.capacity(), SIZE); + CHECK_EQUAL(data.max_size(), SIZE); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + CHECK(data.size() == SIZE); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment) + { + Data data(initial_data.begin(), initial_data.end()); + Data otherData; + + otherData = data; + + bool isEqual = Check_Equal(data.begin(), + data.end(), + otherData.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_begin) + { + Data data(initial_data.begin(), initial_data.end()); + const Data constData(data); + + CHECK_EQUAL(data.begin(), std::begin(data)); + CHECK_EQUAL(constData.begin(), std::begin(constData)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_end) + { + Data data(initial_data.begin(), initial_data.end()); + const Data constData(data); + + CHECK_EQUAL(data.end(), std::end(data)); + CHECK_EQUAL(constData.end(), std::end(constData)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty) + { + Data data; + data.insert(initial_data.begin(), initial_data.end()); + + CHECK(data.full()); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_full) + { + Data data; + + CHECK(!data.full()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data; + + data.assign(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = + data.insert(Data::value_type(std::string("2"), 1)); + Compare_Data::iterator compare_result = + compare_data.insert(std::make_pair(std::string("2"), 1)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + data_result = data.insert(Data::value_type(std::string("1"), 1)); + compare_result = compare_data.insert(std::make_pair(std::string("1"), 1)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + data_result = data.insert(Data::value_type(std::string("2"), 2)); + compare_result = compare_data.insert(std::make_pair(std::string("2"), 2)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + data_result = data.insert(Data::value_type(std::string("3"), 3)); + compare_result = compare_data.insert(std::make_pair(std::string("3"), 3)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + // Adding this next 2 will trigger a 3 node rotate RL on insert + data_result = data.insert(Data::value_type(std::string("2"), 3)); + compare_result = compare_data.insert(std::make_pair(std::string("2"), 3)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + // Adding this next 4 will trigger a 2 node rotate left on insert + data_result = data.insert(Data::value_type(std::string("4"), 4)); + compare_result = compare_data.insert(std::make_pair(std::string("4"), 4)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + data_result = data.insert(Data::value_type(std::string("0"), 0)); + compare_result = compare_data.insert(std::make_pair(std::string("0"), 0)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + // Check that elements in map are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_hint_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = + data.insert(Data::value_type(std::string("0"), 0)); + Compare_Data::iterator compare_result = + compare_data.insert(std::make_pair(std::string("0"), 0)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + // Check that elements in map are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + + data.insert(data_result, std::make_pair(std::string("1"), 1)); + compare_data.insert(compare_result, std::make_pair(std::string("1"), 1)); + + isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_const_hint_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = + data.insert(Data::value_type(std::string("2"), 0)); + Compare_Data::iterator compare_result = + compare_data.insert(std::make_pair(std::string("2"), 0)); + + // Check that both return successful return results + CHECK_EQUAL(compare_result->first, data_result->first); + CHECK_EQUAL(compare_result->second, data_result->second); + + // Check that elements in map are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + + data.insert(Data::const_iterator(data_result), + std::make_pair(std::string("1"), 1)); + compare_data.insert(Compare_Data::const_iterator(compare_result), + std::make_pair(std::string("1"), 1)); + + isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value_excess) + { + Data data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.insert(std::make_pair(std::string("10"), 10)), etl::map_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + Compare_Data compare_data; + Data data; + + data.insert(initial_data.begin(), initial_data.end()); + compare_data.insert(initial_data.begin(), initial_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_random) + { + Compare_Data compare_data; + Data data; + + data.insert(random_data.begin(), random_data.end()); + compare_data.insert(random_data.begin(), random_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_excess) + { + Data data; + + CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::map_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range) + { + Compare_Data compare_data(random_data.begin(), random_data.end()); + Data data(random_data.begin(), random_data.end()); + + // Test a number not available + std::pair data_result = + data.equal_range("1"); + std::pair compare_result = + compare_data.equal_range("1"); + + // Check that both return the same return results + CHECK_EQUAL(compare_result.first->first, data_result.first->first); + CHECK_EQUAL(compare_result.first->second, data_result.first->second); + CHECK_EQUAL(compare_result.second->first, data_result.second->first); + CHECK_EQUAL(compare_result.second->second, data_result.second->second); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_equal_range) + { + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + // Test a number with several of the same key + std::pair data_result = + data.equal_range("2"); + std::pair compare_result = + compare_data.equal_range("2"); + + // Check that both return the same return results + CHECK_EQUAL(compare_result.first->first, data_result.first->first); + CHECK_EQUAL(compare_result.first->second, data_result.first->second); + CHECK_EQUAL(compare_result.second->first, data_result.second->first); + CHECK_EQUAL(compare_result.second->second, data_result.second->second); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_value) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + size_t compare_count = compare_data.erase("2"); + size_t data_count = data.erase("2"); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + // Erase another value + compare_count = compare_data.erase("1"); + data_count = data.erase("1"); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + // Erase another value + compare_count = compare_data.erase("3"); + data_count = data.erase("3"); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.begin(); + Data::iterator i_data = data.begin(); + + compare_data.erase(i_compare); + data.erase(i_data); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_erase_single) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.cbegin(); + Data::const_iterator i_data = data.cbegin(); + + std::advance(i_compare, 8); + std::advance(i_data, 8); + + Compare_Data::const_iterator i_compare1 = compare_data.erase(i_compare); + Data::const_iterator i_data1 = data.erase(i_data); + + CHECK_EQUAL(i_compare1->second, i_data1->second); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + Compare_Data compare_data(random_data.begin(), random_data.end()); + Data data(random_data.begin(), random_data.end()); + + Compare_Data::iterator i_compare = compare_data.begin(); + Data::iterator i_data = data.begin(); + + Compare_Data::iterator i_compare_end = compare_data.begin(); + Data::iterator i_data_end = data.begin(); + + std::advance(i_compare, 1); + std::advance(i_data, 1); + + std::advance(i_compare_end, 4); + std::advance(i_data_end, 4); + + compare_data.erase(i_compare, i_compare_end); + data.erase(i_data, i_data_end); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_erase_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + compare_data.erase(compare_data.cbegin(), compare_data.cend()); + data.erase(data.cbegin(), data.cend()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + data.clear(); + + CHECK_EQUAL(data.size(), size_t(0)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_count) + { + const Data data(initial_data.begin(), initial_data.end()); + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + CHECK_EQUAL(compare_data.count("."), data.count(".")); + CHECK_EQUAL(compare_data.count("0"), data.count("0")); + CHECK_EQUAL(compare_data.count("1"), data.count("1")); + CHECK_EQUAL(compare_data.count("2"), data.count("2")); + CHECK_EQUAL(compare_data.count("3"), data.count("3")); + CHECK_EQUAL(compare_data.count("4"), data.count("4")); + CHECK_EQUAL(compare_data.count("A"), data.count("A")); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.cbegin(), + data.cend(), + compare_data.cbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_reverse_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.rbegin(), + data.rend(), + compare_data.rbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_reverse_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.crbegin(), + data.crend(), + compare_data.crbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Data::iterator i_data = data.find("0"); + Compare_Data::iterator i_compare = compare_data.find("0"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("1"); + i_compare = compare_data.find("1"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("2"); + i_compare = compare_data.find("2"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("3"); + i_compare = compare_data.find("3"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("."); + i_compare = compare_data.find("."); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.find("A"); + i_compare = compare_data.find("A"); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_const) + { + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Data::const_iterator i_data = data.find("0"); + Compare_Data::const_iterator i_compare = compare_data.find("0"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("1"); + i_compare = compare_data.find("1"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("2"); + i_compare = compare_data.find("2"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("3"); + i_compare = compare_data.find("3"); + + // Check that both return successful return results + CHECK_EQUAL(i_compare->first, i_data->first); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_data = data.find("."); + i_compare = compare_data.find("."); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.find("A"); + i_compare = compare_data.find("A"); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal) + { + const Data initial1(initial_data.begin(), initial_data.end()); + const Data initial2(initial_data.begin(), initial_data.end()); + + CHECK(initial1 == initial2); + + const Data different(different_data.begin(), different_data.end()); + + CHECK(!(initial1 == different)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_not_equal) + { + const Data initial1(initial_data.begin(), initial_data.end()); + const Data initial2(initial_data.begin(), initial_data.end()); + + CHECK(!(initial1 != initial2)); + + const Data different(different_data.begin(), different_data.end()); + + CHECK(initial1 != different); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_lower_bound) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.lower_bound("2"); + Data::iterator i_data = data.lower_bound("2"); + CHECK_EQUAL(i_compare->second, i_data->second); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.lower_bound("."); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound("."); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.lower_bound("A"); + i_data = data.lower_bound("A"); + CHECK_EQUAL(i_compare->second, i_data->second); +#else + i_compare = compare_data.lower_bound("."); + i_data = data.lower_bound("."); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_compare = compare_data.lower_bound("A"); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound("A"); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_lower_bound_const) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.lower_bound("4"); + Data::const_iterator i_data = data.lower_bound("4"); + CHECK_EQUAL(i_compare->second, i_data->second); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.lower_bound("."); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound("."); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.lower_bound("A"); + i_data = data.lower_bound("A"); + CHECK_EQUAL(i_compare->second, i_data->second); +#else + i_compare = compare_data.lower_bound("."); + i_data = data.lower_bound("."); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_compare = compare_data.lower_bound("A"); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound("A"); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_upper_bound) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.upper_bound("1"); + Data::iterator i_data = data.upper_bound("1"); + CHECK_EQUAL(i_compare->second, i_data->second); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.upper_bound("."); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound("."); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.upper_bound("A"); + i_data = data.upper_bound("A"); + CHECK_EQUAL(i_compare->second, i_data->second); +#else + i_compare = compare_data.upper_bound("."); + i_data = data.upper_bound("."); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_compare = compare_data.upper_bound("A"); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound("A"); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_upper_bound_const) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.upper_bound("3"); + Data::const_iterator i_data = data.upper_bound("3"); + CHECK_EQUAL(i_compare->second, i_data->second); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.upper_bound("."); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound("."); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.upper_bound("A"); + i_data = data.upper_bound("A"); + CHECK_EQUAL(i_compare->second, i_data->second); +#else + i_compare = compare_data.upper_bound("."); + i_data = data.upper_bound("."); + CHECK_EQUAL(i_compare->second, i_data->second); + + i_compare = compare_data.upper_bound("A"); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound("A"); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + }; +} diff --git a/test/test_multiset.cpp b/test/test_multiset.cpp new file mode 100644 index 000000000..04d2638aa --- /dev/null +++ b/test/test_multiset.cpp @@ -0,0 +1,912 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2014 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "../multiset.h" + +static const size_t SIZE = 10; + +#define TEST_GREATER_THAN +#ifdef TEST_GREATER_THAN +typedef etl::multiset > Data; +typedef std::multiset > Compare_Data; +#else +typedef etl::multiset > Data; +typedef std::multiset > Compare_Data; +#endif + +typedef Data::iterator Data_iterator; +typedef Data::const_iterator Data_const_iterator; +typedef Compare_Data::iterator Compare_Data_iterator; +typedef Compare_Data::const_iterator Compare_Data_const_iterator; + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Data_iterator& it) +{ + os << (*it); + + return os; +} + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Data_const_iterator& it) +{ + os << (*it); + + return os; +} + +//************************************************************************* +static std::ostream& operator << (std::ostream& os, const Compare_Data_iterator& it) +{ + os << (*it); + + return os; +} + +namespace +{ + SUITE(test_multiset) + { + //************************************************************************* + template + bool Check_Equal(T1 begin1, T1 end1, T2 begin2) + { + while (begin1 != end1) + { + if (*begin1 != *begin2) + { + return false; + } + + ++begin1; + ++begin2; + } + + return true; + } + + //************************************************************************* + struct SetupFixture + { + // Multisets of predefined data from which to constuct multisets used in + // each test + std::multiset initial_data; + std::multiset excess_data; + std::multiset different_data; + std::multiset random_data; + + SetupFixture() + { + // Create a multiset of initial data + initial_data.insert(0); + initial_data.insert(0); + initial_data.insert(1); + initial_data.insert(1); + initial_data.insert(1); + initial_data.insert(2); + initial_data.insert(2); + initial_data.insert(2); + initial_data.insert(3); + initial_data.insert(4); + + // Create a multiset of excess data + excess_data.insert(0); + excess_data.insert(1); + excess_data.insert(2); + excess_data.insert(2); + excess_data.insert(3); + excess_data.insert(3); + excess_data.insert(3); + excess_data.insert(4); + excess_data.insert(5); + excess_data.insert(6); + excess_data.insert(10); + + // Create a multiset of different data + different_data.insert(10); + different_data.insert(11); + different_data.insert(12); + different_data.insert(13); + different_data.insert(14); + different_data.insert(15); + different_data.insert(16); + different_data.insert(17); + different_data.insert(18); + different_data.insert(19); + + // Create a multiset of random data + random_data.insert(3); + random_data.insert(3); + random_data.insert(0); + random_data.insert(5); + random_data.insert(6); + random_data.insert(2); + random_data.insert(0); + random_data.insert(2); + random_data.insert(4); + random_data.insert(3); + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + Data data; + + CHECK_EQUAL(data.size(), size_t(0)); + CHECK(data.empty()); + CHECK_EQUAL(data.capacity(), SIZE); + CHECK_EQUAL(data.max_size(), SIZE); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + CHECK(data.size() == SIZE); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assignment) + { + Data data(initial_data.begin(), initial_data.end()); + Data otherData; + + otherData = data; + + bool isEqual = Check_Equal(data.begin(), + data.end(), + otherData.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_begin) + { + Data data(initial_data.begin(), initial_data.end()); + const Data constData(data); + + CHECK_EQUAL(data.begin(), std::begin(data)); + CHECK_EQUAL(constData.begin(), std::begin(constData)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_end) + { + Data data(initial_data.begin(), initial_data.end()); + const Data constData(data); + + CHECK_EQUAL(data.end(), std::end(data)); + CHECK_EQUAL(constData.end(), std::end(constData)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty) + { + Data data; + data.insert(initial_data.begin(), initial_data.end()); + + CHECK(data.full()); + CHECK(!data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_full) + { + Data data; + + CHECK(!data.full()); + CHECK(data.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data; + + data.assign(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = data.insert(2); + Compare_Data::iterator compare_result = compare_data.insert(2); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + data_result = data.insert(1); + compare_result = compare_data.insert(1); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + data_result = data.insert(2); + compare_result = compare_data.insert(2); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + data_result = data.insert(3); + compare_result = compare_data.insert(3); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + // Adding this next 2 will trigger a 3 node rotate RL on insert + data_result = data.insert(2); + compare_result = compare_data.insert(2); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + // Adding this next 4 will trigger a 2 node rotate left on insert + data_result = data.insert(4); + compare_result = compare_data.insert(4); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + data_result = data.insert(0); + compare_result = compare_data.insert(0); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + // Check that elements in multiset are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_hint_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = data.insert(0); + Compare_Data::iterator compare_result = compare_data.insert(0); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + // Check that elements in multiset are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + + data.insert(data_result, 1); + compare_data.insert(compare_result, 1); + + isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_const_hint_value) + { + Compare_Data compare_data; + Data data; + + Data::iterator data_result = data.insert(2); + Compare_Data::iterator compare_result = compare_data.insert(2); + + // Check that both return successful return results + CHECK_EQUAL(*compare_result, *data_result); + + // Check that elements in multiset are the same + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + + data.insert(Data::const_iterator(data_result), 1); + compare_data.insert(Compare_Data::const_iterator(compare_result), 1); + + isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_value_excess) + { + Data data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.insert(10), etl::set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + Compare_Data compare_data; + Data data; + + data.insert(initial_data.begin(), initial_data.end()); + compare_data.insert(initial_data.begin(), initial_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_random) + { + Compare_Data compare_data; + Data data; + + data.insert(random_data.begin(), random_data.end()); + compare_data.insert(random_data.begin(), random_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range_excess) + { + Data data; + + CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal_range) + { + Compare_Data compare_data(random_data.begin(), random_data.end()); + Data data(random_data.begin(), random_data.end()); + + // Test a number not available + std::pair data_result = + data.equal_range(1); + std::pair compare_result = + compare_data.equal_range(1); + + // Check that both return the same return results + CHECK_EQUAL(*compare_result.first, *data_result.first); + CHECK_EQUAL(*compare_result.second, *data_result.second); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_equal_range) + { + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + // Test a number with several of the same key + std::pair data_result = + data.equal_range(2); + std::pair compare_result = + compare_data.equal_range(2); + + // Check that both return the same return results + CHECK_EQUAL(*compare_result.first, *data_result.first); + CHECK_EQUAL(*compare_result.second, *data_result.second); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_value) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + size_t compare_count = compare_data.erase(2); + size_t data_count = data.erase(2); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + // Erase another value + compare_count = compare_data.erase(1); + data_count = data.erase(1); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + // Erase another value + compare_count = compare_data.erase(3); + data_count = data.erase(3); + + // Check that both return the same return results + CHECK_EQUAL(compare_count, data_count); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.begin(); + Data::iterator i_data = data.begin(); + + compare_data.erase(i_compare); + data.erase(i_data); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_erase_single) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.cbegin(); + Data::const_iterator i_data = data.cbegin(); + + std::advance(i_compare, 8); + std::advance(i_data, 8); + + Compare_Data::const_iterator i_compare1 = compare_data.erase(i_compare); + Data::const_iterator i_data1 = data.erase(i_data); + + CHECK_EQUAL(*i_compare1, *i_data1); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + Compare_Data compare_data(random_data.begin(), random_data.end()); + Data data(random_data.begin(), random_data.end()); + + Compare_Data::iterator i_compare = compare_data.begin(); + Data::iterator i_data = data.begin(); + + Compare_Data::iterator i_compare_end = compare_data.begin(); + Data::iterator i_data_end = data.begin(); + + std::advance(i_compare, 1); + std::advance(i_data, 1); + + std::advance(i_compare_end, 4); + std::advance(i_data_end, 4); + + compare_data.erase(i_compare, i_compare_end); + data.erase(i_data, i_data_end); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_erase_range) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + compare_data.erase(compare_data.cbegin(), compare_data.cend()); + data.erase(data.cbegin(), data.cend()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + data.clear(); + + CHECK_EQUAL(data.size(), size_t(0)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_count) + { + const Data data(initial_data.begin(), initial_data.end()); + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + CHECK_EQUAL(compare_data.count(-1), data.count(-1)); + CHECK_EQUAL(compare_data.count(0), data.count(0)); + CHECK_EQUAL(compare_data.count(1), data.count(1)); + CHECK_EQUAL(compare_data.count(2), data.count(2)); + CHECK_EQUAL(compare_data.count(3), data.count(3)); + CHECK_EQUAL(compare_data.count(4), data.count(4)); + CHECK_EQUAL(compare_data.count(99), data.count(99)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.begin(), + data.end(), + compare_data.begin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.cbegin(), + data.cend(), + compare_data.cbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_reverse_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.rbegin(), + data.rend(), + compare_data.rbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_reverse_iterator) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + + Data data(compare_data.begin(), compare_data.end()); + + bool isEqual = Check_Equal(data.crbegin(), + data.crend(), + compare_data.crbegin()); + + CHECK(isEqual); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Data::iterator i_data = data.find(0); + Compare_Data::iterator i_compare = compare_data.find(0); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(1); + i_compare = compare_data.find(1); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(2); + i_compare = compare_data.find(2); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(3); + i_compare = compare_data.find(3); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(-1); + i_compare = compare_data.find(-1); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.find(99); + i_compare = compare_data.find(99); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_find_const) + { + const Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Data::const_iterator i_data = data.find(0); + Compare_Data::const_iterator i_compare = compare_data.find(0); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(1); + i_compare = compare_data.find(1); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(2); + i_compare = compare_data.find(2); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(3); + i_compare = compare_data.find(3); + + // Check that both return successful return results + CHECK_EQUAL(*i_compare, *i_data); + + i_data = data.find(-1); + i_compare = compare_data.find(-1); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.find(99); + i_compare = compare_data.find(99); + + // Check that both return successful return results + CHECK_EQUAL(data.end(), i_data); + CHECK_EQUAL(compare_data.end(), i_compare); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_equal) + { + const Data initial1(initial_data.begin(), initial_data.end()); + const Data initial2(initial_data.begin(), initial_data.end()); + + CHECK(initial1 == initial2); + + const Data different(different_data.begin(), different_data.end()); + + CHECK(!(initial1 == different)); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_not_equal) + { + const Data initial1(initial_data.begin(), initial_data.end()); + const Data initial2(initial_data.begin(), initial_data.end()); + + CHECK(!(initial1 != initial2)); + + const Data different(different_data.begin(), different_data.end()); + + CHECK(initial1 != different); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_lower_bound) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.lower_bound(2); + Data::iterator i_data = data.lower_bound(2); + CHECK_EQUAL(*i_compare, *i_data); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.lower_bound(-1); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound(-1); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.lower_bound(99); + i_data = data.lower_bound(99); + CHECK_EQUAL(*i_compare, *i_data); +#else + i_compare = compare_data.lower_bound(-1); + i_data = data.lower_bound(-1); + CHECK_EQUAL(*i_compare, *i_data); + + i_compare = compare_data.lower_bound(99); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound(99); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_lower_bound_const) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.lower_bound(4); + Data::const_iterator i_data = data.lower_bound(4); + CHECK_EQUAL(*i_compare, *i_data); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.lower_bound(-1); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound(-1); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.lower_bound(99); + i_data = data.lower_bound(99); + CHECK_EQUAL(*i_compare, *i_data); +#else + i_compare = compare_data.lower_bound(-1); + i_data = data.lower_bound(-1); + CHECK_EQUAL(*i_compare, *i_data); + + i_compare = compare_data.lower_bound(99); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.lower_bound(99); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_upper_bound) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::iterator i_compare = compare_data.upper_bound(1); + Data::iterator i_data = data.upper_bound(1); + CHECK_EQUAL(*i_compare, *i_data); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.upper_bound(-1); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound(-1); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.upper_bound(99); + i_data = data.upper_bound(99); + CHECK_EQUAL(*i_compare, *i_data); +#else + i_compare = compare_data.upper_bound(-1); + i_data = data.upper_bound(-1); + CHECK_EQUAL(*i_compare, *i_data); + + i_compare = compare_data.upper_bound(99); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound(99); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_upper_bound_const) + { + Compare_Data compare_data(initial_data.begin(), initial_data.end()); + const Data data(initial_data.begin(), initial_data.end()); + + Compare_Data::const_iterator i_compare = compare_data.upper_bound(3); + Data::const_iterator i_data = data.upper_bound(3); + CHECK_EQUAL(*i_compare, *i_data); + +#ifdef TEST_GREATER_THAN + i_compare = compare_data.upper_bound(-1); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound(-1); + CHECK_EQUAL(data.end(), i_data); + + i_compare = compare_data.upper_bound(99); + i_data = data.upper_bound(99); + CHECK_EQUAL(*i_compare, *i_data); +#else + i_compare = compare_data.upper_bound(-1); + i_data = data.upper_bound(-1); + CHECK_EQUAL(*i_compare, *i_data); + + i_compare = compare_data.upper_bound(99); + CHECK_EQUAL(compare_data.end(), i_compare); + + i_data = data.upper_bound(99); + CHECK_EQUAL(data.end(), i_data); +#endif + } + + }; +} diff --git a/test/test_priority_queue.cpp b/test/test_priority_queue.cpp new file mode 100644 index 000000000..37428ecb0 --- /dev/null +++ b/test/test_priority_queue.cpp @@ -0,0 +1,353 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl + +Copyright(c) 2015 jwellbelove, rlindeman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include + +#include + +#include "../priority_queue.h" + +namespace +{ + SUITE(test_priority_queue) + { + static const size_t SIZE = 4; + + //************************************************************************* + TEST(test_default_constructor) + { + etl::priority_queue priority_queue; + + CHECK_EQUAL(priority_queue.size(), size_t(0)); + CHECK_EQUAL(priority_queue.available(), SIZE); + CHECK_EQUAL(priority_queue.max_size(), SIZE); + } + + //************************************************************************* + TEST(test_copy_constructor) + { + etl::priority_queue priority_queue; + + priority_queue.push(3); + priority_queue.push(1); + priority_queue.push(4); + priority_queue.push(2); + + etl::priority_queue priority_queue2(priority_queue); + + CHECK(priority_queue.size() == priority_queue2.size()); + + while (!priority_queue.empty()) + { + CHECK_EQUAL(priority_queue.top(), priority_queue2.top()); + priority_queue.pop(); + priority_queue2.pop(); + } + } + + //************************************************************************* + TEST(test_constructor_range) + { + int n[] = { 3, 4, 1, 2 }; + etl::priority_queue priority_queue(std::begin(n), std::end(n)); + std::priority_queue compare_priority_queue(std::begin(n), std::end(n)); + + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK(!priority_queue.empty()); + + while (!priority_queue.empty()) + { + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + compare_priority_queue.pop(); + priority_queue.pop(); + } + } + + //************************************************************************* + TEST(test_size) + { + etl::priority_queue priority_queue; + + priority_queue.push(1); + priority_queue.push(2); + priority_queue.push(3); + + CHECK_EQUAL(3, priority_queue.size()); + } + + //************************************************************************* + TEST(test_clear) + { + etl::priority_queue priority_queue; + + priority_queue.push(1); + priority_queue.push(2); + priority_queue.clear(); + CHECK_EQUAL(0, priority_queue.size()); + } + + //************************************************************************* + TEST(test_assign_range) + { + int n[] = { 3, 4, 1, 2 }; + etl::priority_queue priority_queue; + std::priority_queue compare_priority_queue(std::begin(n), std::end(n)); + + priority_queue.assign(std::begin(n), std::end(n)); + + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK(!priority_queue.empty()); + + while (!priority_queue.empty()) + { + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + compare_priority_queue.pop(); + priority_queue.pop(); + } + } + + //************************************************************************* + TEST(test_empty) + { + etl::priority_queue priority_queue; + + CHECK(priority_queue.empty()); + + priority_queue.push(1); + + CHECK(!priority_queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + etl::priority_queue priority_queue; + + CHECK(!priority_queue.full()); + + priority_queue.push(1); + priority_queue.push(2); + priority_queue.push(3); + priority_queue.push(4); + + CHECK(priority_queue.full()); + } + + //************************************************************************* + TEST(test_top) + { + etl::priority_queue priority_queue; + std::priority_queue compare_priority_queue; + + priority_queue.push(1); + compare_priority_queue.push(1); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(3); + compare_priority_queue.push(3); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(2); + compare_priority_queue.push(2); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(4); + compare_priority_queue.push(4); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + } + + //************************************************************************* + TEST(test_top_const) + { + etl::priority_queue priority_queue; + const etl::priority_queue& constQueue = priority_queue; + + priority_queue.push(1); + CHECK_EQUAL(1, constQueue.top()); + priority_queue.push(3); + CHECK_EQUAL(3, constQueue.top()); + priority_queue.push(2); + CHECK_EQUAL(3, constQueue.top()); + priority_queue.push(4); + CHECK_EQUAL(4, constQueue.top()); + } + + //************************************************************************* + TEST(test_push) + { + etl::priority_queue priority_queue; + std::priority_queue compare_priority_queue; + + priority_queue.push(1); + compare_priority_queue.push(1); + + priority_queue.push(2); + compare_priority_queue.push(2); + + priority_queue.push(3); + compare_priority_queue.push(3); + + priority_queue.push(4); + compare_priority_queue.push(4); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + } + + //************************************************************************* + TEST(test_push_excess) + { + etl::priority_queue priority_queue; + + for (size_t i = 0; i < priority_queue.max_size(); ++i) + { + priority_queue.push(1); + } + + CHECK_THROW(priority_queue.push(1), etl::priority_queue_full); + } + + //************************************************************************* + TEST(test_pop) + { + etl::priority_queue priority_queue; + std::priority_queue compare_priority_queue; + + priority_queue.push(1); + compare_priority_queue.push(1); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + + priority_queue.push(3); + compare_priority_queue.push(3); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(1); + compare_priority_queue.push(1); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(1); + compare_priority_queue.push(1); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(2); + compare_priority_queue.push(2); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.push(1); + compare_priority_queue.push(1); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); + + priority_queue.pop(); + compare_priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + + // Go one beyond (which we handle without throwing) + priority_queue.pop(); + CHECK_EQUAL(compare_priority_queue.size(), priority_queue.size()); + } + + //************************************************************************* + TEST(test_assignment) + { + etl::priority_queue priority_queue; + + priority_queue.push(1); + priority_queue.push(4); + priority_queue.push(3); + priority_queue.push(2); + + etl::priority_queue priority_queue2; + + priority_queue2 = priority_queue; + + CHECK(priority_queue.size() == priority_queue2.size()); + + while (!priority_queue.empty()) + { + CHECK_EQUAL(priority_queue.top(), priority_queue2.top()); + priority_queue.pop(); + priority_queue2.pop(); + } + } + + //************************************************************************* + TEST(test_self_assignment) + { + etl::priority_queue priority_queue; + + priority_queue.push(2); + priority_queue.push(1); + priority_queue.push(4); + priority_queue.push(3); + + priority_queue = priority_queue; + + CHECK(priority_queue.max_size() == priority_queue.size()); + + CHECK_EQUAL(4, priority_queue.top()); + priority_queue.pop(); + + CHECK_EQUAL(3, priority_queue.top()); + priority_queue.pop(); + + CHECK_EQUAL(2, priority_queue.top()); + priority_queue.pop(); + + CHECK_EQUAL(1, priority_queue.top()); + priority_queue.pop(); + } + }; +} diff --git a/test/test_set.cpp b/test/test_set.cpp index cb9b5c2cd..71cda3d30 100644 --- a/test/test_set.cpp +++ b/test/test_set.cpp @@ -2,6 +2,7 @@ The MIT License(MIT) Embedded Template Library. +https://github.com/ETLCPP/etl Copyright(c) 2014 jwellbelove, rlindeman @@ -53,25 +54,25 @@ typedef Compare_Data::iterator Compare_Data_iterator; typedef Compare_Data::const_iterator Compare_Data_const_iterator; //************************************************************************* -std::ostream& operator << (std::ostream& os, const Data_iterator& it) +static std::ostream& operator << (std::ostream& os, const Data_iterator& it) { - os << (*it) << " " << (*it); + os << (*it); return os; } //************************************************************************* -std::ostream& operator << (std::ostream& os, const Data_const_iterator& it) +static std::ostream& operator << (std::ostream& os, const Data_const_iterator& it) { - os << (*it) << " " << (*it); + os << (*it); return os; } //************************************************************************* -std::ostream& operator << (std::ostream& os, const Compare_Data_iterator& it) +static std::ostream& operator << (std::ostream& os, const Compare_Data_iterator& it) { - os << (*it) << " " << (*it); + os << (*it); return os; } diff --git a/test/vs2013/etl.vcxproj b/test/vs2013/etl.vcxproj index 417eadc77..89683719e 100644 --- a/test/vs2013/etl.vcxproj +++ b/test/vs2013/etl.vcxproj @@ -153,10 +153,13 @@ + + + @@ -171,6 +174,8 @@ + + @@ -179,6 +184,8 @@ + + @@ -266,12 +273,15 @@ + + false + diff --git a/test/vs2013/etl.vcxproj.filters b/test/vs2013/etl.vcxproj.filters index ec358bea9..ffa44efdb 100644 --- a/test/vs2013/etl.vcxproj.filters +++ b/test/vs2013/etl.vcxproj.filters @@ -375,6 +375,27 @@ ETL\Containers + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + @@ -584,6 +605,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files +