module M

use int.Int
use ref.Ref

(* Tests for proof obligations. *)

val x : ref int

predicate q int

(* basic stuff: assignment, sequence and local variables *)

let p1 () requires { q (!x+1) } ensures { q !x } = x := !x + 1

let p2 () requires { q 7 } ensures { q !x } = x := 3 + 4

let p3 () ensures { !x = (old !x) + 3 } = x := !x + 1; x := !x + 2

let p4 () ensures { !x = 14 } = x := 7; x := 2 * !x

let p5 () ensures { result = 7 } = 3 + 4

let p6 () ensures { result = 7 } = let a = 3 in a + 4

let p7 () ensures { result = 11 } = 3 + (let a = 4 in a + a)

(* side effects in function arguments *)

let p8 ()
  requires { q (!x+1) }
  ensures { q !x /\ result = (old !x) + 4 }
  = 3 + begin x := !x + 1; !x end

(* evaluation order (argument first) *)

let p9 () ensures { result = 2 /\ !x = 2 }
  = begin x := 1; 1 end + begin x := 2; 1 end

let p9a () ensures { result = 2 /\ !x = 1 }
  = begin x := 1; 1 end + 1

(* function with a post-condition *)

val fsucc (x:int) : int ensures { result = x + 1 }

let p10 () ensures { result = 1 } = fsucc 0

let p11 () ensures { result = 5 } = (fsucc 0) + (fsucc 3)

let p11a () ensures { result = 4 } = let a = (fsucc 1) in a + a

(* function with a post-condition and side-effects *)

val incrx () : unit writes {x} ensures { !x = (old !x) + 1 }

let p12 () requires { !x = 0 } ensures { !x = 1 } = incrx ()

let p13 () ensures { !x = (old !x) + 2 } = incrx (); incrx ()

let p13a () ensures { !x = (old !x) + 2 } = incrx (incrx ())

(* function with side-effects, result and post-condition *)

val incrx2 () : int writes {x} ensures { !x = old !x + 1 /\ result = !x }

let p14 () requires { !x = 0 } ensures { result = 1 } = incrx2 ()

end
