/*
    libmaus2
    Copyright (C) 2020 German Tischler

    This program 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.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#if ! defined(LIBMAUS2_AVL_AVLSET_HPP)
#define LIBMAUS2_AVL_AVLSET_HPP

#include <stack>
#include <functional>
#include <vector>
#include <cassert>
#include <ostream>

// #define AVL_TREE_DEBUG

#if defined(AVL_TREE_DEBUG)
#include <iostream>
#endif

#include <libmaus2/exception/LibMausException.hpp>

namespace libmaus2
{
	namespace avl
	{
		template<typename _value_type, typename _index_type = std::size_t, typename _order_type = std::less<_value_type>, template< typename... > typename container_type_template = std::vector, template< typename... > typename freelist_type_template = std::vector>
		struct AVLSet
		{
			typedef _value_type value_type;
			typedef _index_type index_type;
			typedef _order_type order_type;
			typedef AVLSet<value_type,index_type,order_type,container_type_template,freelist_type_template> this_type;
			typedef this_type tree_type;

			struct ConstIterator
			{
				typedef std::forward_iterator_tag iterator_category;
				typedef _value_type const & reference;
				typedef _value_type const * pointer;
				typedef _value_type value_type;
				typedef typename ::std::iterator< ::std::forward_iterator_tag, value_type>::difference_type difference_type;

				tree_type const * tree;
				index_type cur;

				ConstIterator() : tree(nullptr), cur(0) {}
				ConstIterator(tree_type const * rtree, index_type rcur) : tree(rtree), cur(rcur) {}
				ConstIterator(ConstIterator const & O) : tree(O.tree), cur(O.cur) {}

				ConstIterator & operator=(ConstIterator const & O)
				{
					if ( this != &O )
					{
						tree = O.tree;
						cur = O.cur;
					}

					return *this;
				}

				bool operator==(ConstIterator const & O) const
				{
					return
						tree == O.tree
						&&
						cur == O.cur;
				}

				bool operator!=(ConstIterator const & O) const
				{
					return !operator==(O);
				}

				reference operator*() const
				{
					return tree->A[cur].v;
				}

				pointer operator->() const
				{
					if ( cur )
						return &(tree->A[cur].v);
					else
						return nullptr;
				}

				ConstIterator operator++()
				{
					if ( cur )
					{
						// if node has a right child
						if ( tree->A[cur].r )
						{
							// go right
							cur = tree->A[cur].r;

							// follow leftmost path
							while ( tree->A[cur].l )
								cur = tree->A[cur].l;
						}
						else
						{
							// go up tree until we find a node not reached via a link to a right child
							while ( true )
							{
								index_type const p = cur;

								cur = tree->A[cur].p;

								if ( ! cur || tree->A[cur].r != p )
									break;
							}
						}
					}
					else
					{
					}

					return *this;
				}

				ConstIterator operator++(int)
				{
					ConstIterator R = *this;

					++(*this);

					return R;
				}
			};

			struct ConstReverseIterator
			{
				typedef std::forward_iterator_tag iterator_category;
				typedef _value_type const & reference;
				typedef _value_type const * pointer;
				typedef _value_type value_type;
				typedef typename ::std::iterator< ::std::forward_iterator_tag, value_type>::difference_type difference_type;

				tree_type const * tree;
				index_type cur;

				ConstReverseIterator() : tree(nullptr), cur(0) {}
				ConstReverseIterator(tree_type const * rtree, index_type rcur) : tree(rtree), cur(rcur) {}
				ConstReverseIterator(ConstReverseIterator const & O) : tree(O.tree), cur(O.cur) {}

				ConstReverseIterator & operator=(ConstReverseIterator const & O)
				{
					if ( this != &O )
					{
						tree = O.tree;
						cur = O.cur;
					}

					return *this;
				}

				bool operator==(ConstReverseIterator const & O) const
				{
					return
						tree == O.tree
						&&
						cur == O.cur;
				}

				bool operator!=(ConstReverseIterator const & O) const
				{
					return !operator==(O);
				}

				reference operator*() const
				{
					return tree->A[cur].v;
				}


				pointer operator->() const
				{
					if ( cur )
						return &(tree->A[cur].v);
					else
						return nullptr;
				}

				ConstReverseIterator operator++()
				{
					if ( cur )
					{
						// if node has a left child
						if ( tree->A[cur].l )
						{
							// go left
							cur = tree->A[cur].l;

							// follow rightmost path
							while ( tree->A[cur].r )
								cur = tree->A[cur].r;
						}
						else
						{
							// go up tree until we find a node not reached via a link to a left child
							while ( true )
							{
								index_type const p = cur;

								cur = tree->A[cur].p;

								if ( ! cur || tree->A[cur].l != p )
									break;
							}
						}
					}
					else
					{
					}

					return *this;
				}

				ConstReverseIterator operator++(int)
				{
					ConstReverseIterator R = *this;

					++(*this);

					return R;
				}
			};

			typedef ConstIterator const_iterator;
			typedef ConstReverseIterator const_reverse_iterator;

			const_iterator end() const
			{
				return const_iterator(this,0);
			}

			const_reverse_iterator rend() const
			{
				return const_reverse_iterator(this,0);
			}

			const_iterator begin() const
			{
				if ( empty() )
					return end();

				index_type cur = root;
				while ( cur && A[cur].l )
					cur = A[cur].l;

				return const_iterator(this,cur);
			}

			const_reverse_iterator rbegin() const
			{
				if ( empty() )
					return rend();

				index_type cur = root;
				while ( cur && A[cur].r )
					cur = A[cur].r;

				return const_reverse_iterator(this,cur);
			}

			private:
			struct AVLSetNode
			{
				unsigned char h;
				index_type l;
				index_type r;
				index_type p;
				value_type v;
				::std::size_t n;

				AVLSetNode() : h(0), l(0), r(0), p(0), v(), n(0) {}
				AVLSetNode(
					unsigned char rh,
					index_type rl,
					index_type rr,
					index_type rp,
					value_type rv,
					::std::size_t rn
				) : h(rh), l(rl), r(rr), p(rp), v(rv), n(rn) {}

				bool hasParent() const
				{
					return p != 0;
				}
				bool hasLeft() const
				{
					return l != 0;
				}
				bool hasRight() const
				{
					return r != 0;
				}

				std::ostream & print(std::ostream & out) const
				{
					out << "(h=" << static_cast<int>(h) << ",l=" << l << ",r=" << r << ",p=" << p << ",v=" << v << ",n=" << n << ")";

					return out;
				}
			};

			typedef AVLSetNode node_type;
			typedef container_type_template<node_type> container_type;
			typedef freelist_type_template<index_type> freelist_type;

			order_type order;
			container_type A;
			freelist_type F;
			index_type root;

			index_type allocateNode()
			{
				if ( ! F.size() )
				{
					index_type const id = A.size();
					A.push_back(node_type());
					F.push_back(id);
				}

				index_type const id = F.back();
				F.pop_back();

				return id;
			}

			void freeNode(index_type const id)
			{
				F.push_back(id);
			}


			void setParentIf(index_type n, index_type p)
			{
				if ( n )
					A[n].p = p;
			}

			bool isLeftChild(index_type const p, index_type const n)
			{
				if ( ! p )
					return false;

				if ( A[p].l == n )
					return true;
				else if ( A[p].r == n )
					return false;
				else
					throw libmaus2::exception::LibMausException("AVLSet::isLeftChild: internal inconsistency");
			}

			bool isRightChild(index_type const p, index_type const n)
			{
				if ( ! p )
					return false;

				if ( A[p].r == n )
					return true;
				else if ( A[p].l == n )
					return false;
				else
					throw libmaus2::exception::LibMausException("AVLSet::isRightChild: internal inconsistency");
			}

			void replaceInParent(index_type p, index_type y, index_type x)
			{
				if ( p )
				{
					if ( y == A[p].l )
					{
						A[p].l = x;
					}
					else
					{
						assert ( A[p].r == y );
						A[p].r = x;
					}
				}
			}

			int getDepth(index_type cur) const
			{
				if ( !cur )
					return 0;

				typedef std::pair<index_type,int> stype;
				std::stack< stype > S;
				S.push(stype(cur,1));

				int maxd = 1;

				while ( !S.empty() )
				{
					stype top = S.top();
					S.pop();

					node_type const & N = A[top.first];

					if ( N.hasLeft() )
						S.push(stype(N.l,top.second+1));
					if ( N.hasRight() )
						S.push(stype(N.r,top.second+1));

					if ( top.second > maxd )
						maxd = top.second;
				}

				return maxd;
			}

			::std::size_t getCount(index_type cur) const
			{
				if ( !cur )
					return 0;

				std::stack< index_type > S;
				S.push(cur);

				::std::size_t count = 0;

				while ( !S.empty() )
				{
					index_type top = S.top();
					S.pop();

					node_type const & N = A[top];

					if ( N.hasLeft() )
						S.push(N.l);
					if ( N.hasRight() )
						S.push(N.r);

					count += 1;
				}

				return count;
			}

			::std::size_t getN(index_type const cur) const
			{
				if ( cur )
					return A[cur].n;
				else
					return 0;
			}

			int getHeight(index_type const cur) const
			{
				if ( cur )
					return A[cur].h;
				else
					return 0;
			}

			int getBalance(index_type const cur) const
			{
				if ( ! cur )
					return 0;

				return
					getHeight(A[cur].r) - getHeight(A[cur].l);
			}

			index_type rotateRightLeft(index_type const x)
			{
				index_type const p = A[x].p;
				assert ( x );
				index_type const y = A[x].r;
				assert ( y );
				index_type const z = A[y].l;
				assert ( z );

				index_type const t0 = A[x].l;
				index_type const t1 = A[z].l;
				index_type const t2 = A[z].r;
				index_type const t3 = A[y].r;

				A[x].l = t0;
				A[x].r = t1;
				A[x].h = 1+std::max(getHeight(t0),getHeight(t1));
				A[x].n = 1 + getN(t0) + getN(t1);

				A[y].l = t2;
				A[y].r = t3;
				A[y].h = 1+std::max(getHeight(t2),getHeight(t3));
				A[y].n = 1 + getN(t2) + getN(t3);

				A[z].l = x;
				A[z].r = y;
				A[z].h = 1+std::max(getHeight(x),getHeight(y));
				A[z].n = 1 + getN(x) + getN(y);

				setParentIf(t0,x);
				setParentIf(t1,x);
				setParentIf(t2,y);
				setParentIf(t3,y);
				setParentIf(x,z);
				setParentIf(y,z);
				setParentIf(z,p);

				replaceInParent(p,x,z);

				return z;
			}

			index_type rotateLeftRight(index_type const x)
			{
				index_type const p = A[x].p;
				assert ( x );
				index_type const y = A[x].l;
				assert ( y );
				index_type const z = A[y].r;
				assert ( z );

				index_type const t0 = A[y].l;
				index_type const t1 = A[z].l;
				index_type const t2 = A[z].r;
				index_type const t3 = A[x].r;

				A[y].l = t0;
				A[y].r = t1;
				A[y].h = 1+std::max(getHeight(t0),getHeight(t1));
				A[y].n = 1 + getN(t0) + getN(t1);

				A[x].l = t2;
				A[x].r = t3;
				A[x].h = 1+std::max(getHeight(t2),getHeight(t3));
				A[x].n = 1 + getN(t2) + getN(t3);

				A[z].l = y;
				A[z].r = x;
				A[z].h = 1+std::max(getHeight(y),getHeight(x));
				A[z].n = 1 + getN(y) + getN(x);

				setParentIf(t0,y);
				setParentIf(t1,y);
				setParentIf(t2,x);
				setParentIf(t3,x);
				setParentIf(y,z);
				setParentIf(x,z);
				setParentIf(z,p);

				replaceInParent(p,x,z);

				return z;
			}

			index_type rotateRight(index_type y)
			{
				assert ( y );
				assert ( A[y].hasLeft() );

				index_type const x = A[y].l;
				index_type const p = A[y].p;
				index_type const t0 = A[x].l;
				index_type const t1 = A[x].r;
				index_type const t2 = A[y].r;

				A[y].l = t1;
				A[y].r = t2;
				A[y].h = 1+std::max(getHeight(t1),getHeight(t2));
				A[y].n = 1 + getN(t1) + getN(t2);

				A[x].l = t0;
				A[x].r = y;
				A[x].h = 1+std::max(getHeight(t0),getHeight(y));
				A[x].n = 1 + getN(t0) + getN(y);

				setParentIf(t0,x);
				setParentIf(x,p);
				setParentIf(y,x);
				setParentIf(t1,y);
				setParentIf(t2,y);

				replaceInParent(p,y,x);

				return x;
			}

			index_type rotateLeft(index_type y)
			{
				assert ( y );
				assert ( A[y].hasRight() );

				index_type const x = A[y].r;
				index_type const p = A[y].p;
				index_type const t0 = A[y].l;
				index_type const t1 = A[x].l;
				index_type const t2 = A[x].r;

				A[y].l = t0;
				A[y].r = t1;
				A[y].h = 1+std::max(getHeight(t0),getHeight(t1));
				A[y].n = 1 + getN(t0) + getN(t1);

				A[x].l = y;
				A[x].r = t2;
				A[x].h = 1+std::max(getHeight(y),getHeight(t2));
				A[x].n = 1 + getN(y) + getN(t2);

				setParentIf(x,p);
				setParentIf(t2,x);
				setParentIf(y,x);
				setParentIf(t0,y);
				setParentIf(t1,y);

				replaceInParent(p,y,x);

				return x;
			}

			void print(std::ostream & out, index_type root) const
			{
				struct StackNode
				{
					index_type n;
					int depth;
					int visit;

					StackNode()
					{}
					StackNode(index_type const rn, int const rdepth, int const rvisit)
					: n(rn), depth(rdepth), visit(rvisit) {}
				};
				std::stack< StackNode > S;
				S.push( StackNode(root,0,0) );

				while ( !S.empty() )
				{
					StackNode N = S.top();
					S.pop();

					assert ( N.n );
					node_type const & X = A[N.n];

					if ( N.visit == 0 )
					{

						if ( X.hasRight() )
							S.push(StackNode(X.r,N.depth+1,0));

						S.push(StackNode(N.n,N.depth+1,N.visit+1));

						if ( X.hasLeft() )
							S.push(StackNode(X.l,N.depth+1,0));
					}
					else if ( N.visit == 1 )
					{
						out << std::string(N.depth,' ') << N.n << ":";
						X.print(out);
						out << "\n";
					}
				}
			}

			public:
			AVLSet(order_type const & rorder = order_type()) : order(rorder), A(), F(), root(0)
			{
				A.push_back(node_type());
			}

			// get iterator to element at position index of sorted list of elements contained in tree
			const_iterator operator[](::std::size_t index) const
			{
				if ( empty() )
					return end();
				if ( !(index < A[root].n) )
					return end();

				index_type cur = root;

				while ( cur )
				{
					::std::size_t n_left = getN(A[cur].l);

					if ( index < n_left )
					{
						assert ( A[cur].l );
						cur = A[cur].l;
					}
					else if ( index == n_left )
					{
						return const_iterator(this,cur);
					}
					else
					{
						index -= n_left+1;
						cur = A[cur].r;
					}
				}

				// we should never end up here...
				return end();
			}

			::std::size_t size() const
			{
				return getN(root);
			}

			// return iterator to first element >= v (or end() if no such element is in tree)
			const_iterator lower_bound(value_type const & v) const
			{
				if ( empty() )
					return end();

				// search for v
				index_type cur = root;
				index_type prev = 0;

				while ( cur )
				{
					// if v is smaller than current node value
					if ( order(v,A[cur].v) )
					{
						prev = cur;
						cur = A[cur].l;
					}
					// if v is greater than current node value
					else if ( order(A[cur].v,v) )
					{
						prev = cur;
						cur = A[cur].r;
					}
					// if v equals current node value
					else
					{
						return const_iterator(this,cur);
					}
				}

				assert ( ! cur );
				assert ( prev );

				const_iterator it(this,prev);

				assert ( it != end() );
				assert ( order(*it,v) || order(v,*it) );

				if ( order(*it,v) )
					++it;

				return it;
			}

			const_iterator find(value_type const & v) const
			{
				const_iterator const it = lower_bound(v);

				if ( it == end() || order(v,*it) )
					return end();
				else
					return it;
			}

			bool erase(value_type const & v)
			{
				const_iterator const it = lower_bound(v);

				if ( it == end() || order(v,*it) )
					return false;

				assert (
					!order(v,*it)
					&&
					!order(*it,v)
				);

				return erase(it);
			}

			bool erase(const_iterator const it)
			{
				if ( it == end() )
					return false;

				// search for v
				index_type cur = it.cur;

				// while cur node has any children
				while ( A[cur].l || A[cur].r )
				{
					index_type next;

					// if it has a left child
					if ( A[cur].l )
					{
						// go to maximum in left subtree
						next = A[cur].l;

						while ( A[next].r )
							next = A[next].r;
					}
					// if it has a right child
					else
					{
						assert ( A[cur].r );

						// go to minimum in right subtree
						next = A[cur].r;

						while ( A[next].l )
							next = A[next].l;
					}

					index_type const cur_p  = A[cur].p;
					index_type const next_p = A[next].p;

					replaceInParent(next_p,next,cur );
					replaceInParent(cur_p ,cur ,next);

					std::swap(A[cur].l,A[next].l);
					std::swap(A[cur].r,A[next].r);
					std::swap(A[cur].n,A[next].n);
					std::swap(A[cur].h,A[next].h);
					std::swap(A[cur].p,A[next].p);

					setParentIf(A[cur].l, cur);
					setParentIf(A[cur].r, cur);
					setParentIf(A[next].l, next);
					setParentIf(A[next].r, next);
				}

				assert ( cur );
				assert ( ! A[cur].l );
				assert ( ! A[cur].r );

				// if node is the root of the tree
				if ( ! A[cur].p )
				{
					assert ( cur == root );
					freeNode(cur);
					root = 0;
					return true;
				}

				index_type const era = cur;

				if ( isLeftChild(A[cur].p,cur) )
				{
					A[A[cur].p].l = 0;
				}
				else
				{
					assert ( isRightChild(A[cur].p,cur) );
					A[A[cur].p].r = 0;
				}

				// move to parent
				cur = A[cur].p;

				// free node
				freeNode(era);

				// traverse tree bottom up fron newly inserted value and perform rotations as necessary
				while ( cur )
				{
					// left heavy?
					if ( getBalance(cur) < -1 )
					{
						assert ( A[cur].hasLeft() );

						// get left child
						index_type const l = A[cur].l;

						// is left child left heavy?
						if ( getBalance(l) <= 0 )
						{
							#if 0
							std::cerr << "[V] single right" << std::endl;
							#endif
							cur = rotateRight(cur);
						}
						else
						{
							#if 0
							std::cerr << "[V] left/right" << std::endl;
							#endif
							cur = rotateLeftRight(cur);
						}

						// assert ( getBalance(cur) == 0 );
					}
					// right heavy
					else if ( getBalance(cur) > 1 )
					{
						assert ( A[cur].hasRight() );

						index_type const r = A[cur].r;

						if ( getBalance(r) >= 0 )
						{
							#if 0
							std::cerr << "[V] single left" << std::endl;
							#endif
							cur = rotateLeft(cur);
						}
						else
						{
							#if 0
							std::cerr << "[V] right/left" << std::endl;
							#endif
							cur = rotateRightLeft(cur);
						}

						// assert ( getBalance(cur) == 0 );
					}

					assert ( (getBalance(cur) >= -1) && (getBalance(cur) <= 1) );

					A[cur].h = 1+std::max(getHeight(A[cur].l),getHeight(A[cur].r));
					A[cur].n = 1+getN(A[cur].l)+getN(A[cur].r);

					// set new root if node has no parent
					if ( !A[cur].p )
						root = cur;

					// move to parent
					cur = A[cur].p;
				}

				return true;
			}

			bool insert(value_type const & v)
			{
				if ( empty() )
				{
					assert ( root == 0 );

					index_type const id = allocateNode();

					A[id] = node_type(1,0,0,0,v,1/*n*/);

					root = id;

					return root;
				}
				else
				{
					index_type cur = root;

					// traverse tree top down and insert node if not already present
					while ( true )
					{
						if ( order(v,A[cur].v) )
						{
							if ( A[cur].hasLeft() )
							{
								cur = A[cur].l;
							}
							else
							{
								index_type const n = allocateNode();

								A[n] = node_type(1/*h*/,0/*l*/,0/*r*/,cur,v,1/*n*/);
								A[cur].l = n;

								break;
							}
						}
						else if ( order(A[cur].v,v) )
						{
							if ( A[cur].hasRight() )
							{
								cur = A[cur].r;
							}
							else
							{
								index_type const n = allocateNode();

								A[n] = node_type(1/*h*/,0/*l*/,0/*r*/,cur,v,1/*n*/);
								A[cur].r = n;

								break;
							}
						}
						// value is already present
						else
						{
							return false;
						}
					}

					// traverse tree bottom up fron newly inserted value and perform rotations as necessary
					while ( cur )
					{
						if ( getBalance(cur) < -1 )
						{
							assert ( A[cur].hasLeft() );

							index_type const l = A[cur].l;

							if ( getBalance(l) < 0 )
							{
								#if 0
								std::cerr << "[V] single right" << std::endl;
								#endif
								cur = rotateRight(cur);
							}
							else
							{
								#if 0
								std::cerr << "[V] left/right" << std::endl;
								#endif
								cur = rotateLeftRight(cur);
							}

							assert ( getBalance(cur) == 0 );
						}
						else if ( getBalance(cur) > 1 )
						{
							assert ( A[cur].hasRight() );

							index_type const r = A[cur].r;

							if ( getBalance(r) > 0 )
							{
								#if 0
								std::cerr << "[V] single left" << std::endl;
								#endif
								cur = rotateLeft(cur);
							}
							else
							{
								#if 0
								std::cerr << "[V] right/left" << std::endl;
								#endif
								cur = rotateRightLeft(cur);
							}

							assert ( getBalance(cur) == 0 );
						}

						// balance should now be in range
						assert ( getBalance(cur) >= -1 && getBalance(cur) <= 1 );

						A[cur].h = 1+std::max(getHeight(A[cur].l),getHeight(A[cur].r));
						A[cur].n = 1+getN(A[cur].l)+getN(A[cur].r);

						// set new root if node has no parent
						if ( !A[cur].p )
							root = cur;

						// move to parent
						cur = A[cur].p;
					}

					return true;
				}
			}

			void clear()
			{
				index_type cur = root;

				if ( cur )
				{
					std::stack< index_type > S;
					S.push(cur);

					while ( !S.empty() )
					{
						index_type top = S.top();
						S.pop();

						node_type const & N = A[top];

						if ( N.hasLeft() )
							S.push(N.l);
						if ( N.hasRight() )
							S.push(N.r);

						freeNode(top);
					}

					root = 0;
				}
			}

			bool empty() const
			{
				return (root == 0);
			}

			void checkDepth(std::ostream & errstr) const
			{
				if ( root )
				{
					std::stack<index_type> S;
					S.push(root);

					while ( !S.empty() )
					{
						index_type const cur = S.top();
						S.pop();
						node_type const & N = A[cur];

						bool const ok = (getDepth(cur) == getHeight(cur));

						if ( ! ok )
						{
							errstr << "[E] failed depth check for node " << cur << std::endl;
							assert ( ok );
						}

						assert ( getDepth(cur) == 1+std::max(getDepth(A[cur].l),getDepth(A[cur].r)) );

						if ( N.r )
							S.push(N.r);
						if ( N.l )
							S.push(N.l);
					}
				}
			}

			void checkBalance(std::ostream & errstr) const
			{
				if ( root )
				{
					std::stack<index_type> S;
					S.push(root);

					while ( !S.empty() )
					{
						index_type const cur = S.top();
						S.pop();
						node_type const & N = A[cur];

						bool const ok = getBalance(cur) >= -1 && getBalance(cur) <= 1;

						if ( ! ok )
						{
							errstr << "[E] failed balance check for node " << cur << std::endl;
							assert ( ok );
						}

						if ( N.r )
							S.push(N.r);
						if ( N.l )
							S.push(N.l);
					}
				}
			}

			void checkCount(std::ostream & /* errstr */) const
			{
				if ( root )
				{
					std::stack<index_type> S;
					S.push(root);

					while ( !S.empty() )
					{
						index_type const cur = S.top();
						S.pop();
						node_type const & N = A[cur];

						assert ( getCount(cur) == getN(cur) );

						if ( N.r )
							S.push(N.r);
						if ( N.l )
							S.push(N.l);
					}
				}
			}

			void checkParent(std::ostream & /* errstr */) const
			{
				if ( root )
				{
					std::stack<index_type> S;
					S.push(root);

					while ( !S.empty() )
					{
						index_type const cur = S.top();
						S.pop();
						node_type const & N = A[cur];

						if ( A[cur].l )
							assert ( A[A[cur].l].p == cur );
						if ( A[cur].r )
							assert ( A[A[cur].r].p == cur );

						if ( N.r )
							S.push(N.r);
						if ( N.l )
							S.push(N.l);
					}
				}
			}

			void check(std::ostream & errstr) const
			{
				checkParent(errstr);
				checkDepth(errstr);
				checkBalance(errstr);
				checkCount(errstr);
			}

			void print(std::ostream & out) const
			{
				if ( !empty() )
					print(out,root);
			}
		};
	}
}
#endif
