theory a3 imports AutoCorres.AutoCorres "HOL-Number_Theory.Number_Theory" begin (* To run this file you need the AutoCorres tool used in the lecture. 1. Download AutoCorres from \url{http://www.cse.unsw.edu.au/~cs4161/autocorres-1.7.tar} 2. Unpack the .tar.gz file, which will create the directory autocorres-1.7 tar -xzf autocorres-1.7.tar.gz 3. Build the AutoCorres heap L4V_ARCH=X64 isabelle build -v -b -d autocorres-1.7 AutoCorres 4. Load this file using the AutoCorres heap L4V_ARCH=X64 isabelle jedit -d autocorres-1.7 -l AutoCorres a3.thy *) (*---------------------------------------------------------*) (*---------------------------------------------------------*) section \ C verification: doubly-linked list (40 marks) \ (* Installing the C file and converting it using the AutoCorress tool *) install_C_file "dlist.c" autocorres [unsigned_word_abs=is_prime_2, ts_force nondet=is_prime_2] "dlist.c" term "is_valid_node_C p" (* Definition of *path*: (path vld hp nxt p l q) says that you start from pointer p, and "follow" the function nxt, adding each encountered pointer to the list l, until you reach the pointer q, and that all encountered pointers are valid nodes according to the predicate vld, are not NULL, and not q: l=[ p , p1 , p2 , q ] \ \ \ \ []\[]\[]\[] nxt nxt nxt *) primrec path :: "(node_C ptr \ bool) \ \\ valid predicate \ (node_C ptr \ node_C) \ \\ the heap \ (node_C \ node_C ptr) \ \\ the function that maps to next element, i.e. the field of the node \ node_C ptr \ \\ the starting pointer \ node_C ptr list \ \\ the list of traversed pointers \ node_C ptr \ \\ the end pointer \ bool" where "path vld hp nxt p [] q = (p = q)" | "path vld hp nxt p (x#xs) q = (p \ q \ vld p \ p \ NULL \ p=x \ path vld hp nxt (nxt (hp p)) xs q)" (* Definition of *is_dlist*: (is_dlist vld hp p q xs) is True if we have a valid doubly-linked list starting from p and ending in q: xs=[ p , p1, p2, q ] \ \ \ \ NULL\[]\[]\[]\[]\ NULL *) definition "is_dlist vld hp p xs q \ path vld hp next_C p xs NULL \ path vld hp prev_C q (rev xs) NULL" (* (a) Start proof of insert_after_inserts *) (* TODO: start the proof in question (d) below, just to give you a feeling of what the goal looks like and why we need all the helper lemmas *) (* (b) Prove path_upd *) lemma path_upd: "x \ set xs \ path vld (hp (x := y)) n p xs q = path vld hp n p xs q" (*TODO*) sorry (* (c) Prove path_in, path_unique, path_appendD, path_hd_not_in_tl, path_append_last *) lemma path_in: "\path vld hp n p xs q; xs \ []\ \ p \ set xs" (*TODO*) sorry lemma path_unique: "\ path vld hp n p xs q; path vld hp n p ys q \ \ xs = ys" (*TODO*) sorry lemma path_appendD: "path vld hp n p (xs@ys) q \ \r. path vld hp n p xs r \ path vld hp n r ys q" (*TODO*) sorry lemma path_hd_not_in_tl: "path vld hp n (n (hp p)) xs q \ p \ set xs" (*TODO*) sorry lemma path_append_last: "path vld hp n p (xs@ys) q = (path vld hp n p xs (if ys = [] then q else hd ys) \ path vld hp n (if ys = [] then q else hd ys) ys q \ set xs \ set ys = {} \ q \ set xs)" (*TODO*) sorry (* (d) Finish proof of correctness insert_after_inserts *) definition insert_pre where "insert_pre p xs q nd s= (xs\[] \ is_dlist (is_valid_node_C s) (heap_node_C s) p xs q \ nd \ NULL \ is_valid_node_C s nd \ nd \ set xs )" definition insert_post where "insert_post p xs nd s= is_dlist (is_valid_node_C s) (heap_node_C s) p (xs @ [nd]) nd " context dlist begin lemma insert_after_inserts: "\\ s. insert_pre p xs q nd s\ insert_after' nd q \\_ s. insert_post p xs nd s\" (* TODO *) sorry end (*---------------------------------------------------------*) (*---------------------------------------------------------*) section "Primality modulo 2" context dlist begin thm is_prime_2'_def (* Q2 a): Loop invariant for "is_prime_2". *) definition is_prime_inv :: "nat \ nat \ bool" where "is_prime_inv i n \ undefined" (*TODO*) (* Q2 b): Measure function for "is_prime_2". Must be strictly decreasing * for each loop iteration. *) definition is_prime_measure :: "nat \ nat \ nat" where "is_prime_measure i n \ undefined" (*TODO*) (* Q2 c1): A prime is either 2 or an odd number *) lemma prime_2_or_odd: "prime n \ n = 2 \ n mod 2 = Suc 0" sorry (* TODO *) (* Q2 c2): The loop invariant holds coming into the loop. *) lemma is_prime_precond_implies_inv: "True" sorry (*TODO*) (* Q2 c3): The loop invariant holds for each loop iteration. *) lemma is_prime_body_obeys_inv: "True" sorry (*TODO*) (* Q2 c4): The loop measure decrements each loop iteration. *) lemma is_prime_body_obeys_measure: "\ is_prime_inv i n; n mod i \ 0 \ \ is_prime_measure i n > is_prime_measure (i + 2) n" sorry (*TODO*) (* Q2 c5): The loop invariant implies there is no overflow. *) lemma is_prime_body_implies_no_overflow: "\ is_prime_inv i n; n mod i \ 0\ \ i + 2 \ UINT_MAX" sorry (* Q2 c6): The loop invariant implies the post-condition. *) lemma is_prime_inv_implies_postcondition: "True" sorry (*TODO*) (* * Q2 d): Show that "is_prime' n" is correct. * * AutoCorres has applied "word abstraction" to this function, * meaning that you are able to reason using "nats" instead of * "word32" data types, at the price of having to reason that * your values don't overflow UINT_MAX. *) lemma is_prime_correct: "\\s. n \ UINT_MAX \ is_prime_2' n \\r s. r = (if prime n then 1 else 0) \!" (* Unfold the program body. *) apply (unfold is_prime_2'_def) (* Annotate the loop with an invariant and measure. *) apply (subst whileLoop_add_inv [where I="\i s. is_prime_inv i n" and M="\(i, s). is_prime_measure i n" ]) (* * Run "wp" to generate verification conditions. * * The resulting subgoals are: * 1. The loop body obeys the invariant, causes the measure to decrease, and does not lead to overflow; * 2. The invariant implies the post-condition of the function; and * 3. The pre-condition of the function implies the invariant. * *) apply wp sorry (*TODO*) end end