// accelerated binomial heaps, -bds 03Oct,  Reference: CLR chapter 19
#ifndef __BINOMIALHEAP_H
#define __BINOMIALHEAP_H
/*
 * This implementation DOES provide constructors and the CLR functions:
 * Binomial_Heap_Insert(), Binomial_Heap_Minimum(), Binomial_Link(),
 * Binomial_Heap_Extract_Min(), and Binomial_Heap_Union().
 *
 * In this `accelerated' implementation the above functions all run in 
 * O(1) time except Extract_Min() whose amortized worst case time is O(lg(n)),
 * where n is the current size, n, of the heap.  This version of binomial heaps
 * may be viewed as a step towards Fibnonacci heaps.
 * 
 * 
 * This implementation DOES NOT provide:
 * Binomial_Heap_Decrease_Key() or Binomial_Heap_Delete()
 * 
 * An accelerated binomial heap is just an slist of binomial trees,
 * with a pointer to the tree whose root holds the minimum key and a
 * record of the number of keys in the heap.
 * For amortized analysis, define the potential, p(H), of a heap to be 
 * (c1 + c2) times the number of roots in the tree list, where c1 and c2 
 * are constant upper bounds bounds on certain portions of the _Extract_Min() 
 * function.  The potential of an empty heap is 0.
 */

#include "binomialTree.h"
// defines tree, aka binomialTree< Key >, and it's functions.

#include <vector> // for use by _Extract_Min()

// abbreviations:
#define heap binomialHeap< Key >

		template< class Key > 
class binomialHeap 
{ private:  
    treeList _trees;
    treePtr _min;
    int _n; // the total number of nodes (and keys) in all the trees of the heap.

  public: // basic set/getters

    binomialHeap() : _n(0) {} // construct an empty heap by default.

    friend treeList& trees( heap& H ){   return H._trees;   }
    friend const treeList& trees( const heap& H ){   return H._trees;   }

    friend treePtr& min( heap& H ){   return H._min;   }
    friend const treePtr& min( const heap& H ){   return H._min;   }

    friend int& n( heap& H ){   return H._n;   }
    friend const int& n( const heap& H ){   return H._n;   }

}; 
  
		template<class Key> inline 
Key Binomial_Heap_Minimum( const heap& H ) 
    /* Return the minimum key from H.  H must be nonempty.
     * Amortized worst case time is the actual time, Theta(1).  
     */
{   return key( *min(H) );  }   

		template<class Key> inline
void Binomial_Heap_Insert( heap& H, Key k )
    /* Add a one-key tree to H.  Amortized worst case time is the 
     * actual time + c1 + c2 (because of the new root), which is Theta(1). 
     */
{   tree T; 
    initialize( T, k );

    /// add the new tree to H.
    trees(H).push_front( T ); 

    /// fix min if necessary
    if ( ( n(H) == 0 ) || ( k < key( *min(H) ) ) ) min(H) = trees(H).begin();

    /// fix n 
    n(H)++;
}

		template<class Key> 
void Binomial_Heap_Union( heap& A, heap& B )
    /* A becomes ( A U B ).  Amortized worst case time is the actual time which is 
     * Theta(1).  The potential of B is transferred to A, and is still c1 + c2 times
     * the number of roots.
     */
{   
    /// Add B's trees to A's.
    trees(A).splice(trees(A).begin(), trees(B) );

    /// which min points to the tree with smaller key at it's root?
    if ( key( *min(B) ) < key( *min(A) ) ) min(A) = min(B); 

    /// fix n.
    n(A) += n(B);
}   

		template<class Key> inline
void Binomial_Heap_Extract_Min( heap& H )
    /* Remove the minimum key (and restore heap properties). H must be nonempty.
     * Amortized worst case time is O(lg(n)), for n the number of keys in H.
     * 
     * All steps are Theta(1) except where noted otherwise.
     */
{   
    int D = static_cast<int> ( 1 + ceil( log( static_cast<double>( n(H) ))
			 	       /log( 2.0 )));
    /* D is 1 + ceil (lg(n)), for n the current number of keys.  
     * No tree of degree greater than D exists at beginning, during, or at 
     * finish of the Extracti_Min process.
     *
     * First, we add to H those trees which are children of the root we then 
     * remove.  The potential function increases by at most 2D because of the 
     * new roots.
     *
     * Let s denote the number of roots H will have after removing min and 
     * adding it's children to the root list.  
     * Thus s = trees(H).size() - 1 + children(*min(H)).size().
     */

    trees(H).splice(trees(H).begin(), children( *min(H) ) );
    trees(H).erase( min(H) ); 
    n(H)--;

    /* Now s == trees(H).size().  
     * This step took amortized worst case time = actual constant time plus
     * (c1 + c2) times O(D), where the O(D) is for the children of the min node
     * which became roots.  The potential function is now at p(H) = (c1 + c2)*s.
     *
     * dear H, a problem arises:  do you know where your min is?
     * We'll compactify things and find the min at the same time.
     * H may have many trees of the same degree (s may be much bigger than D).  
     * For each degree we'll link them up as we find them.
     */

    /// Next, Create a place to accumulate the combined trees from H

    treePtr nil; 

    typedef vector< treePtr > array;

L0: array A( D, nil ); // place to accumulate the combined trees
	// Worst case time to initialize A is Theta(D).

    /// Accumulate the nodes, linking as we find same degree nodes.

L1: for ( treePtr p = trees(H).begin(); p != trees(H).end(); ++p )
        // Amortized cost O(D) to link up any equal degree trees.
    {   
	tree& T = *p;
	int d;
	for ( d = degree(T); A[d] != nil; d = degree(T) ) 
	{   T = Binomial_Link( T, *A[d] );
	    A[d] = nil;
	}
	A[ d ] = p;
    }
    /* In the above loop each of s nodes is put in A. Some are also linked into 
     * others.  let c1 be an upper bound for this putting and linking.  Note 
     * that the body of the inner for loop is executed at most s times because 
     * each node is linked under another most once.  Those nodes which were 
     * linked as children of others will not be roots in the final result, so 
     * the potential decreases by at most c1*(s - O(D)), where O(D) trees are 
     * directly referenced from A.
     * 
     * One unit of potential from all but O(D) roots is used up: 
     * p(H) -= c1*(s - O(D)).  Now p(H) = c1*(trees in A) + c2*s.
     */

    /// Now put the good nodes in a treeList to go into the revised heap.
    treeList L;
L2: for (typename array::iterator p = A.begin(); p != A.end(); ++p)
        // Worst case time is Theta(D).
    {   treePtr x = *p;
        if ( x != nil ) 
	    L.push_front( *x ); 
	// impt that this {/em copies} something (from H) into L.
    }

L3: trees(H).clear(); 
    /* amortized O(D).  Let c2 be the cost of deallocating a list node from 
     * memory.  The second unit of potential from s - O(D) roots is used up:
     * p(H) -= c2*(s - O(D)).  Now p(H) = (c1 + c2)*(trees in L).
     */
    trees(H).swap(L); // Theta(1), as Zhendong points out.
        // now p(H) = (c1 + c2)*(trees(H).size()) as desired.

    /// now finish by finding the min of the short list.

L4: min( H ) = 
	min_element( trees(H).begin(), trees(H).end(), lessByKey<Key>() );
        // Worst case time is O(D).
    
}   

#endif
#if 0
/* test code. 
 * To use it, copy file to .cc and change #if value above to 1. 
 * To restore as a header, copy file to .h and change #if value above to 0.
 */
#include <iostream>
using namespace std;

int main()
{
    binomialHeap< long > H;
    //slist< binomialTree< long > > H; // binomialHeap< long >

//	Binomial_Heap_Insert( H, long(99) );
//	cout << Binomial_Heap_Minimum( H ) << " first min " << endl;
    for (long i = 0; i < 256; ++i) 
	Binomial_Heap_Insert( H, i+100 );

    for (int i = 0; i < 100; ++i) 
    {
	cout << Binomial_Heap_Minimum( H ) << " ";
	Binomial_Heap_Extract_Min( H );
	Binomial_Heap_Extract_Min( H );
	Binomial_Heap_Insert( H, random()%200 );
    }
    cout << endl;
}
#endif
