Module EnumDef : EnumSig.
Definition enum cT := mixin_enum (class cT).
Definition enumDef := erefl enum.
End EnumDef.
Notation enum := EnumDef.enum.
End Finite.
Export Finite.Exports.
Canonical finEnum_unlock := Unlockable Finite.EnumDef.enumDef.
(* Workaround for the silly syntactic uniformity restriction on coercions;    *)
(* this avoids a cross-dependency between finset.v and prime.v for the        *)
(* definition of the \pi(A) notation.                                         *)
Definition fin_pred_sort (T : finType) (pT : predType T) := pred_sort pT.
Identity Coercion pred_sort_of_fin : fin_pred_sort >-> pred_sort.
Definition enum_mem T (mA : mem_pred _) := filter mA (Finite.enum T).
Notation enum A := (enum_mem (mem A)).
Definition pick (T : finType) (P : pred T) := ohead (enum P).
Notation "[ 'pick' x | P ]" := (pick (fun x => P%B))
(at level 0, x ident, format "[ 'pick'  x  |  P  ]") : form_scope.
Notation "[ 'pick' x : T | P ]" := (pick (fun x : T => P%B))
(at level 0, x ident, only parsing) : form_scope.
Definition pick_true T (x : T) := true.
Notation "[ 'pick' x : T ]" := [pick x : T | pick_true x]
(at level 0, x ident, only parsing).
Notation "[ 'pick' x ]" := [pick x : _]
(at level 0, x ident, only parsing) : form_scope.
Notation "[ 'pic' 'k' x : T ]" := [pick x : T | pick_true _]
(at level 0, x ident, format "[ 'pic' 'k'  x : T ]") : form_scope.
Notation "[ 'pick' x | P & Q ]" := [pick x | P && Q ]
(at level 0, x ident,
format "[ '[hv ' 'pick'  x  |  P '/ '   &  Q ] ']'") : form_scope.
Notation "[ 'pick' x : T | P & Q ]" := [pick x : T | P && Q ]
(at level 0, x ident, only parsing) : form_scope.
Notation "[ 'pick' x 'in' A ]" := [pick x | x \in A]
(at level 0, x ident, format "[ 'pick'  x  'in'  A  ]") : form_scope.
Notation "[ 'pick' x : T 'in' A ]" := [pick x : T | x \in A]
(at level 0, x ident, only parsing) : form_scope.
Notation "[ 'pick' x 'in' A | P ]" := [pick x | x \in A & P ]
(at level 0, x ident,
format "[ '[hv ' 'pick'  x  'in'  A '/ '   |  P ] ']'") : form_scope.
Notation "[ 'pick' x : T 'in' A | P ]" := [pick x : T | x \in A & P ]
(at level 0, x ident, only parsing) : form_scope.
Notation "[ 'pick' x 'in' A | P & Q ]" := [pick x in A | P && Q]
(at level 0, x ident, format
"[ '[hv ' 'pick'  x  'in'  A '/ '   |  P '/ '  &  Q ] ']'") : form_scope.
Notation "[ 'pick' x : T 'in' A | P & Q ]" := [pick x : T in A | P && Q]
(at level 0, x ident, only parsing) : form_scope.
(* We lock the definitions of card and subset to mitigate divergence of the   *)
(* Coq term comparison algorithm.                                             *)
Local Notation card_type := (forall T : finType, mem_pred T -> nat).
Local Notation card_def := (fun T mA => size (enum_mem mA)).
Module Type CardDefSig.
Parameter card : card_type. Axiom cardEdef : card = card_def.
End CardDefSig.
Module CardDef : CardDefSig.
Definition card : card_type := card_def. Definition cardEdef := erefl card.
End CardDef.
(* Should be Include, but for a silly restriction: can't Include at toplevel! *)
Export CardDef.
Canonical card_unlock := Unlockable cardEdef.
(* A is at level 99 to allow the notation #|G : H| in groups. *)
Notation "#| A |" := (card (mem A))
(at level 0, A at level 99, format "#| A |") : nat_scope.
Definition pred0b (T : finType) (P : pred T) := #|P| == 0.
Prenex Implicits pred0b.
Module FiniteQuant.
Variant quantified := Quantified of bool.
Delimit Scope fin_quant_scope with Q. (* Bogus, only used to declare scope. *)
Bind Scope fin_quant_scope with quantified.
Notation "F ^*" := (Quantified F) (at level 2).
Notation "F ^~" := (~~ F) (at level 2).
Section Definitions.
Variable T : finType.
Implicit Types (B : quantified) (x y : T).
Definition quant0b Bp := pred0b [pred x : T | let: F^* := Bp x x in F].
(* The first redundant argument protects the notation from  Coq's K-term      *)
(* display kludge; the second protects it from simpl and /=.                  *)
Definition ex B x y := B.
(* Binding the predicate value rather than projecting it prevents spurious    *)
(* unfolding of the boolean connectives by unification.                       *)
Definition all B x y := let: F^* := B in F^~^*.
Definition all_in C B x y := let: F^* := B in (C ==> F)^~^*.
Definition ex_in C B x y :=  let: F^* := B in (C && F)^*.
End Definitions.
Notation "[ x | B ]" := (quant0b (fun x => B x)) (at level 0, x ident).
Notation "[ x : T | B ]" := (quant0b (fun x : T => B x)) (at level 0, x ident).
Module Exports.
Notation ", F" := F^* (at level 200, format ", '/ '  F") : fin_quant_scope.
Notation "[ 'forall' x B ]" := [x | all B]
(at level 0, x at level 99, B at level 200,
format "[ '[hv' 'forall'  x B ] ']'") : bool_scope.
Notation "[ 'forall' x : T B ]" := [x : T | all B]
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation "[ 'forall' ( x | C ) B ]" := [x | all_in C B]
(at level 0, x at level 99, B at level 200,
format "[ '[hv' '[' 'forall'  ( x '/  '  |  C ) ']' B ] ']'") : bool_scope.
Notation "[ 'forall' ( x : T | C ) B ]" := [x : T | all_in C B]
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation "[ 'forall' x 'in' A B ]" := [x | all_in (x \in A) B]
(at level 0, x at level 99, B at level 200,
format "[ '[hv' '[' 'forall'  x '/  '  'in'  A ']' B ] ']'") : bool_scope.
Notation "[ 'forall' x : T 'in' A B ]" := [x : T | all_in (x \in A) B]
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation ", 'forall' x B" := [x | all B]^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  'forall'  x B") : fin_quant_scope.
Notation ", 'forall' x : T B" := [x : T | all B]^*
(at level 200, x at level 99, B at level 200, only parsing) : fin_quant_scope.
Notation ", 'forall' ( x | C ) B" := [x | all_in C B]^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  '[' 'forall'  ( x '/  '  |  C ) ']' B") : fin_quant_scope.
Notation ", 'forall' ( x : T | C ) B" := [x : T | all_in C B]^*
(at level 200, x at level 99, B at level 200, only parsing) : fin_quant_scope.
Notation ", 'forall' x 'in' A B" := [x | all_in (x \in A) B]^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  '[' 'forall'  x '/  '  'in'  A ']' B") : bool_scope.
Notation ", 'forall' x : T 'in' A B" := [x : T | all_in (x \in A) B]^*
(at level 200, x at level 99, B at level 200, only parsing) : bool_scope.
Notation "[ 'exists' x B ]" := [x | ex B]^~
(at level 0, x at level 99, B at level 200,
format "[ '[hv' 'exists'  x B ] ']'") : bool_scope.
Notation "[ 'exists' x : T B ]" := [x : T | ex B]^~
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation "[ 'exists' ( x | C ) B ]" := [x | ex_in C B]^~
(at level 0, x at level 99, B at level 200,
format "[ '[hv' '[' 'exists'  ( x '/  '  |  C ) ']' B ] ']'") : bool_scope.
Notation "[ 'exists' ( x : T | C ) B ]" := [x : T | ex_in C B]^~
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation "[ 'exists' x 'in' A B ]" := [x | ex_in (x \in A) B]^~
(at level 0, x at level 99, B at level 200,
format "[ '[hv' '[' 'exists'  x '/  '  'in'  A ']' B ] ']'") : bool_scope.
Notation "[ 'exists' x : T 'in' A B ]" := [x : T | ex_in (x \in A) B]^~
(at level 0, x at level 99, B at level 200, only parsing) : bool_scope.
Notation ", 'exists' x B" := [x | ex B]^~^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  'exists'  x B") : fin_quant_scope.
Notation ", 'exists' x : T B" := [x : T | ex B]^~^*
(at level 200, x at level 99, B at level 200, only parsing) : fin_quant_scope.
Notation ", 'exists' ( x | C ) B" := [x | ex_in C B]^~^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  '[' 'exists'  ( x '/  '  |  C ) ']' B") : fin_quant_scope.
Notation ", 'exists' ( x : T | C ) B" := [x : T | ex_in C B]^~^*
(at level 200, x at level 99, B at level 200, only parsing) : fin_quant_scope.
Notation ", 'exists' x 'in' A B" := [x | ex_in (x \in A) B]^~^*
(at level 200, x at level 99, B at level 200,
format ", '/ '  '[' 'exists'  x '/  '  'in'  A ']' B") : bool_scope.
Notation ", 'exists' x : T 'in' A B" := [x : T | ex_in (x \in A) B]^~^*
(at level 200, x at level 99, B at level 200, only parsing) : bool_scope.
End Exports.
End FiniteQuant.
Export FiniteQuant.Exports.
Definition disjoint T (A B : mem_pred _) := @pred0b T (predI A B).
Notation "[ 'disjoint' A & B ]" := (disjoint (mem A) (mem B))
(at level 0,
format "'[hv' [ 'disjoint' '/  '  A '/'  &  B ] ']'") : bool_scope.
Local Notation subset_type := (forall (T : finType) (A B : mem_pred T), bool).
Local Notation subset_def := (fun T A B => pred0b (predD A B)).
Module Type SubsetDefSig.
Parameter subset : subset_type. Axiom subsetEdef : subset = subset_def.
End SubsetDefSig.
Module Export SubsetDef : SubsetDefSig.
Definition subset : subset_type := subset_def.
Definition subsetEdef := erefl subset.
End SubsetDef.
Canonical subset_unlock := Unlockable subsetEdef.
Notation "A \subset B" := (subset (mem A) (mem B))
(at level 70, no associativity) : bool_scope.
Definition proper T A B := @subset T A B && ~~ subset B A.
Notation "A \proper B" := (proper (mem A) (mem B))
(at level 70, no associativity) : bool_scope.
(* image, xinv, inv, and ordinal operations will be defined later. *)
Section OpsTheory.
Variable T : finType.
Implicit Types A B C P Q : pred T.
Implicit Types x y : T.
Implicit Type s : seq T.

(* Specifications: *)
Lemma enumP : Finite.axiom (Finite.enum T).
Lemma enum0 : enum pred0 = Nil T. Proof. exact: filter_pred0. Qed.
Lemma enum1 x : enum (pred1 x) = [:: x].
Variant pick_spec : option T -> Type :=
| Pick x of P x         : pick_spec (Some x)
| Nopick of P =1 xpred0 : pick_spec None.
Lemma pickP : pick_spec (pick P).
End EnumPick.
Lemma eq_enum P Q : P =i Q -> enum P = enum Q.
Lemma cardC A : #|A| + #|[predC A]| = #|T|.
Lemma card2 x y : #|pred2 x y| = (x != y).+1.
Lemma max_card A : #|A| <= #|T|.
Lemma card_uniqP s : reflect (#|s| = size s) (uniq s).
Lemma card0_eq A : #|A| = 0 -> A =i pred0.
Lemma card_gt0P A : reflect (exists i, i \in A) (#|A| > 0).
Lemma subsetPn A B :
reflect (exists2 x, x \in A & x \notin B) (~~ (A \subset B)).
Lemma subset_leq_card A B : A \subset B -> #|A| <= #|B|.
Lemma subxx_hint (mA : mem_pred T) : subset mA mA.
Hint Resolve subxx_hint : core.
(* The parametrization by predType makes it easier to apply subxx. *)
Lemma subxx (pT : predType T) (pA : pT) : pA \subset pA.
Lemma eq_subset_r B1 B2 : B1 =i B2 ->
(@subset T)^~ (mem B1) =1 (@subset T)^~ (mem B2).
Lemma eq_subxx A B : A =i B -> A \subset B.
Lemma subset_cardP A B : #|A| = #|B| -> reflect (A =i B) (A \subset B).
Lemma subset_leqif_card A B : A \subset B -> #|A| <= #|B| ?= iff (B \subset A).
Lemma subset_trans A B C : A \subset B -> B \subset C -> A \subset C.
Lemma subset_all s A : (s \subset A) = all (mem A) s.
Lemma proper_sub A B : A \proper B -> A \subset B.
Lemma proper_sub_trans A B C : A \proper B -> B \subset C -> A \proper C.
Lemma sub_proper_trans A B C : A \subset B -> B \proper C -> A \proper C.
Lemma proper_card A B : A \proper B -> #|A| < #|B|.
Lemma proper_irrefl A : ~~ (A \proper A).
Lemma eq_proper_r A B : A =i B ->
(@proper T)^~ (mem A) =1 (@proper T)^~ (mem B).
Lemma disjoint_sym A B : [disjoint A & B] = [disjoint B & A].
Lemma eq_disjoint_r B1 B2 : B1 =i B2 ->
(@disjoint T)^~ (mem B1) =1 (@disjoint T)^~ (mem B2).
Lemma subset_disjoint A B : (A \subset B) = [disjoint A & [predC B]].
Lemma disjoint_trans A B C :
A \subset B -> [disjoint B & C] -> [disjoint A & C].
Lemma eq_disjoint1 x A B :
A =i pred1 x ->  [disjoint A & B] = (x \notin B).
Lemma disjointU1 x A B :
[disjoint predU1 x A & B] = (x \notin B) && [disjoint A & B].
Lemma disjoint_cat s1 s2 A :
[disjoint s1 ++ s2 & A] = [disjoint s1 & A] && [disjoint s2 & A].
Lemma negb_forall P : ~~ [forall x, P x] = [exists x, ~~ P x].
End Extremum.