File: div.lisp

package info (click to toggle)
acl2 8.5dfsg-5
  • links: PTS
  • area: main
  • in suites: bookworm
  • size: 991,452 kB
  • sloc: lisp: 15,567,759; javascript: 22,820; cpp: 13,929; ansic: 12,092; perl: 7,150; java: 4,405; xml: 3,884; makefile: 3,507; sh: 3,187; ruby: 2,633; ml: 763; python: 746; yacc: 723; awk: 295; csh: 186; php: 171; lex: 154; tcl: 49; asm: 23; haskell: 17
file content (314 lines) | stat: -rw-r--r-- 11,414 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
; Correctness of Division

; Problem: Define an M1 program to compute the natural number quotient (i.e.,
; the floor) of n divided by d, where n and d are natural numbers and d is not
; 0.  Prove that your program is correct.  Note: In ACL2, the floor of n
; divided by d is (floor n d), e.g., (floor 27 6) = 4.

; Design Plan: I will count how many times I can take d away from n before n <
; d.  I will keep the counter in an auxilliary variable, a.  However, I can't
; test n < d directly with M1, so I have to implement that test with a loop.
; So my solution will be a program with nested loops.  To test n < d I'll move
; both n and d into two additional auxiliary variables, x and y, so I can count
; them down without destroying n and d.

; Because of the nested loops, this file deviates a little from our one-loop
; template, but I'll preserve the template's names for the main algorithm and
; its outer loop.

; (0) Start ACL2
; (include-book "m1")

(in-package "M1")

; (1) Write your specification, i.e., define the expected inputs and the
; desired output, theta.

(defun ok-inputs (n d)
  (and (natp n)
       (natp d)
       (not (equal d 0))))

(defun theta (n d)
  (floor n d))

; (2) Write your algorithm.  This will consist of a tail-recursive helper
; function and a wrapper, fn.

; Recall the lessp problem (see lessp.lisp).  We need that concept here.  It
; will be the ``helper's helper'' but we call it ``lessp'' instead.  Every part
; of this problem concerned with lessp is analogous to what we did in
; lessp.lisp, but because we don't have method invocation we have to
; ``re-verify'' the code.

(defun lessp (x y)
  (cond ((zp y) 0)
        ((zp x) 1)
        (t (lessp (- x 1) (- y 1)))))

; Here is the lemma that says the helper's helper does what we intended.

(defthm lessp-lemma
  (implies (and (natp x)
                (natp y))
           (equal (lessp x y)
                  (if (< x y) 1 0))))

; By the way, the theorem above ``ought'' to have been proved below when we
; prove the relations between the algorithms and the spec.  But we actually
; need to know (lessp x y) ``is'' (< x y) to know that the helper terminates.
; That's why we proved it ``early.''  It is not unusual in real projects to see
; the template ``mixed up'' like this.

; When you read the next defun, ignore rest for a moment!

(defun helper (n d a rest)
  (declare (ignore rest))
  (if (and (natp n)
           (natp d)
           (not (equal d 0)))
      (if (equal (lessp n d) 1)
          a
          (helper (- n d) d (+ 1 a) (list (- n d) 0)))
      'illegal))

; The role of rest in the definition of helper is very subtle.  Note that rest
; is an argument that we explicitly ignore and but go to the bother of
; specifying what its value is on each recursive call of helper.  We'll explain
; the role of rest in helper later.  For now, just keep reading, recognizing
; that we're following the template.

(defun fn (n d)
  (helper n d 0 nil))

; (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, a, and rest and fn calls helper initializing a to 0 and rest to nil,
; your helper theorem must be about (helper n m a rest), not just about the
; special case (helper n m 0 nil).

(defthm helper-is-theta
  (implies (and (natp n)
                (natp d)
                (not (equal d 0))
                (natp a))
           (equal (helper n d a rest)
                  (+ a (theta n d)))))

(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*

; We compute (div n d), where n and d are naturals and d is not
; zero.  We use the following locals:
; 0 n
; 1 d
; 2 a - answer
; 3 x - param for lessp
; 4 y - param for lessp
  '((iconst 0)  ;  0
    (istore 2)  ;  1   a = 0
    (iload 0)   ;  2   loop:
    (istore 3)  ;  3   x = n
    (iload 1)   ;  4
    (istore 4)  ;  5   y = d
; (invoke lessp on n d)....
    (iload 4)   ;  6   lessp-loop:          -- the code for lessp
    (ifeq 12)   ;  7   if y=0, goto false
    (iload 3)   ;  8
    (ifeq 12)   ;  9   if x=0, goto to true
    (iload 3)   ; 10
    (iconst 1)  ; 11
    (isub)      ; 12
    (istore 3)  ; 13   x = x-1
    (iload 4)   ; 14
    (iconst 1)  ; 15
    (isub)      ; 16
    (istore 4)  ; 17   y = y-1
    (goto -12)  ; 18   goto lessp-loop
    (iconst 0)  ; 19   lessp is false
    (goto 2)    ; 20
    (iconst 1)  ; 21   lessp is true
    (ifeq 3)    ; 22   if (n >= d) goto continue
    (iload 2)   ; 23
    (halt)      ; 24   return a
    (iload 0)   ; 25   continue:
    (iload 1)   ; 26
    (isub)      ; 27
    (istore 0)  ; 28   n = n-d
    (iload 2)   ; 29
    (iconst 1)  ; 30
    (iadd)      ; 31
    (istore 2)  ; 32   a = a+1
    (goto -31)  ; 33   goto loop
    ))

; Note: We can now foreshadow a little the role of rest in helper.  The outer
; loop, which helper models, starts at pc=2.  Upon the first arrival at pc=2,
; there are exactly 3 locals, n, d, and a.  But upon the next and all
; subsequent arrivals there are 5 locals: x and y were added when we invoked
; the inner loop.  The first three arguments of helper are the contents of the
; first three locals at the top of the outer loop.  The rest argument of helper
; is a list containing the rest of the locals.  Keep reading the template and
; we'll explain the role of rest in helper soon.

; (5) Define the ACL2 function that clocks your program, starting with the
; loop clock and then using it to clock the whole program.  The clock
; should take the program from pc 0 to a HALT statement.  (Sometimes your
; clocks will require multiple inputs or other locals, but our example only
; requires the first local.)

(defun inner-loop-clk (x y)
  (if (zp y)
      4
      (if (zp x)
          5
          (clk+ 13
                (inner-loop-clk (- x 1) (- y 1))))))

(defun outer-loop-clk (n d)
  (if (and (natp n)
           (natp d)
           (not (equal d 0)))
      (if (< n d)
          (clk+ 4
                (clk+ (inner-loop-clk n d)
                      2))
          (clk+ 4
                (clk+ (inner-loop-clk n d)
                      (clk+ 10
                            (outer-loop-clk (- n d) d)))))
      nil))

(defun clk (n d)
  (clk+ 2
        (outer-loop-clk n d)))

; (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 clock function so the theorem
; prover doesn't look any deeper into subsequent code.

; Important Note: Your lemmas about your loops must consider the general case.
; For example, if a 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 inner-loop-is-lessp
  (implies (and (natp x)
                (natp y))
           (equal (m1 (make-state 6
                                  (list n d a x y)
                                  nil
                                  *pi*)
                      (inner-loop-clk x y))
                  (make-state 22
                              (if (< x y)
                                  (list n d a 0 (- y x))
                                  (list n d a (- x y) 0))
                              (push (lessp x y) nil)
                              *pi*))))

(in-theory (disable inner-loop-clk))

; Note: Now we explain the role of rest in helper.  Note the locals of the
; initial state.  (List* n d a rest) is a list whose first three elements are
; n, d, and a, and whose remaining elements are those of rest.  The hypothesis
; stipulates that upon the arrivial at the top of the outer loop (pc = 2) rest
; is either nil or list containing exactly two more locals.

; Of course, this theorem is proved by induction.  The induction hypothesis is
; formed by replacing the variables below, n d, a, and rest, by new values as
; done by some recursive function in the formula.  Helper is actually telling
; the theorem prover what values to use for those variables -- including the
; value of rest when we've traversed the loop.

(defthm outer-loop-is-helper
  (implies (and (natp n)
                (natp d)
                (not (equal d 0))
                (natp a)
                (or (equal rest nil)
                    (equal (cdr (cdr rest)) nil)))
           (equal (m1 (make-state 2
                                  (list* n d a rest)
                                  nil
                                  *pi*)
                      (outer-loop-clk n d))
                  (make-state 24
                              (list (mod n d)
                                    d
                                    (helper n d a rest)
                                    0
                                    (- d (mod n d)))
                              (push (helper n d a rest) nil)
                              *pi*))))

(in-theory (disable outer-loop-clk))

(defthm program-is-fn
  (implies (ok-inputs n d)
           (equal (m1 (make-state 0
                                  (list n d)
                                  nil
                                  *pi*)
                      (clk n d))
                  (make-state 24
                              (list (mod n d) d (fn n d) 0 (- d (mod n d)))
                              (push (fn n d) nil)
                              *pi*))))

(in-theory (disable clk))

; (7) Put the two steps together to get correctness.

(in-theory (enable fn-is-theta))

(defthm program-correct
  (implies (ok-inputs n d)
           (equal (m1 (make-state 0
                                  (list n d)
                                  nil
                                  *pi*)
                      (clk n d))
                  (make-state 24
                              (list (mod n d) d (theta n d) 0 (- d (mod n d)))
                              (push (theta n d)
                                    nil)
                              *pi*))))

; This corollary just shows we did what we set out to do:

(defthm total-correctness
  (implies (and (natp n)
                (natp d)
                (not (equal d 0))
                (equal sf (m1 (make-state 0
                                          (list n d)
                                          nil
                                          *pi*)
                              (clk n d))))
           (and (equal (next-inst sf) '(HALT))
                (equal (top (stack sf)) (floor n d))))
  :rule-classes nil)

; Think of the above theorem as saying: for all natural numbers n and d, there
; exists a clock (for example, the one constructed by (clk n d)) such that
; running *pi* with (list n d) as input produces a state, sf, that is halted
; and which contains (floor n d) on top of the stack.  Note that the algorithm
; used by *pi* is not specified or derivable from this formula.