; Proof of the Program *pi* on the Midterm
; Problem: Define an M1 program to compute either (* 2 x) or (+ y (* 3 x)),
; depending on whether y is zero or not. Both x and y are naturals.
; (0) Start ACL2
; (include-book "m1-lemmas")
(in-package "M1")
; (1) Write your specification, i.e., define the expected inputs and the
; desired output, theta.
(defun ok-inputs (x y)
(and (natp x)
(natp y)))
(defun theta (x y)
(if (zp y)
(* 2 x)
(+ y (* 3 x))))
; (2) Write your algorithm. This will consist of a tail-recursive helper
; function and a wrapper, fn.
(defun helper (x y a)
(if (zp x)
a
(helper (- x 1) y (if (zp y) (+ 1 a) (+ 2 a)))))
(defun fn (x y) (helper x y (+ x y)))
; (3) Prove that the algorithm satisfies the spec, by proving first that the
; helper is appropriately related to theta and then that fn is theta on ok
; inputs.
; Important Note: When you verify your helper function, you must consider the
; most general case. For example, if helper is defined with formal parameters
; n, m, and a and fn calls helper initializing a to 0, your helper theorem must
; be about (helper n m a), not just about the special case (helper n m 0).
(defthm helper-is-theta
(implies (and (ok-inputs x y)
(natp a))
(equal (helper x y a)
(+ a (if (zp y) x (* 2 x))))))
(defthm fn-is-theta
(implies (ok-inputs x y)
(equal (fn x y)
(theta x y))))
; Disable these two lemmas because they confuse the theorem prover when it is
; dealing with the code versus fn.
(in-theory (disable helper-is-theta fn-is-theta))
; (4) Write your M1 program with the intention of implementing your algorithm.
(defconst *pi* '(
(iload 0) ; 0
(iload 1) ; 1
(iadd) ; 2
(istore 2) ; 3
(iload 0) ; 4
(ifeq 14) ; 5
(iload 0) ; 6
(iconst 1) ; 7
(isub) ; 8
(istore 0) ; 9
(iload 2) ; 10
(iload 1) ; 11
(ifeq 3) ; 12
(iconst 1) ; 13
(iadd) ; 14
(iconst 1) ; 15
(iadd) ; 16
(istore 2) ; 17
(goto -14) ; 18
(iload 2) ; 19
(halt) ; 20
))
; (5) Define the ACL2 function that schedules your program, starting with the
; loop schedule and then using it to schedule the whole program. The schedule
; should take the program from pc 0 to a HALT statement. (Sometimes your
; schedules will require multiple inputs or other locals, but our example only
; requires the first local.)
(defun loop-sched (x y)
(if (zp x)
(repeat 'tick 3)
(if (zp y)
(ap (repeat 'tick 13)
(loop-sched (- x 1) y))
(ap (repeat 'tick 15)
(loop-sched (- x 1) y)))))
(defun sched (x y)
(ap (repeat 'tick 4)
(loop-sched x y)))
; (6) Prove that the code implements your algorithm, starting with the lemma
; that the loop implements the helper. Each time you prove a lemma relating
; code to algorithm, disable the corresponding schedule function so the theorem
; prover doesn't look any deeper into subsequent code.
; Important Note: Your lemma about the loop must consider the general case.
; For example, if the loop uses the locals n, m, and a, you must characterize
; its behavior for arbitrary, legal n, m, and a, not just a special case (e.g.,
; where a is 0).
(defthm loop-is-helper
(implies (and (ok-inputs x y)
(natp a))
(equal (run (loop-sched x y)
(make-state 4
(list x y a)
nil
*pi*))
(make-state 20
(list 0 y (helper x y a))
(push (helper x y a) nil)
*pi*))))
(in-theory (disable loop-sched))
(defthm program-is-fn
(implies (ok-inputs x y)
(equal (run (sched x y)
(make-state 0
(list x y)
nil
*pi*))
(make-state 20
(list 0 y (fn x y))
(push (fn x y) nil)
*pi*))))
(in-theory (disable sched))
; (7) Put the two steps together to get correctness.
(in-theory (enable fn-is-theta))
(defthm program-correct
(implies (ok-inputs x y)
(equal (run (sched x y)
(make-state 0
(list x y)
nil
*pi*))
(make-state 20
(list 0 y (theta x y))
(push (theta x y)
nil)
*pi*))))
; This corollary just shows we did what we set out to do:
(defthm total-correctness
(implies (and (natp x)
(natp y)
(equal sf (run (sched x y)
(make-state 0
(list x y)
nil
*pi*))))
(and (equal (next-inst sf) '(HALT))
(equal (top (stack sf))
(if (zp y)
(* 2 x)
(+ y (* 3 x))))))
:rule-classes nil)
; Think of the above theorem as saying: for all natural numbers x and y, there
; exists a schedule (for example, the one constructed by (sched x y)) such that
; running *pi* with (list x y) as input produces a state, sf, that is halted
; and which contains either (* 2 x) or (+ y (* 3 x)) on top of the stack,
; depending on whether y is 0. Note that the algorithm used by *pi* is not
; specified or derivable from this formula.