Compare commits
8 commits
master
...
scapegoat_
Author | SHA1 | Date | |
---|---|---|---|
e61832f7cd | |||
62b22c4288 | |||
b5027762c0 | |||
08b3591c60 | |||
5f9526a19b | |||
633532473b | |||
f085f3c3d2 | |||
74a4e4fb2e |
9 changed files with 1639 additions and 0 deletions
64
include/duckhandy/implem/scapegoat_map.inl
Normal file
64
include/duckhandy/implem/scapegoat_map.inl
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K, typename V, typename Hasher>
|
||||||
|
ScapegoatMap<K, V, Hasher>::DerivedPair::DerivedPair() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K, typename V, typename Hasher>
|
||||||
|
ScapegoatMap<K, V, Hasher>::DerivedPair::DerivedPair (typename Loki::TypeTraits<key_type>::ParameterType parKey, typename Loki::TypeTraits<value_type>::ParameterType parValue) :
|
||||||
|
parent_type(parKey, parValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K, typename V, typename Hasher>
|
||||||
|
ScapegoatMap<K, V, Hasher>::DerivedPair::~DerivedPair() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
// template <typename K, typename V, typename Hasher>
|
||||||
|
// bool ScapegoatMap<K, V, Hasher>::DerivedPair::operator< (const DerivedPair& parOther) const {
|
||||||
|
// const typename Hasher::HashType thisHash = Hasher::ComputeFullHash(this->first);
|
||||||
|
// const typename Hasher::HashType otherHash = Hasher::ComputeFullHash(parOther->first);
|
||||||
|
// if (thisHash == otherHash) {
|
||||||
|
// const bool typeMinor = (this->first < parOther.first);
|
||||||
|
//
|
||||||
|
// else
|
||||||
|
//wrong - this breaks total ordering condition return (thisHash < otherHash);
|
||||||
|
// }
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K, typename V, typename Hasher>
|
||||||
|
bool ScapegoatMap<K, V, Hasher>::DerivedPair::operator== (const DerivedPair& parOther) const {
|
||||||
|
const typename Hasher::HashType thisHash = Hasher::ComputeFullHash(this->first);
|
||||||
|
const typename Hasher::HashType otherHash = Hasher::ComputeFullHash(parOther->first);
|
||||||
|
if (thisHash != otherHash)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return (this->first == parOther.first);
|
||||||
|
}
|
||||||
|
} //namespace duckmem
|
725
include/duckhandy/implem/scapegoat_tree.inl
Normal file
725
include/duckhandy/implem/scapegoat_tree.inl
Normal file
|
@ -0,0 +1,725 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
ScapegoatTree<K>::ScapegoatTree() :
|
||||||
|
m_root(NULL),
|
||||||
|
m_count(0),
|
||||||
|
m_countMax(0),
|
||||||
|
m_reserved(0),
|
||||||
|
m_alpha(0.6f)
|
||||||
|
{
|
||||||
|
m_alphainvloginv = 1.0f / std::log(1.0f / m_alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
ScapegoatTree<K>::ScapegoatTree (float parAlpha) :
|
||||||
|
m_root(NULL),
|
||||||
|
m_count(0),
|
||||||
|
m_countMax(0),
|
||||||
|
m_reserved(0),
|
||||||
|
m_alpha(parAlpha)
|
||||||
|
{
|
||||||
|
Assert(parAlpha < 1.0f);
|
||||||
|
Assert(parAlpha >= 0.5f);
|
||||||
|
m_alphainvloginv = 1.0f / std::log(1.0f / m_alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
ScapegoatTree<K>::~ScapegoatTree() {
|
||||||
|
delete_nodes(m_root);
|
||||||
|
#if defined(DUCK_DEBUG)
|
||||||
|
m_root = NULL;
|
||||||
|
m_count = 0xDEADBEEF;
|
||||||
|
m_countMax = 0xDEADBEEF;
|
||||||
|
m_reserved = 0xDEADBEEF;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///I can't really find a good optimization for this method, so I'll just
|
||||||
|
///ignore the hint and rely on the normal insert().
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::iterator ScapegoatTree<K>::insert (const iterator&, const K& parValue) {
|
||||||
|
std::pair<iterator, bool> retVal = insert(parValue);
|
||||||
|
return retVal.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
std::pair<typename ScapegoatTree<K>::iterator, bool> ScapegoatTree<K>::insert (const K& parKey) {
|
||||||
|
const size_type depthHint = get_max_balanced_depth(std::max<size_type>(m_reserved, m_count + 1), m_alphainvloginv) + 3;
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "insert(): depthHint = " << depthHint << ", m_count = " << m_count << ", m_countMax = " << m_countMax << ", m_reserved = " << m_reserved << std::endl;
|
||||||
|
#endif
|
||||||
|
if (NULL == m_root) {
|
||||||
|
m_root = get_new_node(parKey);
|
||||||
|
m_root->left = m_root->right = NULL;
|
||||||
|
m_root->size = 1;
|
||||||
|
m_count = 1;
|
||||||
|
m_countMax = 1;
|
||||||
|
m_reserved = std::max(m_count, m_reserved);
|
||||||
|
|
||||||
|
IteratorOnPtr<NodeType**, NodeType*, false> rootWrapper(&m_root, 1);
|
||||||
|
return std::pair<iterator, bool>(iterator(rootWrapper, 1, depthHint), true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//Refuse to add a new item if the tree has more items than it
|
||||||
|
//can count
|
||||||
|
if (std::numeric_limits<size_type>::max() == m_count or std::numeric_limits<typename NodeType::size_type>::max() == m_root->size) {
|
||||||
|
AssertNotReached();
|
||||||
|
return std::pair<iterator, bool>(end(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeStack stack;
|
||||||
|
stack.reserve(depthHint);
|
||||||
|
NodeType* const closestMatch = get_insert_parent(m_root, parKey, stack);
|
||||||
|
Assert(stack.size() <= depthHint);
|
||||||
|
if (closestMatch->content == parKey)
|
||||||
|
return std::pair<iterator, bool>(iterator(stack.begin(), stack.size(), depthHint), false);
|
||||||
|
Assert(NULL == closestMatch->left or closestMatch->left->content < parKey);
|
||||||
|
Assert(NULL == closestMatch->right or parKey < closestMatch->right->content);
|
||||||
|
NodeType* const newNode = get_new_node(parKey);
|
||||||
|
newNode->left = newNode->right = NULL;
|
||||||
|
newNode->size = 1;
|
||||||
|
if (parKey < closestMatch->content) {
|
||||||
|
Assert(NULL == closestMatch->left);
|
||||||
|
closestMatch->left = newNode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert(NULL == closestMatch->right);
|
||||||
|
closestMatch->right = newNode;
|
||||||
|
}
|
||||||
|
++m_count;
|
||||||
|
m_countMax = std::max(m_count, m_countMax);
|
||||||
|
m_reserved = std::max(m_reserved, m_count);
|
||||||
|
|
||||||
|
//Update the count of every node
|
||||||
|
for (auto& node : stack) {
|
||||||
|
++node->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add the new node to the stack
|
||||||
|
Assert(stack.capacity() > stack.size());
|
||||||
|
stack.push_back(newNode);
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
const size_type totalSize = AssertNodeSize(m_root);
|
||||||
|
Assert(totalSize == m_count);
|
||||||
|
Assert(m_root->size == duckcore::checked_numcast<typename NodeType::size_type>(m_count));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_type newNodeDepth = stack.size() - 1;
|
||||||
|
//Rebalance if necessary
|
||||||
|
if (not is_alpha_height_balanced(newNodeDepth, this->size())) {
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
Assert(find_max_depth(m_root) == newNodeDepth);
|
||||||
|
#endif
|
||||||
|
Assert(get_max_balanced_depth(static_cast<size_type>(m_root->size), m_alphainvloginv) + 1 == newNodeDepth);
|
||||||
|
std::pair<NodeType*, NodeType*> scapegoatAndParent = find_scapegoat(stack);
|
||||||
|
AssertRelease(NULL != scapegoatAndParent.first);
|
||||||
|
NodeType* const newRoot = rebalance(scapegoatAndParent.first, newNodeDepth + 1);
|
||||||
|
NodeType* const parent = scapegoatAndParent.second;
|
||||||
|
if (parent == NULL) {
|
||||||
|
Assert(scapegoatAndParent.first == m_root);
|
||||||
|
m_root = newRoot;
|
||||||
|
m_countMax = m_count;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert(newRoot != parent);
|
||||||
|
if (parent->left == scapegoatAndParent.first) {
|
||||||
|
parent->left = newRoot;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert(parent->right == scapegoatAndParent.first);
|
||||||
|
parent->right = newRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
AssertNodeSize(m_root);
|
||||||
|
Assert(find_max_depth(m_root) <= get_max_balanced_depth(static_cast<size_type>(m_root->size), m_alphainvloginv));
|
||||||
|
#endif
|
||||||
|
//Rebuild the stack.
|
||||||
|
//TODO: this is ugly and slow, see if you can find a better way
|
||||||
|
stack.clear();
|
||||||
|
get_insert_parent(m_root, parKey, stack);
|
||||||
|
newNodeDepth = stack.size() - 1;
|
||||||
|
Assert(is_alpha_height_balanced(newNodeDepth, this->size()));
|
||||||
|
}
|
||||||
|
return std::pair<iterator, bool>(iterator(stack.begin(), newNodeDepth + 1, duckmath::log2_fast(m_reserved + 1)), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::rebalance (NodeType* parSubtree, size_type parDepthHint) {
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "Rebalancing subtree" << std::endl;
|
||||||
|
#endif
|
||||||
|
Assert(NULL != parSubtree);
|
||||||
|
NodeType* const newRoot = compress_first(parSubtree, parDepthHint);
|
||||||
|
NodeType* const retVal = compress(newRoot);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeTypePair ScapegoatTree<K>::find_scapegoat (NodeStack& parParents) const {
|
||||||
|
Assert(not parParents.empty());
|
||||||
|
const size_type height = parParents.size();
|
||||||
|
for (size_type z = parParents.size() - 1; z > 0; --z) {
|
||||||
|
NodeType& currNode = *(parParents[z - 1]);
|
||||||
|
Assert(height - z > 0);
|
||||||
|
if (not is_alpha_height_balanced(height - z, static_cast<size_type>(currNode.size))) {
|
||||||
|
NodeType* parent;
|
||||||
|
if (z == 1)
|
||||||
|
parent = NULL;
|
||||||
|
else
|
||||||
|
parent = parParents[z - 2];
|
||||||
|
return NodeTypePair(&currNode, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssertNotReached();
|
||||||
|
__builtin_unreachable();
|
||||||
|
return NodeTypePair(NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::get_new_node (const K& parKey) {
|
||||||
|
return new NodeType(parKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
template <typename T>
|
||||||
|
T* ScapegoatTree<K>::find_closest_match (T* parTree, const K& parKey) {
|
||||||
|
Assert(NULL != parTree);
|
||||||
|
//if (parTree->left and parKey <= parTree->left->content)
|
||||||
|
if (parTree->left and not (parTree->left->content < parKey))
|
||||||
|
return FindClosestMatch(parTree->left, parKey);
|
||||||
|
//else if (parTree->right and parKey >= parTree->right->content)
|
||||||
|
else if (parTree->right and not (parKey < parTree->right->content))
|
||||||
|
return FindClosestMatch(parTree->right, parKey);
|
||||||
|
else
|
||||||
|
return parTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::get_insert_parent (NodeType* parTree, const K& parKey, NodeStack& parRewind) {
|
||||||
|
Assert(NULL != parTree);
|
||||||
|
NodeType* retVal = parTree;
|
||||||
|
bool goLeft;
|
||||||
|
while ((goLeft = (retVal->left and parKey < retVal->content)) or (retVal->right and retVal->content < parKey)) {
|
||||||
|
parRewind.push_back(retVal);
|
||||||
|
if (goLeft) {
|
||||||
|
Assert(NULL != retVal->left);
|
||||||
|
Assert(parKey < retVal->content);
|
||||||
|
retVal = retVal->left;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Assert(NULL != retVal->right);
|
||||||
|
Assert(retVal->content < parKey);
|
||||||
|
retVal = retVal->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(NULL != retVal);
|
||||||
|
Assert(parRewind.empty() or parRewind.back() != retVal);
|
||||||
|
parRewind.push_back(retVal);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
void ScapegoatTree<K>::delete_nodes (NodeType* parNode) {
|
||||||
|
if (parNode) {
|
||||||
|
delete_nodes(parNode->left);
|
||||||
|
delete_nodes(parNode->right);
|
||||||
|
delete parNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
bool ScapegoatTree<K>::is_alpha_height_balanced (size_type parHeight, size_type parSize) const {
|
||||||
|
const float sz = static_cast<float>(parSize);
|
||||||
|
const float ha = std::floor(std::log(sz) * m_alphainvloginv);
|
||||||
|
const float height = static_cast<float>(parHeight);
|
||||||
|
return height <= ha;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Stout/Warren vine to tree.
|
||||||
|
///This function destroys the links of the given subtree and creates a
|
||||||
|
///structure that is suitable for compress(). Input is treated as if it
|
||||||
|
///was a linked list.
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::compress_first (NodeType* parFrom, size_type parDepthHint) {
|
||||||
|
const size_type vineSize = static_cast<size_type>(parFrom->size);
|
||||||
|
const size_type iteratorDepth = parDepthHint;
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "compress_first(): vineSize = " << vineSize << ", iteratorDepth = " << iteratorDepth << std::endl;
|
||||||
|
#endif
|
||||||
|
iterator itFirstStep(parFrom, iteratorDepth);
|
||||||
|
|
||||||
|
struct DummyNode {
|
||||||
|
DummyNode ( void ) : right(NULL) {}
|
||||||
|
NodeType* right;
|
||||||
|
};
|
||||||
|
class NodeWrapper {
|
||||||
|
public:
|
||||||
|
NodeWrapper ( void ) {}
|
||||||
|
explicit NodeWrapper ( NodeType* parOther ) : m_current(parOther), m_right(&parOther->right) {}
|
||||||
|
explicit NodeWrapper ( DummyNode* parOther ) : m_current(NULL), m_right(&parOther->right) {}
|
||||||
|
void Replace ( NodeType* parOther ) { m_current = parOther; m_right = &parOther->right; }
|
||||||
|
void Replace ( NodeWrapper* parOther ) { m_current = parOther->m_current; m_right = parOther->m_right; }
|
||||||
|
NodeType*& left ( void ) { Assert(m_current); return m_current->left; }
|
||||||
|
NodeType*& right ( void ) { Assert(m_right); return *m_right; }
|
||||||
|
typename NodeType::size_type& size ( void ) { Assert(m_current); return m_current->size; }
|
||||||
|
NodeType* pointer ( void ) { return m_current; }
|
||||||
|
private:
|
||||||
|
NodeType* m_current;
|
||||||
|
NodeType** m_right;
|
||||||
|
};
|
||||||
|
|
||||||
|
DummyNode pseudorootMem;
|
||||||
|
NodeWrapper parent(&pseudorootMem);
|
||||||
|
NodeWrapper current;
|
||||||
|
NodeWrapper child(&pseudorootMem);
|
||||||
|
|
||||||
|
size_type iterationsCount;
|
||||||
|
if (implem::is_power_of_two(vineSize + 1))
|
||||||
|
iterationsCount = (1 << (duckmath::log2_fast(vineSize))) - 1;
|
||||||
|
else
|
||||||
|
iterationsCount = (vineSize - ((1 << duckmath::log2_fast(vineSize)) - 1));
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "vineSize = " << vineSize << ", iterationsCount = " << iterationsCount << std::endl;
|
||||||
|
#endif
|
||||||
|
Assert(iterationsCount < vineSize); //Underflow error?
|
||||||
|
Assert(iterationsCount > 0);
|
||||||
|
for (size_type z = 0; z < iterationsCount; ++z) {
|
||||||
|
current.Replace(itFirstStep.GetPointer());
|
||||||
|
++itFirstStep;
|
||||||
|
child.Replace(itFirstStep.GetPointer());
|
||||||
|
++itFirstStep;
|
||||||
|
|
||||||
|
current.left() = NULL;
|
||||||
|
current.right() = NULL;
|
||||||
|
current.size() = 1;
|
||||||
|
parent.right() = child.pointer();
|
||||||
|
child.left() = current.pointer();
|
||||||
|
Assert(z * 2 < vineSize);
|
||||||
|
child.size() = static_cast<typename NodeType::size_type>(vineSize - z * 2);
|
||||||
|
Assert(child.size() >= 1);
|
||||||
|
|
||||||
|
parent.Replace(&child);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(iterationsCount * 2 <= vineSize);
|
||||||
|
for (size_type z = iterationsCount * 2; z < vineSize; ++z) {
|
||||||
|
child.right() = itFirstStep.GetPointer();
|
||||||
|
child.Replace(itFirstStep.GetPointer());
|
||||||
|
++itFirstStep;
|
||||||
|
child.left() = NULL;
|
||||||
|
child.size() = static_cast<typename NodeType::size_type>(vineSize - z);
|
||||||
|
}
|
||||||
|
child.right() = NULL;
|
||||||
|
child.size() = (child.left() ? 2 : 1);
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "Original size was " << vineSize << ", root's size is " << pseudorootMem.right->size << std::endl;
|
||||||
|
#endif
|
||||||
|
Assert(vineSize == static_cast<size_type>(pseudorootMem.right->size));
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
AssertNodeSize(pseudorootMem.right);
|
||||||
|
Assert(find_max_depth(pseudorootMem.right) <= vineSize);
|
||||||
|
#endif
|
||||||
|
return pseudorootMem.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Stout/Warren vine to tree.
|
||||||
|
///Performs the second and subsequent steps for compressing.
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::compress (NodeType* parFrom) {
|
||||||
|
Assert(NULL != parFrom);
|
||||||
|
//We don't know if the tree is complete, so let's calculate its depth
|
||||||
|
//rounded up to the nearest complete tree
|
||||||
|
const size_type treeSize = static_cast<size_type>(parFrom->size);
|
||||||
|
const size_type m = duckmath::log2_fast(1 + treeSize);
|
||||||
|
const size_type treeHeight = duckmath::log2_fast(treeSize + (1 << m));
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
size_type maxDepthInTree = find_max_depth(parFrom);
|
||||||
|
{
|
||||||
|
//We know step 0 of compression has already been done, so on with
|
||||||
|
//the spine size at step 1 (the one we're willing to do)
|
||||||
|
const size_type vineSize = (1 << (treeHeight - 1)) - 1;
|
||||||
|
size_type count = 0;
|
||||||
|
NodeType* currNode = parFrom;
|
||||||
|
do {
|
||||||
|
++count;
|
||||||
|
currNode = currNode->right;
|
||||||
|
} while (currNode);
|
||||||
|
const size_type countedNodes = ((count + 1) bitand ~1) - 1;
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "treeSize = " << treeSize << ", vineSize = " << vineSize << ", treeHeight = " << treeHeight << ", manually counted " << countedNodes << " nodes (" << count << ")\n";
|
||||||
|
#endif
|
||||||
|
Assert(implem::is_power_of_two(countedNodes + 1));
|
||||||
|
Assert(vineSize == countedNodes);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NodeType* retVal = parFrom;
|
||||||
|
|
||||||
|
Assert(treeHeight >= 2);
|
||||||
|
|
||||||
|
for (size_type k = 1; k < treeHeight - 1; ++k) {
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
std::cout << "compress() step " << k << std::endl;
|
||||||
|
#endif
|
||||||
|
//The rebalanced tree takes treeHeight-1 steps. One step has been done
|
||||||
|
//already, so we perform the remaining treeHeight-2 steps.
|
||||||
|
NodeType* scanner;
|
||||||
|
NodeType* child = retVal;
|
||||||
|
retVal = child->right;
|
||||||
|
Assert(NULL != retVal);
|
||||||
|
|
||||||
|
const size_type spineSize = (1 << (treeHeight - k - 1)) - 1;
|
||||||
|
Assert(spineSize > 0);
|
||||||
|
for (size_type s = 0; s < spineSize; ++s) {
|
||||||
|
scanner = child->right;
|
||||||
|
Assert(NULL != scanner);
|
||||||
|
child->right = scanner->left;
|
||||||
|
scanner->left = child;
|
||||||
|
|
||||||
|
const typename NodeType::size_type leftBranchSize = (child->right ? child->right->size : 0);
|
||||||
|
|
||||||
|
child->size -= scanner->size - leftBranchSize;
|
||||||
|
scanner->size += child->size - leftBranchSize;
|
||||||
|
|
||||||
|
if (s + 1 < spineSize) {
|
||||||
|
child = scanner->right;
|
||||||
|
Assert(NULL != child);
|
||||||
|
scanner->right = child->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
const size_type subtreeSize = (1 << (k + 1)) - 1;
|
||||||
|
std::cout << "k=" << k << " - Scanner's size updated to " << scanner->size << ", calculated subtreeSize = " << subtreeSize << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
Assert(NULL != scanner);
|
||||||
|
Assert(NULL != retVal);
|
||||||
|
Assert(treeSize == static_cast<size_type>(retVal->size));
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
AssertNodeSize(retVal);
|
||||||
|
{
|
||||||
|
const size_type newDepth = find_max_depth(retVal);
|
||||||
|
Assert(newDepth <= maxDepthInTree);
|
||||||
|
maxDepthInTree = newDepth;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(static_cast<size_type>(retVal->size) == treeSize);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::size_type ScapegoatTree<K>::assert_node_size (const NodeType* parSubtree) {
|
||||||
|
Assert(parSubtree);
|
||||||
|
|
||||||
|
typename NodeType::size_type localSize = 1;
|
||||||
|
if (parSubtree->left) {
|
||||||
|
localSize += AssertNodeSize(parSubtree->left);
|
||||||
|
}
|
||||||
|
if (parSubtree->right) {
|
||||||
|
localSize += AssertNodeSize(parSubtree->right);
|
||||||
|
}
|
||||||
|
Assert(parSubtree->size == localSize);
|
||||||
|
return localSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::size_type ScapegoatTree<K>::find_max_depth_rec (const NodeType* parSubtree) {
|
||||||
|
Assert(parSubtree);
|
||||||
|
typename NodeType::size_type depthLeft = 0;
|
||||||
|
typename NodeType::size_type depthRight = 0;
|
||||||
|
|
||||||
|
if (parSubtree->left) {
|
||||||
|
depthLeft = FindMaxDepth_rec(parSubtree->left);
|
||||||
|
}
|
||||||
|
if (parSubtree->right) {
|
||||||
|
depthRight = FindMaxDepth_rec(parSubtree->right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 + std::max(depthLeft, depthRight);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::size_type ScapegoatTree<K>::get_max_balanced_depth (size_type parSize, float parAlphaInvLogInv) {
|
||||||
|
const float ha = std::log(static_cast<float>(parSize)) * parAlphaInvLogInv;
|
||||||
|
return static_cast<size_type>(ha);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::iterator ScapegoatTree<K>::begin() {
|
||||||
|
if (NULL == m_root)
|
||||||
|
return iterator();
|
||||||
|
|
||||||
|
const size_type depthHint = get_tree_min_depth_ib(m_reserved, m_alphainvloginv) + 1;
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
Assert(is_alpha_height_balanced(find_max_depth(m_root), static_cast<size_type>(m_root->size)));
|
||||||
|
Assert(find_max_depth(m_root) + 1 <= depthHint);
|
||||||
|
#endif
|
||||||
|
return iterator(m_root, depthHint + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::const_iterator ScapegoatTree<K>::begin() const {
|
||||||
|
if (NULL == m_root)
|
||||||
|
return iterator();
|
||||||
|
|
||||||
|
const size_type depthHint = get_tree_min_depth_ib(m_count, m_alphainvloginv) + 1;
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
Assert(is_alpha_height_balanced(find_max_depth(m_root), static_cast<size_type>(m_root->size)));
|
||||||
|
Assert(find_max_depth(m_root) + 1 <= depthHint);
|
||||||
|
#endif
|
||||||
|
return const_iterator(m_root, depthHint + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Get min tree depth if balanced
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::size_type ScapegoatTree<K>::get_tree_min_depth_ib (size_type parSize, float parAlphaInvLogInv) {
|
||||||
|
const float sz = static_cast<float>(parSize);
|
||||||
|
const size_type roundedDownDepthBase2 = duckmath::log2_fast(parSize + 1);
|
||||||
|
const float nodesAtLastLevel = (roundedDownDepthBase2 == 0 ? 0 : static_cast<float>(1 << roundedDownDepthBase2) - 1.0f);
|
||||||
|
const float retVal = std::log(sz + nodesAtLastLevel) * parAlphaInvLogInv;
|
||||||
|
return static_cast<size_type>(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
void ScapegoatTree<K>::clear() {
|
||||||
|
delete_nodes(m_root);
|
||||||
|
m_count = 0;
|
||||||
|
m_countMax = 0;
|
||||||
|
m_root = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::find_ifp (NodeType* parTree, const K& parKey) {
|
||||||
|
while (parTree) {
|
||||||
|
if (parKey < parTree->content)
|
||||||
|
parTree = parTree->left;
|
||||||
|
else if (parTree->content < parKey)
|
||||||
|
parTree = parTree->right;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return parTree;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::find_parent_ifp (NodeType* parTree, const NodeType* parSearchNodeAddr) {
|
||||||
|
if (parTree == parSearchNodeAddr)
|
||||||
|
return NULL;
|
||||||
|
NodeType* parent = parTree;
|
||||||
|
while (parTree and (parTree->left != parSearchNodeAddr and parTree->right != parSearchNodeAddr)) {
|
||||||
|
if (parSearchNodeAddr->content < parTree->content)
|
||||||
|
parTree = parTree->left;
|
||||||
|
else if (parTree->content < parSearchNodeAddr->content)
|
||||||
|
parTree = parTree->right;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
parent = parTree;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Set parSuccessor to true to get the in-order successor, or false to
|
||||||
|
///get the in-order predecessor.
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
typename ScapegoatTree<K>::NodeType* ScapegoatTree<K>::detach_bottom_node (NodeType* parTree, bool parSuccessor) {
|
||||||
|
AssertRelease(NULL != parTree);
|
||||||
|
AssertRelease(NULL != parTree->left and NULL != parTree->right);
|
||||||
|
if (parSuccessor) {
|
||||||
|
NodeType* inorderSuccessor = parTree->right;
|
||||||
|
NodeType* successorsParent = parTree;
|
||||||
|
while (NULL != inorderSuccessor->left) {
|
||||||
|
successorsParent = inorderSuccessor;
|
||||||
|
inorderSuccessor = inorderSuccessor->left;
|
||||||
|
}
|
||||||
|
Assert(inorderSuccessor == successorsParent->left and inorderSuccessor != successorsParent->right);
|
||||||
|
Assert(NULL == inorderSuccessor->left);
|
||||||
|
Assert(NULL == inorderSuccessor->right or inorderSuccessor->right->content < successorsParent->content);
|
||||||
|
successorsParent->left = inorderSuccessor->right;
|
||||||
|
Assert(inorderSuccessor);
|
||||||
|
return inorderSuccessor;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NodeType* inorderPredecessor = parTree->left;
|
||||||
|
NodeType* predecessorsParent = parTree;
|
||||||
|
while (NULL != inorderPredecessor->right) {
|
||||||
|
predecessorsParent = inorderPredecessor;
|
||||||
|
inorderPredecessor = inorderPredecessor->right;
|
||||||
|
}
|
||||||
|
Assert(inorderPredecessor == predecessorsParent->right and inorderPredecessor != predecessorsParent->left);
|
||||||
|
Assert(NULL == inorderPredecessor->right);
|
||||||
|
Assert(NULL == inorderPredecessor->left or predecessorsParent->content < inorderPredecessor->left->content);
|
||||||
|
predecessorsParent->right = inorderPredecessor->left;
|
||||||
|
Assert(inorderPredecessor);
|
||||||
|
return inorderPredecessor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
bool ScapegoatTree<K>::include (const K& parSearch) const {
|
||||||
|
const NodeType* const found = find_ifp(m_root, parSearch);
|
||||||
|
Assert(not found or not (parSearch < found->content or found->content < parSearch));
|
||||||
|
return static_cast<bool>(NULL != found);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
void ScapegoatTree<K>::rebalance_after_deletion_ifn() {
|
||||||
|
const float sz = static_cast<float>(m_count);
|
||||||
|
const float m = static_cast<float>(m_countMax);
|
||||||
|
if (sz < m_alpha * m) {
|
||||||
|
const size_type sizeHint = static_cast<size_type>(std::ceil(std::log(sz) * m_alphainvloginv)) + 1;
|
||||||
|
rebalance(m_root, sizeHint);
|
||||||
|
m_countMax = m_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
void ScapegoatTree<K>::erase (iterator parItem) {
|
||||||
|
NodeType* const dele = parItem.GetPointer();
|
||||||
|
AssertRelease(NULL != dele);
|
||||||
|
Assert(m_count > 0);
|
||||||
|
|
||||||
|
NodeType* parent;
|
||||||
|
if (dele == m_root)
|
||||||
|
parent = m_root;
|
||||||
|
else
|
||||||
|
parent = find_parent_ifp(m_root, dele);
|
||||||
|
Assert(NULL != parent);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
Assert(dele == parent->left or dele == parent->right or dele == parent);
|
||||||
|
NodeType*& parentChildRef = (dele == m_root ? m_root : (dele == parent->left ? parent->left : parent->right));
|
||||||
|
if (NULL == dele->left and NULL == dele->right) {
|
||||||
|
parentChildRef = NULL;
|
||||||
|
}
|
||||||
|
else if (NULL == dele->left xor NULL == dele->right) {
|
||||||
|
parentChildRef = (dele->left ? dele->left : dele->right);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NodeType* const inorderSuccessor = detach_bottom_node(dele, true);
|
||||||
|
|
||||||
|
parentChildRef = inorderSuccessor;
|
||||||
|
inorderSuccessor->left = dele->left;
|
||||||
|
inorderSuccessor->right = dele->right;
|
||||||
|
}
|
||||||
|
Assert(NULL == parent->left or parent->left->content < parent->content);
|
||||||
|
Assert(NULL == parent->right or parent->content < parent->right->content);
|
||||||
|
}
|
||||||
|
--m_count;
|
||||||
|
delete dele;
|
||||||
|
rebalance_after_deletion_ifn();
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
inline typename ScapegoatTree<K>::iterator ScapegoatTree<K>::find (const K& parSearch) {
|
||||||
|
NodeStack stack;
|
||||||
|
const size_type depthHint = get_max_balanced_depth(std::max<size_type>(m_reserved, m_count + 1), m_alphainvloginv) + 3;
|
||||||
|
stack.reserve(depthHint);
|
||||||
|
NodeType* const closestMatch = get_insert_parent(m_root, parSearch, stack);
|
||||||
|
Assert(stack.size() <= depthHint);
|
||||||
|
if (closestMatch->content == parSearch)
|
||||||
|
return iterator(stack.begin(), stack.size(), depthHint);
|
||||||
|
else
|
||||||
|
return this->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename K>
|
||||||
|
inline typename ScapegoatTree<K>::const_iterator ScapegoatTree<K>::find (const K& parSearch) const {
|
||||||
|
return const_cast<ScapegoatTree<K>*>(this)->find(parSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///-------------------------------------------------------------------------
|
||||||
|
// ///-------------------------------------------------------------------------
|
||||||
|
// template <typename K>
|
||||||
|
// void ScapegoatTree<K>::erase (iterator parFrom, iterator parLast) {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ///-------------------------------------------------------------------------
|
||||||
|
// ///-------------------------------------------------------------------------
|
||||||
|
// template <typename K>
|
||||||
|
// typename ScapegoatTree<K>::size_type ScapegoatTree<K>::erase (typename Loki::TypeTraits<value_type>::ParameterType parKey) {
|
||||||
|
// }
|
||||||
|
} //namespace duckmem
|
258
include/duckhandy/implem/tree_iterator.inl
Normal file
258
include/duckhandy/implem/tree_iterator.inl
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
namespace Implem {
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
bool TreeIterator_base<P>::Exhausted() const {
|
||||||
|
return m_stack.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
TreeIterator_base<P>::TreeIterator_base (const TreeIterator_base& parOther) :
|
||||||
|
m_stack()
|
||||||
|
{
|
||||||
|
m_stack.reserve(parOther.m_stack.capacity());
|
||||||
|
m_stack = parOther.m_stack;
|
||||||
|
Assert(m_stack.capacity() == parOther.m_stack.capacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
template <typename P1>
|
||||||
|
TreeIterator_base<P>::TreeIterator_base (const TreeIterator_base<P1>& parOther) {
|
||||||
|
typename TreeIterator_base<P1>::StackType otherStackCopy(parOther.m_stack);
|
||||||
|
std::vector<P> localCopy;
|
||||||
|
localCopy.reserve(parOther.m_stack.size());
|
||||||
|
while (not otherStackCopy.empty()) {
|
||||||
|
P convertedItem = otherStackCopy.back();
|
||||||
|
localCopy.push_back(convertedItem);
|
||||||
|
otherStackCopy.pop_back();
|
||||||
|
}
|
||||||
|
m_stack.reserve(parOther.m_stack.capacity());
|
||||||
|
for (typename std::vector<P>::reverse_iterator itRev = localCopy.rbegin(), itRevEnd = localCopy.rend(); itRev != itRevEnd; ++itRev) {
|
||||||
|
Assert(m_stack.capacity() > m_stack.size());
|
||||||
|
m_stack.push_back(*itRev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::reference TreeIterator_const_layer<T, N, false>::operator* () {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::const_reference TreeIterator_const_layer<T, N, false>::operator* () const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::pointer TreeIterator_const_layer<T, N, false>::operator-> () {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return &this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::const_pointer TreeIterator_const_layer<T, N, false>::operator-> () const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return &this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
N* TreeIterator_const_layer<T, N, false>::GetPointer() {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const N* TreeIterator_const_layer<T, N, false>::GetPointer() const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, true>::const_reference TreeIterator_const_layer<T, N, true>::operator* () const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, true>::const_pointer TreeIterator_const_layer<T, N, true>::operator-> () const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return &this->m_stack.back()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const N* TreeIterator_const_layer<T, N, true>::GetPointer() const {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
return this->m_stack.back();
|
||||||
|
}
|
||||||
|
} //namespace Implem
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator (const TreeIterator& parOther) :
|
||||||
|
parent_type(parOther)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
template <typename T1>
|
||||||
|
TreeIterator<T, N>::TreeIterator (const TreeIterator<T1, N>& parOther) :
|
||||||
|
parent_type(parOther)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
template <typename S>
|
||||||
|
TreeIterator<T, N>::TreeIterator (S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint) {
|
||||||
|
AssertRelease(parStackLen > 0);
|
||||||
|
this->m_stack.reserve(std::max(parStackLen, parMaxDepthHint));
|
||||||
|
typename StackType::value_type prevNode = *parCopyStackBottomUp;
|
||||||
|
++parCopyStackBottomUp;
|
||||||
|
this->m_stack.push_back(prevNode);
|
||||||
|
|
||||||
|
for (size_type z = 1; z < parStackLen; ++z) {
|
||||||
|
typename StackType::value_type currNode = *parCopyStackBottomUp;
|
||||||
|
if (prevNode->left == currNode) {
|
||||||
|
Assert(this->m_stack.capacity() > this->m_stack.size());
|
||||||
|
this->m_stack.push_back(currNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//If you get this assertion make sure the iterator you are
|
||||||
|
//passing in is reversed (ie: from leaf to root)
|
||||||
|
AssertRelease(currNode == prevNode->right);
|
||||||
|
this->m_stack.pop_back();
|
||||||
|
this->m_stack.push_back(currNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevNode = currNode;
|
||||||
|
++parCopyStackBottomUp;
|
||||||
|
}
|
||||||
|
Assert(not this->Exhausted());
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator (NodeTypePointer parRoot, size_type parMaxDepthHint) {
|
||||||
|
if (parMaxDepthHint > 0)
|
||||||
|
this->m_stack.reserve(parMaxDepthHint);
|
||||||
|
RecurseLeft(parRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::~TreeIterator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Post-increment
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N> TreeIterator<T, N>::operator++ (int) {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
TreeIterator<T, N> retVal = *this;
|
||||||
|
++(*this);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Pre-increment
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>& TreeIterator<T, N>::operator++() {
|
||||||
|
AssertRelease(not this->Exhausted());
|
||||||
|
NodeTypePointer currNode = this->m_stack.back();
|
||||||
|
#if defined(ASSERTIONSENABLED)
|
||||||
|
const size_type stackCapacity = this->m_stack.capacity();
|
||||||
|
#endif
|
||||||
|
AssertRelease(not this->m_stack.empty());
|
||||||
|
this->m_stack.pop_back();
|
||||||
|
#if defined(ASSERTIONSENABLED)
|
||||||
|
//It shouldn't normally happen, but it's just to make sure
|
||||||
|
Assert(stackCapacity == this->m_stack.capacity());
|
||||||
|
#endif
|
||||||
|
RecurseLeft(currNode->right);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const TreeIterator<T, N>& TreeIterator<T, N>::operator= (const TreeIterator& parOther) {
|
||||||
|
this->m_stack = parOther.m_stack;
|
||||||
|
Assert(this->m_stack.capacity() >= parOther.m_stack.capacity());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
bool TreeIterator<T, N>::operator== (const TreeIterator& parOther) const {
|
||||||
|
return this->m_stack.size() == parOther.m_stack.size() and (this->m_stack.empty() or parOther.m_stack.back() == this->m_stack.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
void TreeIterator<T, N>::RecurseLeft (NodeTypePointer parFrom) {
|
||||||
|
NodeTypePointer currNode = parFrom;
|
||||||
|
while (NULL != currNode) {
|
||||||
|
Assert(this->m_stack.capacity() > this->m_stack.size());
|
||||||
|
this->m_stack.push_back(currNode);
|
||||||
|
currNode = currNode->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //namespace duckmem
|
76
include/duckhandy/log2_fast.hpp
Normal file
76
include/duckhandy/log2_fast.hpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef id28695193476D4A9499151FC175A49196
|
||||||
|
#define id28695193476D4A9499151FC175A49196
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#if defined(__linux)
|
||||||
|
# include <strings.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace duckmath {
|
||||||
|
namespace implem {
|
||||||
|
#if !defined(__linux)
|
||||||
|
[[gnu::pure]]
|
||||||
|
inline uint_fast32_t LowestBitSet ( uint_fast32_t x ) pure_function;
|
||||||
|
|
||||||
|
//Precomputed lookup table
|
||||||
|
const constexpr uint_fast32_t g_multiplyDeBruijnBitPosition[32] = {
|
||||||
|
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
|
||||||
|
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
|
||||||
|
};
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///Thanks to: http://bits.stephan-brumme.com/lowestBitSet.html
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
[[gnu::pure]]
|
||||||
|
inline uint_fast32_t LowestBitSet (uint_fast32_t x) {
|
||||||
|
//Leave only lowest bit
|
||||||
|
x &= -i32(x);
|
||||||
|
//DeBruijn constant
|
||||||
|
x *= 0x077CB531;
|
||||||
|
//Get upper 5 bits
|
||||||
|
x >>= 27;
|
||||||
|
//Convert to actual position
|
||||||
|
return g_multiplyDeBruijnBitPosition[x];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
inline uint32_t GetHighestBitOnly (uint32_t parIn) {
|
||||||
|
parIn |= (parIn >> 1);
|
||||||
|
parIn |= (parIn >> 2);
|
||||||
|
parIn |= (parIn >> 4);
|
||||||
|
parIn |= (parIn >> 8);
|
||||||
|
parIn |= (parIn >> 16);
|
||||||
|
return parIn - (parIn >> 1);
|
||||||
|
}
|
||||||
|
} //namespace implem
|
||||||
|
|
||||||
|
uint_fast32_t log2_fast (uint_fast32_t parX) {
|
||||||
|
const uint_fast32_t highestPow2 = implem::GetHighestBitOnly(parX);
|
||||||
|
#if defined(__linux)
|
||||||
|
const uint_fast32_t retVal = ffs(highestPow2) - 1;
|
||||||
|
#else
|
||||||
|
const uint_fast32_t retVal = LowestBitSet(highestPow2);
|
||||||
|
#endif
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
} //namespace duckmath
|
||||||
|
|
||||||
|
#endif
|
51
include/duckhandy/scapegoat_map.hpp
Normal file
51
include/duckhandy/scapegoat_map.hpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef idDD2D1A57ABEB4BEEA0F7E46C347D1637
|
||||||
|
#define idDD2D1A57ABEB4BEEA0F7E46C347D1637
|
||||||
|
|
||||||
|
#include "scapegoat_tree.hpp"
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
template <typename K, typename V, typename Hasher>
|
||||||
|
class ScapegoatMap {
|
||||||
|
public:
|
||||||
|
typedef K key_type;
|
||||||
|
typedef V value_type;
|
||||||
|
|
||||||
|
ScapegoatMap ( void );
|
||||||
|
~ScapegoatMap ( void );
|
||||||
|
|
||||||
|
private:
|
||||||
|
class DerivedPair : public std::pair<key_type, value_type> {
|
||||||
|
typedef std::pair<key_type, value_type> parent_type;
|
||||||
|
public:
|
||||||
|
DerivedPair ( void );
|
||||||
|
DerivedPair ( typename Loki::TypeTraits<key_type>::ParameterType parKey, typename Loki::TypeTraits<value_type>::ParameterType parValue );
|
||||||
|
~DerivedPair ( void );
|
||||||
|
|
||||||
|
bool operator< ( const DerivedPair& parOther ) const;
|
||||||
|
bool operator== ( const DerivedPair& parOther ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScapegoatTree<DerivedPair> m_tree;
|
||||||
|
};
|
||||||
|
} //namespace duckmem
|
||||||
|
|
||||||
|
#include "implem/scapegoat_map.inl"
|
||||||
|
|
||||||
|
#endif
|
163
include/duckhandy/scapegoat_tree.hpp
Normal file
163
include/duckhandy/scapegoat_tree.hpp
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef id79CEDB2530B54204A6BEDCBE0B767EA1
|
||||||
|
#define id79CEDB2530B54204A6BEDCBE0B767EA1
|
||||||
|
|
||||||
|
#include "tree_iterator.hpp"
|
||||||
|
#include "implem/IteratorOnPtr.hpp"
|
||||||
|
#include "log2_fast.hpp"
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
//#define SCAPEGOATTREE_VERBOSE
|
||||||
|
//#define SCAPEGOATTREE_PARANOID
|
||||||
|
|
||||||
|
#define SCAPEGOATTREE_DYNAMIC_SIZE_TYPE
|
||||||
|
|
||||||
|
#if defined(DUCK_FINAL)
|
||||||
|
# if defined(SCAPEGOATTREE_VERBOSE)
|
||||||
|
# undef(SCAPEGOATTREE_VERBOSE)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#if !defined(ASSERTIONSENABLED)
|
||||||
|
# if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
# undef(SCAPEGOATTREE_PARANOID)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
using dhandy::IteratorOnPtr;
|
||||||
|
|
||||||
|
namespace implem {
|
||||||
|
[[gnu::pure]]
|
||||||
|
inline bool is_power_of_two (unsigned int parValue) {
|
||||||
|
return (parValue != 0 and (parValue bitand (~parValue + 1)) == parValue);
|
||||||
|
}
|
||||||
|
} //namespace implem
|
||||||
|
|
||||||
|
//TODO: implement or remove
|
||||||
|
class SmallObject {
|
||||||
|
public:
|
||||||
|
protected:
|
||||||
|
SmallObject ( void )=default;
|
||||||
|
~SmallObject ( void ) noexcept =default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
class ScapegoatTree {
|
||||||
|
public:
|
||||||
|
typedef K value_type;
|
||||||
|
typedef size_t size_type;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename SizeType>
|
||||||
|
struct TreeNodeStruct : public SmallObject {
|
||||||
|
typedef SizeType size_type;
|
||||||
|
TreeNodeStruct ( void );
|
||||||
|
TreeNodeStruct ( const K& parContent ) : content(parContent) {}
|
||||||
|
|
||||||
|
K content;
|
||||||
|
TreeNodeStruct* left;
|
||||||
|
TreeNodeStruct* right;
|
||||||
|
size_type size;
|
||||||
|
};
|
||||||
|
#if defined(SCAPEGOATTREE_DYNAMIC_SIZE_TYPE)
|
||||||
|
typedef typename std::conditional<sizeof(TreeNodeStruct<size_type>) == sizeof(TreeNodeStruct<uint32_t>), size_type, uint32_t>::type TreeNodeSizeType;
|
||||||
|
typedef TreeNodeStruct<TreeNodeSizeType> TreeNode;
|
||||||
|
#else
|
||||||
|
typedef TreeNodeStruct<size_type> TreeNode;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static_assert(sizeof(typename TreeNode::size_type) <= sizeof(size_type), "Mismatching size_type size");
|
||||||
|
typedef TreeNode NodeType;
|
||||||
|
typedef std::vector<NodeType*> NodeStack;
|
||||||
|
typedef std::pair<NodeType*, NodeType*> NodeTypePair;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef value_type& reference;
|
||||||
|
typedef const value_type* const_pointer;
|
||||||
|
typedef const value_type& const_reference;
|
||||||
|
typedef duckmem::TreeIterator<value_type, NodeType> iterator;
|
||||||
|
typedef duckmem::TreeIterator<const value_type, NodeType> const_iterator;
|
||||||
|
|
||||||
|
ScapegoatTree ( void );
|
||||||
|
explicit ScapegoatTree ( float parAlpha );
|
||||||
|
~ScapegoatTree ( void );
|
||||||
|
|
||||||
|
float get_alpha ( void ) const { return m_alpha; }
|
||||||
|
bool include ( const K& parSearch ) const;
|
||||||
|
std::pair<iterator, bool> insert ( const K& parKey );
|
||||||
|
iterator insert ( const iterator&, const K& parValue );
|
||||||
|
bool empty ( void ) const { return m_count == 0; }
|
||||||
|
size_type size ( void ) const { return m_count; }
|
||||||
|
void reserve ( size_type parSize ) { m_reserved = std::max(parSize, m_count); }
|
||||||
|
size_type capacity ( void ) const { return m_reserved; }
|
||||||
|
void clear ( void );
|
||||||
|
void erase ( iterator parItem );
|
||||||
|
void erase ( iterator parFrom, iterator parLast );
|
||||||
|
size_type erase ( const value_type& parKey );
|
||||||
|
iterator find ( const K& parSearch );
|
||||||
|
const_iterator find ( const K& parSearch ) const;
|
||||||
|
|
||||||
|
iterator begin ( void );
|
||||||
|
const_iterator begin ( void ) const;
|
||||||
|
iterator end ( void ) { return iterator(); }
|
||||||
|
const_iterator end ( void ) const { return const_iterator(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
static T* find_closest_match ( T* parTree, const K& parKey );
|
||||||
|
static NodeType* get_insert_parent ( NodeType* parTree, const K& parKey, NodeStack& parRewind );
|
||||||
|
static NodeType* find_ifp ( NodeType* parTree, const K& parKey );
|
||||||
|
static NodeType* find_parent_ifp ( NodeType* parTree, const NodeType* parSearchNodeAddr );
|
||||||
|
static NodeType* get_new_node ( const K& parKey );
|
||||||
|
static void delete_nodes ( NodeType* parNode );
|
||||||
|
[[gnu::pure]] bool is_alpha_height_balanced ( size_type parHeight, size_type parSize ) const;
|
||||||
|
NodeTypePair find_scapegoat ( NodeStack& parParents ) const;
|
||||||
|
static NodeType* rebalance ( NodeType* parSubtree, size_type parDepthHint );
|
||||||
|
static NodeType* compress_first ( NodeType* parFrom, size_type parDepthHint );
|
||||||
|
static NodeType* compress ( NodeType* parFrom );
|
||||||
|
[[gnu::pure]] static size_type get_max_balanced_depth ( size_type parSize, float parAlphaInvLogInv );
|
||||||
|
[[gnu::pure]] static size_type get_tree_min_depth_ib ( size_type parSize, float parAlphaInvLogInv );
|
||||||
|
static NodeType* detach_bottom_node ( NodeType* parTree, bool parSuccessor );
|
||||||
|
void rebalance_after_deletion_ifn ( void );
|
||||||
|
|
||||||
|
#if defined(SCAPEGOATTREE_PARANOID)
|
||||||
|
static size_type assert_node_size ( const NodeType* parSubtree );
|
||||||
|
static size_type find_max_depth ( const NodeType* parSubtree ) { return FindMaxDepth_rec(parSubtree) - 1; }
|
||||||
|
static size_type find_max_depth_rec ( const NodeType* parSubtree );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NodeType* m_root;
|
||||||
|
size_type m_count;
|
||||||
|
size_type m_countMax;
|
||||||
|
size_type m_reserved;
|
||||||
|
float m_alpha;
|
||||||
|
float m_alphainvloginv;
|
||||||
|
};
|
||||||
|
} //namespace duckmem
|
||||||
|
|
||||||
|
#include "implem/scapegoat_tree.inl"
|
||||||
|
|
||||||
|
#endif
|
149
include/duckhandy/tree_iterator.hpp
Normal file
149
include/duckhandy/tree_iterator.hpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/* Copyright 2016-2020 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef id6109D5EDE99D43C4909F084A231BF2C2
|
||||||
|
#define id6109D5EDE99D43C4909F084A231BF2C2
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#if !defined(AssertNotReached)
|
||||||
|
# define AssertNotReached() assert(false)
|
||||||
|
#endif
|
||||||
|
#if !defined(AssertRelease)
|
||||||
|
# define AssertRelease(a) assert(a)
|
||||||
|
#endif
|
||||||
|
#if !defined(Assert)
|
||||||
|
# define Assert(a) assert(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace duckmem {
|
||||||
|
namespace Implem {
|
||||||
|
template <typename P>
|
||||||
|
class TreeIterator_base {
|
||||||
|
template <typename P1> friend class TreeIterator_base;
|
||||||
|
public:
|
||||||
|
explicit TreeIterator_base ( void ) {}
|
||||||
|
TreeIterator_base ( const TreeIterator_base& parOther );
|
||||||
|
template <typename P1>
|
||||||
|
explicit TreeIterator_base ( const TreeIterator_base<P1>& parOther );
|
||||||
|
~TreeIterator_base ( void ) {}
|
||||||
|
protected:
|
||||||
|
typedef std::vector<P> StackType;
|
||||||
|
typedef size_t size_type;
|
||||||
|
typedef ptrdiff_t difference_type;
|
||||||
|
bool Exhausted ( void ) const;
|
||||||
|
StackType m_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename N, bool Const>
|
||||||
|
class TreeIterator_const_layer;
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator_const_layer<T, N, true> : protected TreeIterator_base<const N*> {
|
||||||
|
template <typename T1, typename N1, bool Const1> friend class TreeIterator_const_layer;
|
||||||
|
typedef TreeIterator_base<const N*> parent_type;
|
||||||
|
public:
|
||||||
|
typedef const T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef const T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef N NodeType;
|
||||||
|
typedef const N* NodeTypePointer;
|
||||||
|
enum { IS_CONST = 1 };
|
||||||
|
TreeIterator_const_layer ( void ) {}
|
||||||
|
TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {}
|
||||||
|
TreeIterator_const_layer ( TreeIterator_const_layer&& ) = default;
|
||||||
|
template <typename T1, bool C1>
|
||||||
|
explicit TreeIterator_const_layer ( const TreeIterator_const_layer<T1, N, C1>& parOther ) : parent_type(parOther) {}
|
||||||
|
const_reference operator* ( void ) const;
|
||||||
|
const_pointer operator-> ( void ) const;
|
||||||
|
const N* GetPointer ( void ) const;
|
||||||
|
protected:
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
};
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator_const_layer<T, N, false> : protected TreeIterator_base<N*> {
|
||||||
|
template <typename T1, typename N1, bool Const1> friend class TreeIterator_const_layer;
|
||||||
|
typedef TreeIterator_base<N*> parent_type;
|
||||||
|
public:
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef N NodeType;
|
||||||
|
typedef N* NodeTypePointer;
|
||||||
|
enum { IS_CONST = 0 };
|
||||||
|
TreeIterator_const_layer ( void ) {}
|
||||||
|
TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {}
|
||||||
|
template <typename T1, bool C1>
|
||||||
|
explicit TreeIterator_const_layer ( const TreeIterator_const_layer<T1, N, C1>& parOther ) : parent_type(parOther) {}
|
||||||
|
reference operator* ( void );
|
||||||
|
const_reference operator* ( void ) const;
|
||||||
|
pointer operator-> ( void );
|
||||||
|
const_pointer operator-> ( void ) const;
|
||||||
|
const N* GetPointer ( void ) const;
|
||||||
|
N* GetPointer ( void );
|
||||||
|
protected:
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
};
|
||||||
|
} //namespace Implem
|
||||||
|
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator : public Implem::TreeIterator_const_layer<T, N, std::is_const<T>::value> {
|
||||||
|
typedef Implem::TreeIterator_const_layer<T, N, std::is_const<T>::value> parent_type;
|
||||||
|
typedef typename parent_type::NodeTypePointer NodeTypePointer;
|
||||||
|
typedef typename parent_type::NodeType NodeType;
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef std::forward_iterator_tag iterator_category;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::pointer pointer;
|
||||||
|
typedef typename parent_type::const_pointer const_pointer;
|
||||||
|
typedef typename parent_type::reference reference;
|
||||||
|
typedef typename parent_type::const_reference const_reference;
|
||||||
|
|
||||||
|
TreeIterator ( void );
|
||||||
|
TreeIterator ( const TreeIterator& parOther );
|
||||||
|
TreeIterator ( NodeTypePointer parRoot, size_type parMaxDepthHint );
|
||||||
|
template <typename S>
|
||||||
|
TreeIterator ( S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint );
|
||||||
|
template <typename T1>
|
||||||
|
TreeIterator ( const TreeIterator<T1, N>& parOther );
|
||||||
|
~TreeIterator ( void );
|
||||||
|
|
||||||
|
const TreeIterator& operator= ( const TreeIterator& parOther );
|
||||||
|
bool operator== ( const TreeIterator& parOther ) const;
|
||||||
|
bool operator!= ( const TreeIterator& parOther ) const { return not (*this == parOther); }
|
||||||
|
TreeIterator& operator++ ( void ); //pre
|
||||||
|
TreeIterator operator++ ( int ); //post
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RecurseLeft ( NodeTypePointer parFrom );
|
||||||
|
};
|
||||||
|
} //namespace duckmem
|
||||||
|
|
||||||
|
#include "implem/tree_iterator.inl"
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,6 +7,7 @@ add_executable(${PROJECT_NAME}
|
||||||
reversed_sized_array_test.cpp
|
reversed_sized_array_test.cpp
|
||||||
bitfield_pack_test.cpp
|
bitfield_pack_test.cpp
|
||||||
resource_pool_test.cpp
|
resource_pool_test.cpp
|
||||||
|
scapegoat_tree_test.cpp
|
||||||
)
|
)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
|
||||||
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
152
test/unit/scapegoat_tree_test.cpp
Normal file
152
test/unit/scapegoat_tree_test.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/* Copyright 2016-2018 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" 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.
|
||||||
|
*
|
||||||
|
* "duckhandy" 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 "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
#include "duckhandy/scapegoat_tree.hpp"
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
TEST_CASE ("Insert values in a ScapegoatTree", "[Scapegoat][ScapegoatTree][containers]") {
|
||||||
|
using duckmem::ScapegoatTree;
|
||||||
|
|
||||||
|
ScapegoatTree<int> tree;
|
||||||
|
{
|
||||||
|
auto it = tree.insert(25);
|
||||||
|
CHECK(it.second == true);
|
||||||
|
CHECK(it.first != tree.end());
|
||||||
|
CHECK(*it.first == 25);
|
||||||
|
CHECK(tree.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto it = tree.insert(25);
|
||||||
|
CHECK(tree.size() == 1);
|
||||||
|
CHECK(it.second == false);
|
||||||
|
CHECK(it.first != tree.end());
|
||||||
|
CHECK(*it.first == 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto it = tree.insert(26);
|
||||||
|
CHECK(it.second == true);
|
||||||
|
CHECK(it.first != tree.end());
|
||||||
|
CHECK(*it.first == 26);
|
||||||
|
CHECK(tree.size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
for (std::size_t z = 0; z < 100; ++z) {
|
||||||
|
const int val = static_cast<int>(z);
|
||||||
|
|
||||||
|
auto it = tree.insert(val);
|
||||||
|
if (z < 25) {
|
||||||
|
CHECK(tree.size() == z + 2 + 1);
|
||||||
|
CHECK(it.second == true);
|
||||||
|
}
|
||||||
|
else if (z < 26) {
|
||||||
|
CHECK(tree.size() == z + 1 + 1);
|
||||||
|
CHECK(it.second == false);
|
||||||
|
}
|
||||||
|
else if (z < 27) {
|
||||||
|
CHECK(tree.size() == z + 1);
|
||||||
|
CHECK(it.second == false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CHECK(tree.size() == z + 1);
|
||||||
|
CHECK(it.second == true);
|
||||||
|
}
|
||||||
|
CHECK(*it.first == val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto end = tree.end();
|
||||||
|
int val = 0;
|
||||||
|
for (auto it = tree.begin(); it != end; ++it, ++val) {
|
||||||
|
CHECK(val == *it);
|
||||||
|
}
|
||||||
|
CHECK(static_cast<int>(tree.size()) == val);
|
||||||
|
|
||||||
|
val = 75;
|
||||||
|
for (auto it = tree.insert(val).first; it != end; ++it, ++val) {
|
||||||
|
CHECK(val == *it);
|
||||||
|
}
|
||||||
|
CHECK(static_cast<int>(tree.size()) == val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE ("Erase values from a ScapegoatTree", "[Scapegoat][ScapegoatTree][containers]") {
|
||||||
|
using duckmem::ScapegoatTree;
|
||||||
|
|
||||||
|
//auto rand_num = std::bind(std::uniform_int_distribution<int>(1, 1000), std::mt19937(std::time(0)));
|
||||||
|
|
||||||
|
ScapegoatTree<int> tree;
|
||||||
|
for (int z = 0; z < 5000; ++z) {
|
||||||
|
tree.insert(z + 1);
|
||||||
|
}
|
||||||
|
CHECK(tree.size() == 5000);
|
||||||
|
|
||||||
|
std::set<int> cpy(tree.begin(), tree.end());
|
||||||
|
CHECK(cpy.size() == 5000);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto it_num = tree.find(0);
|
||||||
|
CHECK(it_num == tree.end());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto it_num = tree.find(1);
|
||||||
|
REQUIRE(it_num != tree.end());
|
||||||
|
CHECK(*it_num == 1);
|
||||||
|
CHECK(it_num == tree.begin());
|
||||||
|
tree.erase(it_num);
|
||||||
|
cpy.erase(1);
|
||||||
|
CHECK(tree.size() == 4999);
|
||||||
|
it_num = tree.find(1);
|
||||||
|
CHECK(it_num == tree.end());
|
||||||
|
CHECK(not tree.include(1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto it_num = tree.find(2345);
|
||||||
|
REQUIRE(it_num != tree.end());
|
||||||
|
CHECK(*it_num == 2345);
|
||||||
|
tree.erase(it_num);
|
||||||
|
cpy.erase(2345);
|
||||||
|
CHECK(tree.size() == 4998);
|
||||||
|
it_num = tree.find(2345);
|
||||||
|
CHECK(it_num == tree.end());
|
||||||
|
CHECK(not tree.include(2345));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto it_num = tree.find(3000);
|
||||||
|
REQUIRE(it_num != tree.end());
|
||||||
|
int test_num = 3000;
|
||||||
|
for (; it_num != tree.end(); ++it_num) {
|
||||||
|
CHECK(*it_num == test_num);
|
||||||
|
test_num++;
|
||||||
|
}
|
||||||
|
CHECK(test_num == 5001);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const ScapegoatTree<int>& ref = tree;
|
||||||
|
auto it_num = ref.find(18);
|
||||||
|
REQUIRE(it_num != ref.end());
|
||||||
|
CHECK(*it_num == 18);
|
||||||
|
CHECK(ref.include(18));
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(tree.size() == cpy.size());
|
||||||
|
CHECK(std::equal(tree.begin(), tree.end(), cpy.begin()));
|
||||||
|
}
|
Loading…
Reference in a new issue