/* Mergesort, version with additional invariants
 * 15-122 Principles of Imperative Computation, Fall 2010
 * Written by: Frank Pfenning
 * Modified by: Jonathan Clark, Fall 2012
 */

/* 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);
  }
}
