(* The natural numbers in descending order. *)
Global Instance ge_Pre: PreOrder ge.(* These tactics are meant to relieve the user from remembering morphism names.
   In most cases they will apply the most general morphism for the arity
   requested. *)
(* TODO: move this somewhere more general. *)
Ltac postcancel := unfold Basics.flip; trivial.Class Inhabited (A : Type) := { cinhabited : inhabited A}.Class Inhabited (A : Type) := { cinhabited : inhabited A}.Instance inhabitedNat: Inhabited nat.Instance inhabitedBool: Inhabited bool.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* Logical connectives *)
Class ILogicOps (A : Type) : Type := {
  lentails: A -> A -> Prop;
  ltrue: A;
  lfalse: A;
  limpl: A -> A -> A;
  land: A -> A -> A;
  lor: A -> A -> A;
  lforall: forall {T : Type}, (T -> A) -> A;
  lexists: forall {T : Type}, (T -> A) -> A
}.(* These notations have to sit strictly between level 80 (precendence of /\)
   and level 70 (precedence of =). *)
Infix "|--"  := lentails (at level 80, no associativity).Infix "//\\"   := land (at level 75, right associativity).Infix "\\//"   := lor (at level 76, right associativity).Infix "-->>"   := limpl (at level 77, right associativity).Notation "'Forall' x : T , p" :=
  (lforall (fun x : T => p)) (at level 78, x ident, right associativity).Notation "'Forall' x , p" :=
  (lforall (fun x => p)) (at level 78, x ident, right associativity, only parsing).Notation "'Exists' x : T , p" :=
  (lexists (fun x : T => p)) (at level 78, x ident, right associativity).Notation "'Exists' x , p" :=
  (lexists (fun x => p)) (at level 78, x ident, right associativity, only parsing).Definition lequiv P Q := P |-- Q /\ Q |-- P.Infix "-|-"  := lequiv (at level 85, no associativity).(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Experiments with proof search. *)
  Ltac landR :=
    repeat match goal with
    | |- _ |-- _ //\\ _ => apply landR
    end.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.(* Axioms of an intuitionistic logic, presented in a sequent calculus style
   with singleton contexts on the left of the turnstile. This form is
   particularly suited for the undisciplined context manipulation that tends to
   happen in separation logic.

   Every connective has a number of L-rules and a number of R-rules describing
   what can be done to prove a goal that has the connective as the head symbol
   of the left, respectively right, side of a turnstile. The notable exception
   to this pattern is implication, which is required to be the right adjoint of
   conjunction. This makes singleton contexts work. *)
Class ILogic {A : Type} {ILOps: ILogicOps A} : Type := {
  lentailsPre:> PreOrder lentails;
  ltrueR: forall (C : A), C |-- ltrue;
  lfalseL: forall (C : A), lfalse |-- C;
  lforallL: forall (T : Type) x (P: T -> A) C, P x |-- C -> lforall P |-- C;
  lforallR: forall (T : Type) (P: T -> A) C, (forall x, C |-- P x) -> C |-- lforall P;
  lexistsL: forall (T : Type) (P: T -> A) C, (forall x, P x |-- C) -> lexists P |-- C;
  lexistsR: forall (T : Type) (x : T) (P: T -> A) C, C |-- P x -> C |-- lexists P;
  landL1: forall (P Q C : A), P |-- C  ->  P //\\ Q |-- C;
  landL2: forall (P Q C : A), Q |-- C  ->  P //\\ Q |-- C;
  lorR1:  forall (P Q C : A), C |-- P  ->  C |-- P \\// Q;
  lorR2:  forall (P Q C : A), C |-- Q  ->  C |-- P \\// Q;
  landR:  forall (P Q C : A), C |-- P  ->  C |-- Q  ->  C |-- P //\\ Q;
  lorL:   forall (P Q C : A), P |-- C  ->  Q |-- C  ->  P \\// Q |-- C;
  landAdj: forall (P Q C : A), C |-- (P -->> Q) -> C //\\ P |-- Q;
  limplAdj: forall (P Q C : A), C //\\ P |-- Q -> C |-- (P -->> Q)
}.Notation "|--  P" := (ltrue |-- P) (at level 85, no associativity).Hint Extern 0 (?x -|- ?x) => reflexivity.Hint Extern 0 (?P -|- ?Q) => (is_evar P; reflexivity) || (is_evar Q; reflexivity).Global Instance lequivEquivalence : Equivalence lequiv.

(* Specifications: *)
Lemma land_is_forall P Q : P //\\ Q -|- Forall b : bool, if b then P else Q.
Lemma lor_is_exists P Q: P \\// Q -|- Exists b : bool, if b then P else Q.
Lemma ltrue_is_forall: ltrue -|- Forall x: Empty_set, Empty_set_rect _ x.
Lemma lfalse_is_exists: lfalse -|- Exists x: Empty_set, Empty_set_rect _ x.