; Correctness of an Even/Odd Program ; Problem: Define an M1 program to compute 1 if n ; is even (i.e., n mod2 is 0) and 0 if n is odd. ; Prove it correct. ; Design Plan: Use an auxiliary variable, bit, as ; a flag (valued 0 or 1, where 1 means true). ; We'll count n down by 1 and flip the bit each ; time. So if the bit starts at 1 and we flip it ; an even number of times it will end at 1. I.e., ; if we return the bit, we are answering the ; question "is n even?". ; (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) (natp n)) (defun theta (n) (if (equal (mod n 2) 0) 1 0)) ; (2) Write your algorithm. This will consist of ; a tail-recursive helper function and a wrapper, ; fn. (defun helper (n bit) (if (zp n) bit (helper (- n 1) (if (equal bit 0) 1 0)))) (defun fn (n) (helper n 1)) ; (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. (defthm helper-is-theta (implies (and (ok-inputs n) (or (equal bit 0) (equal bit 1))) (equal (helper n bit) (if (equal (theta n) 1) bit (if (equal bit 0) 1 0))))) (defthm fn-is-theta (implies (ok-inputs n) (equal (fn n) ; = (helper n 1) (theta n)))) ; 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 1) ; 0 (ISTORE 1) ; 1 bit = 1; ; loop: (ILOAD 0) ; 2 (IFEQ 12) ; 3 if n=0, goto (ILOAD 0) ; 4 (ICONST 1) ; 5 (ISUB) ; 6 (ISTORE 0) ; 7 n = n-1; (ILOAD 1) ; 8 (IFEQ 3) ; 9 if bit=0, goto (ICONST 0) ;10 (GOTO 2) ;11 ; put-1: (ICONST 1) ;12 (ISTORE 1) ;13 bit = (flip bit); (GOTO -12) ;14 goto loop ; end: (ILOAD 1) ;15 (HALT))) ;16 ``return'' bit; ; (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 bit) (if (zp n) (repeat 'TICK 3) (if (equal bit 0) (ap (repeat 'TICK 11) (loop-sched (- n 1) (if (equal bit 0) 1 0))) (ap (repeat 'TICK 12) (loop-sched (- n 1) (if (equal bit 0) 1 0)))))) (defun sched (n) (ap (repeat 'TICK 2) (loop-sched n 1))) (defun test (n) (let ((sf (run (sched n) (make-state 0 (list n) nil *pi*)))) (list (list :pc (pc sf) '--> (next-inst sf)) (list :locals (locals sf)) (list :stack (stack sf))))) ; (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) (or (equal bit 0) (equal bit 1))) (equal (run (loop-sched n bit) (make-state 2 (list n bit) nil *pi*)) (make-state 16 (list 0 (helper n bit)) (push (helper n bit) nil) *pi*)))) (in-theory (disable loop-sched)) (defthm program-is-fn (implies (ok-inputs n) (equal (run (sched n) (make-state 0 (list n) nil *pi*)) (make-state 16 (list 0 (fn n)) (push (fn n) 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) (equal (run (sched n) (make-state 0 (list n) nil *pi*)) (make-state 16 (list 0 (theta n)) (push (theta n) nil) *pi*)))) ; This corollary just shows we did what we set out ; to do: (defthm total-correctness (implies (and (natp n) (equal sf (run (sched n) (make-state 0 (list n) nil *pi*)))) (and (equal (next-inst sf) '(HALT)) (equal (top (stack sf)) (if (equal (mod n 2) 0) 1 0)))) :rule-classes nil) ; Think of the above theorem as saying: for all ; natural numbers n, there exists a schedule (for ; example, the one constructed by (sched n)) such ; that running *pi* with (list n) as input ; produces a state, sf, that is halted and which ; contains 1 or 0 on top of the stack depending on ; whether n is even or odd. Note that the ; algorithm used by *pi* is not specified or ; derivable from this formula.