Notation "'if' c 'as' x 'return' t 'then' v1 'else' v2" :=
  (if c as x return t then v1 else v2)
  (at level 200, c, t, v1, v2 at level 200, x ident, only parsing)
     : general_if_scope.Notation "'if' c 'as' x 'return' t 'then' v1 'else' v2" :=
  (if c%bool is true as x in bool return t then v1 else v2) : boolean_if_scope.(* Allow similarly Prop annotation for, e.g., rewrite multirules.             *)
Notation "P : 'Prop'" := (P%type : Prop)
  (at level 100, only parsing) : core_scope.(* Constants for abstract: and [: name ] intro pattern *)
Definition abstract_lock := unit.Definition abstract_key := tt.Definition abstract (statement : Type) (id : nat) (lock : abstract_lock) :=
  let: tt := lock in statement.Notation "T (* n *)" := (abstract T n abstract_key).Variant put vT sT (v1 v2 : vT) (s : sT) := Put.Variant put vT sT (v1 v2 : vT) (s : sT) := Put.Definition get vT sT v s (p : @put vT sT v v s) := let: Put := p in s.Definition get_by vT sT of sT -> vT := @get vT sT.Notation "[ 'unlockable' 'fun' C ]" := (@Unlockable _ (fun _ => _) C (unlock _))
  (at level 0, format "[ 'unlockable'  'fun'  C ]") : form_scope.(* We would like to recognize
Notation "[ 'th' 'e' sT 'of' v : 'Type' ]" := (@get Type sT v _ _)
  (at level 0, format "[ 'th' 'e'  sT   'of'  v  :  'Type' ]") : form_scope.
*)

(* Helper notation for canonical structure inheritance support.               *)
(* This is a workaround for the poor interaction between delta reduction and  *)
(* canonical projections in Coq's unification algorithm, by which transparent *)
(* definitions hide canonical instances, i.e., in                             *)
(*   Canonical a_type_struct := @Struct a_type ...                            *)
(*   Definition my_type := a_type.                                            *)
(* my_type doesn't effectively inherit the struct structure from a_type. Our  *)
(* solution is to redeclare the instance as follows                           *)
(*   Canonical my_type_struct := Eval hnf in [struct of my_type].             *)
(* The special notation [str of _] must be defined for each Strucure "str"    *)
(* with constructor "Str", typically as follows                               *)
(*   Definition clone_str s :=                                                *)
(*      let: Str _ x y ... z := s return {type of Str for s} -> str in        *)
(*      fun k => k _ x y ... z.                                               *)
(*    Notation "[ 'str' 'of' T 'for' s ]" := (@clone_str s (@Str T))          *)
(*      (at level 0, format "[ 'str'  'of'  T  'for'  s ]") : form_scope.     *)
(*    Notation "[ 'str' 'of' T ]" := (repack_str (fun x => @Str T x))         *)
(*      (at level 0, format "[ 'str'  'of'  T ]") : form_scope.               *)
(* The notation for the match return predicate is defined below; the eta      *)
(* expansion in the second form serves both to distinguish it from the first  *)
(* and to avoid the delta reduction problem.                                  *)
(*   There are several variations on the notation and the definition of the   *)
(* the "clone" function, for telescopes, mixin classes, and join (multiple    *)
(* inheritance) classes. We describe a different idiom for clones in ssrfun;  *)
(* it uses phantom types (see below) and static unification; see fintype and  *)
(* ssralg for examples.                                                       *)

Definition argumentType T P & forall x : T, P x := T.Definition dependentReturnType T P & forall x : T, P x := P.Definition returnType aT rT & aT -> rT := rT.Notation "{ 'type' 'of' c 'for' s }" := (dependentReturnType c s)
  (at level 0, format "{ 'type'  'of'  c  'for'  s }") : type_scope.(* A generic "phantom" type (actually, a unit type with a phantom parameter). *)
(* This type can be used for type definitions that require some Structure     *)
(* on one of their parameters, to allow Coq to infer said structure so it     *)
(* does not have to be supplied explicitly or via the "[the _ of _]" notation *)
(* (the latter interacts poorly with other Notation).                         *)
(*   The definition of a (co)inductive type with a parameter p : p_type, that *)
(* needs to use the operations of a structure                                 *)
(*  Structure p_str : Type := p_Str {p_repr :> p_type; p_op : p_repr -> ...}  *)
(* should be given as                                                         *)
(*  Inductive indt_type (p : p_str) := Indt ... .                             *)
(*  Definition indt_of (p : p_str) & phantom p_type p := indt_type p.         *)
(*  Notation "{ 'indt' p }" := (indt_of (Phantom p)).                         *)
(*  Definition indt p x y ... z : {indt p} := @Indt p x y ... z.              *)
(*  Notation "[ 'indt' x y ... z ]" := (indt x y ... z).                      *)
(* That is, the concrete type and its constructor should be shadowed by       *)
(* definitions that use a phantom argument to infer and display the true      *)
(* value of p (in practice, the "indt" constructor often performs additional  *)
(* functions, like "locking" the representation -- see below).                *)
(*   We also define a simpler version ("phant" / "Phant") of phantom for the  *)
(* common case where p_type is Type.                                          *)

Variant phantom T (p : T) := Phantom.(* A generic "phantom" type (actually, a unit type with a phantom parameter). *)
(* This type can be used for type definitions that require some Structure     *)
(* on one of their parameters, to allow Coq to infer said structure so it     *)
(* does not have to be supplied explicitly or via the "[the _ of _]" notation *)
(* (the latter interacts poorly with other Notation).                         *)
(*   The definition of a (co)inductive type with a parameter p : p_type, that *)
(* needs to use the operations of a structure                                 *)
(*  Structure p_str : Type := p_Str {p_repr :> p_type; p_op : p_repr -> ...}  *)
(* should be given as                                                         *)
(*  Inductive indt_type (p : p_str) := Indt ... .                             *)
(*  Definition indt_of (p : p_str) & phantom p_type p := indt_type p.         *)
(*  Notation "{ 'indt' p }" := (indt_of (Phantom p)).                         *)
(*  Definition indt p x y ... z : {indt p} := @Indt p x y ... z.              *)
(*  Notation "[ 'indt' x y ... z ]" := (indt x y ... z).                      *)
(* That is, the concrete type and its constructor should be shadowed by       *)
(* definitions that use a phantom argument to infer and display the true      *)
(* value of p (in practice, the "indt" constructor often performs additional  *)
(* functions, like "locking" the representation -- see below).                *)
(*   We also define a simpler version ("phant" / "Phant") of phantom for the  *)
(* common case where p_type is Type.                                          *)

Variant phantom T (p : T) := Phantom.Variant phant (p : Type) := Phant.Variant phant (p : Type) := Phant.(* Internal tagging used by the implementation of the ssreflect elim.         *)

Definition protect_term (A : Type) (x : A) : A := x.(* The ssreflect idiom for a non-keyed pattern:                               *)
(*  - unkeyed t wiil match any subterm that unifies with t, regardless of     *)
(*    whether it displays the same head symbol as t.                          *)
(*  - unkeyed t a b will match any application of a term f unifying with t,   *)
(*    to two arguments unifying with with a and b, repectively, regardless of *)
(*    apparent head symbols.                                                  *)
(*  - unkeyed x where x is a variable will match any subterm with the same    *)
(*    type as x (when x would raise the 'indeterminate pattern' error).       *)

Notation unkeyed x := (let flex := x in flex).(* Ssreflect converse rewrite rule rule idiom. *)
Definition ssr_converse R (r : R) := (Logic.I, r).(* Term tagging (user-level).                                                 *)
(* The ssreflect library uses four strengths of term tagging to restrict      *)
(* convertibility during type checking:                                       *)
(*  nosimpl t simplifies to t EXCEPT in a definition; more precisely, given   *)
(*    Definition foo := nosimpl bar, foo (or foo t') will NOT be expanded by  *)
(*    the /= and //= switches unless it is in a forcing context (e.g., in     *)
(*    match foo t' with ... end, foo t' will be reduced if this allows the    *)
(*    match to be reduced). Note that nosimpl bar is simply notation for a    *)
(*    a term that beta-iota reduces to bar; hence rewrite /foo will replace   *)
(*    foo by bar, and rewrite -/foo will replace bar by foo.                  *)
(*    CAVEAT: nosimpl should not be used inside a Section, because the end of *)
(*    section "cooking" removes the iota redex.                               *)
(*  locked t is provably equal to t, but is not convertible to t; 'locked'    *)
(*    provides support for selective rewriting, via the lock t : t = locked t *)
(*    Lemma, and the ssreflect unlock tactic.                                 *)
(*  locked_with k t is equal but not convertible to t, much like locked t,    *)
(*    but supports explicit tagging with a value k : unit. This is used to    *)
(*    mitigate a flaw in the term comparison heuristic of the Coq kernel,     *)
(*    which treats all terms of the form locked t as equal and conpares their *)
(*    arguments recursively, leading to an exponential blowup of comparison.  *)
(*    For this reason locked_with should be used rather than locked when      *)
(*    defining ADT operations. The unlock tactic does not support locked_with *)
(*    but the unlock rewrite rule does, via the unlockable interface.         *)
(*  we also use Module Type ascription to create truly opaque constants,      *)
(*    because simple expansion of constants to reveal an unreducible term     *)
(*    doubles the time complexity of a negative comparison. Such opaque       *)
(*    constants can be expanded generically with the unlock rewrite rule.     *)
(*    See the definition of card and subset in fintype for examples of this.  *)

Notation nosimpl t := (let: tt := tt in t).

Lemma master_key : unit.