]> matita.cs.unibo.it Git - helm.git/blob - weblib/tutorial/chapter1.ma
commit by user andrea
[helm.git] / weblib / tutorial / chapter1.ma
1 (*
2 This is an interactive tutorial. To let you interact on line with the system, 
3 you must first of all register yourself.
4
5 Before starting, let us briefly explain the meaning of the menu buttons. 
6 With the Advance and Retract buttons you respectively perform and undo single 
7 computational steps. Each step consists in reading a user command, and processing
8 it. The part of the user input file (called script) already executed by the 
9 system will be colored, and will not be editable any more. The advance bottom 
10 will also automatically advance the focus of the window, but you can inspect the 
11 whole file using the scroll bars, as usual. Comments are skipped.
12 Try to advance and retract a few steps, to get the feeling of the system. You can 
13 also come back here by using the top button, that takes you at the beginning of 
14 a file. The play button is meant to process the script up to a position 
15 previously selected by the user; the bottom button execute the whole script. 
16 That's it: we are\ 5span style="font-family: Verdana,sans-serif;"\ 6 \ 5/span\ 6now ready to start.
17
18 The first thing to say is that in a system like Matita's very few things are 
19 built-in: not even booleans or logical connectives. The main philosophy of the 
20 system is to let you define your own data-types and functions using a powerful 
21 computational mechanism based on the declaration of inductive types. 
22
23 Let us start this tutorial with a simple example based on the following well 
24 known problem.
25
26 \ 5h2 class="section"\ 6The goat, the wolf and the cabbage\ 5/h2\ 6
27 A farmer need to transfer a goat, a wolf and a cabbage across a river, but there
28 is only one place available on his boat. Furthermore, the goat will eat the 
29 cabbage if they are left alone on the same bank, and similarly the wolf will eat
30 the goat. The problem consists in bringing all three items safely across the 
31 river. 
32
33 Our first data type defines the two banks of the river, which will be named east
34 and west. It is a simple example of enumerated type, defined by explicitly 
35 declaring all its elements. The type itself is called "bank".
36 Before giving its definition we "include" the file "logic.ma" that contains a 
37 few preliminary notions not worth discussing for the moment.
38 *)
39
40 include "basics/logic.ma".
41
42 inductive bank: Type[0] ≝
43 | east : bank 
44 | west : bank.
45
46 (* We can now define a simple function computing, for each bank of the river, the
47 opposite one. *)
48
49 definition opposite ≝ λs.
50 match s with
51   [ east ⇒ \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6
52   | west ⇒ \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6
53   ].
54
55 (* Functions are live entities, and can be actually computed. To check this, let
56 us state the property that the opposite bank of east is west; every lemma needs a 
57 name for further reference, and we call it "east_to_west". *)
58  
59 lemma east_to_west : \ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a title="leibnitz's equality" href="cic:/fakeuri.def(1)"\ 6=\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6.
60
61 (* If you stop the execution here you will see a new window on the  right side
62 of the screen: it is the goal window, providing a sequent like representation of
63 the form
64
65 B1
66 B2
67 ....
68 Bk
69 -----------------------
70 A
71
72 for each open goal remaining to be solved. A is the conclusion of the goal and 
73 B1, ..., Bk is its context, that is the set of current hypothesis and type 
74 declarations. In this case, we have only one goal, and its context is empty. 
75 The proof proceeds by typing commands to the system. In this case, we
76 want to evaluate the function, that can be done by invoking the  "normalize"
77 command:
78 *)
79
80 normalize
81
82 (* By executing it - just type the advance command - you will see that the goal
83 has changed to west = west, by reducing the subexpression (opposite east). 
84 You may use the retract bottom to undo the step, if you wish. 
85
86 The new goal west = west is trivial: it is just a consequence of reflexivity.
87 Such trivial steps can be just closed in Matita by typing a double slash. 
88 We complete the proof by the qed command, that instructs the system to store the
89 lemma performing some book-keeping operations. 
90 *)
91
92 // qed.
93
94 (* In exactly the same way, we can prove that the opposite side of west is east.
95 In this case, we avoid the unnecessary simplification step: // will take care of 
96 it. *) 
97
98 lemma west_to_east : \ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a title="leibnitz's equality" href="cic:/fakeuri.def(1)"\ 6=\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6.
99 // qed.
100
101 (* A slightly more complex problem consists in proving that opposite is 
102 idempotent *)
103
104 lemma idempotent_opposite : ∀x. \ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 (\ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 x) \ 5a title="leibnitz's equality" href="cic:/fakeuri.def(1)"\ 6=\ 5/a\ 6 x.
105
106 (* we start the proof moving x from the conclusion into the context, that is a 
107 (backward) introduction step. Matita syntax for an introduction step is simply 
108 the sharp character followed by the name of the item to be moved into the 
109 context. This also allows us to rename the item, if needed: for instance if we 
110 wish to rename x into b (since it is a bank), we just type #b. *)
111
112 #b
113
114 (* See the effect of the execution in the goal window on the right: b has been 
115 added to the context (replacing x in the conclusion); moreover its implicit type 
116 "bank" has been made explicit. 
117
118 But how are we supposed to proceed, now? Simplification cannot help us, since b
119 is a variable: just try to call normalize and you will see that it has no effect.
120 The point is that we must proceed by cases according to the possible values of b,
121 namely east and west. To this aim, you must invoke the cases command, followed by
122 the name of the hypothesis (more generally, an arbitrary expression) that must be
123 the object of the case analysis (in our case, b).
124 *)
125
126 cases b
127
128 (* Executing the previous command has the effect of opening two subgoals, 
129 corresponding to the two cases b=east and b=west: you may switch from one to the 
130 other by using the hyperlinks over the top of the goal window. 
131 Both goals can be closed by trivial computations, so we may use // as usual.
132 If we had to treat each subgoal in a different way, we should focus on each of 
133 them in turn, in a way that will be described at the end of this section.
134 *)
135
136 // qed.
137
138 (* Instead of working with functions, it is sometimes convenient to work with
139 predicates. For instance, instead of defining a function computing the opposite 
140 bank, we could declare a predicate stating when two banks are opposite to each 
141 other. Only two cases are possible, leading naturally to the following 
142 definition:
143 *)
144
145 inductive opp : \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6 → \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6 → Prop ≝ 
146 | east_west : opp \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6
147 | west_east : opp \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6.
148
149 (* In precisely the same way as "bank" is the smallest type containing east and
150 west, opp is the smallest predicate containing the two sub-cases east_west and
151 weast_east. If you have some familiarity with Prolog, you may look at opp as the
152 predicate definined by the two clauses - in this case, the two facts -
153
154 Between opp and opposite we have the following relation:
155     opp a b iff a = opposite b
156 Let us prove it, starting from the left to right implication, first *)
157
158 lemma opp_to_opposite: ∀a,b. \ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 a b → a \ 5a title="leibnitz's equality" href="cic:/fakeuri.def(1)"\ 6=\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 b.
159  
160 (* We start the proof introducing a, b and the hypothesis opp a b, that we
161 call oppab. *)
162 #a #b #oppab
163
164 (* Now we proceed by cases on the possible proofs of (opp a b), that is on the 
165 possible shapes of oppab. By definition, there are only two possibilities, 
166 namely east_west or west_east. Both subcases are trivial, and can be closed by
167 automation *)
168
169 cases oppab // qed.
170
171 (* Let us come to the opposite direction. *)
172
173 lemma opposite_to_opp: ∀a,b. a \ 5a title="leibnitz's equality" href="cic:/fakeuri.def(1)"\ 6=\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/opposite.def(1)"\ 6opposite\ 5/a\ 6 b → \ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 a b.
174
175 (* As usual, we start introducing a, b and the hypothesis (a = opposite b), 
176 that we call eqa. *)
177
178 #a #b #eqa
179
180 (* The right way to proceed, now, is by rewriting a into (opposite b). We do
181 this by typing ">eqa". If we wished to rewrite in the opposite direction, namely
182 opposite b into a, we would have typed "<eqa". *)
183
184 >eqa
185
186 (* We conclude the proof by cases on b. *)
187
188 cases b // qed.
189
190 (*
191 It is time to proceed with our formalization of the farmer's problem. 
192 A state of the system is defined by the position of four item: the goat, the 
193 wolf, the cabbage, and the boat. The simplest way to declare such a data type
194 is to use a record.
195 *)
196
197 record state : Type[0] ≝
198   {goat_pos : \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6;
199    wolf_pos : \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6;
200    cabbage_pos: \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6;
201    boat_pos : \ 5a href="cic:/matita/tutorial/chapter1/bank.ind(1,0,0)"\ 6bank\ 5/a\ 6}.
202
203 (* When you define a record named foo, the system automatically defines a record 
204 constructor named mk_foo. To construct a new record you pass as arguments to 
205 mk_foo the values of the record fields *)
206
207 definition start ≝ \ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6.
208 definition end ≝ \ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6.
209
210 (* We must now define the possible moves. A natural way to do it is in the form 
211 of a relation (a binary predicate) over states. *)
212
213 inductive move : \ 5a href="cic:/matita/tutorial/chapter1/state.ind(1,0,0)"\ 6state\ 5/a\ 6 → \ 5a href="cic:/matita/tutorial/chapter1/state.ind(1,0,0)"\ 6state\ 5/a\ 6 → Prop ≝
214 | move_goat: ∀g,g1,w,c. \ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 g g1 → move (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c g) (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g1 w c g1)
215   (* We can move the goat from a bank g to the opposite bank g1 if and only if the
216      boat is on the same bank g of the goat and we move the boat along with it. *)
217 | move_wolf: ∀g,w,w1,c. \ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 w w1 → move (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c w) (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w1 c w1)
218 | move_cabbage: ∀g,w,c,c1.\ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 c c1 → move (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c c) (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c1 c1)
219 | move_boat: ∀g,w,c,b,b1. \ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 b b1 → move (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c b) (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c b1).
220
221 (* A state is safe if either the goat is on the same bank of the boat, or both 
222 the wolf and the cabbage are on the opposite bank of the goat. *)
223
224 inductive safe_state : \ 5a href="cic:/matita/tutorial/chapter1/state.ind(1,0,0)"\ 6state\ 5/a\ 6 → Prop ≝
225 | with_boat : ∀g,w,c.safe_state (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g w c g)
226 | opposite_side : ∀g,g1,b.\ 5a href="cic:/matita/tutorial/chapter1/opp.ind(1,0,0)"\ 6opp\ 5/a\ 6 g g1 → safe_state (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 g g1 g1 b).
227
228 (* Finally, a state y is reachable from x if either there is a single move 
229 leading from x to y, or there is a safe state z such that z is reachable from x 
230 and there is a move leading from z to y *)
231
232 inductive reachable : \ 5a href="cic:/matita/tutorial/chapter1/state.ind(1,0,0)"\ 6state\ 5/a\ 6 → \ 5a href="cic:/matita/tutorial/chapter1/state.ind(1,0,0)"\ 6state\ 5/a\ 6 → Prop ≝
233 | one : ∀x,y.\ 5a href="cic:/matita/tutorial/chapter1/move.ind(1,0,0)"\ 6move\ 5/a\ 6 x y → reachable x y
234 | more : ∀x,z,y. \ 5span style="text-decoration: underline;"\ 6\ 5/span\ 6reachable x z → \ 5a href="cic:/matita/tutorial/chapter1/safe_state.ind(1,0,0)"\ 6safe_state\ 5/a\ 6 z → \ 5span style="text-decoration: underline;"\ 6\ 5/span\ 6\ 5a href="cic:/matita/tutorial/chapter1/move.ind(1,0,0)"\ 6move\ 5/a\ 6 z y → reachable x y.
235
236 (* Remarkably, Matita is now able to solve the problem by itslef, provided
237 you allow automation to exploit more resources. The command /n/ is similar to
238 //, where n is a measure of this complexity: in particular it is a bound to
239 the depth of the expected proof tree (more precisely, to the number of nested
240 applicative nodes). In this case, there is a solution in six moves, and we
241 need a few more applications to handle reachability, and side conditions. 
242 The magic number to let automation work is, in this case, 9.  *)
243
244 lemma problem: \ 5a href="cic:/matita/tutorial/chapter1/reachable.ind(1,0,0)"\ 6reachable\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/start.def(1)"\ 6start\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/end.def(1)"\ 6end\ 5/a\ 6.
245 normalize /9/ qed. 
246
247 (* Let us now try to derive the proof in a more interactive way. Of course, we
248 expect to need several moves to transfer all items from a bank to the other, so 
249 we should start our proof by applying "more".
250 *)
251
252 lemma problem1: \ 5a href="cic:/matita/tutorial/chapter1/reachable.ind(1,0,0)"\ 6reachable\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/start.def(1)"\ 6start\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/end.def(1)"\ 6end\ 5/a\ 6.
253 normalize @\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6
254
255 (* We have now four open subgoals:
256
257   X : STATE
258   Y : reachable [east,east,east,east] X
259   W : safe_state X
260   Z : move X [west,west,west,west]
261  
262 That is, we must guess a state X, such that this is reachable from start, it is 
263 safe, and there is a move leading from X to end. All goals are active, that is
264 emphasized by the fact that they are all red. Any command typed by the user is
265 normally applied in parallel to all active goals, but clearly we must proceed 
266 here is a different way for each of them. The way to do it, is by structuring
267 the script using the following syntax: [...|... |...|...] where we typically have
268 as many cells inside the brackets as the number of the active subgoals. The
269 interesting point is that we can associate with the three symbol "[", "|" and
270 "]" a small-step semantics that allow to execute them individually. In particular
271
272 - the operator "[" opens a new focusing section for the currently active goals,
273   and focus on the first of them
274 - the operator "|" shift the focus to the next goal
275 - the operator "]" close the focusing section, falling back to the previous level
276   and adding to it all remaining goals not yet closed
277
278 Let us see the effect of the "[" on our proof:
279 *)
280
281   [  
282
283 (* As you see, only the first goal has now the focus on. Moreover, all goals got
284 a progressive numerical label, to help designating them, if needed. 
285 We can now proceed in several possible ways. The most straightforward way is to 
286 provide the intermediate state, that is [east,west,west,east]. We can do it, by 
287 just applying this term. *)
288
289    @(\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6
290
291 (* This application closes the goal; at present, no goal has the focus on.
292 In order to act on the next goal, we must focus on it using the "|" operator. In
293 this case, we would like to skip the next goal, and focus on the trivial third 
294 subgoal: a simple way to do it, is by retyping "|". The proof that 
295 [east,west,west,east] is safe is trivial and can be done with //.*)
296
297   || //
298
299 (*
300 We then advance to the next goal, namely the fact that there is a move from 
301 [east,west,west,east] to [west,west,west,west]; this is trivial too, but it 
302 requires /2/ since move_goat opens an additional subgoal. By applying "]" we
303 refocus on the skipped goal, going back to a situation similar to the one we
304 started with. *)
305
306   | /2/ ] 
307
308 (* Let us perform the next step, namely moving back the boat, in a sligtly 
309 different way. The more operation expects as second argument the new 
310 intermediate state, hence instead of applying more we can apply this term
311 already instatated on the next intermediate state. As first argument, we
312 type a question mark that stands for an implicit argument to be guessed by
313 the system. *)
314
315 @(\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6 ? (\ 5a href="cic:/matita/tutorial/chapter1/state.con(0,1,0)"\ 6mk_state\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6))
316
317 (* We now get three independent subgoals, all actives, and two of them are 
318 trivial. We\ 5span style="font-family: Verdana,sans-serif;"\ 6 \ 5/span\ 6can just apply automation to all of them, and it will close the two
319 trivial goals. *)
320
321 /2/
322
323 (* Let us come to the next step, that consists in moving the wolf. Suppose that 
324 instead of specifying the next intermediate state, we prefer to specify the next 
325 move. In the spirit of the previous example, we can do it in the following way 
326 *)
327
328 @(\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6 … (\ 5a href="cic:/matita/tutorial/chapter1/move.con(0,2,0)"\ 6move_wolf\ 5/a\ 6 … ))
329
330 (* The dots stand here for an arbitrary number of implicit arguments, to be 
331 guessed by the system. 
332 Unfortunately, the previous move is not enough to fully instantiate the new 
333 intermediate state: a bank B remains unknown. Automation cannot help here,
334 since all goals depend from this bank and automation refuses to close some
335 subgoals instantiating other subgoals remaining open (the instantiation could
336 be arbitrary). The simplest way to proceed is to focus on the bank, that is
337 the fourth subgoal, and explicitly instatiate it. Instead of repeatedly using "|",
338 we can perform focusing by typing "4:" as described by the following command. *)
339
340 [4: @\ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6] /2/
341
342 (* Alternatively, we can directly instantiate the bank into the move. Let
343 us complete the proof in this, very readable way. *)
344
345 @(\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6 … (\ 5a href="cic:/matita/tutorial/chapter1/move.con(0,1,0)"\ 6move_goat\ 5/a\ 6 \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 … )) /2/
346 @(\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6 … (\ 5a href="cic:/matita/tutorial/chapter1/move.con(0,3,0)"\ 6move_cabbage\ 5/a\ 6 ?? \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,1,0)"\ 6east\ 5/a\ 6 … )) /2/
347 @(\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,2,0)"\ 6more\ 5/a\ 6 … (\ 5a href="cic:/matita/tutorial/chapter1/move.con(0,4,0)"\ 6move_boat\ 5/a\ 6 ??? \ 5a href="cic:/matita/tutorial/chapter1/bank.con(0,2,0)"\ 6west\ 5/a\ 6 … )) /2/
348 @\ 5a href="cic:/matita/tutorial/chapter1/reachable.con(0,1,0)"\ 6one\ 5/a\ 6 /2/ qed.