We first define quotients by equivalence relations and then lift this to quotients by arbitrary relations.
Require Import overture pext inhab fext.

Relation Quotient Types

Section QuotientTypes.
  Variables (A : Type) (R : Rel A).

Equivalence closure of R
  Inductive conv x : Pred A :=
  | convxx : conv x x
  | conv1S y z : R y z -> conv x y -> conv x z
  | conv1R y z : R z y -> conv x y -> conv x z.

  Instance: Reflexive conv.
  Proof. exact: convxx. Qed.

  Instance: Transitive conv.
  Proof. move=> x y z cxy cyz. elim: cyz; eauto using conv. Qed.

  Instance: Symmetric conv.
    move=> x y. elim=>{y}. exact: convxx.
    - move=> y z ryz _ cyx. transitivity y; eauto using conv.
    - move=> y z rzy _ cyx. transitivity y; eauto using conv.

  Instance: Equivalence conv.
  Proof. constructor; exact _. Qed.

The quotient of A by R is the type of inhabited equivalence classes of R.
  Definition quot : Type := { P : Pred A & hexists x : A, conv x = P }.

Representative of x
  Definition repr x : quot := (conv x; tr (x; erefl)).

Paths between representatives
  Lemma conv_repr {x y} (cxy : conv x y) : repr x = repr y.
  Proof. apply: tag_injective. fext=>/=z. apply: pext => []<-//. by symmetry. Qed.

  Lemma reprP {x y} (rxy : R x y) : repr x = repr y.
  Proof. apply: conv_repr; eauto using conv. Qed.

Existence of representatives
  Lemma repr_surjective : surjective repr.
    case=> P. inhab=> x E /=. apply: tr. exists x.
    apply: sigT_eq. by destruct E.

  Lemma exists_repr q : exists x, repr x = q.
    move: (repr_surjective q). apply: inhab_ind => /=; by firstorder.

Lifting of respectful functions
  Program Definition quot_rec {B : Type} (f : A -> B)
    (fP : forall x y, R x y -> f x = f y) (q : quot) : B :=
    inhab_rec (fun p => f p.1) _ q.2.
  Next Obligation.
    suff H: forall x y, conv x y -> f x = f y.
    - apply: H => /=. rewrite X0 -X. exact: convxx.
    - move=> {x X0 y X} x y. by elim=> //{y}[y z /fP->_->|y z /fP->_->].
  Arguments quot_rec {B} f fP q.

  Section Induction.
    Variable P : quot -> Type.
    Variable f : forall x, P (repr x).
    Hypothesis fP : forall x y (r : R x y), reprP r # f x = f y.

    Program Definition quot_rect_total : quot -> { q' : quot & P q' } :=
      quot_rec (fun x => (repr x; f x)) _.
    Next Obligation.
      apply: sigT_eq (reprP H) _. exact: fP.

    Definition quot_rect_total_id (q : quot) : (quot_rect_total q).1 = q.
    Proof. destruct (exists_repr q). subst. reflexivity. Defined.

    Definition quot_rect (q : quot) : P q :=
      quot_rect_total_id q # (quot_rect_total q).2.

    Lemma quot_rectE x : quot_rect (repr x) = f x. by []. Qed.
  End Induction.

  Definition quot_ind (P : quot -> hprop) (f : forall x, P (repr x)) : forall q, P q.
  Proof. apply: (quot_rect (f := f)) => x y rxy. exact: hpropP. Defined.

Paths in quotients
  Section Paths.
    Program Definition in_class x : quot -> Prop :=
      quot_rec (conv x) _.
    Next Obligation.
      move: x0 y H => y z ryz. apply: pext=> [rxy|rxz].
      exact: conv1S ryz _. exact: conv1R ryz _.

    Lemma in_classE x y :
      in_class x (repr y) = conv x y.
    Proof. by []. Qed.

    Lemma repr_conv x y :
      repr x = repr y <-> conv x y.
      split=> [eqn|]. rewrite -in_classE -eqn /=. exact: convxx.
      elim=> //{y}[y z ryz _->|y z ryz _->]; by rewrite (reprP ryz).
  End Paths.

  (* If R is an equivalence, then it is equal to conv. *)
  Context `{Equivalence A R}.

  Lemma conv_equiv x y :
    conv x y <-> R x y.
    split; last first. move=> /conv1S. apply. exact: convxx.
    elim=>{y}. reflexivity. move=> y z ryz _ rxy. by transitivity y.
    move=> y z ryz _ rxy. transitivity y => //. by symmetry.

  Lemma repr_equiv x y : repr x = repr y <-> R x y.
  Proof. by rewrite repr_conv conv_equiv. Qed.
End QuotientTypes.
Arguments repr {A} R x, {A R} x.
Arguments quot_rec {A R B} f fP q.
Arguments quot_ind {A R} P f q.

Definition quot_rec2 {A B C : Type} {R : Rel A} {S : Rel B} (f : A -> B -> C) :
  (forall x y1 y2, S y1 y2 -> f x y1 = f x y2) ->
  (forall x1 x2 y, R x1 x2 -> f x1 y = f x2 y) ->
  quot R -> quot S -> C.
  move=> h1 h2. apply: (quot_rec (fun x => quot_rec (f x) (h1 x)) _).
  abstract (move=>/=x y rxy; fext=> q; case: (exists_repr q) => z<-/=; exact: h2).
Arguments quot_rec2 {A B C R S} f h1 h2 x y : rename.

Characterization of maps out of quotients

Section QuotientMappingProperty.
  Variables (A B : Type) (R : Rel A).

  Definition quot_map (f : quot R -> B) : { f : A -> B | forall x y, R x y -> f x = f y }.
    refine (exist _ (f \o repr) _). move=> x y r /=. exact: (ap f (reprP r)).

  Definition map_quot (p : { f : A -> B | forall x y, R x y -> f x = f y }) : quot R -> B :=
    quot_rec p.1 p.2.

  Lemma quot_map_sect (f : quot R -> B) : map_quot (quot_map f) = f.
  Proof. fext. exact: quot_ind. Qed.

  Lemma quot_map_retr p : quot_map (map_quot p) = p.
  Proof. exact: sig_eq. Qed.

  Definition quot_map_is_equiv : is_equiv quot_map :=
    mk_is_equiv map_quot quot_map_sect quot_map_retr.

  Canonical quot_map_equiv := mk_equiv quot_map quot_map_is_equiv.
End QuotientMappingProperty.