module M

  use int.Int
  use ref.Ref

  (** Recursive functions *)

  (** 1. Pure function *)

  let rec f1 (x:int) : int variant { x }
    requires { x >= 0 } ensures { result = 0 }
    = if x > 0 then (f1 (x-1)) else x

  (** 2. With effects but no argument *)

  val x : ref int

  let rec f2 (_u:unit) : unit variant { !x }
    requires { !x >= 0 } ensures { !x = 0 }
    = if !x > 0 then begin x := !x - 1; f2 () end

  (** 3. With effects and a pure argument *)

  let rec f3 (a:int) : unit variant { a }
    requires { a >= 0 } ensures { !x = old !x + a }
    = if a > 0 then begin x := !x + 1; (f3 (a-1)) end

  (** 4. With effects and a reference as argument *)

  let rec f4 (a:ref int) : unit variant { !a }
    requires { !a >= 0 } ensures { !x = old !x + old !a }
    = if !a > 0 then begin x := !x + 1; a := !a - 1; f4 a end

  (** 5. The acid test:
         partial application of a recursive function with effects *)

  let rec f5 (a b:ref int) variant { !a }
    requires { !a >= 0 } ensures { result = old !a + old !b }
    = if !a = 0 then !b else begin a := !a - 1; b := !b + 1; f5 a b end

  let test_f5 () requires { !x >= 0 } ensures { result = old !x }
    = let b = ref 0 in let f = f5 x in f b

end
