/* Quicksort
 * 15-122 Principles of Imperative Computation, Spring 2011
 * Frank Pfenning
 */

bool is_sorted(int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
{ int i;
  for (i = lower; i < upper-1; i++)
    //@loop_invariant lower == upper || (lower <= i && i <= upper-1);
    if (!(A[i] <= A[i+1])) return false;
  return true;
}

bool leq(int x, int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
{ 
  for (int i = lower; i < upper; i++)
    //@loop_invariant lower <= i && i <= upper;
    if (!(x <= A[i])) return false;
  return true;
}

bool geq(int x, int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
{ 
  for (int i = lower; i < upper; i++)
    //@loop_invariant lower <= i && i <= upper;
    if (!(x >= A[i])) return false;
  return true;
}

bool gt(int x, int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
{ 
  for (int i = lower; i < upper; i++)
    //@loop_invariant lower <= i && i <= upper;
    if (!(x > A[i])) return false;
  return true;
}

bool lt(int x, int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
{ 
  for (int i = lower; i < upper; i++)
    //@loop_invariant lower <= i && i <= upper;
    if (!(x < A[i])) return false;
  return true;
}

void swap(int[] A, int i, int j)
//@requires 0 <= i && i < \length(A);
//@requires 0 <= j && j < \length(A);
//@ensures A[i] == \old(A[j]) && A[j] == \old(A[i]);
{ int tmp = A[i];
  A[i] = A[j];
  A[j] = tmp;
}

int partition(int[] A, int lower, int upper)
//@requires 0 <= lower && lower < upper && upper <= \length(A);
//@ensures lower <= \result && \result < upper;
//@ensures gt(A[\result], A, lower, \result);
//@ensures leq(A[\result], A, \result+1, upper);
{
  int pivot_index = lower+(upper-lower)/2;
  int pivot = A[pivot_index];
  swap(A, pivot_index, upper-1); /* establishes (3) */
  int left = lower;
  int right = lower;
  while (right < upper-1)
    //@loop_invariant lower <= left && left <= right && right < upper;
    //@loop_invariant gt(pivot, A, lower, left);
    //@loop_invariant leq(pivot, A, left, right);
    //@loop_invariant pivot == A[upper-1];
    {
      if (pivot <= A[right]) {
	right++;
      } else {
	swap(A, left, right);
	left++;
	right++;
      }
    }
  swap(A, left, upper-1);
  return left;
}

void qsort(int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
//@ensures is_sorted(A, lower, upper);
{
  if (upper-lower <= 1) return;
  int i = partition(A, lower, upper);
  qsort(A, lower, i);
  qsort(A, i+1, upper);
  return;
}
