#include <iostream>
 
 
/* Node */
 
template <class Elem>
struct Link
{
	Link();
	Link (Link<Elem>* s, Link<Elem>* p, const Elem& v);
	Link (const Link<Elem>& src);
 
	Link<Elem>& operator= (const Link<Elem>& src);
	bool operator== (const Link<Elem>& src);
	bool operator!= (const Link<Elem>& src);
 
	void swap(Link<Elem>& src);
 
	Link* succ;
	Link* prev;
	Elem val;
};
 
//-----------------------------------------------------------------------------
 
template <class Elem>
Link<Elem>::Link()
	: succ(nullptr), prev(nullptr), val(Elem())
{
 
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
Link<Elem>::Link (Link<Elem>* s, Link<Elem>* p, const Elem& v)
	: succ(s), prev(p), val(v)
{
 
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
Link<Elem>::Link (const Link<Elem>& src)
	: succ(src.succ), prev(src.prev), val(src.val)
{
 
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
Link<Elem>& Link<Elem>::operator= (const Link<Elem>& src)
{
	Link<Elem> temp(src);
	swap(*this, temp);
	return *this;
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
bool Link<Elem>::operator== (const Link<Elem>& src)
{
	return succ = src.succ && prev = src.prev;
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
bool Link<Elem>::operator!= (const Link<Elem>& src)
{
	return !(*this == src);
}
 
//-----------------------------------------------------------------------------
 
template <class Elem>
void Link<Elem>::swap(Link<Elem>& src)
{
	std::swap(prev, src.prev);
	std::swap(succ, src.succ);
	std::swap(val, src.val);
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
void swap(Link<Elem>& lhs, Link<Elem>& rhs)
{
	lhs.swap(rhs);
}
 
/* Doubly Linked List */
//----------------------------------------------------------------------------
 
template <class Elem>
class List
{
public:
	class iterator;
 
	List();
 
	iterator begin() { return iterator(first, first, last); }
	iterator end() { return iterator(last, first, last); }
 
	void push_front(const Elem& v);
 
	Elem& front();
 
	size_t size(); 
	void print();
 
private:
	Link<Elem> *first;
	Link<Elem> *last;
};
 
//-----------------------------------------------------------------------------
 
template<class Elem>
List<Elem>::List()
	: first(new Link<Elem>()), last(first)
{
 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
void List<Elem>::push_front(const Elem& v)
{ 
	first = new Link<Elem>(first, nullptr, v); 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
Elem& List<Elem>::front()
{
	return first->val; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
size_t List<Elem>::size() 
{
	size_t count = 0;
	for (iterator p = begin(); p != end(); ++p)
	{
		++count;	 
	}
	return count;
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
void List<Elem>::print()
{
	for (iterator p = begin(); p != end(); ++p)
	{
		std::cout << *p <<' ';	 
	}
}
 
//-----------------------------------------------------------------------------
 
/* a range-checked bidirectional iterator */
template <class Elem>
class List<Elem>::iterator
{
public:
	iterator();											// default constructor
	iterator(Link<Elem>* c, Link<Elem>* b, Link<Elem>* e);					
	iterator(const iterator& src);						// copy constructor
	iterator operator= (const iterator& src);			// copy assignment 
 
	iterator& operator++();								// incrementations
	iterator operator++(int);							// postfix
	iterator& operator--();								// decrementations
	iterator operator--(int);							// postfix
 
	Elem& operator*();									// dereferenceable lvalue
	const Elem& operator*() const;						// dereferenceable rvalue
 
	bool operator== (const iterator& b) const;			// equality comparisons
	bool operator!= (const iterator& b) const;
 
	void swap(iterator& src);
 
private:
	Link<Elem>* curr;
	Link<Elem>* begin;
	Link<Elem>* end;
};
 
//-----------------------------------------------------------------------------
 
template<class Elem>
List<Elem>::iterator::iterator()
	: curr(nullptr), begin(nullptr), end(nullptr)
{
 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
List<Elem>::iterator::iterator(Link<Elem>* p, Link<Elem>* b, Link<Elem>* e)
	: curr(p), begin(b), end(e)
{
 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
List<Elem>::iterator::iterator(const iterator& src)
	: curr(src.curr), begin(src.begin), end(src.end)
{
 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
typename List<Elem>::iterator List<Elem>::iterator::operator= (const iterator& src)
{
	iterator temp(src);
	this->swap(temp);
	return *this;
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
typename List<Elem>::iterator& List<Elem>::iterator::operator++()
{
	if (curr == end)
	{
		throw std::out_of_range("List<Elem>::iterator::operator++(): out of range!\n");
	}
 
	curr = curr->succ;
 
	return *this; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
typename List<Elem>::iterator List<Elem>::iterator::operator++(int)
{
	if (curr == end)
	{
		throw std::out_of_range("List<Elem>::iterator::operator++(): out of range!\n");
	}
 
	Link<Elem>* old = curr;
	curr = curr->succ;
	return old; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
typename List<Elem>::iterator& List<Elem>::iterator::operator--()
{ 
	if (curr == begin)
	{
		throw std::out_of_range("List<Elem>::iterator::operator--(): out of range!\n");
	}
 
	curr = curr->prev;
 
	return *this; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
typename List<Elem>::iterator List<Elem>::iterator::operator--(int)
{ 
	if (curr == begin)
	{
		throw std::out_of_range("List<Elem>::iterator::operator--(): out of range!\n");
	}
 
	iterator old(*this);
	curr = curr->prev;	
	return old; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
Elem& List<Elem>::iterator::operator*() 
{ 
	return curr->val; 
}	
 
//-----------------------------------------------------------------------------
 
template<class Elem>
const Elem& List<Elem>::iterator::operator*() const
{ 
	return curr->val;
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
bool List<Elem>::iterator::operator== (const iterator& b) const
{ 
	return curr == b.curr; 
} 
 
//-----------------------------------------------------------------------------
 
template<class Elem>
bool List<Elem>::iterator::operator!= (const iterator& b) const
{ 
	return curr != b.curr; 
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
void List<Elem>::iterator::swap(iterator& src)
{
	std::swap(curr, src.curr); 
	std::swap(begin, src.begin);
	std::swap(end, src.end);
}
 
//-----------------------------------------------------------------------------
 
template<class Elem>
void swap(typename List<Elem>::iterator& lhs, typename List<Elem>::iterator& rhs)
{
	lhs.swap(rhs);
}
 
/* simple test for iterator */ 
 
int main() 
{
	List<int> l;
		l.push_front(1);
		l.push_front(2);
		l.push_front(3);
		l.push_front(4);
		l.push_front(5);
 
		// defualt constructor, copy assignment
		List<int>::iterator p = l.begin();
 
		// write dereferencing 
		*p = 100;
 
		// incrementation, read dereferencing
		List<int>::iterator i;
		for (i = l.begin(); i != l.end(); ++i)
		{
			std::cout << *i <<' ';
		}
 
		if (i == l.end() && i != l.begin())
		{
			std::cout <<"\ncomparison correct!\n";
		}
 
		// postfix and prefix decrementation; maintain dereferenceability
	    /*
		List<int>::iterator ii = l.end();
			ii--;
		for (; ii != l.begin(); ii--)
		{
			std::cout << *ii <<' ';
		} 
		*/
	return 0;
}