//  string : headerfile for the free standard C++ library
//  
//  Copyright (C) 1999 by the free standard C++ Library Team
//                        see AUTHORS for more details
//
//  Homepage : http://www.inf.fu-berlin.de/~mkrueger/fscl/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either	
//  version 2 of the License, or (at your option) any later version.
//
//  This library 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
//  Library General Public License for more details.
//  You should have received a copy of the GNU Library General Public
//  License along with this library; if not, write to the Free
//  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//  version : 0.1 last modified : 17.09.99

#ifndef __CPP_STRING
#define __CPP_STRING

#include<cstddef>
#include<new>

#include<iosfwd>
#include<iterator>
#include<algorithm>
#include<vector>
#include<stdexcept>

namespace std {

	// 21.3, basic_string:
	template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
	class basic_string
	{
		public:
			typedef vector<charT> Container; // NONSTANDARD

			typedef          traits                     traits_type;
			typedef typename traits::char_type          value_type;
			typedef          Allocator                  allocator_type;
			typedef typename Allocator::size_type       size_type;
			typedef typename Allocator::difference_type difference_type;
			typedef typename Allocator::reference       reference;
			typedef typename Allocator::const_reference const_reference;
			typedef typename Allocator::pointer         pointer;
			typedef typename Allocator::const_pointer   const_pointer;
			
			typedef typename Container::iterator        iterator;
			typedef typename Container::iterator        const_iterator;
			
			typedef std::reverse_iterator<iterator> reverse_iterator;
			typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

			static const size_type npos = -1;

			// 21.3.1 construct/copy/destroy
			
			explicit basic_string(const Allocator &a = Allocator()) 
			: c(a)
			{
			}
		
			basic_string(const basic_string &str, size_type pos = 0,  size_type n = npos, const Allocator& a = Allocator()) 
			: c(a)
			{
				assign(str, pos, n);
			}
			
			basic_string(const charT* s, size_type n, const Allocator& a = Allocator()) 
			: c(a)
			{
			}
			
			basic_string(const charT* s, const Allocator &a = Allocator()) 
			: c(s, s + traits::length(s) + 1)
			{
			}

			basic_string(size_type n, charT c, const Allocator& a = Allocator()) 
			: c(n, c, a)
			{
			}

			template<class InputIterator>
			basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()) 
			: c(begin, end, a)
			{
			}
			
			~basic_string()
			{
			}
			
			basic_string& operator=(const basic_string& str)
			{
				c = str.c;
				return *this;
			}
			
			basic_string& operator=(const charT* s)
			{
				c.assign(s, s + traits::length(s));
				return *this;
			}
			
			basic_string& operator=(charT c)
			{
				c.assign(1, c);			
				return *this;
			}
			
			// 21.3.2 iterators:

			iterator       begin()
			{
				return c.begin();
			}
			
			const_iterator begin() const
			{
				return (const_iterator)c.begin();
			}
			
			iterator       end()
			{
				return c.end();
			}
			
			const_iterator end() const
			{
				return (const_iterator)c.end();
			}
			
			reverse_iterator       rbegin()
			{
				return c.rbegin();
			}
			
			const_reverse_iterator rbegin() const
			{
				return c.rbegin();
			}
			
			reverse_iterator       rend()
			{
				return c.rend();
			}
			
			const_reverse_iterator rend() const
			{
				return c.rend();
			}

			// 21.3.3 capacity:			
			size_type size() const
			{
				return c.size();
			}
			
			size_type length() const
			{
				return c.size();
			}
			
			size_type max_size() const
			{
				return c.max_size();
			}
			
			void resize(size_type n, charT c)
			{
				c.resize(n, c);
			}
			
			void resize(size_type n)
			{
				c.resize(n);
			}
			
			size_type capacity() const
			{
				return c.capacity();
			}
			
			void reserve(size_type res_arg = 0)
			{
				c.reserve(res_arg);
			}
			
			void clear()
			{
				c.clear();
			}
			
			bool empty() const
			{
				return c.empty();
			}
			
			// 21.3.4 elemental access:
			const_reference operator[](size_type pos) const
			{
				return c[pos];
			}
			
			reference       operator[](size_type pos)
			{
				return c[pos];
			}
			
			const_reference at(size_type n) const
			{
				return c.at(n);
			}

			reference       at(size_type n)
			{
				return c.at(n);
			}
			
			// 21.3.5 modifiers:
			basic_string& operator+=(const basic_string& str)
			{
				return append(str);
			} 
			
			basic_string& operator+=(const charT* s)
			{
				return *this += basic_string<charT,traits,Allocator>(s);
			}
			
			basic_string& operator+=(charT c)
			{
				this->c.push_back(c);
				return *this;
			}
			
			basic_string& append(const basic_string& str)
			{
				return append(str, 0 , npos);
			}
			
			basic_string& append(const basic_string& str, size_type pos, size_type n)
			{
				if ( pos > str.size()) 
					throw out_of_range("out of range : pos <= str.size() required");
					
				if (n == npos)
					n = str.size() - pos;

				int rlen = n < str.size() - pos ? n : str.size() - pos;
				
				if (rlen + pos > str.size()) 
					throw length_error("length error : rlen + pos > str.size()");
				
				c.insert(c.end(), str.begin() + pos, str.begin() + rlen);
				return *this;
			}
			
			basic_string &append(const charT* s, size_type n)
			{
				return append(basic_string<charT,traits,Allocator>(s, n));
			}
			
			basic_string &append(const charT* s)
			{
				return append(basic_string<charT,traits,Allocator>(s));
			}
				
			basic_string& append(size_type n, charT c)
			{
				return append(basic_string<charT,traits,Allocator>(n,c));
			}
			
			template<class InputIterator>
			basic_string &append(InputIterator first, InputIterator last)
			{				
				return append(basic_string<charT,traits,Allocator>(first,last));
			}
			
			void push_back(const charT ch)
			{
				c.push_back(ch);
			}
			
			basic_string &assign(const basic_string &str)
			{
				return assign(str, 0, npos);
			}
			
			basic_string &assign(const basic_string& str, size_type pos, size_type n)
			{
				if (pos == npos)
					pos = str.size();
				if (pos > str.size())
					throw out_of_range("out of range : pos <= str.size() required");

				if (n == npos)
					n = str.size() - pos ;

				int rlen = n < str.size() - pos ? n : str.size() - pos;
					
				c.assign(str.begin() + pos, str.begin() + pos + rlen + 1);
				
				return *this;
			}
			
			basic_string &assign(const charT* s, size_type n)
			{
				return assign(basic_string<charT,traits,Allocator>(s,n));
			}
			
			basic_string &assign(const charT* s)
			{
				return assign(basic_string<charT, traits, Allocator>(s));
			}
			
			basic_string &assign(size_type n, charT c)
			{
				return assign(basic_string<charT,traits,Allocator>(n,c));
			}
			
			template<class InputIterator>
			basic_string& assign(InputIterator first, InputIterator last)			
			{
				return assign(basic_string<charT,traits,Allocator>(first,last));
			}
			
			basic_string& insert(size_type pos1, const basic_string &str)
			{
				return insert(pos1,str,0,npos);
			}

			basic_string& insert(size_type pos1, const basic_string &str, size_type pos2, size_type n)
			{
				if (pos1 > size() || pos2 > str.size())
					throw out_of_range("out of range");
				
				if ( n == npos)
					n = str.size() - pos2;
				
				int rlen = n < str.size() - pos2 ? n : str.size() - pos2;

				if (str.size() >= n + rlen)
					throw length_error("length error");
				
				c.insert(c.begin() + pos1, str.begin() + pos2, str.begin() + pos2 + rlen + 1);
				return *this;
			}
			
			basic_string& insert(size_type pos, const charT* s, size_type n)
			{
				return insert(pos,basic_string<charT,traits,Allocator>(s,n));
			}
			
			basic_string& insert(size_type pos, const charT* s)
			{
				return insert(pos,basic_string<charT,traits,Allocator>(s));
			}
			
			basic_string& insert(size_type pos, size_type n, charT c)
			{
				return insert(pos,basic_string<charT,traits,Allocator>(n,c));
			}
			
			iterator insert(iterator p, charT c = charT())
			{
				return 	c.insert(p, c);
			}
			
			void  insert(iterator p, size_type n, charT c)
			{
				c.insert(p, n ,c);
			}
			
			template<class InputIterator>
			void insert(iterator p, InputIterator first, InputIterator last)
			{
				c.insert(p, first ,last);
			}
			
			basic_string& erase(size_type pos = 0, size_type n = npos)
			{
				if (pos > size())
					throw out_of_range("out of range");
				if  (n == npos)
					n = size() - pos;
				
				int xlen = n < size() - pos ? n : size() - pos;
				
				c.erase(begin() + pos, begin() + pos + xlen);
					
				return *this;
			}
			
			iterator erase(iterator position)
			{
				return c.erase(position);
			}
			
			iterator erase(iterator first, iterator last)
			{
				return c.erase(first, last);
			}
			
			basic_string& replace(size_type pos1, size_type n1, const basic_string& str)
			{
				return replace(pos1, n1, str, 0, npos);
			}
			
			basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2)
			{
				if (pos1 > size() || pos2 > str.size())
					throw out_of_range("out of range");
				
				if (n1 == npos)
					n1 = size() - pos1;
				if (n2 == npos)
					n2 = str.size() - pos2;

				int xlen = n1 < size() - pos ? n1 : size() - pos1;
				int rlen = n2 < str.size() - pos2 ? n2 : str.size() - pos2;
				erase(begin() + pos1, begin() + pos1 + xlen);
				insert(begin() + pos1, str.begin() + pos2, str.begin() + pos2 + rlen);
				return *this;
			}
			
			basic_string& replace(size_type pos, size_type n1, const charT* s, size_type n2)
			{
				return replace(pos,n1,basic_string<charT,traits,Allocator>(s,n2));
			}
			
			basic_string& replace(size_type pos, size_type n1, const charT* s)
			{
				return replace(pos,n1,basic_string<charT,traits,Allocator>(s)); 
			}
			
			basic_string& replace(size_type pos, size_type n1, size_type n2, charT c)
			{
				return replace(pos,n1,basic_string<charT,traits,Allocator>(n2,c));
			}
			
			basic_string& replace(iterator i1, iterator i2, const basic_string& str)
			{
				erase(i1, i2);
				insert(i1, str);
				return *this;
			}
			
			basic_string& replace(iterator i1, iterator i2, const charT* s, size_type n)
			{
				return replace(i1,i2,basic_string<charT,traits,Allocator>(s,n));
			}
			
			basic_string& replace(iterator i1, iterator i2, const charT* s)
			{
				return replace(i1,i2,basic_string<charT,traits,Allocator>(s));
			}
			
			basic_string& replace(iterator i1, iterator i2, size_type n, charT c)
			{
				return replace(i1,i2,basic_string(n,c));
			}
			
			template<class InputIterator>
			basic_string& replace(iterator i1, iterator i2, InputIterator j1, InputIterator j2)
			{
				return replace(i1,i2,basic_string(j1,j2));
			}
			
			size_type copy(charT* s, size_type n, size_type pos = 0) const
			{
				if (pos > size())
					throw out_of_range("out of range");
				if (n == npos)
					n = size() - pos;
				int rlen = n < size() - pos ? n : size() - pos;
				copy(begin() + pos, begin() + pos + rlen, s);
			}

			void swap(basic_string<charT,traits,Allocator> &x)
			{
				c.swap(x.c);
			}
			
			//21.3.6 string operations:
			const charT* c_str() // const
			{
				c.push_back((charT)0);
				c.pop_back();
				return (const charT*)begin();
			}
			
			const charT* data() const
			{
				return (const charT*)begin();
			}
			
			allocator_type get_allocator() const
			{
				return c.get_allocator();
			}
			
			size_type find (const basic_string& str, size_type pos = 0) const
			{
				// MAIN FIND
				return search(begin() + pos, end(), str.begin(), str.end()) - begin();
			}			
			size_type find (const charT* s, size_type pos, size_type n) const
			{
				return find(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type find (const charT* s, size_type pos = 0) const
			{
				return find(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type find (charT c, size_type pos = 0) const
			{				
				return find(basic_string<charT,traits,Allocator>(1,c),pos);
			}
			
			size_type rfind(const basic_string& str, size_type pos = npos) const
			{
				// MAIN RFIND
				if (pos == npos)
					pos = 0;
				return find(rbegin() - pos, rend(), str.rbegin(), str.rend()) - rbegin();				
			}			
			size_type rfind(const charT* s, size_type pos, size_type n) const
			{
				return rfind(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type rfind(const charT* s, size_type pos = npos) const
			{
				return rfind(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type rfind(charT c, size_type pos = npos) const
			{
				return rfind(basic_string<charT,traits,Allocator>(1,c),pos);
			}
						
			size_type find_first_of(const basic_string& str, size_type pos = 0) const
			{ // TODO : VERIFY
				return find_first_of(begin() + pos, end(), str.begin(), str.end()) - begin();
			}			
			size_type find_first_of(const charT* s, size_type pos, size_type n) const
			{
				return find_first_of(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type find_first_of(const charT* s, size_type pos = 0) const
			{
				return find_first_of(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type find_first_of(charT c, size_type pos = 0) const
			{
				return find_first_of(basic_string<charT,traits,Allocator>(1,c),pos);
			}
			
			size_type find_last_of (const basic_string& str, size_type pos = npos) const
			{ // TODO 			
				return 0;
			}			
			size_type find_last_of (const charT* s, size_type pos, size_type n) const
			{
				return find_last_of(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type find_last_of (const charT* s, size_type pos = npos) const
			{
				return find_last_of(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type find_last_of (charT c, size_type pos = npos) const
			{
				return find_last_of(basic_string<charT,traits,Allocator>(1,c),pos);
			}
			
			size_type find_first_not_of(const basic_string& str, size_type pos = 0) const
			{
				// TODO
				return 0;
			}			
			size_type find_first_not_of(const charT* s, size_type pos, size_type n) const
			{
				return find_first_not_of(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type find_first_not_of(const charT* s, size_type pos = 0) const
			{
				return find_first_not_of(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type find_first_not_of(charT c, size_type pos = 0) const
			{
				return find_first_not_of(basic_string<charT,traits,Allocator>(1,c),pos);
			}
			
			size_type find_last_not_of (const basic_string& str, size_type pos = npos) const
			{
				// TODO
				return 0;
			}			
			size_type find_last_not_of (const charT* s, size_type pos, size_type n) const
			{
				return find_last_not_of(basic_string<charT,traits,Allocator>(s,n),pos);
			}			
			size_type find_last_not_of (const charT* s, size_type pos = npos) const
			{
				return find_last_not_of(basic_string<charT,traits,Allocator>(s),pos);
			}			
			size_type find_last_not_of (charT c, size_type pos = npos) const
			{
				return find_last_not_of(basic_string<charT,traits,Allocator>(1,c),pos);
			}
			
			basic_string substr(size_type pos = 0, size_type n = npos) const
			{
				if (pos > size())
					throw out_of_range("out of range");
				int rlen = n < size() - pos ? n : size() - pos;
				return basic_string<charT,traits,Allocator>(data()+pos,rlen);
			}

			int compare(const basic_string& str) const
			{				
				return traits::compare(data(), str.data() , length() < str.length() ? length() : str.length());
			}			
			int compare(size_type pos1, size_type n1, const basic_string& str) const
			{
				return basic_string<charT,traits,Allocator>(*this,pos1,n1).compare(str);
			}			
			int compare(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2) const
			{
				return basic_string<charT,traits,Allocator>(*this,pos1,n1).compare(basic_string<charT,traits,Allocator>(str,pos2,n2));
			}			
			int compare(const charT* s) const
			{
				return this->compare(basic_string<charT,traits,Allocator>(s));
			}
			int compare(size_type pos1, size_type n1, const charT* s, size_type n2 = npos) const
			{
				return basic_string<charT,traits,Allocator>(*this,pos,n1).compare(basic_string<charT,traits,Allocator>(s,n2));
			}

		private:
			Container c;
	};
		
	template<class charT, class traits, class Allocator>
	basic_string<charT,traits,Allocator> operator+(const basic_string<charT,traits,Allocator>& lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return basic_string<charT, traits, Allocator>(lhs) += basic_string<charT, traits, Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	basic_string<charT,traits,Allocator> operator+(const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) + basic_string<charT, traits, Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	basic_string<charT,traits,Allocator> operator+(charT lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return basic_string<charT,traits,Allocator>(1,lhs) + basic_string<charT, traits, Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	basic_string<charT,traits,Allocator> operator+(const basic_string<charT,traits,Allocator> &lhs, const charT *rhs)
	{
		return basic_string<charT, traits, Allocator>(lhs) + basic_string<charT,traits,Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	basic_string<charT,traits,Allocator> operator+(const basic_string<charT,traits,Allocator>& lhs, charT rhs)
	{
		return basic_string<charT, traits, Allocator>(lhs) + basic_string<charT,traits,Allocator>(1,rhs);		
	}

	template<class charT, class traits, class Allocator>
	bool operator==(const basic_string<charT,traits,Allocator> &lhs, const basic_string<charT,traits,Allocator> &rhs)
	{
		return lhs.compare(rhs) == 0;
	}

	template<class charT, class traits, class Allocator>
	bool operator==(const charT* lhs, const basic_string<charT,traits,Allocator> &rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) == rhs;
	}
		
	template<class charT, class traits, class Allocator>
	bool operator==(const basic_string<charT,traits,Allocator> &lhs, const charT *rhs)
	{
		return lhs == basic_string<charT,traits,Allocator>(rhs);
	}
	
	template<class charT, class traits, class Allocator>
	bool operator!=(const basic_string<charT,traits,Allocator> &lhs, const basic_string<charT,traits,Allocator> &rhs)
	{
		return !(lhs == rhs);
	}

	template<class charT, class traits, class Allocator>
	bool operator!=(const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) != rhs;
	}
	
	template<class charT, class traits, class Allocator>
	bool operator!=(const basic_string<charT,traits,Allocator> &lhs, const charT* rhs)
	{
		return lhs != basic_string<charT,traits,Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	bool operator< (const basic_string<charT,traits,Allocator> &lhs, const basic_string<charT,traits,Allocator> &rhs)
	{
		return lhs.compare(rhs) < 0;
	}
	
	template<class charT, class traits, class Allocator>
	bool operator< (const basic_string<charT,traits,Allocator>& lhs, const charT* rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) < rhs;
	}

	template<class charT, class traits, class Allocator>
	bool operator< (const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs < basic_string<charT,traits,Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	bool operator> (const basic_string<charT,traits,Allocator>& lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs.compare(rhs) > 0;
	}

	template<class charT, class traits, class Allocator>
	bool operator> (const basic_string<charT,traits,Allocator>& lhs, const charT* rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) > rhs;
	}
	
	template<class charT, class traits, class Allocator>
	bool operator> (const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs > basic_string<charT,traits,Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	bool operator<=(const basic_string<charT,traits,Allocator> &lhs, const basic_string<charT,traits,Allocator> &rhs)
	{
		return lhs.compare(rhs) <= 0;
	}

	template<class charT, class traits, class Allocator>
	bool operator<=(const basic_string<charT,traits,Allocator> &lhs, const charT* rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) <= rhs;
	}

	template<class charT, class traits, class Allocator>
	bool operator<=(const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs <= basic_string<charT,traits,Allocator>(rhs);
	}

	template<class charT, class traits, class Allocator>
	bool operator>=(const basic_string<charT,traits,Allocator>& lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs.compare(rhs) >= 0;
	}

	template<class charT, class traits, class Allocator>
	bool operator>=(const basic_string<charT,traits,Allocator>& lhs, const charT* rhs)
	{
		return basic_string<charT,traits,Allocator>(lhs) >= rhs;
	}

	template<class charT, class traits, class Allocator>
	bool operator>=(const charT* lhs, const basic_string<charT,traits,Allocator>& rhs)
	{
		return lhs >= basic_string<charT,traits,Allocator>(rhs);
	}


	// 21.3.7.8 :
	template<class charT, class traits, class Allocator>
	void swap(basic_string<charT,traits,Allocator>& lhs, basic_string<charT,traits,Allocator>& rhs)
	{
		lhs.swap(rhs);
	}

	template<class charT, class traits, class Allocator>
	basic_istream<charT,traits> &operator>>(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator> &str)
	{	
		ws(is);
		if (is.fail())
			return is;
		if (is.eof())
			return is;
		str = "";
		char c = is.get();
		while (c != ' ' && c != '\t' && c !='\n' && c != 0 && !is.eof())
		{	
			str += c;
			c = is.get();
		}
		if (!is.eof())
			is.unget();
		return is;
	} 
		
	template<class charT, class traits, class Allocator>
	basic_ostream<charT, traits> &operator<<(basic_ostream<charT, traits> &os, basic_string<charT,traits,Allocator> &str)
	{
		os.rdbuf()->sputn(str.begin(), str.size());
		return os;
	}

	template<class charT, class traits, class Allocator>
	basic_istream<charT,traits> &getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str, charT delim)
	{	
		ws(is);
		if (is.fail())
			return is;
		if (is.eof())
			return is;
		str = "";
		char c = is.get();
		while (c != delim && !is.eof())
		{	
			str += c;
			c = is.get();
		}
		if (!is.eof())
			is.unget();
		return is;
	}
	
	template<class charT, class traits, class Allocator>
	basic_istream<charT,traits> &getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str)
	{
		return getline(is, str, '\n');
	}

	typedef basic_string<char>    string;
	typedef basic_string<wchar_t> wstring;

}
#endif
