(** This module presents a proof of the pigeon hole principle, which states that if you place n things into m containers, and n > m, then one or more of the containers have more than one thing in it.  Our proof proceeds by contradiction. We first show that if we have a collection of containers, cs, where every container is either empty or has a single item in it, then the number of items contained in all of the containers is less than or equal to the number of containers.  Then we show that if, the number of items contained within a collection of containers is greater than the number of containers, and every container is either empty or has a single item in it, we have a contradiction. Accordingly, we cannot place n items into m collections without placing more than one item in a single container.  Copyright (C) 2018 Larry D. Lee Jr. <llee454@gmail.com>  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.  You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. *)
Module Pigeonhole_Principle.
(** * I. Basic Concepts *)
(** Represents things (such as pigeons). *)
Parameter thing : Set.
(** Represents containers (such as cubbies). *)
Definition container := list thing.
(** Represents some collection of containers. *)
Definition containers := list container.
(** Counts the number of things in a container. *)
Definition container_num_things := length (A := thing).
(** Counts the number of containers. *)
Definition num_containers := length (A := container).
(** Counts the number of things in some containers. *)
Definition num_things (cs : containers) : nat
:= fold_right (fun c n => container_num_things c + n) 0 cs.
(** Accepts a container and asserts that it is either empty or has only one item in it. *)
Definition container_ok (c : container) : Prop
:= container_num_things c <= 1.
(** This lemma proves that the OK predicate is decideable.  Note: we will need this when proving that [~ Forall container_ok] implies [Exists ~ container_ok]. *)
Definition container_ok_dec
:  forall (c : container), {container_ok c}+{~ container_ok c}
:= fun c => le_dec (container_num_things c) 1.
(** Accepts a set of containers and asserts that all of the containers are either empty or only contain a single item. *)
Definition containers_ok (cs : containers) : Prop
:= Forall container_ok cs.
(** * II. Fundamental Proof *)

(* Specifications: *)
(** This theorem proves that given a collection of containers, where every container is either empty or has one item in it, then the number of items contained in all of the containers must be less than or equal to the number of containers. *)
Theorem num_things_num_containers
:  forall cs : containers,
containers_ok cs ->
num_things cs <= num_containers cs.