#include <catch2/catch.hpp>

#include <alib/cpp_heap>
#include <alib/binomial_heap>
#include <alib/fibonacci_heap>

static int less ( const int & a, const int & b ) {
	return a < b;
}

TEST_CASE ( "Heaps Test", "[unit][std][bits]" ) {
	SECTION ( "Heaps" ) {
		alib::BinomialHeap < int, int ( * ) ( const int &, const int & ) > bHeap ( less );
		alib::CppHeap < int, int ( * ) ( const int &, const int & ) > cHeap ( less );
		alib::FibonacciHeap < int, int ( * ) ( const int &, const int & ) > fHeap ( less );

		auto size = [ & ] ( ) {
			int b = bHeap.size ( );
			int c = cHeap.size ( );
			int f = fHeap.size ( );
			CHECK ( ( b == c && c == f ) );
			return b;
		};

		auto push = [ & ] ( int a ) {
			bHeap.insert ( a );
			cHeap.insert ( a );
			fHeap.insert ( a );
		};

		auto pop = [ & ] ( ) {
			int b = bHeap.extractMax ( );
			int c = cHeap.extractMax ( );
			int f = fHeap.extractMax ( );
			if ( b != c || c != f) {
				INFO ( "??" << "size = " << size ( ) );
				INFO ( "b = " << b << " c = " << c << " f = " << f );
			}
			CHECK ( b == c );
			CHECK ( c == f );
			return b;
		};

		auto mergeRandom = [ & ] ( unsigned limit ) {
			alib::BinomialHeap < int, int ( * ) ( const int &, const int & ) > rbHeap ( less );
			alib::CppHeap < int, int ( * ) ( const int &, const int & ) > rcHeap ( less );
			alib::FibonacciHeap < int, int ( * ) ( const int &, const int & ) > rfHeap ( less );

			for ( unsigned i = 0; i < limit; ++ i ) {
				unsigned val = rand ( ) % 1000;
				rbHeap.insert ( val );
				rcHeap.insert ( val );
				rfHeap.insert ( val );
			}

			bHeap.mergeWith ( std::move ( rbHeap ) );
			cHeap.mergeWith ( std::move ( rcHeap ) );
			fHeap.mergeWith ( std::move ( rfHeap ) );
		};

		push ( 1 );
		CHECK ( size ( ) == 1 );
		CHECK ( pop ( ) == 1 );
		INFO ( "merge random 50" );
		mergeRandom ( 50 );
		CHECK ( size ( ) == 50 );
		INFO ( "pop 25" );
		for ( unsigned i = 0; i < 25; ++ i ) {
			INFO ( "pop = " << pop ( ) );
		}
		INFO ( "merge random 50" );
		mergeRandom ( 50 );
		CHECK ( size ( ) == 75 );
		INFO ( "pop 25" );
		for ( unsigned i = 0; i < 25; ++ i ) {
			INFO ( "pop = " << pop ( ) );
		}
		INFO ( "merge random 50" );
		mergeRandom ( 50 );
		CHECK ( size ( ) == 100 );
		INFO ( "pop 25" );
		for ( unsigned i = 0; i < 25; ++ i ) {
			INFO ( "pop = " << pop ( ) );
		}
		INFO ( "merge random 50" );
		mergeRandom ( 50 );
		CHECK ( size ( ) == 125 );
		INFO ( "pop 25" );
		for ( unsigned i = 0; i < 25; ++ i ) {
			INFO ( "pop = " << pop ( ) );
		}
		INFO ( "merge random 50" );
		mergeRandom ( 50 );
		CHECK ( size ( ) == 150 );
		INFO ( "push random 25" );
		//	fHeap.checkConsystency ( );
		for ( unsigned i = 0; i < 25; ++ i ) {
			push ( rand ( ) % 1000 );
		}
		INFO ( "pop all" );
		for ( unsigned i = 0; i < 175; ++ i ) {
			INFO ( "pop = " << pop ( ) );
		}
	}
}
