(** * 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.

(* Specifications: *)
Theorem num_things_num_containers : forall cs : containers, containers_ok cs -> num_things cs <= num_containers cs.
Lemma lemma_0 : forall cs : containers, num_things cs > num_containers cs -> ~ containers_ok cs.
Lemma lemma_1 : forall cs : containers, num_things cs > num_containers cs -> Exists (fun c : container => ~ container_ok c) cs.
Theorem pigeonhole_principle : forall cs : containers, num_things cs > num_containers cs -> Exists (fun c : container => container_num_things c > 1) cs.