Newer
Older
* trie.hpp
*
* This file is part of Algorithms library toolkit.
* Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)
* Algorithms library toolkit is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* Algorithms library toolkit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Algorithms library toolkit. If not, see <http://www.gnu.org/licenses/>.
*
* Created on: Jul 2, 2016
* Author: Jan Travnicek
*/
#ifndef __TRIE_HPP_
#define __TRIE_HPP_
#include <memory>
#include <iterator>
#include <sstream>
#include "pair.hpp"
#include "tuple.hpp"
/**
* \brief
* Class introducing a trie with interface trying to be close to the interface of standard library containers.
*
* The trie is a hierarchical structure of nodes with parent child relationship, where the children are accessible by their keys.
*
* \tparam T the type of values inside nodes of the trie
template < class Key, class Value >
class trie {
* Pointer to the parent of the node. Null pointer for root of a trie.
* Container of children of the root node of the trie.
*
* The children are tries themselves, hence the subtrie of a trie is itself a trie.
*/
/**
* \brief
* Getter of the parent node. Null if the node is root.
*
* \return pointer to the parent node
*/
trie * getParent ( ) {
return m_parent;
}
/**
* \brief
* Getter of the parent node. Null if the node is root.
*
* \return pointer to the parent node
*/
const trie * getParent ( ) const {
return m_parent;
}
/**
* \brief
* Getter of the value in the root node.
*
* \return the value in the root node
*/
Value & getData ( ) {
return m_data;
}
/**
* \brief
* Getter of the value in the root node.
*
* \return the value in the root node
*/
const Value & getData ( ) const {
return m_data;
}
/**
* \brief
* Getter of children of the root node.
*
* \return children of the root node
*/
/**
* \brief
* Getter of children of the root node.
*
* \return children of the root node
*/
const ext::map < Key, trie > & getChildren ( ) const {
/**
* \brief
* The iterator type over children of the node
*/
typedef typename ext::map < Key, trie >::iterator children_iterator;
/**
* \brief
* The iterator type over children of the node
*/
typedef typename ext::map < Key, trie >::const_iterator const_children_iterator;
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Inserts a subtrie into a trie.
*
* \param key the associating key for the inserted subtrie
* \param value a subtrie to insert
*
* \return updated position iterator pointing to the first node inserted
*/
const_children_iterator insert ( Key key, trie < Key, Value > && value ) {
ext::map < Key, trie > & children = const_cast < ext::map < Key, trie > & > ( getChildren ( ) );
typename ext::map < Key, trie >::iterator iter = children.insert ( std::move ( key ), std::move ( value ) ).first;
iter->second.m_parent = this;
return iter;
/**
* \brief
* Inserts a subtrie into a trie.
*
* \param key the associating key for the inserted subtrie
* \param value a subtrie to insert
*
* \return updated position iterator pointing to the first node inserted
*/
const_children_iterator insert ( Key key, const trie < Key, Value > & value ) {
return insert ( std::move ( key ), trie < Key, Value > ( value ) );
/**
* \brief
* Inserts subtries given by range of key-value pairs at specified position.
*
* \param begin the start of the range of subtries
* \param end the end of the range of subtries
*/
void insert ( const_children_iterator begin, const_children_iterator end ) {
ext::map < Key, trie > & children = const_cast < ext::map < Key, trie > & > ( getChildren ( ) );
children.insert ( * begin ).first->second.m_parent = this;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Constructor of the trie from value to be stored in the root node and children tries.
*
* \param data the value to be stored in the root
* \param children map of subtries
*/
trie ( Value && data, ext::map < Key, trie > && children ) : m_data ( std::move ( data ) ), m_parent ( nullptr ), m_children ( std::move ( children ) ) {
for ( std::pair < const Key, trie > & child : m_children )
child.second.m_parent = this;
}
/**
* \brief
* Constructor of the trie from value to be stored in the root node and children tries.
*
* \param data the value to be stored in the root
* \param children map of subtries
*/
trie ( const Value & data, const ext::map < Key, trie > & subtries ) : trie ( Value ( data ), ext::map < Key, trie > ( subtries ) ) {
/**
* \brief
* Constructor of the trie from value to be stored in the root node and pack of children tries.
*
* \tparam Types ... pack of types convertible to trie
*
* \param data the value to be stored in the root
* \param subtries ... pack of subtries
*/
trie ( const Value & data, Types ... subtries ) : trie ( data, ext::map < Key, trie > { subtries ... } ) {
/**
* \brief
* Constructor of the trie from value to be stored in the root node and pack of children tries.
*
* \tparam Types ... pack of types convertible to trie
*
* \param data the value to be stored in the root
* \param subtries ... pack of subtries
*/
trie ( Value && data, Types ... subtries ) : trie ( std::move ( data ), ext::map < Key, trie > { subtries ... } ) {
/**
* \brief
* Constructor of the trie from value to be stored in the root node and range of key-value pairs to construct nullary nodes.
*
* \param data the value to be stored in the root
* \param begin the iterator to the begining of key-value pairs range
* \param end the iterator to the end of key-value pairs range
*/
trie ( const Value & data, const_children_iterator begin, const_children_iterator end ) : trie ( data, ext::map < Key, trie > ( begin, end ) ) {
/**
* \brief
* Dectructor of the trie
*/
/**
* \brief
* Copy constructor of the trie.
*
* \param other the other instace of the trie
*/
trie ( const trie & other ) : m_data ( other.m_data ), m_parent ( other.m_parent ), m_children ( other.m_children ) {
for ( std::pair < const Key, trie > & child : m_children )
child.second.m_parent = this;
}
/**
* \brief
* Move constructor of the trie.
*
* \param other the other instace of the trie
*/
trie ( trie && other ) noexcept : m_data ( std::move ( other.m_data ) ), m_parent ( other.m_parent ), m_children ( std::move ( other.m_children ) ) {
for ( std::pair < const Key, trie > & child : m_children )
child.second.m_parent = this;
}
/**
* \brief
* Copy operator of assignment of the trie.
*
* \param other the other instace of the trie
*
* \return the assigned to instance
*/
trie & operator =( const trie & node ) {
return this->operator =( trie ( node ) );
}
/**
* \brief
* Move operator of assignment of the trie.
*
* \param other the other instace of the trie
*
* \return the assigned to instance
*/
trie & operator =( trie && node ) noexcept {
m_data = std::move ( node.m_data );
m_children = std::move ( node.m_children );
for ( std::pair < const Key, trie > & child : m_children )
child.second.m_parent = this;
return * this;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Getter of a children iterator to the begining of children
*
* \return iterator to the first child
*/
const_children_iterator begin ( ) const {
return m_children.begin ( );
/**
* \brief
* Getter of a children iterator to the begining of children
*
* \return iterator to the first child
*/
return m_children.begin ( );
}
/**
* \brief
* Getter of a children iterator one after the last child
*
* \return iterator one after the last child
*/
const_children_iterator end ( ) const {
return m_children.end ( );
}
/**
* \brief
* Getter of a children iterator one after the last child
*
* \return iterator one after the last child
*/
children_iterator end ( ) {
return m_children.end ( );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Erases a subtrie from a trie on given by \p position
*
* \param position the specification of position in children where to erase the subtrie
*/
children_iterator erase ( const_children_iterator position ) {
ext::map < Key, trie > & children = const_cast < ext::map < Key, trie > & > ( getChildren ( ) );
return children.erase ( position );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Access value given indexes to chindren allong the selection path.
*
* \tparam Indexes ... types of child keys
*
* \param indexes actual keys
*
* \return the value in the selected node
*/
template < class ... Indexes >
const Value & operator ()( Indexes ... indexes ) const {
return this->operator ()( { ( Key ) indexes ... } );
}
/**
* \brief
* Access value given key to chindren allong the selection path.
*
* \param indexes list of actual keys
*
* \return the value in the selected node
*/
const Value & operator ()( std::initializer_list < Key > indexes ) const {
const trie * node = this;
for ( Key index : indexes ) {
node = & node->getChildren ( ).find ( index )->second;
}
return node->getData ( );
}
/**
* \brief
* Access value given indexes to chindren allong the selection path.
*
* \tparam Indexes ... types of child keys
*
* \param indexes actual keys
*
* \return the value in the selected node
*/
template < class ... Indexes >
Value & operator ()( Indexes ... indexes ) {
return this->operator ()( { ( Key ) indexes ... } );
}
/**
* \brief
* Access value given key to chindren allong the selection path.
*
* \param indexes list of actual keys
*
* \return the value in the selected node
*/
Value & operator ()( std::initializer_list < Key > indexes ) {
trie * node = this;
for ( Key index : indexes ) {
node = & node->getChildren ( ).find ( index )->second;
}
return node->getData ( );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Helper method to traverse the trie and check all parent references are set correctly.
*
* \param node the node to test
*
* \return true if the parent child relationship of nodes is sound
*/
bool checkStructure ( const trie & node ) const {
bool sign = true;
for ( const std::pair < const Key, trie > & child : node.getChildren ( ) )
sign &= child.second.getParent ( ) == & node && checkStructure ( child.second );
return sign;
}
/**
* \brief
* Helper method to traverse the trie and check all parent references are set correctly. Starts at root node and recursively tests all its child nodes.
*
* \param node the node to test
*
* \return true if the parent child relationship of nodes is sound
*/
bool checkStructure ( ) const {
return m_parent == nullptr && checkStructure ( * this );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Swap method of two tries
*
* \param first the first forward trie to swap
* \param second the second forward trie to swap
*/
friend void swap ( trie & first, trie & second ) {
swap ( first.m_data, second.m_data );
swap ( first.m_children, second.m_children );
swap ( first.m_parent, second.m_parent );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Three-way comparison method of forward tries.
*
* \param other the other instance to compare against
*
* \return negative if this instance is smaller than \p other instance
* zero if this is equal to \p other instance
* positive if this instance is bigger than \p other instance
*/
int compare ( const trie & other ) const {
auto first = ext::tie ( this->getData ( ), this->getChildren ( ) );
auto second = ext::tie ( other.getData ( ), other.getChildren ( ) );
static ext::compare < decltype ( first ) > comp;
return comp ( first, second );
}
/**
* \brief
* Equality comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is equal to other instance, false othervise
*/
bool operator ==( const trie & other ) const {
return compare ( other ) == 0;
}
/**
* \brief
* Non-equality comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is not equal to other instance, false othervise
*/
bool operator !=( const trie & other ) const {
return compare ( other ) != 0;
}
/**
* \brief
* Less comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is smaller than other instance, false othervise
*/
bool operator <( const trie & other ) const {
return compare ( other ) < 0;
}
/**
* \brief
* Less or equal comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is smaller or equal than other instance, false othervise
*/
bool operator <=( const trie & other ) const {
return compare ( other ) <= 0;
}
/**
* \brief
* Greater comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is greater than other instance, false othervise
*/
bool operator >( const trie & other ) const {
return compare ( other ) > 0;
}
/**
* \brief
* Greater or equal comparison operator.
*
* \param other the other instance to compare with
*
* \return true if this instance is greater or equal than other instance, false othervise
*/
bool operator >=( const trie & other ) const {
return compare ( other ) >= 0;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* \brief
* Internal method of printing a trie into a stream
*
* The trie is printed hierarchically. Key-value pair components are printed separated by colon.
*
* Example trie a ( k:b ( m:c ), l:b ( n:c ) ) would be printed like
*
* \-a
* |
* |-k:b
* | |
* | \-m:c
* |
* \-l:b
* |
* \-n:c
*
* \param os the stream to print to
*
* \return the changed output stream
*/
std::ostream & nicePrint ( std::ostream & os ) const {
os << "\\-";
std::string prefix ( " " );
os << getData ( ) << std::endl;
unsigned int i = 0;
for ( const std::pair < const Key, trie < Key, Value > > & subdata : getChildren ( ) ) {
os << prefix << "|" << std::endl;
nicePrint ( os, subdata, prefix, i == m_children.size ( ) - 1 );
++i;
}
return os;
}
/**
* \brief
* Method of printing a trie into a stream
*
* The trie is printed hierarchically. Key-value pair components are printed separated by colon.
*
* Example trie a ( k:b ( m:c ), l:b ( n:c ) ) would be printed like
*
* \-a
* |
* |-k:b
* | |
* | \-m:c
* |
* \-l:b
* |
* \-n:c
*
* \param os the stream to print to
*
* \return the changed output stream
*/
static void nicePrint ( std::ostream & os, const std::pair < const Key, trie < Key, Value > > & data, std::string prefix, const bool last ) {
os << prefix;
if ( last ) {
os << "\\-";
prefix += " ";
} else {
os << "|-";
prefix += "| ";
}
os << data.first << ":" << data.second.getData ( ) << std::endl;
unsigned int i = 0;
for ( const std::pair < const Key, trie < Key, Value > > & subdata : data.second ) {
os << prefix << "|" << std::endl;
nicePrint ( os, subdata, prefix, i == data.second.m_children.size ( ) - 1 );
++i;
}
}
/**
* \brief
* Operator to print the trie to the output stream.
*
* \param out the output stream
* \param trie the trie to print
*
* \tparam T the type of values inside the trie
*
* \return the output stream from the \p out
*/
template < class Key, class Value >
std::ostream & operator <<( std::ostream & out, const trie < Key, Value > & t ) {
out << "[";
out << t.getData ( ) << ";";
for ( typename ext::map < Key, trie < Key, Value > >::const_iterator iter = t.getChildren ( ).begin ( ); iter != t.getChildren ( ).end ( ); ++ iter) {
if ( iter != t.getChildren ( ).begin ( ) ) {
out << ",";
}
out << iter->first << ":" << iter->second;
}
out << "]";
return out;
}
/**
* \brief
* Specialisation of the compare structure implementing the three-way comparison.
*
* \tparam T the type of values inside the trie
*/
struct compare < ext::trie < Key, Value > > {
/**
* \brief
* Implementation of the three-way comparison.
*
* \param first the left operand of the comparison
* \param second the right operand of the comparison
*
* \return negative value of left < right, positive value if left > right, zero if left == right
*/
int operator ()( const ext::trie < Key, Value > & first, const ext::trie < Key, Value > & second ) const {
return first.compare ( second );
}
};
/**
* \brief
* Overload of to_string function.
*
* \param value the trie to be converted to string
*
* \tparam T the type of values inside the trie
*
* \return string representation
*/
std::string to_string ( const ext::trie < Key, Value > & value ) {
std::stringstream ss;
ss << value;
return ss.str();
}