aboutsummaryrefslogtreecommitdiffhomepage
path: root/lego/root2.l
blob: 5712b129ce665e8a7dea1dca813e31a65ae41263 (plain)
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
(************************************************************************
   Conor McBride's proof that 2 has no rational root.

   This proof is accepted by LEGO version 1.3.1 with its standard library.
*************************************************************************)

Make lib_nat; (* loading basic logic, nat, plus, times etc *)

(* note, plus and times are defined by recursion on their first arg *)


(************************************************************************
   Alternative eliminators for nat

   LEGO's induction tactic figures out which induction principle to use
   by looking at the type of the variable on which we're doing induction.
   Consequently, we can persuade the tactic to use an alternative induction
   principle if we alias the type.

   Nat_elim is just the case analysis principle for natural numbers---the
   same as the induction principle except that there's no inductive hypothesis
   in the step case. It's intended to be used in combination with...

   ...NAT_elim, which performs no case analysis but says you can have an
   inductive hypothesis for any smaller value, where y is smaller than
   suc (plus x y). This is `well-founded induction' for the < relation,
   but expressed more concretely.

   The effect is very similar to that of `Case' and `Fix' in Coq.
************************************************************************)

[Nat = nat];
[NAT = Nat];

(* case analysis: just a weakening of induction *)

Goal Nat_elim : {Phi:nat->Type}
                {phiz:Phi zero}
                {phis:{n:Nat}Phi (suc n)}
                {n:Nat}Phi n;
intros ___;
  Expand Nat; Induction n;
    Immed;
    intros; Immed;
Save;

(* suc-plus guarded induction: the usual proof *)

Goal NAT_elim :
     {Phi:nat->Type}
     {phi:{n:Nat}
          {ih:{x,y|Nat}(Eq n (suc (plus x y)))->Phi y}
          Phi n}
     {n:NAT}Phi n;
intros Phi phi n';
(* claim that we can build the hypothesis collector for each n *)
Claim {n:nat}{x,y|Nat}(Eq n (suc (plus x y)))->Phi y;
(* use phi on the claimed collector *)
Refine phi n' (?+1 n');
(* now build the collector by one-step induction *)
  Induction n;
    Qnify; (* nothing to collect for zero *)
    intros n nhyp;
      Induction x; (* case analysis on the slack *)
        Qnify;
          Refine phi;  (* if the bound is tight, use phi to         *)
            Immed;     (* generate the new member of the collection *)
        Qnify;
          Refine nhyp; (* otherwise, we've already collected it *)
            Immed;
Save;


(***************************************************************************
   Equational laws governing plus and times:
   some of these are doubtless in the library, but it takes longer to
   remember their names than to prove them again.
****************************************************************************)

Goal plusZero : {x:nat}Eq (plus x zero) x;
Induction x;
  Refine Eq_refl;
  intros;
    Refine Eq_resp suc;
      Immed;
Save;

Goal plusSuc : {x,y:nat}Eq (plus x (suc y)) (suc (plus x y));
Induction x;
  intros; Refine Eq_refl;
  intros;
    Refine Eq_resp suc;
      Immed;
Save;

Goal plusAssoc : {x,y,z:nat}Eq (plus (plus x y) z) (plus x (plus y z));
Induction x;
  intros; Refine Eq_refl;
  intros;
    Refine Eq_resp suc;
      Immed;
Save;

Goal plusComm : {x,y:nat}Eq (plus x y) (plus y x);
Induction y;
  Refine plusZero;
  intros y yh x;
    Refine Eq_trans (plusSuc x y);
      Refine Eq_resp suc;
        Immed;
Save;

Goal plusCommA : {x,y,z:nat}Eq (plus x (plus y z)) (plus y (plus x z));
intros;
  Refine Eq_trans ? (plusAssoc ???);
    Refine Eq_trans (Eq_sym (plusAssoc ???));
      Refine Eq_resp ([w:nat]plus w z);
        Refine plusComm;
Save;

Goal timesZero : {x:nat}Eq (times x zero) zero;
Induction x;
  Refine Eq_refl;
    intros;
      Immed;
Save;

Goal timesSuc : {x,y:nat}Eq (times x (suc y)) (plus x (times x y));
Induction x;
  intros; Refine Eq_refl;
  intros x xh y;
    Equiv Eq (suc (plus y (times x (suc y)))) ?;
      Equiv Eq ? (suc (plus x (plus y (times x y))));
        Refine Eq_resp;
          Qrepl xh y;
            Refine plusCommA;
Save;

Goal timesComm : {x,y:nat}Eq (times x y) (times y x);
Induction y;
  Refine timesZero;
  intros y yh x;
    Refine Eq_trans (timesSuc ??);
      Refine Eq_resp (plus x);
        Immed;
Save;

Goal timesDistL : {x,y,z:nat}Eq (times (plus x y) z)
                                (plus (times x z) (times y z));
Induction x;
  intros; Refine Eq_refl;
    intros x xh y z;
      Refine Eq_trans (Eq_resp (plus z) (xh y z));
        Refine Eq_sym (plusAssoc ???);
Save;

Goal timesAssoc : {x,y,z:nat}Eq (times (times x y) z) (times x (times y z));
Induction x;
  intros; Refine Eq_refl;
  intros x xh y z;
    Refine Eq_trans (timesDistL ???);
      Refine Eq_resp (plus (times y z));
        Immed;
Save;

(**********************************************************************
   Inversion principles for equations governing plus and times:
   these aren't in the library, at least not in this form.
***********************************************************************)

[Phi|Type]; (* Inversion principles are polymorphic in any goal *)

Goal plusCancelL : {y,z|nat}{phi:{q':Eq y z}Phi}{x|nat}
                   {q:Eq (plus x y) (plus x z)}Phi;
intros ___;
Induction x;
  intros;
    Refine phi q;
  intros x xh; Qnify;
    Refine xh;
      Immed;
Save;

Goal timesToZero : {a,b|Nat}
                   {phiL:(Eq a zero)->Phi}
                   {phiR:(Eq b zero)->Phi}
                   {tz:Eq (times a b) zero}
                   Phi;
Induction a;
  intros; Refine phiL (Eq_refl ?);
  intros a;
    Induction b;
      intros; Refine phiR (Eq_refl ?);
      Qnify;
Save;

Goal timesToNonZero : {x,y|nat}
                      {phi:{x',y'|nat}(Eq x (suc x'))->(Eq y (suc y'))->Phi}
                      {z|nat}{q:Eq (times x y) (suc z)}Phi;
Induction x;
  Qnify;
  intros x xh;
    Induction y;
      intros __; Qrepl timesZero (suc x); Qnify;
      intros;
        [EQR=Eq_refl]; Refine phi Then Immed;
Save;

(* I actually want plusDivisionL, but plusDivisionR is easier to prove,
   because here we do induction where times does computation. *)
Goal plusDivisionR : {b|nat}{a,x,c|Nat}
                     {phi:{c'|nat}(Eq (times c' (suc x)) c)->
                                  (Eq a (plus b c'))->Phi}
                     {q:Eq (times a (suc x)) (plus (times b (suc x)) c)}
                     Phi;
Induction b;
  intros _____; Refine phi;
    Immed;
    Refine Eq_refl;
  intros b bh;
    Induction a;
      Qnify;
      intros a x c phi;
        Qrepl plusAssoc (suc x) (times b (suc x)) c;
          Refine plusCancelL;
            Refine bh;
              intros c q1 q2; Refine phi q1;
                Refine Eq_resp ? q2;
Save;

(* A bit of timesComm gives us the one we really need. *)
Goal plusDivisionL : {b|nat}{a,x,c|Nat}
                     {phi:{c'|nat}(Eq (times (suc x) c') c)->
                                  (Eq a (plus b c'))->Phi}
                     {q:Eq (times (suc x) a) (plus (times (suc x) b) c)}
                     Phi;
intros _____;
  Qrepl timesComm (suc x) a; Qrepl timesComm (suc x) b;
    Refine plusDivisionR;
      intros c'; Qrepl timesComm c' (suc x);
        Immed;
Save;

Discharge Phi;


(**************************************************************************
   Definition of primality:

   This choice of definition makes primality easy to exploit
   (especially as it's presented as an inversion principle), but hard to
   establish.
***************************************************************************)

[Prime = [p:nat]
         {a|NAT}{b,x|Nat}{Phi|Prop}
         {q:Eq (times p x) (times a b)}
         {phiL:{a':nat}
               (Eq a (times p a'))->(Eq x (times a' b))->Phi}
         {phiR:{b':nat}
               (Eq b (times p b'))->(Eq x (times a b'))->Phi}
         Phi
];


(**************************************************************************
   Proof that 2 is Prime. Nontrivial because of the above definition.
   Manageable because 1 is the only number between 0 and 2.
***************************************************************************)

Goal doublePlusGood : {x,y:nat}Eq (times (suc (suc x)) y)
                                  (plus (times two y) (times x y));
intros __;
  Refine Eq_trans ? (Eq_sym (plusAssoc ???));
    Refine Eq_resp (plus y);
      Refine Eq_trans ? (Eq_sym (plusAssoc ???));
        Refine Eq_refl;
Save;

Goal twoPrime : Prime two;
Expand Prime;
  Induction a;
    Induction n;
      intros useless b x _;
        Refine timesToZero Then Expand Nat Then Qnify;
                            (* Qnify needs to know it's a nat *)
          intros; Refine phiL;
            Refine +1 (Eq_sym (timesZero ?));
            Refine Eq_refl;
      Induction n;
        intros useless b x _;
          Qrepl plusZero b;
            intros; Refine phiR;
              Refine +1 Eq_sym q;
              Refine Eq_sym (plusZero x);
        intros n nhyp b x _;
          Qrepl doublePlusGood n b;
            Refine plusDivisionL;
              intros c q1 q2; Qrepl q2; intros __;
                Refine nhyp|one (Eq_refl ?) q1;
                  intros a' q3 q4; Refine phiL (suc a');
                    Refine Eq_resp suc;
                      Refine Eq_trans ? (Eq_sym (plusSuc ??));
                        Refine Eq_resp ? q3;
                    Refine Eq_resp (plus b) q4;
                  intros b' q3 q4; Refine phiR b';
                    Immed;
                    Qrepl q3; Qrepl q4;
                      Refine Eq_sym (doublePlusGood ??);
Save;


(**************************************************************************
   Now the proof that primes (>=2) have no rational root. It's the
   classic `minimal counterexample' proof unwound as an induction: we
   apply the inductive hypothesis to the smaller counterexample we
   construct.
***************************************************************************)

[pm2:nat]
[p=suc (suc pm2)] (* p is at least 2 *)
[Pp:Prime p];

Goal noRatRoot : {b|NAT}{a|Nat}{q:Eq (times p (times a a)) (times b b)}
                          and (Eq a zero) (Eq b zero);
Induction b;
  Induction n; (* if b is zero, so is a, and the result holds *)
    intros useless;
      intros a;
        Refine timesToZero;
          Expand Nat; Qnify;
          Refine timesToZero; Refine ?+1;
            intros; Refine pair; Immed; Refine Eq_refl;
    intros b hyp a q; (* otherwise, build a smaller counterexample *)
      Refine Pp q; (* use primality once *)
        Refine cut ?+1; (* standard technique for exploiting symmetry *)
          intros H a' aq1 aq2; Refine H;
            Immed;
            Refine Eq_trans aq2;
              Refine timesComm;
        intros c bq; Qrepl bq; Qrepl timesAssoc p c c;
          Refine timesToNonZero ? (Eq_sym bq); (* need c to be nonzero *)
            intros p' c' dull cq; Qrepl cq; intros q2;
              Refine Pp (Eq_sym q2); (* use primality twice *)
                Refine cut ?+1; (* symmetry again *)
                  intros H a' aq1 aq2; Refine H;
                    Immed;
                    Refine Eq_trans aq2;
                      Refine timesComm;
                intros d aq; Qrepl aq; Qrepl timesAssoc p d d;
                  intros q3;
                    Refine hyp ? (Eq_sym q3); (* now use ind hyp *)
                      Next +2; Expand NAT Nat; Qnify; (* trivial solution *)
                      Next +1; (* show induction was properly guarded *)
                        Refine Eq_trans bq; Expand p; Qrepl cq;
                          Refine plusComm;
Save;

Discharge pm2;

(**********************************************************************
   Putting it all together
***********************************************************************)

[noRatRootTwo = noRatRoot zero twoPrime
  : {b|nat}{a|nat}(Eq (times two (times a a)) (times b b))->
     (Eq a zero /\ Eq b zero)];