import edu.rice.hj.api.HjPoint;

import java.util.Random;

import static edu.rice.hj.Module1.newPoint;

/**
 * Quicksort program adapted for CS 181E homework
 *
 * @author Vivek Sarkar (vsarkar@rice.edu)
 */
public class QuicksortSeq {

    /**
     * Method that sorts an array using the quicksort algorithm
     *
     * @param A - Array of comparable objects
     * @param M - the lower bounds of the area to sort
     * @param N - the upper bounds of the area to sort
     */
    protected static void quicksort(final Comparable[] A, final int M, final int N) {
        if (M < N) {
            // A point in HJ is an integer tuple (see Section 9.1 of Module 1 handout)
            HjPoint p = partition(A, M, N);
            int I = p.get(0);
            int J = p.get(1);
            quicksort(A, M, I);
            quicksort(A, J, N);
        }
    }

    /**
     * Mutates an array such that between two points in the array, all
     * points less then a pivot point are to the left of that point, and
     * all points greater then a pivot point are to the right of that point
     *
     * @param A - a comparable array
     * @param M - the lower bounds of the area to partition
     * @param N - the upper bounds of the area to partition
     * @return - A point consisting of two locations around the pivot point
     */
    @SuppressWarnings("unchecked")
    private static HjPoint partition(final Comparable[] A, final int M, final int N) {

        int I;
        int storeIndex = M;
        final Random rand = new Random(M + N);
        final int pivot = M + rand.nextInt(N - M + 1);
        final Comparable pivotValue = A[pivot];

        QuicksortUtil.exchange(A, pivot, N);
        for (I = M; I < N; I++) {
            if (A[I].compareTo(pivotValue) <= 0) {
                QuicksortUtil.exchange(A, I, storeIndex);
                storeIndex++;
            }
        }
        QuicksortUtil.exchange(A, storeIndex, N);

        if (storeIndex == N) {
            return newPoint(storeIndex - 1, N);
        } else if (storeIndex == M) {
            return newPoint(M, storeIndex + 1);
        }
        return newPoint(storeIndex - 1, storeIndex + 1);
    }

}