Theory Euler_Formula

section ‹Euler's Polyhedron Formula›

text ‹One of the Famous 100 Theorems, ported from HOL Light›
text‹Cited source:  
Lawrence, J. (1997). A Short Proof of Euler's Relation for Convex Polytopes. 
\emph{Canadian Mathematical Bulletin}, \textbf{40}(4), 471--474.›

theory Euler_Formula
  imports 
    "HOL-Analysis.Analysis" 
begin


text‹ Interpret which "side" of a hyperplane a point is on.                     ›

definition hyperplane_side
  where "hyperplane_side  λ(a,b). λx. sgn (a  x - b)"

text‹ Equivalence relation imposed by a hyperplane arrangement.                 ›

definition hyperplane_equiv
 where "hyperplane_equiv  λA x y. h  A. hyperplane_side h x = hyperplane_side h y"

lemma hyperplane_equiv_refl [iff]: "hyperplane_equiv A x x"
  by (simp add: hyperplane_equiv_def)

lemma hyperplane_equiv_sym:
   "hyperplane_equiv A x y  hyperplane_equiv A y x"
  by (auto simp: hyperplane_equiv_def)

lemma hyperplane_equiv_trans:
   "hyperplane_equiv A x y; hyperplane_equiv A y z  hyperplane_equiv A x z"
  by (auto simp: hyperplane_equiv_def)

lemma hyperplane_equiv_Un:
   "hyperplane_equiv (A  B) x y  hyperplane_equiv A x y  hyperplane_equiv B x y"
  by (meson Un_iff hyperplane_equiv_def)

subsection‹ Cells of a hyperplane arrangement›

definition hyperplane_cell :: "('a::real_inner × real) set  'a set  bool"
  where "hyperplane_cell  λA C. x. C = Collect (hyperplane_equiv A x)"

lemma hyperplane_cell: "hyperplane_cell A C  (x. C = {y. hyperplane_equiv A x y})"
  by (simp add: hyperplane_cell_def)

lemma not_hyperplane_cell_empty [simp]: "¬ hyperplane_cell A {}"
  using hyperplane_cell by auto

lemma nonempty_hyperplane_cell: "hyperplane_cell A C  (C  {})"
  by auto

lemma Union_hyperplane_cells: " {C. hyperplane_cell A C} = UNIV"
  using hyperplane_cell by blast

lemma disjoint_hyperplane_cells:
   "hyperplane_cell A C1; hyperplane_cell A C2; C1  C2  disjnt C1 C2"
  by (force simp: hyperplane_cell_def disjnt_iff hyperplane_equiv_def)

lemma disjoint_hyperplane_cells_eq:
   "hyperplane_cell A C1; hyperplane_cell A C2  (disjnt C1 C2  (C1  C2))"
  using disjoint_hyperplane_cells by auto

lemma hyperplane_cell_empty [iff]: "hyperplane_cell {} C  C = UNIV"
  by (simp add: hyperplane_cell hyperplane_equiv_def)

lemma hyperplane_cell_singleton_cases:
  assumes "hyperplane_cell {(a,b)} C"
  shows "C = {x. a  x = b}  C = {x. a  x < b}  C = {x. a  x > b}"
proof -
  obtain x where x: "C = {y. hyperplane_side (a, b) x = hyperplane_side (a, b) y}"
    using assms by (auto simp: hyperplane_equiv_def hyperplane_cell)
  then show ?thesis
    by (auto simp: hyperplane_side_def sgn_if split: if_split_asm)
qed

lemma hyperplane_cell_singleton:
   "hyperplane_cell {(a,b)} C 
    (if a = 0 then C = UNIV else C = {x. a  x = b}  C = {x. a  x < b}  C = {x. a  x > b})"
  apply (simp add: hyperplane_cell_def hyperplane_equiv_def hyperplane_side_def sgn_if split: if_split_asm)
  by (smt (verit) Collect_cong gt_ex hyperplane_eq_Ex lt_ex)

lemma hyperplane_cell_Un:
   "hyperplane_cell (A  B) C 
        C  {} 
        (C1 C2. hyperplane_cell A C1  hyperplane_cell B C2  C = C1  C2)"
  by (auto simp: hyperplane_cell hyperplane_equiv_def)

lemma finite_hyperplane_cells:
   "finite A  finite {C. hyperplane_cell A C}"
proof (induction rule: finite_induct)
  case (insert p A)
  obtain a b where peq: "p = (a,b)"
    by fastforce
  have "Collect (hyperplane_cell {p})  {{x. a  x = b},{x. a  x < b},{x. a  x > b}}"
    using hyperplane_cell_singleton_cases
    by (auto simp: peq)
  then have *: "finite (Collect (hyperplane_cell {p}))"
    by (simp add: finite_subset)
  define 𝒞 where "𝒞  (C1  {C. hyperplane_cell A C}.  C2  {C. hyperplane_cell {p} C}. {C1  C2})"
  have "{a. hyperplane_cell (insert p A) a}   𝒞"
    using hyperplane_cell_Un [of "{p}" A] by (auto simp: 𝒞_def)
  moreover have "finite 𝒞"
    using * 𝒞_def insert.IH by blast
  ultimately show ?case
    using finite_subset by blast
qed auto

lemma finite_restrict_hyperplane_cells:
   "finite A  finite {C. hyperplane_cell A C  P C}"
  by (simp add: finite_hyperplane_cells)

lemma finite_set_of_hyperplane_cells:
   "finite A; C. C  𝒞  hyperplane_cell A C  finite 𝒞"
  by (metis finite_hyperplane_cells finite_subset mem_Collect_eq subsetI)

lemma pairwise_disjoint_hyperplane_cells:
   "(C. C  𝒞  hyperplane_cell A C)  pairwise disjnt 𝒞"
  by (metis disjoint_hyperplane_cells pairwiseI)

lemma hyperplane_cell_Int_open_affine:
  assumes "finite A" "hyperplane_cell A C"
  obtains S T where "open S" "affine T" "C = S  T"
  using assms
proof (induction arbitrary: thesis C rule: finite_induct)
  case empty
  then show ?case
    by auto 
next
  case (insert p A thesis C')
  obtain a b where peq: "p = (a,b)"
    by fastforce
  obtain C C1 where C1: "hyperplane_cell {(a,b)} C1" and C: "hyperplane_cell A C" 
              and "C'  {}" and C': "C' = C1  C"
    by (metis hyperplane_cell_Un insert.prems(2) insert_is_Un peq)
  then obtain S T where ST: "open S" "affine T" "C = S  T"
    by (meson insert.IH)
  show ?case
  proof (cases "a=0")
    case True
    with insert.prems show ?thesis
      by (metis C1 Int_commute ST C' = C1  C hyperplane_cell_singleton inf_top.right_neutral) 
  next
    case False
    then consider "C1 = {x. a  x = b}" | "C1 = {x. a  x < b}" | "C1 = {x. b < a  x}"
      by (metis C1 hyperplane_cell_singleton)
    then show ?thesis
    proof cases
      case 1
      then show thesis
        by (metis C' ST affine_Int affine_hyperplane inf_left_commute insert.prems(1))
    next
      case 2
      with ST show thesis
          by (metis Int_assoc C' insert.prems(1) open_Int open_halfspace_lt)
    next
      case 3
      with ST show thesis
        by (metis Int_assoc C' insert.prems(1) open_Int open_halfspace_gt)
    qed
  qed
qed

lemma hyperplane_cell_relatively_open:
  assumes "finite A" "hyperplane_cell A C"
  shows "openin (subtopology euclidean (affine hull C)) C"
proof -
  obtain S T where "open S" "affine T" "C = S  T"
    by (meson assms hyperplane_cell_Int_open_affine)
  show ?thesis
  proof (cases "S  T = {}")
    case True
    then show ?thesis
      by (simp add: C = S  T)
  next
    case False
    then have "affine hull (S  T) = T"
      by (metis affine T open S affine_hull_affine_Int_open hull_same inf_commute)
    then show ?thesis
      using C = S  T open S openin_subtopology by fastforce
  qed
qed

lemma hyperplane_cell_relative_interior:
   "finite A; hyperplane_cell A C  rel_interior C = C"
  by (simp add: hyperplane_cell_relatively_open rel_interior_openin)

lemma hyperplane_cell_convex:
  assumes "hyperplane_cell A C"
  shows "convex C"
proof -
  obtain c where c: "C = {y. hyperplane_equiv A c y}"
    by (meson assms hyperplane_cell)
  have "convex (hA. {y. hyperplane_side h c = hyperplane_side h y})"
  proof (rule convex_INT)
    fix h :: "'a × real"
    assume "h  A"
    obtain a b where heq: "h = (a,b)"
      by fastforce
    have [simp]: "{y. ¬ a  c < a  y  a  y = a  c} = {y. a  y = a  c}"
                 "{y. ¬ b < a  y  a  y  b} = {y. b > a  y}"
      by auto
    then show "convex {y. hyperplane_side h c = hyperplane_side h y}"
      by (fastforce simp: heq hyperplane_side_def sgn_if convex_halfspace_gt convex_halfspace_lt convex_hyperplane cong: conj_cong)
  qed
  with c show ?thesis
    by (simp add: hyperplane_equiv_def INTER_eq)
qed

lemma hyperplane_cell_Inter:
  assumes "C. C  𝒞  hyperplane_cell A C"
    and "𝒞  {}" and INT: "𝒞  {}"
  shows "hyperplane_cell A (𝒞)"
proof -
  have "𝒞 = {y. hyperplane_equiv A z y}" 
    if "z  𝒞" for z
      using assms that by (force simp: hyperplane_cell hyperplane_equiv_def)
  with INT hyperplane_cell show ?thesis
    by fastforce
qed


lemma hyperplane_cell_Int:
   "hyperplane_cell A S; hyperplane_cell A T; S  T  {}  hyperplane_cell A (S  T)"
  by (metis hyperplane_cell_Un sup.idem)

subsection‹ A cell complex is considered to be a union of such cells›

definition hyperplane_cellcomplex 
  where "hyperplane_cellcomplex A S 
        𝒯. (C  𝒯. hyperplane_cell A C)  S = 𝒯"

lemma hyperplane_cellcomplex_empty [simp]: "hyperplane_cellcomplex A {}"
  using hyperplane_cellcomplex_def by auto

lemma hyperplane_cell_cellcomplex:
   "hyperplane_cell A C  hyperplane_cellcomplex A C"
  by (auto simp: hyperplane_cellcomplex_def)

lemma hyperplane_cellcomplex_Union:
  assumes "S. S  𝒞  hyperplane_cellcomplex A S"
  shows "hyperplane_cellcomplex A ( 𝒞)"
proof -
  obtain  where : "S. S  𝒞  (C   S. hyperplane_cell A C)  S = ( S)"
    by (metis assms hyperplane_cellcomplex_def)
  show ?thesis
    unfolding hyperplane_cellcomplex_def
    using  by (fastforce intro: exI [where x=" ( ` 𝒞)"])
qed

lemma hyperplane_cellcomplex_Un:
   "hyperplane_cellcomplex A S; hyperplane_cellcomplex A T
         hyperplane_cellcomplex A (S  T)"
  by (smt (verit) Un_iff Union_Un_distrib hyperplane_cellcomplex_def)

lemma hyperplane_cellcomplex_UNIV [simp]: "hyperplane_cellcomplex A UNIV"
  by (metis Union_hyperplane_cells hyperplane_cellcomplex_def mem_Collect_eq)

lemma hyperplane_cellcomplex_Inter:
  assumes "S. S  𝒞  hyperplane_cellcomplex A S"
  shows "hyperplane_cellcomplex A (𝒞)"
proof (cases "𝒞 = {}")
  case True
  then show ?thesis
    by simp
next
  case False
  obtain  where : "S. S  𝒞  (C   S. hyperplane_cell A C)  S = ( S)"
    by (metis assms hyperplane_cellcomplex_def)
  have *: "𝒞 = (λS. ( S)) ` 𝒞"
    using  by force
  define U where "U   {T  {(g ` 𝒞) |g. S𝒞. g S   S}. T  {}}"
  have "𝒞 = {(g ` 𝒞) |g. S𝒞. g S   S}"
    using False  unfolding Inter_over_Union [symmetric]
    by blast
  also have " = U"
    unfolding U_def
    by blast
  finally have "𝒞 = U" .
  have "hyperplane_cellcomplex A U"
    using False  unfolding U_def
    apply (intro hyperplane_cellcomplex_Union hyperplane_cell_cellcomplex)
    by (auto intro!: hyperplane_cell_Inter)
  then show ?thesis
     by (simp add: 𝒞 = U)
qed

lemma hyperplane_cellcomplex_Int:
   "hyperplane_cellcomplex A S; hyperplane_cellcomplex A T
         hyperplane_cellcomplex A (S  T)"
  using hyperplane_cellcomplex_Inter [of "{S,T}"] by force

lemma hyperplane_cellcomplex_Compl:
  assumes "hyperplane_cellcomplex A S"
  shows "hyperplane_cellcomplex A (- S)"
proof -
  obtain 𝒞 where 𝒞: "C. C  𝒞  hyperplane_cell A C" and "S = 𝒞"
    by (meson assms hyperplane_cellcomplex_def)
  have "hyperplane_cellcomplex A (T  𝒞. -T)"
  proof (intro hyperplane_cellcomplex_Inter)
    fix C0
    assume "C0  uminus ` 𝒞"
    then obtain C where C: "C0 = -C" "C  𝒞"
      by auto
    have *: "-C =  {D. hyperplane_cell A D  D  C}"  (is "_ = ?rhs")
    proof
      show "- C  ?rhs"
        using hyperplane_cell by blast
      show "?rhs  - C"
        by clarify (meson C  𝒞 𝒞 disjnt_iff disjoint_hyperplane_cells)
    qed
    then show "hyperplane_cellcomplex A C0"
      by (metis (no_types, lifting) C(1) hyperplane_cell_cellcomplex hyperplane_cellcomplex_Union mem_Collect_eq)
  qed
  then show ?thesis
    by (simp add: S =  𝒞 uminus_Sup)
qed

lemma hyperplane_cellcomplex_diff:
   "hyperplane_cellcomplex A S; hyperplane_cellcomplex A T
         hyperplane_cellcomplex A (S - T)"
  using hyperplane_cellcomplex_Inter [of "{S,-T}"] 
  by (force simp: Diff_eq hyperplane_cellcomplex_Compl)

lemma hyperplane_cellcomplex_mono:
  assumes "hyperplane_cellcomplex A S" "A  B"
  shows "hyperplane_cellcomplex B S"
proof -
  obtain 𝒞 where 𝒞: "C. C  𝒞  hyperplane_cell A C" and eq: "S = 𝒞"
    by (meson assms hyperplane_cellcomplex_def)
  show ?thesis
    unfolding eq
  proof (intro hyperplane_cellcomplex_Union)
    fix C
    assume "C  𝒞"
    have "x. x  C  D'. (D. D' = D  C  hyperplane_cell (B - A) D  D  C  {})  x  D'"
      unfolding hyperplane_cell_def by blast
    then
    have "hyperplane_cellcomplex (A  (B - A)) C"
      unfolding hyperplane_cellcomplex_def hyperplane_cell_Un
      using 𝒞 C  𝒞 by (fastforce intro!: exI [where x=" {D  C |D. hyperplane_cell (B - A) D  D  C  {}}"])
    moreover have "B = A  (B - A)"
      using A  B by auto
    ultimately show "hyperplane_cellcomplex B C" by simp
  qed
qed

lemma finite_hyperplane_cellcomplexes:
  assumes "finite A"
  shows "finite {C. hyperplane_cellcomplex A C}"
proof -
  have "{C. hyperplane_cellcomplex A C}  image  {T. T  {C. hyperplane_cell A C}}"
    by (force simp: hyperplane_cellcomplex_def subset_eq)
  with finite_hyperplane_cells show ?thesis
    by (metis assms finite_Collect_subsets finite_surj)
qed

lemma finite_restrict_hyperplane_cellcomplexes:
   "finite A  finite {C. hyperplane_cellcomplex A C  P C}"
  by (simp add: finite_hyperplane_cellcomplexes)

lemma finite_set_of_hyperplane_cellcomplex:
  assumes "finite A" "C. C  𝒞  hyperplane_cellcomplex A C"
  shows "finite 𝒞"
  by (metis assms finite_hyperplane_cellcomplexes mem_Collect_eq rev_finite_subset subsetI)

lemma cell_subset_cellcomplex:
   "hyperplane_cell A C; hyperplane_cellcomplex A S  C  S  ~ disjnt C S"
  by (smt (verit) Union_iff disjnt_iff disjnt_subset1 disjoint_hyperplane_cells_eq hyperplane_cellcomplex_def subsetI)


subsection ‹Euler characteristic›


definition Euler_characteristic :: "('a::euclidean_space × real) set  'a set  int"
  where "Euler_characteristic A S 
        (C | hyperplane_cell A C  C  S. (-1) ^ nat (aff_dim C))"

lemma Euler_characteristic_empty [simp]: "Euler_characteristic A {} = 0"
  by (simp add: sum.neutral Euler_characteristic_def)

lemma Euler_characteristic_cell_Union:
  assumes "C. C  𝒞  hyperplane_cell A C"
  shows "Euler_characteristic A ( 𝒞) = (C𝒞. (- 1) ^ nat (aff_dim C))"
proof -
  have "x. hyperplane_cell A x; x   𝒞  x  𝒞"
    by (metis assms disjnt_Union1 disjnt_subset1 disjoint_hyperplane_cells_eq)
  then have "{C. hyperplane_cell A C  C   𝒞} = 𝒞"
    by (auto simp: assms)
  then show ?thesis
    by (auto simp: Euler_characteristic_def)
qed

lemma Euler_characteristic_cell:
   "hyperplane_cell A C  Euler_characteristic A C = (-1) ^ (nat(aff_dim C))"
  using Euler_characteristic_cell_Union [of "{C}"] by force

lemma Euler_characteristic_cellcomplex_Un:
  assumes "finite A" "hyperplane_cellcomplex A S" 
    and AT: "hyperplane_cellcomplex A T" and "disjnt S T"
  shows "Euler_characteristic A (S  T) =
         Euler_characteristic A S + Euler_characteristic A T"
proof -
  have *: "{C. hyperplane_cell A C  C  S  T} =
        {C. hyperplane_cell A C  C  S}  {C. hyperplane_cell A C  C  T}"
    using cell_subset_cellcomplex [OF _ AT] by (auto simp: disjnt_iff)
  have **: "{C. hyperplane_cell A C  C  S}  {C. hyperplane_cell A C  C  T} = {}"
    using assms cell_subset_cellcomplex disjnt_subset1 by fastforce
  show ?thesis
  unfolding Euler_characteristic_def
    by (simp add: finite_restrict_hyperplane_cells assms * ** flip: sum.union_disjoint)
qed

lemma Euler_characteristic_cellcomplex_Union:
  assumes "finite A" 
    and 𝒞: "C. C  𝒞  hyperplane_cellcomplex A C" "pairwise disjnt 𝒞"
  shows "Euler_characteristic A ( 𝒞) = sum (Euler_characteristic A) 𝒞"
proof -
  have "finite 𝒞"
    using assms finite_set_of_hyperplane_cellcomplex by blast
  then show ?thesis
    using 𝒞
  proof (induction rule: finite_induct)
    case empty
    then show ?case
      by auto
  next
    case (insert C 𝒞)
    then obtain "disjoint 𝒞" "disjnt C ( 𝒞)"
      by (metis disjnt_Union2 pairwise_insert)
    with insert show ?case
      by (simp add: Euler_characteristic_cellcomplex_Un hyperplane_cellcomplex_Union finite A)
  qed
qed

lemma Euler_characteristic:
  fixes A :: "('n::euclidean_space * real) set"
  assumes "finite A"
  shows "Euler_characteristic A S =
        (d = 0..DIM('n). (-1) ^ d * int (card {C. hyperplane_cell A C  C  S  aff_dim C = int d}))"
        (is "_ = ?rhs")
proof -
  have "T. hyperplane_cell A T; T  S  aff_dim T  {0..DIM('n)}"
    by (metis atLeastAtMost_iff nle_le order.strict_iff_not aff_dim_negative_iff 
        nonempty_hyperplane_cell aff_dim_le_DIM)
  then have *: "aff_dim ` {C. hyperplane_cell A C  C  S}  int ` {0..DIM('n)}"
    by (auto simp: image_int_atLeastAtMost)
  have "Euler_characteristic A  S = (yint ` {0..DIM('n)}.
       C{x. hyperplane_cell A x  x  S  aff_dim x = y}. (- 1) ^ nat y) "
    using sum.group [of "{C. hyperplane_cell A C  C  S}" "int ` {0..DIM('n)}" aff_dim "λC. (-1::int) ^ nat(aff_dim C)", symmetric]
    by (simp add: assms Euler_characteristic_def finite_restrict_hyperplane_cells *)
  also have " = ?rhs"
    by (simp add: sum.reindex mult_of_nat_commute)
  finally show ?thesis .
qed

subsection ‹Show that the characteristic is invariant w.r.t. hyperplane arrangement.›

lemma hyperplane_cells_distinct_lemma:
   "{x. a  x = b}  {x. a  x < b} = {} 
         {x. a  x = b}  {x. a  x > b} = {} 
         {x. a  x < b}  {x. a  x = b} = {} 
         {x. a  x < b}  {x. a  x > b} = {} 
         {x. a  x > b}  {x. a  x = b} = {} 
         {x. a  x > b}  {x. a  x < b} = {}"
  by auto

proposition Euler_characterstic_lemma:
  assumes "finite A" and "hyperplane_cellcomplex A S"
  shows "Euler_characteristic (insert h A) S = Euler_characteristic A S"
proof -
  obtain 𝒞 where 𝒞: "C. C  𝒞  hyperplane_cell A C" and "S = 𝒞"
              and "pairwise disjnt 𝒞"
    by (meson assms hyperplane_cellcomplex_def pairwise_disjoint_hyperplane_cells)
  obtain a b where "h = (a,b)"
    by fastforce
  have "C. C  𝒞  hyperplane_cellcomplex A C  hyperplane_cellcomplex (insert (a,b) A) C"
    by (meson 𝒞 hyperplane_cell_cellcomplex hyperplane_cellcomplex_mono subset_insertI)
  moreover
  have "sum (Euler_characteristic (insert (a,b) A)) 𝒞 = sum (Euler_characteristic A) 𝒞"
  proof (rule sum.cong [OF refl])
    fix C
    assume "C  𝒞"
    have "Euler_characteristic (insert (a, b) A) C = (-1) ^ nat(aff_dim C)"
    proof (cases "hyperplane_cell (insert (a,b) A) C")
      case True
      then show ?thesis
        using Euler_characteristic_cell by blast
    next
      case False
      with 𝒞[OF C  𝒞] have "a  0"
        by (smt (verit, ccfv_threshold) hyperplane_cell_Un hyperplane_cell_empty hyperplane_cell_singleton insert_is_Un sup_bot_left)
      have "convex C"
        using hyperplane_cell A C hyperplane_cell_convex by blast
      define r where "r  (D{C'  C |C'. hyperplane_cell {(a, b)} C'  C'  C  {}}. (-1::int) ^ nat (aff_dim D))"
      have "Euler_characteristic (insert (a, b) A) C 
           = (D | (D  {} 
                     (C1 C2. hyperplane_cell {(a, b)} C1  hyperplane_cell A C2  D = C1  C2))  D  C.
              (- 1) ^ nat (aff_dim D))"
        unfolding r_def Euler_characteristic_def insert_is_Un [of _ A] hyperplane_cell_Un ..
      also have " = r"
        unfolding r_def
        apply (rule sum.cong [OF _ refl])
        using hyperplane_cell A C disjoint_hyperplane_cells disjnt_iff
        by (smt (verit, ccfv_SIG) Collect_cong Int_iff disjoint_iff subsetD subsetI)
      also have " = (-1) ^ nat(aff_dim C)"
      proof -
        have "C  {}"
          using hyperplane_cell A C by auto
        show ?thesis
        proof (cases "C  {x. a  x < b}  C  {x. a  x > b}  C  {x. a  x = b}")
          case Csub: True
          with C  {} have "r = sum (λc. (-1) ^ nat (aff_dim c)) {C}"
            unfolding r_def
            apply (intro sum.cong [OF _ refl])
            by (auto simp: a  0 hyperplane_cell_singleton)
          also have " = (-1) ^ nat(aff_dim C)"
            by simp
          finally show ?thesis .
        next
          case False
          then obtain u v where uv: "u  C" "¬ a  u < b" "v  C" "¬ a  v > b"
            by blast
          have CInt_ne: "C  {x. a  x = b}  {}"
          proof (cases "a  u = b  a  v = b")
            case True
            with uv show ?thesis
              by blast
          next
            case False
            have "a  v < a  u"
              using False uv by auto
            define w where "w  v + ((b - a  v) / (a  u - a  v)) *R (u - v)"
            have **: "v + a *R (u - v) = (1 - a) *R v + a *R u" for a
              by (simp add: algebra_simps)
            have "w  C"
              unfolding w_def **
            proof (intro convexD_alt)
            qed (use a  v < a  u convex C uv in auto)
            moreover have "w  {x. a  x = b}"
              using a  v < a  u by (simp add: w_def inner_add_right inner_diff_right)
            ultimately show ?thesis
              by blast
          qed
          have Cab: "C  {x. a  x < b}  {}  C  {x. b < a  x}  {}"
          proof -
            obtain u v where "u  C" "a  u = b" "v  C" "a  v  b" "uv"
              using False C  {x. a  x = b}  {} by blast
            have "openin (subtopology euclidean (affine hull C)) C"
              using hyperplane_cell A C finite A hyperplane_cell_relatively_open by blast
            then obtain ε where "0 < ε"
                  and ε: "x'. x'  affine hull C; dist x' u < ε  x'  C"
              by (meson u  C openin_euclidean_subtopology_iff)
            define ξ where "ξ  u - (ε / 2 / norm (v - u)) *R (v - u)"
            have "ξ  C"
            proof (rule ε)
              show "ξ  affine hull C"
                by (simp add: ξ_def u  C v  C hull_inc mem_affine_3_minus2)
            qed (use ξ_def 0 < ε in force)
            consider "a  v < b" | "a  v > b"
              using a  v  b by linarith
            then show ?thesis
            proof cases
              case 1
              moreover have "ξ  {x. b < a  x}"
                using "1" 0 < ε a  u = b divide_less_cancel 
                by (fastforce simp: ξ_def algebra_simps)
              ultimately show ?thesis
                using v  C ξ  C by blast
            next
              case 2
              moreover have "ξ  {x. b > a  x}"
                using "2" 0 < ε a  u = b divide_less_cancel 
                by (fastforce simp: ξ_def algebra_simps)
              ultimately show ?thesis
                using v  C ξ  C by blast
            qed
          qed
          have "r = (C{{x. a  x = b}  C, {x. b < a  x}  C, {x. a  x < b}  C}.
                     (- 1) ^ nat (aff_dim C))"
            unfolding r_def 
          proof (intro sum.cong [OF _ refl] equalityI)
            show "{{x. a  x = b}  C, {x. b < a  x}  C, {x. a  x < b}  C}
                {C'  C |C'. hyperplane_cell {(a, b)} C'  C'  C  {}}"
              apply clarsimp
              using Cab Int_commute C  {x. a  x = b}  {} hyperplane_cell_singleton a  0
              by metis
          qed (auto simp: a  0 hyperplane_cell_singleton)
          also have " = (-1) ^ nat (aff_dim (C  {x. a  x = b})) 
                        + (-1) ^ nat (aff_dim (C  {x. b < a  x})) 
                        + (-1) ^ nat (aff_dim (C  {x. a  x < b}))"
            using hyperplane_cells_distinct_lemma [of a b] Cab
            by (auto simp: sum.insert_if Int_commute Int_left_commute)
          also have " = (- 1) ^ nat (aff_dim C)"
          proof -
            have *: "aff_dim (C  {x. a  x < b}) = aff_dim C  aff_dim (C  {x. a  x > b}) = aff_dim C"
              by (metis Cab open_halfspace_lt open_halfspace_gt aff_dim_affine_hull 
                        affine_hull_convex_Int_open[OF convex C])
            obtain S T where "open S" "affine T" and Ceq: "C = S  T"
              by (meson hyperplane_cell A C finite A hyperplane_cell_Int_open_affine)
            have "affine hull C = affine hull T"
              by (metis Ceq C  {} affine T open S affine_hull_affine_Int_open inf_commute)
            moreover
            have "T  ({x. a  x = b}  S)  {}"
              using Ceq C  {x. a  x = b}  {} by blast
            then have "affine hull (C  {x. a  x = b}) = affine hull (T  {x. a  x = b})"
              using affine_hull_affine_Int_open[of "T  {x. a  x = b}" S] 
              by (simp add: Ceq Int_ac affine T open S affine_Int affine_hyperplane)
            ultimately have "aff_dim (affine hull C) = aff_dim(affine hull (C  {x. a  x = b})) + 1"
              using CInt_ne False Ceq
              by (auto simp: aff_dim_affine_Int_hyperplane affine T)
            moreover have "0  aff_dim (C  {x. a  x = b})"
              by (metis CInt_ne aff_dim_negative_iff linorder_not_le)
            ultimately show ?thesis
              by (simp add: * nat_add_distrib)
          qed
          finally show ?thesis .
        qed
      qed
      finally show "Euler_characteristic (insert (a, b) A) C = (-1) ^ nat(aff_dim C)" .
    qed
    then show "Euler_characteristic (insert (a, b) A) C = (Euler_characteristic A C)"
      by (simp add: Euler_characteristic_cell 𝒞 C  𝒞)
  qed
  ultimately show ?thesis
    by (simp add: Euler_characteristic_cellcomplex_Union S =  𝒞 disjoint 𝒞 h = (a, b) assms(1))
qed


lemma Euler_characterstic_invariant_aux:
  assumes "finite B" "finite A" "hyperplane_cellcomplex A S" 
  shows "Euler_characteristic (A  B) S = Euler_characteristic A S"
  using assms
  by (induction rule: finite_induct) (auto simp: Euler_characterstic_lemma hyperplane_cellcomplex_mono)

lemma Euler_characterstic_invariant:
  assumes "finite A" "finite B" "hyperplane_cellcomplex A S" "hyperplane_cellcomplex B S"
  shows "Euler_characteristic A S = Euler_characteristic B S"
  by (metis Euler_characterstic_invariant_aux assms sup_commute)

lemma Euler_characteristic_inclusion_exclusion:
  assumes "finite A" "finite 𝒮" "K. K  𝒮  hyperplane_cellcomplex A K"
  shows "Euler_characteristic A ( 𝒮) = (𝒯 | 𝒯  𝒮  𝒯  {}. (- 1) ^ (card 𝒯 + 1) * Euler_characteristic A (𝒯))"
proof -
  interpret Incl_Excl "hyperplane_cellcomplex A" "Euler_characteristic A"
    proof
  show "Euler_characteristic A (S  T) = Euler_characteristic A S + Euler_characteristic A T"
    if "hyperplane_cellcomplex A S" and "hyperplane_cellcomplex A T" and "disjnt S T" for S T
    using that Euler_characteristic_cellcomplex_Un assms(1) by blast 
  qed (use hyperplane_cellcomplex_Int hyperplane_cellcomplex_Un hyperplane_cellcomplex_diff in auto)
  show ?thesis
    using restricted assms by blast
qed



subsection ‹Euler-type relation for full-dimensional proper polyhedral cones›

lemma Euler_polyhedral_cone:
  fixes S :: "'n::euclidean_space set"
  assumes "polyhedron S" "conic S" and intS: "interior S  {}" and "S  UNIV"
  shows "(d = 0..DIM('n). (- 1) ^ d * int (card {f. f face_of S  aff_dim f = int d})) = 0"  (is "?lhs = 0")
proof -
  have [simp]: "affine hull S = UNIV"
    by (simp add: affine_hull_nonempty_interior intS)
  with polyhedron S
  obtain H where "finite H"
    and Seq: "S = H"
    and Hex: "h. hH  a b. a0  h = {x. a  x  b}"
    and Hsub: "𝒢. 𝒢  H  S  𝒢"
    by (fastforce simp: polyhedron_Int_affine_minimal)
  have "0  S"
    using assms(2) conic_contains_0 intS interior_empty by blast
  have *: "a. a0  h = {x. a  x  0}" if "h  H" for h
  proof -
    obtain a b where "a0" and ab: "h = {x. a  x  b}"
      using Hex [OF h  H] by blast
    have "0  H"
      using Seq 0  S by force
    then have "0  h"
      using that by blast
    consider "b=0" | "b < 0" | "b > 0"
      by linarith
    then
    show ?thesis
    proof cases
      case 1
      then show ?thesis
        using a  0 ab by blast
    next
      case 2
      then show ?thesis
        using 0  h ab by auto
    next
      case 3
      have "S  (H - {h})"
        using Hsub [of "H - {h}"] that by auto
      then obtain x where x: "x  (H - {h})" and "x  S"
        by auto
      define ε where "ε  min (1/2) (b / (a  x))"
      have "b < a  x"
        using x  S ab x by (fastforce simp: S = H)
      with 3 have "0 < a  x"
        by auto
      with 3 have "0 < ε"
        by (simp add: ε_def)
      have "ε < 1"
        using ε_def by linarith
      have "ε * (a  x)  b"
        unfolding ε_def using 0 < a  x pos_le_divide_eq by fastforce
      have "x = inverse ε *R ε *R x"
        using 0 < ε by force
      moreover 
      have "ε *R x  S"
      proof -
        have  "ε *R x  h"
          by (simp add: ε * (a  x)  b ab)
        moreover have "ε *R x  (H - {h})"
        proof -
          have "ε *R x  k" if "x  k" "k  H" "k  h" for k
          proof -
            obtain a' b' where "a'0" "k = {x. a'  x  b'}"
              using Hex k  H by blast
            have "(0  a'  x  a'  ε *R x  a'  x)"
              by (metis ε < 1 inner_scaleR_right order_less_le pth_1 real_scaleR_def scaleR_right_mono)
            moreover have "(0  -(a'  x)  0  -(a'  ε *R x)) "
              using 0 < ε mult_le_0_iff order_less_imp_le by auto
            ultimately
            have "a'  x  b'  a'  ε *R x  b'"
              by (smt (verit) InterD 0  H k = {x. a'  x  b'} inner_zero_right mem_Collect_eq that(2))
            then show ?thesis
              using k = {x. a'  x  b'} x  k by fastforce
          qed
          with x show ?thesis
            by blast
        qed
        ultimately show ?thesis
          using Seq by blast
      qed
      with conic S have "inverse ε *R ε *R x  S"
        by (meson 0 < ε conic_def inverse_nonnegative_iff_nonnegative order_less_le)
      ultimately show ?thesis
        using x  S by presburger
    qed
  qed
  then obtain fa where fa: "h. h  H  fa h  0  h = {x. fa h  x  0}"
    by metis
  define fa_le_0 where "fa_le_0  λh. {x. fa h  x  0}"
  have fa': "h. h  H  fa_le_0 h = h"
    using fa fa_le_0_def by blast
  define A where "A  (λh. (fa h,0::real)) ` H"
  have "finite A"
    using finite H by (simp add: A_def)
  then have "?lhs = Euler_characteristic A S"
  proof -
    have [simp]: "card {f. f face_of S  aff_dim f = int d} = card {C. hyperplane_cell A C  C  S  aff_dim C = int d}"
      if "finite A" and "d  card (Basis::'n set)"
      for d :: nat
    proof (rule bij_betw_same_card)
      have hyper1: "hyperplane_cell A (rel_interior f)  rel_interior f  S
           aff_dim (rel_interior f) = d  closure (rel_interior f) = f" 
        if "f face_of S" "aff_dim f = d" for f
      proof -
        have 1: "closure(rel_interior f) = f" 
        proof -
          have "closure(rel_interior f) = closure f"
            by (meson convex_closure_rel_interior face_of_imp_convex that(1))
          also have " = f"
            by (meson assms(1) closure_closed face_of_polyhedron_polyhedron polyhedron_imp_closed that(1))
          finally show ?thesis .
        qed
        then have 2: "aff_dim (rel_interior f) = d"
          by (metis closure_aff_dim that(2))
        have "f  {}"
          using aff_dim_negative_iff [of f] by (simp add: that(2))
        obtain J0 where "J0  H" and J0: "f =  (fa_le_0 ` H)  (h  J0. {x. fa h  x = 0})"
        proof (cases "f = S")
          case True
          have "S =  (fa_le_0 ` H)"
            using Seq fa by (auto simp: fa_le_0_def)
          then show ?thesis
            using True that by blast
        next
          case False
          have fexp: "f = {S  {x. fa h  x = 0} | h. h  H  f  S  {x. fa h  x = 0}}"
            proof (rule face_of_polyhedron_explicit)
              show "S = affine hull S   H"
                by (simp add: Seq hull_subset inf.absorb2)
            qed (auto simp: False f  {} f face_of S finite H Hsub fa)
          show ?thesis
          proof
            have *: "x h. x  f; h  H  fa h  x  0"
              using Seq fa face_of_imp_subset f face_of S by fastforce
            show "f =  (fa_le_0 ` H)  (h  {h  H.  f  S  {x. fa h  x = 0}}. {x. fa h  x = 0})"
                 (is "f = ?I")
            proof
              show "f  ?I"
                using f face_of S fa face_of_imp_subset by (force simp: * fa_le_0_def)
              show "?I  f"
              apply (subst (2) fexp)
              apply (clarsimp simp: * fa_le_0_def)
                by (metis Inter_iff Seq fa mem_Collect_eq)
            qed
          qed blast
        qed 
        define H' where "H' = (λh. {x. -(fa h)  x  0}) ` H"
        have "J. finite J  J  H  H'  f = affine hull f  J"
        proof (intro exI conjI)
          let ?J = "H  image (λh. {x. -(fa h)  x  0}) J0"
          show "finite (?J::'n set set)"
            using J0  H finite H finite_subset by fastforce
          show "?J  H  H'"
            using J0  H by (auto simp: H'_def)
          have "f = ?J"
          proof
            show "f  ?J"
              unfolding J0 by (auto simp: fa')
            have "x j. j  J0; hH. x  h; jJ0. 0  fa j  x  fa j  x = 0"
              by (metis J0  H fa in_mono inf.absorb2 inf.orderE mem_Collect_eq)
            then show "?J  f"
              unfolding J0 by (auto simp: fa')
          qed
          then show "f = affine hull f  ?J"
            by (simp add: Int_absorb1 hull_subset)
        qed 
        then have **: "n J. finite J  card J = n  J  H  H'  f = affine hull f  J"
          by blast
        obtain J nJ where J: "finite J" "card J = nJ" "J  H  H'" and feq: "f = affine hull f  J"
          and minJ:  "m J'. finite J'; m < nJ; card J' = m; J'  H  H'  f  affine hull f  J'"
          using exists_least_iff [THEN iffD1, OF **] by metis
        have FF: "f  (affine hull f  J')" if "J'  J" for J'
        proof -
          have "f  affine hull f  J'"
            using minJ
            by (metis J finite_subset psubset_card_mono psubset_imp_subset psubset_subset_trans that)
          then show ?thesis
            by (metis Int_subset_iff Inter_Un_distrib feq hull_subset inf_sup_ord(2) psubsetI sup.absorb4 that)
        qed
        have "a. {x. a  x  0} = h  (h  H  a = fa h  (h'. h'  H  a = -(fa h')))" 
          if "h  J" for h
        proof -
          have "h  H  H'"
            using J  H  H' that by blast
          then show ?thesis
          proof
            show ?thesis if "h  H"
              using that fa by blast
          next
            assume "h  H'"
            then obtain h' where "h'  H" "h = {x. 0  fa h'  x}"
              by (auto simp: H'_def)
            then show ?thesis
              by (force simp: intro!: exI[where x="- (fa h')"])
          qed
        qed
        then obtain ga 
          where ga_h: "h. h  J  h = {x. ga h  x  0}" 
            and ga_fa: "h. h  J  h  H  ga h = fa h  (h'. h'  H  ga h = -(fa h'))" 
          by metis
        have 3: "hyperplane_cell A (rel_interior f)"
        proof -
          have D: "rel_interior f = {x  f. hJ. ga h  x < 0}"
          proof (rule rel_interior_polyhedron_explicit [OF finite J feq])
            show "ga h  0  h = {x. ga h  x  0}" if "h  J" for h
              using that fa ga_fa ga_h by force
          qed (auto simp: FF)
          have H: "h  H  ga h = fa h" if "h  J" for h
          proof -
            obtain z where z: "z  rel_interior f"
              using "1" f  {} by force
            then have "z  f  z  S"
              using D f face_of S face_of_imp_subset by blast
            then show ?thesis
              using ga_fa [OF that]
              by (smt (verit, del_insts) D InterE Seq fa inner_minus_left mem_Collect_eq that z)
          qed
          then obtain K where "K  H" 
            and K: "f =  (fa_le_0 ` H)  (h  K. {x. fa h  x = 0})"
            using J0 J0  H by blast
          have E: "rel_interior f = {x. (h  H. fa h  x  0)  (h  K. fa h  x = 0)  (h  J. ga h  x < 0)}"
            unfolding D by (simp add: K fa_le_0_def)
          have relif: "rel_interior f  {}"
            using "1" f  {} by force
          with E have "disjnt J K"
            using H disjnt_iff by fastforce
          define IFJK where "IFJK  λh. if h  J then {x. fa h  x < 0}
                         else if h  K then {x. fa h  x = 0}
                         else if rel_interior f  {x. fa h  x = 0}
                         then {x. fa h  x = 0}
                         else {x. fa h  x < 0}"
          have relint_f: "rel_interior f = (IFJK ` H)" 
          proof
            have A: False 
              if x: "x  rel_interior f" and y: "y  rel_interior f" and less0: "fa h  y < 0"
                and fa0:  "fa h  x = 0" and "h  H" "h  J" "h  K"  for x h y
            proof -
              obtain ε where "x  f" "ε>0" 
                and ε: "t. dist x t  ε; t  affine hull f  t  f"
                using x by (force simp: mem_rel_interior_cball)
              then have "y  x"
                using fa0 less0 by force
              define x' where "x'  x + (ε / norm(y - x)) *R (x - y)"
              have "x  affine hull f  y  affine hull f"
                by (metis x  f hull_inc mem_rel_interior_cball y)
              moreover have "dist x x'  ε"
                using 0 < ε y  x by (simp add: x'_def divide_simps dist_norm norm_minus_commute)
              ultimately have "x'  f"
                by (simp add: ε mem_affine_3_minus x'_def)
              have "x'  S"
                using f face_of S x'  f face_of_imp_subset by auto
              then have "x'  h"
                using Seq that(5) by blast
              then have "x'  {x. fa h  x  0}"
                using fa that(5) by blast
              moreover have "ε / norm (y - x) * -(fa h  y) > 0"
                using  0 < ε y  x less0 by (simp add: field_split_simps)
              ultimately show ?thesis
                by (simp add: x'_def fa0 inner_diff_right inner_right_distrib)
            qed
            show "rel_interior f  (IFJK ` H)"
              unfolding IFJK_def by (smt (verit, ccfv_SIG) A E H INT_I in_mono mem_Collect_eq subsetI)
            show "(IFJK ` H)  rel_interior f"
              using K  H disjnt J K
              apply (clarsimp simp add: ball_Un E H disjnt_iff IFJK_def)
              apply (smt (verit, del_insts) IntI Int_Collect subsetD)
              done
          qed
          obtain z where zrelf: "z  rel_interior f"
            using relif by blast
          moreover
          have H: "z  IFJK h  (x  IFJK h) = (hyperplane_side (fa h, 0) z = hyperplane_side (fa h, 0) x)" for h x
            using zrelf by (auto simp: IFJK_def hyperplane_side_def sgn_if split: if_split_asm)
          then have "z  (IFJK ` H)  (x  (IFJK ` H)) = hyperplane_equiv A z x" for x
            unfolding A_def Inter_iff hyperplane_equiv_def ball_simps using H by blast
          then have "x  rel_interior f  hyperplane_equiv A z x" for x
            using relint_f zrelf by presburger
          ultimately show ?thesis
            by (metis equalityI hyperplane_cell mem_Collect_eq subset_iff)
        qed
        have 4: "rel_interior f  S"
          by (meson face_of_imp_subset order_trans rel_interior_subset that(1))
        show ?thesis
          using "1" "2" "3" "4" by blast
      qed
      have hyper2: "(closure c face_of S  aff_dim (closure c) = d)  rel_interior (closure c) = c"
        if c: "hyperplane_cell A c" and "c  S" "aff_dim c = d" for c
      proof (intro conjI)
        obtain J where "J  H" and J: "c = (h  J. {x. (fa h)  x < 0})  (h  (H - J). {x. (fa h)  x = 0})"
        proof -
          obtain z where z: "c = {y. x  H.  sgn (fa x  y) = sgn (fa x  z)}"
            using c by (force simp: hyperplane_cell A_def hyperplane_equiv_def hyperplane_side_def)
          show thesis
          proof
            let ?J = "{h  H. sgn(fa h  z) = -1}"
            have 1: "fa h  x < 0"
              if "hH. sgn (fa h  x) = sgn (fa h  z)" and "h  H" and "sgn (fa h  z) = - 1" for x h
              using that by (metis sgn_1_neg)
            have 2: "sgn (fa h  z) = - 1"
              if "hH. sgn (fa h  x) = sgn (fa h  z)" and "h  H" and "fa h  x  0" for x h
            proof -
              have "0 < fa h  x; 0 < fa h  z  False"
                using that fa by (smt (verit, del_insts) Inter_iff Seq c  S mem_Collect_eq subset_iff z)
              then show ?thesis
                by (metis that sgn_if sgn_zero_iff)
            qed
            have 3: "sgn (fa h  x) = sgn (fa h  z)"
              if "h  H" and "h. h  H  sgn (fa h  z) = - 1  fa h  x < 0"
                and "hH - {h  H. sgn (fa h  z) = - 1}. fa h  x = 0"
              for x h
              using that 2 by (metis (mono_tags, lifting) Diff_iff mem_Collect_eq sgn_neg)   
            show "c = (h ?J. {x. fa h  x < 0})  (hH - ?J. {x. fa h  x = 0})"
              unfolding z by (auto intro: 1 2 3)
          qed auto
        qed
        have "finite J"
          using J  H finite H finite_subset by blast
        show "closure c face_of S"
        proof -
          have cc: "closure c = closure (hJ. {x. fa h  x < 0})  closure (hH - J. {x. fa h  x = 0})"
            unfolding J
          proof (rule closure_Int_convex)
            show "convex (hJ. {x. fa h  x < 0})"
              by (simp add: convex_INT convex_halfspace_lt)
            show "convex (hH - J. {x. fa h  x = 0})"
              by (simp add: convex_INT convex_hyperplane)
            have o1: "open (hJ. {x. fa h  x < 0})"
              by (metis open_INT[OF finite J] open_halfspace_lt)
            have o2: "openin (top_of_set (affine hull (hH - J. {x. fa h  x = 0}))) (hH - J. {x. fa h  x = 0})"
            proof -
              have "affine (hH - J. {n. fa h  n = 0})"
                using affine_hyperplane by auto
              then show ?thesis
                by (metis (no_types) affine_hull_eq openin_subtopology_self)
            qed
            show "rel_interior (hJ. {x. fa h  x < 0})  rel_interior (hH - J. {x. fa h  x = 0})  {}"
              by (metis nonempty_hyperplane_cell c rel_interior_open o1 rel_interior_openin o2 J)
          qed
          have clo_im_J: "closure ` ((λh. {x. fa h  x < 0}) ` J) = (λh. {x. fa h  x  0}) ` J"
            using J  H by (force simp: image_comp fa)
          have cleq: "closure (hH - J. {x. fa h  x = 0}) = (hH - J. {x. fa h  x = 0})"
            by (intro closure_closed) (blast intro: closed_hyperplane)
          have **: "(hJ. {x. fa h  x  0})  (hH - J. {x. fa h  x = 0}) face_of S"
            if "(hJ. {x. fa h  x < 0})  {}"
          proof (cases "J=H")
            case True
            have [simp]: "(xH. {xa. fa x  xa  0}) = H"
              using fa by auto
            show ?thesis
              using polyhedron S by (simp add: Seq True polyhedron_imp_convex face_of_refl)
          next
            case False
            have **: "(hJ. {n. fa h  n  0})  (hH - J. {x. fa h  x = 0}) =
                     (hH - J. S  {x. fa h  x = 0})"  (is "?L = ?R")
            proof
              show "?L  ?R"
                by clarsimp (smt (verit) DiffI InterI Seq fa mem_Collect_eq)
              show "?R  ?L"
                using False Seq J  H fa by blast
            qed
            show ?thesis
              unfolding **
            proof (rule face_of_Inter)
              show "(λh. S  {x. fa h  x = 0}) ` (H - J)  {}"
                using False J  H by blast
              show "T face_of S"
                if T: "T  (λh. S  {x. fa h  x = 0}) ` (H - J)" for T
              proof -
                obtain h where h: "T = S  {x. fa h  x = 0}" and "h  H" "h  J"
                  using T by auto
                have "S  {x. fa h  x = 0} face_of S"
                proof (rule face_of_Int_supporting_hyperplane_le)
                  show "convex S"
                    by (simp add: assms(1) polyhedron_imp_convex)
                  show "fa h  x  0" if "x  S" for x
                    using that Seq fa h  H by auto
                qed
                then show ?thesis
                  using h by blast
              qed
            qed
          qed
          have *: "S. S  (λh. {x. fa h  x < 0}) ` J  convex S  open S"
            using convex_halfspace_lt open_halfspace_lt by fastforce
          show ?thesis
            unfolding cc 
            apply (simp add: * closure_Inter_convex_open)
            by (metis "**" cleq clo_im_J image_image)
        qed
        show "aff_dim (closure c) = int d"
          by (simp add: that)
        show "rel_interior (closure c) = c"
          by (metis finite A c convex_rel_interior_closure hyperplane_cell_convex hyperplane_cell_relative_interior)
      qed
      have "rel_interior ` {f. f face_of S  aff_dim f = int d} 
            = {C. hyperplane_cell A C  C  S  aff_dim C = int d}"
        using hyper1 hyper2 by fastforce
      then show "bij_betw (rel_interior) {f. f face_of S  aff_dim f = int d} {C. hyperplane_cell A C  C  S  aff_dim C = int d}"
        unfolding bij_betw_def inj_on_def by (metis (mono_tags) hyper1 mem_Collect_eq) 
    qed
    show ?thesis
      by (simp add: Euler_characteristic finite A)
  qed
  also have " = 0"
  proof -
    have A: "hyperplane_cellcomplex A (- h)" if "h  H" for h
    proof (rule hyperplane_cellcomplex_mono [OF hyperplane_cell_cellcomplex])
      have "- h = {x. fa h  x = 0}  - h = {x. fa h  x < 0}  - h = {x. 0 < fa h  x}"
        by (smt (verit, ccfv_SIG) Collect_cong Collect_neg_eq fa that)
      then show "hyperplane_cell {(fa h,0)} (- h)"
        by (simp add: hyperplane_cell_singleton fa that)
      show "{(fa h,0)}  A"
        by (simp add: A_def that)
    qed
    then have "h. h  H  hyperplane_cellcomplex A h"
      using hyperplane_cellcomplex_Compl by fastforce
    then have "hyperplane_cellcomplex A S"
      by (simp add: Seq hyperplane_cellcomplex_Inter)
    then have D: "Euler_characteristic A (UNIV::'n set) =
             Euler_characteristic A (H) + Euler_characteristic A (- H)"
      using Euler_characteristic_cellcomplex_Un 
      by (metis Compl_partition Diff_cancel Diff_eq Seq finite A disjnt_def hyperplane_cellcomplex_Compl)
    have "Euler_characteristic A UNIV = Euler_characteristic {} (UNIV::'n set)"
      by (simp add: Euler_characterstic_invariant finite A)
    then have E: "Euler_characteristic A UNIV = (-1) ^ (DIM('n))"
      by (simp add: Euler_characteristic_cell)
    have DD: "Euler_characteristic A ( (uminus ` J)) = (- 1) ^ DIM('n)"
      if "J  {}" "J  H" for J
    proof -
      define B where "B  (λh. (fa h,0::real)) ` J"
      then have "B  A"
        by (simp add: A_def image_mono that)
      have "x. y = -x" if "y   (uminus ` H)" for y::'n  ― ‹Weirdly, the assumption is not used›
        by (metis add.inverse_inverse)
      moreover have "-x   (uminus ` H)  x  interior S" for x
      proof -
        have 1: "interior S = {x  S. hH. fa h  x < 0}"
          using rel_interior_polyhedron_explicit [OF finite H _ fa]
          by (metis (no_types, lifting) inf_top_left  Hsub Seq affine hull S = UNIV rel_interior_interior)
        have 2: "x y. y  H; hH. fa h  x < 0; - x  y  False" 
          by (smt (verit, best) fa inner_minus_right mem_Collect_eq)
        show ?thesis
          apply (simp add: 1)
          by (smt (verit) 2 * fa Inter_iff Seq inner_minus_right mem_Collect_eq)
      qed
      ultimately have INT_Compl_H: " (uminus ` H) = uminus ` interior S"
        by blast
      obtain z where z: "z   (uminus ` J)" 
        using J  H  (uminus ` H) = uminus ` interior S intS by fastforce
      have " (uminus ` J) = Collect (hyperplane_equiv B z)" (is "?L = ?R")
      proof
        show "?L  ?R"
          using fa J  H z 
          by (fastforce simp: hyperplane_equiv_def hyperplane_side_def B_def set_eq_iff )
        show "?R  ?L"
          using z J  H apply (clarsimp simp add: hyperplane_equiv_def hyperplane_side_def B_def)
          by (metis fa in_mono mem_Collect_eq sgn_le_0_iff)
      qed
      then have hyper_B: "hyperplane_cell B ( (uminus ` J))"
        by (metis hyperplane_cell)
      have "Euler_characteristic A ( (uminus ` J)) = Euler_characteristic B ( (uminus ` J))"
      proof (rule Euler_characterstic_invariant [OF finite A])
        show "finite B"
          using B  A finite A finite_subset by blast
        show "hyperplane_cellcomplex A ( (uminus ` J))"
          by (meson B  A hyper_B hyperplane_cell_cellcomplex hyperplane_cellcomplex_mono)
        show "hyperplane_cellcomplex B ( (uminus ` J))"
          by (simp add: hyper_B hyperplane_cell_cellcomplex)
      qed
      also have " = (- 1) ^ nat (aff_dim ( (uminus ` J)))"
        using Euler_characteristic_cell hyper_B by blast
      also have " = (- 1) ^ DIM('n)"
      proof -
        have "affine hull  (uminus ` H) = UNIV"
          by (simp add: INT_Compl_H affine_hull_nonempty_interior intS interior_negations)
        then have "affine hull  (uminus ` J) = UNIV"
          by (metis Inf_superset_mono hull_mono subset_UNIV subset_antisym subset_image_iff that(2))
        with aff_dim_eq_full show ?thesis
          by (metis nat_int)
      qed
      finally show ?thesis .
    qed
    have EE: "(𝒯 | 𝒯  uminus ` H  𝒯{}. (-1) ^ (card 𝒯 + 1) * Euler_characteristic A (𝒯))
             = (𝒯 | 𝒯  uminus ` H  𝒯  {}. (-1) ^ (card 𝒯 + 1) * (- 1) ^ DIM('n))"
      by (intro sum.cong [OF refl]) (fastforce simp: subset_image_iff intro!: DD)
    also have " = (-1) ^ DIM('n)"
    proof -
      have A: "(y = 1..card H. t{x  {𝒯. 𝒯  uminus ` H  𝒯  {}}. card x = y}. (- 1) ^ (card t + 1)) 
          = (𝒯{𝒯. 𝒯  uminus ` H  𝒯  {}}. (- 1) ^ (card 𝒯 + 1))"
      proof (rule sum.group)
        have "C. C  uminus ` H; C  {}  Suc 0  card C  card C  card H"
          by (meson finite H card_eq_0_iff finite_surj le_zero_eq not_less_eq_eq surj_card_le)
        then show "card ` {𝒯. 𝒯  uminus ` H  𝒯  {}}  {1..card H}"
          by force
      qed (auto simp: finite H)

      have "(n = Suc 0..card H. - (int (card {x. x  uminus ` H  x  {}  card x = n}) * (- 1) ^ n))
          = (n = Suc 0..card H. (-1) ^ (Suc n) * (card H choose n))"
      proof (rule sum.cong [OF refl])
        fix n
        assume "n  {Suc 0..card H}"
        then have "{𝒯. 𝒯  uminus ` H  𝒯  {}  card 𝒯 = n} = {𝒯. 𝒯  uminus ` H  card 𝒯 = n}"
          by auto
        then have "card{𝒯. 𝒯  uminus ` H  𝒯  {}  card 𝒯 = n} = card (uminus ` H) choose n"
          by (simp add: finite H n_subsets)
        also have " = card H choose n"
          by (metis card_image double_complement inj_on_inverseI)
        finally
        show "- (int (card {𝒯. 𝒯  uminus ` H  𝒯  {}  card 𝒯 = n}) * (- 1) ^ n) = (- 1) ^ Suc n * int (card H choose n)"
          by simp
      qed
      also have " = - (k = Suc 0..card H. (-1) ^ k * (card H choose k))"
        by (simp add: sum_negf)
      also have " = 1 - (k=0..card H. (-1) ^ k * (card H choose k))"
        using atLeastSucAtMost_greaterThanAtMost by (simp add: sum.head [of 0])
      also have " = 1 - 0 ^ card H"
        using binomial_ring [of "-1" "1::int" "card H"] by (simp add: mult.commute atLeast0AtMost)
      also have " = 1"
        using Seq finite H S  UNIV card_0_eq by auto
      finally have C: "(n = Suc 0..card H. - (int (card {x. x  uminus ` H  x  {}  card x = n}) * (- 1) ^ n)) = (1::int)" .

      have "(𝒯 | 𝒯  uminus ` H  𝒯  {}. (- 1) ^ (card 𝒯 + 1)) = (1::int)"
        unfolding A [symmetric] by (simp add: C)
      then show ?thesis
        by (simp flip: sum_distrib_right power_Suc)
    qed
    finally have "(𝒯 | 𝒯  uminus ` H  𝒯{}. (-1) ^ (card 𝒯 + 1) * Euler_characteristic A (𝒯))
             = (-1) ^ DIM('n)" .
    then have  "Euler_characteristic A ( (uminus ` H)) = (-1) ^ (DIM('n))"
      using Euler_characteristic_inclusion_exclusion [OF finite A]
      by (smt (verit) A Collect_cong finite H finite_imageI image_iff sum.cong)
    then show ?thesis
      using D E by (simp add: uminus_Inf Seq)
  qed
  finally show ?thesis .
qed



subsection ‹Euler-Poincare relation for special $(n-1)$-dimensional polytope›

lemma Euler_Poincare_lemma:
  fixes p :: "'n::euclidean_space set"
  assumes "DIM('n)  2" "polytope p" "i  Basis" and affp: "affine hull p = {x. x  i = 1}"
  shows "(d = 0..DIM('n) - 1. (-1) ^ d * int (card {f. f face_of p  aff_dim f = int d})) = 1"
proof -
  have "aff_dim p = aff_dim {x. i  x = 1}"
    by (metis (no_types, lifting) Collect_cong aff_dim_affine_hull affp inner_commute)
  also have "... = int (DIM('n) - 1)"
    using aff_dim_hyperplane [of i 1] i  Basis by fastforce
  finally have AP: "aff_dim p = int (DIM('n) - 1)" .
  show ?thesis
  proof (cases "p = {}")
    case True
    with AP show ?thesis by simp
  next
    case False
    define S where "S  conic hull p"
    have 1: "(conic hull f)  {x. x  i = 1} = f" if "f  {x. x  i = 1}" for f
      using that
      by (smt (verit, ccfv_threshold) affp conic_hull_Int_affine_hull hull_hull inner_zero_left mem_Collect_eq)
    obtain K where "finite K" and K: "p = convex hull K"
      by (meson assms(2) polytope_def)
    then have "convex_cone hull K = conic hull (convex hull K)"
      using False convex_cone_hull_separate_nonempty by auto
    then have "polyhedron S"
      using polyhedron_convex_cone_hull
      by (simp add: S_def polytope p polyhedron_conic_hull_polytope)
    then have "convex S"
      by (simp add: polyhedron_imp_convex)
    then have "conic S"
      by (simp add: S_def conic_conic_hull)
    then have "0  S"
      by (simp add: False S_def)
    have "S  UNIV"
    proof
      assume "S = UNIV"
      then have "conic hull p  {x. xi = 1} = p"
        by (metis "1" affp hull_subset)
      then have "bounded {x. x  i = 1}"
        using S_def S = UNIV assms(2) polytope_imp_bounded by auto
      then obtain B where "B>0" and B: "x. x  {x. x  i = 1}  norm x  B"
        using bounded_normE by blast
      define x where "x  (bBasis. (if b=i then 1 else B+1) *R b)"
      obtain j where j: "j  Basis" "ji"
        using DIM('n)  2
        by (metis DIM_complex DIM_ge_Suc0 card_2_iff' card_le_Suc0_iff_eq euclidean_space_class.finite_Basis le_antisym)
      have "B+1  ¦x  j¦"
        using j by (simp add: x_def)
      also have "  norm x"
        using Basis_le_norm j by blast
      finally have "norm x > B"
        by simp
      moreover have "x  i = 1"
        by (simp add: x_def i  Basis)
      ultimately show False
        using B by force
    qed
    have "S  {}"
      by (metis False S_def empty_subsetI equalityI hull_subset)
    have "c x. 0 < c; x  p; x  0  0 < (c *R x)  i"
      by (metis (mono_tags) Int_Collect Int_iff affp hull_inc inner_commute inner_scaleR_right mult.right_neutral)
    then have doti_gt0: "0 < x  i" if S: "x  S" and "x  0" for x
      using that by (auto simp: S_def conic_hull_explicit)
    have "a. {a} face_of S  a = 0"
      using conic S conic_contains_0 face_of_conic by blast
    moreover have "{0} face_of S"
    proof -
      have "a b u. a  S; b  S; a  b; u < 1; 0 < u; (1 - u) *R a + u *R b = 0  False"
        using conic_def euclidean_all_zero_iff inner_left_distrib scaleR_eq_0_iff
        by (smt (verit, del_insts) doti_gt0 conic S i  Basis)
      then show ?thesis
        by (auto simp: in_segment face_of_singleton extreme_point_of_def 0  S)
    qed
    ultimately have face_0: "{f. f face_of S  (a. f = {a})} = {{0}}"
      by auto
    have "interior S  {}"
    proof
      assume "interior S = {}"
      then obtain a b where "a  0" and ab: "S  {x. a  x = b}"
        by (metis convex S empty_interior_subset_hyperplane)
      have "{x. x  i = 1}  {x. a  x = b}"
        by (metis S_def ab affine_hyperplane affp hull_inc subset_eq subset_hull)
      moreover have "¬ {x. x  i = 1}  {x. a  x = b}"
        using aff_dim_hyperplane [of a b]
        by (metis AP a  0 aff_dim_eq_full_gen affine_hyperplane affp hull_subset less_le_not_le subset_hull)
      ultimately have "S  {x. x  i = 1}"
        using ab by auto
      with S  {} show False
        using conic S conic_contains_0 by fastforce
    qed
    then have "(d = 0..DIM('n). (-1) ^ d * int (card {f. f face_of S  aff_dim f = int d})) = 0"
      using Euler_polyhedral_cone S  UNIV conic S polyhedron S by blast
    then have "1 + (d = 1..DIM('n). (-1) ^ d * (card {f. f face_of S  aff_dim f = d})) = 0"
      by (simp add: sum.atLeast_Suc_atMost aff_dim_eq_0 face_0)
    moreover have "(d = 1..DIM('n). (-1) ^ d * (card {f. f face_of S  aff_dim f = d}))
                 = - (d = 0..DIM('n) - 1. (-1) ^ d * int (card {f. f face_of p  aff_dim f = int d}))"
    proof -
      have "(d = 1..DIM('n). (-1) ^ d * (card {f. f face_of S  aff_dim f = d}))
          = (d = Suc 0..Suc (DIM('n)-1). (-1) ^ d * (card {f. f face_of S  aff_dim f = d}))"
        by auto
      also have "... = - (d = 0..DIM('n) - 1. (-1) ^ d * card {f. f face_of S  aff_dim f = 1 + int d})"
        unfolding sum.atLeast_Suc_atMost_Suc_shift by (simp add: sum_negf)
      also have "... = - (d = 0..DIM('n) - 1. (-1) ^ d * card {f. f face_of p  aff_dim f = int d})"
      proof -
        { fix d
          assume "d  DIM('n) - Suc 0"
          have conic_face_p: "(conic hull f) face_of S" if "f face_of p" for f
          proof (cases "f={}")
            case False
            have "{c *R x |c x. 0  c  x  f}  {c *R x |c x. 0  c  x  p}"
              using face_of_imp_subset that by blast
            moreover
            have "convex {c *R x |c x. 0  c  x  f}"
              by (metis (no_types) cone_hull_expl convex_cone_hull face_of_imp_convex that)
            moreover
            have "(c x. ca *R a = c *R x  0  c  x  f)  (c x. cb *R b = c *R x  0  c  x  f)"
              if "ap. bp. (xf. x  open_segment a b)  a  f  b  f"
                and "0  ca" "a  p" "0  cb" "b  p"
                and "0  cx" "x  f" and oseg: "cx *R x  open_segment (ca *R a) (cb *R b)"
              for ca a cb b cx x
            proof -
              have ai: "a  i = 1" and bi: "b  i = 1"
                using affp hull_inc that(3,5) by fastforce+
              have xi: "x  i = 1"
                using affp that f face_of p face_of_imp_subset hull_subset by fastforce
              show ?thesis
              proof (cases "cx *R x = 0")
                case True
                then show ?thesis
                  using {0} face_of S face_ofD conic S that
                  by (smt (verit, best) S_def conic_def hull_subset insertCI singletonD subsetD)
              next
                case False
                then have "cx  0" "x  0"
                  by auto
                obtain u where "0 < u" "u < 1" and u: "cx *R x = (1 - u) *R (ca *R a) + u *R (cb *R b)"
                  using oseg in_segment(2) by metis
                show ?thesis
                proof (cases "x = a")
                  case True
                  then have ua: "(cx - (1 - u) * ca) *R a = (u * cb) *R b"
                    using u by (simp add: algebra_simps)
                  then have "(cx - (1 - u) * ca) * 1 = u * cb * 1"
                    by (metis ai bi inner_scaleR_left)
                  then have "a=b  cb = 0"
                    using ua 0 < u by force
                  then show ?thesis
                    by (metis True scaleR_zero_left that(2) that(4) that(7))
                next
                  case False
                  show ?thesis
                  proof (cases "x = b")
                    case True
                    then have ub: "(cx - (u * cb)) *R b = ((1 - u) * ca) *R a"
                      using u by (simp add: algebra_simps)
                    then have "(cx - (u * cb)) * 1 = ((1 - u) * ca) * 1"
                      by (metis ai bi inner_scaleR_left)
                    then have "a=b  ca = 0"
                      using u < 1 ub by auto
                    then show ?thesis
                      using False True that(4) that(7) by auto
                  next
                    case False
                    have "cx > 0"
                      using cx  0 0  cx by linarith
                    have False if "ca = 0"
                    proof -
                      have "cx = u * cb"
                        by (metis add_0 bi inner_real_def inner_scaleR_left real_inner_1_right scale_eq_0_iff that u xi)
                      then show False
                        using x  b cx  0 that u by force
                    qed
                    with 0  ca have "ca > 0"
                      by force
                    have aff: "x  affine hull p  a  affine hull p  b  affine hull p"
                      using affp xi ai bi by blast
                    show ?thesis
                    proof (cases "cb=0") 
                      case True
                      have u': "cx *R x = ((1 - u) * ca) *R a"
                        using u by (simp add: True)
                      then have "cx = ((1 - u) * ca)"
                        by (metis ai inner_scaleR_left mult.right_neutral xi)
                      then show ?thesis
                        using True u' cx  0 ca  0 x  f by auto
                    next
                      case False
                      with cb  0 have "cb > 0"
                        by linarith
                      { have False if "a=b"
                        proof -
                          have *: "cx *R x = ((1 - u) * ca + u * cb) *R b"
                            using u that by (simp add: algebra_simps)
                          then have "cx = ((1 - u) * ca + u * cb)"
                            by (metis xi bi inner_scaleR_left mult.right_neutral)
                          with x  b cx  0 * show False
                            by force
                        qed
                      }
                      moreover 
                      have "cx *R x /R cx = (((1 - u) * ca) *R a + (cb * u) *R b) /R cx"
                        using u by simp
                      then have xeq: "x = ((1-u) * ca / cx) *R a + (cb * u / cx) *R b"
                        by (simp add: cx  0 divide_inverse_commute scaleR_right_distrib)
                      then have proj: "1 = ((1-u) * ca / cx) + (cb * u / cx)"
                        using ai bi xi by (simp add: inner_left_distrib)
                      then have eq: "cx + ca * u = ca + cb * u"
                        using cx > 0 by (simp add: field_simps)
                      have "u>0. u < 1  x = (1 - u) *R a + u *R b"
                      proof (intro exI conjI)
                        show "0 < inverse cx * u * cb"
                          by (simp add: 0 < cb 0 < cx 0 < u)
                        show "inverse cx * u * cb < 1"
                          using proj 0 < ca 0 < cx u < 1 by (simp add: divide_simps)
                        show "x = (1 - inverse cx * u * cb) *R a + (inverse cx * u * cb) *R b"
                          using eq cx  0 by (simp add: xeq field_simps)
                      qed
                      ultimately show ?thesis
                        using that by (metis in_segment(2))
                    qed
                  qed
                qed
              qed
            qed
            ultimately show ?thesis
              using that by (auto simp: S_def conic_hull_explicit face_of_def)
          qed auto
          moreover
          have conic_hyperplane_eq: "conic hull (f  {x. x  i = 1}) = f"
            if "f face_of S" "0 < aff_dim f" for f
          proof
            show "conic hull (f  {x. x  i = 1})  f"
              by (metis conic S face_of_conic inf_le1 subset_hull that(1))
            have "c x'. x = c *R x'  0  c  x'  f  x'  i = 1" if "x  f" for x
            proof (cases "x=0")
              case True
              obtain y where "y  f" "y  0"
                by (metis 0 < aff_dim f aff_dim_sing aff_dim_subset insertCI linorder_not_le subset_iff)
              then have "y  i > 0"
                using f face_of S doti_gt0 face_of_imp_subset by blast
              then have "y /R (y  i)  f  (y /R (y  i))  i = 1"
                using conic S f face_of S y  f conic_def face_of_conic by fastforce
              then show ?thesis
                using True by fastforce
            next
              case False
              then have "x  i > 0"
                using f face_of S doti_gt0 face_of_imp_subset that by blast
              then have "x /R (x  i)  f  (x /R (x  i))  i = 1"
                using conic S f face_of S x  f conic_def face_of_conic by fastforce
              then show ?thesis
                by (metis 0 < x  i divideR_right eucl_less_le_not_le)
            qed
            then show "f  conic hull (f  {x. x  i = 1})"
              by (auto simp: conic_hull_explicit)
          qed

          have conic_face_S: "conic hull f face_of S" 
            if "f face_of S" for f
            by (metis conic S face_of_conic hull_same that)

          have aff_1d: "aff_dim (conic hull f) = aff_dim f + 1" (is "?lhs = ?rhs")
            if "f face_of p" and "f  {}" for f
          proof (rule order_antisym)
            have "?lhs  aff_dim(affine hull (insert 0 (affine hull f)))"
            proof (intro aff_dim_subset hull_minimal)
              show "f  affine hull insert 0 (affine hull f)"
                by (metis hull_insert hull_subset insert_subset)
              show "conic (affine hull insert 0 (affine hull f))"
                by (metis affine_hull_span_0 conic_span hull_inc insertI1)
            qed
            also have "  ?rhs"
              by (simp add: aff_dim_insert)
            finally show "?lhs  ?rhs" .
            have "aff_dim f < aff_dim (conic hull f)"
            proof (intro aff_dim_psubset psubsetI)
              show "affine hull f  affine hull (conic hull f)"
                by (simp add: hull_mono hull_subset)
              have "0  affine hull f"
                using affp face_of_imp_subset hull_mono that(1) by fastforce
              moreover have "0  affine hull (conic hull f)"
                by (simp add: f  {} hull_inc)
              ultimately show "affine hull f  affine hull (conic hull f)"
                by auto
            qed
            then show "?rhs  ?lhs"
              by simp
          qed 

          have face_S_imp_face_p: "f. f face_of S   f  {x. x  i = 1} face_of p"
            by (metis "1" S_def affp convex_affine_hull face_of_slice hull_subset)

          have conic_eq_f: "conic hull f  {x. x  i = 1} = f"
            if "f face_of p" for f
            by (metis "1" affp face_of_imp_subset hull_subset le_inf_iff that)

          have dim_f_hyperplane: "aff_dim (f  {x. x  i = 1}) = int d"
            if "f face_of S" "aff_dim f = 1 + int d" for f
          proof -
            have "conic f"
              using conic S face_of_conic that(1) by blast
            then have "0  f"
              using conic_contains_0 that by force
            moreover have "¬ f  {0}"
              using subset_singletonD that(2) by fastforce
            ultimately obtain y where y: "y  f" "y  0"
              by blast
            then have "y  i > 0"
              using doti_gt0 face_of_imp_subset that(1) by blast
            have "aff_dim (conic hull (f  {x. x  i = 1})) = aff_dim (f  {x. x  i = 1}) + 1"
            proof (rule aff_1d)
              show "f  {x. x  i = 1} face_of p"
                by (simp add: face_S_imp_face_p that(1))
              have "inverse(y  i) *R y  f"
                using 0 < y  i conic S conic_mul face_of_conic that(1) y(1) by fastforce
              moreover have "inverse(y  i) *R y  {x. x  i = 1}"
                using y  i > 0 by (simp add: field_simps)
              ultimately show "f  {x. x  i = 1}  {}"
                by blast
            qed
            then show ?thesis
              by (simp add: conic_hyperplane_eq that)
          qed
          have "card {f. f face_of S  aff_dim f = 1 + int d}
             =  card {f. f face_of p  aff_dim f = int d}"
          proof (intro bij_betw_same_card bij_betw_imageI)
            show "inj_on (λf. f  {x. x  i = 1}) {f. f face_of S  aff_dim f = 1 + int d}"
              by (smt (verit) conic_hyperplane_eq inj_on_def mem_Collect_eq of_nat_less_0_iff)     
            show "(λf. f  {x. x  i = 1}) ` {f. f face_of S  aff_dim f = 1 + int d} = {f. f face_of p  aff_dim f = int d}"
              using aff_1d conic_eq_f conic_face_p
              by (fastforce simp: image_iff face_S_imp_face_p dim_f_hyperplane)
          qed
        }
        then show ?thesis
          by force
      qed
      finally show ?thesis .
    qed
    ultimately show ?thesis
      by auto
  qed
qed


corollary Euler_poincare_special:
  fixes p :: "'n::euclidean_space set"
  assumes "2  DIM('n)" "polytope p" "i  Basis" and affp: "affine hull p = {x. x  i = 0}"
  shows "(d = 0..DIM('n) - 1. (-1) ^ d * card {f. f face_of p  aff_dim f = d}) = 1"
proof -
  { fix d
    have eq: "image((+) i) ` {f. f face_of p}  image((+) i) ` {f. aff_dim f = int d}
             = image((+) i) ` {f. f face_of p}  {f. aff_dim f = int d}"
      by (auto simp: aff_dim_translation_eq)
    have "card {f. f face_of p  aff_dim f = int d} = card (image((+) i) ` {f. f face_of p  aff_dim f = int d})"
      by (simp add: inj_on_image card_image)
    also have "  = card (image((+) i) ` {f. f face_of p}  {f. aff_dim f = int d})"
      by (simp add: Collect_conj_eq image_Int inj_on_image eq)
    also have " = card {f. f face_of (+) i ` p  aff_dim f = int d}"
      by (simp add: Collect_conj_eq faces_of_translation)
    finally have "card {f. f face_of p  aff_dim f = int d} = card {f. f face_of (+) i ` p  aff_dim f = int d}" .
  } 
  then
  have "(d = 0..DIM('n) - 1. (-1) ^ d * card {f. f face_of p  aff_dim f = d})
      = (d = 0..DIM('n) - 1. (-1) ^ d * card {f. f face_of (+) i ` p  aff_dim f = int d})"
    by simp
  also have " = 1"
  proof (rule Euler_Poincare_lemma)
    have "x. i  Basis; x  i = 1  y. y  i = 0  x = y + i"
      by (metis add_cancel_left_left eq_diff_eq inner_diff_left inner_same_Basis)
    then show "affine hull (+) i ` p = {x. x  i = 1}"
      using i  Basis unfolding affine_hull_translation affp by (auto simp: algebra_simps)
  qed (use assms polytope_translation_eq in auto)
  finally show ?thesis .
qed


subsection ‹Now Euler-Poincare for a general full-dimensional polytope›

theorem Euler_Poincare_full:
  fixes p :: "'n::euclidean_space set"
  assumes "polytope p" "aff_dim p = DIM('n)"
  shows "(d = 0..DIM('n). (-1) ^ d * (card {f. f face_of p  aff_dim f = d})) = 1"
proof -
  define augm:: "'n  'n × real" where "augm  λx. (x,0)"
  define S where "S  augm ` p"
  obtain i::'n where i: "i  Basis"
    by (meson SOME_Basis)
  have "bounded_linear augm"
    by (auto simp: augm_def bounded_linearI')
  then have "polytope S"
    unfolding S_def using polytope_linear_image polytope p bounded_linear.linear by blast
  have face_pS: "F. F face_of p  augm ` F face_of S"
    using S_def bounded_linear augm augm_def bounded_linear.linear face_of_linear_image inj_on_def by blast
  have aff_dim_eq[simp]: "aff_dim (augm ` F) = aff_dim F" for F
    using bounded_linear augm aff_dim_injective_linear_image bounded_linear.linear 
    unfolding augm_def inj_on_def by blast
  have *: "{F. F face_of S  aff_dim F = int d} = (image augm) ` {F. F face_of p  aff_dim F = int d}"
            (is "?lhs = ?rhs") for d
  proof
    have "G. G face_of S; aff_dim G = int d
          F. F face_of p  aff_dim F = int d  G = augm ` F"
      by (metis face_pS S_def aff_dim_eq face_of_imp_subset subset_imageE)
    then show "?lhs  ?rhs"
      by (auto simp: image_iff)
  qed (auto simp: image_iff face_pS)
  have ceqc: "card {f. f face_of S  aff_dim f = int d} = card {f. f face_of p  aff_dim f = int d}" for d
    unfolding *
    by (rule card_image) (auto simp: inj_on_def augm_def)
  have "(d = 0..DIM('n × real) - 1. (- 1) ^ d * int (card {f. f face_of S  aff_dim f = int d})) = 1"
  proof (rule Euler_poincare_special)
    show "2  DIM('n × real)"
      by auto
    have snd0: "(a, b)  affine hull S  b = 0" for a b
      using S_def bounded_linear augm affine_hull_linear_image augm_def by blast
    moreover have "a. (a, 0)  affine hull S"
      using S_def bounded_linear augm aff_dim_eq_full affine_hull_linear_image assms(2) augm_def by blast
    ultimately show "affine hull S = {x. x  (0::'n, 1::real) = 0}"
      by auto
  qed (auto simp: polytope S Basis_prod_def)
  then show ?thesis
    by (simp add: ceqc)
qed


text ‹In particular, the Euler relation in 3 dimensions›
corollary Euler_relation:
  fixes p :: "'n::euclidean_space set"
  assumes "polytope p" "aff_dim p = 3" "DIM('n) = 3"
  shows "(card {v. v face_of p  aff_dim v = 0} + card {f. f face_of p  aff_dim f = 2}) - card {e. e face_of p  aff_dim e = 1} = 2"
proof -
  have "x. x face_of p; aff_dim x = 3  x = p"
    using assms by (metis face_of_aff_dim_lt less_irrefl polytope_imp_convex)
  then have 3: "{f. f face_of p  aff_dim f = 3} = {p}"
    using assms by (auto simp: face_of_refl polytope_imp_convex)
  have "(d = 0..3. (-1) ^ d * int (card {f. f face_of p  aff_dim f = int d})) = 1"
    using Euler_Poincare_full [of p] assms by simp
  then show ?thesis
    by (simp add: sum.atLeast0_atMost_Suc_shift numeral_3_eq_3 3)
qed

end