; Template for Proving Correctness of One-Loop M1 Programs
; [The template is filled in with the details for our program *g-program* of
; lecture06.pdf.]
; Problem: Define an M1 program to compute some result theta, when certain
; preconditions are met.
; (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 (n m)
(and (natp n)
(natp m)))
(defun theta (n m)
(* n m))
; (2) Write your algorithm. This will consist of a tail-recursive helper
; function and a wrapper, fn.
(defun helper (n m a)
(if (zp n)
a
(helper (- n 1) m (+ m a))))
(defun fn (n m) (helper n m 0))
; (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 n m)
(natp a))
(equal (helper n m a)
(+ a (theta n m)))))
(defthm fn-is-theta
(implies (ok-inputs n m)
(equal (fn n m)
(theta n m))))
; 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*
'((ICONST 0) ; 0
(ISTORE 2) ; 1 a = 0;
(ILOAD 0) ; 2 [loop:]
(IFEQ 10) ; 3 if x=0 then go to end;
(ILOAD 0) ; 4
(ICONST 1) ; 5
(ISUB) ; 6
(ISTORE 0) ; 7 x = x-1;
(ILOAD 1) ; 8
(ILOAD 2) ; 9
(IADD) ;10
(ISTORE 2) ;11 a = y+a;
(GOTO -10) ;12 go to loop
(ILOAD 2) ;13 [end:]
(HALT)) ;14 ``return'' a
)
; (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 (n)
(if (zp n)
(repeat 'TICK 3)
(ap (repeat 'TICK 11)
(loop-sched (- n 1)))))
(defun sched (n)
(ap (repeat 'TICK 2)
(loop-sched n)))
; (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 n m)
(natp a))
(equal (run (loop-sched n)
(make-state 2
(list n m a)
nil
*pi*))
(make-state 14
(list 0 m (helper n m a))
(push (helper n m a) nil)
*pi*))))
(in-theory (disable loop-sched))
(defthm program-is-fn
(implies (ok-inputs n m)
(equal (run (sched n)
(make-state 0
(list n m)
nil
*pi*))
(make-state 14
(list 0 m (fn n m))
(push (fn n m) 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 n m)
(equal (run (sched n)
(make-state 0
(list n m)
nil
*pi*))
(make-state 14
(list 0 m (theta n m))
(push (theta n m)
nil)
*pi*))))
; This corollary just shows we did what we set out to do:
(defthm total-correctness
(implies (and (natp n)
(natp m)
(equal sf (run (sched n)
(make-state 0
(list n m)
nil
*pi*))))
(and (equal (next-inst sf) '(HALT))
(equal (top (stack sf)) (* n m))))
:rule-classes nil)