
#define MP_TPL	template < typename Key, typename T, typename Compare, typename Allocator >
#define MP		map<Key, T, Compare, Allocator>

namespace ft {


	/************
	 * copliens :
	 ************/
// constructors ------------------------------
MP_TPL MP::
	map (const key_compare & comp, const allocator_type & alloc)
	: _size(0)
	, _root(NULL)
	, _comp(comp)
	, _allocator(alloc) {

	_init_sentinel();
}
MP_TPL template < typename InputIt > MP::
	map (InputIt first, InputIt last, const key_compare& comp, const allocator_type& alloc)
	: _size(0)
	, _root(NULL)
	, _comp(comp)
	, _allocator(alloc) {

	_init_sentinel();
	insert(first, last);
}
MP_TPL MP::
	map(const map& src)
	: _size(0)
	, _root(NULL)
	, _comp(src._comp)
	, _allocator(src._allocator) {

	_init_sentinel();
	*this = src;
}
// destructor --------------------------------
MP_TPL MP::
	~map() {

	clear();
	_allocator_sentinel.destroy(_sentinel);
	_allocator_sentinel.deallocate(_sentinel, 1);	
}
// operator= ---------------------------------
MP_TPL MP& MP::
	operator=(const map& rhs) {

	if (this == &rhs)
		return (*this);
	map	new_bst(rhs.begin(), rhs.end());
	swap(new_bst);
	return (*this);
}


	/*************
	 * iterators :
	 *************/
// begin -------------------------------------
MP_TPL typename MP::iterator MP::
	begin() {

	if (_root)
		return iterator(_root->min(), _sentinel);
	else
		return end();
}
MP_TPL typename MP::const_iterator MP::
	begin() const {

	if (_root)
		return const_iterator(_root->min(), _sentinel);
	else
		return end();
}
// end ---------------------------------------
MP_TPL typename MP::iterator MP::
	end() { return iterator(NULL, _sentinel); }
MP_TPL typename MP::const_iterator MP::
	end() const { return const_iterator(NULL, _sentinel); }
// rbegin ------------------------------------
MP_TPL typename MP::reverse_iterator MP::
	rbegin() { return reverse_iterator(end()); }
MP_TPL typename MP::const_reverse_iterator MP::
	rbegin() const { return const_reverse_iterator(end()); }
// rend --------------------------------------
MP_TPL typename MP::reverse_iterator MP::
	rend() { return reverse_iterator(begin()); }
MP_TPL typename MP::const_reverse_iterator MP::
	rend() const { return const_reverse_iterator(begin()); }


	/************
	 * capacity :
	 ************/
// empty -------------------------------------
MP_TPL bool MP::	
	empty() const { return (_size == 0); }
// size --------------------------------------
MP_TPL typename MP::size_type MP::
	size() const { return (_size); }
// max_size ----------------------------------
MP_TPL typename MP::size_type MP::
	max_size() const { return ( _allocator_node.max_size() ); }


	/******************
	 * element access :
	 ******************/
// operator[] --------------------------------
MP_TPL typename MP::mapped_type& MP::
	operator[](const Key& key) {

	node<value_type>*	n = _root;

	while (n)
	{
		if (_comp(key, n->value.first))
			n = n->left;
		else if (_comp(n->value.first, key))
			n = n->right;
		else
			return (n->value.second);
	}

	n = insert( ft::make_pair(key, mapped_type()) ).first.getNode();

	return (n->value.second);
}


	/*************
	 * modifiers :
	 *************/
// insert ------------------------------------
MP_TPL pair<typename MP::iterator, bool> MP::
	insert(const value_type& value) {

	node<value_type>*					n = _root;
	node<value_type>*					next = n;

	while (next != NULL)
	{
		if (value.first == n->value.first)
			return ft::make_pair(iterator(n, _sentinel), false);
		n = next;
		if (value.first < n->value.first)
			next = n->left;
		else if (value.first > n->value.first)
			next = n->right;
	}

	next = _allocator_node.allocate(1);
	_allocator_node.construct(next, node<value_type>(value));

	if (_root == NULL)
	{
		_root = next;
		_sentinel->child = next;
	}
	else
	{
		if (value.first < n->value.first)
			n->left = next;
		else if (value.first > n->value.first)
			n->right = next;
	}
	next->up = n;
	_size++;

	_balance(n, INSERT);
	return (ft::make_pair(iterator(next, _sentinel), true));
}
MP_TPL typename MP::iterator MP::
	insert(iterator hint, const value_type& value) {

	(void)hint;
	return insert(value).first;
}
MP_TPL template < typename InputIt > void MP::
	insert(InputIt first, InputIt last) {

	for (; first != last; first++)
		insert(*first);
}
// erase -------------------------------------
// https://www.geeksforgeeks.org/binary-search-tree-set-2-delete
MP_TPL void MP::
	erase(iterator pos) {

	node<value_type>*	n = pos.getNode();
	node<value_type>*	n_del = NULL;
	node<value_type>*	next;

	if (n->left && n->right)
	{
		next = n->right->min();

		if (next->up != n)
		{
			_subtree_shift(next, next->right);
			next->right = n->right;
			next->right->up = next;
		}
		n_del = _subtree_shift(n, next);
		next->left = n->left;
		next->left->up = next;
	}
	else
	{
		if (n->left)
			n_del = _subtree_shift(n, n->left);
		else if (n->right)
			n_del = _subtree_shift(n, n->right);
		else
			n_del = _subtree_shift(n, NULL);
	}

	_allocator_node.destroy(n);
	_allocator_node.deallocate(n, 1);
	_size--;

	_balance(n_del, ERASE);
}
MP_TPL void MP::
	erase(iterator first, iterator last) {

	while (first != last)
		erase(first++);
}
MP_TPL typename MP::size_type MP::
	erase(const Key& key) {

	iterator	pos;

	pos = find(key);
	if (pos == end())
		return (0);
	erase(pos);
	return (1);
}
// swap --------------------------------------
MP_TPL void MP::
	swap(map& other) {

	node<value_type>*		tmp_root = _root;
	sentinel<value_type>*	tmp_sentinel = _sentinel;
	size_type				tmp_size = _size;

	_root = other._root;
	_sentinel = other._sentinel;
	_size = other._size;

	other._root = tmp_root;
	other._sentinel = tmp_sentinel;
	other._size = tmp_size;
}
// clear -------------------------------------
MP_TPL void MP::
	clear() { erase(begin(), end()); }


	/*************
	 * observers :
	 *************/
// key_comp ----------------------------------
MP_TPL typename MP::key_compare MP::
	key_comp() const { return (value_compare(_comp).comp); }
// value_comp --------------------------------
MP_TPL typename MP::value_compare MP::
	value_comp() const { return (value_compare(_comp)); }


	/**************
	 * operations :
	 **************/
// find --------------------------------------
MP_TPL typename MP::iterator MP::
	find(const Key& key) {

	node<value_type>*	n = _root;

	while (n)
	{
		if (_comp(key, n->value.first))
			n = n->left;
		else if (_comp(n->value.first, key))
			n = n->right;
		else
			return (iterator(n, _sentinel));
	}
	return (end());
}
MP_TPL typename MP::const_iterator MP::
	find(const Key& key) const {

	node<value_type>*	n = _root;

	while (n)
	{
		if (_comp(key, n->value.first))
			n = n->left;
		else if (_comp(n->value.first, key))
			n = n->right;
		else
			return (const_iterator(n, _sentinel));
	}
	return (end());
}
// count -------------------------------------
MP_TPL typename MP::size_type MP::
	count(const Key& key) const {

	if (find(key) != end())
		return (1);
	else
		return (0);
}
// lower_bound -------------------------------
MP_TPL typename MP::iterator MP::
	lower_bound (const key_type& k) {

	iterator	it = begin();
	iterator	it_end = end();

	while (it != it_end)
	{
		if (_comp(it->first, k) == false)
			return (it);
		++it;
	}
	return (it_end);
}
MP_TPL typename MP::const_iterator MP::
	lower_bound (const key_type& k) const {

	const_iterator	it = begin();
	const_iterator	it_end = end();

	while (it != it_end)
	{
		if (_comp(it->first, k) == false)
			return (it);
		++it;
	}
	return (it_end);
}
// upper_bound -------------------------------
MP_TPL typename MP::iterator MP::
	upper_bound (const key_type& k) {

	iterator	it = begin();
	iterator	it_end = end();

	while (it != it_end)
	{
		if (_comp(k, it->first))
			return (it);
		++it;
	}
	return (it_end);
}
MP_TPL typename MP::const_iterator MP::
	upper_bound (const key_type& k) const {

	const_iterator	it = begin();
	const_iterator	it_end = end();

	while (it != it_end)
	{
		if (_comp(k, it->first))
			return (it);
		++it;
	}
	return (it_end);
}
// equal_range -------------------------------
MP_TPL pair<typename MP::const_iterator, typename MP::const_iterator> MP::
	equal_range (const key_type& k) const {

	return ft::make_pair( lower_bound(k), upper_bound(k) );
}
MP_TPL pair<typename MP::iterator, typename MP::iterator> MP::
	equal_range (const key_type& k) {

	return ft::make_pair( lower_bound(k), upper_bound(k) );
}


	/*************
	 * allocator :
	 *************/
// get_allocator -----------------------------
MP_TPL typename MP::allocator_type MP::
	get_allocator() const { return (_allocator); }


	/*********************
	 * private functions :
	 *********************/
MP_TPL void MP::
	_init_sentinel() {

	_sentinel = _allocator_sentinel.allocate(1);
	_allocator_sentinel.construct(_sentinel, sentinel<value_type>());
}
MP_TPL node<typename MP::value_type>* MP::
	_subtree_shift(node<value_type>* n_old, node<value_type>* n_new) {

	node<value_type>*	p = n_old->up;

	if (n_old == _root)
	{	
		_root = n_new;
		_sentinel->child = _root;
	}
	else if (n_old == p->left)
		p->left = n_new;
	else
		p->right = n_new;
	
	if (n_new == NULL)
		return (p);
	n_new->up = p;
	return (n_new);
}
MP_TPL void MP::
	_balance(node<value_type>* n, bool action) {

	node<value_type>*	old_n;
	node<value_type>*	parent = NULL;

	while (n)
	{
		n->height = _compute_height(n);

		if (_balance_factor(n) > 1) // Left Heavy
		{
			parent = n->up;
			if (_balance_factor(n->left) < 0) // Left-Right Case (BF == -1)
				n->left = _rotate_left(n->left);
			// Left-Left Case
			n = _rotate_right(n);
			old_n = n->right;
		}
		else if (_balance_factor(n) < -1) // Right Heavy
		{
			parent = n->up;
			if (_balance_factor(n->right) > 0) // Right-Left Case (BF == 1)
				n->right = _rotate_right(n->right);
			// Right-Right Case
			n = _rotate_left(n);
			old_n = n->left;
		}

		if (parent)
		{
			if (parent->left == old_n)
				parent->left = n;
			else
				parent->right = n;
			if (action == INSERT)
				break;
			else if (action == ERASE)
				parent = NULL;
		}

		n = n->up;
	}

	if (action == INSERT)
	{
		while (n)
		{
			n->height = _compute_height(n);
			n = n->up;
		}
	}
}
MP_TPL short MP::
	_compute_height(node<value_type>* n) {

	if (n->left && n->right)
		return std::max(n->left->height, n->right->height) + 1;
	else if (n->left)
		return n->left->height + 1;
	else if (n->right)
		return n->right->height + 1;
	else
		return 1;
}
MP_TPL short MP::
	_balance_factor(node<value_type>* n) {

	if (n->left && n->right)
		return n->left->height - n->right->height;
	else if (n->left)
		return n->left->height;
	else if (n->right)
		return (-(n->right->height));
	else
		return 0;
}
MP_TPL node<typename MP::value_type>* MP::
	_rotate_left(node<value_type>* n) {

	node<value_type>*	ori_right = n->right;

	ori_right->up = n->up;
	n->up = ori_right;

	n->right = ori_right->left;
	if (n->right != NULL)
		n->right->up = n;
	ori_right->left = n;

	n->height = _compute_height(n);
	ori_right->height = _compute_height(ori_right);

	if (n == _root)
	{
		_root = ori_right;
		_sentinel->child = _root;
	}

	return ori_right;
}
MP_TPL node<typename MP::value_type>* MP::
	_rotate_right(node<value_type>* n) {

	node<value_type>*	ori_left = n->left;

	ori_left->up = n->up;
	n->up = ori_left;
	
	n->left = ori_left->right;
	if (n->left != NULL)
		n->left->up = n;
	ori_left->right = n;

	n->height = _compute_height(n);
	ori_left->height = _compute_height(ori_left);

	if (n == _root)
	{
		_root = ori_left;
		_sentinel->child = _root;
	}

	return ori_left;
}


	/************************
	 * non-member functions :
	 ************************/
// operator == -------------------------------
MP_TPL bool operator== (const MP& lhs, const MP& rhs) {

	if (lhs.size() != rhs.size())
		return false;
	return ft::equal(lhs.begin(), lhs.end(), rhs.begin());
}
// operator < --------------------------------
MP_TPL bool operator< (const MP& lhs, const MP& rhs) {

	return ft::lexicographical_compare(
		lhs.begin(),
		lhs.end(),
		rhs.begin(),
		rhs.end()
	);
}
// operator != -------------------------------
MP_TPL bool operator!= (const MP& lhs, const MP& rhs) {
	return !(lhs == rhs); }
// operator <= -------------------------------
MP_TPL bool operator<= (const MP& lhs, const MP& rhs) {
	return !(lhs > rhs); }
// operator > --------------------------------
MP_TPL bool operator> (const MP& lhs, const MP& rhs) {
	return (rhs < lhs); }
// operator >= -------------------------------
MP_TPL bool operator>= (const MP& lhs, const MP& rhs) {
	return !(lhs < rhs); }
// swap (map) -----------------------------
MP_TPL void swap(MP& lhs, MP& rhs) {
	lhs.swap(rhs); }


} // namespace ft

#undef MP
#undef MP_TPL

