/*
   CS:5810 Formal Methods in Software Engineering
   Fall 2023
   The University of Iowa
   
   Instructor: Cesare Tinelli
*/

///////////////////
// Binary search
///////////////////


predicate isSorted(a:array<int>)
  reads a
{
  forall i:nat, j:nat :: i <= j < a.Length ==> a[i] <= a[j]
}


// a[lo] <= a[lo+1] <= ... <= a[hi-2] <= a[hi-1]  
method binSearch(a:array<int>, K:int) returns (b:bool)
  requires isSorted(a)
  ensures b == exists i:nat :: i < a.Length && a[i] == K
{
	var lo: nat := 0 ;
	var hi: nat := a.Length ;
	while (lo < hi)
    decreases hi - lo
    invariant  0 <= lo <= hi <= a.Length
    //invariant forall j:nat :: j < lo ==> a[j] < K
    invariant forall i:nat :: (i < lo || hi <= i < a.Length) ==> a[i] != K
	{
		var mid: nat := (lo + hi) / 2 ;   assert  lo <= mid <= hi ;
		if (a[mid] < K) {                 assert  a[lo] <= a[mid]; 
                                      assert  a[mid] < K ;
			lo := mid + 1 ;                 assert              mid < lo <= hi;
		} else if (a[mid] > K) {          assert  K < a[mid];
			hi := mid ;                     assert              lo <= hi == mid;
		} else {
			return true ;                   assert a[mid] == K;
		}
	}
	return false ;                     
}

/* Note: the following definition of isSorted:

predicate isSorted(a:array<int>)
  reads a
{
  forall i:nat :: i < a.Length - 1 ==> a[i] <= a[i+1]
}

although equivalent to the one above is not enough for Dafny to be able 
to prove the invariants for the loop in binSearch.

The given one works because it *explicitly* states that every element 
of the input array is smaller than or equal to all later elements. 
This fact is implied by the alternative definition of isSorted given 
here (which only talks about array elements and their successors). 
However, it needs to be derived as an auxiliary lemma first, something 
that Dafny is not currently able to do automatically. 
*/

