
module Fib

  use int.Int
  use int.Fibonacci

  let cfg fib (n: int) : int
    requires { n >= 0 }
    ensures  { result = fib n }
  = var x: int;
    var y: int;
    var i: int;
    {
      x <- 0;
      y <- 1;
      i <- 0;
      goto L1
    }
    L1 {
      invariant { 0 <= i <= n };
      invariant { x = fib i /\ y = fib (i + 1) };
      switch (i = n)
      | True  -> return x
      | False -> goto L2
      end
    }
    L2 {
      y <- x + y; x <- y - x; i <- i + 1;
      goto L1
    }
end

module Fact

  (* Factorial computation, following Nielson and Nielson's implementation
     in their book "Formal Methods -- An Appetizer" (Section 1.1, page 1) *)

  use int.Int, int.Fact

  let cfg fact (x0: int) : int
    requires { x0 >= 0 }
    ensures  { result = fact x0 }
  = var y: int;
    var x: int;
    {
      y <- 1;
      x <- x0;
      goto L1
    }
    L1 {
      invariant { 0 <= x <= x0 };
      invariant { fact x * y = fact x0 };
      switch (x <= 0)
      | True  -> return y
      | False -> goto L2
      end
    }
    L2 { assert { fact x = fact (x - 1) * x }; y <- x * y; x <- x - 1; goto L1 }
end
