(* Inverse of a permutation, in place

     Algorithm I
     The Art of Computer Programming, volume 1, Sec. 1.3.3, page 176

   The idea is to inverse each orbit at a time, using negative values to
   denote elements already inversed. The main loop scans the array and
   proceeds as follows: a negative value -x-1 is simply changed into x; a
   nonnegative value is the topmost element of a new orbit, which is
   inversed by the inner loop.

   Authors: Martin Clochard (École Normale Supérieure)
            Jean-Christophe Filliâtre (CNRS)
            Andrei Paskevich (Université Paris Sud)
*)

use int.Int
use int.NumOf
use ref.Ref
use array.Array

let function (~_) (x: int) : int = -x-1

(* To prove the termination of the inner loop, we count the number of
   nonnegative values in the array, using the following shortcut. *)
function numof (m: int -> int) (l r: int) : int =
  NumOf.numof (fun n -> m n >= 0) l r

predicate is_permutation (a: array int) =
  forall i. 0 <= i < length a ->
    0 <= a[i] < length a /\
    forall j. 0 <= j < length a -> i <> j -> a[i] <> a[j]

(* In order to use the same predicate "loopinvariant" in both loops,
   we generalize its definition wrt the paper, using an extra argument m'.

   In the inner loop, this argument is m-1 and the definition coincides with
   that of the paper.

   In the outer loop, this argument is m, which means that properties 4 and 6
   hold up to m included (instead of up to m excluded). *)
predicate loopinvariant (olda a: array int) (m m' n: int) =
    (forall e. 0 <= e < n -> -n <= a[e] < n)
 /\ (forall e. m < e < n -> 0 <= a[e])
 /\ (forall e. m < e < n -> olda[a[e]] = e)
 /\ (forall e. 0 <= e <= m' -> a[e] >= 0 -> a[e] = olda[e])
 /\ (forall e. 0 <= e <= m -> a[e] <= m)
 /\ (forall e. 0 <= e <= m' -> a[e] < 0 ->
      olda[~a[e]] = e /\ (~a[e] <= m -> a[~a[e]] < 0))

let inverse_in_place (a: array int) : unit
  requires { is_permutation a }
  ensures  { is_permutation a }
  ensures  { forall i. 0 <= i < length a -> (old a)[a[i]] = i }
= let n = length a in
  for m = n - 1 downto 0 do
    invariant { loopinvariant (old a) a m m n }
    let ref i = a[m] in
    if i >= 0 then begin
      (* unrolled loop once *)
      a[m] <- -1;
      let ref j = (~m) in
      let ref k = i in
      i := a[i];
      while i >= 0 do
        invariant { a[k] = i <= m /\ 0 <= k <= m /\ ~m <= j < 0 /\
                    (old a)[~ j] = k /\ a[~ j] < 0 /\ a[m] < 0 }
        invariant { forall e. 0 <= e < m -> a[e] < 0 -> a[e] <> j }
        invariant { loopinvariant (old a) a m (m-1) n }
        variant   { numof a.elts 0 n }
        a[k] <- j;
        j := ~ k;
        k := i;
        i := a[k]
      done;
      assert { k = m };
      i := j
    end;
    assert { (old a)[~ i] = m };
    a[m] <- ~ i
  done


Generated by why3doc 1.3.3+git