Theory Integer_Compositions

section "Integer Compositions"

theory Integer_Compositions
  imports
    Common_Lemmas
begin

subsection"Definition"

definition integer_compositions :: "nat  nat list set" where
  "integer_compositions i = {xs. sum_list xs = i  0  set xs}"
text "Integer compositions are integer_partitions› where the order matters."
text "Cardinality: sum from n = 1 to i (binomial (i-1) (n-1)) = 2^(i-1)›"
text "Example: integer_compositions 3 = {[3], [2,1], [1,2], [1,1,1]}›"

subsection"Algorithm"

fun integer_composition_enum :: "nat  nat list list" where
  "integer_composition_enum 0 = [[]]"
| "integer_composition_enum (Suc n) =
    [Suc m #xs. m  [0..< Suc n], xs  integer_composition_enum (n-m)]"

subsection"Verification"

subsubsection"Correctness"

lemma integer_composition_enum_tail_elem:
  "x#xs  set (integer_composition_enum n)  xs  set (integer_composition_enum (n - x))"
  by(induct n) auto

lemma integer_composition_enum_not_null_aux:
  "x#xs  set (integer_composition_enum n)  x  0"
  by(induct n) auto

lemma integer_composition_enum_not_null: "xs  set (integer_composition_enum n)  0  set xs"
proof(induct xs arbitrary: n)
  case Nil
  then show ?case
    by simp
next
  case (Cons a xs)
  then show ?case 
    using integer_composition_enum_not_null_aux integer_composition_enum_tail_elem
    by fastforce
qed

lemma integer_composition_enum_empty: "[]  set (integer_composition_enum n)  n = 0"
  by(induct n) auto

lemma integer_composition_enum_sum: "xs  set (integer_composition_enum n)  sum_list xs = n"
proof(induct n arbitrary: xs rule: integer_composition_enum.induct)
  case 1
  then show ?case by simp
next
  case (2 x)
  show ?case proof(cases xs)
    case Nil
    then show ?thesis using 2 by auto
  next
    case (Cons y ys)
    have p1: "sum_list ys = Suc x - y" using 2 Cons
      by auto

    have "Suc x  y"
      using 2 Cons by auto
    then have p2: "sum_list ys = Suc x - y  y + sum_list ys = Suc x"
      by simp

    show ?thesis
      using p1 p2 Cons by simp
  qed
qed

lemma integer_composition_enum_head_set:
  assumes"x  0" and "x  n"
  shows" xs  set (integer_composition_enum (n-x))  x#xs  set (integer_composition_enum n)"
using assms proof(induct n arbitrary: x xs)
  case 0
  then show ?case
    by simp
next
  case c1: (Suc n)
  from c1.prems have 1:
    "y{0..<n}. x = Suc y  xs  set (integer_composition_enum (n - y))  x = Suc n"
    by(induct x) simp_all

  then have 2: "y{0..<n}. x = Suc y  xs  set (integer_composition_enum (n - y))  xs = []"
    using c1.prems(1) by simp
  show ?case using 1 2 by auto
qed

lemma integer_composition_enum_correct_aux:
  "0  set xs  xs  set (integer_composition_enum (sum_list xs))"
  by(induct xs) (auto simp: integer_composition_enum_head_set) 

theorem integer_composition_enum_correct: 
  "set (integer_composition_enum n) = integer_compositions n"
proof standard
  show "set (integer_composition_enum n)  integer_compositions n"
    unfolding integer_compositions_def
    using integer_composition_enum_not_null integer_composition_enum_sum by auto
next
  show "integer_compositions n  set (integer_composition_enum n)"
    unfolding integer_compositions_def
    using integer_composition_enum_correct_aux by auto
qed

subsubsection"Distinctness"

theorem integer_composition_enum_distinct:
  "distinct (integer_composition_enum n)"
proof(induct n rule: integer_composition_enum.induct)
  case 1
  then show ?case by auto
next
  case (2 n)

  have h1: "x  set [0..<Suc n]  distinct (integer_composition_enum (n - x))" for x
    using "2" by simp
    
  have h2: "distinct [0..<n]"
    by simp

  have "distinct [Suc m #xs. m  [0..< n], xs  integer_composition_enum (n-m)]"
    using h1 h2 Cons_Suc_distinct_concat_map_function by simp
  then show ?case by auto 
qed

subsubsection"Cardinality"

lemma sum_list_two_pow_aux:
  "(x[0..< n]. (2::nat) ^ (n - x)) + 2 ^ (0 - 1) + 2 ^ 0 = 2 ^ (Suc n)"
proof(induct n)
  case 0
  then show ?case by simp
next
  case c1: (Suc n)

  have "x  n  2 ^ (Suc n - x) = 2 * 2^ (n - x)" for x
    by (simp add: Suc_diff_le)
  also have "x  set [0..<Suc n]  x  n" for x
    by auto
  ultimately have "(x[0..<Suc n]. 2 ^ (Suc n - x)) = (x[0..<Suc n]. 2* 2 ^ (n - x))"
     by (metis (mono_tags, lifting) map_eq_conv)
  
  also have "... = (x[0..< n]. 2* 2 ^ (n - x)) + 2* 2 ^ (0)"
    using sum_list_extract_last by simp
  
  also have "(x[0..< n]. (2::nat)* (2::nat) ^ (n - x)) = 2*(x[0..< n]. 2 ^ (n - x))"
    using sum_list_const_mult by fast

  ultimately have "(x[0..<Suc n]. (2::nat) ^ (Suc n - x))
      = 2*(x[0..< n]. 2 ^ (n - x)) + 2* 2 ^ (0)"
    by metis

  then show ?case using c1
    by simp 
qed

lemma sum_list_two_pow:
  "Suc (x[0..<n]. 2 ^ (n - Suc x)) = 2 ^ n"
  using sum_list_two_pow_aux sum_list_extract_last by(cases n) auto

lemma integer_composition_enum_length:
  "length (integer_composition_enum n) = 2^(n-1)"
proof(induct n rule: integer_composition_enum.induct)
  case 1
  then show ?case by simp
next
  case (2 n)
  then have "length [Suc m #xs. m  [0..< n], xs  integer_composition_enum (n-m)]
        = (x[0..<n]. 2 ^ (n - x - 1))"
    using length_concat_map_function_sum_list [of
        "[0..< n]"
        "λ x. integer_composition_enum (n - x)"
        "λ x. 2 ^ (n - x - 1)"
        "λ m xs. Suc m #xs"]
    by auto

  then show ?case
    using sum_list_two_pow
    by simp
qed

theorem integer_compositions_card:
  "card (integer_compositions n) = 2^(n-1)"
  using integer_composition_enum_correct integer_composition_enum_length
    integer_composition_enum_distinct distinct_card by metis

end