-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlhanoi3.v
523 lines (500 loc) · 20.6 KB
/
lhanoi3.v
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
From mathcomp Require Import all_ssreflect.
From hanoi Require Import gdist ghanoi ghanoi3.
(******************************************************************************)
(* *)
(* Linear Hanoi Problem with 3 pegs *)
(* *)
(******************************************************************************)
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Section LHanoi3.
Lemma lrel3D (p1 p2 : peg 3) : p1 != p2 -> lrel p1 p2 || lrel p1 `p[p1, p2].
Proof.
move=> p1Dp2.
have D p3 p4 : (p3 == p4) = (val p3 == val p4).
by apply/eqP/idP => /val_eqP.
move: p1Dp2 (opegDl p1 p2) (opegDr p1 p2).
by case: (peg3E `p[p1, p2]) => ->;
case: (peg3E p1) => ->;
case: (peg3E p2) => ->;
rewrite !D /lrel /= ?inordK.
Qed.
Lemma lrel3B (p1 p2 : peg 3) : p1 != p2 ->
~~ [&& lrel p1 p2, lrel p1 `p[p1, p2] & lrel p2 `p[p1, p2]].
Proof.
have D p3 p4 : (p3 == p4) = (val p3 == val p4).
by apply/eqP/idP => /val_eqP.
move: (opegDl p1 p2) (opegDr p1 p2).
by case: (peg3E `p[p1, p2]) => ->;
case: (peg3E p1) => ->;
case: (peg3E p2) => ->;
rewrite !D /lrel /= ?inordK.
Qed.
Lemma lrel3O (p1 p2 : peg 3) : p1 != p2 ->
~~ lrel p1 p2 -> lrel p1 `p[p1, p2].
Proof. by move/lrel3D; case: lrel. Qed.
Lemma lrel3ON (p1 p2 : peg 3) : p1 != p2 ->
lrel p1 p2 -> ~~ lrel p1 `p[p1, p2] -> lrel p2 `p[p1, p2].
Proof.
have D p3 p4 : (p3 == p4) = (val p3 == val p4).
by apply/eqP/idP => /val_eqP.
move: (opegDl p1 p2) (opegDr p1 p2).
by case: (peg3E `p[p1, p2]) => ->;
case: (peg3E p1) => ->;
case: (peg3E p2) => ->;
rewrite !D /lrel /= ?inordK.
Qed.
Lemma lrel3ON4 (p1 p2 p3 p4 : peg 3) :
p1 != p2 -> p1 != p3 -> p2 != p3 ->
lrel p1 p2 -> lrel p1 p3 -> lrel p3 p4 -> p4 = p1.
Proof.
have D p5 p6 : (p5 == p6) = (val p5 == val p6).
by apply/eqP/idP => /val_eqP.
by case: (peg3E p1) => ->;
case: (peg3E p2) => ->;
case: (peg3E p3) => ->;
case: (peg3E p4) => ->;
rewrite !D /lrel /= ?inordK.
Qed.
Local Notation "c1 `-->_r c2" := (lmove c1 c2)
(format "c1 `-->_r c2", at level 60).
Local Notation "c1 `-->*_r c2" := (connect lmove c1 c2)
(format "c1 `-->*_r c2", at level 60).
(******************************************************************************)
(* Function that builds a path from a configuration to a peg *)
(******************************************************************************)
Fixpoint lhanoi3 {n : nat} : configuration 3 n -> configuration _ n -> _ :=
match n with
| 0 => fun _ _ => [::] : seq (configuration _ 0)
| _.+1 =>
fun c1 c2 =>
let p1 := c1 ldisk in
let p2 := c2 ldisk in
if p1 == p2 then [seq ↑[i]_p2 | i <- lhanoi3 ↓[c1] ↓[c2]] else
let p3 := `p[p1, p2] in
if lrel p1 p2 then
[seq ↑[i]_p1 | i <- lhanoi3 ↓[c1] `c[p3]] ++
[seq ↑[i]_p2 | i <- `c[p3] :: lhanoi3 `c[p3] ↓[c2]]
else
[seq ↑[i]_p1 | i <- lhanoi3 ↓[c1] `c[p2]] ++
[seq ↑[i]_p3 | i <- `c[p2] :: lhanoi3 `c[p2] `c[p1]] ++
[seq ↑[i]_p2 | i <- `c[p1] :: lhanoi3 `c[p1] ↓[c2]]
end.
Lemma lhanoi3_nil_inv n (c1 c2 : _ _ n) : lhanoi3 c1 c2 = [::] -> c1 = c2.
Proof.
elim: n c1 c2 => [c1 c2 _|n IH c1 c2] /=; first by apply/ffunP=> [] [].
case: eqP => [H | H] //=; last by case: lrel; case: map.
rewrite -{2}[c1]cunliftrK -{3}[c2]cunliftrK.
case: lhanoi3 (IH ↓[c1] ↓[c2]) => //= -> // _.
by rewrite H.
Qed.
Lemma last_lhanoi3 n (c1 c2 : _ _ n) (cs := lhanoi3 c1 c2) :
last c1 cs = c2.
Proof.
have HH := @lirr 3.
rewrite /cs; elim: n c1 c2 {cs} => /= [c1 c2| n IH c1 c2].
by apply/ffunP=> [] [].
case: eqP => [Ho|/eqP Do].
by rewrite -{1}[c1](cunliftrK) Ho last_map IH // cunliftrK.
set p1 := _ ldisk; set p2 := opeg _ _.
set cp2 := `c[_]; set cp := `c[_]; set cp1 := `c[_].
case: (boolP (lrel _ _)) => [Hrel|Hrel].
by rewrite last_cat /= last_map IH cunliftrK.
by rewrite last_cat /= last_cat /= last_map IH cunliftrK.
Qed.
Lemma path_lhanoi3 n (c1 c2 : _ _ n) (cs := lhanoi3 c1 c2) :
path lmove c1 cs.
Proof.
have HH := @lirr 3.
rewrite /cs; elim: n c1 c2 {cs} => //= n IH c1 c2.
case: eqP => [Ho|/eqP Do].
by rewrite -{1}[c1](cunliftrK) Ho path_liftr.
set p1 := _ ldisk; set p2 := opeg _ _.
set cp2 := `c[_]; set cp := `c[_]; set cp1 := `c[_].
case: (boolP (lrel _ _)) => [Hrel|Hrel].
rewrite cat_path /=; apply/and3P; repeat split.
- by rewrite -{1}[c1]cunliftrK path_liftr.
- rewrite -{1}[c1]cunliftrK last_map last_lhanoi3.
by apply: move_liftr_perfect; rewrite // eq_sym (opegDl, opegDr).
by rewrite path_liftr.
rewrite cat_path /= cat_path /=; apply/and5P; split.
- by rewrite -{1}[c1]cunliftrK /= path_liftr.
- rewrite -{1}[c1]cunliftrK /= last_map last_lhanoi3.
- apply: move_liftr_perfect => //; first by apply: lrel3O.
by rewrite opegDr.
- by rewrite path_liftr.
- rewrite last_map last_lhanoi3.
apply: move_liftr_perfect => //.
- rewrite /p2 opeg_sym lsym lrel3O ?opegDl //; first by rewrite eq_sym.
by rewrite lsym.
- by rewrite opegDl.
by rewrite eq_sym.
by rewrite path_liftr.
Qed.
(* Two configurations are always connected *)
Lemma move_lconnect3 n (c1 c2 : configuration 3 n) : c1 `-->*_r c2.
Proof.
apply/connectP; exists (lhanoi3 c1 c2); first by apply: path_lhanoi3.
by rewrite last_lhanoi3.
Qed.
(* lhanoi gives the smallest path connecting c1 to c2 *)
(* This path is unique *)
Lemma lhanoi3_min n (c1 c2 : configuration 3 n) cs :
path lmove c1 cs -> last c1 cs = c2 ->
size (lhanoi3 c1 c2) <= size cs ?= iff (cs == lhanoi3 c1 c2).
Proof.
(* we adapt the proof for the standard ha
noi problem
surely a shorter proof exists *)
have [m sLm] := ubnP (size cs); elim: m => // m IHm in n c1 c2 cs sLm *.
(* The usual induction on the number of disks *)
elim : n c1 c2 cs sLm => [c1 p [|] //=|n IH c1 c cs Scs c1Pcs lc1csEp /=].
set (p := c ldisk).
rewrite !fun_if !size_cat /= !size_cat /= !size_map.
case: (c1 _ =P p) => [lc1Ep |/eqP lc1Dp].
(* the largest disk is already well-placed *)
have [cs1 [c1'Pcs1 lc1'csElc1cs' /leqifP]] :=
pathS_restrict (@lirr 3) c1Pcs.
have lc1'cs1E : last ↓[c1] cs1 = ↓[c].
by rewrite lc1'csElc1cs'; congr cunliftr.
case: eqP=> [csEcs1 /eqP<- |/eqP csDcs1 scs1L].
rewrite csEcs1 lc1Ep eq_map_liftr.
apply: IH => //.
by move: Scs; rewrite csEcs1 size_map.
apply/leqifP; case: eqP => [->//|_].
by rewrite size_map.
apply: leq_ltn_trans (scs1L).
apply: IH => //.
by apply: ltn_trans Scs.
pose f (c : configuration 3 n.+1) := c ldisk.
have HHr := @lirr 3.
have HHs := @lsym 3.
(* We need to move the largest disk *)
case: path3SP c1Pcs => // [c1' cs' p1 csE c1'Ecs1'|
cs1 cs2 p1 p2 p3 c1' c2 p1Dp2 p1Rp2
lc1'cs1Epp3 csE c1'Pcs1 c2Pcs2 _].
(* this case is impossible the largest disk has to move *)
case/eqP: lc1Dp.
move: lc1csEp => /(congr1 f).
by rewrite /f csE -{1}[c1]cunliftrK last_map cliftr_ldisk.
(* c4 is the first configuration when the largest disk has moved *)
rewrite csE size_cat -/p1.
have Scs1 : size cs1 < m.+1.
apply: ltn_trans Scs.
by rewrite csE size_cat /= size_map addnS ltnS leq_addr.
have Scs2 : size cs2 < m.+1.
apply: ltn_trans Scs.
by rewrite csE size_cat /= size_map addnS ltnS leq_addl.
have [p1Rp| p1NRp] := boolP (lrel p1 p).
case: (p2 =P p) => [p2Ep|/eqP p2Dp].
(* the first moves of largest disk of cs is the right one *)
rewrite -p2Ep -/p3 size_map /=.
have/(pathS_restrict (@lirr 3))[cs2' [c2'Pcs2' lc2'cs2'E cs2'L]] := c2Pcs2.
have Scs2' : size cs2' < m.+1.
by apply: leq_ltn_trans Scs2; rewrite cs2'L.
have /IH := lc1'cs1Epp3 => // /(_ Scs1 c1'Pcs1) IHc1.
have /IH : last ↓[c2] cs2' = ↓[c]
=> [| /(_ Scs2' c2'Pcs2') IHc2].
rewrite lc2'cs2'E; congr cunliftr.
by move: lc1csEp; rewrite csE last_cat /= => ->.
rewrite cliftrK in IHc2.
move /leqifP : cs2'L.
case: eqP => [cs2E _ | /eqP cs2D Lcs2].
(* there is only one move of the largest disk in cs *)
rewrite cs2E size_map cliftr_ldisk /=.
have /leqifP := IHc1; case: eqP => [->_ |_ Lc1].
(* the first part of cs is perfect *)
have /leqifP := IHc2; case: eqP => [->_ |_ Lc2].
(* the second part of cs is perfect, only case of equality *)
apply/leqifP; case: eqP => [/(congr1 size)|[]].
by rewrite !size_cat /= !size_map => ->.
by congr (_ ++ _ :: _); rewrite !cliftK.
apply/leqifP; case: eqP => [/(congr1 size)|_].
by rewrite size_cat /= size_cat /= !size_map => ->.
by rewrite ltn_add2l ltnS.
apply/leqifP; case: eqP => [/(congr1 size)|_].
by rewrite !size_cat /= !size_map => ->.
by rewrite -addSn leq_add // ltnS IHc2.
apply/leqifP; case: eqP => [/(congr1 size)|_].
by rewrite !size_cat /= !size_map => ->.
rewrite -addnS leq_add //= ?ltnS.
by rewrite IHc1.
by apply: leq_ltn_trans Lcs2; rewrite IHc2.
(* The largest disk jumped to an intermediate peg *)
have p3Ep : p3 = p by apply/eqP; rewrite opeg3E // lc1Dp.
have p1Dp : p1 != p by rewrite eq_sym -p3Ep opeg3E // eqxx.
(* cs cannot be optimal *)
apply/leqifP; case: eqP => [/(congr1 size)|_].
by rewrite !size_cat /= !size_map => ->.
case: path3SP c2Pcs2 => // [c2' cs2' p2' cs2E c2'Pcs2' _|
cs3 cs4 p4 p5 p6 c2' c3
p4Dp5 p4Rp5 lc2'cs3Epp6 cs2E c2'Pcs3 c3Pcs4 _].
(* this is impossible we need another move of the largest disk *)
case/eqP: p2Dp.
have := lc1csEp.
rewrite csE last_cat /= cs2E -[c2]cunliftrK last_map => /(congr1 f).
by rewrite /f !cliftr_ldisk.
(* cs has a duplicate *)
have p4Ep2 : p4 = p2 by rewrite /p4 !cliftr_ldisk.
have p5Ep1: p5 = p1.
apply: (@lrel3ON4 p1 p p2 p5) => //; first by rewrite eq_sym.
by rewrite -p4Ep2.
have p6Ep3 : p6 = p3.
by apply/eqP; rewrite /p6 opeg3E // p4Ep2 p5Ep1 p3Ep p2Dp.
(* exhibit a shortest path *)
pose cs5 := [seq ↑[i]_p1 | i <- cs1] ++ cs4.
have scs5Lscs : size cs5 < size cs.
rewrite /cs5 csE cs2E !size_cat /= !size_cat /= !size_map.
by rewrite ltn_add2l // !addnS! ltnS -addSn leq_addl.
have c1Mcs5 : path lmove c1 cs5.
rewrite cat_path -{1}[c1]cunliftrK /= !path_liftr //=.
rewrite c1'Pcs1.
rewrite -{1}[c1]cunliftrK last_map lc1'cs1Epp3 //.
by rewrite -p6Ep3 -/p1 -p5Ep1.
have lc1cs5E : last c1 cs5 = c.
rewrite last_cat.
rewrite -[c1]cunliftrK last_map lc1'cs1Epp3 //.
rewrite -p6Ep3 -/p1 -p5Ep1 //.
have := lc1csEp.
by rewrite csE cs2E last_cat /= last_cat.
apply: leq_trans (_ : size cs5 < _); last first.
by rewrite cs2E /= !size_cat /= !size_map ltn_add2l //=
ltnS -addSnnS leq_addl.
rewrite ltnS.
have /IHm : size cs5 < m.
rewrite -ltnS.
by apply: leq_ltn_trans Scs.
move=> /(_ c1 c c1Mcs5 lc1cs5E) /=.
by rewrite /= (negPf lc1Dp) p1Rp -/p1 size_cat /= !size_map => ->.
have p2Dp : p2 != p by apply: contra p1NRp => /eqP<-.
have p3Ep : p3 = p.
by apply/eqP; rewrite opeg3E // lc1Dp.
case: path3SP c2Pcs2 => // [c2' cs2' p2' cs2E c2'Pcs2' _|
cs3 cs4 p4 p5 p6 c2' c3
p4Dp5 p4Rp5 lc2'cs3Epp6 cs2E c2'Pcs3 c3Pcs4 _].
(* this is impossible at least two moves to reach p *)
case/eqP: p2Dp.
move: lc1csEp.
rewrite csE cs2E last_cat /= -[c2]cunliftrK last_map !cliftr_ldisk.
by move => /(congr1 f); rewrite /f !cliftr_ldisk.
rewrite cs2E /= size_cat !size_map /=.
have Scs3 : size cs3 < m.+1.
by apply: leq_trans Scs2; rewrite ltnS cs2E size_cat /= size_map leq_addr.
have Scs4 : size cs4 < m.+1.
by apply: leq_trans Scs2; rewrite ltnS cs2E size_cat /= -addSnnS leq_addl.
have p4Ep2 : p4 = p2 by rewrite [LHS]cliftr_ldisk.
case: (p5 =P p1) => [p5Ep1|/eqP p5Dp1].
(* cs has a duplicate *)
apply/leqifP; case: eqP => [/(congr1 size)|_].
rewrite !size_cat /= size_cat !size_map size_cat /= !size_map => ->.
by rewrite !(addSn, addnS) addnA.
have p6Ep3 : p6 = p3.
by rewrite /p6 /p3 opeg_sym; congr opeg.
have lc1'cs1Ec3 : cliftr p1 (last c1' cs1) = c3.
by rewrite lc1'cs1Epp3 -p6Ep3 -p5Ep1.
(* exhibit a shortest path *)
pose cs5 := [seq ↑[i]_p1 | i <- cs1] ++ cs4.
have scs5Lscs : size cs5 < size cs.
rewrite /cs5 csE cs2E !size_cat /= !size_cat /= !size_map.
by rewrite ltn_add2l // addnS !ltnS -addSn leq_addl.
have c1Mcs5 : path lmove c1 cs5.
rewrite cat_path -{1}[c1]cunliftrK /= !path_liftr //=.
rewrite c1'Pcs1.
rewrite -{1}[c1]cunliftrK last_map lc1'cs1Epp3 //.
by rewrite -p6Ep3 -/p1 -p5Ep1.
have lc1cs5E : last c1 cs5 = c.
rewrite last_cat.
rewrite -[c1]cunliftrK last_map lc1'cs1Epp3 //.
rewrite -p6Ep3 -/p1 -p5Ep1 //.
have := lc1csEp.
by rewrite csE cs2E last_cat /= last_cat.
apply: leq_trans (_ : size cs5 < _); last first.
by rewrite !size_cat /= !size_map ltn_add2l //= ltnS -addSnnS leq_addl.
have /IHm : size cs5 < m.
rewrite -ltnS.
by apply: leq_ltn_trans Scs.
move=> /(_ c1 c c1Mcs5 lc1cs5E) /=.
rewrite /= (negPf lc1Dp) (negPf p1NRp) -/p1 ltnS.
by rewrite !size_cat /= size_cat /= !size_map => ->.
have p5Ep : p5 = p.
by apply/eqP; rewrite eq_sym -p3Ep opeg3E // eq_sym p5Dp1 -p4Ep2.
have p2Ep1p : p2 = opeg p1 p.
by apply/eqP; rewrite eq_sym opeg3E // p1Dp2 eq_sym p2Dp.
have p6Ep1 : p6 = p1.
by apply/eqP; rewrite opeg3E // p4Ep2 eq_sym p1Dp2.
case: path3SP c3Pcs4 => // [c3' cs4' p8 cs4E c3'Pcs4' _|
cs5 cs6 p8 p9 p10 c3' c4
p8Dp9 p8Rp9 lc3'cs5Epp10 cs4E c3'Pcs5 c4Pcs6 _].
have Scs4' : size cs4' < m.+1 by rewrite cs4E size_map in Scs4.
rewrite cs4E size_map.
rewrite csE cs2E last_cat /= last_cat /=
-[c3]cunliftrK cs4E last_map in lc1csEp.
have /(congr1 cunliftr) := lc1csEp.
rewrite cliftrK => lc3'cs4'Ec'.
have /IH := c1'Pcs1 => /(_ _ Scs1 lc1'cs1Epp3) IH1.
rewrite p3Ep in IH1.
have /IH := c2'Pcs3 => /(_ _ Scs3 lc2'cs3Epp6) IH2.
rewrite [c2']cliftrK p3Ep p6Ep1 in IH2.
have /IH := c3'Pcs4' => /(_ _ Scs4' lc3'cs4'Ec') IH3.
rewrite [c3']cliftrK p6Ep1 in IH3.
move /leqifP : IH1.
case: eqP => [E1 _ | /eqP cs1D Lcs1].
(* the first part cs1 is perfect *)
have /leqifP := IH2; case: eqP => [E2 _ |_ Lc1].
(* the second part cs3 is perfect *)
have /leqifP := IH3; case: eqP => [E3 _ |_ Lc2].
(* the third part cs4 is perfect, only case of equality *)
apply/leqifP; case: eqP => [/(congr1 size)|[]].
rewrite !size_cat /= size_cat /= size_cat /= !size_map => ->.
by rewrite !(addSn, addnS) addnA.
congr (_ ++ _ :: _ ++ _ :: _).
- by rewrite E1.
- by rewrite /c2 p3Ep p2Ep1p.
- by rewrite p4Ep2 -p2Ep1p E2.
- by rewrite /c3 p6Ep1 p5Ep.
- congr (map (cliftr _) _).
by rewrite /p8 !cliftr_ldisk.
by [].
apply/leqifP; case: eqP => [/(congr1 size)|_].
rewrite !size_cat /= !size_cat /= !size_map => ->.
by rewrite !(addSn, addnS) addnA.
by rewrite E1 E2 !(addSn, addnS) !ltnS !ltn_add2l.
apply/leqifP; case: eqP => [/(congr1 size)|_].
rewrite !size_cat /= !size_cat /= !size_map => ->.
by rewrite !(addSn, addnS) addnA.
rewrite E1 !(addSn, addnS) !ltnS !ltn_add2l.
apply: leq_ltn_trans (_ : _ <= size (lhanoi3 `c[p, n] `c[p1])
+ size cs4') _.
by rewrite leq_add2l IH3.
by rewrite ltn_add2r.
apply/leqifP; case: eqP => [/(congr1 size)|_].
rewrite !size_cat /= !size_cat /= !size_map => ->.
by rewrite !(addSn, addnS).
by rewrite !(addSn, addnS) !ltnS -addSn !leq_add ?IH2 ?IH3.
(* three moves in a row -> duplicate *)
have p8Ep : p8 = p by rewrite [LHS]cliftr_ldisk.
have p9Ep2 : p9 = p2.
apply: lrel3ON4 p8Rp9.
- by rewrite eq_sym; exact: p1Dp2.
- by rewrite p8Ep.
- by rewrite p8Ep.
- by rewrite lsym.
by rewrite p8Ep -p4Ep2 -p5Ep.
have p10Ep1 : p10 = p1.
by apply/eqP; rewrite opeg3E // p8Ep eq_sym lc1Dp p9Ep2 eq_sym.
have cc : cliftr p2 (last c2' cs3) = c4.
by rewrite /c4 lc2'cs3Epp6 p6Ep1 p10Ep1 p9Ep2.
apply/leqifP; case: eqP => [/(congr1 size)|_].
rewrite !size_cat /= size_cat !size_map size_cat /= !size_map => ->.
by rewrite !(addSn, addnS).
(* exhibit a shortest path *)
pose cs7 := [seq ↑[i]_p1 | i <- cs1] ++ c2 :: [seq ↑[i]_p4 | i <- cs3] ++ cs6.
have scs7Lscs7: size cs7 < size cs.
rewrite /cs7 csE cs2E cs4E.
rewrite size_cat /= size_cat /= size_cat /= size_cat /= size_cat /= !size_map.
by rewrite !addnS ltnS -!addnS !leq_add2l addnS ltnS -addSnnS leq_addl.
have c1Mcs7 : path lmove c1 cs7.
rewrite cat_path -{1}[c1]cunliftrK /= !path_liftr //=.
rewrite c1'Pcs1.
rewrite -{1}[c1]cunliftrK last_map lc1'cs1Epp3 //.
rewrite p4Ep2 cat_path /c2 -/p1 /=.
apply/and3P; split => //.
- apply: move_liftr_perfect => //.
by rewrite eq_sym opegDl.
by rewrite eq_sym opegDr.
by rewrite path_liftr //; rewrite [c2']cliftrK in c2'Pcs3.
by rewrite last_map; rewrite [c2']cliftrK in cc; rewrite cc.
have lc1cs7E : last c1 cs7 = c.
rewrite last_cat /= last_cat p4Ep2.
rewrite -[c2]cunliftrK !cliftr_ldisk.
rewrite last_map lc2'cs3Epp6.
have := lc1csEp.
rewrite csE cs2E cs4E last_cat /= last_cat /= last_cat /=.
by rewrite /c4 p10Ep1 p6Ep1 p9Ep2.
apply: leq_trans (_ : size cs7 < _); last first.
rewrite !size_cat /= size_cat /= !size_map ltn_add2l //=.
rewrite cs4E size_cat /= size_map !addnS !ltnS -addnS -addSn.
by rewrite leq_add2l leq_addl.
have /IHm : size cs7 < m.
rewrite -ltnS.
by apply: leq_ltn_trans Scs.
move=> /(_ c1 c c1Mcs7 lc1cs7E) /=.
rewrite /= (negPf lc1Dp) (negPf p1NRp) -/p1 ltnS.
by rewrite size_cat /= size_cat /= !size_map => ->.
Qed.
(* size on a perfect configuration depends which peg we consider *)
Lemma size_app_lhanoi3_p n p1 p2 :
size (lhanoi3 `c[p1, n] `c[p2]) =
if lrel p1 p2 then (3 ^ n).-1./2 else (3 ^ n).-1 * (p1 != p2).
Proof.
elim: n p1 p2 => [p1 p2|n IH p1 p2] /=; first by rewrite if_same.
rewrite !ffunE; case: eqP => [p1Ep2|/eqP p1Dp2].
by rewrite !perfect_unliftr size_map IH p1Ep2 eqxx lirr !muln0.
rewrite !perfect_unliftr !fun_if !size_cat /= !size_cat /= !size_map.
rewrite !IH eq_sym opegDl // opegDr //= !muln1.
have Hd : (3 ^ n.+1).-1 = (3 ^ n).-1 + (3 ^ n).*2.
rewrite expnS -[3 ^n]prednK ?expn_gt0 //.
by rewrite mulnS /= doubleS !addnS mulSn mul2n.
rewrite ![lrel p2 _]lsym.
case: (boolP (lrel _ _)) => [Hrel1|Hrel1]; last first.
rewrite [p2 == _]eq_sym p1Dp2 !muln1.
rewrite expnS !mulSn addn0.
by case: expn (expn_gt0 3 n) => // k _; rewrite !addnS addnA.
case: (boolP (lrel _ _)) => [Hrel2|Hrel2].
rewrite !ifN.
by rewrite Hd halfD /= odd_double andbF add0n doubleK prednK ?expn_gt0.
by have := lrel3B p1Dp2; rewrite Hrel1 Hrel2 lsym.
rewrite ifT; last by rewrite lsym; apply: lrel3ON.
rewrite Hd halfD /= odd_double andbF add0n doubleK -addSnnS addnC.
by rewrite prednK // expn_gt0.
Qed.
Fixpoint size_lhanoi3 {n : nat} : configuration 3 n -> configuration 3 n -> _ :=
match n with
| 0 => fun _ _ => 0
| n1.+1 =>
fun c1 c2 =>
let p1 := c1 ldisk in
let p2 := c2 ldisk in
if p1 == p2 then size_lhanoi3 ↓[c1] ↓[c2] else
let p3 := `p[p1, p2] in
if lrel p1 p2 then
(size_lhanoi3 ↓[c1] `c[p3] + size_lhanoi3 `c[p3] ↓[c2]).+1
else
(size_lhanoi3 ↓[c1] `c[p2] +
(3 ^ n1).-1 +
size_lhanoi3 `c[p1] ↓[c2]).+2
end.
Lemma size_lhanoi3E n (c1 c2 : _ _ n) :
size_lhanoi3 c1 c2 = size (lhanoi3 c1 c2).
Proof.
elim: n c1 c2 => //= n IH c1 c2.
case: eqP => [lc1Elc2|/eqP lc1Dlc2].
by rewrite size_map IH.
case: (boolP (lrel _ _)) => [lc1Rlc2|lc1NRlc2].
by rewrite size_cat /= !size_map !IH addnS.
rewrite size_cat /= size_cat /= !size_map size_app_lhanoi3_p
lsym (negPf lc1NRlc2).
by rewrite eq_sym (negPf lc1Dlc2) muln1 !addnS addnA !IH.
Qed.
(* size on a perfect configuration depends which peg we consider *)
Lemma size_lhanoi3_p n p1 p2 :
size_lhanoi3 `c[p1, n] `c[p2] =
if lrel p1 p2 then (3 ^ n).-1./2 else (3 ^ n).-1 * (p1 != p2).
Proof. by rewrite size_lhanoi3E size_app_lhanoi3_p. Qed.
Lemma gdist_lhanoi3_size n (c1 c2 : _ _ n) :
`d[c1, c2]_lmove = size_lhanoi3 c1 c2.
Proof.
apply/eqP; rewrite eqn_leq [size_lhanoi3 _ _]size_lhanoi3E.
rewrite gdist_path_le //=; last 2 first.
- by apply: path_lhanoi3.
- by apply: last_lhanoi3.
have /gpath_connect[p1 p1H] : connect lmove c1 c2 by apply: move_lconnect3.
rewrite (gpath_dist p1H) lhanoi3_min //; first by apply: gpath_path p1H.
by apply: gpath_last p1H.
Qed.
Lemma gdist_lhanoi3p n (p1 p2 : peg 3) :
`d[`c[p1, n], `c[p2, n]]_lmove =
if lrel p1 p2 then (3 ^ n).-1./2 else (3 ^ n).-1 * (p1 != p2).
Proof. by rewrite gdist_lhanoi3_size size_lhanoi3_p. Qed.
End LHanoi3.