/* VerifyThis 2017: CIVL Solution to Challenge 1 : Pair Insertion Sort
 * Stephen Siegel
 *
 * Modification by Ziqing Luo: 
 *  1. Got rid of the upper bound over n.
 *
 *  2. Manual side-effect removing.  The loop invariants transformer
 *  relies on the fact of side-effect free in loop conditions. It
 *  copies and moves the loop conditions hence side-effects can make
 *  it wrong.  Applying side-effect remover introduces jump statements
 *  which are not handled well by the loop invariants transformer.
 */
#include <stdio.h>
#include <string.h>

#pragma CIVL ACSL

$input int n; // size of array
$assume(1<=n);
$input int A[n];
$input int LEFT;
$input int RIGHT;
$assume(1<=LEFT && LEFT <= RIGHT && RIGHT<n);

int count(int n, int* p, int val) {
  int result = 0;
  for (int i=0; i < n; i++)
    if ((p[i]) == val)
      result++;
  return result;
}

int main() {
  // this is the "sentinel" assumption...
  $assume($forall (int j : LEFT .. RIGHT) A[j] >= A[LEFT-1]);

  int a[n];

  memcpy(a, A, n * sizeof(int));
  printf("n=%d\n", n);

  int left = LEFT, right = RIGHT;
  int k = left;
  
  left++;
  /*@ loop invariant 2 <= left && left <= right + 2;
    @ loop invariant k == left - 1;
    @ loop invariant LEFT <= k && k <= RIGHT+1 && k == left - 1;
    @ loop invariant \forall int t; LEFT <= t && t < k ==> a[t - 1] <= a[t];
    @ loop invariant \forall int t; LEFT <= t && t <= RIGHT ==> a[LEFT-1] <= a[t];
    @ loop assigns k, left, a[LEFT .. n-1];
    @*/
  for (; left <= right; k = left++) {
    int a1 = a[k], a2 = a[left];

    if (a1 < a2) {
      a2 = a1; a1 = a[left];
    }
    k--;
    /*@ loop invariant LEFT - 1 <= k && k < left - 1;
      @ loop invariant a1 < a[k] ==> k >= LEFT;
      @ loop invariant \forall int t; LEFT <= t && t <= k ==> a[t - 1] <= a[t];
      @ loop invariant \forall int t; k + 3 < t && t <= left ==> a[t - 1] <= a[t];
      @ loop invariant \forall int t; LEFT <= t && t <= RIGHT ==> a[LEFT-1] <= a[t];
      @ loop invariant k + 3 <= left ==> a[k + 3] >= a1 && a[k+3] >= a[k];
      @ loop assigns k, a[LEFT .. n-1];
      @*/
    while (a1 < a[k]) {
      a[k + 2] = a[k];
      k--;
    }
    a[++k + 1] = a1;
    k--;
    /*@ loop invariant LEFT - 1 <= k && k < left - 1;
      @ loop invariant a2 < a[k] ==> k >= LEFT;
      @ loop invariant \forall int t; LEFT <= t && t <= k ==> a[t - 1] <= a[t];
      @ loop invariant \forall int t; k + 2 < t && t <= left ==> a[t - 1] <= a[t];
      @ loop invariant \forall int t; LEFT <= t && t <= RIGHT ==> a[LEFT-1] <= a[t];
      @ loop invariant k + 2 <= left ==> a[k + 2] >= a2 && a[k + 2] >= a[k];
      @ loop assigns k, a[LEFT .. n-1];
      @*/
    while (a2 < a[k]) {
      printf("k=%d, LEFT=%d\n", k, LEFT);
      a[k + 1] = a[k];
      k--;
    }
    a[k + 1] = a2;
    left++;
  }

  $assert( $forall (int i : LEFT .. RIGHT-2) a[i] <= a[i+1] );
  
  int last = a[right--];
  
  /*@ loop invariant LEFT-1 <= right && right <= RIGHT-1;
    @ loop invariant right < RIGHT-1 ==>
    @                   (\forall int i; LEFT <= i && i <= RIGHT ==>
    @                     a[i-1] <= a[i]); 
    @ loop invariant right == RIGHT-1 ==> 
    @                 (\forall int i; LEFT <= i && i <= RIGHT-1 ==>
    @                     a[i-1] <= a[i]);
    @ loop invariant right < RIGHT-1 ==> last < a[right+1];
    @ loop assigns right, a[LEFT .. RIGHT];
    @*/
  while (last < a[right]) {
    a[right + 1] = a[right];
    right--;
  }
  a[right + 1] = last;
  $assert( $forall (int i : LEFT .. RIGHT-1) a[i] <= a[i+1] );
}
