Theory Affine_Arithmetic.Polygon

theory Polygon
imports Counterclockwise_2D_Strict
begin

subsection ‹Polygonal chains›

definition "polychain xs = (i. Suc i<length xs  snd (xs ! i) = (fst (xs ! Suc i)))"

lemma polychainI:
  assumes "i. Suc i < length xs  snd (xs ! i) = fst (xs ! Suc i)"
  shows "polychain xs"
  by (auto intro!: assms simp: polychain_def)

lemma polychain_Nil[simp]: "polychain [] = True"
  and polychain_singleton[simp]: "polychain [x] = True"
  by (auto simp: polychain_def)

lemma polychain_Cons:
  "polychain (y # ys) = (if ys = [] then True else snd y = fst (ys ! 0)  polychain ys)"
  by (auto simp: polychain_def nth_Cons split: nat.split)

lemma polychain_appendI:
  "polychain xs  polychain ys  (xs  []  ys  []  snd (last xs) = fst (hd ys)) 
    polychain (xs @ ys)"
  by (induct xs arbitrary: ys)
    (auto simp add: polychain_Cons nth_append hd_conv_nth split: if_split_asm)

fun pairself where "pairself f (x, y) = (f x, f y)"

lemma pairself_apply: "pairself f x = (f (fst x), f (snd x))"
  by (cases x, simp)

lemma polychain_map_pairself: "polychain xs  polychain (map (pairself f) xs)"
  by (auto simp: polychain_def pairself_apply)

definition "convex_polychain xs 
  (polychain xs 
  (i. Suc i < length xs  det3 (fst (xs ! i)) (snd (xs ! i)) (snd (xs ! Suc i)) > 0))"

lemma convex_polychain_Cons2[simp]:
  "convex_polychain (x#y#zs) 
    snd x = fst y  det3 (fst x) (fst y) (snd y) > 0  convex_polychain (y#zs)"
  by (auto simp add: convex_polychain_def polychain_def nth_Cons split: nat.split)

lemma convex_polychain_ConsD:
  assumes "convex_polychain (x#xs)"
  shows "convex_polychain xs"
  using assms by (auto simp: convex_polychain_def polychain_def nth_Cons split: nat.split)

definition
  "convex_polygon xs  (convex_polychain xs  (xs  []  fst (hd xs) = snd (last xs)))"

lemma convex_polychain_Nil[simp]: "convex_polychain [] = True"
  and convex_polychain_Cons[simp]: "convex_polychain [x] = True"
  by (auto simp: convex_polychain_def)

lemma convex_polygon_Cons2[simp]:
  "convex_polygon (x#y#zs)  fst x = snd (last (y#zs))  convex_polychain (x#y#zs)"
  by (auto simp: convex_polygon_def convex_polychain_def polychain_def nth_Cons)

lemma polychain_append_connected:
  "polychain (xs @ ys)  xs  []  ys  []  fst (hd ys) = snd (last xs)"
  by (auto simp: convex_polychain_def nth_append not_less polychain_def last_conv_nth hd_conv_nth
    dest!: spec[where x = "length xs - 1"])

lemma convex_polychain_appendI:
  assumes cxs: "convex_polychain xs"
  assumes cys: "convex_polychain ys"
  assumes pxy: "polychain (xs @ ys)"
  assumes "xs  []  ys  []  det3 (fst (last xs)) (snd (last xs)) (snd (hd ys)) > 0"
  shows "convex_polychain (xs @ ys)"
proof -
  {
    fix i
    assume "i < length xs" "length xs  Suc i" "Suc i < length xs + length ys"
    hence "xs  []" "ys  []" "i = length xs - 1" by auto
  }
  thus ?thesis
    using assms
    by (auto simp: hd_conv_nth convex_polychain_def nth_append Suc_diff_le last_conv_nth )
qed

lemma convex_polychainI:
  assumes "polychain xs"
  assumes "i. Suc i < length xs  det3 (fst (xs ! i)) (snd (xs ! i)) (snd (xs ! Suc i)) > 0"
  shows "convex_polychain xs"
  by (auto intro!: assms simp: convex_polychain_def ccw'_def)

lemma convex_polygon_skip:
  assumes "convex_polygon (x # y # z # w # ws)"
  assumes "ccw'.sortedP (fst x) (map snd (butlast (x # y # z # w # ws)))"
  shows "convex_polygon ((fst x, snd y) # z # w # ws)"
  using assms by (auto elim!: ccw'.sortedP_Cons simp: ccw'_def[symmetric])


primrec polychain_of::"'a::ab_group_add  'a list  ('a*'a) list" where
  "polychain_of xc [] = []"
| "polychain_of xc (xm#xs) = (xc, xc + xm)#polychain_of (xc + xm) xs"

lemma in_set_polychain_ofD: "ab  set (polychain_of x xs)  (snd ab - fst ab)  set xs"
  by (induct xs arbitrary: x) auto

lemma fst_polychain_of_nth_0[simp]: "xs  []  fst ((polychain_of p xs) ! 0) = p"
  by (cases xs) (auto simp: Let_def)

lemma fst_hd_polychain_of: "xs  []  fst (hd (polychain_of x xs)) = x"
  by (cases xs) auto

lemma length_polychain_of_eq[simp]:
  shows "length (polychain_of p qs) = length qs"
  by (induct qs arbitrary: p) simp_all

lemma
  polychain_of_subsequent_eq:
  assumes "Suc i < length qs"
  shows "snd (polychain_of p qs ! i) = fst (polychain_of p qs ! Suc i)"
  using assms
  by (induct qs arbitrary: p i) (auto simp add: nth_Cons split: nat.split)

lemma polychain_of_eq_empty_iff[simp]: "polychain_of p xs = []  xs = []"
  by (cases xs) (auto simp: Let_def)

lemma in_set_polychain_of_imp_sum_list:
  assumes "z  set (polychain_of Pc Ps)"
  obtains d where "z = (Pc + sum_list (take d Ps), Pc + sum_list (take (Suc d) Ps))"
  using assms
  apply atomize_elim
proof (induction Ps arbitrary: Pc z)
  case Nil thus ?case by simp
next
  case (Cons P Ps)
  hence "z = (Pc, Pc + P)  z  set (polychain_of (Pc + P) Ps)"
    by auto
  thus ?case
  proof
    assume "z  set ((polychain_of (Pc + P) Ps))"
    from Cons.IH[OF this]
    obtain d
    where "z = (Pc + P + sum_list (take d Ps), Pc + P + sum_list (take (Suc d) Ps))"
      by auto
    thus ?case
      by (auto intro!: exI[where x="Suc d"])
  qed (auto intro!: exI[where x=0])
qed

lemma last_polychain_of: "length xs > 0  snd (last (polychain_of p xs)) = p + sum_list xs"
  by (induct xs arbitrary: p) simp_all

lemma polychain_of_singleton_iff: "polychain_of p xs = [a]  fst a = p  xs = [(snd a - p)]"
  by (induct xs) auto

lemma polychain_of_add: "polychain_of (x + y) xs = map (((+) (y, y))) (polychain_of x xs)"
  by (induct xs arbitrary: x y) (auto simp: algebra_simps)

subsection ‹Dirvec: Inverse of Polychain›

primrec dirvec where "dirvec (x, y) = (y - x)"

lemma dirvec_minus: "dirvec x = snd x - fst x"
  by (cases x) simp

lemma dirvec_nth_polychain_of: "n < length xs  dirvec ((polychain_of p xs) ! n ) = (xs ! n)"
  by (induct xs arbitrary: p n) (auto simp: nth_Cons split: nat.split)

lemma dirvec_hd_polychain_of: "xs  []  dirvec (hd (polychain_of p xs)) = (hd xs)"
  by (simp add: hd_conv_nth dirvec_nth_polychain_of)

lemma dirvec_last_polychain_of: "xs  []  dirvec (last (polychain_of p xs)) = (last xs)"
  by (simp add: last_conv_nth dirvec_nth_polychain_of)

lemma map_dirvec_polychain_of[simp]: "map dirvec (polychain_of x xs) = xs"
  by (induct xs arbitrary: x) simp_all


subsection ‹Polychain of Sorted (@{term polychain_of}, @{term ccw'.sortedP})›

lemma ccw'_sortedP_translateD:
  "linorder_list0.sortedP (ccw' x0) (map ((+) x  g) xs) 
    linorder_list0.sortedP (ccw' (x0 - x)) (map g xs)"
proof (induct xs arbitrary: x0 x)
  case Nil thus ?case by (auto simp: linorder_list0.sortedP.Nil)
next
  case (Cons a xs x0 x)
  hence "yset xs. ccw' (x0 - x) (g a) (g y)"
    by (auto elim!: linorder_list0.sortedP_Cons simp: ccw'.translate_origin algebra_simps)
  thus ?case
    using Cons.prems(1)
    by (auto elim!: linorder_list0.sortedP_Cons intro!: linorder_list0.sortedP.Cons simp: Cons.hyps)
qed

lemma ccw'_sortedP_translateI:
  "linorder_list0.sortedP (ccw' (x0 - x)) (map g xs) 
    linorder_list0.sortedP (ccw' x0) (map ((+) x  g) xs)"
  using ccw'_sortedP_translateD[of "x0 - x" "-x" "(+) x o g" xs]
  by (simp add: o_def)

lemma ccw'_sortedP_translate_comp[simp]:
  "linorder_list0.sortedP (ccw' x0) (map ((+) x  g) xs) 
    linorder_list0.sortedP (ccw' (x0 - x)) (map g xs)"
  by (metis ccw'_sortedP_translateD ccw'_sortedP_translateI)

lemma snd_plus_commute: "snd  (+) (x0, x0) = (+) x0 o snd"
  by auto

lemma ccw'_sortedP_renormalize:
  "ccw'.sortedP a (map snd (polychain_of (x0 + x) xs)) 
   ccw'.sortedP (a - x0) (map snd (polychain_of x xs))"
  by (simp add: polychain_of_add add.commute snd_plus_commute)

lemma ccw'_sortedP_polychain_of01:
  shows "ccw'.sortedP 0 [u]  ccw'.sortedP x0 (map snd (polychain_of x0 [u]))"
    and "ccw'.sortedP 0 []  ccw'.sortedP x0 (map snd (polychain_of x0 []))"
  by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons  simp: ac_simps)

lemma ccw'_sortedP_polychain_of2:
  assumes "ccw'.sortedP 0 [u, v]"
  shows "ccw'.sortedP x0 (map snd (polychain_of x0 [u, v]))"
  using assms
  by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
    elim!: linorder_list0.sortedP_Cons simp: ac_simps ccw'.translate_origin)

lemma ccw'_sortedP_polychain_of3:
  assumes "ccw'.sortedP 0 (u#v#w#xs)"
  shows "ccw'.sortedP x0 (map snd (polychain_of x0 (u#v#w#xs)))"
  using assms
proof (induct xs arbitrary: x0 u v w)
  case Nil
  then have *: "ccw' 0 u v" "ccw' 0 v w" "ccw' 0 u w"
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ac_simps)
  moreover have "ccw' 0 (u + v) (u + (v + w))"
    by (metis add.assoc ccw'.add1 ccw'.add3_self *(2-))
  ultimately show ?case
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ac_simps ccw'.translate_origin ccw'.add3)
next
  case (Cons y ys)
  have s1: "linorder_list0.sortedP (ccw' 0)  ((u + v)#w#y#ys)" using Cons.prems
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ccw'.add1)
  have s2: "linorder_list0.sortedP (ccw' 0)  (u#(v + w)#y#ys)" using Cons.prems
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ccw'.add3 ccw'.add1)
  have s3: "linorder_list0.sortedP (ccw' 0)  (u#v#(w + y)#ys)" using Cons.prems
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ccw'.add3 ccw'.add1)
  show ?case
    using Cons.hyps[OF s1, of x0] Cons.hyps[OF s2, of x0] Cons.hyps[OF s3, of x0] Cons.prems
    by (auto intro!: linorder_list0.sortedP.Nil linorder_list0.sortedP.Cons
      elim!: linorder_list0.sortedP_Cons simp: ac_simps)
qed

lemma ccw'_sortedP_polychain_of_snd:
  assumes "ccw'.sortedP 0 xs"
  shows "ccw'.sortedP x0 (map snd (polychain_of x0 xs))"
  using assms
  by (metis ccw'_sortedP_polychain_of01 ccw'_sortedP_polychain_of2 ccw'_sortedP_polychain_of3
    list.exhaust)

lemma ccw'_sortedP_implies_distinct:
  assumes "ccw'.sortedP x qs"
  shows "distinct qs"
  using assms
proof induct
  case Cons thus ?case by (meson ccw'_contra distinct.simps(2))
qed simp

lemma ccw'_sortedP_implies_nonaligned:
  assumes "ccw'.sortedP x qs"
  assumes "y  set qs" "z  set qs" "y  z"
  shows "¬ coll x y z"
  using assms
  by induct (force simp: ccw'_def det3_def' algebra_simps)+

lemma list_all_mp: "list_all P xs  (x. x  set xs  P x  Q x)  list_all Q xs"
  by (auto simp: list_all_iff)

lemma
  ccw'_scale_origin:
  assumes "e  UNIV  {0<..<1}"
  assumes "x  set (polychain_of Pc (P # QRRs))"
  assumes "ccw'.sortedP 0 (P # QRRs)"
  assumes "ccw' (fst x) (snd x) (P + (Pc + (Pset QRRs. e P *R P)))"
  shows "ccw' (fst x) (snd x) (e P *R P + (Pc + (Pset QRRs. e P *R P)))"
proof -
  from assms(2) have "fst x = Pc  snd x = Pc + P  x  set (polychain_of (Pc + P) QRRs)" by auto
  thus ?thesis
  proof
    assume x: "x  set (polychain_of (Pc + P) QRRs)"
    define q where "q = snd x - fst x"
    from Polygon.in_set_polychain_of_imp_sum_list[OF x]
    obtain d where d: "fst x = (Pc + P + sum_list (take d QRRs))" by (auto simp: prod_eq_iff)
    from in_set_polychain_ofD[OF x]
    have q_in: "q  set QRRs" by (simp add: q_def)
    define R where "R = set QRRs - {q}"
    hence QRRs: "set QRRs = R  {q}" "q  R" "finite R" using q_in by auto
    have "ccw' 0 q (-P)"
      using assms(3)
      by (auto simp: ccw'.sortedP_Cons_iff q_in)
    hence "ccw' 0 q ((1 - e P) *R (-P))"
      using assms(1) by (subst ccw'.scaleR2_eq) (auto simp: algebra_simps)
    moreover
    from assms(4) have "ccw' 0 q ((Pset QRRs. e P *R P) - sum_list (take d QRRs))"
      by (auto simp: q_def ccw'.translate_origin d)
    ultimately
    have "ccw' 0 q ((1 - e P) *R (-P) + ((Pset QRRs. e P *R P) - sum_list (take d QRRs)))"
      by (intro ccw'.add3) auto
    thus ?thesis
      by (auto simp: ccw'.translate_origin q_def algebra_simps d)
  qed (metis (no_types, lifting) add.left_commute assms(4) ccw'.add3_self ccw'.scale_add3
    ccw'.translate)
qed

lemma polychain_of_ccw_convex:
  assumes "e  UNIV  {0 <..< 1}"
  assumes sorted: "linorder_list0.sortedP (ccw' 0) (P#Q#Ps)"
  shows "list_all
    (λ(xi, xj). ccw' xi xj (Pc + (P  set (P#Q#Ps). e P *R P)))
    (polychain_of Pc (P#Q#Ps))"
  using assms(1) assms(2)
proof (induct Ps arbitrary: P Q Pc)
  case Nil
  have eq: "e P *R P + e Q *R Q - P = (1 - e P) *R (- P) + e Q *R Q"
    using e  _
    by (auto simp add: algebra_simps)
  from Nil ccw'_sortedP_implies_distinct[OF Nil(2)]
  have "P  Q" "e P < 1" "0 < e Q" "ccw' 0 P Q"
    by (auto elim!: linorder_list0.sortedP_Cons)
  thus ?case
    by (auto simp: ccw'_not_coll ccw'.translate_origin eq)
next
  case (Cons R Rs)
  hence "ccw' 0 P Q" "P  Q" using ccw'_sortedP_implies_distinct[OF Cons(3)]
    by (auto elim!: linorder_list0.sortedP_Cons)
  have "list_all (λ(xi, xj). ccw' xi xj ((Pc + P) + (Pset (Q # R # Rs). e P *R P)))
    (polychain_of (Pc + P) (Q # R # Rs))"
    using Cons(2-)
    by (intro Cons(1)) (auto elim: linorder_list0.sortedP_Cons)
  also have "polychain_of (Pc + P) (Q # R # Rs) = tl (polychain_of Pc (P # Q # R # Rs))"
    by simp
  finally have "list_all (λ(xi, xj). ccw' xi xj (Pc + P + (Pset (Q # R # Rs). e P *R P)))
    (tl (polychain_of Pc (P # Q # R # Rs)))" .
  moreover
  have "list_all
      (λ(xi, xj). ccw' xi xj (P + (Pset (Q # R # Rs). e P *R P)))
      (polychain_of P (Q # R # Rs))"
    using Cons(2-)
    by (intro Cons(1)) (auto elim: linorder_list0.sortedP_Cons)
  have "(λ(xi, xj). ccw' xi xj (Pc + P + (Pset (Q # R # Rs). e P *R P)))
    (hd (polychain_of Pc (P # Q # R # Rs)))"
    using ccw'_sortedP_implies_nonaligned[OF Cons(3), of P Q]
      ccw'_sortedP_implies_nonaligned[OF Cons(3), of Q R]
      ccw'_sortedP_implies_nonaligned[OF Cons(3), of P R]
      Cons(2,3)
    by (auto simp add: Pi_iff add.assoc simp del: scaleR_Pair intro!: ccw'.sum
        elim!: linorder_list0.sortedP_Cons)
  ultimately
  have "list_all
      (λ(xi, xj). ccw' xi xj (P + (Pc + (Pset (Q # R # Rs). e P *R P))))
      (polychain_of Pc (P # Q # R # Rs))"
    by (simp add: ac_simps)
  hence "list_all
      (λ(xi, xj). ccw' xi xj (e P *R P + (Pc + (Pset (Q # R # Rs). e P *R P))))
      (polychain_of Pc (P # Q # R # Rs))"
    unfolding split_beta'
    by (rule list_all_mp, intro ccw'_scale_origin[OF assms(1)])
      (auto intro!: ccw'_scale_origin Cons(3))
  thus ?case
    using ccw'_sortedP_implies_distinct[OF Cons(3)] Cons
    by (simp add: ac_simps)
qed

lemma polychain_of_ccw:
  assumes "e  UNIV  {0 <..< 1}"
  assumes sorted: "ccw'.sortedP 0 qs"
  assumes qs: "length qs  1"
  shows "list_all (λ(xi, xj). ccw' xi xj (Pc + (P  set qs. e P *R P))) (polychain_of Pc qs)"
  using assms
proof (cases qs)
  case (Cons Q Qs)
  note CQ = this
  show ?thesis using assms
  proof (cases Qs)
    case (Cons R Rs)
    thus ?thesis using assms
      unfolding CQ Cons
      by (intro polychain_of_ccw_convex) (auto simp: CQ Cons intro!: polychain_of_ccw_convex)
  qed (auto simp: CQ)
qed simp

lemma in_polychain_of_ccw:
  assumes "e  UNIV  {0 <..< 1}"
  assumes "ccw'.sortedP 0 qs"
  assumes "length qs  1"
  assumes "seg  set (polychain_of Pc qs)"
  shows "ccw' (fst seg) (snd seg) (Pc + (P  set qs. e P *R P))"
  using polychain_of_ccw[OF assms(1,2,3)] assms(4)
  by (simp add: list_all_iff split_beta)

lemma distinct_butlast_ne_last: "distinct xs  x  set (butlast xs)  x  last xs"
  by (metis append_butlast_last_id distinct_butlast empty_iff in_set_butlastD list.set(1)
    not_distinct_conv_prefix)

lemma
  ccw'_sortedP_convex_rotate_aux:
  assumes "ccw'.sortedP 0 (zs)" "ccw'.sortedP x (map snd (polychain_of x (zs)))"
  shows "ccw'.sortedP (snd (last (polychain_of x (zs)))) (map snd (butlast (polychain_of x (zs))))"
  using assms
proof (induct zs arbitrary: x rule: list.induct)
  case (Cons z zs)
  {
    assume "zs  []"
    have "ccw'.sortedP (snd (last (polychain_of (x + z) zs)))
      (map snd (butlast (polychain_of (x + z) zs)))"
      using Cons.prems
      by (auto elim!: ccw'.sortedP_Cons intro!: ccw'_sortedP_polychain_of_snd Cons.hyps)
    from _ this
    have "linorder_list0.sortedP (ccw' (snd (last (polychain_of (x + z) zs))))
       ((x + z) # map snd (butlast (polychain_of (x + z) zs)))"
    proof (rule ccw'.sortedP.Cons, safe)
      fix c d
      assume cd: "(c, d)  set (map snd (butlast (polychain_of (x + z) zs)))"
      then obtain a b where ab: "((a, b), c, d)  set (butlast (polychain_of (x + z) zs))"
        by auto
      have cd': "(c, d)  set (butlast (map snd (polychain_of (x + z) zs)))" using cd
        by (auto simp: map_butlast)
      have "ccw' (x + z) (c, d) (last (map snd (polychain_of (x + z) zs)))"
      proof (rule ccw'.sortedP_right_of_last)
        show "ccw'.sortedP (x + z) (map snd (polychain_of (x + z) zs))"
           using Cons
           by (force intro!: ccw'.sortedP.Cons ccw'.sortedP.Nil ccw'_sortedP_polychain_of_snd
             elim!: ccw'.sortedP_Cons)
        show "(c, d)  set (map snd (polychain_of (x + z) zs))"
          using in_set_butlastD[OF ab]
          by force
        from Cons(3) cd'
        show "(c, d)  last (map snd (polychain_of (x + z) zs))"
          by (intro distinct_butlast_ne_last ccw'_sortedP_implies_distinct[where x=x])
            (auto elim!: ccw'.sortedP_Cons)
      qed
      thus "ccw' (snd (last (polychain_of (x + z) zs))) (x + z) (c, d)"
         by (auto simp: last_map[symmetric, where f= snd] zs  [] intro: ccw'.cyclicI)
    qed
  }
  thus ?case
    by (auto simp: ccw'.sortedP.Nil)
qed (simp add: ccw'.sortedP.Nil)

lemma ccw'_polychain_of_sorted_center_last:
  assumes set_butlast: "(c, d)  set (butlast (polychain_of x0 xs))"
  assumes sorted: "ccw'.sortedP 0 xs"
  assumes ne: "xs  []"
  shows "ccw' x0 d (snd (last (polychain_of x0 xs)))"
proof -
  from ccw'_sortedP_polychain_of_snd[OF sorted, of x0]
  have "ccw'.sortedP x0 (map snd (polychain_of x0 xs))" .
  also
  from set_butlast obtain ys zs where "butlast (polychain_of x0 xs) = ys@((c, d)#zs)"
    by (auto simp add: in_set_conv_decomp)
  hence "polychain_of x0 xs = ys @ (c, d) # zs @ [last (polychain_of x0 xs)]"
    by (metis append_Cons append_assoc append_butlast_last_id ne polychain_of_eq_empty_iff)
  finally show ?thesis by (auto elim!: ccw'.sortedP_Cons simp: ccw'.sortedP_append_iff)
qed

end