// =====================================================================================================================
//  fennec, a free and open source game engine
//  Copyright © 2025 - 2026  Medusa Slockbower
//
//  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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================

///
/// \file fennec/containers/bintree.h
/// \brief A header containing the definition for an in-array binary tree.
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///

#ifndef FENNEC_CONTAINERS_BINTREE_H
#define FENNEC_CONTAINERS_BINTREE_H

#include <fennec/memory/allocator.h>

#include <fennec/containers/deque.h>
#include <fennec/containers/list.h>
#include <fennec/containers/optional.h>
#include <fennec/containers/pair.h>
#include <fennec/containers/traversal.h>

namespace fennec
{

///
/// \brief Structure defining an in-array binary tree
///
/// \details
/// | Property    | Value       |
/// |:-----------:|:-----------:|
/// | stable      |      ⛔     |
/// | dynamic     |      ✅     |
/// | homogeneous |      ✅     |
/// | distinct    |      ⛔     |
/// | ordered     |      ⛔     |
/// | linear      |      ⛔     |
/// | space       | \emph{O(N)} |
/// | access      | \emph{O(1)} |
/// | find        | \emph{O(N)} |
/// | insertion   | \emph{O(1)} |
/// | deletion    | \emph{O(1)} |
///
/// \tparam TypeT The data type
/// \tparam AllocT An allocator class
template<typename TypeT, class AllocT = allocator<TypeT>>
struct bintree {

// Definitions =========================================================================================================
private:
	struct node;

public:

	/// \name Definitions
	/// @{

	using value_t = TypeT;                                           //!< The element type
	using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The allocator type
	static constexpr size_t npos = -1;                               //!< null position constant

	///
	/// \brief Enum representing node side relative to parent
	enum side_ : bool {
		side_left  = false, //!< Left Side
		side_right = true   //!< Right Side
	};

	/// @}

	friend class iterator;
	friend class const_iterator;


private:
	using table_t = allocation<node, alloc_t>;
	using freed_t = list<size_t, alloc_t>;

// Constructors & Destructor ===========================================================================================
public:

	/// \name Constructors & Destructor
	/// @{

	///
	/// \brief Default Constructor, initializes an empty tree
	///
	/// \details The underlying allocation is not initialized.
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bintree()
		: _table()
		, _freed()
		, _root(npos)
		, _size(0) {
	}

	///
	/// \brief Move Constructor, takes ownership of a tree
	/// \param tree The tree to take ownership of
	///
	/// \details Takes ownership of the underlying allocation of \emph{tree}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bintree(bintree&& tree) noexcept
		: _table(fennec::move(tree._table))
		, _freed(fennec::move(tree._freed))
		, _root(tree._root)
		, _size(tree._size) {
	}

	///
	/// \brief Copy Constructor, copies a tree
	/// \param tree The tree to copy
	///
	/// \details Copies the contents of \emph{tree} into a new tree. Invokes copy constructor.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr bintree(const bintree& tree)
		: _table(tree._table.capacity())
		, _freed()
		, _root(nullid)
		, _size(0) {

		// Visitor for copying
		struct {
			bintree&                    self;
			const bintree&              target;
			deque<pair<size_t, size_t>> stack;

			uint8_t operator()(const value_t& x, size_t n) {
				while (stack.back().second != target.parent(n)) {
					stack.pop_back();
				}
				stack.emplace_back(self.insert(stack.back().first, target.side(n), x), n);
				return traversal_control_continue;
			}
		} visitor = {
			.self = *this,
			.target = tree
		};

		tree.template traverse<pre_order>(
			visitor, tree._root
		);
	}

	///
	/// \brief Destructor, clears the tree
	///
	/// \details Clears the contents of \emph{tree}. Invokes destructor.
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr ~bintree() {
		clear();
	}

	/// @}


// Properties ==========================================================================================================
public:

	/// \name Properties
	/// @{

	///
	/// \returns The number of elements in the tree
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t size() const {
		return _size;
	}

	///
	/// \returns \emph{true} when there are no elements in the tree, \emph{false} otherwise.
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool is_empty() const {
		return _size == 0;
	}

	///
	/// \returns The capacity of the underlying allocation
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t capacity() const {
		return _table.capacity();
	}

	///
	/// \returns The next id to be returned by `bintree::insert` or `bintree::emplace`.
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t next_id() const {
		size_t i = _size;
		if (not _freed.is_empty()) {
			i = _freed.front();
		}
		return i;
	}

	///
	/// \returns The next id to be returned by `bintree::insert` or `bintree::emplace`.
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t root() const {
		return _root;
	}

	/// @}



// Navigation ==========================================================================================================
public:

	/// \name Navigation
	/// @{

	///
	/// \param i The node id
	/// \returns The parent of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t parent(size_t i) const {
		return i == npos ? npos : _table[i].parent;
	}

	///
	/// \param i The node id
	/// \returns The grandparent of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t grandparent(size_t i) const {
		return parent(parent(i));
	}

	///
	/// \param i The node id
	/// \returns The left child of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t left(size_t i) const {
		return i == npos ? npos : _table[i].child[false];
	}

	///
	/// \param i The node id
	/// \returns The right child of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t right(size_t i) const {
		return i == npos ? npos : _table[i].child[true];
	}

	///
	/// \param i The node id
	/// \param dir The direction to go \emph{true} for right, \emph{false} for left
	/// \returns The child in the direction specified by \emph{dir}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t child(size_t i, bool dir) const {
		return i == npos ? npos : _table[i].child[dir];
	}

	///
	/// \param i The node id
	/// \returns \emph{true} if \emph{i} is the right node of \emph{parent(i)}, \emph{false} otherwise
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr bool side(size_t i) const {
		return i == npos ? false : i == right(_parent(i));
	}

	///
	/// \param i The id of the node
	/// \returns The id of the sibling of \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t sibling(size_t i) const {
		if (i == npos) {
			return npos;
		}
		if (i == _root) {
			return npos;
		}
		size_t p = _parent(i);
		const bool d = i == _right(p);
		return _child(p, !d);
	}

	///
	/// \param i The node id
	/// \returns The depth of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(\log n)}
	///
	constexpr size_t depth(size_t i) const {
		size_t d = 0;
		while (i != npos) {
			i = _parent(i);
			++d;
		}
		return d;
	}

	///
	/// \param i The node id
	/// \returns The id of the left-most node of \emph{i}
	///
	/// \par Complexity
	/// \emph{O(\log n)}
	///
	constexpr size_t left_most(size_t i) const {
		if (i >= _table.size()) {
			return npos;
		}
		while (_table[i].child[false] != npos) {
			i = _table[i].child[false];
		}
		return i;
	}

	///
	/// \param i The node id
	/// \returns The id of the right-most node of \emph{i}
	///
	/// \par Complexity
	/// \emph{O(\log n)}
	///
	constexpr size_t right_most(size_t i) const {
		if (i >= _table.size()) {
			return npos;
		}
		while (_table[i].right[false] != npos) {
			i = _table[i].right[false];
		}
		return i;
	}

	/// @}


// Access ==============================================================================================================
public:

	/// \name Access
	/// @{

	///
	/// \param i The node id
	/// \returns a reference to the value of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr value_t& operator[](size_t i) {
		assertd(i < _table.size(), "Index out of bounds.");
		return _table[i].value;
	}

	///
	/// \details Node access
	/// \param i The node id
	/// \returns a reference to the value of node \emph{i}
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr const value_t& operator[](size_t i) const {
		assertd(i < _table.size(), "Index out of bounds.");
		return _table[i].value;
	}

	/// @}


// Modifiers ===========================================================================================================
public:

	/// \name Modifiers
	/// @{

	///
	/// \details If the left node of \emph{p} already exists, the move assignment operator is used instead
	/// \param p The parent node
	/// \param val The object to move into the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert_left(size_t p, value_t&& val) {
		return this->_insert_left(p, fennec::forward<value_t>(val));
	}

	///
	/// \details If the left node of \emph{p} already exists, the copy assignment operator is used instead
	/// \param p The parent node
	/// \param val The object to copy to the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert_left(size_t p, const value_t& val) {
		return this->_insert_left(p, val);
	}

	///
	/// \brief Left Insertion, constructs a new node as the left child of \emph{p}
	/// \details If the left node of \emph{p} already exists, the move assignment operator is used instead
	/// \param p The parent node
	/// \param args The arguments to construct the new node with
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename...ArgsT>
	constexpr size_t emplace_left(size_t p, ArgsT&&...args) {
		return this->_insert_left(p, fennec::forward<ArgsT>(args)...);
	}

	///
	/// \details If the right node of \emph{p} already exists, the move assignment operator is used instead
	/// \param p The parent node
	/// \param val The object to move into the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert_right(size_t p, value_t&& val) {
		return this->_insert_right(p, fennec::forward<value_t>(val));
	}

	///
	/// \details If the right node of \emph{p} already exists, the copy assignment operator is used instead
	/// \param p The parent node
	/// \param val The object to copy to the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert_right(size_t p, const value_t& val) {
		return this->_insert_right(p, val);
	}

	///
	/// \brief Right Insertion, constructs a new node as the right child of \emph{p}
	/// \details If the right node of \emph{p} already exists, the move assignment operator is used instead
	/// \param p The parent node
	/// \param args The arguments to construct the new node with
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename...ArgsT>
	constexpr size_t emplace_right(size_t p, ArgsT&&...args) {
		return this->_insert_right(p, fennec::forward<ArgsT>(args)...);
	}



	///
	/// \brief Perform a Tree Rotation at \emph{i} in the specified direction
	/// \param sub The root node for the rotation
	/// \param dir The direction to rotate, \emph{true} for right, \emph{false} for left
	/// \returns the new root node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t rotate(size_t sub, bool dir) {
		if (sub == npos) {
			return npos;
		}
		const size_t sub_parent = _parent(sub);
		const size_t new_root   = _child(sub, not dir);
		const size_t new_child  = _child(new_root, dir);

		_child(sub, not dir) = new_child;
		if (new_child != npos) {
			_parent(new_child) = sub;
		}
		_child(new_root, dir) = sub;

		_parent(new_root) = sub_parent;
		_parent(sub) = new_root;
		if (sub_parent != npos) {
			_child(sub_parent, sub == _right(sub_parent)) = new_root;
		} else {
			_root = new_root;
		}
		return new_root;
	}

	///
	/// \details If the child of \emph{p} already exists, the move assignment operator is used instead
	/// \param parent The parent node
	/// \param side The side to insert on
	/// \param val The object to move into the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert(size_t parent, bool side, value_t&& val) {
		return this->_insert(parent, side, fennec::forward<value_t>(val));
	}

	///
	/// \details If the child of \emph{p} already exists, the copy assignment operator is used instead
	/// \param parent The parent node
	/// \param side The side to insert on
	/// \param val The object to copy to the new node
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	constexpr size_t insert(size_t parent, bool side, const value_t& val) {
		return this->_insert(parent, side, val);
	}

	///
	/// \brief Insertion, constructs a new node as the child of \emph{p}
	/// \details If the child of \emph{p} already exists, the move assignment operator is used instead
	/// \param parent The parent node
	/// \param side The side to insert on
	/// \param args The arguments to construct the new node with
	/// \returns The id of the new node
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename...ArgsT>
	constexpr size_t emplace(size_t parent, bool side, ArgsT&&...args) {
		return this->_insert(parent, side, fennec::forward<ArgsT>(args)...);
	}

	///
	/// \brief Clears the tree, destroying all elements
	///
	/// \par Complexity
	/// \emph{O(N)}
	///
	constexpr void clear() {
		list<size_t> queue;

		if (_root != npos) {
			queue.push_back(_root);
		}

		while (not queue.is_empty()) {
			size_t i = queue.front();
			queue.pop_front();

			if (_right(i)  != npos) {
				queue.push_front(_right(i));
			}
			if (_left(i)  != npos) {
				queue.push_front(_left(i));
			}

			fennec::destruct(&_table[i]);
		}

		_size = 0;
		_root = npos;
	}

	/// @}


// Traversal ===========================================================================================================

	/// \name Traversal
	/// @{

	///
	/// \details
	/// The visitor should accept a reference to a value of type \emph{TypeT} and a \emph{size_t} which contains the node's id.
	/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
	///
	/// \tparam OrderT The order with which to traverse the tree.
	/// \tparam VisitorT The visitor, should fulfill the signature \emph{uint8_t visit(TypeT&, size_t)}
	/// \param visit The visiting object
	/// \param i The node to start at
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename OrderT, typename VisitorT>
	constexpr void traverse(VisitorT&& visit, size_t i) {
		OrderT order;
		i = order(*this, i);
		while (i != npos) {
			uint8_t mode = traversal_control_continue;
			if (_table[i].value) {
				mode = visit(_table[i].value, i);
			}
			if (mode == traversal_control_break) {
				break;
			}
			i = order[*this, i, mode];
		}
	}

	///
	/// \brief Traverse the tree using a specified order and visiting functor
	///
	/// \details
	/// The visitor should accept a reference to a value of type \emph{TypeT} and a \emph{size_t} which contains the node's id.
	/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
	///
	/// \tparam OrderT The order with which to traverse the tree.
	/// \tparam VisitorT The visitor, should fulfill the signature \emph{uint8_t visit(TypeT&, size_t)}
	/// \param visit The visiting object
	/// \param i The node to start at
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	template<typename OrderT, typename VisitorT>
	constexpr void traverse(VisitorT&& visit, size_t i) const {
		OrderT order;
		i = order(*this, i);
		while (i != npos) {
			uint8_t mode = traversal_control_continue;
			if (_table[i].value) {
				mode = visit(_table[i].value, i);
			}
			if (mode == traversal_control_break) {
				break;
			}
			i = order[*this, i, mode];
		}
	}

	///
	/// \brief Traverser pattern for breadth-first traversal
	struct breadth_first {

		///
		/// \brief Traversal Init
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		size_t operator()(const bintree&, size_t start) {
			return head = start;
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \param mode The mode specifying branch conditions
		/// \returns The next index according to the traversal order
		size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
			if (node == npos) {
				return npos;
			}

			const size_t lft = tree.left(tree.parent(node));
			const size_t nxt = lft == node ? tree.right(tree.parent(node)) : npos;
			const size_t chd = tree.left(node);

			if (nxt != npos && node != head) {
				visit.push_front(nxt);
			}

			if (chd != npos && mode != traversal_control_jump_over) {
				visit.push_back(chd);
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head;
	};

	///
	/// \brief Traverser pattern for pre-order traversal
	struct pre_order {

		///
		/// \brief Traversal Init
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const bintree&, size_t start) {
			head = start;
			return start;
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \param mode The mode specifying branch conditions
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) {
			if (node == npos) {
				return npos;
			}

			size_t nxt = tree.right(tree.parent(node));
			const size_t chd = tree.left(node);
			nxt = node == nxt ? npos : nxt;

			if (nxt != npos && node != head) {
				visit.push_front(nxt);
			}

			if (chd != npos && mode != traversal_control_jump_over) {
				visit.push_front(chd);
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = { 0 };
	};

	///
	/// \brief Traverser pattern for in-order traversal
	struct in_order {

		///
		/// \brief Traversal Init
		/// \param tree The tree we are operating on
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const bintree& tree, size_t start) {
			head = start;
			return tree.left_most(start);
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
			if (node == npos) {
				return npos;
			}

			const size_t parent = tree.parent(node);
			const size_t pright = tree.right(parent);
			const size_t next   = tree.left_most(tree.right(node));

			if (node != pright && parent != npos) {
				visit.push_front(parent);
			}

			if (next != npos) {
				visit.push_front(next);
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}

			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = { 0 };
	};

	///
	/// \brief Traverser pattern for post-order traversal
	struct post_order {

		///
		/// \brief Traversal Init
		/// \param tree The tree we are operating on
		/// \param start The node in the tree to start at
		/// \returns The first index of the specified order
		constexpr size_t operator()(const bintree& tree, size_t start) {
			head = start;
			return this->_successor(tree, start);
		}

		///
		/// \brief Traverser Step
		/// \param tree The tree we are operating on
		/// \param node The current node
		/// \returns The next index according to the traversal order
		constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) {
			if (node == npos) {
				return npos;
			}

			const size_t parent = tree.parent(node);
			const size_t right  = tree.right(parent);

			if (node == right) {
				if (parent != npos) {
					visit.push_front(parent);
				}
			} else if (right != npos) {
				visit.push_front(this->_successor(tree, right));
			}

			if (not visit.is_empty()) {
				node = visit.front();
				visit.pop_front();
			} else {
				node = npos;
			}


			return node;
		}

	private:
		list<size_t> visit;
		size_t       head = { 0 };

		constexpr size_t _successor(const bintree& tree, size_t n) {
			size_t s = tree.left_most(n);
			while (n == s) {
				size_t r = tree.right(n);
				if (r != npos) {
					n = r;
					s = tree.left_most(n);
				} else {
					break;
				}
			}
			return s == npos ? n : s;
		}
	};

	/// @}

// Iterator ============================================================================================================

	/// \name Iteration
	/// @{

	///
	/// \brief C++ Iterator Specification \emph{iterator}
	/// \details Performs pre-order traversal
	class iterator {

	public:
		///
		/// \brief bintree iterator constructor
		/// \param tree A reference to the tree
		/// \param root The root node for iteration
		constexpr iterator(bintree* tree, size_t root)
			: _tree(tree)
			, _order()
			, _n(_order(*tree, root)) {
		}

		///
		/// \brief bintree iterator constructor
		/// \param tree A reference to the tree
		/// \param root The root node for iteration
		/// \param node The node to jump to
		constexpr iterator(bintree* tree, size_t root, size_t node)
			: _tree(tree)
			, _order()
			, _n(node) {
			_order.head = root;
		}

		///
		/// \returns The index of the current node
		size_t index() const {
			return _n;
		}

		///
		/// \brief iterator pre-increment operator
		/// \returns A reference to \emph{this} after having stepped to the next node
		iterator& operator++() {
			return _n = _order[*_tree, _n, traversal_control_continue], *this;
		}

		///
		/// \brief dereference operator
		/// \returns the value held in the current node
		value_t& operator*() {
			return (*_tree)[_n];
		}

		///
		/// \returns the value held in the current node
		const value_t& operator*() const {
			return (*_tree)[_n];
		}

		///
		/// \brief pointer access operator
		/// \returns a reference to the value held in the current node
		value_t* operator->() {
			return &(*_tree)[_n];
		}

		///
		/// \returns a reference to the value held in the current node
		const value_t* operator->() const {
			return &(*_tree)[_n];
		}

		///
		/// \brief iterator equality operator
		/// \param it the iterator to compare with
		/// \returns \emph{true} if iterators are identical, \emph{false} otherwise
		constexpr bool operator==(const iterator& it) {
			return _tree == it._tree and _n == it._n;
		}

		///
		/// \brief iterator inequality operator
		/// \param it the iterator to compare with
		/// \returns \emph{true} if iterators are different, \emph{false} otherwise
		constexpr bool operator!=(const iterator& it) {
			return _tree != it._tree or _n != it._n;
		}

	private:
		bintree* _tree;
		in_order _order;
		size_t   _n;
	};


	///
	/// \brief C++ Iterator Specification \emph{iterator}
	/// \details Performs pre-order traversal
	class const_iterator {

	public:
		///
		/// \brief bintree iterator constructor
		/// \param tree A reference to the tree
		/// \param root The root node for iteration
		constexpr const_iterator(bintree* tree, size_t root)
			: _tree(tree)
			, _order()
			, _n(_order(*tree, root)) {
		}

		///
		/// \brief bintree iterator constructor
		/// \param tree A reference to the tree
		/// \param root The root node for iteration
		/// \param node The node to jump to
		constexpr const_iterator(bintree* tree, size_t root, size_t node)
			: _tree(tree)
			, _order()
			, _n(node) {
			_order.head = root;
		}

		///
		/// \returns The index of the current node
		size_t index() const {
			return _n;
		}

		///
		/// \brief iterator pre-increment operator
		/// \returns A reference to \emph{this} after having stepped to the next node
		const_iterator& operator++() {
			return _n = _order[*_tree, _n, traversal_control_continue], *this;
		}

		///
		/// \returns the value held in the current node
		const value_t& operator*() const {
			return (*_tree)[_n];
		}

		///
		/// \returns a reference to the value held in the current node
		const value_t* operator->() const {
			return &(*_tree)[_n];
		}

		///
		/// \brief iterator equality operator
		/// \param it the iterator to compare with
		/// \returns \emph{true} if iterators are identical, \emph{false} otherwise
		constexpr bool operator==(const iterator& it) {
			return _tree == it._tree and _n == it._n;
		}

		///
		/// \brief iterator inequality operator
		/// \param it the iterator to compare with
		/// \returns \emph{true} if iterators are different, \emph{false} otherwise
		constexpr bool operator!=(const iterator& it) {
			return _tree != it._tree or _n != it._n;
		}

	private:
		bintree* _tree;
		in_order _order;
		size_t   _n;
	};

	///
	/// \returns an iterator at the first element in pre-order traversal
	///
	/// \par Complexity
	/// \emph{O(\log N)}
	///
	iterator begin() {
		return iterator(this, _root);
	}

	///
	/// \brief C++ Iterator Specification \emph{begin()}
	/// \returns an iterator at the first element in pre-order traversal
	///
	/// \par Complexity
	/// \emph{O(\log N)}
	///
	const_iterator begin() const {
		return iterator(this, _root);
	}

	///
	/// \returns an iterator at the first element in pre-order traversal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	iterator end() {
		return iterator(this, _root, nullid);
	}

	///
	/// \brief C++ Iterator Specification \emph{end()}
	/// \returns an iterator at the first element in pre-order traversal
	///
	/// \par Complexity
	/// \emph{O(1)}
	///
	const_iterator end() const {
		return iterator(this, _root, nullid);
	}

	/// @}


// Private Member Variables ============================================================================================
private:
	table_t _table;
	freed_t _freed;
	size_t  _root, _size;


// Private Helpers =====================================================================================================
private:
	constexpr size_t _next_free() {
		size_t i = _size;
		if (not _freed.is_empty()) {
			i = _freed.front();
			_freed.pop_front();
		}
		if (i >= _table.capacity()) {
			_table.reallocate(2 * fennec::max(_table.capacity(), size_t(4)));
		}
		++_size;
		return i;
	}

	template<typename...ArgsT>
	constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
		size_t i = p >= capacity() ? npos : p;
		i = i == npos ? _root : _left(i);
		if (i != npos) {
			_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
		} else {
			i = _next_free();
			if (p != npos) {
				_left(p) = i;
			}
			if (_root == npos) {
				_root = i;
			}
			fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
		}
		return i;
	}

	template<typename...ArgsT>
	constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
		size_t i = p == npos ? _root : _right(p);
		if (i != npos) {
			_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
		} else {
			i = _next_free();
			if (p != npos) {
				_right(p) = i;
			}
			if (_root == npos) {
				_root = i;
			}
			fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
		}
		return i;
	}

	template<typename...ArgsT>
	constexpr size_t _insert(size_t p, bool d, ArgsT&&...args) {
		size_t i = p == npos ? _root : _child(p, d);
		if (i != npos) {
			_table[i].value = value_t(fennec::forward<ArgsT>(args)...);
			return i;
		}

		i = _next_free();
		if (p != npos) {
			_child(p, d) = i;
		}
		if (_root == npos) {
			_root = i;
		}
		fennec::construct(&_table[i], p, npos, npos, fennec::forward<ArgsT>(args)...);
		return i;
	}


	constexpr size_t& _parent(size_t i) {
		return _table[i].parent;
	}

	constexpr size_t& _grandparent(size_t i) {
		return _parent(_parent(i));
	}

	constexpr size_t& _left(size_t i) {
		return _table[i].child[false];
	}

	constexpr size_t& _right(size_t i) {
		return _table[i].child[true];
	}

	constexpr size_t& _child(size_t i, bool dir) {
		return _table[i].child[dir];
	}

	constexpr size_t& _sibling(size_t i) {
		const size_t p = _parent(i);
		const bool   d = i == _right(p);
		return _child(p, !d);
	}

// Private Definitions =================================================================================================
private:
	struct node {
		value_t value;
		size_t  parent;
		size_t  child[2];

		constexpr node()
			: value()
			, parent(npos), child{ npos, npos } {
		}

		template<typename...ArgsT>
		constexpr node(size_t p, size_t l, size_t r, ArgsT&&...args)
			: value(fennec::forward<ArgsT>(args)...)
			, parent(p), child{ l, r } {
		}

		constexpr ~node() {
			parent   = npos;
			child[0] = npos;
			child[1] = npos;
		}

		size_t& operator[](bool d) {
			return child[d];
		}
	};
};

}

#endif // FENNEC_CONTAINERS_BINTREE_H