Theory Integer_Partitions

section "Integer Paritions"

theory Integer_Partitions
  imports
    "HOL-Library.Multiset"
    Common_Lemmas
    Card_Number_Partitions.Card_Number_Partitions
begin

subsection"Definition"

definition integer_partitions :: "nat  nat multiset set" where
  "integer_partitions i = {A. sum_mset A = i  0 ∉# A}"
text ‹Cardinality: Partition i› (from Card_Number_Partitions.Card_Number_Partitions› \cite{AFPnumpat})›
text "Example: integer_partitions 4 = {{4}, {3,1}, {2,2} {2,1,1}, {1,1,1,1}}›"

subsection"Algorithm"

fun integer_partitions_enum_aux :: "nat  nat  nat list list" where
  "integer_partitions_enum_aux 0 m = [[]]"
| "integer_partitions_enum_aux n m =
  [h#r . h  [1..< Suc (min n m)], r  integer_partitions_enum_aux (n-h) h]"

fun integer_partitions_enum :: "nat  nat list list" where
 "integer_partitions_enum n = integer_partitions_enum_aux n n"

subsection"Verification"

subsubsection"Correctness"

lemma integer_partitions_empty: "[]  set (integer_partitions_enum_aux n m)  n = 0"
  by(induct n) auto

lemma integer_partitions_enum_aux_first:
  "x # xs  set (integer_partitions_enum_aux n m)
     xs  set (integer_partitions_enum_aux (n-x) x)"
  by(induct n) auto

lemma integer_partitions_enum_aux_max_n:
  "x#xs  set (integer_partitions_enum_aux n m)  x  n"
  by (induct n) auto

lemma integer_partitions_enum_aux_max_head:
  "x#xs  set (integer_partitions_enum_aux n m)  x  m"
  by (induct n) auto

(*not used, but nice to have nonetheless*)
lemma integer_partitions_enum_aux_max:
  "xs  set (integer_partitions_enum_aux n m)  x  set xs  x  m"
proof(induct xs arbitrary: n m x)
  case Nil
  then show ?case using integer_partitions_enum_aux_max_head by simp
next
  case (Cons y xs)
  then show ?case 
    using integer_partitions_enum_aux_max_head integer_partitions_enum_aux_first  
    by fastforce                                         
qed

lemma integer_partitions_enum_aux_sum:
  "xs  set (integer_partitions_enum_aux n m)  sum_list xs = n"
proof(induct xs arbitrary: n m)
  case Nil
  then show ?case using integer_partitions_empty by simp
next
  case (Cons x xs)
  then have "xs  set (integer_partitions_enum_aux (n-x) x)  sum_list xs = (n-x)"
    by simp
  moreover have "xs  set (integer_partitions_enum_aux (n-x) x)"
    using Cons integer_partitions_enum_aux_first by simp
  moreover have "x  n"
    using Cons integer_partitions_enum_aux_max_n by simp
  ultimately show ?case
    by simp
qed

lemma integer_partitions_enum_aux_not_null_aux:
  "x#xs  set (integer_partitions_enum_aux n m)  x  0"
  by (induct n) auto

lemma integer_partitions_enum_aux_not_null:
  "xs  set (integer_partitions_enum_aux n m)  x  set xs  x  0"
proof(induct xs arbitrary: x n m)
  case Nil
  then show ?case by simp
next
  case (Cons y xs)
  show ?case proof(cases "y = x")
    case True
    then show ?thesis
      using Cons integer_partitions_enum_aux_not_null_aux by simp 
  next
    case False
    then show ?thesis
       using Cons integer_partitions_enum_aux_not_null_aux integer_partitions_enum_aux_first
       by fastforce
  qed
qed

lemma integer_partitions_enum_aux_head_minus:
     "h  m  h > 0  n  h 
  ys  set (integer_partitions_enum_aux (n-h) h) h#ys  set (integer_partitions_enum_aux n m)"
proof(induct n)
  case 0
  then show ?case by simp
next
  case (Suc n)
  then have 1: "1  m" by simp

  have 2: "(x. (x = min (Suc n) m  Suc 0  x  x < Suc n  x < m)  h # ys
       (#) x ` set (integer_partitions_enum_aux (Suc n - x) x))"
    unfolding image_def using Suc by auto

  from 1 2 have "Suc 0  m (x. (x = min (Suc n) m  Suc 0  x  x < Suc n  x < m)
          h # ys  (#) x ` set (integer_partitions_enum_aux (Suc n - x) x))"
    by simp
    
  then show ?case by auto
qed

lemma integer_partitions_enum_aux_head_plus:
  "h  m  h > 0  ys  set (integer_partitions_enum_aux n h)
     h#ys  set (integer_partitions_enum_aux (h + n) m)"
  using integer_partitions_enum_aux_head_minus by simp 

lemma integer_partitions_enum_correct_aux1:
  assumes "0 ∉# A "
  and "x ∈# A. x  m"
shows" xsset (integer_partitions_enum_aux (# A) m). A = mset xs"
using assms proof(induct A arbitrary: m rule: multiset_induct_max)
  case empty
  then show ?case by simp
next
  case (add h A)
  have hc1: "h  m"
    using add by simp

  have hc2: "h > 0"
    using add by simp

  obtain ys where o1: "ys  set (integer_partitions_enum_aux (# A) h)" and o2: " A = mset ys"
    using add by force

  have "h#ys  set (integer_partitions_enum_aux (h + # A) m)"
    using integer_partitions_enum_aux_head_plus hc1 o1 hc2 by blast 

  then show ?case
    using o2 by force
qed

theorem integer_partitions_enum_correct:
  "set (map mset (integer_partitions_enum n)) = integer_partitions n"
proof(standard)
  have "xs  set (integer_partitions_enum_aux n n)  # (mset xs) = n" for xs
    by (simp add: integer_partitions_enum_aux_sum sum_mset_sum_list)
  moreover have "xs  set (integer_partitions_enum_aux n n)  0 ∉# mset xs" for xs
    using integer_partitions_enum_aux_not_null by auto
  ultimately show "set (map mset (integer_partitions_enum n))  integer_partitions n"
    unfolding integer_partitions_def by auto
next
  have "0 ∉# A  A  mset ` set (integer_partitions_enum_aux (# A) (# A))" for A
    unfolding image_def
    using integer_partitions_enum_correct_aux1 by (simp add: sum_mset.remove)
  then show "integer_partitions n  set (map mset (integer_partitions_enum n))"
    unfolding integer_partitions_def by auto
qed

subsubsection"Distinctness"

lemma integer_partitions_enum_aux_distinct:
  "distinct (integer_partitions_enum_aux n m)"
proof(induct n m rule:integer_partitions_enum_aux.induct)
  case (1 m)
  then show ?case by simp
next
  case (2 n m)
  have "distinct [h#r . h  [1..< Suc (min (Suc n) m)], r  integer_partitions_enum_aux ((Suc n)-h) h]"
    apply(subst Cons_distinct_concat_map_function)
    using 2 by auto
  then show ?case by simp
qed

theorem integer_partitions_enum_distinct:
  "distinct (integer_partitions_enum n)"
  using integer_partitions_enum_aux_distinct by simp

subsubsection"Cardinality"

lemma partitions_bij_betw_count:
  "bij_betw count {N. count N partitions n} {p. p partitions n}"
  by (rule bij_betw_byWitness[where f'="Abs_multiset"]) (auto simp: partitions_imp_finite_elements)

lemma card_partitions_count_partitions:
  "card {p. p partitions n} = card {N. count N partitions n}"
  using bij_betw_same_card partitions_bij_betw_count by metis

text"this sadly is not proven in Card_Number_Partitions.Card_Number_Partitions›"
lemma card_partitions_number_partition:
  "card {p. p partitions n} = card {N. number_partition n N}"
  using card_partitions_count_partitions count_partitions_iff by simp

lemma integer_partitions_number_partition_eq:
  "integer_partitions n = {N. number_partition n N}"
  using integer_partitions_def number_partition_def by auto

lemma integer_partitions_cardinality_aux:
  "card (integer_partitions n) = (kn. Partition n k)"
  using card_partitions_number_partition integer_partitions_number_partition_eq card_partitions
  by simp

theorem integer_partitions_cardinality:
  "card (integer_partitions n) = Partition (2*n) n"
  using integer_partitions_cardinality_aux Partition_sum_Partition_diff add_implies_diff le_add1 mult_2
  by simp  

end