Timings for Sum.v
(** * Theorems about disjoint unions *)
 
Require Import HoTT.Basics.
 
Require Import Types.Empty Types.Unit Types.Prod Types.Sigma.
 
(** The following are only required for the equivalence between [sum] and a sigma type *)
 
Require Import Types.Bool.
 
Local Open Scope trunc_scope.
 
Local Open Scope path_scope.
 
Local Set Universe Minimization ToSet.
 
Generalizable Variables X A B f g n.
 
Scheme sum_ind := Induction for sum Sort Type.
 
Arguments sum_ind {A B} P f g s : rename.
 
Scheme sum_rec := Minimality for sum Sort Type.
 
Arguments sum_rec {A B} P f g s : rename.
 
(** ** Co-Unpacking *)
(** Sums are coproducts, so there should be a dual to [unpack_prod].  I'm not sure what it is, though. *)
(** ** Eta conversion *)
 
Definition eta_sum `(z : A + B) : match z with
                                    | inl z' => inl z'
                                    | inr z' => inr z'
                                  end = z
  := match z with inl _ => 1 | inr _ => 1 end.
 
Definition code_sum {A B} (z z' : A + B) : Type
  := match z, z' with
    | inl a, inl a' => a = a'
    | inr b, inr b' => b = b'
    | _, _ => Empty end.
 
Definition path_sum {A B : Type} {z z' : A + B} (c : code_sum z z') : z = z'.
 
Definition path_sum_inv {A B : Type} {z z' : A + B}
           (pq : z = z')
: match z, z' with
    | inl z0, inl z'0 => z0 = z'0
    | inr z0, inr z'0 => z0 = z'0
    | _, _ => Empty
  end
  := match pq with
       | 1 => match z with
                | inl _ => 1
                | inr _ => 1
              end
     end.
 
Definition inl_ne_inr {A B : Type} (a : A) (b : B)
: inl a <> inr b
:= path_sum_inv.
 
Definition inr_ne_inl {A B : Type} (b : B) (a : A)
: inr b <> inl a
:= path_sum_inv.
 
(** This version produces only paths between closed terms, as opposed to paths between arbitrary inhabitants of sum types. *)
 
Definition path_sum_inl {A : Type} (B : Type) {x x' : A}
: inl x = inl x' -> x = x'
  := fun p => @path_sum_inv A B _ _ p.
 
Definition path_sum_inr (A : Type) {B : Type} {x x' : B}
: inr x = inr x' -> x = x'
  := fun p => @path_sum_inv A B _ _ p.
 
(** This lets us identify the path space of a sum type, up to equivalence. *)
 
Definition eisretr_path_sum {A B} {z z' : A + B}
  : path_sum o (@path_sum_inv _ _ z z') == idmap
  := fun p => match p as p in (_ = z') return
                    path_sum (path_sum_inv p) = p
              with
                | 1 => match z as z return
                             (@path_sum _ _ z z) (path_sum_inv 1) = 1
                       with
                         | inl _ => 1
                         | inr _ => 1
                       end
              end.
 
Definition eissect_path_sum {A B} {z z' : A + B}
  : path_sum_inv o (@path_sum _ _ z z') == idmap.
 
  destruct z, z', p; exact idpath.
 
Instance isequiv_path_sum {A B : Type} {z z' : A + B}
: IsEquiv (@path_sum _ _ z z') | 0.
 
  refine (Build_IsEquiv _ _ path_sum path_sum_inv
    eisretr_path_sum eissect_path_sum _).
 
  destruct z, z';
  intros [];
  exact idpath.
 
Definition equiv_path_sum {A B : Type} (z z' : A + B)
  := Build_Equiv _ _ _ (@isequiv_path_sum A B z z').
 
(** ** Fibers of [inl] and [inr] *)
(** It follows that the fibers of [inl] and [inr] are decidable hprops. *)
 
Instance ishprop_hfiber_inl {A B : Type} (z : A + B)
: IsHProp (hfiber inl z).
 
  destruct z as [a|b]; unfold hfiber.
 
 exact (istrunc_equiv_istrunc _
              (equiv_functor_sigma_id
                 (fun x => equiv_path_sum (inl x) (inl a)))).
 
 exact (istrunc_isequiv_istrunc _
              (fun xp => inl_ne_inr (xp.1) b xp.2)^-1).
 
Instance decidable_hfiber_inl {A B : Type} (z : A + B)
: Decidable (hfiber inl z).
 
  destruct z as [a|b]; unfold hfiber.
 
 exact (decidable_equiv' _
              (equiv_functor_sigma_id
                 (fun x => equiv_path_sum (inl x) (inl a))) _).
 
 exact (decidable_equiv _
              (fun xp => inl_ne_inr (xp.1) b xp.2)^-1 _).
 
Instance ishprop_hfiber_inr {A B : Type} (z : A + B)
: IsHProp (hfiber inr z).
 
  destruct z as [a|b]; unfold hfiber.
 
 exact (istrunc_isequiv_istrunc _
              (fun xp => inr_ne_inl (xp.1) a xp.2)^-1).
 
 exact (istrunc_equiv_istrunc _
              (equiv_functor_sigma_id
                 (fun x => equiv_path_sum (inr x) (inr b)))).
 
Instance decidable_hfiber_inr {A B : Type} (z : A + B)
: Decidable (hfiber inr z).
 
  destruct z as [a|b]; unfold hfiber.
 
 exact (decidable_equiv _
              (fun xp => inr_ne_inl (xp.1) a xp.2)^-1 _).
 
 exact (decidable_equiv' _
              (equiv_functor_sigma_id
                 (fun x => equiv_path_sum (inr x) (inr b))) _).
 
(** ** Decomposition *)
(** Conversely, a decidable predicate decomposes a type as a sum. *)
 
  Context `{Funext} {A : Type} (P : A -> Type)
          `{forall a, IsHProp (P a)} `{forall a, Decidable (P a)}.
 
  Definition equiv_decidable_sum
    : A <~> {x:A & P x} + {x:A & ~(P x)}.
 
    transparent assert (f : (A -> {x:A & P x} + {x:A & ~(P x)})).
 
      destruct (dec (P x)) as [p|np].
 
    refine (Build_Equiv _ _ f _).
 
    refine (isequiv_adjointify
              _ (fun z => match z with
                          | inl (x;p) => x
                          | inr (x;np) => x
                          end) _ _).
 
 intros [[x p]|[x np]]; unfold f;
        destruct (dec (P x)) as [p'|np'].
 
 apply ap, ap, path_ishprop.
 
 apply ap, ap, path_ishprop.
 
      destruct (dec (P x)); cbn; reflexivity.
 
  Definition equiv_decidable_sum_l (a : A) (p : P a)
    : equiv_decidable_sum a = inl (a;p).
 
    unfold equiv_decidable_sum; cbn.
 
    destruct (dec (P a)) as [p'|np'].
 
 apply ap, path_sigma_hprop; reflexivity.
 
  Definition equiv_decidable_sum_r (a : A) (np : ~ (P a))
    : equiv_decidable_sum a = inr (a;np).
 
    unfold equiv_decidable_sum; cbn.
 
    destruct (dec (P a)) as [p'|np'].
 
 apply ap, path_sigma_hprop; reflexivity.
 
Definition transport_sum {A : Type} {P Q : A -> Type} {a a' : A} (p : a = a')
           (z : P a + Q a)
: transport (fun a => P a + Q a) p z = match z with
                                         | inl z' => inl (p # z')
                                         | inr z' => inr (p # z')
                                       end
  := match p with idpath => match z with inl _ => 1 | inr _ => 1 end end.
 
(** ** Detecting the summands *)
 
Definition is_inl_and {A B} (P : A -> Type@{p}) : A + B -> Type@{p}
  := fun x => match x with inl a => P a | inr _ => Empty end.
 
Definition is_inr_and {A B} (P : B -> Type@{p}) : A + B -> Type@{p}
  := fun x => match x with inl _ => Empty | inr b => P b end.
 
Definition is_inl {A B} : A + B -> Type0
  := is_inl_and (fun _ => Unit).
 
Definition is_inr {A B} : A + B -> Type0
  := is_inr_and (fun _ => Unit).
 
Instance ishprop_is_inl {A B} (x : A + B)
: IsHProp (is_inl x).
 
Instance ishprop_is_inr {A B} (x : A + B)
: IsHProp (is_inr x).
 
Instance decidable_is_inl {A B} (x : A + B)
: Decidable (is_inl x).
 
Instance decidable_is_inr {A B} (x : A + B)
: Decidable (is_inr x).
 
Definition un_inl {A B} (z : A + B)
: is_inl z -> A.
 
Definition un_inr {A B} (z : A + B)
: is_inr z -> B.
 
Definition is_inl_not_inr {A B} (x : A + B)  (na : ~ A)
: is_inr x
  := match x with
     | inl a => na a
     | inr b => tt
     end.
 
Definition is_inr_not_inl {A B} (x : A + B)  (nb : ~ B)
: is_inl x
  := match x with
     | inl a => tt
     | inr b => nb b
     end.
 
Definition un_inl_inl {A B : Type} (a : A) (w : is_inl (inl a))
: un_inl (@inl A B a) w = a
  := 1.
 
Definition un_inr_inr {A B : Type} (b : B) (w : is_inr (inr b))
: un_inr (@inr A B b) w = b
  := 1.
 
Definition inl_un_inl {A B : Type} (z : A + B) (w : is_inl z)
: inl (un_inl z w) = z.
 
  destruct z as [a|b]; simpl.
 
Definition inr_un_inr {A B : Type} (z : A + B) (w : is_inr z)
: inr (un_inr z w) = z.
 
  destruct z as [a|b]; simpl.
 
Definition not_is_inl_and_inr {A B} (P : A -> Type) (Q : B -> Type)
           (x : A + B)
: is_inl_and P x -> is_inr_and Q x -> Empty.
 
  destruct x as [a|b]; simpl.
 
Definition not_is_inl_and_inr' {A B} (x : A + B)
: is_inl x -> is_inr x -> Empty
  := not_is_inl_and_inr (fun _ => Unit) (fun _ => Unit) x.
 
Definition is_inl_or_is_inr {A B} (x : A + B)
: (is_inl x) + (is_inr x)
  := match x return (is_inl x) + (is_inr x) with
       | inl _ => inl tt
       | inr _ => inr tt
     end.
 
Definition is_inl_ind {A B : Type} (P : A + B -> Type)
           (f : forall a:A, P (inl a))
: forall (x:A+B), is_inl x -> P x.
 
  intros [a|b] H; [ exact (f a) | elim H ].
 
Definition is_inr_ind {A B : Type} (P : A + B -> Type)
           (f : forall b:B, P (inr b))
: forall (x:A+B), is_inr x -> P x.
 
  intros [a|b] H; [ elim H | exact (f b) ].
 
(** ** Functorial action *)
 
  Context {A A' B B' : Type} (f : A -> A') (g : B -> B').
 
  Definition functor_sum : A + B -> A' + B'
    := fun z => match z with inl z' => inl (f z') | inr z' => inr (g z') end.
 
  Definition functor_code_sum {z z' : A + B} (c : code_sum z z')
    : code_sum (functor_sum z) (functor_sum z').
 
  Definition ap_functor_sum {z z' : A + B} (c : code_sum z z')
    : ap functor_sum (path_sum c) = path_sum (functor_code_sum c).
 
  (** The fibers of [functor_sum] are those of [f] and [g]. *)
 
 
Definition hfiber_functor_sum_l (a' : A')
  : hfiber functor_sum (inl a') <~> hfiber f a'.
 
    simple refine (equiv_adjointify _ _ _ _).
 
        exact (path_sum_inl _ p).
 
      (** Why doesn't Coq find this? *)
     
 
pose (@isequiv_path_sum A' B' (inl (f a)) (inl a')).
 
      exact (eissect (@path_sum A' B' (inl (f a)) (inl a')) p).
 
        pose (@isequiv_path_sum A' B' (inl (f a)) (inl a')).
 
        exact (eisretr (@path_sum A' B' (inl (f a)) (inl a')) p).
 
  Definition hfiber_functor_sum_r (b' : B')
  : hfiber functor_sum (inr b') <~> hfiber g b'.
 
    simple refine (equiv_adjointify _ _ _ _).
 
        exact (path_sum_inr _ p).
 
      (** Why doesn't Coq find this? *)
     
 
pose (@isequiv_path_sum A' B' (inr (g b)) (inr b')).
 
      exact (eissect (@path_sum A' B' (inr (g b)) (inr b')) p).
 
        pose (@isequiv_path_sum A' B' (inr (g b)) (inr b')).
 
        exact (eisretr (@path_sum A' B' (inr (g b)) (inr b')) p).
 
Definition functor_sum_homotopic {A A' B B' : Type}
  {f f' : A -> A'} {g g' : B -> B'} (p : f == f') (q : g == g')
  : functor_sum f g == functor_sum f' g'.
 
Definition functor_sum_compose {A A' A'' B B' B'' : Type}
  {f : A -> A'} {f' : A' -> A''} {g : B -> B'} {g' : B' -> B''}
  : functor_sum (f' o f) (g' o g) == functor_sum f' g' o functor_sum f g.
 
  intros [a|b]; reflexivity.
 
Definition functor_sum_idmap {A B : Type}
  : functor_sum (A:=A) (B:=B) idmap idmap == idmap.
 
  intros [a|b]; reflexivity.
 
(** ** "Unfunctorial action" *)
(** Not every function [A + B -> A' + B'] is of the form [functor_sum f g].  However, this is the case if it preserves the summands, i.e. if it maps [A] into [A'] and [B] into [B'].  More generally, if a function [A + B -> A' + B'] maps [A] into [A'] only, then we can extract from it a function [A -> A'].  Since these operations are a sort of inverse to [functor_sum], we call them [unfunctor_sum_*]. *)
 
Definition unfunctor_sum_l {A A' B B' : Type} (h : A + B -> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
: A -> A'
  := fun a => un_inl (h (inl a)) (Ha a).
 
Definition unfunctor_sum_r {A A' B B' : Type} (h : A + B -> A' + B')
           (Hb : forall b:B, is_inr (h (inr b)))
: B -> B'
  := fun b => un_inr (h (inr b)) (Hb b).
 
Definition unfunctor_sum_eta {A A' B B' : Type} (h : A + B -> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: functor_sum (unfunctor_sum_l h Ha) (unfunctor_sum_r h Hb) == h.
 
 unfold unfunctor_sum_l; apply inl_un_inl.
 
 unfold unfunctor_sum_r; apply inr_un_inr.
 
Definition unfunctor_sum_l_beta {A A' B B' : Type} (h : A + B -> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
: inl o unfunctor_sum_l h Ha == h o inl.
 
  intros a; unfold unfunctor_sum_l; apply inl_un_inl.
 
Definition unfunctor_sum_r_beta {A A' B B' : Type} (h : A + B -> A' + B')
           (Hb : forall b:B, is_inr (h (inr b)))
: inr o unfunctor_sum_r h Hb == h o inr.
 
  intros b; unfold unfunctor_sum_r; apply inr_un_inr.
 
Definition unfunctor_sum_l_compose {A A' A'' B B' B'' : Type}
           (h : A + B -> A' + B') (k : A' + B' -> A'' + B'')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Ha' : forall a':A', is_inl (k (inl a')))
: unfunctor_sum_l k Ha' o unfunctor_sum_l h Ha
  == unfunctor_sum_l (k o h)
     (fun a => is_inl_ind (fun x' => is_inl (k x')) Ha' (h (inl a)) (Ha a)).
 
  refine (path_sum_inl B'' _).
 
  refine (unfunctor_sum_l_beta _ _ _ @ _).
 
  refine (ap k (unfunctor_sum_l_beta _ _ _) @ _).
 
  exact ((unfunctor_sum_l_beta _ _ _)^).
 
Definition unfunctor_sum_r_compose {A A' A'' B B' B'' : Type}
           (h : A + B -> A' + B') (k : A' + B' -> A'' + B'')
           (Hb : forall b:B, is_inr (h (inr b)))
           (Hb' : forall b':B', is_inr (k (inr b')))
: unfunctor_sum_r k Hb' o unfunctor_sum_r h Hb
  == unfunctor_sum_r (k o h)
     (fun b => is_inr_ind (fun x' => is_inr (k x')) Hb' (h (inr b)) (Hb b)).
 
  refine (path_sum_inr A'' _).
 
  refine (unfunctor_sum_r_beta _ _ _ @ _).
 
  refine (ap k (unfunctor_sum_r_beta _ _ _) @ _).
 
  exact ((unfunctor_sum_r_beta _ _ _)^).
 
(** [unfunctor_sum] also preserves fibers, if both summands are preserved. *)
 
Definition hfiber_unfunctor_sum_l {A A' B B' : Type}
           (h : A + B -> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
           (a' : A')
: hfiber (unfunctor_sum_l h Ha) a' <~> hfiber h (inl a').
 
  simple refine (equiv_adjointify _ _ _ _).
 
    symmetry; apply inl_un_inl.
 
      apply path_sum_inl with B'.
 
      abstract (rewrite p in Hb; elim Hb).
 
      exact (eisretr (@path_sum A' B' _ _)
                     (inl_un_inl (h (inl a)) (Ha a) @ p)).
 
      abstract (rewrite p in Hb; elim Hb).
 
    pose (@isequiv_path_sum
            A' B' (inl (unfunctor_sum_l h Ha a)) (inl a')).
 
    exact (eissect (@path_sum A' B' (inl (unfunctor_sum_l h Ha a)) (inl a')) p).
 
Definition hfiber_unfunctor_sum_r {A A' B B' : Type}
           (h : A + B -> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
           (b' : B')
: hfiber (unfunctor_sum_r h Hb) b' <~> hfiber h (inr b').
 
  simple refine (equiv_adjointify _ _ _ _).
 
    symmetry; apply inr_un_inr.
 
      abstract (rewrite p in Ha; elim Ha).
 
      apply path_sum_inr with A'.
 
      abstract (rewrite p in Ha; elim Ha).
 
      exact (eisretr (@path_sum A' B' _ _)
                     (inr_un_inr (h (inr b)) (Hb b) @ p)).
 
    pose (@isequiv_path_sum
            A' B' (inr (unfunctor_sum_r h Hb b)) (inr b')).
 
    exact (eissect (@path_sum A' B' (inr (unfunctor_sum_r h Hb b)) (inr b')) p).
 
(** ** Functoriality on equivalences *)
 
Instance isequiv_functor_sum `{IsEquiv A A' f} `{IsEquiv B B' g}
: IsEquiv (functor_sum f g) | 1000.
 
 exact (functor_sum f^-1 g^-1).
 
 intros [?|?]; simpl; apply ap; apply eisretr.
 
 intros [?|?]; simpl; apply ap; apply eissect.
 
 intros [?|?]; simpl; lhs napply (ap _ (eisadj _ _)); symmetry.
 
 exact ((ap_compose inl _ _)^ @ ap_compose f inl _).
 
 exact ((ap_compose inr _ _)^ @ ap_compose g inr _).
 
Definition equiv_functor_sum `{IsEquiv A A' f} `{IsEquiv B B' g}
: A + B <~> A' + B'
  := Build_Equiv _ _ (functor_sum f g) _.
 
Definition equiv_functor_sum' {A A' B B' : Type} (f : A <~> A') (g : B <~> B')
: A + B <~> A' + B'
  := equiv_functor_sum (f := f) (g := g).
 
Notation "f +E g" := (equiv_functor_sum' f g) : equiv_scope.
 
Definition equiv_functor_sum_l {A B B' : Type} (g : B <~> B')
: A + B <~> A + B'
  := 1 +E g.
 
Definition equiv_functor_sum_r {A A' B : Type} (f : A <~> A')
: A + B <~> A' + B
  := f +E 1.
 
Definition iff_functor_sum {A A' B B' : Type} (f : A <-> A') (g : B <-> B')
  : A + B <-> A' + B'
  := (functor_sum (fst f) (fst g), functor_sum (snd f) (snd g)).
 
(** ** Unfunctoriality on equivalences *)
 
Instance isequiv_unfunctor_sum_l {A A' B B' : Type}
           (h : A + B <~> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: IsEquiv (unfunctor_sum_l h Ha).
 
  simple refine (isequiv_adjointify _ _ _ _).
 
 refine (unfunctor_sum_l h^-1 _); intros a'.
 
    remember (h^-1 (inl a')) as x eqn:p.
 
 apply moveL_equiv_M in p.
 
    refine (unfunctor_sum_l_compose _ _ _ _ _ @ _).
 
    refine (path_sum_inl B' _).
 
    refine (unfunctor_sum_l_beta _ _ _ @ _).
 
    refine (unfunctor_sum_l_compose _ _ _ _ _ @ _).
 
    refine (path_sum_inl B _).
 
    refine (unfunctor_sum_l_beta (h^-1 o h) _ a @ _).
 
Definition equiv_unfunctor_sum_l {A A' B B' : Type}
           (h : A + B <~> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: A <~> A'
  := Build_Equiv _ _ (unfunctor_sum_l h Ha)
                (isequiv_unfunctor_sum_l h Ha Hb).
 
Instance isequiv_unfunctor_sum_r {A A' B B' : Type}
           (h : A + B <~> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: IsEquiv (unfunctor_sum_r h Hb).
 
  simple refine (isequiv_adjointify _ _ _ _).
 
 refine (unfunctor_sum_r h^-1 _); intros b'.
 
    remember (h^-1 (inr b')) as x eqn:p.
 
 apply moveL_equiv_M in p.
 
    refine (unfunctor_sum_r_compose _ _ _ _ _ @ _).
 
    refine (path_sum_inr A' _).
 
    refine (unfunctor_sum_r_beta _ _ _ @ _).
 
    refine (unfunctor_sum_r_compose _ _ _ _ _ @ _).
 
    refine (path_sum_inr A _).
 
    refine (unfunctor_sum_r_beta (h^-1 o h) _ b @ _).
 
Definition equiv_unfunctor_sum_r {A A' B B' : Type}
           (h : A + B <~> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: B <~> B'
  := Build_Equiv _ _ (unfunctor_sum_r h Hb)
                (isequiv_unfunctor_sum_r h Ha Hb).
 
Definition equiv_unfunctor_sum {A A' B B' : Type}
           (h : A + B <~> A' + B')
           (Ha : forall a:A, is_inl (h (inl a)))
           (Hb : forall b:B, is_inr (h (inr b)))
: (A <~> A') * (B <~> B')
  := (equiv_unfunctor_sum_l h Ha Hb , equiv_unfunctor_sum_r h Ha Hb).
 
(** ** Symmetry *)
(* This is a special property of [sum], of course, not an instance of a general family of facts about types. *)
 
Definition equiv_sum_symm@{u v k | u <= k, v <= k} (A : Type@{u}) (B : Type@{v})
  : Equiv@{k k} (A + B) (B + A).
 
  apply (equiv_adjointify
           (fun ab => match ab with inl a => inr a | inr b => inl b end)
           (fun ab => match ab with inl a => inr a | inr b => inl b end));
  intros [?|?]; exact idpath.
 
Definition equiv_sum_assoc (A B C : Type) : (A + B) + C <~> A + (B + C).
 
  simple refine (equiv_adjointify _ _ _ _).
 
 intros [[a|b]|c];
    [ exact (inl a) | exact (inr (inl b)) | exact (inr (inr c)) ].
 
 intros [a|[b|c]];
    [ exact (inl (inl a)) | exact (inl (inr b)) | exact (inr c) ].
 
 intros [a|[b|c]]; reflexivity.
 
 intros [[a|b]|c]; reflexivity.
 
Definition sum_empty_l@{u|} (A : Type@{u}) : Equiv@{u u} (Empty + A) A.
 
  snapply equiv_adjointify@{u u}.
 
 intros [e|a]; [ exact (Empty_rec@{u} e) | exact a ].
 
 intros a; exact (inr@{Set u} a).
 
 intro x; exact idpath@{u}.
 
 intros [e|z]; [ elim e | exact idpath@{u}].
 
Definition sum_empty_r@{u} (A : Type@{u}) : Equiv@{u u} (A + Empty) A
  := equiv_compose'@{u u u} (sum_empty_l A) (equiv_sum_symm@{u Set u} _ _).
 
Definition sum_distrib_l A B C
: A * (B + C) <~> (A * B) + (A * C).
 
  2: snapply Build_IsEquiv.
 
 exact (inl@{u u} (a, b)).
 
 exact (inr@{u u} (a, c)).
 
 intros [[a b]|[a c]]; reflexivity.
 
 intros [a [b|c]]; reflexivity.
 
 intros [a [b|c]]; reflexivity.
 
Definition sum_distrib_r A B C
: (B + C) * A <~> (B * A) + (C * A).
 
  refine (Build_Equiv ((B + C) * A) ((B * A) + (C * A))
            (fun abc => let (bc,a) := abc in
                        match bc with
                          | inl b => inl (b,a)
                          | inr c => inr (c,a)
                        end) _).
 
  simple refine (Build_IsEquiv ((B + C) * A) ((B * A) + (C * A)) _
            (fun ax => match ax with
                         | inl (b,a) => (inl b,a)
                         | inr (c,a) => (inr c,a)
                       end) _ _ _).
 
 intros [[b a]|[c a]]; reflexivity.
 
 intros [[b|c] a]; reflexivity.
 
 intros [[b|c] a]; reflexivity.
 
(** ** Extensivity *)
(** We can phrase extensivity in two ways, using either dependent types or functions. *)
(** The first is a statement about types dependent on a sum type. *)
 
Definition equiv_sigma_sum A B (C : A + B -> Type)
: { x : A+B & C x } <~>
  { a : A & C (inl a) } + { b : B & C (inr b) }.
 
  refine (Build_Equiv { x : A+B & C x }
                     ({ a : A & C (inl a) } + { b : B & C (inr b) })
           (fun xc => let (x,c) := xc in
                      match x return
                            C x -> ({ a : A & C (inl a) } + { b : B & C (inr b) })
                      with
                        | inl a => fun c => inl (a;c)
                        | inr b => fun c => inr (b;c)
                      end c) _).
 
  simple refine (Build_IsEquiv { x : A+B & C x }
                       ({ a : A & C (inl a) } + { b : B & C (inr b) }) _
           (fun abc => match abc with
                         | inl (a;c) => (inl a ; c)
                         | inr (b;c) => (inr b ; c)
                       end) _ _ _).
 
 intros [[a c]|[b c]]; reflexivity.
 
 intros [[a|b] c]; reflexivity.
 
 intros [[a|b] c]; reflexivity.
 
(** The second is a statement about functions into a sum type. *)
 
Definition decompose_l {A B C} (f : C -> A + B) : Type
  := { c:C & is_inl (f c) }.
 
Definition decompose_r {A B C} (f : C -> A + B) : Type
  := { c:C & is_inr (f c) }.
 
Definition equiv_decompose {A B C} (f : C -> A + B)
: decompose_l f + decompose_r f <~> C.
 
  simple refine (equiv_adjointify (sum_ind (fun _ => C) pr1 pr1) _ _ _).
 
 intros c; destruct (is_inl_or_is_inr (f c));
    [ left | right ]; exists c; assumption.
 
 intros c; destruct (is_inl_or_is_inr (f c)); reflexivity.
 
 intros [[c l]|[c r]]; simpl; destruct (is_inl_or_is_inr (f c)).
 
 apply ap, ap, path_ishprop.
 
 elim (not_is_inl_and_inr' _ l i).
 
 elim (not_is_inl_and_inr' _ i r).
 
 apply ap, ap, path_ishprop.
 
Definition is_inl_decompose_l {A B C} (f : C -> A + B)
           (z : decompose_l f)
: is_inl (f (equiv_decompose f (inl z)))
  := z.2.
 
Definition is_inr_decompose_r {A B C} (f : C -> A + B)
           (z : decompose_r f)
: is_inr (f (equiv_decompose f (inr z)))
  := z.2.
 
(** ** Indecomposability *)
(** A type is indecomposable if whenever it maps into a finite sum, it lands entirely in one of the summands.  It suffices to assert this for binary and nullary sums; in the latter case it reduces to nonemptiness. *)
 
Class Indecomposable (X : Type) :=
  { indecompose : forall A B (f : X -> A + B),
                    (forall x, is_inl (f x)) + (forall x, is_inr (f x))
  ; indecompose0 : ~~X }.
 
(** For instance, contractible types are indecomposable. *)
 
Instance indecomposable_contr `{Contr X} : Indecomposable X.
 
    destruct (is_inl_or_is_inr (f (center X))); [ left | right ]; intros x.
 
    all:refine (transport _ (ap f (contr x)) _); assumption.
 
 intros nx; exact (nx (center X)).
 
(** In particular, if an indecomposable type is equivalent to a sum type, then one summand is empty and it is equivalent to the other. *)
 
Definition equiv_indecomposable_sum {X A B} `{Indecomposable X}
           (f : X <~> A + B)
: ((X <~> A) * (Empty <~> B)) + ((X <~> B) * (Empty <~> A)).
 
  destruct (indecompose A B f) as [i|i]; [ left | right ].
 
  1:pose (g := (f oE sum_empty_r X)).
 
  2:pose (g := (f oE sum_empty_l X)).
 
  2:apply (equiv_prod_symm _ _).
 
  all:refine (equiv_unfunctor_sum g _ _); try assumption; try intros [].
 
(** Summing with an indecomposable type is injective on equivalence classes of types. *)
 
Definition equiv_unfunctor_sum_indecomposable_ll {A B B' : Type}
           `{Indecomposable A} (h : A + B <~> A + B')
: B <~> B'.
 
  pose (f := equiv_decompose (h o inl)).
 
  pose (g := equiv_decompose (h o inr)).
 
  pose (k := (h oE (f +E g))).
 
  (** This looks messy, but it just amounts to swapping the middle two summands in [k]. *)
 
 
pose (k' := k
                oE (equiv_sum_assoc _ _ _)
                oE ((equiv_sum_assoc _ _ _)^-1 +E 1)
                oE (1 +E (equiv_sum_symm _ _) +E 1)
                oE ((equiv_sum_assoc _ _ _) +E 1)
                oE (equiv_sum_assoc _ _ _)^-1).
 
  destruct (equiv_unfunctor_sum k'
       (fun x : decompose_l (h o inl) + decompose_l (h o inr) =>
        match x as x0 return (is_inl (k' (inl x0))) with
        | inl x0 => x0.2
        | inr x0 => x0.2
        end)
       (fun x : decompose_r (h o inl) + decompose_r (h o inr) =>
        match x as x0 return (is_inr (k' (inr x0))) with
        | inl x0 => x0.2
        | inr x0 => x0.2
        end)) as [s t]; clear k k'.
 
  refine (t oE (_ +E 1) oE g^-1).
 
  destruct (equiv_indecomposable_sum s^-1) as [[p q]|[p q]];
  destruct (equiv_indecomposable_sum f^-1) as [[u v]|[u v]].
 
 elim (indecompose0 (v^-1 o p)).
 
 refine (Empty_rec (indecompose0 _)); intros a.
 
    destruct (is_inl_or_is_inr (h (inl a))) as [l|r].
 
Definition equiv_unfunctor_sum_contr_ll {A A' B B' : Type}
           `{Contr A} `{Contr A'}
           (h : A + B <~> A' + B')
: B <~> B'
  := equiv_unfunctor_sum_indecomposable_ll
       ((equiv_contr_contr +E 1) oE h).
 
(** ** Universal mapping properties *)
(** Ordinary universal mapping properties are expressed as equivalences of sets or spaces of functions.  In type theory, we can go beyond this and express an equivalence of types of *dependent* functions. *)
 
Definition sum_ind_uncurried {A B} (P : A + B -> Type)
           (fg : (forall a, P (inl a)) * (forall b, P (inr b)))
: forall s, P s
  := @sum_ind A B P (fst fg) (snd fg).
 
(* First the positive universal property.
   Doing this sort of thing without adjointifying will require very careful use of funext. *)
 
Instance isequiv_sum_ind `{Funext} `(P : A + B -> Type)
: IsEquiv (sum_ind_uncurried P) | 0.
 
  apply (isequiv_adjointify
           (sum_ind_uncurried P)
           (fun f => (fun a => f (inl a), fun b => f (inr b))));
  repeat ((exact idpath)
            || intros []
            || intro
            || apply path_forall).
 
Definition equiv_sum_ind `{Funext} `(P : A + B -> Type)
  := Build_Equiv _ _ _ (isequiv_sum_ind P).
 
(* The non-dependent version, which is a special case, is the sum-distributive equivalence. *)
 
Definition equiv_sum_distributive `{Funext} (A B C : Type)
: (A -> C) * (B -> C) <~> (A + B -> C)
  := equiv_sum_ind (fun _ => C).
 
(** ** Sums preserve most truncation *)
 
Instance istrunc_sum n' (n := n'.+2)
         `{IsTrunc n A, IsTrunc n B}
: IsTrunc n (A + B) | 100.
 
  eapply istrunc_equiv_istrunc;
    [ exact (equiv_path_sum _ _) | ].
 
Instance ishset_sum `{HA : IsHSet A, HB : IsHSet B} : IsHSet (A + B) | 100
  := @istrunc_sum (-2) A HA B HB.
 
(** Sums don't preserve hprops in general, but they do for disjoint sums. *)
 
Instance ishprop_sum A B `{IsHProp A} `{IsHProp B}
: (A -> B -> Empty) -> IsHProp (A + B).
 
  apply hprop_allpath; intros [a1|b1] [a2|b2].
 
(** ** Decidability *)
(** Sums preserve decidability *)
 
Instance decidable_sum@{u v k | u <= k, v <= k} {A : Type@{u}} {B : Type@{v}}
  `{Decidable A} `{Decidable B}
  : Decidable@{k} (A + B).
 
  destruct (dec A) as [x1|y1].
 
 exact (inl@{k k} (inl x1)).
 
 destruct (dec@{v} B) as [x2|y2].
 
 exact (inl@{k k} (inr x2)).
 
 apply inr@{k k}; intros z.
 
(** Sums preserve decidable paths *)
 
Instance decidablepaths_sum {A B}
       `{DecidablePaths A} `{DecidablePaths B}
: DecidablePaths (A + B).
 
 destruct (dec_paths a1 a2) as [p|np].
 
      exact (np (path_sum^-1 p)).
 
 destruct (dec_paths b1 b2) as [p|np].
 
      exact (np (path_sum^-1 p)).
 
(** Because of [ishprop_sum], decidability of an hprop is again an hprop. *)
 
Instance ishprop_decidable_hprop `{Funext} A `{IsHProp A}
: IsHProp (Decidable A).
 
  unfold Decidable; refine (ishprop_sum _ _ _).
 
  intros a na; exact (na a).
 
(** ** Binary coproducts are equivalent to dependent sigmas where the first component is a [Bool]. *)
 
Definition sig_of_sum A B (x : A + B)
: { b : Bool & if b then A else B }
  := (_;
      match
        x as s
        return
        (if match s with
              | inl _ => true
              | inr _ => false
            end then A else B)
      with
        | inl a => a
        | inr b => b
      end).
 
Definition sum_of_sig A B (x : { b : Bool & if b then A else B })
: A + B
  := match x with
       | (true; a) => inl a
       | (false; b) => inr b
     end.
 
Instance isequiv_sig_of_sum A B : IsEquiv (@sig_of_sum A B) | 0.
 
  apply (isequiv_adjointify (@sig_of_sum A B)
                            (@sum_of_sig A B)).
 
 intros [[] ?]; exact idpath.
 
Instance isequiv_sum_of_sig A B : IsEquiv (sum_of_sig A B)
  := isequiv_inverse (@sig_of_sum A B).
 
(** An alternative way of proving the truncation property of [sum]. *)
 
Definition trunc_sum' n A B `{IsTrunc n Bool, IsTrunc n A, IsTrunc n B}
: (IsTrunc n (A + B)).
 
  eapply istrunc_equiv_istrunc; [ esplit;
                         exact (@isequiv_sum_of_sig _ _)
                       | ].