This file contains the processed and commented output of HOL4 when run on the two-way-sort example. The unprocessed output can be found here. The original output was produced by holfoot-full -f twowaysort.hol.
First HOL4 processes the setup of the framework. Then an auxiliary lemma about sorted lists is proved.
val SORTED_EL_DEF = |- SORTED R L ⇔ (L = []) ∨ ∀n. n < LENGTH L − 1 ⇒ R (EL n L) (EL (SUC n) L): thm
After printing the input file, the specialiased tactics to verify the procedures are defined.
val swap_TAC = fn: tactic val two_way_sort_TAC = fn: tactic
Finally the input file twowaysort.dsf is processed using these tactics.
parsing ... 0.051 s - OK HOLFOOT_SPECIFICATION ( swap(; a_const, i_const, j_const) [w/r: ; | data_array(#a_const, #m, dta:data) ★ (#i_const < #m ) ★ (#j_const < #m )] { local (a = a_const), (i = i_const), (j = j_const), ti, tj; ti = (a + i)→dta; tj = (a + j)→dta; (a + j)→dta = ti; (a + i)→dta = tj; } [w/r: ; | data_array(#a_const, #m, dta:SWAP_ELEMENTS j_const i_const data)] two_way_sort(; a_const, m_const) [w/r: ; | data_array(#a_const, #m_const, dta:data) ★ (#m_const > 0) ★ pure(EVERY IS_BOOL_TO_NUM data)] { local (a = a_const), (m = m_const), i, j, temp; i = 0; j = (m − 1); /* Loop Invariant: [w/r: i, j, temp; a, m | data_array(a, m, dta:_rdata) ★ (j < m) ★ pure(PERM !data _rdata ∧ EVERY IS_BOOL_TO_NUM !data ∧ (∀n. n < i ⇒ (EL n _rdata = 0)) ∧ ∀n. j < n ∧ n < m ⇒ (EL n _rdata = 1))] */ while (i ≤ j) { temp = (a + i)→dta; if (temp = 0) { i = (i + 1) } else { temp = (a + j)→dta; if (temp = 1) { j = (j − 1) } else { swap(; a, i, j); i = (i + 1); j = (j − 1); }; }; }; } [w/r: ; | data_array(#a_const, #m_const, dta:_rdata) ★ pure(SORTED $<= _rdata ∧ PERM data _rdata)] ) preprocessing ... 0.068 s - OK verifying specification ... * swap ... 0.301 s - OK * two_way_sort ... 2.469 s - OK done time needed: 3.513 s
This processing proves a HOL4 theorem that states that the specification of the program is indeed correct. The pretty-printed version of this HOL4-theorem looks very similar. Here the theorem is presented next to the input file twowaysort.dsf.
val final_thm = |- HOLFOOT_SPECIFICATION ( swap(; a_const, i_const, j_const) [w/r: ; | data_array(#a_const, #m, dta:data) ★ (#i_const < #m ) ★ (#j_const < #m )] { local (a = a_const), (i = i_const), (j = j_const), ti, tj; ti = (a + i)→dta; tj = (a + j)→dta; (a + j)→dta = ti; (a + i)→dta = tj; } [w/r: ; | data_array(#a_const, #m, dta:SWAP_ELEMENTS j_const i_const data)] two_way_sort(; a_const, m_const) [w/r: ; | data_array(#a_const, #m_const, dta:data) ★ (#m_const > 0) ★ pure(EVERY IS_BOOL_TO_NUM data)] { local (a = a_const), (m = m_const), i, j, temp; i = 0; j = (m − 1); /* Loop Invariant: [w/r: i, j, temp; a, m | data_array(a, m, dta:_rdata) ★ (j < m) ★ pure(PERM !data _rdata ∧ EVERY IS_BOOL_TO_NUM !data ∧ (∀n. n < i ⇒ (EL n _rdata = 0)) ∧ ∀n. j < n ∧ n < m ⇒ (EL n _rdata = 1))] */ while (i ≤ j) { temp = (a + i)→dta; if (temp = 0) { i = (i + 1) } else { temp = (a + j)→dta; if (temp = 1) { j = (j − 1) } else { swap(; a, i, j); i = (i + 1); j = (j − 1); }; }; }; } [w/r: ; | data_array(#a_const, #m_const, dta:_rdata) ★ pure(SORTED $<= _rdata ∧ PERM data _rdata)] ): thm |
/* swap gets 3 call by value arguments: "a", "i", and "j". The precondition states that there is an array in memory starting at location "a", with size of constant "m". Moreover, the content "data" of the array is specified explicitly. The indices "i" and "j" are smaller than "m" and therefore the array accesses are within the bound. */ swap (a,i,j) [data_array(a,#m,data) * i < #m * j < #m]{ local ti, tj; /* Holfoot supports only very simple operations. There is no special expressions or commands for arrays. Arrays are accessed using explicit pointer arithmetic. For example, "(a+i) -> dta" means adding "i" to the value of "a" and looking up the value stored in memory at this position indexed by the tag "dta". More traditionally, it is written as "a[i]". Moreover, memory lookups can not be used in expressions directly. They have to be stored in a local variable first. Therefore, the code becomes: */ ti = (a + i) -> dta; /* ti <- a[i] */ tj = (a + j) -> dta; /* tj <- a[j] */ (a + j) -> dta = ti; /* a[j] <- ti */ (a + i) -> dta = tj; /* a[i] <- tj */ /* The post-condition states that there is still an array of the original size at location m. However, the data content changed. It now contains the original list with the elements at positions "i" and "j" swapped. The quotation mark denote a HOL4-term. SWAP_ELEMENTS is a HOL4 function. */ } [data_array(a,#m,``SWAP_ELEMENTS j i data``)] /* Holfoot does not support typing. All values are natural numbers. Therefore, Boolean values are mapped to numbers (True => 1, False => 0). The function "two_way_sort" gets two call-by-value arguments "a" and "m". "a" is the location of an array containing some data "data". Because the size is not stored in the array directly, the argument "m" is used to pass the size of the array. The condition "m > 0" therefore demands that the array is not empty. Moreover the pure condition ``EVERY IS_BOOL_TO_NUM data`` demands that only Boolean values (i.e. only 0 and 1) are stored in the array. */ two_way_sort (a, m) [data_array (a, m, data) * m > 0 * ``EVERY IS_BOOL_TO_NUM data``] { local i, j, temp; i = 0; j = m - 1; while (i <= j) /* The loop invariant says that there is always an array of the right size at "a". This array contains some existentially quantified data "_rdata". (Following Smallfoot, the underscore is used to denote existential quantification). Moreover, the upper index "j" is always smaller than the size of the array. There is a pure side-condition as well. It demands that "_rdata" is a permutation of "data" and that "data" contains only Boolean values. Moreover all array entries below "i" are 0, all above "j" are 1. Notice that the termination of the loop is not proven. */ [data_array (a, m, _rdata) * j < m * ``(PERM data _rdata) /\ EVERY IS_BOOL_TO_NUM data /\ (!n. n < i ==> (EL n _rdata = 0)) /\ (!n. (j < n /\ n < m) ==> (EL n _rdata = 1))``] { temp = (a + i) -> dta; if (temp == 0) { i = i+1; } else { temp = (a + j) -> dta; if (temp == 1) { j = j-1; } else { swap(a, i, j); i = i+1; j = j-1; } } } /* The post-condition guarantees that there is still the array with size "m" at "a". However, it now contains some existentially quantified data "_rdata" such that "_rdata" is a permutation of the original data "data" and it is sorted according to the "<=" relation. Holfoot uses fault avoiding semantics. Therefore, it is implicitly guaranteed that all memory accesses were within the boundaries of the array. Moreover it is guaranteed that there is no memory leak, i.e. that no junk is left on the heap. */ } [data_array (a, m, _rdata) * ``(SORTED $<= _rdata) /\ (PERM data _rdata)``] |