The person charging this material is re- sponsible for its return to the library from which it was withdrawn on or before the Latest Date stamped below. Theft, mutilation, and underlining of books are reasons for disciplinary action and moy result in dismissal from the University. To renew call Telephone Center, 333-8400 UNIVERSITY OF ILLINOIS LIBRARY AT URBANA-CHAMPAIGN DEC 7 987 L161— O-1096 . Or Report No. UIUCDCS-R-81-1011 UILU-ENG 81 1717 June 1981 THE EVOLUTION OF PROGRAMS: PROGRAM ABSTRACTION AND INSTANTIATION by Nachum Dershowitz NSF-MCS-79-04897 DEPARTMENT OF COMPUTER SCIENCE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN URBANA, ILLINOIS ' THE EVOLUTION OF PROGRAMS: PROGRAM ABSTRACTION AND INSTANTIATION* Nachum Dershowitz Department of Computer Science University of Illinois Urbana, IL 61801 June 1981 *Research supported in part by the National Science Foundation under grant MCS-79-04897. ABSTRACT Our goal is to develop techniques for abstracting a given set of programs into a program schema and for instantiating a given schema to satisfy concrete specifications. Abstraction and instantiation are two important phases in software development that allow programmers to apply knowledge learned in the solution of past problems when faced with a new situation. For example, from two programs using the binary-search method, one to compute quo- tients and another to compute cube-roots, an abstract schema can be derived that embodies the shared method and that can be instantiated to solve similar new problems. We suggest the formulation of analogies as a basic tool in program abstraction. An analogy is first sought between the specifications of the given programs; this yields an abstract specification that may be instantiated to any of the given concrete specifications. The analogy is then used as a basis for transforming the existing programs into an abstract schema that represents the embedded technique, with the invariant assertions and correct- ness proofs of the given programs helping to verify and complete the analogy. A given concrete specification of a new problem may then be compared with the abstract specifications of the schema to suggest an instantiation of the sche- ma that yields a correct program. These methods appear to be amenable to im- plementation. A collection of program schemata is included in an appendix. Keywords: abstraction, instantiation, program schemata, analogy, transforma- tions, program manipulation, program development, program correctness CR Categories: 3.62, 4.43, 5.24 Digitized by the Internet Archive in 2013 http://archive.org/details/evolutionofprogr1011ders - 2 - I. INTRODUCTION Chaque verite que je trouvais etant une regie qui me servait apres a en trouver d'autres, non seulement je vins a bout plusieurs que j'avais jugees autrefois tres difficiles, mais il me sembla aussi, vers la fin, que je pouvais determiner, en celles meme que j'ignorais, par quels moyens, et jusques ou, il etait possible de les resoudre. - Rene Descartes, Discours de la Methode When confronted with a task, one often perceives some measure of resemblance between the given task and a previously accomplished one. In such a case, rather than "reinvent the wheel," one is likely to conserve effort by adapting the known solution of the old problem to the problem now at hand. After having solved several similar problems, one might come to formulate a general paradigm for solving such problems by highlighting the shared aspects of the individual instances and supressing their inconsequential or idiosyn- cratic particulars. The process of formulating a general scheme from concrete instances is termed abstraction ; that of applying an abstract scheme to a par- ticular problem is termed instantiation . Abstraction and instantiation are two phases in the evolutionary cycle of many typical programs, a cycle that, in addition, often includes the debugging of early versions, modifications to meet amended specifications, and extensions for expanded capabilities. The more experience a programmer has had, the more programming methods he is likely to have assimilated, and the more judiciously he can apply them to new problems. After having written several similar programs, a programmer is apt to formulate for himself — and - 3 - perhaps for others as well — an abstract notion of the underlying principle and reuse it in solving related problems. Program schemata are a convenient form for remembering such programming knowledge. A schema may embody basic programming techniques or specialized strategies for solving some class of problems; its input-output specifications are stated in terms of abstract predicate, function, and constant symbols. Our goal is to formalize this aspect of programming by developing automatable techniques for abstracting a given set of concrete programs into a program schema and for instantiating a schema to satisfy a given concrete specification. The programs are assumed to be annotated with an output specification (stating the desired relationship between the input and output variables upon termination of the program), an input specification (defining the set of legal inputs on which the program is intended to operate) , and invariant assertions (relations that are known to always hold at specific points in the program for the current values of variables) demonstrating its correctness. To date there has been a limited amount of research on program abstraction. The STRIPS system (Fikes, Hart, and Nilsson [1972]) generalized the loop-free robot plans that it generated; the HACKER system (Sussman [1975]) "8ubroutinized" and generalized the "blocks-world" plans it syn- thesized, executing the plan to determine which program constants could be abstracted. Dershowitz and Manna [1975] suggest using the proof of correct- ness of a program to guide the abstraction process. - A - A number of researchers have dealt with the use of program schemata. Dijkstra [1972] maintains that theorems about schemata are unconsciously invoked by the programmer. Wirth [1975] illustrates the use of basic schemata in the systematic development of programs. Gerhart [1975], Gerhart and Yelowitz [1976], and Yelowitz and Duncan [1977] have all advocated and illus- trated the use of schemata as a powerful programming tool. Other examples of the use of abstract algorithms as first steps in the development of programs are Darlington [1978], Deussen [1979], Duncan and Yelowitz [1979], and Lee, deRoever, and Gerhart [1979]. In a similar vein, Plaisted [1980] demonstrates how abstractions may be used, with great effectivity, for theorem proving. An approach to the specification and verification of program schemata is given in Misra [1978]. We suggest the formulation of analogies as a basic tool in program abstraction. First, an analogy is sought between the output specifications of the given programs. This yields an abstract output specification that may be instantiated to any of the given concrete specifications. The analogy is then used as a basis for transforming the existing programs into an abstract schema that represents the embedded technique. The invariant assertions and correct- ness proofs of the given programs are used to extend and complete the analogy. A schema, derived in this manner, is usually not applicable to all possible instantiations of its specifications. In that case, the schema Is accompanied by an input specification containing conditions that must be satisfied by the instantiation in order to guarantee correctness. These - 5 - preconditions may be derived from the verification conditions or correctness proof of the schema. The abstract specifications of the schema may then be compared with a given concrete specification of a new problem. By formulating an analogy between the two specifications, an instantiation is found that yields a concrete program when applied to the schema. If the instantiation satisfies the preconditions, then the correctness of the new program is guaranteed. If not, analysis of the unsatisfied conditions may suggest modif- ications that will lead to a correct program. The importance of analogical reasoning has been stressed by many, from Descartes to Polya. For a review of psychological theories of analogical reasoning see Sternberg [1977]. The use of analogy in automated problem solv- ing in general, and theorem proving in particular, was proposed by Kling [1971]. Other works employing analogy as an implement in problem solving include Brown [1976], Chen and Findler [1976], McDermott [1979], and Winston [1980]. Using analogies to guide the modification of programs has been inves- tigated by Manna and Waldinger [1975], Dershowitz and Manna [1977], Ulrich and Moll [1977], and Dershowitz [1978]. In the next section we present several illustrations of our approach. Some schemata that have appeared in the literature are collected In the Appendix. - 6 - II. EXAMPLES This section contains three detailed examples of program abstraction and instantiation. Each example begins with two concrete programs, annotated with their specifications and invariant assertions. Then, the programs are abstracted to obtain a more general program schema. Finally, each schema is instantiated to satisfy a third concrete specification. Example 1: Extremum Schema Consider the following program: P i : begin comment minimum value program assert nelN (z,i) := (A[0],0) loop assert zB [ j : X] , p.jeZ while j>k j :- j-l if ~l(B[p]>B[j]) then p := j fi repeat assert B[p]>B[k:£] end, where Z is the set of all integers. This program finds the position p of a maximal element in the array segment B[k:A]; its output specification is assert B[p]>B[k:A] . Its input specification assert k,£eZ, k<£ requires that the two input variables k and A be integers and that k not be greater than A so that the array segment B[k:£] is nonempty. Both programs perform a linear search for an extremum in an array segment. Our task is to extract an abstract version of these two programs that captures the essence of the technique used, but that is not specific to either problem. The resultant schema can then be used as a model of linear search for the solution of future problems. The first step in abstracting these two programs is to find an anal- ogy between their respective output specifications, zB [k: I] . The obvious analogy is that where the specification of Pj has 0, n, <, A, and z, the specification of Qi has k, I, >, B, and B[p], respectively. This anal- ogy is denoted by - 9 - «-► k n <— ► I < +-► > A -*-♦ B z *-*- B[p] . Now abstract entities may be substituted for analogous parts. Each pair in the analogy is replaced by an abstract variable of the same kind: the scalar constant in P^ and the corresponding scalar input variable k in Q^ may be replaced by an abstract input variable <; the corresponding input variables n and X may be replaced by \; the predicate constants < and > are abstracted to a predicate variable a; the input arrays A and B generalize to A; and the variable z and variable expression B[p] generalize to a program variable \l. (We shall use Greek letters to distinguish all abstract entities.) Applying the transformations -► K n -+ X < -»• a A -*> A z -* u to the output specification of Pj yields the abstract specification assert -* a B -* A p -► pos(A,u), where pos(A,|i) is a function that returns the position of some occurrence of [i in the array A, yields assert a(A[pos( A,u) ] ,A[<:\] ) when applied to the output specification of Qi . This simplifies to the same abstract specification assert a(u,A[<:\]), since by definition A[pos(A,u) ]=u. In general there are several possible ways in which an expression of the form f(q,r) can be compared with an expression h. If h is of the form g(s,t), then the imitating mapping f-*g, q— »-s, and r— *-t suggests itself. If f has an inverse f in its first argument, i.e. f(f (u,v),v)=u, then the invert- ing mapping q— »-f (h,r) would work. If f has an identity element f in its first argument, i.e. f(f ,v)=v, then the collapsing mapping q— »-f and r— ►h is possible. Another possibility is the projecting mapping f—*% and r— ►h, where % projects its first argument, i.e. ti(u,v) sb u. (Similar mappings work for other than the first argument.) For a discussion of a second-order pattern- matcher that uses imitating and projecting mappings and its application to program manipulation see Huet and Lang [1978]. In our example, to compare zB[k: I] , the imitating mapping O*— »-a, n«-+-b, and zB[u] is first used (eliminating the quantifiers [0:n] and [k:A]). To compare zB[u], another imi- tating mapping, viz. <■*—►>, z«— >B[p], and A[u]+->B[u], is used. An inverting mapping is needed, for example, to abstract B[p] into \i for Qj : since B is abstracted into A, we need to map A[p] into [i; since pos is an inverse of the - 11 - array access function, we get p— ►pos(A,u) . An example of a projecting mapping would be A— *it and p— *-[i. The next step in abstracting Pi and Qj is to apply the mappings to the corresponding programs. Applying the first set of transformations to P^ yields S i : begin comment tentative extremum schema suggest XeIN (u,i) := (A[<],<) loop suggest a(n,A[<:i]), ieIN until i=\ i := i+1 if A[i] a(A[i+l] ,A[<:i] ) For convenience, we shall replace this with (the more general condition) a(w,u) A ~|a(w,v) 3 a(v,u). Finally, we consider the verification condition for termination: \e!N D GuelN)K+u=\ (i.e. the exit test i=*\ will eventually hold for some value k+u of i) . Since K,\elN is assumed to hold (XeIN appears in the suggested input specification and keIN is a precondition), this termination condition is equivalent to < it a(v,u) -► odd(u)3f(v) f(u)0 (q,y) :- (0,1) loop assert ql, t>0 (r,w) :- (l,x) „ 1/3 1/3^ loop assert rt s := (w+r)/2 else w := s fl 3 if s l, within positive tolerance t. In comparing their respective output specifications, an obvious analogy is q «— ► r u/d «-* u c •*-► X e «— > t. If we apply the abstraction mapping q - I u/d -*■ p e -► e to P2» we obtain the schema - 21 - S2 : begin comment tentative binary-search schema suggest 00 (F,y) := (0,1) loop suggest !;<<(>( p), 4>(p)<£+y until y(p)|(p), (p) |t) . Applying the abstraction mapping r -*• I u ■* (u) x -»■ p t -* e to these subgoals yields purpose Z<$( p), (p)e). This is not however identical with the subgoals for P2; to make them equivalent requires extending the analogy with S+y «— ► w. If we let ti be their abstract counterpart, then y -* r\-Z must be added to the abstraction mapping of P2 and - 23 - w to the mapping for Q2 . There are, however, problems with applying the transformation y-*"n-£ to the statements of P2« If we apply it in a straightforward manner to the initialization assignment (E ,y) : = (0,1) , then we obtain (F,,t)-?;): = (0,1) . But the assignment n-£:=l is illegal, as an expression such as n-^ may not appear on the left-hand side of an assignment. Nevertheless, the desired effect of making the difference between the new values of r\ and E equal to 1 can be achieved by the legal assignment r|:=l+0, since is the new value of £. Simi- larly, the transformed loop-body assignment n-!;: =(n-£)/2 is illegal. To make it legal, the £ must be transposed to the right-hand side; the resulant assignment is n:=(Ti+^)/2 . We are not yet finished, however, as the value of the difference Tp£ also changes whenever £ is assigned to. Accordingly, we must look at the then-branch assignment £:=£+y, or rather at (5,y) :=(£+y ,y) , where we have explicitly included a dummy assignment to the variable y. Transforming this gives (r, ,ti-C): = (^+ti-^,ti-^). Thus, £ should get the value n and n should get the value rr? plus the new value of £, i.e. the old value of n. The appropri- ate legal assignment is accordingly (£,n) : = (Ti,2*n-£). At this stage, the abstracted program is - 24 - begin comment tentative binary-search schema suggest 00 a,n) :- (0,1) loop suggest £<(p), (p) I ( p ) (p) 4>(v)(u)(v). Combined with the previous precondition, we have 4»(u)(v) , which holds, in particular, if <\> is the inverse of a monotonic function , i.e. if <()((p(u)) ! »u and u(u)<(v) • Putting everything together, the complete abstraction mapping for P2 is u/d -»• 4>(u) c -»• p e -*■ e dx u -► 4>(u) y -*> rrl -+ o 1 -► 1 and we have derived the schema - 27 - begin comment binary-search schema assert (Ku)(v) > o<(p)0 (E,n) := (0,1) loop assert £<(p), (p)(p)|(v) » 0<4>(p)0 (£>n) := (o,\-o) loop assert £<<|>(p), (p ) | at the point p within a tolerance e. We show now how this binary-search schema may be applied to the com- putation of integer square-roots. Our goal is to construct a program that sets the value of a variable z to L^nJ , where nelN and (_uj is the largest integer less than or equal to u. This attempt will illustrate some of the - 28 - difficulties that may arise in trying to apply a schema. In this case, we cannot directly match our goal achieve z=j_/nj varying z with the output specification of the schema assert |e-<(>(p) | we can compare these invariants with our goal* This suggests the instantia- tion mapping S - z -* /"" p -* n e -► 1 to achieve the first two conjuncts of the goal. In addition we will have to somehow achieve zeZ. The preconditions for the schema's correctness are assert 4>(u)(v) , o<(u)0, the first condition may be satis- 2 fied by taking 4>(u) to be u , provided that the argument u is never negative. Noting that /n>0 suggests letting o=0; /n -* r P -*■ n veQ. Now, we must consider the verification conditions. Applying the transformations to the initialization condition of P3 we get T(6( X ),i)-e(x) on the other hand, applying the transformations to the initialization condi- tion of Q3 gives t(0(x),O)=0( X ) XeQ. - 35 - The shared condition xeQ becomes a precondition for the schema. To unify the remaining conditions of the two programs we add to the analogy 1 -*• a) +- 0, to obtain the abstract condition T(e(x),w)-e veQ until v»y (C,v) :- (T(a(v),C),6(v)) repeat assert C"9(x) end. In this manner we have obtained a general schema for computing a function 9(x). It applies to recursive functions 9(x) such that 9(y)™w is a unit of an associative and commutative function x, and 9(u)°t(9(6(u) ) ,o(u) ) - 38 - when i#y The schema is similar to one of the recursion-to-iteration transformations of Darlington and Burstall [1978]. To see how this schema may be applied to another problem, consider the specifications R3 : begin comment list-reversal program assert lei achieve z=reverse(J? ) varying z end, where L is a set of lists and reverse(X) is the list containing the elements of I in reverse order. Assume that we are also given two relevant facts about reverse: reverse( ())=■() , where () is the empty list, and reverse(u)=rever8e(tail(u) )*(head(u) ) when u*(), where u*v concatenates the two lists u and v, (head(u)) is a one element list containing the first ele- ment of u, and tail(u) is a list of all but the first element. An initial comparison of the schema's output specification C = 9(x) with the new specification z=reverse(A) suggests the instantiation 9 -+■ reverse C -*• z X - *• Instantiating the precondition v#Y D T(9(v),u)-T(e(6(v)),T(o(v),u)) gives v*Y 3 x(reverse(v) ,u) s »T(reverse(6(v)),x(a(v) ,u) ). By the second of the above two facts, we have that reverse(v) may be replaced by reverse(tail(v))*(head(v)), provided that v is not the empty list (). This - 39 - suggests the possibility of instantiating y— ► () to obtain v* ( ) D t(reverse( tail( v) )*(head(v) ) ,u)=-t(reverse(6 (v) ) ,t(o(v) ,u) ) . The function reverse appears on the two sides of the equality, so we try to generalize this condition by replacing both occurrences of reverse with an arbitrary list w. (This is similar to the generalization technique used in the theorem prover described in Boyer and Moore [1980].) To do that, we must first unify reverse(6(v)) with reverse(tail(v) ) by instantiating 6— »-tail. We are left with v*() D x(w*(head(v)),u)=T(w,T(a(v) ,u)). Similarly, we unify a(v) with (head(v)), the list containing just the first element of v, obtaining v*() Z) x(w*v,u)=t(w,t(v,u)). This matches with the fact that * is associative, i.e. (w*v)*u=w*(v*u) , by instantiating t-*-*. Applying the instantiations that we have found to the other five preconditions t(u,w)=u t(6(y),u)=u veQ A v*y D Mv)eQ (3ueIN)6 U (x)=Y yields - 40 - U*0)=*U reverse( ())*u=u veQ A v*() D tail(v)eQ (3uelN)tail U (JD=(). But reverse( ())=() and ()*u=u, since the empty list () is an identity element of the function *. Thus, the second condition holds. The first suggests let- ting ar-^O; the third and fourth suggest letting Q-*L. The last condition then follows from the third, as a property of lists. The completed instantiation is -*■ reverse C -► z X -* r Y -► () 6 -► tail a(u) -» (head(u)) T -► * (d - (). In all, we have derived the following program R-3 : begin comment list reversal program assert £eL (z,v) :- ((),*) loop assert reverse(v )*z=»reverse(A) , veL until v*() (z,v) := ((head(v))*z,tail(v)) repeat assert z=reverse(A) end. - 41 - In this example most of the instantiation mapping was derived from an analysis of the preconditions. The preconditions served, In this way, to guide the construction of the list-reversal program in the pattern of other recursive functions. - 42 - III . DISCUSSION We have presented several examples demonstrating a methodology for deriving an abstract program schema that captures the technique underlying a given set of concrete programs. Once derived, the schema may be applied to solve new problems by instantiating the abstract entitites of the schema with concrete elements from the problem domain. We have also seen how this metho- dology can be used to derive correctness-preserving program transformations and to guide their application. Abstraction and instantiation complement techniques of program transformation, such as have been advocated by Knuth [1974]. When faced with the task of developing a new program (or subprogram) to meet a set of specifi- cations, the programmer ought to first search for an applicable schema. After instantiating the schema, transformations may be applied to solve any remain- ing specifications or to increase efficiency. If no applicable schema can be found, one might still be able to find a schema or program solving some analo- gous problem, and modify it (see, for example, Dershowitz and Manna [1977]). The two programs together would then be used to formulate a schema for future use. There are a few problems inherent in the use of analogies for pro- gram abstraction and instantiation. These include "hidden" analogies, "misleading" analogies, "incomplete" analogies, and "overzealous" analogies. Hidden analogies arise when given specifications (of the two or more existing programs in the case of abstraction, and of the abstract schema and concrete problem in the case of instantiation) that are to be compared with one another have little syntactically in common. Since the pattern-matching ideas that we have employed are syntax based, when the specifications are not syntactically similar, the underlying analogy would be hidden. In such a situation it is necessary to rephrase the specifications in some equivalent manner that brings their similarity out, before an analogy can be found. This is clearly a difficult problem in its own right; in general some form of means-end analysis seems appropriate. - 43 - At the opposite extreme, a syntactic analogy may be misleading. The same symbol may appear in the specifications of two programs, yet may play nonanalogous roles in the two programs. Two programs might even have the exact same specifications, but employ totally different methods of solution. Situations such as these would be detected in the course of analyzing the correctness conditions for the abstracted programs. We have seen how the proof of correctness of a program can be used to help avoid overzealously applying transformations to unrelated parts of a program. The proof also helps complete an analogy between two programs, only part of which was found by a comparison of specifications. The methods we have described appear to be amenable to automation. The necessary reasoning ability is the same as is needed for a program- verification system; the program manipulation abilities are similar to what is required of program-transformation systems. Of course, we do not expect these methods alone to suffice for an automatic program-abstraction system to pro- duce and apply program schemata. But we can envision the possibility of such methods being embedded in a semi-automatic program-development environment in which the system performs the more straightforward steps, and the human pro- grammer guides the machine in the more creative ones. - 44 - ACKNOWLEDGMENT I sincerely thank Zohar Manna and Richard Waldinger for their gui- dance in initiating this research. - 45 - APPENDIX Gerhart [1975] recommended the hand-compilation of a handbook of program schemata. Such a collection of shemata, together with a library of program transformations, could be used in an interactive program-development system. In this appendix, we have culled fifteen representative schemata from the programming literature. We use the following nomenclature: IN set of natural numbers 3R set of real numbers Z set of integers [u:v] set of integers between u and v j,k,t input variable x input variable or vector of variables p,q predicate symbol c,d,e,f,g,h function symbol a,b constant symbol z,r output variable i,s,y,m program variable u,v,w universally quantified variable (quantifier often omitted) n existentially quantified variable Each of the following schemata is followed by an output assertion giving its abstract Input-output specification. They are preceded by an input assertion containing the preconditions for correct application. The general format is - 46 - S : begin comment title (source) purpose assert type conditions, correctness preconditions, termination precondition • • • schema body • • • assert output specification end. The references are to sources that present an abstract schema; they are some- what arbitrary, as the methods themselves are all well known. (The schemata may differ in details from those given in the referenced sources.) S i : begin comment element-by-element (Gerhart [1975]) achieve q for all integers between j and k assert jeZ, kelR, bex, ue[j:k]Avex 2) h(u,v)ex, ue[j:k]Avex 3 q(h(u,v),u), u,we[ j:k]AvexAuk+1, zex until i>k (z,i) := (h(i,z),i+l) repeat assert ( Vue[ j:k] )q(z ,u) , zex end - 47 - S 2 : begin comment extremum (Dershowitz and Manna [1975]) achieve q for all Integers between j and k assert jeZ, kelR, ue[j:k] D q(u,u), u,v,we[ j:k]Auk until i>k-l i := 1+1 if ~|q(z,i) then z := i fi repeat assert (Vue[ j:k] )q(z ,u) , ze[j:k]Vj>k end S3 : begin comment linear-search (Dijkstra [1972]) find least integer between j and k such that q holds assert jeZ , kelR z := j loop assert ( Vue[ j:z-l] )~lq(u) , ze[ j:k+l] Vz=j>k+1 until z>kVq(z) z := z+1 repeat assert (3ne[ j:k] )q(n) Z) z=(min ne[ j :k] )q(n) , z>j, z>kVq(z) end - 48 - Si*: begin comment gradient-search (Misra [1978]) search for local extremum assert aex, uex 2) t(u) Lx, uex 3 q(u,u), u,v,wexAq(w,u)A~lq(w,v) D q(v,u), uLx 3 g(u)eu, u,v,wexAq(u,w) 3 q(u,v)Vq(v,w), (3nelN)|x|=n comment |x| denotes the number of elements In the set x z := a loop (m, s) := (z,t(z)) loop assert ( Vuet(m)-s)q(z ,u) , z,mex, sl_x until s={} y :■ g(s) s := s-{y} if ~" lq(z,y) then z :■ y fi repeat assert (Vuet(m))q(z,u) , zex until z=m repeat assert ( Vuet(z) )q(z ,u) , zex end - 49 - S5 : begin comment binary-search (Dershowitz and Manna [1975]) approximate transition from q true to q false assert a,b,telR, q(a), ~lq(b), q(u+t)At>v D q(u+v), t>0 (z,y) := (a,b-a) loop assert q(z), ~lq(z+y), ab until yb end - 50 - Sg: begin proc sort(x[j:k] ) comment sorting (Darlington [1978]) sort array segment x[j:k] assert x[ j:k]ea[ j:k] , uea[u':w'] 2) f(u)ea[u" :w' ]*[u' :w'-l] , uea[u' :v' ] Avea[v'+1: w'] Z) g(u,v)ea[u' :w'] , uea[u':v']Avea[v'+l:w'] 2) bag(g(u,v))=bag(u) Ubag(v) , uea[u':w']Af(u)=(w,v) 3 bag(u)=bag(w) , uea[u' :w'] Af (u) = (w[u' :w'] ,v) Asorted(w[u' :v] )Asorted(w[v+l:w' ] ) 2) sorted(g(w[u': v] ,w[v+l:w'])) comment bag(u)=bag(v) means that for each occurrence of an element in u there is an occurrence of the element in v; sorted(w[u' :w'] ) = ( Vve[u' :w'-l] )w[v] ~lp(u) D f(u)=h(f(d(u)),u), (3nelN)p(d n (x)) comment s is the list (s ,s ,, ,,, ,s, ); n n-1 1 head(s)=s ; tail(s)=(s ..•••,8 1 ); n n-1 1 uo s =(u > s n ,-«-,s 1 ) (y,s) := (x,()) loop assert f (x)=h(- • -h(h(f (y) ,s n ) »3 j^) • • • .s^ until p(y) (y,s) := (d(y),yo s ) repeat z := c(y) loop assert f(x)=h( ♦ • «h(h(z ,s ),s , )»««,s, ) n n— l l until s=() (z,s) := (h(z,head(s)),tail(s)) repeat assert z=f(x) end - 54 - » 12 : begin coament double-recursion (Knuth [1974]) compute f(x) assert p(u) Z) f(u)=c(u), lp(u) ^ f(u)-h(f(e(u)),f(d(u))), h(a,u)=u, h(u,h(v,w))=h(h(u,v),w), p(x)V(3nem)g n (x) = {}, where g(u)={e(v) IveuAlp(v)} U{d(v) IveuAlp(v)} come nt s Is the head(s)=s ; n list \ tail(s n n — 1 ')=(s n _ r -. •,s 1 ); -,8^5 (z,s) uos=(u,s , • n :- (a,(x)) ",8^ loop assert f(x) : »h(z,h( ;s 1 ,h(s 2> h( •"' h( Vr V until s=() (y,s) := (head(s), tail(s)) if p(y) then z := h(z,c (y)) else s :■ e(y)c d(y)os fl repeat assert z=f(x) end )))) - 55 - s 13 : begin proc back(t,x,z) consent backtracking (Gerhart and Yelowitz [1976]) collect all vectors beginning with x satisfying p assert xea, 2Q, uea 3 t( u ) La, uLa 2) g(u)eu, (3nelN)|t*({x})|=n comment t*(u)=u|_ft (u) Ut~ 2 (u) U- • • , where T(u) = U t(v); veu I* * t (u) I denotes the number of elements in the set t (u); z denotes the value of z upon procedure entry if p(x) then z := zU{x} fi y := t(x) loop assert zU{uet (x)|p(u)} =zU{uet*(y)|p(u)} until y={ } m := g(y) y := y-{m} back(t ,m,z) repeat assert z=z|_l{uet (x)|p(u)} end - 56 -