Theory Cauchy_Binet

(*
    Author:      Jose Divasón
    Email:       jose.divason@unirioja.es
*)

section ‹The Cauchy--Binet formula›

theory Cauchy_Binet
  imports
  Diagonal_To_Smith
  SNF_Missing_Lemmas
begin

subsection ‹Previous missing results about @{text "pick"} and @{text "insert"}

lemma pick_insert:
  assumes a_notin_I: "a  I" and i2: "i < card I"
    and a_def: "pick (insert a I) a' = a" (*Alternative: index (insort a (sorted_list_of_set I)) a = a'*)
    and ia': "i < a'" (*Case 1*)
    and a'_card: "a' < card I + 1"
  shows "pick (insert a I) i = pick I i"
proof -
  have finI: "finite I"
    using i2
    using card.infinite by force
  have "pick (insert a I) i = sorted_list_of_set (insert a I) ! i"
  proof (rule sorted_list_of_set_eq_pick[symmetric])
    have "finite (insert a I)"
      using card.infinite i2 by force
    thus "i < length (sorted_list_of_set (insert a I))"
      by (metis a_notin_I card_insert_disjoint distinct_card finite_insert
          i2 less_Suc_eq sorted_list_of_set(1) sorted_list_of_set(3))
  qed
  also have "... = insort a (sorted_list_of_set I) ! i"
    by (simp add: a_notin_I finI)
  also have "... = (sorted_list_of_set I) ! i"
  proof (rule insort_nth[OF])
     show "sorted (sorted_list_of_set I)" by auto
     show "a  set (sorted_list_of_set I)" using a_notin_I
       by (metis card.infinite i2 not_less_zero set_sorted_list_of_set)
     have "index (sorted_list_of_set (insert a I)) a = a'"
       using pick_index a_def
       using a'_card a_notin_I finI by auto
     hence "index (insort a (sorted_list_of_set I)) a = a'"
       by (simp add: a_notin_I finI)
     thus "i < index (insort a (sorted_list_of_set I)) a" using ia' by auto
     show "sorted_list_of_set I  []" using finI i2 by fastforce
   qed
  also have "... = pick I i"
  proof (rule sorted_list_of_set_eq_pick)
    have "finite I" using card.infinite i2 by fastforce
    thus "i < length (sorted_list_of_set I)"
      by (metis distinct_card distinct_sorted_list_of_set i2 set_sorted_list_of_set)
  qed
  finally show ?thesis .
qed


lemma pick_insert2:
  assumes a_notin_I: "a  I" and i2: "i < card I"
    and a_def: "pick (insert a I) a' = a" (*Alternative: index (sorted_list_of_set (insert a I)) a = a'*)
    and ia': "i  a'" (*Case 2*)
    and a'_card: "a' < card I + 1"
  shows "pick (insert a I) i < pick I i"
proof (cases "i = 0")
  case True
  then show ?thesis
    by (auto, metis (mono_tags, lifting) DL_Missing_Sublist.pick.simps(1) Least_le a_def a_notin_I
        dual_order.order_iff_strict i2 ia' insertCI le_zero_eq not_less_Least pick_in_set_le)
next
  case False
  hence i0: "i = Suc (i - 1)" using a'_card ia' by auto
  have finI: "finite I"
    using i2 card.infinite by force
  have index_a'1: "index (sorted_list_of_set (insert a I)) a = a'"
    using pick_index
    using a'_card a_def a_notin_I finI by auto
  hence index_a': "index (insort a (sorted_list_of_set I)) a = a'"
    by (simp add: a_notin_I finI)
  have i1_length: "i - 1 < length (sorted_list_of_set I)" using i2
    by (metis distinct_card distinct_sorted_list_of_set finI
        less_imp_diff_less set_sorted_list_of_set)
  have 1: "pick (insert a I) i = sorted_list_of_set (insert a I) ! i"
  proof (rule sorted_list_of_set_eq_pick[symmetric])
    have "finite (insert a I)"
      using card.infinite i2 by force
    thus "i < length (sorted_list_of_set (insert a I))"
      by (metis a_notin_I card_insert_disjoint distinct_card finite_insert
          i2 less_Suc_eq sorted_list_of_set(1) sorted_list_of_set(3))
  qed
  also have 2: "... = insort a (sorted_list_of_set I) ! i"
    by (simp add: a_notin_I finI)
  also have "... = insort a (sorted_list_of_set I) ! Suc (i-1)" using i0 by auto
  also have "... < pick I i"
  proof (cases "i = a'")
    case True
    have "(sorted_list_of_set I) ! i > a"
      by (smt "1" Suc_less_eq True a_def a_notin_I distinct_card distinct_sorted_list_of_set finI i2
          ia' index_a' insort_nth2 length_insort lessI list.size(3) nat_less_le not_less_zero
          pick_in_set_le set_sorted_list_of_set sorted_list_of_set(2)
          sorted_list_of_set_insert
          sorted_list_of_set_eq_pick sorted_wrt_nth_less)
    moreover have "a = insort a (sorted_list_of_set I) ! i" using True 1 2 a_def by auto
    ultimately show ?thesis using 1 2
      by (metis distinct_card finI i0 i2 set_sorted_list_of_set
          sorted_list_of_set(3) sorted_list_of_set_eq_pick)
  next
    case False
    have "insort a (sorted_list_of_set I) ! Suc (i-1) = (sorted_list_of_set I) ! (i-1)"
      by (rule insort_nth2, insert i1_length False ia' index_a', auto simp add: a_notin_I finI)
    also have "... = pick I (i-1)"
      by (rule sorted_list_of_set_eq_pick[OF i1_length])
    also have "... < pick I i" using i0 i2 pick_mono_le by auto
    finally show ?thesis .
  qed
  finally show ?thesis .
qed

lemma pick_insert3:
  assumes a_notin_I: "a  I" and i2: "i < card I"
    and a_def: "pick (insert a I) a' = a" (*Alternative: index (sorted_list_of_set (insert a I)) a = a'.*)
    and ia': "i  a'" (*Case 2*)
    and a'_card: "a' < card I + 1"
  shows "pick (insert a I) (Suc i) = pick I i"
proof (cases "i = 0")
  case True
  have a_LEAST: "a < (LEAST aa. aaI)"
    using True a_def a_notin_I i2 ia' pick_insert2 by fastforce
  have Least_rw: "(LEAST aa. aa = a  aa  I) = a"
    by (rule Least_equality, insert a_notin_I, auto,
        metis a_LEAST le_less_trans nat_le_linear not_less_Least)
  let ?P = "λaa. (aa = a  aa  I)  (LEAST aa. aa = a  aa  I) < aa"
  let ?Q = "λaa. aa  I"
  have "?P = ?Q" unfolding Least_rw fun_eq_iff
    by (auto, metis a_LEAST le_less_trans not_le not_less_Least)
  thus ?thesis using True by auto
next
  case False
  have finI: "finite I"
    using i2 card.infinite by force
  have index_a'1: "index (sorted_list_of_set (insert a I)) a = a'"
    using pick_index
    using a'_card a_def a_notin_I finI by auto
  hence index_a': "index (insort a (sorted_list_of_set I)) a = a'"
    by (simp add: a_notin_I finI)
  have i1_length: "i < length (sorted_list_of_set I)" using i2
    by (metis distinct_card distinct_sorted_list_of_set finI set_sorted_list_of_set)
  have 1: "pick (insert a I) (Suc i) = sorted_list_of_set (insert a I) ! (Suc i)"
  proof (rule sorted_list_of_set_eq_pick[symmetric])
    have "finite (insert a I)"
      using card.infinite i2 by force
    thus "Suc i < length (sorted_list_of_set (insert a I))"
      by (metis Suc_mono a_notin_I card_insert_disjoint distinct_card distinct_sorted_list_of_set
          finI i2 set_sorted_list_of_set)
  qed
  also have 2: "... = insort a (sorted_list_of_set I) ! Suc i"
    by (simp add: a_notin_I finI)
  also have "... = pick I i"
  proof (cases "i = a'")
    case True
    show ?thesis
      by (metis True a_notin_I finI i1_length index_a' insort_nth2 le_refl list.size(3) not_less0
          set_sorted_list_of_set sorted_list_of_set(2) sorted_list_of_set_eq_pick)
  next
    case False
    have "insort a (sorted_list_of_set I) ! Suc i = (sorted_list_of_set I) ! i"
      by (rule insort_nth2, insert i1_length False ia' index_a', auto simp add: a_notin_I finI)
    also have "... = pick I i"
      by (rule sorted_list_of_set_eq_pick[OF i1_length])
    finally show ?thesis .
  qed
  finally show ?thesis .
qed


lemma pick_insert_index:
  assumes Ik: "card I = k"
  and a_notin_I: "a  I"
  and ik: "i < k"
  and a_def: "pick (insert a I) a' = a"
  and a'k: "a' < card I + 1"
shows "pick (insert a I) (insert_index a' i) = pick I i"
proof (cases "i<a'")
  case True
  have "pick (insert a I) i = pick I i"
    by (rule pick_insert[OF a_notin_I _ a_def _ a'k], auto simp add: Ik ik True)
  thus ?thesis using True unfolding insert_index_def by auto
next
  case False note i_ge_a' = False
  have fin_aI: "finite (insert a I)"
    using Ik finite_insert ik by fastforce
  let ?P = "λaa. (aa = a  aa  I)  pick (insert a I) i < aa"
  let ?Q = "λaa. aa  I  pick (insert a I) i < aa"
  have "?P = ?Q" using a_notin_I unfolding fun_eq_iff
    by (auto, metis False Ik a_def card.infinite card_insert_disjoint ik less_SucI
        linorder_neqE_nat not_less_zero order.asym pick_mono_le)
  hence "Least ?P = Least ?Q" by simp
  also have "... = pick I i"
  proof (rule Least_equality, rule conjI)
    show "pick I i  I"
      by (simp add: Ik ik pick_in_set_le)
    show "pick (insert a I) i < pick I i"
      by (rule pick_insert2[OF a_notin_I _ a_def _ a'k], insert False, auto simp add: Ik ik)
    fix y assume y: "y  I  pick (insert a I) i < y"
    let ?xs = "sorted_list_of_set (insert a I)"
    have "y  set ?xs" using y by (metis fin_aI insertI2 set_sorted_list_of_set y)
    from this obtain j where xs_j_y: "?xs ! j = y" and j: "j < length ?xs"
      using in_set_conv_nth by metis
    have ij: "i<j"
      by (metis (no_types, lifting) Ik a_notin_I card.infinite card_insert_disjoint ik j less_SucI
          linorder_neqE_nat not_less_zero order.asym pick_mono_le sorted_list_of_set_eq_pick xs_j_y y)
    have "pick I i = pick (insert a I) (Suc i)"
      by (rule pick_insert3[symmetric, OF a_notin_I _ a_def _ a'k], insert False Ik ik, auto)
    also have "...  pick (insert a I) j"
      by (metis Ik Suc_lessI card.infinite distinct_card distinct_sorted_list_of_set eq_iff
          finite_insert ij ik j less_imp_le_nat not_less_zero pick_mono_le set_sorted_list_of_set)
    also have "... = ?xs ! j" by (rule sorted_list_of_set_eq_pick[symmetric, OF j])
    also have "... = y" by (rule xs_j_y)
    finally show "pick I i  y" .
  qed
  finally show ?thesis unfolding insert_index_def using False by auto
qed


subsection‹Start of the proof›

definition "strict_from_inj n f = (λi. if i{0..<n} then (sorted_list_of_set (f`{0..<n})) ! i else i)"

lemma strict_strict_from_inj:
  fixes f::"nat  nat"
  assumes "inj_on f {0..<n}" shows "strict_mono_on {0..<n} (strict_from_inj n f)"
proof -
  let ?I="f`{0..<n}"
  have "strict_from_inj n f x < strict_from_inj n f y"
    if xy: "x < y" and x: "x  {0..<n}" and y: "y  {0..<n}" for x y
  proof -
    let ?xs = "(sorted_list_of_set ?I)"
    have sorted_xs: "sorted ?xs" by (rule sorted_sorted_list_of_set)
    have "strict_from_inj n f x = (sorted_list_of_set ?I) ! x"
      unfolding strict_from_inj_def using x by auto
    also have "... < (sorted_list_of_set ?I) ! y"
    proof (rule sorted_nth_strict_mono; clarsimp?)
      show "y < card (f ` {0..<n})"
        by (metis assms atLeastLessThan_iff card_atLeastLessThan card_image diff_zero y)
    qed (simp add: xy)
    also have "... = strict_from_inj n f y" using y unfolding strict_from_inj_def by simp
    finally show ?thesis .
  qed
  thus ?thesis unfolding strict_mono_on_def by simp
qed




lemma strict_from_inj_image':
  assumes f: "inj_on f {0..<n}"
  shows "strict_from_inj n f ` {0..<n} = f`{0..<n}"
proof (auto)
  let ?I = "f ` {0..<n}"
  fix xa assume xa: "xa < n"
  have inj_on: "inj_on f {0..<n}" using f  by auto
  have length_I: "length (sorted_list_of_set ?I) = n"
    by (metis card_atLeastLessThan card_image diff_zero distinct_card distinct_sorted_list_of_set
        finite_atLeastLessThan finite_imageI inj_on sorted_list_of_set(1))

  have "strict_from_inj n f xa = sorted_list_of_set ?I ! xa"
    using xa unfolding strict_from_inj_def by auto
  also have "... = pick ?I xa"
    by (rule sorted_list_of_set_eq_pick, unfold length_I, auto simp add: xa)
  also have "...  f ` {0..<n}" by (rule pick_in_set_le, simp add: card_image inj_on xa)
  finally show "strict_from_inj n f xa  f ` {0..<n}" .
  obtain i where "sorted_list_of_set (f`{0..<n}) ! i = f xa" and "i<n"
    by (metis atLeast0LessThan finite_atLeastLessThan finite_imageI imageI
        in_set_conv_nth length_I lessThan_iff sorted_list_of_set(1) xa)
  thus "f xa  strict_from_inj n f ` {0..<n}"
    by (metis atLeast0LessThan imageI lessThan_iff strict_from_inj_def)
qed



definition "Z (n::nat) (m::nat) = {(f,π)|f π. f  {0..<n}  {0..<m}
   (i. i  {0..<n}  f i = i)
   π permutes {0..<n}}"

lemma Z_alt_def: "Z n m = {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)} × {π. π permutes {0..<n}}"
  unfolding Z_def by auto

lemma det_mul_finsum_alt:
  assumes A: "A  carrier_mat n m"
    and B: "B  carrier_mat m n"
  shows "det (A*B) = det (matr n n (λi. finsum_vec TYPE('a::comm_ring_1) n
  (λk. B $$ (k, i) v Matrix.col A k) {0..<m}))"
proof -
  have AT: "AT  carrier_mat m n" using A by auto
  have BT: "BT  carrier_mat n m" using B by auto
  let ?f = "(λi. finsum_vec TYPE('a) n (λk. BT $$ (i, k) v Matrix.row AT k) {0..<m})"
  let ?g = "(λi. finsum_vec TYPE('a) n (λk. B $$ (k, i) v Matrix.col A k) {0..<m})"
  let ?lhs = "matr n n ?f"
  let ?rhs = "matr n n ?g"
  have lhs_rhs: "?lhs = ?rhs"
  proof (rule eq_matI)
    show "dim_row ?lhs = dim_row ?rhs" by auto
    show "dim_col ?lhs = dim_col ?rhs" by auto
    fix i j assume i: "i < dim_row ?rhs" and j: "j < dim_col ?rhs"
    have j_n: "j<n" using j by auto
    have "?lhs $$ (i, j) = ?f i $v j" by (rule index_mat, insert i j, auto)
    also have "... = (k{0..<m}. (BT $$ (i, k) v row AT k) $ j)"
      by (rule index_finsum_vec[OF _ j_n], auto simp add: A)
    also have "... = (k{0..<m}. (B $$ (k, i) v col A k) $ j)"
    proof (rule sum.cong, auto)
      fix x assume x: "x<m"
      have row_rw: "Matrix.row AT x = col A x" by (rule row_transpose, insert A x, auto)
      have B_rw: "BT $$ (i,x) = B $$ (x, i)"
        by (rule index_transpose_mat, insert x i B, auto)
      have "(BT $$ (i, x) v Matrix.row AT x) $v j = BT $$ (i, x) * Matrix.row AT x $v j"
        by (rule index_smult_vec, insert A j_n, auto)
      also have "... = B $$ (x, i) * col A x $v j" unfolding row_rw B_rw by simp
      also have "... = (B $$ (x, i) v col A x) $v j"
        by (rule index_smult_vec[symmetric], insert A j_n, auto)
      finally show " (BT $$ (i, x) v Matrix.row AT x) $v j = (B $$ (x, i) v col A x) $v j" .
    qed
    also have "... = ?g i $v j"
      by (rule index_finsum_vec[symmetric, OF _ j_n], auto simp add: A)
    also have "... = ?rhs $$ (i, j)" by (rule index_mat[symmetric], insert i j, auto)
    finally show "?lhs $$ (i, j) = ?rhs $$ (i, j)" .
  qed
  have "det (A*B) = det (BT*AT)"
    using det_transpose
    by (metis A B Matrix.transpose_mult mult_carrier_mat)
  also have "... =  det (matr n n (λi. finsum_vec TYPE('a) n (λk. BT $$ (i, k) v Matrix.row AT k) {0..<m}))"
    using mat_mul_finsum_alt[OF BT AT] by auto
  also have "... = det (matr n n (λi. finsum_vec TYPE('a) n (λk. B $$ (k, i) v Matrix.col A k) {0..<m}))"
    by (rule arg_cong[of _ _ det], rule lhs_rhs)
  finally show ?thesis .
qed


lemma det_cols_mul:
  assumes A: "A  carrier_mat n m"
    and B: "B  carrier_mat m n"
  shows "det (A*B) = (f | (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i).
       (i = 0..<n. B $$ (f i, i)) * Determinant.det (matr n n (λi. col A (f i))))"
proof -
  let ?V="{0..<n}"
  let ?U = "{0..<m}"
  let ?F = " {f. (i {0..<n}. f i  ?U)  (i. i  {0..<n}  f i = i)}"
  let ?g = "λf. det (matr n n (λ i. B $$ (f i, i) v col A (f i)))"
  have fm: "finite {0..<m}" by auto
  have fn: "finite {0..<n}" by auto
  have det_rw: "det (matr n n (λi. B $$ (f i, i) v col A (f i))) =
    (prod (λi. B $$ (f i, i)) {0..<n}) * det (matr n n (λi. col A (f i)))"
    if  f: "(i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)" for f
    by (rule det_rows_mul, insert A col_dim, auto)
  have "det (A*B) = det (matr n n (λi. finsum_vec TYPE('a::comm_ring_1) n (λk. B $$ (k, i) v Matrix.col A k) ?U))"
    by (rule det_mul_finsum_alt[OF A B])
  also have "... = sum ?g ?F" by (rule det_linear_rows_sum[OF fm], auto simp add: A)
  also have "... = (f?F. prod (λi. B $$ (f i, i)) {0..<n} * det (matr n n (λi. col A (f i))))"
    using det_rw by auto
  finally show ?thesis .
qed

lemma det_cols_mul':
  assumes A: "A  carrier_mat n m"
    and B: "B  carrier_mat m n"
  shows "det (A*B) = (f | (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i).
       (i = 0..<n. A $$ (i, f i)) * det (matr n n (λi. row B (f i))))"
proof -
  let ?F="{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)}"
  have t: "A * B = (BT*AT)T" using transpose_mult[OF A B] transpose_transpose by metis
  have "det (BT*AT) = (f?F. (i = 0..<n. AT $$ (f i, i)) * det (matr n n (λi. col BT (f i))))"
    by (rule det_cols_mul, auto simp add: A B)
  also have "... = (f ?F. (i = 0..<n. A $$ (i, f i)) * det (matr n n (λi. row B (f i))))"
  proof (rule sum.cong, rule refl)
    fix f assume f: "f  ?F"
    have "(i = 0..<n. AT $$ (f i, i)) = (i = 0..<n. A $$ (i, f i))"
    proof (rule prod.cong, rule refl)
      fix x assume x: "x  {0..<n}"
      show "AT $$ (f x, x) = A $$ (x, f x)"
        by (rule index_transpose_mat(1), insert f A x, auto)
    qed
    moreover have "det (matr n n (λi. col BT (f i))) = det (matr n n (λi. row B (f i)))"
    proof -
      have row_eq_colT: "row B (f i) $v j = col BT (f i) $v j" if i: "i < n" and j: "j < n" for i j
      proof -
        have fi_m: "f i < m" using f i by auto
        have "col BT (f i) $v j = BT $$(j, f i)" by (rule index_col, insert B fi_m j, auto)
        also have "... = B $$ (f i, j)" using B fi_m j by auto
        also have "... = row B (f i) $v j" by (rule index_row[symmetric], insert B fi_m j, auto)
        finally show ?thesis ..
      qed
      show ?thesis by (rule arg_cong[of _ _ det], rule eq_matI, insert row_eq_colT, auto)
    qed
    ultimately show "(i = 0..<n. AT $$ (f i, i)) * det (matr n n (λi. col BT (f i))) =
         (i = 0..<n. A $$ (i, f i)) * det (matr n n (λi. row B (f i)))" by simp
  qed
  finally show ?thesis
    by (metis (no_types, lifting) A B det_transpose transpose_mult mult_carrier_mat)
qed

(*We need a more general version of this lemma*)
lemma
  assumes F: "F= {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)}"
  and p: " π permutes {0..<n}"
  shows " (fF. (i = 0..<n. B $$ (f i, π i))) = (fF. (i = 0..<n. B $$ (f i, i)))"
proof -
  let ?h = "(λf. f  π)"
  have inj_on_F: "inj_on ?h F"
  proof (rule inj_onI)
    fix f g assume fop: "f  π = g  π"
    have "f x = g x" for x
    proof (cases "x  {0..<n}")
      case True
      then show ?thesis
        by (metis fop comp_apply p permutes_def)
    next
      case False
      then show ?thesis
        by (metis fop comp_eq_elim p permutes_def)
    qed
    thus "f = g" by auto
  qed
  have hF: "?h` F = F"
    unfolding image_def
  proof auto
    fix xa assume xa: "xa  F" show "xa  π  F"
      unfolding o_def F
      using F PiE p xa
      by (auto, smt F atLeastLessThan_iff mem_Collect_eq p permutes_def xa)
    show "xF. xa = x  π"
    proof (rule bexI[of _ "xa  Hilbert_Choice.inv π"])
      show "xa = xa  Hilbert_Choice.inv π  π"
        using p by auto
      show "xa  Hilbert_Choice.inv π  F"
        unfolding o_def F
        using F PiE p xa
        by (auto, smt atLeastLessThan_iff permutes_def permutes_less(3))
    qed
  qed
  have prod_rw: "(i = 0..<n. B $$ (f i, i)) = (i = 0..<n. B $$ (f (π i), π i))" if "fF" for f
  using prod.permute[OF p] by auto
  let ?g = "λf. (i = 0..<n. B $$ (f i, π i))"
  have "(fF. (i = 0..<n. B $$ (f i, i))) = (fF. (i = 0..<n. B $$ (f (π i), π i)))"
    using prod_rw by auto
  also have "... = (f(?h`F). i = 0..<n. B $$ (f i, π i))"
    using sum.reindex[OF inj_on_F, of ?g] unfolding hF by auto
  also have "... = (fF. i = 0..<n. B $$ (f i, π i))" unfolding hF by auto
  finally show ?thesis ..
qed


lemma detAB_Znm_aux:
  assumes F: "F= {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)}"
  shows"(π | π permutes {0..<n}. (fF. prod (λi. B $$ (f i, i)) {0..<n}
        * (signof π * (i = 0..<n. A $$ (π i, f i)))))
    = (π | π permutes {0..<n}. fF. (i = 0..<n. B $$ (f i, π i))
        * (signof π * (i = 0..<n. A $$ (i, f i))))"
proof -
  have "(π | π permutes {0..<n}. (fF. prod (λi. B $$ (f i, i)) {0..<n}
      * (signof π * (i = 0..<n. A $$ (π i, f i))))) =
    (π | π permutes {0..<n}. fF. signof π * (i = 0..<n. B $$ (f i, i) * A $$ (π i, f i)))"
    by (smt mult.left_commute prod.cong prod.distrib sum.cong)
  also have "... = (π | π permutes {0..<n}. fF. signof (Hilbert_Choice.inv π)
    * (i = 0..<n. B $$ (f i, i) * A $$ (Hilbert_Choice.inv π i, f i)))"
    by (rule sum_permutations_inverse)
  also have "... = (π | π permutes {0..<n}. fF. signof (Hilbert_Choice.inv π)
    * (i = 0..<n. B $$ (f (π i), (π i)) * A $$ (Hilbert_Choice.inv π (π i), f (π i))))"
  proof (rule sum.cong)
    fix x assume x: "x  {π. π permutes {0..<n}}"
    let ?inv_x = "Hilbert_Choice.inv x"
    have p: "x permutes {0..<n}" using x by simp
    have prod_rw: "(i = 0..<n. B $$ (f i, i) * A $$ (?inv_x i, f i))
        = (i = 0..<n. B $$ (f (x i), x i) * A $$ (?inv_x (x i), f (x i)))" if "f  F" for f
      using prod.permute[OF p] by auto
    then show "(fF. signof ?inv_x * (i = 0..<n. B $$ (f i, i) * A $$ (?inv_x i, f i))) =
         (fF. signof ?inv_x * (i = 0..<n. B $$ (f (x i), x i) * A $$ (?inv_x (x i), f (x i))))"
      by auto
  qed (simp)
  also have "... = (π | π permutes {0..<n}. fF. signof π
    * (i = 0..<n. B $$ (f (π i), (π i)) * A $$ (i, f (π i))))"
    by (rule sum.cong, auto, rule sum.cong, auto)
        (metis (no_types, lifting) finite_atLeastLessThan signof_inv)
  also have "... = (π | π permutes {0..<n}. fF. signof π
    * (i = 0..<n. B $$ (f i, (π i)) * A $$ (i, f i)))"
  proof (rule sum.cong)
    fix π assume p: "π  {π. π permutes {0..<n}}"
    hence p: "π permutes {0..<n}" by auto
    let ?inv_pi = "(Hilbert_Choice.inv π)"
    let ?h = "(λf. f  (Hilbert_Choice.inv π))"
  have inj_on_F: "inj_on ?h F"
  proof (rule inj_onI)
    fix f g assume fop: "f  ?inv_pi = g  ?inv_pi"
    have "f x = g x" for x
    proof (cases "x  {0..<n}")
      case True
      then show ?thesis
        by (metis fop o_inv_o_cancel p permutes_inj)
    next
      case False
      then show ?thesis
        by (metis fop o_inv_o_cancel p permutes_inj)
    qed
    thus "f = g" by auto
  qed
  have hF: "?h` F = F"
    unfolding image_def
  proof auto
    fix xa assume xa: "xa  F" show "xa  ?inv_pi  F"
      unfolding o_def F
      using F PiE p xa
      by (auto, smt atLeastLessThan_iff permutes_def permutes_less(3))
    show "xF. xa = x  ?inv_pi"
    proof (rule bexI[of _ "xa  π"])
      show "xa = xa  π  Hilbert_Choice.inv π "
        using p by auto
      show "xa  π  F"
        unfolding o_def F
        using F PiE p xa
        by (auto, smt atLeastLessThan_iff permutes_def permutes_less(3))
    qed
  qed
  let ?g = "λf. signof π * (i = 0..<n. B $$ (f (π i), π i) * A $$ (i, f (π i)))"
    show "(fF. signof π * (i = 0..<n. B $$ (f (π i), π i) * A $$ (i, f (π i)))) =
         (fF. signof π * (i = 0..<n. B $$ (f i, π i) * A $$ (i, f i)))"
      using sum.reindex[OF inj_on_F, of "?g"] p unfolding hF unfolding o_def by auto
  qed (simp)
  also have "... = (π | π permutes {0..<n}. fF. (i = 0..<n. B $$ (f i, π i))
  * (signof π * (i = 0..<n. A $$ (i, f i))))"
    by (smt mult.left_commute prod.cong prod.distrib sum.cong)
  finally show ?thesis .
qed


lemma detAB_Znm:
  assumes A: "A  carrier_mat n m"
    and B: "B  carrier_mat m n"
  shows "det (A*B) = ((f, π)Z n m. signof π * (i = 0..<n. A $$ (i, f i) * B $$ (f i, π i)))"
proof -
  let ?V="{0..<n}"
  let ?U = "{0..<m}"
  let ?PU = "{p. p permutes ?U}"
  let ?F = " {f. (i {0..<n}. f i  ?U)  (i. i  {0..<n}  f i = i)}"
  let ?f = "λf. det (matr n n (λ i. A $$ (i, f i) v row B (f i)))"
  let ?g = "λf. det (matr n n (λ i. B $$ (f i, i) v col A (f i)))"
  have fm: "finite {0..<m}" by auto
  have fn: "finite {0..<n}" by auto
  have F: "?F= {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)}" by auto
  have det_rw: "det (matr n n (λi. B $$ (f i, i) v col A (f i))) =
    (prod (λi. B $$ (f i, i)) {0..<n}) * det (matr n n (λi. col A (f i)))"
    if  f: "(i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)" for f
    by (rule det_rows_mul, insert A col_dim, auto)
  have det_rw2: "det (matr n n (λi. col A (f i)))
  = (π | π permutes {0..<n}. signof π * (i = 0..<n. A $$ (π i, f i)))"
    if f: "f  ?F" for f
  proof (unfold Determinant.det_def, auto, rule sum.cong, auto)
    fix x assume x: "x permutes {0..<n}"
    have "(i = 0..<n. col A (f i) $v x i) = (i = 0..<n. A $$ (x i, f i))"
    proof (rule prod.cong)
      fix xa assume xa: "xa  {0..<n}" show "col A (f xa) $v x xa = A $$ (x xa, f  xa)"
        by (metis A atLeastLessThan_iff carrier_matD(1) col_def index_vec permutes_less(1) x xa)
    qed (auto)
    then show "signof x * (i = 0..<n. col A (f i) $v x i)
      = signof x * (i = 0..<n. A $$ (x i, f i))" by auto
  qed
  have fin_n: "finite {0..<n}" and fin_m: "finite {0..<m}" by auto
  have "det (A*B) = det (matr n n (λi. finsum_vec TYPE('a::comm_ring_1) n
    (λk. B $$ (k, i) v Matrix.col A k) {0..<m}))"
    by (rule det_mul_finsum_alt[OF A B])
  also have "... = sum ?g ?F" by (rule det_linear_rows_sum[OF fm], auto simp add: A)
  also have "... = (f?F. prod (λi. B $$ (f i, i)) {0..<n} * det (matr n n (λi. col A (f i))))"
    using det_rw by auto
  also have "... = (f?F. prod (λi. B $$ (f i, i)) {0..<n} *
  (π | π permutes {0..<n}. signof π * (i = 0..<n. A $$ (π i, f (i)))))"
    by (rule sum.cong, auto simp add: det_rw2)
  also have "... =
  (f?F. π | π permutes {0..<n}. prod (λi. B $$ (f i, i)) {0..<n}
    * (signof π * (i = 0..<n. A $$ (π i, f (i)))))"
    by (simp add: mult_hom.hom_sum)
  also have "... = (π | π permutes {0..<n}. f?F.prod (λi. B $$ (f i, i)) {0..<n}
    * (signof π * (i = 0..<n. A $$ (π i, f i))))"
    by (rule VS_Connect.class_semiring.finsum_finsum_swap,
      insert finite_permutations finite_bounded_functions[OF fin_m fin_n], auto)
  thm detAB_Znm_aux
  also have "... = (π | π permutes {0..<n}. f?F. (i = 0..<n. B $$ (f i, π i))
  * (signof π * (i = 0..<n. A $$ (i, f i))))" by (rule detAB_Znm_aux, auto)
  also have "... = (f?F.π | π permutes {0..<n}. (i = 0..<n. B $$ (f i, π i))
  * (signof π * (i = 0..<n. A $$ (i, f i))))"
    by (rule VS_Connect.class_semiring.finsum_finsum_swap,
      insert finite_permutations finite_bounded_functions[OF fin_m fin_n], auto)
  also have "... =  (f?F.π | π permutes {0..<n}. signof π
    * (i = 0..<n. A $$ (i, f i) * B $$ (f i, π i)))"
    unfolding prod.distrib by (rule sum.cong, auto, rule sum.cong, auto)
  also have "... = sum (λ(f,π). (signof π)
    * (prod (λi. A$$(i,f i) * B $$ (f i, π i)) {0..<n})) (Z n m)"
    unfolding Z_alt_def unfolding sum.cartesian_product[symmetric] F by auto
  finally show ?thesis .
qed

(*Several lemmas here can be moved outside the context*)
context
  fixes n m and A B::"'a::comm_ring_1 mat"
  assumes A: "A  carrier_mat n m"
    and B: "B  carrier_mat m n"
begin

private definition "Z_inj = ({f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   inj_on f {0..<n}} × {π. π permutes {0..<n}})"

private definition "Z_not_inj = ({f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   ¬ inj_on f {0..<n}} × {π. π permutes {0..<n}})"

private definition "Z_strict = ({f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   strict_mono_on {0..<n} f} × {π. π permutes {0..<n}})"

private definition "Z_not_strict = ({f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   ¬ strict_mono_on {0..<n} f} × {π. π permutes {0..<n}})"

private definition "weight f π
  = (signof π) * (prod (λi. A$$(i,f i) * B $$ (f i, π i)) {0..<n})"

private definition "Z_good g = ({f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   inj_on f {0..<n}  (f`{0..<n} = g`{0..<n})} × {π. π permutes {0..<n}})"

private definition "F_strict = {f. f  {0..<n}  {0..<m}
   (i. i  {0..<n}  f i = i)  strict_mono_on {0..<n} f}"

private definition "F_inj = {f. f  {0..<n}  {0..<m}
   (i. i  {0..<n}  f i = i)  inj_on f {0..<n}}"

private definition "F_not_inj = {f. f  {0..<n}  {0..<m}
   (i. i  {0..<n}  f i = i)  ¬ inj_on f {0..<n}}"

private definition "F = {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)}"

text‹The Cauchy--Binet formula is proven in \url{https://core.ac.uk/download/pdf/82475020.pdf}
  In that work, they define @{text "σ ≡ inv φ ∘ π"}. I had problems following this proof
  in Isabelle, since I was demanded to show that such permutations commute, which is false.
  It is a notation problem of the  @{text "∘"} operator, the author means @{text "σ ≡ π ∘ inv φ"} using
  the Isabelle notation (i.e., @{text "σ x = π ((inv φ) x)"}).
›

lemma step_weight:
  fixes φ π
  defines "σ  π  Hilbert_Choice.inv φ"
  assumes f_inj: "f  F_inj" and gF: "g  F" and pi: "π permutes {0..<n}"
  and phi: "φ permutes {0..<n}" and fg_phi: "x  {0..<n}. f x = g (φ x)"
shows "weight f π = (signof φ) * (i = 0..<n. A $$ (i, g (φ i)))
  * (signof σ) * (i = 0..<n. B $$ (g i, σ i))"
proof -
  let ?A = "(i = 0..<n. A $$ (i, g (φ i))) "
  let ?B = "(i = 0..<n. B $$ (g i, σ i))"
  have sigma: "σ permutes {0..<n}" unfolding σ_def
    by (rule permutes_compose[OF permutes_inv[OF phi] pi])
  have A_rw: "?A = (i = 0..<n. A $$ (i, f i))" using fg_phi by auto
  have "?B = (i = 0..<n. B $$ (g (φ i), σ (φ i)))"
    by (rule prod.permute[unfolded o_def, OF phi])
  also have "... = (i = 0..<n. B $$ (f i, π i))"
    using fg_phi
    unfolding σ_def unfolding o_def unfolding permutes_inverses(2)[OF phi] by auto
  finally have B_rw: "?B = (i = 0..<n. B $$ (f i, π i))" .
  have "(signof φ) * ?A * (signof σ) * ?B = (signof φ) * (signof σ) * ?A * ?B" by auto
  also have "... = signof (φ  σ) * ?A * ?B" unfolding signof_compose[OF phi sigma] by simp
  also have "... = signof π * ?A * ?B"
    by (metis (no_types, lifting) σ_def mult.commute o_inv_o_cancel permutes_inj
        phi sigma signof_compose)
  also have "... = signof π * (i = 0..<n. A $$ (i, f i)) * (i = 0..<n. B $$ (f i, π i))"
    using A_rw B_rw by auto
  also have "... = signof π * (i = 0..<n. A $$ (i, f i) * B $$ (f i, π i))" by auto
  also have "... = weight f π" unfolding weight_def by simp
  finally show ?thesis ..
qed


lemma Z_good_fun_alt_sum:
  fixes g
  defines "Z_good_fun  {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
     inj_on f {0..<n}  (f`{0..<n} = g`{0..<n})}"
  assumes g: "g  F_inj"
  shows "(fZ_good_fun. P f)= (π{π. π permutes {0..<n}}. P (g  π))"
proof -
  let ?f = "λπ. g  π"
  let ?P = "{π. π permutes {0..<n}}"
  have fP: "?f`?P = Z_good_fun"
  proof (unfold Z_good_fun_def, auto)
    fix xa xb assume "xa permutes {0..<n}" and "xb < n"
    hence "xa xb < n" by auto
    thus "g (xa xb) < m" using g unfolding F_inj_def by fastforce
  next
    fix xa i assume "xa permutes {0..<n}" and i_ge_n: "¬ i < n"
    hence "xa i = i" unfolding permutes_def by auto
    thus "g (xa i) = i" using g i_ge_n unfolding F_inj_def by auto
  next
    fix xa assume "xa permutes {0..<n}" thus "inj_on (g  xa) {0..<n}"
      by (metis (mono_tags, lifting) F_inj_def atLeast0LessThan comp_inj_on g
          mem_Collect_eq permutes_image permutes_inj_on)
  next
    fix π xb assume "π permutes {0..<n}" and "xb < n" thus " g xb  (λx. g (π x)) ` {0..<n}"
      by (metis (full_types) atLeast0LessThan imageI image_image lessThan_iff permutes_image)
  next
   fix x assume x1: "x  {0..<n}  {0..<m}" and x2: "i. ¬ i < n  x i = i"
     and inj_on_x: "inj_on x {0..<n}" and xg: "x ` {0..<n} = g ` {0..<n}"
   let  = "λi. if i<n then (THE j. j<n  x i = g j) else i"
   show "x  (∘) g ` {π. π permutes {0..<n}}"
   proof (unfold image_def, auto, rule exI[of _ ], rule conjI)
     have " i = i" if i: "i  {0..<n}" for i
       using i by auto
     moreover have "∃!j.  j = i" for i
     proof (cases "i<n")
       case True
       obtain a where xa_gi: "x a = g i" and a: "a < n" using xg True
         by (metis (mono_tags, opaque_lifting) atLeast0LessThan imageE imageI lessThan_iff)
       show ?thesis
       proof (rule ex1I[of _ a])
         have the_ai: "(THE j. j < n  x a = g j) = i"
         proof (rule theI2)
           show "i < n  x a = g i" using xa_gi True by auto
           fix xa assume "xa < n  x a = g xa" thus "xa = i"
             by (metis (mono_tags, lifting) F_inj_def True atLeast0LessThan
                g inj_onD lessThan_iff mem_Collect_eq xa_gi)
           thus "xa = i" .
         qed
         thus ta: " a = i" using a by auto
         fix j assume tj: " j = i"
         show "j = a"
         proof (cases "j<n")
           case True
           obtain b where xj_gb: "x j = g b" and b: "b < n" using xg True
             by (metis (mono_tags, opaque_lifting) atLeast0LessThan imageE imageI lessThan_iff)
           let ?P = "λja. ja < n  x j = g ja"
           have the_ji: "(THE ja. ja < n  x j = g ja) = i" using tj True by auto
           have "?P (THE ja. ?P ja)"
           proof (rule theI)
            show "b < n  x j = g b" using xj_gb b by auto
            fix xa assume "xa < n  x j = g xa" thus "xa = b"
              by (metis (mono_tags, lifting) F_inj_def b atLeast0LessThan
                  g inj_onD lessThan_iff mem_Collect_eq xj_gb)
           qed
           hence "x j = g i" unfolding the_ji by auto
           hence "x j = x a" using xa_gi by auto
           then show ?thesis using inj_on_x a True unfolding inj_on_def by auto
         next
           case False
           then show ?thesis using tj True by auto
         qed
       qed
     next
       case False note i_ge_n = False
       show ?thesis
       proof (rule ex1I[of _ i])
         show " i = i" using False by simp
         fix j assume tj: " j = i"
         show "j = i"
         proof (cases "j<n")
           case True
           obtain a where xj_ga: "x j = g a" and a: "a < n" using xg True
             by (metis (mono_tags, opaque_lifting) atLeast0LessThan imageE imageI lessThan_iff)
           have "(THE ja. ja < n  x j = g ja) < n"
           proof (rule theI2)
             show "a < n  x j = g a" using xj_ga a by auto
             fix xa assume a1: "xa < n  x j = g xa" thus "xa = a"
               using F_inj_def  a atLeast0LessThan g inj_on_eq_iff xj_ga by fastforce
             show "xa < n" by (simp add: a1)
           qed
            then show ?thesis using tj i_ge_n by auto
          next
            case False
            then show ?thesis using tj  by auto
          qed
       qed
     qed
     ultimately show " permutes {0..<n}" unfolding permutes_def by auto
     show "x = g  "
     proof -
       have "x xa = g (THE j. j < n  x xa = g j)" if xa: "xa < n" for xa
       proof -
         obtain c where c: "c < n" and xxa_gc: "x xa = g c"
           by (metis (mono_tags, opaque_lifting) atLeast0LessThan imageE imageI lessThan_iff xa xg)
         show ?thesis
         proof (rule theI2)
           show c1: "c < n  x xa = g c" using c xxa_gc by auto
           fix xb assume c2: "xb < n  x xa = g xb" thus "xb = c"
             by (metis (mono_tags, lifting) F_inj_def c1 atLeast0LessThan
                 g inj_onD lessThan_iff mem_Collect_eq)
           show "x xa = g xb" using c1 c2 by simp
         qed
       qed
       moreover have "x xa = g xa" if xa: "¬ xa < n" for xa
         using g x1 x2 xa unfolding F_inj_def by simp
       ultimately show ?thesis unfolding o_def fun_eq_iff by auto
     qed
   qed
 qed
  have inj: "inj_on ?f ?P"
  proof (rule inj_onI)
    fix x y assume x: "x  ?P" and y: "y  ?P" and gx_gy: "g  x = g  y"
    have "x i = y i" for i
    proof (cases "i<n")
      case True
      hence xi: "x i  {0..<n}" and yi: "y i  {0..<n}" using x y by auto
      have "g (x i) = g (y i)" using gx_gy unfolding o_def by meson
      thus ?thesis using xi yi using g unfolding F_inj_def inj_on_def by blast
    next
      case False
      then show ?thesis using x y unfolding permutes_def by auto
    qed
    thus "x = y" unfolding fun_eq_iff by auto
  qed
  have "(fZ_good_fun. P f) = (f?f`?P. P f)" using fP by simp
  also have "... =  sum (P  (∘) g) {π. π permutes {0..<n}}"
    by (rule sum.reindex[OF inj])
  also have "... =  (π | π permutes {0..<n}. P (g  π))" by auto
  finally show ?thesis .
qed


lemma F_injI:
  assumes "f  {0..<n}  {0..<m}"
  and "(i. i  {0..<n}  f i = i)" and "inj_on f {0..<n}"
  shows "f  F_inj" using assms unfolding F_inj_def by simp

lemma F_inj_composition_permutation:
  assumes phi: "φ permutes {0..<n}"
  and g: "g  F_inj"
  shows "g  φ  F_inj"
proof (rule F_injI)
  show "g  φ  {0..<n}  {0..<m}"
    using g unfolding permutes_def F_inj_def
    by (simp add: Pi_iff phi)
  show "i. i  {0..<n}  (g  φ) i = i"
    using g phi unfolding permutes_def F_inj_def by simp
  show "inj_on (g  φ) {0..<n}"
    by (rule comp_inj_on, insert g permutes_inj_on[OF phi] permutes_image[OF phi])
       (auto simp add: F_inj_def)
qed


lemma F_strict_imp_F_inj:
  assumes f: "f  F_strict"
  shows "f  F_inj"
  using f strict_mono_on_imp_inj_on
  unfolding F_strict_def F_inj_def by auto


lemma one_step:
  assumes g1: "g  F_strict"
  shows "det (submatrix A UNIV (g`{0..<n})) * det (submatrix B (g`{0..<n}) UNIV)
    = ((x, y)  Z_good g. weight x y)" (is "?lhs = ?rhs")
proof -
  define Z_good_fun where "Z_good_fun = {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
     inj_on f {0..<n}  (f`{0..<n} = g`{0..<n})}"
  let ?Perm = "{π. π permutes {0..<n}}"
  let ?P = "(λf. π  ?Perm. weight f π)"
  let ?inv = "Hilbert_Choice.inv"
  have g: "g  F_inj" by (rule F_strict_imp_F_inj[OF g1])
  have detA: "(φ{π. π permutes {0..<n}}. signof φ * (i = 0..<n. A $$ (i, g (φ i))))
    = det (submatrix A UNIV (g`{0..<n}))"
  proof -
    have "{j. j < dim_col A  j  g ` {0..<n}} = {j. j  g ` {0..<n}}"
      using g A unfolding F_inj_def by fastforce
    also have "card ... = n" using F_inj_def card_image g by force
    finally have card_J: "card {j. j < dim_col A  j  g ` {0..<n}} = n" by simp
    have subA_carrier: "submatrix A UNIV (g ` {0..<n})  carrier_mat n n"
      unfolding submatrix_def card_J using A by auto
    have "det (submatrix A UNIV (g`{0..<n})) = (p | p permutes {0..<n}.
            signof p * (i = 0..<n. submatrix A UNIV (g ` {0..<n}) $$ (i, p i)))"
      using subA_carrier unfolding Determinant.det_def by auto
    also have "... = (φ{π. π permutes {0..<n}}. signof φ * (i = 0..<n. A $$ (i, g (φ i))))"
    proof (rule sum.cong)
      fix x assume x: "x  {π. π permutes {0..<n}}"
      have "(i = 0..<n. submatrix A UNIV (g ` {0..<n}) $$ (i, x i))
          = (i = 0..<n. A $$ (i, g (x i)))"
      proof (rule prod.cong, rule refl)
        fix i assume i: "i  {0..<n}"
        have pick_rw: "pick (g ` {0..<n}) (x i) = g (x i)"
        proof -
          have "index (sorted_list_of_set (g ` {0..<n})) (g (x i)) = x i"
          proof -
            have rw: "sorted_list_of_set (g ` {0..<n}) = map g [0..<n]"
              by (rule sorted_list_of_set_map_strict, insert g1, simp add: F_strict_def)
            have "index (sorted_list_of_set (g`{0..<n})) (g (x i)) = index (map g [0..<n]) (g (x i))"
              unfolding rw by auto
            also have "... = index [0..<n] (x i)"
              by (rule index_map_inj_on[of _ "{0..<n}"], insert x i g, auto simp add: F_inj_def)
            also have "... = x i" using x i by auto
            finally show ?thesis .
          qed
          moreover have "(g (x i))   (g ` {0..<n})" using x g i unfolding F_inj_def by auto
          moreover have "x i < card (g ` {0..<n})" using x i g by (simp add: F_inj_def card_image)
          ultimately show ?thesis using pick_index by auto
        qed
        have "submatrix A UNIV (g`{0..<n}) $$ (i, x i) = A $$ (pick UNIV i, pick (g`{0..<n}) (x i))"
          by (rule submatrix_index, insert i A card_J x, auto)
        also have "... = A $$ (i, g (x i))" using pick_rw pick_UNIV by auto
        finally show "submatrix A UNIV (g ` {0..<n}) $$ (i, x i) = A $$ (i, g (x i))" .
      qed
      thus "signof x * (i = 0..<n. submatrix A UNIV (g ` {0..<n}) $$ (i, x i))
        = signof x * (i = 0..<n. A $$ (i, g (x i)))" by auto
    qed (simp)
    finally show ?thesis by simp
  qed
  have detB_rw: "(π  ?Perm. signof (π  ?inv φ) * (i = 0..<n. B $$ (g i, (π  ?inv φ) i)))
   = (π  ?Perm. signof (π) * (i = 0..<n. B $$ (g i, π i)))"
    if phi: "φ permutes {0..<n}" for φ
  proof -
    let ?h="λπ. π  ?inv φ"
    let ?g = "λπ. signof (π) * (i = 0..<n. B $$ (g i, π i))"
    have "?h`?Perm = ?Perm"
    proof -
      have "π  ?inv φ permutes {0..<n}" if pi: "π permutes {0..<n}" for π
        using permutes_compose permutes_inv phi that by blast
      moreover have "x  (λπ. π  ?inv φ) ` ?Perm" if "x permutes {0..<n}" for x
      proof -
        have "x  φ permutes {0..<n}"
          using permutes_compose phi that by blast
        moreover have "x = x  φ  ?inv φ" using phi by auto
        ultimately show ?thesis unfolding image_def by auto
      qed
      ultimately show ?thesis by auto
    qed
    hence "(π  ?Perm. ?g π) = (π  ?h`?Perm. ?g π)" by simp
    also have "... = sum (?g  ?h) ?Perm"
    proof (rule sum.reindex)
      show "inj_on (λπ. π  ?inv φ) {π. π permutes {0..<n}}"
        by (metis (no_types, lifting) inj_onI o_inv_o_cancel permutes_inj phi)
    qed
    also have "... = (π  ?Perm. signof (π  ?inv φ) * (i = 0..<n. B $$ (g i, (π  ?inv φ) i)))"
      unfolding o_def by auto
    finally show ?thesis by simp
  qed

  have detB: "det (submatrix B (g`{0..<n}) UNIV)
    = (π  ?Perm. signof π * (i = 0..<n. B $$ (g i, π i)))"
  proof -
    have "{i. i < dim_row B  i  g ` {0..<n}} = {i. i  g ` {0..<n}}"
     using g B unfolding F_inj_def by fastforce
    also have "card ... = n" using F_inj_def card_image g by force
    finally have card_I: "card {j. j < dim_row B  j  g ` {0..<n}} = n" by simp
    have subB_carrier: "submatrix B (g ` {0..<n}) UNIV  carrier_mat n n"
      unfolding submatrix_def using card_I B by auto
    have "det (submatrix B (g`{0..<n}) UNIV) = (p  ?Perm. signof p
      * (i=0..<n. submatrix B (g ` {0..<n}) UNIV $$ (i, p i)))"
      unfolding Determinant.det_def using subB_carrier by auto
    also have "... = (π  ?Perm. signof π * (i = 0..<n. B $$ (g i, π i)))"
    proof (rule sum.cong, rule refl)
      fix x assume x: "x  {π. π permutes {0..<n}}"
      have "(i=0..<n. submatrix B (g`{0..<n}) UNIV $$ (i, x i)) = (i=0..<n. B $$ (g i, x i))"
      proof (rule prod.cong, rule refl)
        fix i assume i: "i  {0..<n}"
        have pick_rw: "pick (g ` {0..<n}) i = g i"
        proof -
          have "index (sorted_list_of_set (g ` {0..<n})) (g i) = i"
          proof -
            have rw: "sorted_list_of_set (g ` {0..<n}) = map g [0..<n]"
              by (rule sorted_list_of_set_map_strict, insert g1, simp add: F_strict_def)
            have "index (sorted_list_of_set (g`{0..<n})) (g i) = index (map g [0..<n]) (g i)"
              unfolding rw by auto
            also have "... = index [0..<n] (i)"
              by (rule index_map_inj_on[of _ "{0..<n}"], insert x i g, auto simp add: F_inj_def)
            also have "... = i" using i by auto
            finally show ?thesis .
          qed
          moreover have "(g i)   (g ` {0..<n})" using x g i unfolding F_inj_def by auto
          moreover have "i < card (g ` {0..<n})" using x i g by (simp add: F_inj_def card_image)
          ultimately show ?thesis using pick_index by auto
        qed
        have "submatrix B (g`{0..<n}) UNIV $$ (i, x i) = B $$ (pick (g`{0..<n}) i, pick UNIV (x i))"
          by (rule submatrix_index, insert i B card_I x, auto)
        also have "... = B $$ (g i, x i)" using pick_rw pick_UNIV by auto
        finally show "submatrix B (g ` {0..<n}) UNIV $$ (i, x i) = B $$ (g i, x i)" .
      qed
      thus "signof x * (i = 0..<n. submatrix B (g ` {0..<n}) UNIV $$ (i, x i))
          = signof x * (i = 0..<n. B $$ (g i, x i))" by simp
    qed
    finally show ?thesis .
  qed

  have "?rhs = (fZ_good_fun. π?Perm. weight f π)"
    unfolding Z_good_def sum.cartesian_product Z_good_fun_def by blast
  also have "... = (φ{π. π permutes {0..<n}}. ?P (g  φ))" unfolding Z_good_fun_def
    by (rule Z_good_fun_alt_sum[OF g])
  also have "... = (φ{π. π permutes {0..<n}}. π{π. π permutes {0..<n}}.
    signof φ * (i = 0..<n. A $$ (i, g (φ i))) * signof (π  ?inv φ)
    * (i = 0..<n. B $$ (g i, (π  ?inv φ) i)))"
  proof (rule sum.cong, simp, rule sum.cong, simp)
    fix φ π assume phi: "φ  ?Perm" and pi: "π  ?Perm"
    show "weight (g  φ) π = signof φ * (i = 0..<n. A $$ (i, g (φ i))) *
      signof (π  ?inv φ) * (i = 0..<n. B $$ (g i, (π  ?inv φ) i))"
    proof (rule step_weight)
      show "g  φ  F_inj" by (rule F_inj_composition_permutation[OF _ g], insert phi, auto)
      show "g  F" using g unfolding F_def F_inj_def by simp
    qed (insert phi pi, auto)
  qed
  also have "... = (φ{π. π permutes {0..<n}}. signof φ * (i = 0..<n. A $$ (i, g (φ i))) *
     (π | π permutes {0..<n}. signof (π  ?inv φ) * (i = 0..<n. B $$ (g i, (π  ?inv φ) i))))"
    by (metis (mono_tags, lifting) Groups.mult_ac(1) semiring_0_class.sum_distrib_left sum.cong)
  also have "... = (φ  ?Perm. signof φ * (i = 0..<n. A $$ (i, g (φ i))) *
    (π  ?Perm. signof π * (i = 0..<n. B $$ (g i, π i))))" using detB_rw by auto
  also have "... = (φ  ?Perm. signof φ * (i = 0..<n. A $$ (i, g (φ i)))) *
    (π  ?Perm. signof π * (i = 0..<n. B $$ (g i, π i)))"
    by (simp add: semiring_0_class.sum_distrib_right)
  also have "... = ?lhs" unfolding detA detB ..
  finally show ?thesis ..
qed


lemma gather_by_strictness:
"sum (λg. sum (λ(f,π). weight f π) (Z_good g)) F_strict
  = sum (λg. det (submatrix A UNIV (g`{0..<n})) * det (submatrix B (g`{0..<n}) UNIV)) F_strict"
proof (rule sum.cong)
  fix f assume f: "f  F_strict"
  show "((x, y)Z_good f. weight x y)
    = det (submatrix A UNIV (f ` {0..<n})) * det (submatrix B (f ` {0..<n}) UNIV)"
    by (rule one_step[symmetric], rule f)
qed (simp)

lemma finite_Z_strict[simp]: "finite Z_strict"
proof (unfold Z_strict_def, rule finite_cartesian_product)
  have finN: "finite {0..<n}" and finM: "finite {0..<m}" by auto
  let ?A="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)  strict_mono_on {0..<n} f}"
  let ?B="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)}"
  have B: "{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)} = ?B" by auto
  have "?A?B" by auto
  moreover have "finite ?B" using B finite_bounded_functions[OF finM finN] by auto
  ultimately show "finite ?A" using rev_finite_subset by blast
  show "finite {π. π permutes {0..<n}}" using finite_permutations by blast
qed

lemma finite_Z_not_strict[simp]: "finite Z_not_strict"
proof (unfold Z_not_strict_def, rule finite_cartesian_product)
  have finN: "finite {0..<n}" and finM: "finite {0..<m}" by auto
  let ?A="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)  ¬ strict_mono_on {0..<n} f}"
  let ?B="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)}"
  have B: "{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)} = ?B" by auto
  have "?A?B" by auto
  moreover have "finite ?B" using B finite_bounded_functions[OF finM finN] by auto
  ultimately show "finite ?A" using rev_finite_subset by blast
  show "finite {π. π permutes {0..<n}}" using finite_permutations by blast
qed

lemma finite_Znm[simp]: "finite (Z n m)"
proof (unfold Z_alt_def, rule finite_cartesian_product)
  have finN: "finite {0..<n}" and finM: "finite {0..<m}" by auto
  let ?A="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i) }"
  let ?B="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)}"
  have B: "{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)} = ?B" by auto
  have "?A?B" by auto
  moreover have "finite ?B" using B finite_bounded_functions[OF finM finN] by auto
  ultimately show "finite ?A" using rev_finite_subset by blast
  show "finite {π. π permutes {0..<n}}" using finite_permutations by blast
qed

lemma finite_F_inj[simp]: "finite F_inj"
proof -
  have finN: "finite {0..<n}" and finM: "finite {0..<m}" by auto
  let ?A="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)  inj_on f {0..<n}}"
  let ?B="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)}"
  have B: "{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)} = ?B" by auto
  have "?A?B" by auto
  moreover have "finite ?B" using B finite_bounded_functions[OF finM finN] by auto
  ultimately show "finite F_inj" unfolding F_inj_def using rev_finite_subset by blast
qed

lemma finite_F_strict[simp]: "finite F_strict"
proof -
 have finN: "finite {0..<n}" and finM: "finite {0..<m}" by auto
  let ?A="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)  strict_mono_on {0..<n} f}"
  let ?B="{f  {0..<n}  {0..<m}. (i. i  {0..<n}  f i = i)}"
  have B: "{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)} = ?B" by auto
  have "?A?B" by auto
  moreover have "finite ?B" using B finite_bounded_functions[OF finM finN] by auto
  ultimately show "finite F_strict" unfolding F_strict_def using rev_finite_subset by blast
qed

lemma nth_strict_mono:
  fixes f::"nat  nat"
  assumes  strictf: "strict_mono f" and i: "i<n"
shows "f i = (sorted_list_of_set (f`{0..<n})) ! i"
proof -
  let ?I = "f`{0..<n}"
  have "length (sorted_list_of_set (f ` {0..<n})) = card ?I"
    by (metis distinct_card finite_atLeastLessThan finite_imageI
        sorted_list_of_set(1) sorted_list_of_set(3))
  also have "... = n"
    by (simp add: card_image strict_mono_imp_inj_on strictf)
  finally have length_I: "length (sorted_list_of_set ?I) = n" .
  have card_eq: "card {a  ?I. a < f i} = i"
    using i
  proof (induct i)
    case 0
    then show ?case
      by (auto simp add: strict_mono_less strictf)
  next
    case (Suc i)
    have i: "i < n" using Suc.prems by auto
    let ?J'="{a  f ` {0..<n}. a < f i}"
    let ?J = "{a  f ` {0..<n}. a < f (Suc i)}"
    have cardJ': "card ?J' = i" by (rule Suc.hyps[OF i])
    have J: "?J = insert (f i) ?J'"
    proof (auto)
      fix xa assume 1: "f xa  f i" and 2: "f xa < f (Suc i)"
      show "f xa < f i"
        using 1 2 not_less_less_Suc_eq strict_mono_less strictf by fastforce
    next
      fix xa assume "f xa < f i" thus "f xa < f (Suc i)"
        using less_SucI strict_mono_less strictf by blast
    next
      show "f i  f ` {0..<n}" using i by auto
      show "f i < f (Suc i)" using strictf strict_mono_less by auto
    qed
    have "card ?J = Suc (card ?J')" by (unfold J, rule card_insert_disjoint, auto)
    then show ?case using cardJ' by auto
  qed
  have "sorted_list_of_set ?I ! i = pick ?I i"
    by (rule sorted_list_of_set_eq_pick, simp add: card (f ` {0..<n}) = n i)
  also have "... =  pick ?I (card {a  ?I. a < f i})" unfolding card_eq by simp
  also have "... = f i" by (rule pick_card_in_set, simp add: i)
  finally show ?thesis ..
qed

lemma nth_strict_mono_on:
  fixes f::"nat  nat"
  assumes  strictf: "strict_mono_on {0..<n} f" and i: "i<n"
shows "f i = (sorted_list_of_set (f`{0..<n})) ! i"
proof -
  let ?I = "f`{0..<n}"
  have "length (sorted_list_of_set (f ` {0..<n})) = card ?I"
    by (metis distinct_card finite_atLeastLessThan finite_imageI
        sorted_list_of_set(1) sorted_list_of_set(3))
  also have "... = n"
    by (metis (mono_tags, lifting) card_atLeastLessThan card_image diff_zero
        inj_on_def strict_mono_on_eqD strictf)
  finally have length_I: "length (sorted_list_of_set ?I) = n" .
  have card_eq: "card {a  ?I. a < f i} = i"
    using i
  proof (induct i)
    case 0
    then show ?case
      by (auto, metis (no_types, lifting) atLeast0LessThan lessThan_iff less_Suc_eq
          not_less0 not_less_eq strict_mono_on_def strictf)
  next
    case (Suc i)
    have i: "i < n" using Suc.prems by auto
    let ?J'="{a  f ` {0..<n}. a < f i}"
    let ?J = "{a  f ` {0..<n}. a < f (Suc i)}"
    have cardJ': "card ?J' = i" by (rule Suc.hyps[OF i])
    have J: "?J = insert (f i) ?J'"
    proof (auto)
      fix xa assume 1: "f xa  f i" and 2: "f xa < f (Suc i)" and 3: "xa < n"
      show "f xa < f i"
        by (metis (full_types) 1 2 3 antisym_conv3 atLeast0LessThan i lessThan_iff
            less_SucE order.asym strict_mono_onD strictf)
    next
      fix xa assume "f xa < f i" and "xa < n" thus "f xa < f (Suc i)"
        using less_SucI strictf
        by (metis (no_types, lifting) Suc.prems atLeast0LessThan
            lessI lessThan_iff less_trans strict_mono_onD)
    next
      show "f i  f ` {0..<n}" using i by auto
      show "f i < f (Suc i)"
        using Suc.prems strict_mono_onD strictf by fastforce
    qed
    have "card ?J = Suc (card ?J')" by (unfold J, rule card_insert_disjoint, auto)
    then show ?case using cardJ' by auto
  qed
  have "sorted_list_of_set ?I ! i = pick ?I i"
    by (rule sorted_list_of_set_eq_pick, simp add: card (f ` {0..<n}) = n i)
  also have "... =  pick ?I (card {a  ?I. a < f i})" unfolding card_eq by simp
  also have "... = f i" by (rule pick_card_in_set, simp add: i)
  finally show ?thesis ..
qed

lemma strict_fun_eq:
  assumes f: "f  F_strict" and g: "g  F_strict" and fg: "f`{0..<n} = g`{0..<n}"
  shows "f = g"
proof (unfold fun_eq_iff, auto)
  fix x
  show "f x = g x"
  proof (cases "x<n")
    case True
    have strictf: "strict_mono_on {0..<n} f" and strictg: "strict_mono_on {0..<n} g"
      using f g unfolding F_strict_def by auto
    have "f x = (sorted_list_of_set (f`{0..<n})) ! x" by (rule nth_strict_mono_on[OF strictf True])
    also have "... = (sorted_list_of_set (g`{0..<n})) ! x" unfolding fg by simp
    also have "... = g x" by (rule nth_strict_mono_on[symmetric, OF strictg True])
    finally show ?thesis .
  next
    case False
    then show ?thesis using f g unfolding F_strict_def by auto
  qed
qed


lemma strict_from_inj_preserves_F:
  assumes f: "f  F_inj"
  shows "strict_from_inj n f  F"
proof -
  {
    fix x assume x: "x < n"
    have inj_on: "inj_on f {0..<n}" using f unfolding F_inj_def by auto
    have "{a. a < m  a  f ` {0..<n}} = f`{0..<n}" using f unfolding F_inj_def by auto
    hence card_eq: "card {a. a < m  a  f ` {0..<n}} = n"
      by (simp add: card_image inj_on)
    let ?I = "f`{0..<n}"
    have "length (sorted_list_of_set (f ` {0..<n})) = card ?I"
      by (metis distinct_card finite_atLeastLessThan finite_imageI
          sorted_list_of_set(1) sorted_list_of_set(3))
    also have "... = n"
      by (simp add: card_image strict_mono_imp_inj_on inj_on)
    finally have length_I: "length (sorted_list_of_set ?I) = n" .
    have "sorted_list_of_set (f ` {0..<n}) ! x = pick (f ` {0..<n}) x"
      by (rule sorted_list_of_set_eq_pick, unfold length_I, auto simp add: x)
    also have "... < m" by (rule pick_le, unfold card_eq, rule x)
    finally have "sorted_list_of_set (f ` {0..<n}) ! x < m" .
  }
  thus ?thesis unfolding strict_from_inj_def F_def by auto
qed

lemma strict_from_inj_F_strict: "strict_from_inj n xa  F_strict"
  if xa: "xa  F_inj" for xa
proof -
  have "strict_mono_on {0..<n} (strict_from_inj n xa)"
    by (rule strict_strict_from_inj, insert xa, simp add: F_inj_def)
  thus ?thesis using strict_from_inj_preserves_F[OF xa] unfolding F_def F_strict_def by auto
qed

lemma strict_from_inj_image:
  assumes f: "f F_inj"
  shows "strict_from_inj n f ` {0..<n} = f`{0..<n}"
proof (auto)
  let ?I = "f ` {0..<n}"
  fix xa assume xa: "xa < n"
  have inj_on: "inj_on f {0..<n}" using f unfolding F_inj_def by auto
    have "{a. a < m  a  f ` {0..<n}} = f`{0..<n}" using f unfolding F_inj_def by auto
    hence card_eq: "card {a. a < m  a  f ` {0..<n}} = n"
      by (simp add: card_image inj_on)
    let ?I = "f`{0..<n}"
    have "length (sorted_list_of_set (f ` {0..<n})) = card ?I"
      by (metis distinct_card finite_atLeastLessThan finite_imageI
          sorted_list_of_set(1) sorted_list_of_set(3))
    also have "... = n"
      by (simp add: card_image strict_mono_imp_inj_on inj_on)
    finally have length_I: "length (sorted_list_of_set ?I) = n" .
  have "strict_from_inj n f xa = sorted_list_of_set ?I ! xa"
    using xa unfolding strict_from_inj_def by auto
  also have "... = pick ?I xa"
    by (rule sorted_list_of_set_eq_pick, unfold length_I, auto simp add: xa)
  also have "...  f ` {0..<n}" by (rule pick_in_set_le, simp add: card (f ` {0..<n}) = n xa)
  finally show "strict_from_inj n f xa  f ` {0..<n}" .
  obtain i where "sorted_list_of_set (f`{0..<n}) ! i = f xa" and "i<n"
    by (metis atLeast0LessThan finite_atLeastLessThan finite_imageI imageI
        in_set_conv_nth length_I lessThan_iff sorted_list_of_set(1) xa)
  thus "f xa  strict_from_inj n f ` {0..<n}"
    by (metis atLeast0LessThan imageI lessThan_iff strict_from_inj_def)
qed


lemma Z_good_alt:
  assumes g: "g  F_strict"
  shows "Z_good g = {x  F_inj. strict_from_inj n x = g} × {π. π permutes {0..<n}}"
proof -
  define Z_good_fun where "Z_good_fun = {f. f  {0..<n}  {0..<m}  (i. i  {0..<n}  f i = i)
   inj_on f {0..<n}  (f`{0..<n} = g`{0..<n})}"
  have "Z_good_fun = {x  F_inj. strict_from_inj n x = g}"
  proof (auto)
    fix f assume f: "f  Z_good_fun" thus f_inj: "f  F_inj" unfolding F_inj_def Z_good_fun_def by auto
    show "strict_from_inj n f = g"
    proof (rule strict_fun_eq[OF _ g])
      show "strict_from_inj n f ` {0..<n} = g ` {0..<n}"
        using f_inj f strict_from_inj_image
        unfolding Z_good_fun_def F_inj_def by auto
      show "strict_from_inj n f  F_strict"
        using F_strict_def f_inj strict_from_inj_F_strict by blast
    qed
  next
    fix f assume f_inj: "f  F_inj" and g_strict_f: "g = strict_from_inj n f"
    have "f xa  g ` {0..<n}" if "xa < n" for xa
      using f_inj g_strict_f strict_from_inj_image that by auto
    moreover have "g xa  f ` {0..<n}" if "xa < n" for xa
      by (metis f_inj g_strict_f imageI lessThan_atLeast0 lessThan_iff strict_from_inj_image that)
    ultimately show "f  Z_good_fun"
      using f_inj g_strict_f unfolding Z_good_fun_def F_inj_def
      by auto
  qed
  thus ?thesis unfolding Z_good_fun_def Z_good_def by simp
qed


lemma weight_0: "((f, π)  Z_not_inj. weight f π) = 0"
proof -
  let ?F="{f. (i{0..<n}. f i  {0..<m})  (i. i  {0..<n}  f i = i)}"
  let ?Perm = "{π. π permutes {0..<n}}"
  have "((f, π)Z_not_inj. weight f π)
    = (f  F_not_inj. (i = 0..<n. A $$ (i, f i)) * det (matr n n (λi. row B (f i))))"
  proof -
    have dim_row_rw: "dim_row (matr n n (λi. col A (f i))) = n" for f by auto
    have dim_row_rw2: "dim_row (matr n n (λi. Matrix.row B (f i))) = n" for f by auto
    have prod_rw: "(i = 0..<n. B $$ (f i, π i)) =  (i = 0..<n. row B (f i) $v π i)"
      if f: "f  F_not_inj" and pi: "π  ?Perm" for f π
    proof (rule prod.cong, rule refl)
      fix x assume x: "x  {0..<n}"
      have "f x < dim_row B" using f B x unfolding F_not_inj_def by fastforce
      moreover have "π x < dim_col B" using x pi B by auto
      ultimately show "B $$ (f x, π x) = Matrix.row B (f x) $v π x" by (rule index_row[symmetric])
    qed
    have sum_rw: "(π | π permutes {0..<n}. signof π * (i = 0..<n. B $$ (f i, π i)))
      = det (matr n n (λi. row B (f i)))" if f: "f  F_not_inj" for f
      unfolding Determinant.det_def using dim_row_rw2 prod_rw f by auto
    have "((f, π)Z_not_inj. weight f π) = (fF_not_inj.π  ?Perm. weight f π)"
      unfolding Z_not_inj_def unfolding sum.cartesian_product
      unfolding F_not_inj_def by simp
    also have "... = (fF_not_inj. π | π permutes {0..<n}. signof π
      * (i = 0..<n. A $$ (i, f i) * B $$ (f i, π i)))"
      unfolding weight_def by simp
    also have "... = (fF_not_inj. (i = 0..<n. A $$ (i, f i))
      * (π | π permutes {0..<n}. signof π * (i = 0..<n. B $$ (f i, π i))))"
      by (rule sum.cong, rule refl, auto)
         (metis (no_types, lifting) mult.left_commute mult_hom.hom_sum sum.cong)
    also have "... = (f  F_not_inj. (i = 0..<n. A $$ (i, f i))
      * det (matr n n (λi. row B (f i))))" using sum_rw by auto
    finally show ?thesis by auto
  qed
  also have "... = 0"
    by (rule sum.neutral, insert det_not_inj_on[of _ n B], auto simp add: F_not_inj_def)
  finally show ?thesis .
qed

subsection ‹Final theorem›

lemma Cauchy_Binet1:
  shows "det (A*B) =
  sum (λf. det (submatrix A UNIV (f`{0..<n})) * det (submatrix B (f`{0..<n}) UNIV)) F_strict"
(is "?lhs = ?rhs")
proof -
  have sum0: "((f, π)  Z_not_inj. weight f π) = 0" by (rule weight_0)
  let ?f = "strict_from_inj n"
  have sum_rw: "sum g F_inj = (y  F_strict. sum g {x  F_inj. ?f x = y})" for g
    by (rule sum.group[symmetric], insert strict_from_inj_F_strict, auto)
  have Z_Union: "Z_inj  Z_not_inj = Z n m"
    unfolding Z_def Z_not_inj_def Z_inj_def by auto
  have Z_Inter: "Z_inj  Z_not_inj = {}"
    unfolding Z_def Z_not_inj_def Z_inj_def by auto
  have "det (A*B) = ((f, π)Z n m. weight f π)"
    using detAB_Znm[OF A B] unfolding weight_def by auto
  also have "... = ((f, π)Z_inj. weight f π) + ((f, π)Z_not_inj. weight f π)"
    by (metis Z_Inter Z_Union finite_Un finite_Znm sum.union_disjoint)
  also have "... = ((f, π)Z_inj. weight f π)" using sum0 by force
  also have "... = (f  F_inj. π{π. π permutes {0..<n}}. weight f π)"
    unfolding Z_inj_def unfolding F_inj_def sum.cartesian_product ..
  also have "... =  (yF_strict. f{x  F_inj. strict_from_inj n x = y}.
    sum (weight f) {π. π permutes {0..<n}})" unfolding sum_rw ..
  also have "... =  (yF_strict. (f,π)({x  F_inj. strict_from_inj n x = y}
  × {π. π permutes {0..<n}}). weight f π)"
    unfolding F_inj_def sum.cartesian_product ..
  also have "... = sum (λg. sum (λ(f,π). weight f π) (Z_good g)) F_strict"
    using Z_good_alt by auto
  also have "... = ?rhs" unfolding gather_by_strictness by simp
  finally show ?thesis .
qed


lemma Cauchy_Binet:
  "det (A*B) = (I{I. I{0..<m}  card I=n}. det (submatrix A UNIV I) * det (submatrix B I UNIV))"
proof -
  let ?f="(λI. (λi. if i<n then sorted_list_of_set I ! i else i))"
  let ?setI = "{I. I  {0..<m}  card I = n}"
  have inj_on: "inj_on ?f ?setI"
  proof (rule inj_onI)
    fix I J assume I: "I  ?setI" and J: "J  ?setI" and fI_fJ: "?f I = ?f J"
    have "x  J" if x: "x  I" for x
      by (metis (mono_tags) fI_fJ I J distinct_card in_set_conv_nth mem_Collect_eq
          sorted_list_of_set(1) sorted_list_of_set(3) subset_eq_atLeast0_lessThan_finite x)
    moreover have "x  I" if x: "x  J" for x
      by (metis (mono_tags) fI_fJ I J distinct_card in_set_conv_nth mem_Collect_eq
          sorted_list_of_set(1) sorted_list_of_set(3) subset_eq_atLeast0_lessThan_finite x)
    ultimately show "I = J" by auto
  qed
  have rw: "?f I ` {0..<n} = I" if I: "I  ?setI" for I
  proof -
    have "sorted_list_of_set I ! xa  I" if "xa < n" for xa
      by (metis (mono_tags, lifting) I distinct_card distinct_sorted_list_of_set mem_Collect_eq
          nth_mem set_sorted_list_of_set subset_eq_atLeast0_lessThan_finite that)
    moreover have "xa{0..<n}. x = sorted_list_of_set I ! xa" if x: "xI" for x
      by (metis (full_types) x I atLeast0LessThan distinct_card in_set_conv_nth mem_Collect_eq
         lessThan_iff sorted_list_of_set(1) sorted_list_of_set(3) subset_eq_atLeast0_lessThan_finite)
    ultimately show ?thesis unfolding image_def by auto
  qed
  have f_setI: "?f` ?setI = F_strict"
  proof -
    have "sorted_list_of_set I ! xa < m" if I: "I  {0..<m}" and "n = card I" and "xa < card I"
        for I xa
      by (metis I xa < card I atLeast0LessThan distinct_card finite_atLeastLessThan lessThan_iff
          pick_in_set_le rev_finite_subset sorted_list_of_set(1)
          sorted_list_of_set(3) sorted_list_of_set_eq_pick subsetCE)
    moreover have "strict_mono_on {0..<card I} (λi. if i < card I then sorted_list_of_set I ! i else i)"
      if "I  {0..<m}" and "n = card I" for I
      by (smt I  {0..<m} atLeastLessThan_iff distinct_card finite_atLeastLessThan pick_mono_le
          rev_finite_subset sorted_list_of_set(1) sorted_list_of_set(3)
          sorted_list_of_set_eq_pick strict_mono_on_def)
    moreover have "x  ?f ` {I. I  {0..<m}  card I = n}"
      if x1: "x  {0..<n}  {0..<m}" and x2: "i. ¬ i < n  x i = i"
      and s: "strict_mono_on {0..<n} x" for x
    proof -
      have inj_x: "inj_on x {0..<n}"
        using s strict_mono_on_imp_inj_on by blast
      hence card_xn: "card (x ` {0..<n}) = n" by (simp add: card_image)
      have x_eq: "x = (λi. if i < n then sorted_list_of_set (x ` {0..<n}) ! i else i)"
        unfolding fun_eq_iff
        using nth_strict_mono_on s using x2 by auto
      show ?thesis
        unfolding image_def by (auto, rule exI[of _"x`{0..<n}"], insert card_xn x1 x_eq, auto)
    qed
    ultimately show ?thesis unfolding F_strict_def by auto
  qed
  let ?g = "(λf. det (submatrix A UNIV (f`{0..<n})) * det(submatrix B (f`{0..<n}) UNIV))"
  have "det (A*B) = sum ((λf. det (submatrix A UNIV (f ` {0..<n}))
    * det (submatrix B (f ` {0..<n}) UNIV))  ?f) {I. I  {0..<m}  card I = n}"
    unfolding Cauchy_Binet1 f_setI[symmetric] by (rule sum.reindex[OF inj_on])
  also have "... = (I{I. I{0..<m}  card I=n}.det(submatrix A UNIV I)*det(submatrix B I UNIV))"
    by (rule sum.cong, insert rw, auto)
  finally show ?thesis .
qed
end

end