Skip to content
Snippets Groups Projects
Commit c14bf93f authored by Jan Trávníček's avatar Jan Trávníček
Browse files

base implementation of generic trie

parent b16b312b
No related branches found
No related tags found
No related merge requests found
/*
* bidirectional_tree.hpp
*
* Created on: Jul 2, 2016
* Author: Jan Travnicek
*/
#ifndef __TRIE_HPP_
#define __TRIE_HPP_
namespace std {
template < class Key, class Value >
class trie {
Value m_data;
trie * m_parent;
std::map < Key, trie > m_children;
public:
trie * getParent ( ) {
return m_parent;
}
const trie * getParent ( ) const {
return m_parent;
}
Value & getData ( ) {
return m_data;
}
const Value & getData ( ) const {
return m_data;
}
std::map < Key, trie > & getChildren ( ) {
return m_children;
}
const std::map < Key, trie > & getChildren ( ) const {
return m_children;
}
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;
}
}
typedef typename std::map < Key, trie >::const_iterator const_children_iterator;
typedef const trie * const_child_iterator;
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
private:
void insert_helper ( const_child_iterator under, const_children_iterator begin, const_children_iterator end ) {
std::map < Key, trie > & children = const_cast < std::map < Key, trie > & > ( under->getChildren ( ) );
for ( ; begin != end; ++begin )
children.insert ( * begin ).first->second.m_parent = const_cast < trie * > ( & * under );
}
public:
void insert ( const_child_iterator under, Key key, trie < Key, Value > && value ) {
std::map < Key, trie > & children = const_cast < std::map < Key, trie > & > ( under->getChildren ( ) );
std::pair < typename std::map < Key, trie >::iterator, bool > iter = children.insert ( std::make_pair ( std::move ( key ), std::move ( value ) ) );
iter.first->second.m_parent = const_cast < trie * > ( & * under );
}
void insert ( const_children_iterator under, Key key, const trie < Key, Value > & value ) {
insert ( under, std::move ( key ), trie < Key, Value > ( value ) );
}
void insert ( const_child_iterator under, const_children_iterator begin, const_children_iterator end ) {
insert_helper ( under, begin, end );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
public:
trie ( Value && data, std::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;
}
trie ( const Value & data, const std::map < Key, trie > & subtrees ) : trie ( Value ( data ), std::map < Key, trie > ( subtrees ) ) {
}
template < typename ... Types >
trie ( const Value & data, Types ... subtrees ) : trie ( data, std::map < Key, trie > { subtrees ... } ) {
}
template < typename ... Types >
trie ( Value && data, Types ... subtrees ) : trie ( std::move ( data ), std::map < Key, trie > { subtrees ... } ) {
}
trie ( const Value & data, const_children_iterator begin, const_children_iterator end ) : trie ( data, std::map < Key, trie > ( begin, end ) ) {
}
~trie ( ) noexcept {
}
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;
}
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;
}
trie & operator =( const trie & node ) {
return this->operator =( trie ( node ) );
}
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;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
const_child_iterator root ( ) const {
return this;
}
const_child_iterator parent ( ) const {
return this->getParent ( );
}
const_children_iterator begin ( ) const {
return m_children.begin ( );
}
const_children_iterator end ( ) const {
return m_children.end ( );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
const_children_iterator erase ( const_child_iterator under, const_children_iterator position ) {
std::map < Key, trie > & children = const_cast < std::map < Key, trie > & > ( under->getChildren ( ) );
return children.erase ( position );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
template < class ... Indexes >
const Value & operator ()( Indexes ... indexes ) const {
return this->operator ()( { ( Key ) indexes ... } );
}
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 ( );
}
template < class ... Indexes >
Value & operator ()( Indexes ... indexes ) {
return this->operator ()( { ( Key ) indexes ... } );
}
Value & operator ()( std::initializer_list < Key > indexes ) {
trie * node = this;
for ( Key index : indexes ) {
node = & node->getChildren ( ).find ( index )->second;
}
return node->getData ( );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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;
}
bool checkStructure ( ) const {
return m_parent == nullptr && checkStructure ( * this );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
friend void swap ( trie & first, trie & second ) {
swap ( std::move ( first.m_data ), std::move ( second.m_data ) );
swap ( std::move ( first.m_children ), std::move ( second.m_children ) );
swap ( std::move ( first.m_parent ), std::move ( second.m_parent ) );
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int compare ( const trie & other ) const {
auto first = std::tie ( this->getData ( ), this->getChildren ( ) );
auto second = std::tie ( other.getData ( ), other.getChildren ( ) );
std::compare < decltype ( first ) > comp;
return comp ( first, second );
}
bool operator ==( const trie & other ) {
return compare ( other ) == 0;
}
bool operator !=( const trie & other ) {
return compare ( other ) != 0;
}
bool operator <( const trie & other ) {
return compare ( other ) < 0;
}
bool operator <=( const trie & other ) {
return compare ( other ) <= 0;
}
bool operator >( const trie & other ) {
return compare ( other ) > 0;
}
bool operator >=( const trie & other ) {
return compare ( other ) >= 0;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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;
}
};
template < class Key, class Value >
std::ostream & operator <<( std::ostream & out, const trie < Key, Value > & t ) {
out << "[";
out << t.getData ( ) << ";";
for ( typename std::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;
}
template < class Key, class Value >
struct compare < trie < Key, Value > > {
int operator ()( const trie < Key, Value > & first, const trie < Key, Value > & second ) const {
return first.compare ( second );
}
};
template < class Key, class Value >
string to_string ( const std::trie < Key, Value > & value ) {
std::stringstream ss;
ss << value;
return ss.str();
}
} /* namespace std */
#endif /* __TRIE_HPP_ */
#ifndef __TRIE_HEADER_WRAPPER_
#define __TRIE_HEADER_WRAPPER_
#include <memory>
#include <map>
#include <deque>
#include <tuple>
#include "iterator"
#include "string"
#include "sstream"
#include "extensions/trie.hpp"
#endif /* __TRIE_HEADER_WRAPPER_ */
#include "TrieTest.h"
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION ( TrieTest, "bits" );
CPPUNIT_TEST_SUITE_REGISTRATION ( TrieTest );
void TrieTest::setUp ( ) {
}
void TrieTest::tearDown ( ) {
}
void TrieTest::print_tree ( const std::trie < char, int > & tree, std::string indent ) {
std::cout << indent << tree.getData ( ) << std::endl;
for ( const std::pair < const char, std::trie < char, int > > & child : tree.getChildren ( ) ) {
std::cout << indent << child.first << ":" << std::endl;
print_tree ( child.second, indent + " " );
}
}
void TrieTest::testTrieStdStructure ( ) {
std::trie < char, int > t ( 0, std::map < char, std::trie < char, int > > { std::make_pair ( 'a', std::trie < char, int > ( 1 ) ), std::make_pair ( 'b', std::trie < char, int > ( 2 ) ) } );
// std::cout << "structure t " << std::boolalpha << t.checkStructure() << std::endl;
std::trie < char, int > t2 ( 3 );
CPPUNIT_ASSERT ( t2 == ( std::trie < char, int > ( 3 ) ) );
// std::cout << "structure t2 " << std::boolalpha << t2.checkStructure() << std::endl;
t2.insert ( t2.root ( ), 'c', std::trie < char, int > ( 4 ) );
std::cout << t2 << std::endl;
CPPUNIT_ASSERT ( t2 == ( std::trie < char, int > ( 3, std::map < char, std::trie < char, int > > { std::make_pair ( 'c', std::trie < char, int > ( 4 ) ) } ) ) );
std::swap ( t2.getChildren ( ).begin()->second, t.getChildren().begin()->second );
CPPUNIT_ASSERT ( t2 == ( std::trie < char, int > ( 3, std::map < char, std::trie < char, int > > { std::make_pair ( 'c', std::trie < char, int > ( 1 ) ) } ) ) );
CPPUNIT_ASSERT ( t == ( std::trie < char, int > ( 0, std::map < char, std::trie < char, int > > { std::make_pair ( 'a', std::trie < char, int > ( 4 ) ), std::make_pair ( 'b', std::trie < char, int > ( 2 ) ) } ) ) );
CPPUNIT_ASSERT ( t2.checkStructure ( ) );
CPPUNIT_ASSERT ( t.checkStructure ( ) );
std::swap ( t2.getChildren ( ).begin()->second, t.getChildren().begin()->second );
CPPUNIT_ASSERT ( t2.checkStructure ( ) );
t2.insert ( t2.root ( ), t.begin ( ), t.end ( ) );
CPPUNIT_ASSERT ( t2.checkStructure ( ) );
print_tree ( t2, "" );
t2.erase ( t2.root ( ), std::prev ( t2.getChildren ( ).end ( ) ) );
CPPUNIT_ASSERT ( t2.checkStructure ( ) );
print_tree ( t2, "" );
t2.insert ( & t2.getChildren().begin()->second, 'a', std::trie < char, int > ( 5 ) );
CPPUNIT_ASSERT_EQUAL ( ( int ) t2 ( ), 3 );
CPPUNIT_ASSERT_EQUAL ( ( int ) t2 ( 'a' ), 1 );
CPPUNIT_ASSERT_EQUAL ( ( int ) t2 ( 'a', 'a' ), 5 );
t2 ( 'a', 'a' ) = 6;
CPPUNIT_ASSERT_EQUAL ( ( int ) t2 ( 'a', 'a' ), 6 );
CPPUNIT_ASSERT ( t2.checkStructure ( ) );
std::trie < char, int > cmp1 ( 1 );
std::trie < char, int > cmp2 ( 2 );
std::trie < char, int > cmp3 ( 3 );
CPPUNIT_ASSERT ( cmp1 < cmp2 );
CPPUNIT_ASSERT ( cmp2 < cmp3 );
CPPUNIT_ASSERT ( cmp2 <= cmp3 );
CPPUNIT_ASSERT ( cmp2 == cmp2 );
CPPUNIT_ASSERT ( cmp1 != cmp2 );
}
#ifndef TRIE_TEST_H_
#define TRIE_TEST_H_
#include <cppunit/extensions/HelperMacros.h>
#include <compare>
#include <trie>
class TrieTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE ( TrieTest );
CPPUNIT_TEST ( testTrieStdStructure );
CPPUNIT_TEST_SUITE_END ( );
public:
struct RankedArityChecker {
bool operator ()( char symbol, unsigned wantedRank ) const {
switch ( symbol ) {
case 'a':
return wantedRank == 2;
case 'b':
return wantedRank == 1;
default:
return wantedRank == 0;
}
}
};
void print_tree ( const std::trie < char, int > & trie, std::string indent );
void setUp ( );
void tearDown ( );
void testTrieStdStructure();
};
#endif // TRIE_TEST_H_
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment