/////////////////////////////////////////////////////////////////////////////// | |
/// \file symbols.hpp | |
/// Contains the Ternary Search Trie implementation. | |
/// Based on the following papers: | |
/// J. Bentley and R. Sedgewick. (1998) Ternary search trees. Dr. Dobbs Journal | |
/// G. Badr and B.J. Oommen. (2005) Self-Adjusting of Ternary Search Tries Using | |
/// Conditional Rotations and Randomized Heuristics | |
// | |
// Copyright 2007 David Jenkins. | |
// Copyright 2007 Eric Niebler. | |
// | |
// Distributed under the Boost Software License, Version 1.0. (See | |
// accompanying file LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#ifndef BOOST_XPRESSIVE_DETAIL_SYMBOLS_HPP_DRJ_06_11_2007 | |
#define BOOST_XPRESSIVE_DETAIL_SYMBOLS_HPP_DRJ_06_11_2007 | |
// MS compatible compilers support #pragma once | |
#if defined(_MSC_VER) && (_MSC_VER >= 1020) | |
# pragma once | |
#endif | |
#include <boost/noncopyable.hpp> | |
#include <boost/range/begin.hpp> | |
#include <boost/range/end.hpp> | |
#include <boost/range/value_type.hpp> | |
#include <boost/range/const_iterator.hpp> | |
#include <boost/shared_ptr.hpp> | |
namespace boost { namespace xpressive { namespace detail | |
{ | |
/////////////////////////////////////////////////////////////////////////////// | |
// symbols (using a ternary search trie) | |
// | |
template<typename Map> | |
struct symbols | |
{ | |
typedef typename range_value<Map>::type::first_type key_type; | |
typedef typename range_value<Map>::type::second_type value_type; | |
typedef typename range_value<key_type>::type char_type; | |
typedef typename range_const_iterator<Map>::type iterator; | |
typedef typename range_const_iterator<key_type>::type key_iterator; | |
typedef value_type const *result_type; | |
// copies of this symbol table share the TST | |
template<typename Trans> | |
void load(Map const &map, Trans trans) | |
{ | |
iterator begin = boost::begin(map); | |
iterator end = boost::end(map); | |
node* root_p = this->root.get(); | |
for(; begin != end; ++begin) | |
{ | |
key_iterator kbegin = boost::begin(begin->first); | |
key_iterator kend = boost::end(begin->first); | |
root_p = this->insert(root_p, kbegin, kend, &begin->second, trans); | |
} | |
this->root.reset(root_p); | |
} | |
template<typename BidiIter, typename Trans> | |
result_type operator ()(BidiIter &begin, BidiIter end, Trans trans) const | |
{ | |
return this->search(begin, end, trans, this->root.get()); | |
} | |
template<typename Sink> | |
void peek(Sink const &sink) const | |
{ | |
this->peek_(this->root.get(), sink); | |
} | |
private: | |
/////////////////////////////////////////////////////////////////////////////// | |
// struct node : a node in the TST. | |
// The "eq" field stores the result pointer when ch is zero. | |
// | |
struct node | |
: boost::noncopyable | |
{ | |
node(char_type c) | |
: ch(c) | |
, lo(0) | |
, eq(0) | |
, hi(0) | |
#ifdef BOOST_DISABLE_THREADS | |
, tau(0) | |
#endif | |
{} | |
~node() | |
{ | |
delete lo; | |
if (ch) | |
delete eq; | |
delete hi; | |
} | |
void swap(node& that) | |
{ | |
std::swap(ch, that.ch); | |
std::swap(lo, that.lo); | |
std::swap(eq, that.eq); | |
std::swap(hi, that.hi); | |
#ifdef BOOST_DISABLE_THREADS | |
std::swap(tau, that.tau); | |
#endif | |
} | |
char_type ch; | |
node* lo; | |
union | |
{ | |
node* eq; | |
result_type result; | |
}; | |
node* hi; | |
#ifdef BOOST_DISABLE_THREADS | |
long tau; | |
#endif | |
}; | |
/////////////////////////////////////////////////////////////////////////////// | |
// insert : insert a string into the TST | |
// | |
template<typename Trans> | |
node* insert(node* p, key_iterator &begin, key_iterator end, result_type r, Trans trans) const | |
{ | |
char_type c1 = 0; | |
if(begin != end) | |
{ | |
c1 = trans(*begin); | |
} | |
if(!p) | |
{ | |
p = new node(c1); | |
} | |
if(c1 < p->ch) | |
{ | |
p->lo = this->insert(p->lo, begin, end, r, trans); | |
} | |
else if(c1 == p->ch) | |
{ | |
if(0 == c1) | |
{ | |
p->result = r; | |
} | |
else | |
{ | |
p->eq = this->insert(p->eq, ++begin, end, r, trans); | |
} | |
} | |
else | |
{ | |
p->hi = this->insert(p->hi, begin, end, r, trans); | |
} | |
return p; | |
} | |
#ifdef BOOST_DISABLE_THREADS | |
/////////////////////////////////////////////////////////////////////////////// | |
// conditional rotation : the goal is to minimize the overall | |
// weighted path length of each binary search tree | |
// | |
bool cond_rotation(bool left, node* const i, node* const j) const | |
{ | |
// don't rotate top node in binary search tree | |
if (i == j) | |
return false; | |
// calculate psi (the rotation condition) | |
node* const k = (left ? i->hi : i->lo); | |
long psi = 2*i->tau - j->tau - (k ? k->tau : 0); | |
if (psi <= 0) | |
return false; | |
// recalculate the tau values | |
j->tau += -i->tau + (k ? k->tau : 0); | |
i->tau += j->tau - (k ? k->tau : 0); | |
// fixup links and swap nodes | |
if (left) | |
{ | |
j->lo = k; | |
i->hi = i; | |
} | |
else | |
{ | |
j->hi = k; | |
i->lo = i; | |
} | |
(*i).swap(*j); | |
return true; | |
} | |
#endif | |
/////////////////////////////////////////////////////////////////////////////// | |
// search : find a string in the TST | |
// | |
template<typename BidiIter, typename Trans> | |
result_type search(BidiIter &begin, BidiIter end, Trans trans, node* p) const | |
{ | |
result_type r = 0; | |
node* p2 = p; | |
bool left = false; | |
char_type c1 = (begin != end ? trans(*begin) : 0); | |
while(p) | |
{ | |
#ifdef BOOST_DISABLE_THREADS | |
++p->tau; | |
#endif | |
if(c1 == p->ch) | |
{ | |
// conditional rotation test | |
#ifdef BOOST_DISABLE_THREADS | |
if (this->cond_rotation(left, p, p2)) | |
p = p2; | |
#endif | |
if (0 == p->ch) | |
{ | |
// it's a match! | |
r = p->result; | |
} | |
if(begin == end) | |
break; | |
++begin; | |
p = p->eq; | |
// search for the longest match first | |
r = search(begin,end,trans,p); | |
if (0 == r) | |
{ | |
// search for a match ending here | |
r = search(end,end,trans,p); | |
if (0 == r) | |
{ | |
--begin; | |
} | |
} | |
break; | |
} | |
else if(c1 < p->ch) | |
{ | |
left = true; | |
p2 = p; | |
p = p->lo; | |
} | |
else // (c1 > p->ch) | |
{ | |
left = false; | |
p2 = p; | |
p = p->hi; | |
} | |
} | |
return r; | |
} | |
template<typename Sink> | |
void peek_(node const *const &p, Sink const &sink) const | |
{ | |
if(p) | |
{ | |
sink(p->ch); | |
this->peek_(p->lo, sink); | |
this->peek_(p->hi, sink); | |
} | |
} | |
boost::shared_ptr<node> root; | |
}; | |
}}} // namespace boost::xpressive::detail | |
#endif |