import java.util.Arrays;

/**
 * A class that contains several sorting routines, implemented as static
 * methods.  Arrays are rearranged with smallest item first, using compares.
 *                                                                          <p>
 * Use the method tracingOn to enable or disable printed tracing information
 * as the methods run.
 *
 * @author Mark Allen Weiss (tracing additions by Gene Fisher)
 */
public final class Sort
{
    /**
     * Simple insertion sort.
     * @param a an array of Comparable items.
     */
    public static void insertionSort( Comparable [ ] a )
    {
        for( int p = 1; p < a.length; p++ )
        {
            Comparable tmp = a[ p ];
            int j = p;

            for( ; j > 0 && tmp.compareTo( a[ j - 1 ])<0 ; j-- )
            {
                printTraceLine("    p=" + p + ",j=" + j + " ", a);

                a[ j ] = a[ j - 1 ];

            }
            a[ j ] = tmp;
        }
    }


    /**
     * Shellsort, using a sequence suggested by Gonnet.
     * @param a an array of Comparable items.
     */
    public static void shellsort( Comparable [ ] a )
    {
        for( int gap = a.length / 2; gap > 0;
                     gap = gap == 2 ? 1 : (int) ( gap / 2.2 ) )
            for( int i = gap; i < a.length; i++ )
            {
                Comparable tmp = a[ i ];
                int j = i;

                for( ; j >= gap && tmp.compareTo( a[ j - gap ] )<0; j -= gap )
                {
                  printTraceLine("    i=" + i + ",j=" + j + " ", a);

                  a[ j ] = a[ j - gap ];

                }

                a[ j ] = tmp;
            }
    }

    /**
     * Standard heapsort.
     * @param a an array of Comparable items.
     */
    public static void heapsort( Comparable [ ] a )
    {
        for( int i = a.length / 2; i >= 0; i-- )  /* buildHeap */
            percDown( a, i, a.length );

        for( int i = a.length - 1; i > 0; i-- )
        {
            printTraceLine("    pre-swap, i=" + i + " ", a);

            swapReferences( a, 0, i );            /* deleteMax */

            printTraceLine("    pre-perc, i=" + i + " ", a);

            percDown( a, 0, i );
        }
    }

    /**
     * Internal method for heapsort.
     * @param i the index of an item in the heap.
     * @return the index of the left child.
     */
    private static int leftChild( int i )
    {
        return 2 * i + 1;
    }

    /**
     * Internal method for heapsort that is used in
     * deleteMax and buildHeap.
     * @param a an array of Comparable items.
     * @index i the position from which to percolate down.
     * @int n the logical size of the binary heap.
     */
    private static void percDown( Comparable [ ] a, int i, int n )
    {
        int child;
        Comparable tmp;

        for( tmp = a[ i ]; leftChild( i ) < n; i = child )
        {
            child = leftChild( i );
            
            if( child != n - 1 && a[ child ].compareTo( a[ child + 1 ] )<0 )
                 child++;
           if( tmp.compareTo( a[ child ] ) <0)
                a[ i ] = a[ child ];
            else
                break;
        }
        a[ i ] = tmp;
    }

    /**
     * Mergesort algorithm.
     * @param a an array of Comparable items.
     */
    public static void mergeSort( Comparable [ ] a )
    {
        Comparable [ ] tmpArray = new Comparable[ a.length ];

        mergeSort( a, tmpArray, 0, a.length - 1, 0 );
    }

    /**
     * Internal method that makes recursive calls.
     * @param a an array of Comparable items.
     * @param tmpArray an array to place the merged result.
     * @param left the left-most index of the subarray.
     * @param right the right-most index of the subarray.
     */
    private static void mergeSort( Comparable [ ] a, Comparable [ ] tmpArray,
               int left, int right, int phase )
    {
        if( left < right )
        {
            int center = ( left + right ) / 2;

            mergeSort( a, tmpArray, left, center, 0 );
            mergeSort( a, tmpArray, center + 1, right, 1 );
            merge( a, tmpArray, left, center + 1, right );
        }
    }

    /**
     * Internal method that merges two sorted halves of a subarray.
     * @param a an array of Comparable items.
     * @param tmpArray an array to place the merged result.
     * @param leftPos the left-most index of the subarray.
     * @param rightPos the index of the start of the second half.
     * @param rightEnd the right-most index of the subarray.
     */
    private static void merge( Comparable [ ] a, Comparable [ ] tmpArray,
           int leftPos, int rightPos, int rightEnd )
    {
        int leftEnd = rightPos - 1;
        int tmpPos = leftPos;
        int numElements = rightEnd - leftPos + 1;

//	if (tracingOn) {
	    System.out.println(
		"    merging " + leftPos + "," + rightPos + "," + rightEnd);
//	}

        // Main loop
        while( leftPos <= leftEnd && rightPos <= rightEnd )
            if( a[ leftPos ].compareTo( a[ rightPos ] )<0 )
                tmpArray[ tmpPos++ ] = a[ leftPos++ ];
            else
                tmpArray[ tmpPos++ ] = a[ rightPos++ ];

        while( leftPos <= leftEnd )    // Copy rest of first half
            tmpArray[ tmpPos++ ] = a[ leftPos++ ];

        while( rightPos <= rightEnd )  // Copy rest of right half
            tmpArray[ tmpPos++ ] = a[ rightPos++ ];

        // Copy TmpArray back
        for( int i = 0; i < numElements; i++, rightEnd-- )
            a[ rightEnd ] = tmpArray[ rightEnd ];

        System.out.println("        " + Arrays.asList(a));
    }

    /**
     * Quicksort algorithm.
     * @param a an array of Comparable items.
     */
    public static void quicksort( Comparable [ ] a )
    {
        quicksort( a, 0, a.length - 1 );
    }

    private static final int CUTOFF = 10;

    /**
     * Method to swap to elements in an array.
     * @param a an array of objects.
     * @param index1 the index of the first object.
     * @param index2 the index of the second object.
     */
    public static void swapReferences( Object [ ] a, int index1, int index2 )
    {
        Object tmp = a[ index1 ];
        a[ index1 ] = a[ index2 ];
        a[ index2 ] = tmp;
    }

    /**
     * Internal quicksort method that makes recursive calls.
     * Uses median-of-three partitioning and a cutoff of 10.
     * @param a an array of Comparable items.
     * @param low the left-most index of the subarray.
     * @param high the right-most index of the subarray.
     */
    private static void quicksort( Comparable [ ] a, int low, int high )
    {
        if( low + CUTOFF > high )
            insertionSort( a, low, high );
        else
        {

            printTraceLine("    before pivot placement,   low=" + low +
                ",high=" + high + " ", a);

                // Sort low, middle, high
            int middle = ( low + high ) / 2;
            if( a[ middle ].compareTo( a[ low ] )<0 )
                swapReferences( a, low, middle );
            if( a[ high ].compareTo( a[ low ] )<0 )
                swapReferences( a, low, high );
            if( a[ high ].compareTo( a[ middle ] ) <0)
                swapReferences( a, middle, high );

                // Place pivot at position high - 1
            swapReferences( a, middle, high - 1 );
            Comparable pivot = a[ high - 1 ];

            printTraceLine("    before partioning,        low=" + low +
                ",high=" + high + " ", a);

                // Begin partitioning
            int i, j;
            for( i = low, j = high - 1; ; )
            {
                while( a[ ++i ].compareTo( pivot )<0 )
                    ;
                while( pivot.compareTo( a[ --j ] )<0 )
                    ;
                if( i < j )
                    swapReferences( a, i, j );
                else
                    break;
            }

                // Restore pivot
            swapReferences( a, i, high - 1 );

            quicksort( a, low, i - 1 );    // Sort small elements
            quicksort( a, i + 1, high );   // Sort large elements
        }
    }

    /**
     * Internal insertion sort routine for subarrays
     * that is used by quicksort.
     * @param a an array of Comparable items.
     * @param low the left-most index of the subarray.
     * @param n the number of items to sort.
     */
    private static void insertionSort( Comparable [ ] a, int low, int high )
    {
        for( int p = low + 1; p <= high; p++ )
        {
            Comparable tmp = a[ p ];
            int j;

            for( j = p; j > low && tmp.compareTo( a[ j - 1 ] )<0; j-- )
                a[ j ] = a[ j - 1 ];
            a[ j ] = tmp;
        }
    }

    /**
     * Quick selection algorithm.
     * Places the kth smallest item in a[k-1].
     * @param a an array of Comparable items.
     * @param k the desired rank (1 is minimum) in the entire array.
     */     
    public static void quickSelect( Comparable [ ] a, int k )
    {
        quickSelect( a, 0, a.length - 1, k );
    }

    /**
     * Internal selection method that makes recursive calls.
     * Uses median-of-three partitioning and a cutoff of 10.
     * Places the kth smallest item in a[k-1].
     * @param a an array of Comparable items.
     * @param low the left-most index of the subarray.
     * @param high the right-most index of the subarray.
     * @param k the desired rank (1 is minimum) in the entire array.
     */
    private static void quickSelect( Comparable [ ] a, int low, int high, int k )
    {
        if( low + CUTOFF > high )
            insertionSort( a, low, high );
        else
        {
                // Sort low, middle, high
            int middle = ( low + high ) / 2;
              if( a[ middle ].compareTo( a[ low ] ) <0)
                swapReferences( a, low, middle );
              if( a[ high ].compareTo( a[ low ] ) <0)
                swapReferences( a, low, high );
              if( a[ high ].compareTo( a[ middle ] )<0 )
                swapReferences( a, middle, high );

                // Place pivot at position high - 1
            swapReferences( a, middle, high - 1 );
            Comparable pivot = a[ high - 1 ];

                // Begin partitioning
            int i, j;
            for( i = low, j = high - 1; ; )
            {
                  while( a[ ++i ].compareTo( pivot )<0 )
                    ;
                  while( pivot.compareTo( a[ --j ] )<0 )
                    ;
                if( i < j )
                    swapReferences( a, i, j );
                else
                    break;
            }

                // Restore pivot
            swapReferences( a, i, high - 1 );

                // Recurse; only this part changes
            if( k <= i )
                quickSelect( a, low, i - 1, k );
            else if( k > i + 1 )
                quickSelect( a, i + 1, high, k );
        }
    }

    /**
     * Turn tracing on or off.
     */
    public static void setTracingOn(boolean on) {
        tracingOn = on;
    }

    /**
     * If this.tracingOn is true, print a line of tracing information to
     * stdout.  The inforation consists of the given string message followed by
     * a space-delimited list of the elements in the give array.  This method
     * is called from within the sorting methods to help trace the progress of
     * the sort.
     */
    protected static void printTraceLine(String message, Object[] array) {

        if (tracingOn) {
            System.out.println(message + Arrays.asList(array));
        }

    }

    /**
     * Like printTraceLine, but also prints a stack trace, for use in tracing
     * recursive sort algorithms.
     */
    protected static void printTraceLine2(String message, Object[] array) {

        if (tracingOn) {
            System.out.println(message + Arrays.asList(array));
            try {
                throw (new Exception());
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /** Method tracing is on if tracingOn == true. */
    protected static boolean tracingOn = false;

}