summaryrefslogtreecommitdiff
path: root/plugins/ssr/ssrfun.v
blob: ac2c78249b5c05966b0b5387c70ae938231a7159 (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
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
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
(************************************************************************)
(*         *   The Coq Proof Assistant / The Coq Development Team       *)
(*  v      *   INRIA, CNRS and contributors - Copyright 1999-2018       *)
(* <O___,, *       (see CREDITS file for the list of authors)           *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

(* This file is (C) Copyright 2006-2015 Microsoft Corporation and Inria. *)

Require Import ssreflect.

(******************************************************************************)
(* This file contains the basic definitions and notations for working with    *)
(* functions. The definitions provide for:                                    *)
(*                                                                            *)
(* - Pair projections:                                                        *)
(*    p.1  == first element of a pair                                         *)
(*    p.2  == second element of a pair                                        *)
(*   These notations also apply to p : P /\ Q, via an and >-> pair coercion.  *)
(*                                                                            *)
(* - Simplifying functions, beta-reduced by /= and simpl:                     *)
(*           [fun : T => E] == constant function from type T that returns E   *)
(*             [fun x => E] == unary function                                 *)
(*         [fun x : T => E] == unary function with explicit domain type       *)
(*           [fun x y => E] == binary function                                *)
(*       [fun x y : T => E] == binary function with common domain type        *)
(*         [fun (x : T) y => E] \                                             *)
(* [fun (x : xT) (y : yT) => E] | == binary function with (some) explicit,    *)
(*         [fun x (y : T) => E] / independent domain types for each argument  *)
(*                                                                            *)
(* - Partial functions using option type:                                     *)
(*     oapp f d ox == if ox is Some x returns f x,        d otherwise         *)
(*      odflt d ox == if ox is Some x returns x,          d otherwise         *)
(*      obind f ox == if ox is Some x returns f x,        None otherwise      *)
(*       omap f ox == if ox is Some x returns Some (f x), None otherwise      *)
(*                                                                            *)
(* - Singleton types:                                                         *)
(*  all_equal_to x0 == x0 is the only value in its type, so any such value    *)
(*                     can be rewritten to x0.                                *)
(*                                                                            *)
(* - A generic wrapper type:                                                  *)
(*       wrapped T == the inductive type with values Wrap x for x : T.        *)
(*        unwrap w == the projection of w : wrapped T on T.                   *)
(*          wrap x == the canonical injection of x : T into wrapped T; it is  *)
(*                    equivalent to Wrap x, but is declared as a (default)    *)
(*                    Canonical Structure, which lets the Coq HO unification  *)
(*                    automatically expand x into unwrap (wrap x). The delta  *)
(*                    reduction of wrap x to Wrap can be exploited to         *)
(*                    introduce controlled nondeterminism in Canonical        *)
(*                    Structure inference, as in the implementation of        *)
(*                    the mxdirect predicate in matrix.v.                     *)
(*                                                                            *)
(* - Sigma types:                                                             *)
(*           tag w == the i of w : {i : I & T i}.                             *)
(*        tagged w == the T i component of w : {i : I & T i}.                 *)
(*      Tagged T x == the {i : I & T i} with component x : T i.               *)
(*          tag2 w == the i of w : {i : I & T i & U i}.                       *)
(*       tagged2 w == the T i component of w : {i : I & T i & U i}.           *)
(*      tagged2' w == the U i component of w : {i : I & T i & U i}.           *)
(* Tagged2 T U x y == the {i : I & T i} with components x : T i and y : U i.  *)
(*          sval u == the x of u : {x : T | P x}.                             *)
(*         s2val u == the x of u : {x : T | P x & Q x}.                       *)
(*   The properties of sval u, s2val u are given by lemmas svalP, s2valP, and *)
(*   s2valP'. We provide coercions sigT2 >-> sigT and sig2 >-> sig >-> sigT.  *)
(*   A suite of lemmas (all_sig, ...) let us skolemize sig, sig2, sigT, sigT2 *)
(*   and pair, e.g.,                                                          *)
(*     have /all_sig[f fP] (x : T): {y : U | P y} by ...                      *)
(*   yields an f : T -> U such that fP : forall x, P (f x).                   *)
(* - Identity functions:                                                      *)
(*    id           == NOTATION for the explicit identity function fun x => x. *)
(*    @id T        == notation for the explicit identity at type T.           *)
(*    idfun        == an expression with a head constant, convertible to id;  *)
(*                    idfun x simplifies to x.                                *)
(*    @idfun T     == the expression above, specialized to type T.            *)
(*    phant_id x y == the function type phantom _ x -> phantom _ y.           *)
(* *** In addition to their casual use in functional programming, identity    *)
(* functions are often used to trigger static unification as part of the      *)
(* construction of dependent Records and Structures. For example, if we need  *)
(* a structure sT over a type T, we take as arguments T, sT, and a "dummy"    *)
(* function T -> sort sT:                                                     *)
(*   Definition foo T sT & T -> sort sT := ...                                *)
(* We can avoid specifying sT directly by calling foo (@id T), or specify     *)
(* the call completely while still ensuring the consistency of T and sT, by   *)
(* calling @foo T sT idfun. The phant_id type allows us to extend this trick  *)
(* to non-Type canonical projections. It also allows us to sidestep           *)
(* dependent type constraints when building explicit records, e.g., given     *)
(*    Record r := R {x; y : T(x)}.                                            *)
(* if we need to build an r from a given y0 while inferring some x0, such     *)
(* that y0 : T(x0), we pose                                                   *)
(*    Definition mk_r .. y .. (x := ...) y' & phant_id y y' := R x y'.        *)
(* Calling @mk_r .. y0 .. id will cause Coq to use y' := y0, while checking   *)
(* the dependent type constraint y0 : T(x0).                                  *)
(*                                                                            *)
(* - Extensional equality for functions and relations (i.e. functions of two  *)
(*   arguments):                                                              *)
(*    f1 =1 f2      ==  f1 x is equal to f2 x for all x.                      *)
(*    f1 =1 f2 :> A ==    ... and f2 is explicitly typed.                     *)
(*    f1 =2 f2      ==  f1 x y is equal to f2 x y for all x y.                *)
(*    f1 =2 f2 :> A ==    ... and f2 is explicitly typed.                     *)
(*                                                                            *)
(* - Composition for total and partial functions:                             *)
(*            f^~ y == function f with second argument specialised to y,      *)
(*                     i.e., fun x => f x y                                   *)
(*                     CAVEAT: conditional (non-maximal) implicit arguments   *)
(*                     of f are NOT inserted in this context                  *)
(*            @^~ x == application at x, i.e., fun f => f x                   *)
(*          [eta f] == the explicit eta-expansion of f, i.e., fun x => f x    *)
(*                     CAVEAT: conditional (non-maximal) implicit arguments   *)
(*                     of f are NOT inserted in this context.                 *)
(*          fun=> v := the constant function fun _ => v.                      *)
(*        f1 \o f2  == composition of f1 and f2.                              *)
(*                     Note: (f1 \o f2) x simplifies to f1 (f2 x).            *)
(*         f1 \; f2 == categorical composition of f1 and f2. This expands to  *)
(*                     to f2 \o f1 and (f1 \; f2) x simplifies to f2 (f1 x).  *)
(*      pcomp f1 f2 == composition of partial functions f1 and f2.            *)
(*                                                                            *)
(*                                                                            *)
(* - Properties of functions:                                                 *)
(*      injective f <-> f is injective.                                       *)
(*       cancel f g <-> g is a left inverse of f / f is a right inverse of g. *)
(*      pcancel f g <-> g is a left inverse of f where g is partial.          *)
(*      ocancel f g <-> g is a left inverse of f where f is partial.          *)
(*      bijective f <-> f is bijective (has a left and right inverse).        *)
(*     involutive f <-> f is involutive.                                      *)
(*                                                                            *)
(* - Properties for operations.                                               *)
(*              left_id e op <-> e is a left identity for op (e op x = x).    *)
(*             right_id e op <-> e is a right identity for op (x op e = x).   *)
(*     left_inverse e inv op <-> inv is a left inverse for op wrt identity e, *)
(*                               i.e., (inv x) op x = e.                      *)
(*    right_inverse e inv op <-> inv is a right inverse for op wrt identity e *)
(*                               i.e., x op (i x) = e.                        *)
(*         self_inverse e op <-> each x is its own op-inverse (x op x = e).   *)
(*             idempotent op <-> op is idempotent for op (x op x = x).        *)
(*              associative op <-> op is associative, i.e.,                   *)
(*                               x op (y op z) = (x op y) op z.               *)
(*            commutative op <-> op is commutative (x op y = y op x).         *)
(*       left_commutative op <-> op is left commutative, i.e.,                *)
(*                                   x op (y op z) = y op (x op z).           *)
(*      right_commutative op <-> op is right commutative, i.e.,               *)
(*                                   (x op y) op z = (x op z) op y.           *)
(*            left_zero z op <-> z is a left zero for op (z op x = z).        *)
(*           right_zero z op <-> z is a right zero for op (x op z = z).       *)
(*  left_distributive op1 op2 <-> op1 distributes over op2 to the left:       *)
(*                             (x op2 y) op1 z = (x op1 z) op2 (y op1 z).     *)
(* right_distributive op1 op2 <-> op distributes over add to the right:       *)
(*                             x op1 (y op2 z) = (x op1 z) op2 (x op1 z).     *)
(*        interchange op1 op2 <-> op1 and op2 satisfy an interchange law:     *)
(*                        (x op2 y) op1 (z op2 t) = (x op1 z) op2 (y op1 t).  *)
(*  Note that interchange op op is a commutativity property.                  *)
(*         left_injective op <-> op is injective in its left argument:        *)
(*                             x op y = z op y -> x = z.                      *)
(*        right_injective op <-> op is injective in its right argument:       *)
(*                             x op y = x op z -> y = z.                      *)
(*          left_loop inv op <-> op, inv obey the inverse loop left axiom:    *)
(*                              (inv x) op (x op y) = y for all x, y, i.e.,   *)
(*                              op (inv x) is always a left inverse of op x   *)
(*      rev_left_loop inv op <-> op, inv obey the inverse loop reverse left   *)
(*                              axiom: x op ((inv x) op y) = y, for all x, y. *)
(*         right_loop inv op <-> op, inv obey the inverse loop right axiom:   *)
(*                              (x op y) op (inv y) = x for all x, y.         *)
(*     rev_right_loop inv op <-> op, inv obey the inverse loop reverse right  *)
(*                              axiom: (x op y) op (inv y) = x for all x, y.  *)
(*   Note that familiar "cancellation" identities like x + y - y = x or       *)
(* x - y + y = x are respectively instances of right_loop and rev_right_loop  *)
(* The corresponding lemmas will use the K and NK/VK suffixes, respectively.  *)
(*                                                                            *)
(* - Morphisms for functions and relations:                                   *)
(*    {morph f : x / a >-> r} <-> f is a morphism with respect to functions   *)
(*                               (fun x => a) and (fun x => r); if r == R[x], *)
(*                               this states that f a = R[f x] for all x.     *)
(*          {morph f : x / a} <-> f is a morphism with respect to the         *)
(*                               function expression (fun x => a). This is    *)
(*                               shorthand for {morph f : x / a >-> a}; note  *)
(*                               that the two instances of a are often        *)
(*                               interpreted at different types.              *)
(*  {morph f : x y / a >-> r} <-> f is a morphism with respect to functions   *)
(*                               (fun x y => a) and (fun x y => r).           *)
(*        {morph f : x y / a} <-> f is a morphism with respect to the         *)
(*                               function expression (fun x y => a).          *)
(*     {homo f : x / a >-> r} <-> f is a homomorphism with respect to the     *)
(*                               predicates (fun x => a) and (fun x => r);    *)
(*                               if r == R[x], this states that a -> R[f x]   *)
(*                               for all x.                                   *)
(*           {homo f : x / a} <-> f is a homomorphism with respect to the     *)
(*                               predicate expression (fun x => a).           *)
(*   {homo f : x y / a >-> r} <-> f is a homomorphism with respect to the     *)
(*                               relations (fun x y => a) and (fun x y => r). *)
(*         {homo f : x y / a} <-> f is a homomorphism with respect to the     *)
(*                               relation expression (fun x y => a).          *)
(*     {mono f : x / a >-> r} <-> f is monotone with respect to projectors    *)
(*                               (fun x => a) and (fun x => r); if r == R[x], *)
(*                               this states that R[f x] = a for all x.       *)
(*           {mono f : x / a} <-> f is monotone with respect to the projector *)
(*                               expression (fun x => a).                     *)
(*   {mono f : x y / a >-> r} <-> f is monotone with respect to relators      *)
(*                               (fun x y => a) and (fun x y => r).           *)
(*         {mono f : x y / a} <-> f is monotone with respect to the relator   *)
(*                               expression (fun x y => a).                   *)
(*                                                                            *)
(* The file also contains some basic lemmas for the above concepts.           *)
(* Lemmas relative to cancellation laws use some abbreviated suffixes:        *)
(*   K - a cancellation rule like esymK : cancel (@esym T x y) (@esym T y x). *)
(*  LR - a lemma moving an operation from the left hand side of a relation to *)
(*       the right hand side, like canLR: cancel g f -> x = g y -> f x = y.   *)
(*  RL - a lemma moving an operation from the right to the left, e.g., canRL. *)
(* Beware that the LR and RL orientations refer to an "apply" (back chaining) *)
(* usage; when using the same lemmas with "have" or "move" (forward chaining) *)
(* the directions will be reversed!.                                          *)
(******************************************************************************)

Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.

Delimit Scope fun_scope with FUN.
Open Scope fun_scope.

(* Notations for argument transpose *)
Notation "f ^~ y" := (fun x => f x y)
  (at level 10, y at level 8, no associativity, format "f ^~  y") : fun_scope.
Notation "@^~ x" := (fun f => f x)
  (at level 10, x at level 8, no associativity, format "@^~  x") : fun_scope.

Delimit Scope pair_scope with PAIR.
Open Scope pair_scope.

(* Notations for pair/conjunction projections *)
Notation "p .1" := (fst p)
  (at level 2, left associativity, format "p .1") : pair_scope.
Notation "p .2" := (snd p)
  (at level 2, left associativity, format "p .2") : pair_scope.

Coercion pair_of_and P Q (PandQ : P /\ Q) := (proj1 PandQ, proj2 PandQ).

Definition all_pair I T U (w : forall i : I, T i * U i) :=
  (fun i => (w i).1, fun i => (w i).2).

(* Complements on the option type constructor, used below to  *)
(* encode partial functions.                                  *)

Module Option.

Definition apply aT rT (f : aT -> rT) x u := if u is Some y then f y else x.

Definition default T := apply (fun x : T => x).

Definition bind aT rT (f : aT -> option rT) := apply f None.

Definition map aT rT (f : aT -> rT) := bind (fun x => Some (f x)).

End Option.

Notation oapp := Option.apply.
Notation odflt := Option.default.
Notation obind := Option.bind.
Notation omap := Option.map.
Notation some := (@Some _) (only parsing).

(* Shorthand for some basic equality lemmas. *)

Notation erefl := refl_equal.
Notation ecast i T e x := (let: erefl in _ = i := e return T in x).
Definition esym := sym_eq.
Definition nesym := sym_not_eq.
Definition etrans := trans_eq.
Definition congr1 := f_equal.
Definition congr2 := f_equal2.
(* Force at least one implicit when used as a view. *)
Prenex Implicits esym nesym.

(* A predicate for singleton types.                                           *)
Definition all_equal_to T (x0 : T) := forall x, unkeyed x = x0.

Lemma unitE : all_equal_to tt. Proof. by case. Qed.

(* A generic wrapper type *)

Structure wrapped T := Wrap {unwrap : T}.
Canonical wrap T x := @Wrap T x.

Prenex Implicits unwrap wrap Wrap.

(* Syntax for defining auxiliary recursive function.          *)
(*  Usage:                                                    *)
(* Section FooDefinition.                                     *)
(* Variables (g1 : T1) (g2 : T2).  (globals)                  *)
(* Fixoint foo_auxiliary (a3 : T3) ... :=                     *)
(*        body, using [rec e3, ...] for recursive calls       *)
(* where "[ 'rec' a3 , a4 , ... ]" := foo_auxiliary.          *)
(* Definition foo x y .. := [rec e1, ...].                    *)
(* + proofs about foo                                         *)
(* End FooDefinition.                                         *)

Reserved Notation "[ 'rec' a0 ]"
  (at level 0, format "[ 'rec'  a0 ]").
Reserved Notation "[ 'rec' a0 , a1 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ,  a2 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 , a5 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ,  a5 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 , a5 , a6 ]"
  (at level 0, format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ,  a5 ,  a6 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 ]"
  (at level 0,
  format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ,  a5 ,  a6 ,  a7 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 ]"
  (at level 0,
  format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ,  a5 ,  a6 ,  a7 ,  a8 ]").
Reserved Notation "[ 'rec' a0 , a1 , a2 , a3 , a4 , a5 , a6 , a7 , a8 , a9 ]"
  (at level 0,
  format "[ 'rec'  a0 ,  a1 ,  a2 ,  a3 ,  a4 ,  a5 ,  a6 ,  a7 ,  a8 ,  a9 ]").

(* Definitions and notation for explicit functions with simplification,     *)
(* i.e., which simpl and /= beta expand (this is complementary to nosimpl). *)

Section SimplFun.

Variables aT rT : Type.

CoInductive simpl_fun := SimplFun of aT -> rT.

Definition fun_of_simpl f := fun x => let: SimplFun lam := f in lam x.

Coercion fun_of_simpl : simpl_fun >-> Funclass.

End SimplFun.

Notation "[ 'fun' : T => E ]" := (SimplFun (fun _ : T => E))
  (at level 0,
   format "'[hv' [ 'fun' :  T  => '/ '  E ] ']'") : fun_scope.

Notation "[ 'fun' x => E ]" := (SimplFun (fun x => E))
  (at level 0, x ident,
   format "'[hv' [ 'fun'  x  => '/ '  E ] ']'") : fun_scope.

Notation "[ 'fun' x : T => E ]" := (SimplFun (fun x : T => E))
  (at level 0, x ident, only parsing) : fun_scope.

Notation "[ 'fun' x y => E ]" := (fun x => [fun y => E])
  (at level 0, x ident, y ident,
   format "'[hv' [ 'fun'  x  y  => '/ '  E ] ']'") : fun_scope.

Notation "[ 'fun' x y : T => E ]" := (fun x : T => [fun y : T => E])
  (at level 0, x ident, y ident, only parsing) : fun_scope.

Notation "[ 'fun' ( x : T ) y => E ]" := (fun x : T => [fun y => E])
  (at level 0, x ident, y ident, only parsing) : fun_scope.

Notation "[ 'fun' x ( y : T ) => E ]" := (fun x => [fun y : T => E])
  (at level 0, x ident, y ident, only parsing) : fun_scope.

Notation "[ 'fun' ( x : xT ) ( y : yT ) => E ]" :=
    (fun x : xT => [fun y : yT => E])
  (at level 0, x ident, y ident, only parsing) : fun_scope.

(* For delta functions in eqtype.v. *)
Definition SimplFunDelta aT rT (f : aT -> aT -> rT) := [fun z => f z z].

(* Extensional equality, for unary and binary functions, including syntactic  *)
(* sugar.                                                                     *)

Section ExtensionalEquality.

Variables A B C : Type.

Definition eqfun (f g : B -> A) : Prop := forall x, f x = g x.

Definition eqrel (r s : C -> B -> A) : Prop := forall x y, r x y = s x y.

Lemma frefl f : eqfun f f. Proof. by []. Qed.
Lemma fsym f g : eqfun f g -> eqfun g f. Proof. by move=> eq_fg x. Qed.

Lemma ftrans f g h : eqfun f g -> eqfun g h -> eqfun f h.
Proof. by move=> eq_fg eq_gh x; rewrite eq_fg. Qed.

Lemma rrefl r : eqrel r r. Proof. by []. Qed.

End ExtensionalEquality.

Typeclasses Opaque eqfun.
Typeclasses Opaque eqrel.

Hint Resolve frefl rrefl.

Notation "f1 =1 f2" := (eqfun f1 f2)
  (at level 70, no associativity) : fun_scope.
Notation "f1 =1 f2 :> A" := (f1 =1 (f2 : A))
  (at level 70, f2 at next level, A at level 90) : fun_scope.
Notation "f1 =2 f2" := (eqrel f1 f2)
  (at level 70, no associativity) : fun_scope.
Notation "f1 =2 f2 :> A" := (f1 =2 (f2 : A))
  (at level 70, f2 at next level, A at level 90) : fun_scope.

Section Composition.

Variables A B C : Type.

Definition funcomp u (f : B -> A) (g : C -> B) x := let: tt := u in f (g x).
Definition catcomp u g f := funcomp u f g.
Local Notation comp := (funcomp tt).

Definition pcomp (f : B -> option A) (g : C -> option B) x := obind f (g x).

Lemma eq_comp f f' g g' : f =1 f' -> g =1 g' -> comp f g =1 comp f' g'.
Proof. by move=> eq_ff' eq_gg' x; rewrite /= eq_gg' eq_ff'. Qed.

End Composition.

Notation comp := (funcomp tt).
Notation "@ 'comp'" := (fun A B C => @funcomp A B C tt).
Notation "f1 \o f2" := (comp f1 f2)
  (at level 50, format "f1  \o '/ '  f2") : fun_scope.
Notation "f1 \; f2" := (catcomp tt f1 f2)
  (at level 60, right associativity, format "f1  \; '/ '  f2") : fun_scope.

Notation "[ 'eta' f ]" := (fun x => f x)
  (at level 0, format "[ 'eta'  f ]") : fun_scope.

Notation "'fun' => E" := (fun _ => E) (at level 200, only parsing) : fun_scope.

Notation id := (fun x => x).
Notation "@ 'id' T" := (fun x : T => x)
  (at level 10, T at level 8, only parsing) : fun_scope.

Definition id_head T u x : T := let: tt := u in x.
Definition explicit_id_key := tt.
Notation idfun := (id_head tt).
Notation "@ 'idfun' T " := (@id_head T explicit_id_key)
  (at level 10, T at level 8, format "@ 'idfun'  T") : fun_scope.

Definition phant_id T1 T2 v1 v2 := phantom T1 v1 -> phantom T2 v2.

(* Strong sigma types. *)

Section Tag.

Variables (I : Type) (i : I) (T_ U_ : I -> Type).

Definition tag := projT1.
Definition tagged : forall w, T_(tag w) := @projT2 I [eta T_].
Definition Tagged x := @existT I [eta T_] i x.

Definition tag2 (w : @sigT2 I T_ U_) := let: existT2 _ _ i _ _ := w in i.
Definition tagged2 w : T_(tag2 w) := let: existT2 _ _ _ x _ := w in x.
Definition tagged2' w : U_(tag2 w) := let: existT2 _ _ _ _ y := w in y.
Definition Tagged2 x y := @existT2 I [eta T_] [eta U_] i x y.

End Tag.

Arguments Tagged [I i].
Arguments Tagged2 [I i].
Prenex Implicits tag tagged Tagged tag2 tagged2 tagged2' Tagged2.

Coercion tag_of_tag2 I T_ U_ (w : @sigT2 I T_ U_) :=
  Tagged (fun i => T_ i * U_ i)%type (tagged2 w, tagged2' w).

Lemma all_tag I T U :
   (forall x : I, {y : T x & U x y}) ->
  {f : forall x, T x & forall x, U x (f x)}.
Proof. by move=> fP; exists (fun x => tag (fP x)) => x; case: (fP x). Qed.

Lemma all_tag2 I T U V :
    (forall i : I, {y : T i & U i y & V i y}) ->
  {f : forall i, T i & forall i, U i (f i) & forall i, V i (f i)}.
Proof. by case/all_tag=> f /all_pair[]; exists f. Qed.

(* Refinement types. *)

(* Prenex Implicits and renaming. *)
Notation sval := (@proj1_sig _ _).
Notation "@ 'sval'" := (@proj1_sig) (at level 10, format "@ 'sval'").

Section Sig.

Variables (T : Type) (P Q : T -> Prop).

Lemma svalP (u : sig P) : P (sval u). Proof. by case: u. Qed.

Definition s2val (u : sig2 P Q) := let: exist2 _ _ x _ _ := u in x.

Lemma s2valP u : P (s2val u). Proof. by case: u. Qed.

Lemma s2valP' u : Q (s2val u). Proof. by case: u. Qed.

End Sig.

Prenex Implicits svalP s2val s2valP s2valP'.

Coercion tag_of_sig I P (u : @sig I P) := Tagged P (svalP u).

Coercion sig_of_sig2 I P Q (u : @sig2 I P Q) :=
  exist (fun i => P i /\ Q i) (s2val u) (conj (s2valP u) (s2valP' u)).

Lemma all_sig I T P :
    (forall x : I, {y : T x | P x y}) ->
  {f : forall x, T x | forall x, P x (f x)}.
Proof. by case/all_tag=> f; exists f. Qed.

Lemma all_sig2 I T P Q :
    (forall x : I, {y : T x | P x y & Q x y}) ->
  {f : forall x, T x | forall x, P x (f x) & forall x, Q x (f x)}.
Proof. by case/all_sig=> f /all_pair[]; exists f. Qed.

Section Morphism.

Variables (aT rT sT : Type) (f : aT -> rT).

(* Morphism property for unary and binary functions *)
Definition morphism_1 aF rF := forall x, f (aF x) = rF (f x).
Definition morphism_2 aOp rOp := forall x y, f (aOp x y) = rOp (f x) (f y).

(* Homomorphism property for unary and binary relations *)
Definition homomorphism_1 (aP rP : _ -> Prop) := forall x, aP x -> rP (f x).
Definition homomorphism_2 (aR rR : _ -> _ -> Prop) :=
  forall x y, aR x y -> rR (f x) (f y).

(* Stability property for unary and binary relations *)
Definition monomorphism_1 (aP rP : _ -> sT) := forall x, rP (f x) = aP x.
Definition monomorphism_2 (aR rR : _ -> _ -> sT) :=
  forall x y, rR (f x) (f y) = aR x y.

End Morphism.

Notation "{ 'morph' f : x / a >-> r }" :=
  (morphism_1 f (fun x => a) (fun x => r))
  (at level 0, f at level 99, x ident,
   format "{ 'morph'  f  :  x  /  a  >->  r }") : type_scope.

Notation "{ 'morph' f : x / a }" :=
  (morphism_1 f (fun x => a) (fun x => a))
  (at level 0, f at level 99, x ident,
   format "{ 'morph'  f  :  x  /  a }") : type_scope.

Notation "{ 'morph' f : x y / a >-> r }" :=
  (morphism_2 f (fun x y => a) (fun x y => r))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'morph'  f  :  x  y  /  a  >->  r }") : type_scope.

Notation "{ 'morph' f : x y / a }" :=
  (morphism_2 f (fun x y => a) (fun x y => a))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'morph'  f  :  x  y  /  a }") : type_scope.

Notation "{ 'homo' f : x / a >-> r }" :=
  (homomorphism_1 f (fun x => a) (fun x => r))
  (at level 0, f at level 99, x ident,
   format "{ 'homo'  f  :  x  /  a  >->  r }") : type_scope.

Notation "{ 'homo' f : x / a }" :=
  (homomorphism_1 f (fun x => a) (fun x => a))
  (at level 0, f at level 99, x ident,
   format "{ 'homo'  f  :  x  /  a }") : type_scope.

Notation "{ 'homo' f : x y / a >-> r }" :=
  (homomorphism_2 f (fun x y => a) (fun x y => r))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'homo'  f  :  x  y  /  a  >->  r }") : type_scope.

Notation "{ 'homo' f : x y / a }" :=
  (homomorphism_2 f (fun x y => a) (fun x y => a))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'homo'  f  :  x  y  /  a }") : type_scope.

Notation "{ 'homo' f : x y /~ a }" :=
  (homomorphism_2 f (fun y x => a) (fun x y => a))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'homo'  f  :  x  y  /~  a }") : type_scope.

Notation "{ 'mono' f : x / a >-> r }" :=
  (monomorphism_1 f (fun x => a) (fun x => r))
  (at level 0, f at level 99, x ident,
   format "{ 'mono'  f  :  x  /  a  >->  r }") : type_scope.

Notation "{ 'mono' f : x / a }" :=
  (monomorphism_1 f (fun x => a) (fun x => a))
  (at level 0, f at level 99, x ident,
   format "{ 'mono'  f  :  x  /  a }") : type_scope.

Notation "{ 'mono' f : x y / a >-> r }" :=
  (monomorphism_2 f (fun x y => a) (fun x y => r))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'mono'  f  :  x  y  /  a  >->  r }") : type_scope.

Notation "{ 'mono' f : x y / a }" :=
  (monomorphism_2 f (fun x y => a) (fun x y => a))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'mono'  f  :  x  y  /  a }") : type_scope.

Notation "{ 'mono' f : x y /~ a }" :=
  (monomorphism_2 f (fun y x => a) (fun x y => a))
  (at level 0, f at level 99, x ident, y ident,
   format "{ 'mono'  f  :  x  y  /~  a }") : type_scope.

(* In an intuitionistic setting, we have two degrees of injectivity. The     *)
(* weaker one gives only simplification, and the strong one provides a left  *)
(* inverse (we show in `fintype' that they coincide for finite types).       *)
(* We also define an intermediate version where the left inverse is only a   *)
(* partial function.                                                         *)

Section Injections.

(* rT must come first so we can use @ to mitigate the Coq 1st order   *)
(* unification bug (e..g., Coq can't infer rT from a "cancel" lemma). *)
Variables (rT aT : Type) (f : aT -> rT).

Definition injective := forall x1 x2, f x1 = f x2 -> x1 = x2.

Definition cancel g := forall x, g (f x) = x.

Definition pcancel g := forall x, g (f x) = Some x.

Definition ocancel (g : aT -> option rT) h := forall x, oapp h x (g x) = x.

Lemma can_pcan g : cancel g -> pcancel (fun y => Some (g y)).
Proof. by move=> fK x; congr (Some _). Qed.

Lemma pcan_inj g : pcancel g -> injective.
Proof. by move=> fK x y /(congr1 g); rewrite !fK => [[]]. Qed.

Lemma can_inj g : cancel g -> injective.
Proof. by move/can_pcan; apply: pcan_inj. Qed.

Lemma canLR g x y : cancel g -> x = f y -> g x = y.
Proof. by move=> fK ->. Qed.

Lemma canRL g x y : cancel g -> f x = y -> x = g y.
Proof. by move=> fK <-. Qed.

End Injections.

Lemma Some_inj {T} : injective (@Some T). Proof. by move=> x y []. Qed.

(* Force implicits to use as a view. *)
Prenex Implicits Some_inj.

(* cancellation lemmas for dependent type casts.                             *)
Lemma esymK T x y : cancel (@esym T x y) (@esym T y x).
Proof. by case: y /. Qed.

Lemma etrans_id T x y (eqxy : x = y :> T) : etrans (erefl x) eqxy = eqxy.
Proof. by case: y / eqxy. Qed.

Section InjectionsTheory.

Variables (A B C : Type) (f g : B -> A) (h : C -> B).

Lemma inj_id : injective (@id A).
Proof. by []. Qed.

Lemma inj_can_sym f' : cancel f f' -> injective f' -> cancel f' f.
Proof. by move=> fK injf' x; apply: injf'. Qed.

Lemma inj_comp : injective f -> injective h -> injective (f \o h).
Proof. by move=> injf injh x y /injf; apply: injh. Qed.

Lemma can_comp f' h' : cancel f f' -> cancel h h' -> cancel (f \o h) (h' \o f').
Proof. by move=> fK hK x; rewrite /= fK hK. Qed.

Lemma pcan_pcomp f' h' :
  pcancel f f' -> pcancel h h' -> pcancel (f \o h) (pcomp h' f').
Proof. by move=> fK hK x; rewrite /pcomp fK /= hK. Qed.

Lemma eq_inj : injective f -> f =1 g -> injective g.
Proof. by move=> injf eqfg x y; rewrite -2!eqfg; apply: injf. Qed.

Lemma eq_can f' g' : cancel f f' -> f =1 g -> f' =1 g' -> cancel g g'.
Proof. by move=> fK eqfg eqfg' x; rewrite -eqfg -eqfg'. Qed.

Lemma inj_can_eq f' : cancel f f' -> injective f' -> cancel g f' -> f =1 g.
Proof. by move=> fK injf' gK x; apply: injf'; rewrite fK. Qed.

End InjectionsTheory.

Section Bijections.

Variables (A B : Type) (f : B -> A).

CoInductive bijective : Prop := Bijective g of cancel f g & cancel g f.

Hypothesis bijf : bijective.

Lemma bij_inj : injective f.
Proof. by case: bijf => g fK _; apply: can_inj fK. Qed.

Lemma bij_can_sym f' : cancel f' f <-> cancel f f'.
Proof.
split=> fK; first exact: inj_can_sym fK bij_inj.
by case: bijf => h _ hK x; rewrite -[x]hK fK.
Qed.

Lemma bij_can_eq f' f'' : cancel f f' -> cancel f f'' -> f' =1 f''.
Proof.
by move=> fK fK'; apply: (inj_can_eq _ bij_inj); apply/bij_can_sym.
Qed.

End Bijections.

Section BijectionsTheory.

Variables (A B C : Type) (f : B -> A) (h : C -> B).

Lemma eq_bij : bijective f -> forall g, f =1 g -> bijective g.
Proof. by case=> f' fK f'K g eqfg; exists f'; eapply eq_can; eauto. Qed.

Lemma bij_comp : bijective f -> bijective h -> bijective (f \o h).
Proof.
by move=> [f' fK f'K] [h' hK h'K]; exists (h' \o f'); apply: can_comp; auto.
Qed.

Lemma bij_can_bij : bijective f -> forall f', cancel f f' -> bijective f'.
Proof. by move=> bijf; exists f; first by apply/(bij_can_sym bijf). Qed.

End BijectionsTheory.

Section Involutions.

Variables (A : Type) (f : A -> A).

Definition involutive := cancel f f.

Hypothesis Hf : involutive.

Lemma inv_inj : injective f. Proof. exact: can_inj Hf. Qed.
Lemma inv_bij : bijective f. Proof. by exists f. Qed.

End Involutions.

Section OperationProperties.

Variables S T R : Type.

Section SopTisR.
Implicit Type op :  S -> T -> R.
Definition left_inverse e inv op := forall x, op (inv x) x = e.
Definition right_inverse e inv op := forall x, op x (inv x) = e.
Definition left_injective op := forall x, injective (op^~ x).
Definition right_injective op := forall y, injective (op y).
End SopTisR.


Section SopTisS.
Implicit Type op :  S -> T -> S.
Definition right_id e op := forall x, op x e = x.
Definition left_zero z op := forall x, op z x = z.
Definition right_commutative op := forall x y z, op (op x y) z = op (op x z) y.
Definition left_distributive op add :=
  forall x y z, op (add x y) z = add (op x z) (op y z).
Definition right_loop inv op := forall y, cancel (op^~ y) (op^~ (inv y)).
Definition rev_right_loop inv op := forall y, cancel (op^~ (inv y)) (op^~ y).
End SopTisS.

Section SopTisT.
Implicit Type op :  S -> T -> T.
Definition left_id e op := forall x, op e x = x.
Definition right_zero z op := forall x, op x z = z.
Definition left_commutative op := forall x y z, op x (op y z) = op y (op x z).
Definition right_distributive op add :=
  forall x y z, op x (add y z) = add (op x y) (op x z).
Definition left_loop inv op := forall x, cancel (op x) (op (inv x)).
Definition rev_left_loop inv op := forall x, cancel (op (inv x)) (op x).
End SopTisT.

Section SopSisT.
Implicit Type op :  S -> S -> T.
Definition self_inverse e op := forall x, op x x = e.
Definition commutative op := forall x y, op x y = op y x.
End SopSisT.

Section SopSisS.
Implicit Type op :  S -> S -> S.
Definition idempotent op := forall x, op x x = x.
Definition associative op := forall x y z, op x (op y z) = op (op x y) z.
Definition interchange op1 op2 :=
  forall x y z t, op1 (op2 x y) (op2 z t) = op2 (op1 x z) (op1 y t).
End SopSisS.

End OperationProperties.