/* Mergesort, version with additional invariants
 * 15-122 Principles of Imperative Computation, Fall 2010
 * 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;
}

/* merge(A, lower, mid, upper) merges two adjacent sorted
 * segments A[lower..mid) and A[mid..upper) into A[lower..upper)
 * This is the fully explicit version, with all invariants
 */
void merge(int[] A, int lower, int mid, int upper)
//@requires 0 <= lower && lower < mid && mid < upper && upper <= \length(A);
//@requires is_sorted(A, lower, mid) && is_sorted(A, mid, upper);
//@ensures is_sorted(A, lower, upper);
{ 
  int[] B = alloc_array(int, upper-lower);
  int i = lower; int j = mid; int k = 0;
  while (i < mid && j < upper)
    //@loop_invariant lower <= i && i <= mid;
    //@loop_invariant mid <= j && j <= upper;
    //@loop_invariant k == (i-lower)+(j-mid);
    //@loop_invariant is_sorted(B, 0, k);
    //@loop_invariant is_sorted(A, i, mid) && is_sorted(A, j, upper);
    /*@loop_invariant k == 0 || ((i == mid || B[k-1] <= A[i])
                              && (j == upper || B[k-1] <= A[j])); @*/
    {
      if (A[i] <= A[j]) {
	B[k] = A[i]; i++;
      } else {
	B[k] = A[j]; j++;
      }
      k++;
    }
  //@assert i == mid || j == upper;
  while (i < mid)
    //@loop_invariant lower <= i && i <= mid && k == (i-lower)+(j-mid);
    //@loop_invariant is_sorted(B, 0, k) && is_sorted(A, i, mid);
    //@loop_invariant k == 0 || i == mid || B[k-1] <= A[i];
    { B[k] = A[i]; i++; k++; }
  while (j < upper)
    //@loop_invariant mid <= j && j <= upper && k == (i-lower)+(j-mid); 
    //@loop_invariant is_sorted(B, 0, k) && is_sorted(A, j, upper);
    //@loop_invariant k == 0 || j == upper || B[k-1] <= A[j];
    { B[k] = A[j]; j++; k++; }
  //@assert k == upper-lower && is_sorted(B, 0, upper-lower);
  for (k = 0; k < upper-lower; k++)
    //@loop_invariant lower <= lower+k && lower+k <= upper;
    A[lower+k] = B[k];
}

void mergesort (int[] A, int lower, int upper)
//@requires 0 <= lower && lower <= upper && upper <= \length(A);
// modifies A;
//@ensures is_sorted(A, lower, upper);
{
  if (upper-lower <= 1) return;
  else {
    int mid = lower + (upper-lower)/2;
    mergesort(A, lower, mid); //@assert is_sorted(A, lower, mid);
    mergesort(A, mid, upper); //@assert is_sorted(A, mid, upper);
    merge(A, lower, mid, upper);
  }
}
