/*
    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/>.
*/
#include <iostream>
#include <algorithm>
#include <libmaus2/avl/AVLSet.hpp>
#include <cassert>
#include <cstdlib>
#include <set>

int main()
{
	try
	{
		::std::srand(10);

		typedef libmaus2::avl::AVLSet<uint64_t> tree_type;
		tree_type AVL;

		uint64_t const n = 10000;

		std::vector<uint64_t> VT;

		for ( uint64_t i = 0; i < n; ++i )
		// for ( uint64_t i = 0; i < 16; ++i )
		{
			uint64_t const v = ::std::rand();
			AVL.insert(v);
			VT.push_back(v);

			#if defined(AVL_TREE_DEBUG)
			AVL.check(std::cerr);
			#endif
		}

		std::set<uint64_t> ST(VT.begin(),VT.end());
		std::vector<uint64_t> VU(ST.begin(),ST.end());

		for ( uint64_t i = 0; i < VU.size(); ++i )
		{
			tree_type::const_iterator const it = AVL.find(VU[i]);
			assert ( it != AVL.end() && *it == VU[i] );
		}

		#if 0
		std::cerr << std::string(80,'-') << std::endl;
		AVL.print(std::cerr);
		#endif

		AVL.check(std::cerr);

		std::vector<uint64_t> V;
		for ( tree_type::const_iterator it = AVL.begin(); it != AVL.end(); ++it )
		{
			uint64_t const v = *it;
			std::cerr << *it << ";";

			V.push_back(v);
		}
		std::cerr << std::endl;

		// check whether extract sequence is sorted
		for ( uint64_t i = 1; i < V.size(); ++i )
			assert ( V[i-1] < V[i] );

		assert ( V.size() == ST.size() );

		std::vector<uint64_t> const VV(AVL.begin(),AVL.end());
		assert ( VV == VU );

		std::sort(VT.begin(),VT.end());
		VT.resize(std::unique(VT.begin(),VT.end())-VT.begin());
		assert ( VT == VV );

		// test reverse iterator
		std::vector<uint64_t> R;
		for ( tree_type::const_reverse_iterator it = AVL.rbegin(); it != AVL.rend(); ++it )
		{
			uint64_t const v = *it;
			R.push_back(v);
		}

		assert ( R.size() == V.size() );
		for ( uint64_t i = 0; i < R.size(); ++i )
			assert (
				R[i]
				==
				V[V.size()-i-1]
			);

		// test lower bound
		for ( uint64_t i = 0; i < V.size(); ++i )
		{
			// std::cerr << "searching for " << V[i] << std::endl;

			if ( i == 0 )
			{
				if ( V[i] > 0 )
				{
					tree_type::const_iterator it = AVL.lower_bound(V[i]-1);
					assert (
						it != AVL.end()
						&&
						*it == V[i]
					);
				}
				else
				{
					tree_type::const_iterator it = AVL.lower_bound(V[i]);
					assert (
						it != AVL.end()
						&&
						*it == V[i]
					);
				}
			}
			else
			{
				if ( V[i] > V[i-1]+1 )
				{
					assert ( V[i] );

					tree_type::const_iterator it = AVL.lower_bound(V[i]-1);
					assert (
						it != AVL.end()
						&&
						*it == V[i]
					);
				}
				else
				{
					tree_type::const_iterator it = AVL.lower_bound(V[i]);
					assert (
						it != AVL.end()
						&&
						*it == V[i]
					);
				}
			}

			tree_type::const_iterator it = AVL.lower_bound(V[i]);
			assert ( it != AVL.end() && *it == V[i] );
		}

		// test operator[]
		for ( uint64_t i = 0; i < V.size(); ++i )
		{
			tree_type::const_iterator it = AVL[i];
			assert ( it != AVL.end() && *it == V[i] );
		}

		uint64_t erased = 0;

		std::vector<bool> Verased(V.size(),false);

		while ( erased < V.size() )
		{
			uint64_t const i = ::std::rand() % V.size();

			if ( ! Verased[i] )
			{
				bool const ok = AVL.erase(V[i]);

				assert ( ok );

				#if defined(AVL_TREE_DEBUG)
				AVL.check(std::cerr);
				#endif

				Verased[i] = true;
				erased += 1;
			}
		}

		for ( uint64_t i = 0; i < n; ++i )
			AVL.insert(VT[i]);

		AVL.clear();

		assert ( AVL.empty() );

		std::cerr << "[V] testavlset succesful" << std::endl;

		return 0;
	}
	catch(std::exception const & ex)
	{
		std::cerr << ex.what() << std::endl;
		return EXIT_FAILURE;
	}
}
