Built with Alectryon, running Coq+SerAPI v8.19.0+0.19.3. 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.
[Loading ML file number_string_notation_plugin.cmxs (using legacy method) ... done]
Local Set Universe Minimization ToSet.Local Unset Elimination Schemes.LocalOpen 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]. *)Fixpointnat_choose_stepnf :=
match n with
| 0 => 0
| S n' => f n' + nat_choose_step n' f
end.Fixpointnat_choose (nm : nat) {structm} : nat
:= match m with
| 0 => 1
| S m' => nat_choose_step n (funn => 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. *)Definitionnat_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. *)Definitionnat_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. *)Definitionnat_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 01 = 0
m: nat H: 1 <= m
nat_choose 0 m.+1 = 0
n: nat IHn: forallm : nat, n < m -> nat_choose n m = 0
nat_choose n.+1 n.+2 = 0
n: nat IHn: forallm : nat, n < m -> nat_choose n m = 0 m: nat H: n.+2 <= m
nat_choose n.+1 m.+1 = 0
n: nat IHn: forallm : nat, n < m -> nat_choose n m = 0
nat_choose n.+1 n.+2 = 0
n: nat IHn: forallm : nat, n < m -> nat_choose n m = 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.+11 = 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. *)
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))
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