Theory Polytope_More

section ‹Polytopes›

theory Polytope_More
  imports
    Convex_Euclidean_Space_More
    "HOL-Analysis.Analysis"
begin

subsection ‹Invariance under isometries›

lemma facet_of_translation_eq:
    "((+) a ` T facet_of (+) a ` S) = (T facet_of S)"
  by (simp add: aff_dim_translation_eq facet_of_def)

(* This proof is basically a copy of faces_of_translation from the Polytope
   library, only using facet_of instead. *)
lemma facets_of_translation:
  shows "{F. F facet_of (+) a ` S} = (`) ((+) a) ` {F. F facet_of S}"
proof -
  have "F. F facet_of (+) a ` S  G. G facet_of S  F = (+) a ` G"
    by (metis facet_of_imp_subset facet_of_translation_eq subset_imageE)
  then show ?thesis
    by (auto simp: image_iff facet_of_translation_eq)
qed

lemma polyhedron_translation_eq:
  shows "polyhedron S = polyhedron ((+) x ` S)"
proof -
  have translation: "S x. polyhedron S  polyhedron ((+) x ` S)"
  proof -
    fix S::"'b set" and x::"'b"
    assume "polyhedron S"
    then obtain F where F_props:
      "finite F"
      "S = F"
      "h. h  F  a b. a  0  h = {y. a  y  b}"
      unfolding polyhedron_def by force
    let ?F' = "((`) ((+) x)) ` F"
    have "finite ?F'"
      using F_props(1) by blast
    moreover have "((+) x ` S) = ?F'"
      by (metis (no_types, lifting) F_props(1,2) Inf_fin.hom_commute
          Inf_fin_Inf Inf_top_conv(2) calculation empty_iff empty_is_image
          surj_plus translation_Int)
    moreover have "h'. h'  ?F'  a b. a  0  h' = {y. a  y  b}"
    proof -
      fix h'
      assume "h'  ?F'"
      then obtain h a b where
        h'_eq: "h' = ((+) x) ` h" and
        h_props: "h  F" "a  0" "h = {y. a  y  b}"
        using F_props(3) by blast
      have "h' = {y + x | y. a  y  b}"
        using h'_eq h_props(3) by auto
      also have "... = {y. a  (y - x)  b}"
        by force
      finally have "h' = {y. a  y  b + a  x}"
        by (auto simp: inner_diff_right)
      then show "a b. a  0  h' = {y. a  y  b}"
        using h_props(2) by blast
    qed
    ultimately show "polyhedron ((+) x ` S)"
      unfolding polyhedron_def by meson
  qed
  show ?thesis
    using translation[of S x] translation[of "((+) x ` S)" "-x"]
    by (metis translation_galois)
qed

lemma face_of_isometry_eq:
  assumes "orthogonal_transformation f"
  shows "((λx. c + f x) ` T face_of (λx. c + f x) ` S)  T face_of S"
  by (metis assms face_of_translation_eq face_of_linear_image image_image
      orthogonal_transformation_inj orthogonal_transformation_linear)

lemma extreme_point_of_isometry_eq:
  assumes "orthogonal_transformation f"
  shows "(c + f T extreme_point_of (λx. c + f x) ` S)  T extreme_point_of S"
  using face_of_isometry_eq[OF assms, of c "{T}" S]
  by (simp add: face_of_singleton)

lemma aff_dim_isometry_eq:
  assumes "orthogonal_transformation f"
  shows "aff_dim S = aff_dim ((λx. c + f x) ` S)"
  by (metis assms aff_dim_injective_linear_image aff_dim_translation_eq image_image
      orthogonal_transformation_def orthogonal_transformation_inj)

subsection ‹Distinctness of facets›

lemma facets_of_polyhedron_explicit_distinct:
  fixes S :: "'a :: euclidean_space set"
  assumes finite: "finite F"
      and seq: "S = affine hull S  F"
      and faceq: "h. h  F  a h  0  h = {x. a h  x  b h}"
      and psub: "F'. F'  F  S  affine hull S  F'"
      and h1_in: "h1  F"
      and h2_in: "h2  F"
      and eq: "S  {x. a h1  x = b h1} = S  {x. a h2  x = b h2}"
    shows "h1 = h2"
proof (rule ccontr)
  assume *: "h1  h2"
  have "S  {}"
    using h1_in psub by force
  have "polyhedron S"
    by (meson polyhedron_Int_affine[of S] seq faceq finite)
  have "rel_interior S  {}"
    using rel_interior_polyhedron_explicit
    by (simp add: S  {} polyhedron S polyhedron_imp_convex
        rel_interior_eq_empty)
  then obtain z where z_props: "z  S" "h. h  F  a h  z < b h"
    using rel_interior_polyhedron_explicit[OF finite seq] faceq psub
    by blast
  have "S  affine hull S  (F - {h2})"
    using h2_in psub[of "F - {h2}"]
    by blast
  then obtain x where x_props: "x  affine hull S" "x  (F - {h2})" "x  S"
    by auto
  then have "a h2  x > b h2"
    using Diff_iff empty_iff faceq insert_iff seq
    by fastforce
  have "h. h  (F - {h2})  a h  x  b h"
    using x_props(2) faceq by auto
  have "closed_segment x z  rel_frontier S  {}"
    by (metis Diff_iff Int_iff closed_segment_subset connected_Int_rel_frontier
        connected_segment convex_affine_hull empty_iff ends_in_segment(1,2) seq
        x_props(1,3) z_props(1))
  then have "closed_segment x z  (S - rel_interior S)  {}"
    by (simp add: polyhedron S rel_boundary_of_polyhedron
        rel_frontier_of_polyhedron)
  then obtain y where y_props:
    "y  closed_segment x z"
    "y  S - rel_interior S"
    "y  S"
    by blast
  have "S - rel_interior S = {S  {x. a h  x = b h} | h. h  F}"
    using facet_of_polyhedron_explicit[OF finite seq faceq psub]
      rel_boundary_of_polyhedron[OF polyhedron S]
    by auto
  then obtain h where h_props: "h  F" "a h  y = b h"
    using y_props(2) by auto
  have "k. k  F  k  h2  a k  y = b k"
  proof (cases "h = h2")
    case True
    then have "y  S  {x. a h2  x = b h2}"
      using h_props(2) y_props(3) by blast
    then have "a h1  y = b h1"
      using eq by blast
    then show ?thesis
      using h1_in * by blast
  next
    case False
    then show ?thesis
      using h_props by blast 
  qed
  then obtain k where k_props: "k  F" "k  h2" "a k  y = b k"
    by blast
  have "y  open_segment x z"
    by (metis DiffI k. k  F  k  h2  a k  y = b k
        all_not_in_conv insertE open_segment_def order_less_irrefl
        x_props(3) y_props(1,3) z_props(2))
  then obtain u where u_props: "y = (1 - u) *R x + u *R z" "0 < u" "u < 1"
    using in_segment(2)[of y x z] by blast
  have "(1 - u) * (a k  x) + u * (a k  z) = b k"
    using k_props(3) unfolding u_props(1)
    by (simp add: inner_right_distrib)
  moreover have "(1 - u) * (a k  x)  (1 - u) * b k"
    by (simp add: ha. ha  F - {h2}  a ha  x  b ha k_props(1,2)
        u_props(3))
  moreover have "u * (a k  z) < u * b k"
    by (simp add: k_props(1) u_props(2) z_props(2))
  ultimately show "False"
    by argo
qed

subsection ‹Ridges lie in two facets›

lemma polyhedron_ridge_two_facets_0:
  fixes p :: "'a :: euclidean_space set"
  assumes "polyhedron p"
    and "r face_of p"
    and "0  r"
    and "aff_dim r = aff_dim p - 2"
  shows "f1 f2. f1 face_of p  aff_dim f1 = aff_dim p - 1 
                 f2 face_of p  aff_dim f2 = aff_dim p - 1 
                 f1  f2  r  f1  r  f2  f1  f2 = r 
                 (f. f face_of p  aff_dim f = aff_dim p - 1  r  f
                       f = f1  f = f2)"
proof -
  let ?S = "{f. f face_of p  aff_dim f = aff_dim p - 1  r  f}"
  have "finite ?S"
    by (simp add: assms(1) finite_polyhedron_faces)
  have "r = {f. f facet_of p  r  f}"
    using face_of_polyhedron[of p r] assms by fastforce
  then have r_eq: "r = ?S"
    using assms(3) facet_of_def by blast
  have "0  p"
    using assms(2,3) face_of_imp_subset by auto

  have "card ?S = 0  card ?S = 1  card ?S = 2  card ?S  3"
    by linarith
  then consider
    (card0) "card ?S = 0" |
    (card1) "card ?S = 1" |
    (card2) "card ?S = 2" |
    (card3) "card ?S  3"
    by argo

  then show ?thesis
  proof (cases)
    case card0
    then show ?thesis
      by (smt (verit, best) Inf_empty finite ?S aff_dim_UNIV
          aff_dim_le_DIM assms(4) card_gt_0_iff nless_le r_eq)

  next
    case card1
    then obtain f1 where
      S_eq: "?S = {f1}" and
      aff_dim_f1: "aff_dim f1 = aff_dim p - 1"
      by (metis (mono_tags, lifting) all_not_in_conv card_1_singletonE
          insert_not_empty mem_Collect_eq singletonD)
    show ?thesis
      using r_eq aff_dim_f1 assms(4) unfolding S_eq
      by simp

  next
    case card2
    then obtain f1 f2 where
      S_eq: "?S = {f1, f2}" and
      "f1 face_of p" "aff_dim f1 = aff_dim p - 1" "r  f1" and
      "f2 face_of p" "aff_dim f2 = aff_dim p - 1" "r  f2" and
      "f1  f2"
      by (smt (verit) card_2_iff insert_iff mem_Collect_eq)
    moreover have "f1  f2 = r"
      using S_eq r_eq by auto
    moreover have "(f. f face_of p  aff_dim f = aff_dim p - 1  r  f
                       f = f1  f = f2)"
      using S_eq by auto
    ultimately show ?thesis
      by blast

  next
    case card3
    then have "card ?S  Suc (Suc (Suc 0))"
      by presburger
    then obtain f1 f2 f3 S2 where
      S_eq: "?S = insert f3 (insert f2 (insert f1 S2))" and
      fs_neq: "f1  f2" "f1  f3" "f2  f3"
      by (smt (verit) card_le_Suc_iff insertCI)
    have f1_props: "f1 face_of p" "aff_dim f1 = aff_dim p - 1" "r  f1" and
         f2_props: "f2 face_of p" "aff_dim f2 = aff_dim p - 1" "r  f2" and
         f3_props: "f3 face_of p" "aff_dim f3 = aff_dim p - 1" "r  f3"
      using S_eq
      by auto

    obtain F a b where
      "finite F" and
      p_eq: "p = affine hull p  F" and
      halfspaces: "h. hF  a h  0  h = {x. a h  x  b h} 
               (x  affine hull p. x + a h  affine hull p)" and
      minimal: "F'. F'  F  p  affine hull p  F'"
      using polyhedron_Int_affine_parallel_minimal[of p] assms(1)
      by metis
    obtain h1 where "h1  F" "a h1  0" and f1_eq: "f1 = p  {x. a h1  x = b h1}"
      using facet_of_polyhedron_explicit[OF finite F p_eq, of a b f1]
        halfspaces minimal f1_props unfolding facet_of_def
      using assms(3) by blast
    obtain h2 where "h2  F" "a h2  0" and f2_eq: "f2 = p  {x. a h2  x = b h2}"
      using facet_of_polyhedron_explicit[OF finite F p_eq, of a b f2]
        halfspaces minimal f2_props unfolding facet_of_def
      using assms(3) by blast
    obtain h3 where "h3  F" "a h3  0" and f3_eq: "f3 = p  {x. a h3  x = b h3}"
      using facet_of_polyhedron_explicit[OF finite F p_eq, of a b f3]
        halfspaces minimal f3_props unfolding facet_of_def
      using assms(3) by blast

    have all_x_in_r: "x. x  r  a h1  x = b h1  a h2  x = b h2  a h3  x = b h3"
      using f1_props(3) f2_props(3) f3_props(3) f1_eq f2_eq f3_eq
      by blast
    then have b_hs_eq_0: "b h1 = 0" "b h2 = 0" "b h3 = 0"
      using 0  r by force+
    then have all_x_in_r_0: "x. x  r  a h1  x = 0  a h2  x = 0  a h3  x = 0"
      using all_x_in_r by auto
    have a_hs_neq: "a h1  a h2" "a h1  a h3" "a h2  a h3"
      using b_hs_eq_0 f1_eq f2_eq f3_eq fs_neq by force+
    have hs_neq: "h1  h2" "h1  h3" "h2  h3"
      using fs_neq f1_eq f2_eq f3_eq by auto

    have "aff_dim r = dim r"
      using aff_dim_zero[OF hull_inc[OF 0  r]] .
    moreover have "aff_dim p = dim p"
      using aff_dim_zero[OF hull_inc[OF 0  p]] .
    ultimately have "dim r = dim p - 2"
      using assms(4) by presburger

    have not_contained_xy_z:
      "¬ ({x. a hx  x  0}  {x. a hy  x  0}  {x. a hz  x  0})"
      if "hx  F" "hy  F" "hz  F" "b hx = 0" "b hy = 0" "b hz = 0" "hx  hz" "hy  hz" for hx hy hz
    proof
      assume "{x. a hx  x  0}  {x. a hy  x  0}  {x. a hz  x  0}"
      then have *: "hx  hy  hz"
        using that halfspaces
        by metis
      have "F - {hz}  F"
        using hz  F finite F by blast
      then have p_psubset: "p  affine hull p  (F - {hz})"
        using minimal by presburger
      have "hx  F - {hz}" "hy  F - {hz}"
        using that by auto
      then show "False"
        using * p_eq p_psubset by blast
    qed
    have not_contained_12_3:
      "¬ ({x. a h1  x  0}  {x. a h2  x  0}  {x. a h3  x  0})"
      using not_contained_xy_z[OF h1  F h2  F h3  F] b_hs_eq_0 hs_neq
      by blast
    have not_contained_13_2:
      "¬ ({x. a h1  x  0}  {x. a h3  x  0}  {x. a h2  x  0})"
      using not_contained_xy_z[OF h1  F h3  F h2  F] b_hs_eq_0 hs_neq
      by blast
    have not_contained_23_1:
      "¬ ({x. a h2  x  0}  {x. a h3  x  0}  {x. a h1  x  0})"
      using not_contained_xy_z[OF h2  F h3  F h1  F] b_hs_eq_0 hs_neq
      by blast

    obtain w where "w  rel_interior p"
      using 0  p polyhedron_imp_convex rel_interior_eq_empty assms(1)
      by auto
    moreover have "rel_interior p = {x  p. hF. a h  x < b h}"
      using rel_interior_polyhedron_explicit[OF finite F p_eq, of a b]
        halfspaces minimal
      by blast
    ultimately have w_props: "a h1  w < 0" "a h2  w < 0" "a h3  w < 0"
      using b_hs_eq_0 h1  F h2  F h3  F
      by auto

    have "r  span p"
      using face_of_imp_subset[OF assms(2)] span_superset[of p]
      by blast
    have "a h1  span p" "a h2  span p" "a h3  span p"
      using halfspaces h1  F h2  F h3  F 0  p
      by (metis add_0 affine_hull_span_0 hull_inc)+
    then have "span {a h1, a h2, a h3}  span p"
      using subspace_span[of p]
      by (simp add: span_minimal)
    then have "r  span {a h1, a h2, a h3}  span p"
      using r  span p subspace_span[of p]
      by (simp add: subspace_sum_minimal)
    have "dim (span p) = dim p"
      by simp
    have "(x y. x  r  y  span {a h1, a h2, a h3}  x  y = 0)"
    proof -
      fix x y
      assume x_in_r: "x  r"
      assume "y  span {a h1, a h2, a h3}"
      then show "x  y = 0"
      proof (induct y rule: span_induct)
        case base
        then show ?case
          using subspace_hyperplane by blast
      next
        case (step y)
        then show ?case
          using all_x_in_r_0[OF x_in_r]
          by (metis inner_commute empty_iff insertE)
      qed
    qed
    then have "dim (r  span {a h1, a h2, a h3}) = dim r + dim (span {a h1, a h2, a h3})"
      using dim_orthogonal_sum[of r "span {a h1, a h2, a h3}"]
      by blast
    moreover have "dim (r  span {a h1, a h2, a h3})  dim p"
      using dim_subset[OF r  span {a h1, a h2, a h3}  span p]
      by auto
    ultimately have "dim (span {a h1, a h2, a h3})  2"
      using dim r = dim p - 2
      by linarith

    have "independent {a h1, a h2, a h3}"
    proof
      assume "dependent {a h1, a h2, a h3}"
      have "finite {a h1, a h2, a h3}" by simp
      obtain c where c_sum: "(v{a h1, a h2, a h3}. c v *R v) = 0" 
                 and nonzero: "v{a h1, a h2, a h3}. c v  0"
        using dependent {a h1, a h2, a h3} dependent_finite[OF finite {a h1, a h2, a h3}] 
        by auto

      let ?c1 = "c (a h1)" and ?c2 = "c (a h2)" and ?c3 = "c (a h3)"
      have sum_0: "?c1 *R a h1 + ?c2 *R a h2 + ?c3 *R a h3 = 0"
        using c_sum a_hs_neq
        by (simp add: group_cancel.add1)
      have "?c1  0"
      proof 
        assume "?c1 = 0"
        then have "?c2 *R a h2 + ?c3 *R a h3 = 0" using sum_0 by simp
        then have "?c2  0" "?c3  0" 
          using nonzero ?c1 = 0
          using a h2  0 a h3  0 by auto

        define k where "k = - (?c3 / ?c2)"
        have "a h2 = k *R a h3"
          using ?c2 *R a h2 + ?c3 *R a h3 = 0 ?c2  0 
          unfolding k_def
          by (smt (verit, ccfv_SIG) eq_vector_fraction_iff neg_eq_iff_add_eq_0 scaleR_left.minus scaleR_minus_right) 

        show False
        proof (cases "k > 0")
          case True
          then have "{x. a h3  x  0} = {x. a h2  x  0}"
            unfolding a h2 = k *R a h3 by (auto simp: mult_le_0_iff)
          then have "{x. a h1  x  0}  {x. a h2  x  0}  {x. a h3  x  0}"
            by blast
          then show False using not_contained_12_3 by simp
        next
          case False
          then have "k < 0" 
            using a h2 = k *R a h3 a_hs_neq(3) a h2  0 by fastforce
          have "a h2  w = k * (a h3  w)" using a h2 = k *R a h3 a h2  0 by auto
          moreover have "a h2  w < 0" using w_props(2) by simp
          moreover have "k * (a h3  w) > 0" using k < 0 w_props(3) mult_neg_neg by blast
          ultimately show False by simp
        qed
      qed

      define u where "u = -(?c2 / ?c1)"
      define v where "v = -(?c3 / ?c1)"
      have "?c1 *R a h1 = -?c2 *R a h2 - ?c3 *R a h3"
        by (metis (no_types, lifting) sum_0 add.left_commute add.right_neutral add_minus_cancel scaleR_left.minus
            uminus_add_conv_diff)
      then have "(1/?c1) *R (?c1 *R a h1) = (1/?c1) *R (-?c2 *R a h2 - ?c3 *R a h3)"
        by auto
      then have a1_eq: "a h1 = u *R a h2 + v *R a h3"
        using ?c1  0 unfolding u_def v_def 
        by (simp add: scaleR_right_diff_distrib)

      have "u  0"
      proof
        assume "u = 0"
        then have "a h1 = v *R a h3"
          using a1_eq by simp
        show False
        proof (cases "v > 0")
          case True
          then have "{x. a h3  x  0} = {x. a h1  x  0}"
            unfolding a h1 = v *R a h3
            by (auto simp: mult_le_0_iff)
          then have "{x. a h2  x  0}  {x. a h3  x  0}  {x. a h1  x  0}"
            by blast
          then show False
            using not_contained_23_1
            by simp
        next
          case False
          then have "v < 0" 
            using a h1 = v *R a h3 a_hs_neq(2) a h1  0
            by auto
          have "a h1  w = v * (a h3  w)"
            using a h1 = v *R a h3
            by simp
          moreover have "a h1  w < 0"
            using w_props(1) by simp
          moreover have "v * (a h3  w) > 0"
            using v < 0 w_props(3) mult_neg_neg
            by blast
          ultimately show False by simp
        qed
      qed

      have "v  0"
      proof
        assume "v = 0"
        then have "a h1 = u *R a h2"
          using a1_eq by simp
        show False
        proof (cases "u > 0")
          case True
          then have "{x. a h2  x  0} = {x. a h1  x  0}"
            unfolding a h1 = u *R a h2
            by (auto simp: mult_le_0_iff)
          then have "{x. a h2  x  0}  {x. a h3  x  0}  {x. a h1  x  0}"
            by blast
          then show False
            using not_contained_23_1
            by simp
        next
          case False
          then have "u < 0" 
            using a h1 = u *R a h2 a_hs_neq(1) a h1  0
            by auto
          have "a h1  w = u * (a h2  w)"
            using a h1 = u *R a h2
            by simp
          moreover have "a h1  w < 0"
            using w_props(1) by simp
          moreover have "u * (a h2  w) > 0"
            using u < 0 w_props(2) mult_neg_neg
            by blast
          ultimately show False by simp
        qed
      qed

      consider (pos_pos) "u > 0" "v > 0" | 
               (pos_neg) "u > 0" "v < 0" | 
               (neg_pos) "u < 0" "v > 0" | 
               (neg_neg) "u < 0" "v < 0"
        using u  0 v  0 by linarith
      then show "False"
      proof cases
        case pos_pos
        have "{x. a h2  x  0}  {x. a h3  x  0}  {x. a h1  x  0}"
        proof
          fix x
          assume x_in: "x  {x. a h2  x  0}  {x. a h3  x  0}"
          have "a h1  x = u * (a h2  x) + v * (a h3  x)"
            using a1_eq by (simp add: inner_add)
          also have "...  0"
            using x_in pos_pos
            by (simp add: add_decreasing2 split_mult_neg_le)
          finally show "x  {x. a h1  x  0}"
            by blast
        qed
        then show False
          using not_contained_23_1 by simp
      next
        case pos_neg
        have "u *R a h2 = a h1 + (-v) *R a h3" 
          using a1_eq
          by (simp add: algebra_simps)
        have "{x. a h1  x  0}  {x. a h3  x  0}  {x. a h2  x  0}"
        proof
          fix x assume x_in: "x  {x. a h1  x  0}  {x. a h3  x  0}"
          have "u * (a h2  x) = (a h1  x) + (-v) * (a h3  x)"
            using u *R a h2 = a h1 + (-v) *R a h3
            by (metis inner_left_distrib inner_scaleR_left)
          also have "...  0"
            using x_in pos_neg
            by (smt (verit, best) IntE mem_Collect_eq mult_le_0_iff) 
          finally have "u * (a h2  x)  0" .
          then show "x  {x. a h2  x  0}"
            using u > 0
            by (simp add: mult_le_0_iff)
        qed
        then show False
          using not_contained_13_2
          by simp
      next
        case neg_pos
        have "v *R a h3 = a h1 + (-u) *R a h2" 
          using a1_eq
          by (simp add: algebra_simps)
        have "{x. a h1  x  0}  {x. a h2  x  0}  {x. a h3  x  0}"
        proof
          fix x
          assume x_in: "x  {x. a h1  x  0}  {x. a h2  x  0}"
          have "v * (a h3  x) = (a h1  x) + (-u) * (a h2  x)"
            using v *R a h3 = a h1 + (-u) *R a h2
            by (metis inner_left_distrib inner_scaleR_left)
          also have "...  0"
            using x_in neg_pos
            by (smt (verit, best) IntE mem_Collect_eq mult_le_0_iff)
          finally show "x  {x. a h3  x  0}"
            using v > 0
            by (simp add: mult_le_0_iff)
        qed
        then show False
          using not_contained_12_3
          by simp
      next
        case neg_neg
        have "a h1  w = u * (a h2  w) + v * (a h3  w)"
          using a1_eq
          by (simp add: inner_add)
        moreover have "u * (a h2  w) > 0" "v * (a h3  w) > 0"
          using neg_neg w_props(2,3) mult_neg_neg
          by blast+
        ultimately have "a h1  w > 0"
          by simp
        then show False
          using w_props(1) by simp
      qed
    qed

    then have "dim (span {a h1, a h2, a h3}) = 3"
      using a_hs_neq(1,2,3) dim_span_eq_card_independent
      by fastforce
    then show ?thesis
      using dim (span {a h1, a h2, a h3})  2
      by linarith
  qed
qed

lemma polyhedron_ridge_two_facets:
  fixes p :: "'a :: euclidean_space set"
  assumes "polyhedron p"
    and "r face_of p"
    and "r  {}"
    and "aff_dim r = aff_dim p - 2"
  shows "f1 f2. f1 face_of p  aff_dim f1 = aff_dim p - 1 
                 f2 face_of p  aff_dim f2 = aff_dim p - 1 
                 f1  f2  r  f1  r  f2  r = f1  f2 
                 (f. f face_of p  aff_dim f = aff_dim p - 1  r  f
                       f = f1  f = f2)"
proof -
  obtain z where "z  r"
    using assms(3) by blast
  then have "z  p"
    using assms(2) face_of_imp_subset by auto
  let ?r' = "((+) (-z)) ` r"
  let ?p' = "((+) (-z)) ` p"
  have origin_r': "0  ?r'"
    using z  r by simp
  have origin_p': "0  ?p'"
    using z  p by simp
  have polyhedron: "polyhedron ?p'"
    using assms(1) polyhedron_translation_eq[of p "-z"] by blast
  have face_of: "?r' face_of ?p'"
    using assms(2) face_of_translation_eq by blast
  have nonempty: "?r'  {}"
    using assms(3) by blast
  have aff_dims: "aff_dim ?r' = aff_dim ?p' - 2"
    by (metis aff_dim_translation_eq assms(4))
  obtain f1 f2 where
    f1_face_of: "f1 face_of ?p'" and
    aff_dim_f1: "aff_dim f1 = aff_dim ?p' - 1" and
    f2_face_of: "f2 face_of ?p'" and
    aff_dim_f2: "aff_dim f2 = aff_dim ?p' - 1" and
    fs_neq: "f1  f2" and
    sub_f1: "?r'  f1" and
    sub_f2: "?r'  f2" and
    fs_inter_eq: "f1  f2 = ?r'" and
    only_fs: "(f. f face_of ?p' 
                      aff_dim f = aff_dim ?p' - 1  ?r'  f 
                      f = f1  f = f2)"
    using polyhedron_ridge_two_facets_0[OF polyhedron face_of origin_r' aff_dims]
    by auto
  let ?f1' = "((+) z) ` f1"
  let ?f2' = "((+) z) ` f2"
  have "?f1' face_of p"
    by (metis f1_face_of face_of_translation_eq translation_galois)
  moreover have "aff_dim ?f1' = aff_dim p - 1"
    by (metis aff_dim_f1 aff_dim_translation_eq)
  moreover have "?f2' face_of p"
    by (metis f2_face_of face_of_translation_eq translation_galois)
  moreover have "aff_dim ?f2' = aff_dim p - 1"
    by (metis aff_dim_f2 aff_dim_translation_eq)
  moreover have "?f1'  ?f2'"
    using fs_neq translation_invert
    by auto
  moreover have "r  ?f1'"
    by (metis sub_f1 subset_image_iff translation_galois)
  moreover have "r  ?f2'"
    by (metis sub_f2 subset_image_iff translation_galois)
  moreover have "?f1'  ?f2' = r"
    by (metis fs_inter_eq translation_Int translation_galois)
  moreover have "(f. f face_of p 
                      aff_dim f = aff_dim p - 1  r  f 
                      f = ?f1'  f = ?f2')"
    by (smt (verit, best) aff_dim_translation_eq face_of_translation_eq image_mono
        only_fs translation_galois)
  ultimately show ?thesis
    by blast
qed

subsection ‹Lower bounds on face counts›

subsubsection ‹Vertices›

lemma polytope_vertex_lower_bound:
  assumes "polytope p"
  shows "aff_dim p + 1  card {v. v face_of p  aff_dim v = 0}"
proof -
  let ?V = "{v. v extreme_point_of p}"
  have sets_eq: "{v. v face_of p  aff_dim v = 0} = (λv. {v}) ` ?V"
    using face_of_singleton aff_dim_sing aff_dim_eq_0
    by auto
  then have cards_eq: "card {v. v face_of p  aff_dim v = 0} = card ?V"
    unfolding sets_eq using card_image inj_singleton
    by blast
  have "aff_dim p + 1  aff_dim (convex hull ?V) + 1"
    using Krein_Milman_Minkowski[OF polytope_imp_compact[OF assms] polytope_imp_convex[OF assms]]
    by simp
  moreover have "aff_dim (convex hull ?V) + 1  card ?V"
    using finite_polyhedron_extreme_points[OF polytope_imp_polyhedron[OF assms]]
      aff_dim_le_card[of ?V] aff_dim_convex_hull[of ?V]
    by simp
  ultimately show ?thesis
    unfolding cards_eq by auto
qed

subsubsection‹Facets›

lemma polytope_facet_lower_bound_0:
  assumes "polytope p"
  assumes "aff_dim p  0"
  assumes origin_in_p: "0  p"
  shows "aff_dim p + 1  card {f. f facet_of p}"
proof -
  have aff_dim_eq_dim: "aff_dim p = dim p"
    using origin_in_p aff_dim_zero[of p]
    by (simp add: hull_inc)

  obtain H a b where
    "finite H" and
    p_eq: "p = affine hull p  H" and
    halfspaces: "h. hH  a h  0  h = {x. a h  x  b h} 
             (x  affine hull p. x + a h  affine hull p)" and
    minimal: "F'. F'  H  p  affine hull p  F'"
    using polyhedron_Int_affine_parallel_minimal[of p]
      polytope_imp_polyhedron[OF assms(1)]
    by metis
  have p_eq_span: "p = span p  H"
    using p_eq 0  p affine_hull_span_0
    by blast
  have "h. hH  0h"
    using 0  p p_eq
    by blast
  then have b_nonneg: "h. hH  0  b h"
    by (metis halfspaces inner_zero_right mem_Collect_eq)

  have ineq1: "dim p + 1  card H"
  proof (rule ccontr)
    assume "¬(dim p + 1  card H)"
    then have *: "card H  dim p"
      by auto
    have "H  {}"
      by (smt (verit, ccfv_threshold) IntE Int_lower2 affine_affine_hull
          affine_bounded_eq_lowdim closure_Inter_convex_open
          convex_rel_interior_finite_Inter emptyE finite.intros(1) image_is_empty
          inf.orderE local.aff_dim_eq_dim of_nat_0_le_iff origin_in_p p_eq
          polytope_eq_bounded_polyhedron rel_interior_eq_closure subset_hull
          assms(1,2))
    then obtain h where "hH"
      by blast
    have "h'H. a h'  span p"
      by (metis add_0 affine_hull_span_0 halfspaces hull_inc origin_in_p)
    then have "span (a ` (H - {h}))  span p"
      using span_mono[of "a ` (H - {h})" "span p"] span_span[of p]
      by blast
    moreover have "span (a ` (H - {h}))  span p"
    proof
      assume "span (a ` (H - {h})) = span p"
      then have "dim (span p) = dim (span (a ` (H - {h})))"
        by argo
      also have "...  card (a ` (H - {h}))"
        by (simp add: finite H dim_le_card')
      finally show "False"
        by (metis (no_types, lifting) "*" finite H h  H card_Diff1_less_iff
            card_image_le dim_span finite_Diff linorder_not_le order_trans)
    qed
    ultimately have span_psubset: "span (a ` (H - {h}))  span p"
      by blast
    obtain n where
      "n  0"
      "n  span p" and
      orthogonal: "x. x  span (a ` (H - {h}))  orthogonal n x"
      using orthogonal_to_subspace_exists_gen[OF span_psubset]
      by blast
    obtain B where bounded: "x. xp  norm x  B"
      using polytope_imp_bounded[OF assms(1)] bounded_pos[of p]
      by blast
    {assume "a h  n  0"
      let ?x = "-((B + 1) / (norm n)) *R n"
      have "?x  span p"
        using n  span p span_mul
        by blast
      moreover have "k. kH  (a k)  ?x  (b k)"
      proof -
        fix k
        assume "kH"
        {assume "k = h"
          have "a h  ?x  b h"
            using 0  a h  n h  H b_nonneg bounded divide_nonneg_nonneg
              inner_simps(6) origin_in_p real_0_le_add_iff
            by fastforce
        } moreover {assume "k  h"
          have "(a k)  ?x  (b k)"
            by (smt (verit, best) k  H k  h b_nonneg image_eqI inner_commute
                inner_simps(6) insert_Diff_single insert_absorb insert_iff mult_eq_0_iff
                orthogonal orthogonal_def span_superset subsetD)
        } ultimately show "a k  ?x  b k"
          by blast
      qed
      ultimately have "?x  p"
        using p_eq_span halfspaces
        by blast
      have "norm ?x > B"
        by (simp add: n  0)
      then have "False"
        using bounded[OF ?x  p]
        by linarith
    } moreover {assume "a h  n  0"
      let ?x = "((B + 1) / (norm n)) *R n"
      have "?x  span p"
        using n  span p span_mul
        by blast
      moreover have "k. kH  a k  ?x  b k"
      proof -
        fix k
        assume "kH"
        {assume "k = h"
          have "a h  ?x  b h"
            by (smt (verit, best) a h  n  0 h  H b_nonneg bounded inner_simps(6)
                norm_ge_zero norm_zero origin_in_p real_scaleR_def split_scaleR_neg_le
                zero_le_divide_iff zero_less_one_class.zero_le_one)
        } moreover {assume "k  h"
          have "(a k)  ?x  (b k)"
            by (smt (verit, best) k  H k  h b_nonneg image_eqI inner_commute
                inner_simps(6) insert_Diff_single insert_absorb insert_iff mult_eq_0_iff
                orthogonal orthogonal_def span_superset subsetD)
        } ultimately show "a k  ?x  b k"
          by blast
      qed
      ultimately have "?x  p"
        using p_eq_span halfspaces
        by blast
      have "norm ?x > B"
        by (simp add: n  0)
      then have "False"
        using bounded[OF ?x  p]
        by linarith
    } ultimately show "False"
      by linarith
  qed

  have ineq2: "card H = card {f. f facet_of p}"
  proof -
    let ?g = "λh. p  {x. a h  x = b h}"
    have halfspaces_simple: "h'. h'  H  a h'  0  h' = {x. a h'  x  b h'}"
      using halfspaces by blast
    have "{f. f facet_of p} = ?g ` H"
      using facet_of_polyhedron_explicit[OF finite H p_eq halfspaces_simple] minimal
      by auto
    moreover have "inj_on ?g H"
      using facets_of_polyhedron_explicit_distinct[OF finite H p_eq halfspaces_simple]
      by (auto simp: minimal inj_on_def)
    ultimately show ?thesis
      using card_image by fastforce
  qed

  show ?thesis
    unfolding aff_dim_eq_dim
    using ineq1 ineq2
    by linarith
qed

lemma polytope_facet_lower_bound:
  assumes "polytope p"
  assumes "aff_dim p  0"
  shows "aff_dim p + 1  card {f. f facet_of p}"
proof (cases "p = {}")
  case True
  then show ?thesis
    using aff_dim_empty[of p] facet_of_empty
    by simp
next
  case False
  then obtain z where "z  p"
    by blast
  have has_origin: "0  ((+) (-z) ` p)"
    using z  p by simp
  have is_polytope: "polytope ((+) (-z) ` p)"
    using polytope_translation_eq[of "-z" p] assms(1)
    by blast
  have nonzero_aff_dim: "aff_dim ((+) (-z) ` p)  0"
    using aff_dim_translation_eq[of "-z" p] assms(2)
    by argo
  have h1: "card {f. f facet_of (+) (- z) ` p} = card ((`) ((+) (- z)) ` {f. f facet_of p})"
    using facets_of_translation[of "-z" p]
    by argo
  moreover have "inj_on ((`) ((+) (- z))) {f. f facet_of p}"
      by (simp add: inj_on_image)
  ultimately have "card {f. f facet_of (+) (- z) ` p} = card {f. f facet_of p}"
    by (metis card_image)
  then show ?thesis
    using polytope_facet_lower_bound_0[OF is_polytope nonzero_aff_dim has_origin]
    unfolding aff_dim_translation_eq[of "-z" p]
    by argo
qed

subsection‹Double counting›

lemma multiple_counting:
  fixes R :: "'a  'b  bool"
  assumes "finite s"
  assumes "finite t"
  assumes "x. xs  card {yt. R x y} = m"
  assumes "y. yt  card {xs. R x y} = n"
  shows "m * card s = n * card t"
proof -
  have lhs: "(xs. y{yt. R x y}. 1) = m * card s"
    using assms(1,3) by simp
  have rhs: "(yt. x{xs. R x y}. 1) = n * card t"
    using assms(2,4) by simp
  show ?thesis
    using sum.swap_restrict[OF assms(1) assms(2), of "λx y. (1::nat)" R]
    unfolding lhs rhs .
qed

subsubsection‹$m F = 2 E$›

text‹Let F be the number of facets and E be the number of ridges.
     If there are m ridges per facet, then m*F = 2*E.›

lemma facet_ridge_relation:
  assumes "polytope p"
      and "aff_dim p  2"
      and edges_per_face: "f. f face_of p  aff_dim f = aff_dim p - 1 
                card {e. e face_of p  aff_dim e = aff_dim p - 2  e  f} = m"
  shows "m * card {f. f face_of p  aff_dim f = aff_dim p - 1} =
         2 * card {e. e face_of p  aff_dim e = aff_dim p - 2}"
proof -
  let ?faces_set = "{f. f face_of p  aff_dim f = aff_dim p - 1}"
  let ?edges_set = "{e. e face_of p  aff_dim e = aff_dim p - 2}"
  have finite_faces: "finite ?faces_set"
    using finite_polytope_faces[OF polytope p]
    by fast
  have finite_edges: "finite ?edges_set"
    using finite_polytope_faces[OF polytope p]
    by fast
  have lhs_m: "f. f?faces_set  card {e?edges_set. e  f} = m"
    using edges_per_face by auto
  have rhs_2: "e. e?edges_set  card {f?faces_set. e  f} = 2"
  proof -
    fix e
    assume *: "e?edges_set"
    have "e face_of p"
      using *
      by blast
    have aff_dim_e: "aff_dim e = aff_dim p - 2"
      using * by auto
    then have "e  {}"
      using assms(2) by auto
    have "polyhedron p"
      using polytope_imp_polyhedron[OF polytope p] .
    obtain f1 f2 where 
      f1_props: "f1 face_of p" "aff_dim f1 = aff_dim p - 1" "e  f1" and
      f2_props: "f2 face_of p" "aff_dim f2 = aff_dim p - 1" "e  f2" and
      distinct: "f1  f2" and
      characterization: "f. f face_of p  aff_dim f = aff_dim p - 1  e  f  f = f1  f = f2"
      using polyhedron_ridge_two_facets[OF polyhedron p e face_of p e  {} aff_dim_e]
      by auto
    have "{f?faces_set. e  f} = {f1, f2}"
      using characterization f1_props f2_props
      by auto
    then show "card {f?faces_set. e  f} = 2"
      using distinct
      by simp
  qed
  show ?thesis
    using multiple_counting[OF finite_faces finite_edges lhs_m rhs_2] .
qed

subsubsection‹$n V = 2 E$›

text‹Let V be the number of vertices and E be the number of edges.
     If there are n edges per vertex, then n*V = 2*E.›

lemma edge_vertex_relation:
  assumes "polytope p"
      and edges_per_vert: "v. v face_of p  aff_dim v = 0 
                card {e. e face_of p  aff_dim e = 1  v  e} = n"
  shows "n * card {v. v face_of p  aff_dim v = 0} =
         2 * card {e. e face_of p  aff_dim e = 1}"
proof -
  let ?vertices_set = "{v. v face_of p  aff_dim v = 0}"
  let ?edges_set = "{e. e face_of p  aff_dim e = 1}"
  have finite_vertices: "finite ?vertices_set"
    using finite_polytope_faces[OF polytope p]
    by fast
  have finite_edges: "finite ?edges_set"
    using finite_polytope_faces[OF polytope p]
    by fast
  have lhs_n: "v. v?vertices_set  card {e?edges_set. v  e} = n"
    using edges_per_vert by auto
  have rhs_2: "e. e?edges_set  card {v?vertices_set. v  e} = 2"
  proof -
    fix e
    assume *: "e?edges_set"
    have "aff_dim e = 1"
      using * by auto
    then have "e  {}"
      by force
    have "compact e"
      using * face_of_polytope_polytope[OF polytope p] polytope_imp_compact[of e]
      by blast
    have "convex e"
      using * unfolding face_of_def
      by blast
    have "collinear e"
    proof -
      obtain B where
        aff_hull_B_e: "affine hull B = affine hull e" and
        "¬ affine_dependent B" and
        "int (card B) = 2"
        using aff_dim e = 1 aff_dim_basis_exists[of e] by auto
      then obtain x y where B_eq: "B = {x, y}" "x  y"
        by (meson card_2_iff of_nat_eq_numeral_iff)
      have "e  affine hull B"
        by (simp add: aff_hull_B_e hull_subset) 
      then show ?thesis
        unfolding B_eq using collinear_affine_hull
        by fast
    qed
    then obtain a b where e_eq: "e = closed_segment a b"
      using compact_convex_collinear_segment[OF e  {} compact e convex e collinear e]
      by blast
    then have "a  b"
      using aff_dim e = 1
      by force

    have "{v. v extreme_point_of e} = {v. {v} face_of e  aff_dim {v} = 0}"
      unfolding face_of_singleton[symmetric] using aff_dim_sing
      by blast
    then have "(λx. {x}) ` {v. v extreme_point_of e} = {v. v face_of e  aff_dim v = 0}"
      using aff_dim_eq_0 by auto
    then have "(λx. {x}) ` {v. v extreme_point_of e} = {v. v face_of p  aff_dim v = 0  v  e}"
      using face_of_face * by force
    then have "bij_betw (λx. {x}) {v. v extreme_point_of e} {v?vertices_set. v  e}"
      by (simp add: bij_betw_imageI inj_on_def)
    then have "card {v?vertices_set. v  e} = card {v. v extreme_point_of e}"
      using bij_betw_same_card by force
    moreover have "{v. v extreme_point_of e} = {a, b}"
      unfolding e_eq using extreme_point_of_segment[of _ a b] a  b
      by auto
    moreover have "card {a, b} = 2"
      using a  b by simp
    ultimately show "card {v?vertices_set. v  e} = 2"
      by argo
  qed
  show ?thesis
    using multiple_counting[OF finite_vertices finite_edges lhs_n rhs_2] .
qed

(* Don't expose multiple_counting because it doesn't have anything to do with polytopes *)
hide_fact multiple_counting

subsection ‹Face containment›

lemma ridge_belongs_to_facet:
  assumes "e face_of p"
      and "aff_dim e = aff_dim p - 2"
      and "aff_dim p  2"
      and "polyhedron p"
  obtains f where "f face_of p" "aff_dim f = aff_dim p - 1" "e face_of f"
proof -
  have "e  {}"
    using assms(2,3) by auto
  moreover have "e  p"
    using assms(2,3) by auto
  ultimately obtain f where "f facet_of p" "e  f"
    using assms(1,4) face_of_polyhedron_subset_facet
    by blast
  then have "f face_of p" "aff_dim f = aff_dim p - 1"
    unfolding facet_of_def by simp+
  moreover have "e face_of f"
    using f face_of p assms(1) e  f face_of_face
    by blast
  ultimately show "(f. f face_of p  aff_dim f = aff_dim p - 1  e face_of f  thesis)  thesis"
    by blast
qed

lemma peak_belongs_to_ridge:
  assumes "v face_of p"
      and "aff_dim v = aff_dim p - 3"
      and "aff_dim p  3"
      and "polyhedron p"
  obtains e where "e face_of p" "aff_dim e = aff_dim p - 2" "v face_of e"
proof -
  have "v  {}"
    using assms(2,3) by auto
  moreover have "v  p"
    using assms(2,3) by auto
  ultimately obtain f where "f facet_of p" "v  f"
    using assms(1,4) face_of_polyhedron_subset_facet
    by blast
  then have "f face_of p" "aff_dim f = aff_dim p - 1"
    unfolding facet_of_def assms(3)
    by simp+
  moreover have "v face_of f"
    using f face_of p assms(1) v  f face_of_face
    by blast
  obtain e where "e face_of f" "aff_dim e = aff_dim p - 2" "v face_of e"
    using ridge_belongs_to_facet[OF v face_of f _ _ 
          face_of_polyhedron_polyhedron[OF assms(4) f face_of p]]
      assms(2,3) aff_dim f = aff_dim p - 1
    by auto
  then show "(e. e face_of p  aff_dim e = aff_dim p - 2  v face_of e  thesis)  thesis"
    using face_of_trans[OF e face_of f f face_of p]
    by blast
qed

end