X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fstlplus%2Fcontainers%2Fntree.tpp;fp=src%2Fstlplus%2Fcontainers%2Fntree.tpp;h=6fd019d9f30b7eee6fe71ebd682e9667172060c4;hp=0000000000000000000000000000000000000000;hb=6b0a0d0efafe34d48ab344fca3b479553bd4e62c;hpb=85783316365181491a3e3c0c63659972477cebba diff --git a/src/stlplus/containers/ntree.tpp b/src/stlplus/containers/ntree.tpp new file mode 100644 index 0000000..6fd019d --- /dev/null +++ b/src/stlplus/containers/ntree.tpp @@ -0,0 +1,913 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // ntree_node + + template + class ntree_node + { + public: + master_iterator, ntree_node > m_master; + T m_data; + ntree_node* m_parent; + std::vector*> m_children; + + public: + ntree_node(const ntree* owner, const T& data = T()) : + m_master(owner,this), m_data(data), m_parent(0) + { + } + + void change_owner(const ntree* owner) + { + m_master.change_owner(owner); + for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) + (*i)->change_owner(owner); + } + + ~ntree_node(void) + { + m_parent = 0; + for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) + delete *i; + } + + }; + + template + static ntree_node* ntree_copy(const ntree* new_owner, ntree_node* root) + { + if (!root) return 0; + ntree_node* new_tree = new ntree_node(new_owner, root->m_data); + for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) + { + ntree_node* new_child = ntree_copy(new_owner, *i); + new_tree->m_children.push_back(new_child); + new_child->m_parent = new_tree; + } + return new_tree; + } + + template + static unsigned ntree_size(ntree_node* root) + { + if (!root) return 0; + unsigned result = 1; + for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) + result += ntree_size(*i); + return result; + } + + template + static unsigned ntree_depth(ntree_node* root) + { + unsigned depth = 0; + for (ntree_node* i = root; i; i = i->m_parent) + depth++; + return depth; + } + + //////////////////////////////////////////////////////////////////////////////// + // ntree_iterator + + // constructor to create a null iterator - you must assign a valid value to this iterator before using it + template + ntree_iterator::ntree_iterator(void) + { + } + + // used to create an alias of an iterator + template + ntree_iterator::ntree_iterator(const safe_iterator, ntree_node >& iterator) : + safe_iterator,ntree_node >(iterator) + { + } + + // constructor used by ntree to create a non-null iterator + template + ntree_iterator::ntree_iterator(ntree_node* node) : + safe_iterator,ntree_node >(node->m_master) + { + } + + // constructor used by ntree to create an end iterator + template + ntree_iterator::ntree_iterator(const ntree* owner) : + safe_iterator,ntree_node >(owner) + { + } + + // destructor + template + ntree_iterator::~ntree_iterator(void) + { + } + + template + TYPENAME ntree_iterator::const_iterator ntree_iterator::constify(void) const + { + return ntree_iterator(*this); + } + + template + TYPENAME ntree_iterator::iterator ntree_iterator::deconstify(void) const + { + return ntree_iterator(*this); + } + + template + bool ntree_iterator::operator == (const TYPENAME ntree_iterator::this_iterator& r) const + { + return equal(r); + } + + template + bool ntree_iterator::operator != (const TYPENAME ntree_iterator::this_iterator& r) const + { + return !operator==(r); + } + + template + bool ntree_iterator::operator < (const TYPENAME ntree_iterator::this_iterator& r) const + { + return compare(r) < 0; + } + + template + TYPENAME ntree_iterator::reference ntree_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + this->assert_valid(); + return this->node()->m_data; + } + + template + TYPENAME ntree_iterator::pointer ntree_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return &(operator*()); + } + + //////////////////////////////////////////////////////////////////////////////// + // ntree_prefix_iterator + + template + ntree_prefix_iterator::ntree_prefix_iterator(void) + { + } + + template + ntree_prefix_iterator::~ntree_prefix_iterator(void) + { + } + + template + ntree_prefix_iterator::ntree_prefix_iterator(const ntree_iterator& i) : + m_iterator(i) + { + // this is initialised with the root node + // which is also the first node in prefix traversal order + } + + template + bool ntree_prefix_iterator::null(void) const + { + return m_iterator.null(); + } + + template + bool ntree_prefix_iterator::end(void) const + { + return m_iterator.end(); + } + + template + bool ntree_prefix_iterator::valid(void) const + { + return m_iterator.valid(); + } + + template + TYPENAME ntree_prefix_iterator::const_iterator ntree_prefix_iterator::constify(void) const + { + return ntree_prefix_iterator(m_iterator); + } + + template + TYPENAME ntree_prefix_iterator::iterator ntree_prefix_iterator::deconstify(void) const + { + return ntree_prefix_iterator(m_iterator); + } + + template + ntree_iterator ntree_prefix_iterator::simplify(void) const + { + return m_iterator; + } + + template + bool ntree_prefix_iterator::operator == (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator == r.m_iterator; + } + + template + bool ntree_prefix_iterator::operator != (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator != r.m_iterator; + } + + template + bool ntree_prefix_iterator::operator < (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator < r.m_iterator; + } + + template + TYPENAME ntree_prefix_iterator::this_iterator& ntree_prefix_iterator::operator ++ (void) + throw(null_dereference,end_dereference) + { + // pre-increment operator + // algorithm: if there are any children, visit child 0, otherwise, go to + // parent and deduce which child the start node was of that parent - if + // there are further children, go into the next one. Otherwise, go up the + // tree and test again for further children. Return null if there are no + // further nodes + m_iterator.assert_valid(); + ntree_node* old_node = m_iterator.node(); + if (!old_node->m_children.empty()) + { + // simply take the first child of this node + m_iterator.set(old_node->m_children[0]->m_master); + } + else + { + // this loop walks up the parent pointers + // either it will walk off the top and exit or a new node will be found and the loop will exit + for (;;) + { + // go up a level + ntree_node* parent = old_node->m_parent; + if (!parent) + { + // we've walked off the top of the tree, so return end + m_iterator.set_end(); + break; + } + else + { + // otherwise walk down the next child - if there is one + // find which index the old node was relative to this node + TYPENAME std::vector*>::iterator found = + std::find(parent->m_children.begin(), parent->m_children.end(), old_node); + // if this was found, then see if there is another and if so return that + found++; + if (found != parent->m_children.end()) + { + // visit the next child + m_iterator.set((*found)->m_master); + break; + } + else + { + // keep going up + old_node = parent; + } + } + } + } + return *this; + } + + template + TYPENAME ntree_prefix_iterator::this_iterator ntree_prefix_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + ntree_prefix_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME ntree_prefix_iterator::reference ntree_prefix_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator*(); + } + + template + TYPENAME ntree_prefix_iterator::pointer ntree_prefix_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator->(); + } + + template + const ntree_iterator& ntree_prefix_iterator::get_iterator(void) const + { + return m_iterator; + } + + template + ntree_iterator& ntree_prefix_iterator::get_iterator(void) + { + return m_iterator; + } + + //////////////////////////////////////////////////////////////////////////////// + // ntree_postfix_iterator + + template + ntree_postfix_iterator::ntree_postfix_iterator(void) + { + } + + template + ntree_postfix_iterator::~ntree_postfix_iterator(void) + { + } + + template + ntree_postfix_iterator::ntree_postfix_iterator(const ntree_iterator& i) : + m_iterator(i) + { + // this is initialised with the root node + // initially traverse to the first node to be visited + if (m_iterator.valid()) + { + ntree_node* node = m_iterator.node(); + while (!node->m_children.empty()) + node = node->m_children[0]; + m_iterator.set(node->m_master); + } + } + + template + bool ntree_postfix_iterator::null(void) const + { + return m_iterator.null(); + } + + template + bool ntree_postfix_iterator::end(void) const + { + return m_iterator.end(); + } + + template + bool ntree_postfix_iterator::valid(void) const + { + return m_iterator.valid(); + } + + template + TYPENAME ntree_postfix_iterator::const_iterator ntree_postfix_iterator::constify(void) const + { + return ntree_postfix_iterator(m_iterator); + } + + template + TYPENAME ntree_postfix_iterator::iterator ntree_postfix_iterator::deconstify(void) const + { + return ntree_postfix_iterator(m_iterator); + } + + template + ntree_iterator ntree_postfix_iterator::simplify(void) const + { + return m_iterator; + } + + template + bool ntree_postfix_iterator::operator == (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator == r.m_iterator; + } + + template + bool ntree_postfix_iterator::operator != (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator != r.m_iterator; + } + + template + bool ntree_postfix_iterator::operator < (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator < r.m_iterator; + } + + template + TYPENAME ntree_postfix_iterator::this_iterator& ntree_postfix_iterator::operator ++ (void) + throw(null_dereference,end_dereference) + { + // pre-increment operator + // algorithm: this node has been visited, therefore all children must have + // already been visited. So go to parent. Return null if the parent is null. + // Otherwise deduce which child the start node was of that parent - if there + // are further children, go into the next one and then walk down any + // subsequent first-child pointers to the bottom. Otherwise, if there are no + // children then the parent node is the next in the traversal. + m_iterator.assert_valid(); + // go up a level + ntree_node* old_node = m_iterator.node(); + ntree_node* parent = old_node->m_parent; + if (!parent) + { + // we've walked off the top of the tree, so return end + m_iterator.set_end(); + } + else + { + // otherwise find which index the old node was relative to this node + TYPENAME std::vector*>::iterator found = + std::find(parent->m_children.begin(), parent->m_children.end(), old_node); + // if this was found, then see if there is another + found++; + if (found != parent->m_children.end()) + { + // if so traverse to it and walk down the leftmost child pointers to the bottom of the new sub-tree + ntree_node* new_node = *found; + while (!new_node->m_children.empty()) + new_node = new_node->m_children[0]; + m_iterator.set(new_node->m_master); + } + else + { + // the parent's children have all been visited - so the parent is visited + m_iterator.set(parent->m_master); + } + } + return *this; + } + + template + TYPENAME ntree_postfix_iterator::this_iterator ntree_postfix_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + ntree_postfix_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME ntree_postfix_iterator::reference ntree_postfix_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator*(); + } + + template + TYPENAME ntree_postfix_iterator::pointer ntree_postfix_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator->(); + } + + template + const ntree_iterator& ntree_postfix_iterator::get_iterator(void) const + { + return m_iterator; + } + + template + ntree_iterator& ntree_postfix_iterator::get_iterator(void) + { + return m_iterator; + } + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // ntree + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + template + ntree::ntree(void) : m_root(0) + { + } + + template + ntree::~ntree(void) + { + if (m_root) delete m_root; + } + + template + ntree::ntree(const ntree& r) : m_root(0) + { + *this = r; + } + + template + ntree& ntree::operator=(const ntree& r) + { + if (m_root) delete m_root; + m_root = ntree_copy(this, r.m_root); + return *this; + } + + template + bool ntree::empty(void) const + { + return m_root == 0; + } + + template + unsigned ntree::size(void) const + { + return ntree_size(m_root); + } + + template + unsigned ntree::size(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_size(i.node()); + } + + template + unsigned ntree::size(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_size(i.node()); + } + + template + unsigned ntree::depth(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_depth(i.node()); + } + + template + unsigned ntree::depth(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_depth(i.node()); + } + + template + TYPENAME ntree::const_iterator ntree::root(void) const + { + if (!m_root) return ntree_iterator(this); + return ntree_iterator(m_root); + } + + template + TYPENAME ntree::iterator ntree::root(void) + { + if (!m_root) return ntree_iterator(this); + return ntree_iterator(m_root); + } + + template + unsigned ntree::children(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return i.node()->m_children.size(); + } + + template + unsigned ntree::children(const ntree_iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return i.node()->m_children.size(); + } + + template + TYPENAME ntree::const_iterator ntree::child(const TYPENAME ntree::const_iterator& i, unsigned child) const + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + i.assert_valid(this); + if (child >= children(i)) throw std::out_of_range("stlplus::ntree"); + return ntree_iterator(i.node()->m_children[child]); + } + + template + TYPENAME ntree::iterator ntree::child(const TYPENAME ntree::iterator& i, unsigned child) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + i.assert_valid(this); + if (child >= children(i)) throw std::out_of_range("stlplus::ntree"); + return ntree_iterator(i.node()->m_children[child]); + } + + template + TYPENAME ntree::const_iterator ntree::parent(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + ntree_node* parent = i.node()->m_parent; + if (!parent) return ntree_iterator(this); + return ntree_iterator(parent); + } + + template + TYPENAME ntree::iterator ntree::parent(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + ntree_node* parent = i.node()->m_parent; + if (!parent) return ntree_iterator(this); + return ntree_iterator(parent); + } + + template + TYPENAME ntree::const_prefix_iterator ntree::prefix_begin(void) const + { + return ntree_prefix_iterator(root()); + } + + template + TYPENAME ntree::prefix_iterator ntree::prefix_begin(void) + { + return ntree_prefix_iterator(root()); + } + + template + TYPENAME ntree::const_prefix_iterator ntree::prefix_end(void) const + { + return ntree_prefix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::prefix_iterator ntree::prefix_end(void) + { + return ntree_prefix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::const_postfix_iterator ntree::postfix_begin(void) const + { + return ntree_postfix_iterator(root()); + } + + template + TYPENAME ntree::postfix_iterator ntree::postfix_begin(void) + { + return ntree_postfix_iterator(root()); + } + + template + TYPENAME ntree::const_postfix_iterator ntree::postfix_end(void) const + { + return ntree_postfix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::postfix_iterator ntree::postfix_end(void) + { + return ntree_postfix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::iterator ntree::insert(const T& data) + { + // insert a new node as the root + return insert(ntree_iterator(this), 0, data); + } + + template + TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::iterator& i, unsigned offset, const T& data) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + // if i is the end iterator, this means insert a new root + if (i.end()) + erase(); + else + { + i.assert_valid(this); + if (offset > children(i)) throw std::out_of_range("stlplus::ntree"); + } + ntree_node* new_node = new ntree_node(this,data); + if (i.end()) + { + m_root = new_node; + } + else + { + i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node); + new_node->m_parent = i.node(); + } + return ntree_iterator(new_node); + } + + template + TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const T& data) + throw(wrong_object,null_dereference,end_dereference) + { + return insert(i, i.node()->m_children.size(), data); + } + + template + TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::iterator& i, unsigned offset, const ntree& tree) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + // insert a whole tree as a child of i + i.assert_valid(this); + if (offset > children(i)) throw std::out_of_range("stlplus::ntree"); + ntree_node* new_node = ntree_copy(this, tree.m_root); + i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node); + new_node->m_parent = i.node(); + return ntree_iterator(new_node); + } + + template + TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const ntree& tree) + throw(wrong_object,null_dereference,end_dereference) + { + return insert(i, children(i), tree); + } + + template + TYPENAME ntree::iterator ntree::push(const TYPENAME ntree::iterator& node, const T& data) + throw(wrong_object,null_dereference,end_dereference) + { + // insert a new node to replace the existing node in the tree + // making the original node the child of the new node + // i.e. (node) becomes (new)->(node) + // afterwards, the iterator still points to the old node, now the child + // returns the iterator to the new node + node.assert_valid(this); + ntree_node* new_node = new ntree_node(this,data); + if (node.node() == m_root) + { + // pushing the root node + m_root = new_node; + new_node->m_parent = 0; + } + else + { + // pushing a sub-node + *(std::find(node.node()->m_parent->m_children.begin(), node.node()->m_parent->m_children.end(), node.node())) = new_node; + new_node->m_parent = node.node()->m_parent; + } + // link up the old node as the child of the new node + new_node->m_children.insert(new_node->m_children.begin(),node.node()); + node.node()->m_parent = new_node; + return ntree_iterator(new_node); + } + + template + void ntree::pop(const TYPENAME ntree::iterator& parent, unsigned offset) + throw(wrong_object,null_dereference,end_dereference) + { + // inverse of push + // removes the specified child of the parent node, adding its children to the parent node at the same offset + parent.assert_valid(this); + ntree_node* node = parent.node(); + if (offset >= node->m_children.size()) throw std::out_of_range("stlplus::ntree"); + // move the grandchildren first + ntree_node* child = parent.node()->m_children[offset]; + while (!child->m_children.empty()) + { + // remove the last grandchild and insert into node just after the child to be removed + ntree_node* grandchild = child->m_children[child->m_children.size()-1]; + child->m_children.pop_back(); + node->m_children.insert(node->m_children.begin()+offset+1, grandchild); + grandchild->m_parent = node; + } + // now remove the child + node->m_children.erase(node->m_children.begin()+offset); + delete child; + } + + template + void ntree::erase(void) + { + // erase the whole tree + erase(root()); + } + + template + void ntree::erase(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + if (!i.end()) + { + // erase this node and its subtree + // do this by erasing this child of its parent + // handle the case of erasing the root + i.assert_valid(this); + ntree_node* node = i.node(); + if (node == m_root) + { + delete m_root; + m_root = 0; + } + else + { + ntree_node* parent = node->m_parent; + // impossible for parent to be null - should assert this + TYPENAME std::vector*>::iterator found = + std::find(parent->m_children.begin(), parent->m_children.end(), node); + // impossible for find to fail - should assert this + parent->m_children.erase(found); + delete node; + } + } + } + + template + void ntree::erase(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + erase(child(i, offset)); + } + + template + ntree ntree::subtree(void) + { + return subtree(root()); + } + + template + ntree ntree::subtree(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + ntree result; + if (!i.end()) + { + i.assert_valid(this); + result.m_root = ntree_copy(&result, i.node()); + } + return result; + } + + template + ntree ntree::subtree(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + return subtree(child(i, offset)); + } + + template + ntree ntree::cut(void) + { + return cut(root()); + } + + template + ntree ntree::cut(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + ntree result; + if (!i.end()) + { + i.assert_valid(this); + ntree_node* node = i.node(); + if (node == m_root) + { + result.m_root = m_root; + m_root = 0; + } + else + { + ntree_node* parent = node->m_parent; + // impossible for parent to be null - should assert this + TYPENAME std::vector*>::iterator found = + std::find(parent->m_children.begin(), parent->m_children.end(), node); + // impossible for find to fail - should assert this + result.m_root = *found; + parent->m_children.erase(found); + } + if (result.m_root) + { + result.m_root->m_parent = 0; + result.m_root->set_new_owner(&result); + } + } + return result; + } + + template + ntree ntree::cut(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + return cut(child(i, offset)); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus +