Built with Alectryon. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.
Require Import Basics.Overture Basics.Tactics Basics.PathGroupoidsRequire Import Basics.Overture Basics.Tactics Basics.PathGroupoids
  Basics.Decidable Spaces.Nat.Core Spaces.Nat.Division Spaces.Nat.Factorial
  Tactics.EvalIn.

Local Set Universe Minimization ToSet.
Local Unset Elimination Schemes.

Local Open Scope nat_scope.

(** * Binomial coefficients *)

(** ** Definition *)

(** The binomial coefficient [nat_choose n m] is the number of ways to choose [m] elements from a set of [n] elements. We define it recursively using Pascal's identity. We use nested [Fixpoint]s in order to get a function that computes definitionally on the edge cases as well as the inductive case. *)

(** We separate out this helper result to prevent Coq from unfolding things into a larger term. This computes [n] choose [m.+1] given a function [f] that computes [n] choose [m]. *)
Fixpoint nat_choose_step n f :=
  match n with
  | 0 => 0
  | S n' => f n' + nat_choose_step n' f
  end.

Fixpoint nat_choose (n m : nat) {struct m} : nat
  := match m with
    | 0 => 1
    | S m' => nat_choose_step n (fun n => nat_choose n m')
    end.

(** We make sure we never unfold binomial coefficients with [simpl] or [cbn] since the terms do not look good. Instead, we should use lemmas we prove about it to make proofs clearer to read. *)
Arguments nat_choose n m : simpl never.

(** ** Properties *)

(** The three defining properties of [nat_choose] hold definitionally. *)

(** By definition, we have Pascal's identity. *)
Definition nat_choose_succ@{} n m
  : nat_choose n.+1 m.+1 = nat_choose n m + nat_choose n m.+1
  := idpath.

(** There is only one way to choose [0] elements from any number of elements. *)
Definition nat_choose_zero_r@{} n : nat_choose n 0 = 1
  := idpath.

(** There are no ways to choose a non-zero number of elements from [0] elements. *)
Definition nat_choose_zero_l@{} m : nat_choose 0 m.+1 = 0
  := idpath.

(** The binomial coefficient is zero if [m] is greater than [n]. *)
n, m: nat

n < m -> nat_choose n m = 0
n, m: nat

n < m -> nat_choose n m = 0

nat_choose 0 1 = 0
m: nat
H: 1 <= m
nat_choose 0 m.+1 = 0
n: nat
IHn: forall m : nat, n < m -> nat_choose n m = 0
nat_choose n.+1 n.+2 = 0
n: nat
IHn: forall m0 : nat, n < m0 -> nat_choose n m0 = 0
m: nat
H: n.+2 <= m
nat_choose n.+1 m.+1 = 0
n: nat
IHn: forall m : nat, n < m -> nat_choose n m = 0

nat_choose n.+1 n.+2 = 0
n: nat
IHn: forall m0 : nat, n < m0 -> nat_choose n m0 = 0
m: nat
H: n.+2 <= m
nat_choose n.+1 m.+1 = 0
1, 2: rewrite_refl nat_choose_succ; exact (ap011 nat_add (IHn _ _) (IHn _ _)). Defined. (** There is only one way to choose [n] elements from [n] elements. *)
n: nat

nat_choose n n = 1
n: nat

nat_choose n n = 1
n: nat
IHn: nat_choose n n = 1

nat_choose n.+1 n.+1 = 1
n: nat
IHn: nat_choose n n = 1

nat_choose n n + nat_choose n n.+1 = 1
n: nat
IHn: nat_choose n n = 1

nat_choose n n + nat_choose n n.+1 = 1 + 0
n: nat
IHn: nat_choose n n = 1

nat_choose n n = 1
n: nat
IHn: nat_choose n n = 1
nat_choose n n.+1 = 0
n: nat
IHn: nat_choose n n = 1

nat_choose n n = 1
exact IHn.
n: nat
IHn: nat_choose n n = 1

nat_choose n n.+1 = 0
rapply nat_choose_lt. Defined. (** There are [n] ways to choose [1] element from [n] elements. *)
n: nat

nat_choose n 1 = n
n: nat

nat_choose n 1 = n
n: nat
IHn: nat_choose n 1 = n

nat_choose n.+1 1 = n.+1
n: nat
IHn: nat_choose n 1 = n

nat_choose n 0 + nat_choose n 1 = n.+1
exact (ap nat_succ IHn). Defined. (** There are [n.+1] ways to choose [n] elements from [n.+1] elements. *)
n: nat

nat_choose n.+1 n = n.+1
n: nat

nat_choose n.+1 n = n.+1
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+2 n.+1 = n.+2
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+1 n + nat_choose n.+1 n.+1 = n.+2
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+1 n + nat_choose n.+1 n.+1 = n.+1 + 1
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+1 n = n.+1
n: nat
IHn: nat_choose n.+1 n = n.+1
nat_choose n.+1 n.+1 = 1
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+1 n = n.+1
exact IHn.
n: nat
IHn: nat_choose n.+1 n = n.+1

nat_choose n.+1 n.+1 = 1
apply nat_choose_diag. Defined. (** The binomial coefficients can be written as a quotient of factorials. This is typically used as a definition of the binomial coefficients. *)
n, m: nat

m <= n -> nat_choose n m = factorial n / (factorial m * factorial (n - m))
n, m: nat

m <= n -> nat_choose n m = factorial n / (factorial m * factorial (n - m))
n: nat

nat_choose n 0 = factorial n / (factorial 0 * factorial (n - 0))
n: nat
nat_choose n.+1 n.+1 = factorial n.+1 / (factorial n.+1 * factorial (n.+1 - n.+1))
n: nat
forall m : nat, m < n -> (forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))) -> nat_choose n.+1 m.+1 = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
(* The case when [m = 0]. *)
n: nat

nat_choose n 0 = factorial n / (factorial 0 * factorial (n - 0))
n: nat

nat_choose n 0 = factorial n / factorial (n - 0)
n: nat

nat_choose n 0 = factorial n / factorial n
symmetry; rapply nat_div_cancel.
n: nat

nat_choose n.+1 n.+1 = factorial n.+1 / (factorial n.+1 * factorial (n.+1 - n.+1))
n: nat
forall m : nat, m < n -> (forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))) -> nat_choose n.+1 m.+1 = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
(* The case when [m = n]. *)
n: nat

nat_choose n.+1 n.+1 = factorial n.+1 / (factorial n.+1 * factorial (n.+1 - n.+1))
n: nat

nat_choose n.+1 n.+1 = factorial n.+1 / (factorial n.+1 * factorial 0)
n: nat

nat_choose n.+1 n.+1 = factorial n.+1 / factorial n.+1
n: nat

nat_choose n.+1 n.+1 = 1
napply nat_choose_diag.
n: nat

forall m : nat, m < n -> (forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))) -> nat_choose n.+1 m.+1 = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
(* The case with [n.+1] and [m.+1] with [m < n] and an induction hypothesis. *)
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

nat_choose n.+1 m.+1 = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

nat_choose n m + nat_choose n m.+1 = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

factorial n / (factorial m * factorial (n - m)) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
m.+1 <= n
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
m <= n
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

factorial n / (factorial m * factorial (n - m)) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (m.+1 * (factorial m * factorial (n - m))) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
0 < m.+1
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (m.+1 * (factorial m * factorial (n - m))) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (m.+1 * factorial m * factorial (n - m)) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + factorial n / (factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / ((n - m) * (factorial m.+1 * factorial (n - m.+1))) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
0 < n - m
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / ((n - m) * (factorial m.+1 * factorial (n - m.+1))) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / ((n - m) * factorial m.+1 * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / (factorial m.+1 * (n - m) * factorial (n - m.+1)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / (factorial m.+1 * ((n - m) * factorial (n - m.+1))) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / (factorial m.+1 * ((n - m) * factorial (nat_pred (n - m)))) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
0 < n - m
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) + ((n - m) * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(factorial m.+1 * factorial (n - m) | m.+1 * factorial n)
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
(m.+1 * factorial n + (n - m) * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(factorial m.+1 * factorial (n - m) | m.+1 * factorial n)
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial m * factorial (n - m) | m.+1 * factorial n)
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * (factorial m * factorial (n - m)) | m.+1 * factorial n)
exact _.
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(m.+1 * factorial n + (n - m) * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

((m.+1 + (n - m)) * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

((m + (n - m)).+1 * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(n.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))
m <= n
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

(n.+1 * factorial n) / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
n, m: nat
H: m < n
IHn: forall m' : nat, m' <= n -> nat_choose n m' = factorial n / (factorial m' * factorial (n - m'))

factorial n.+1 / (factorial m.+1 * factorial (n - m)) = factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1))
reflexivity. Defined. (** Another recursive property of the binomial coefficients. To choose [m+1] elements from a set of size [n+1], one has [n+1] choices for the first element and then can make the remaining [m] choices from the remaining [n] elements. This overcounts by a factor of [m+1], since there are [m+1] elements that could have been called the "first" element. *)
n, m: nat

nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat

nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat
H: m <= n

nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat
H: m > n
nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat
H: m > n

nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat
H: m > n

0 = (n.+1 * 0) / m.+1
n, m: nat
H: m > n

0 = 0 / m.+1
symmetry; apply nat_div_zero_l.
n, m: nat
H: m <= n

nat_choose n.+1 m.+1 = (n.+1 * nat_choose n m) / m.+1
n, m: nat
H: m <= n

factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1)) = (n.+1 * (factorial n / (factorial m * factorial (n - m)))) / m.+1
n, m: nat
H: m <= n
m <= n
n, m: nat
H: m <= n
m.+1 <= n.+1
n, m: nat
H: m <= n

factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1)) = (n.+1 * (factorial n / (factorial m * factorial (n - m)))) / m.+1
n, m: nat
H: m <= n

factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1)) = ((n.+1 * factorial n) / (factorial m * factorial (n - m))) / m.+1
n, m: nat
H: m <= n
(factorial m * factorial (n - m) | factorial n)
n, m: nat
H: m <= n

factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1)) = ((n.+1 * factorial n) / (factorial m * factorial (n - m))) / m.+1
n, m: nat
H: m <= n

factorial n.+1 / (factorial m.+1 * factorial (n.+1 - m.+1)) = (n.+1 * factorial n) / (factorial m * factorial (n - m) * m.+1)
by rewrite (nat_mul_comm _ m.+1), nat_mul_assoc. Defined. (** The binomial coefficients are symmetric about the middle of the range [0 <= n]. *)
n, m: nat

m <= n -> nat_choose n m = nat_choose n (n - m)
n, m: nat

m <= n -> nat_choose n m = nat_choose n (n - m)
n, m: nat
H: m <= n

nat_choose n m = nat_choose n (n - m)
n, m: nat
H: m <= n

factorial n / (factorial m * factorial (n - m)) = factorial n / (factorial (n - m) * factorial (n - (n - m)))
n, m: nat
H: m <= n
n - m <= n
n, m: nat
H: m <= n
m <= n
n, m: nat
H: m <= n

factorial n / (factorial m * factorial (n - m)) = factorial n / (factorial (n - m) * factorial (n - (n - m)))
n, m: nat
H: m <= n

factorial n / (factorial m * factorial (n - m)) = factorial n / (factorial (n - m) * factorial m)
n, m: nat
H: m <= n
m <= n
n, m: nat
H: m <= n

factorial n / (factorial m * factorial (n - m)) = factorial n / (factorial (n - m) * factorial m)
by rewrite nat_mul_comm. Defined.