CubicPathwidth.Alg.Proofs2.L4Connector
Source file CubicPathwidth/Alg/Proofs2/L4Connector.lean of the Lean project (how it is built).
Statements of the paper formalised here: Lemma A.33.
import CubicPathwidth.Alg.Plan2
import CubicPathwidth.Alg.Proofs.C3Connector
/-!
# L4: `ConnectorLemma10` (Lemma A.33, `lem:connector-linear`) for the port's connector order
Main theorem: `connectorLemma10 : ConnectorLemma10`.
Structure (all helpers in namespace `CubicPathwidth.Alg.L4`):
* counts of items inside a set (`cntIn`, strict monotonicity, additivity over
separated lists);
* incidence lists `inc` of `connectorIncidence` (membership, total size bound);
* the DFS `connDfs` (completion, closure, connectivity of its members);
* the outer scan `connOuter` (components are closed, connected, pairwise disjoint,
cover `Qs`);
* the greedy loop `connectorGreedy.loop` (completion, Perm, and the prefix count
`p ≤ c(p) + 1` along the greedy order);
* the excess identity `connectorExcess = |C| - #items inside C`;
* the main induction over the sorted list of components (prefix sums of sorted
excesses are at most `max 0 (total)`), and the Perm part.
-/
namespace CubicPathwidth.Alg
namespace L4
/-- Adjacency in the hypergraph with hyperedges `items`. -/
def Adj (items : List (List Nat)) (x y : Nat) : Prop := ∃ e ∈ items, x ∈ e ∧ y ∈ e
/-- Reachability in the hypergraph `items`. -/
def Reach (items : List (List Nat)) (x y : Nat) : Prop :=
Relation.ReflTransGen (Adj items) x y
/-- Number of items contained in the set `L` (the count `c(j)` of Lemma A.33 (`lem:connector-linear`)). -/
def cntIn (items : List (List Nat)) (L : List Nat) : Nat :=
items.countP fun e => subsetB e L
theorem subsetB_iff {S B : List Nat} : subsetB S B = true ↔ ∀ z ∈ S, z ∈ B := by
simp [subsetB]
theorem cntIn_eq_filter (items : List (List Nat)) (L : List Nat) :
cntIn items L = (items.filter fun e => subsetB e L).length := by
unfold cntIn; rw [List.countP_eq_length_filter]
theorem cntIn_congr {items : List (List Nat)} {L L' : List Nat}
(h : ∀ z, z ∈ L ↔ z ∈ L') : cntIn items L = cntIn items L' := by
unfold cntIn
apply List.countP_congr
intro e _
simp only [subsetB_iff]
exact ⟨fun h' z hz => (h z).1 (h' z hz), fun h' z hz => (h z).2 (h' z hz)⟩
theorem cntIn_le_length (items : List (List Nat)) (L : List Nat) :
cntIn items L ≤ items.length := List.countP_le_length
theorem cntIn_strict {items : List (List Nat)} {L L' e : List Nat}
(hLL : ∀ z ∈ L, z ∈ L') (he : e ∈ items) (heL' : ∀ z ∈ e, z ∈ L')
(heL : ¬ ∀ z ∈ e, z ∈ L) : cntIn items L < cntIn items L' := by
rw [cntIn_eq_filter, cntIn_eq_filter]
have hsub : List.Sublist (items.filter fun e => subsetB e L)
(items.filter fun e => subsetB e L') :=
List.monotone_filter_right items (fun a ha => by
rw [subsetB_iff] at ha ⊢
exact fun z hz => hLL z (ha z hz))
have hmem : e ∈ items.filter fun e => subsetB e L' :=
List.mem_filter.2 ⟨he, subsetB_iff.2 heL'⟩
have hnot : e ∉ items.filter fun e => subsetB e L := by
intro h
rw [List.mem_filter] at h
exact heL (subsetB_iff.1 h.2)
refine lt_of_le_of_ne hsub.length_le (fun h => hnot ?_)
rw [hsub.eq_of_length h]
exact hmem
theorem countP_or_add {α : Type} (p q : α → Bool) : ∀ l : List α,
(∀ x ∈ l, p x = true → q x = false) →
l.countP (fun x => p x || q x) = l.countP p + l.countP q
| [], _ => by simp
| a :: l, h => by
have ih := countP_or_add p q l (fun x hx => h x (List.mem_cons_of_mem _ hx))
have ha := h a List.mem_cons_self
simp only [List.countP_cons]
rw [ih]
cases hp : p a <;> cases hq : q a <;> simp_all <;> omega
/-- Items inside a union of two separated sets: additivity of the count. -/
theorem cntIn_append {items : List (List Nat)} {X Y : List Nat}
(hne : ∀ e ∈ items, e ≠ []) (hXY : ∀ x ∈ X, x ∉ Y)
(hsplit : ∀ e ∈ items, (∀ z ∈ e, z ∈ X ++ Y) → (∀ z ∈ e, z ∈ X) ∨ (∀ z ∈ e, z ∈ Y)) :
cntIn items (X ++ Y) = cntIn items X + cntIn items Y := by
unfold cntIn
have key : items.countP (fun e => subsetB e (X ++ Y)) =
items.countP (fun e => subsetB e X || subsetB e Y) := by
apply List.countP_congr
intro e he
simp only [Bool.or_eq_true, subsetB_iff]
constructor
· intro h
rcases hsplit e he (fun z hz => h z hz) with h1 | h1
· exact Or.inl h1
· exact Or.inr h1
· rintro (h | h) z hz
· exact List.mem_append_left _ (h z hz)
· exact List.mem_append_right _ (h z hz)
rw [key]
apply countP_or_add
intro e he h1
rw [Bool.eq_false_iff]
intro h2
obtain ⟨z, hz⟩ : ∃ z, z ∈ e := List.exists_mem_of_ne_nil _ (hne e he)
exact hXY z (subsetB_iff.1 h1 z hz) (subsetB_iff.1 h2 z hz)
/-! ## Incidence lists -/
theorem mem_inc_inner (e : List Nat) (k : Nat) : ∀ (m : Std.HashMap Nat (List Nat))
(y k' : Nat),
k' ∈ (e.foldl (fun (m : Std.HashMap Nat (List Nat)) x => m.insert x (m.getD x [] ++ [k])) m).getD y [] ↔
k' ∈ m.getD y [] ∨ (y ∈ e ∧ k' = k) := by
induction e with
| nil => intro m y k'; simp
| cons x e ih =>
intro m y k'
rw [List.foldl_cons, ih]
by_cases hxy : x = y
· subst hxy
simp only [Std.HashMap.getD_insert, beq_self_eq_true, ite_true, List.mem_append,
List.mem_cons, List.mem_singleton]
tauto
· have hne : (x == y) = false := by simpa using hxy
have hyx : y ≠ x := fun h => hxy h.symm
simp only [Std.HashMap.getD_insert, hne, Bool.false_eq_true, ite_false, List.mem_cons]
simp [hyx]
theorem mem_inc_outer (L : List (List Nat × Nat)) : ∀ (m : Std.HashMap Nat (List Nat))
(y k' : Nat),
k' ∈ (L.foldl (fun (m : Std.HashMap Nat (List Nat)) (p : List Nat × Nat) =>
p.1.foldl (fun (m : Std.HashMap Nat (List Nat)) x => m.insert x (m.getD x [] ++ [p.2])) m) m).getD y []
↔ k' ∈ m.getD y [] ∨ ∃ p ∈ L, y ∈ p.1 ∧ k' = p.2 := by
induction L with
| nil => intro m y k'; simp
| cons p L ih =>
intro m y k'
rw [List.foldl_cons, ih, mem_inc_inner]
constructor
· rintro ((h | ⟨h1, h2⟩) | ⟨q, hq, h1, h2⟩)
· exact Or.inl h
· exact Or.inr ⟨p, List.mem_cons_self, h1, h2⟩
· exact Or.inr ⟨q, List.mem_cons_of_mem _ hq, h1, h2⟩
· rintro (h | ⟨q, hq, h1, h2⟩)
· exact Or.inl (Or.inl h)
· rcases List.mem_cons.1 hq with rfl | hq
· exact Or.inl (Or.inr ⟨h1, h2⟩)
· exact Or.inr ⟨q, hq, h1, h2⟩
/-- Membership in the incidence list: `k ∈ inc y ↔ y ∈ items[k]`. -/
theorem inc_mem (Qs : List Nat) (items : List (List Nat)) (y k : Nat) :
k ∈ (connectorIncidence Qs items).getD y [] ↔ y ∈ items.getD k [] := by
unfold connectorIncidence
rw [mem_inc_outer]
simp only [Std.HashMap.getD_empty, List.not_mem_nil, false_or]
constructor
· rintro ⟨p, hp, hy, rfl⟩
have h1 := List.mem_zipIdx_iff_getElem?.1 hp
rw [List.getD_eq_getElem?_getD, h1]
simpa using hy
· intro hy
have hk : k < items.length := by
by_contra h
push_neg at h
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_none h] at hy
simp at hy
refine ⟨(items[k], k), List.mem_zipIdx_iff_getElem?.2 (by simp [List.getElem?_eq_getElem hk]), ?_, rfl⟩
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk] at hy
simpa using hy
/-- An element of `items.getD k []` lying in it makes that list an item. -/
theorem getD_mem_of_mem {items : List (List Nat)} {k y : Nat}
(h : y ∈ items.getD k []) : items.getD k [] ∈ items := by
rw [List.getD_eq_getElem?_getD] at h ⊢
cases hk : items[k]? with
| none => simp [hk] at h
| some e => exact List.mem_of_getElem? hk
/-- Size of the incidence lists restricted to `c`. -/
def sumInc (c : List Nat) (m : Std.HashMap Nat (List Nat)) : Nat :=
(c.map fun x => (m.getD x []).length).sum
theorem sumInc_congr (c : List Nat) (m m' : Std.HashMap Nat (List Nat))
(h : ∀ y ∈ c, m'.getD y [] = m.getD y []) : sumInc c m' = sumInc c m := by
unfold sumInc
congr 1
apply List.map_congr_left
intro y hy
simp [h y hy]
theorem sumInc_insert (c : List Nat) (hc : c.Nodup) (m : Std.HashMap Nat (List Nat))
(x k : Nat) :
sumInc c (m.insert x (m.getD x [] ++ [k])) ≤ sumInc c m + (if x ∈ c then 1 else 0) := by
induction c with
| nil => simp [sumInc]
| cons a c ih =>
rw [List.nodup_cons] at hc
unfold sumInc at ih ⊢
simp only [List.map_cons, List.sum_cons]
by_cases hax : a = x
· subst hax
have hrest : ∀ y ∈ c, (m.insert a (m.getD a [] ++ [k])).getD y [] = m.getD y [] := by
intro y hy
have hya : (a == y) = false := by
simp only [beq_eq_false_iff_ne, ne_eq]
exact fun h => hc.1 (h ▸ hy)
rw [Std.HashMap.getD_insert, hya]; simp
have hr := sumInc_congr c m _ hrest
unfold sumInc at hr
rw [Std.HashMap.getD_insert_self]
have hmem : a ∉ c := hc.1
simp only [List.mem_cons, true_or, if_true]
simp only [List.length_append, List.length_singleton]
omega
· have hne : (x == a) = false := by simpa using (Ne.symm hax)
rw [Std.HashMap.getD_insert, hne]
simp only [Bool.false_eq_true, ite_false]
have ih' := ih hc.2
have hmem : x ∈ a :: c ↔ x ∈ c := by
simp only [List.mem_cons]
exact ⟨fun h => h.resolve_left (Ne.symm hax), fun h => Or.inr h⟩
by_cases hxc : x ∈ c
· have := hmem.2 hxc
simp only [if_pos hxc] at ih' ⊢
simp only [if_pos this]
omega
· have := mt hmem.1 hxc
simp only [if_neg hxc] at ih' ⊢
simp only [if_neg this]
omega
theorem sumInc_fold (c : List Nat) (hc : c.Nodup) (k : Nat) (e : List Nat) :
∀ m : Std.HashMap Nat (List Nat),
sumInc c (e.foldl (fun (m : Std.HashMap Nat (List Nat)) x => m.insert x (m.getD x [] ++ [k])) m)
≤ sumInc c m + e.length := by
induction e with
| nil => intro m; simp
| cons x e ih =>
intro m
rw [List.foldl_cons]
have h1 := ih (m.insert x (m.getD x [] ++ [k]))
have h2 := sumInc_insert c hc m x k
have h3 : (if x ∈ c then 1 else 0) ≤ 1 := by split_ifs <;> omega
simp only [List.length_cons]
omega
theorem sumInc_outer (c : List Nat) (hc : c.Nodup) (L : List (List Nat × Nat)) :
∀ m : Std.HashMap Nat (List Nat),
sumInc c (L.foldl (fun (m : Std.HashMap Nat (List Nat)) (p : List Nat × Nat) =>
p.1.foldl (fun (m : Std.HashMap Nat (List Nat)) x => m.insert x (m.getD x [] ++ [p.2])) m) m)
≤ sumInc c m + (L.map fun p => p.1.length).sum := by
induction L with
| nil => intro m; simp
| cons p L ih =>
intro m
rw [List.foldl_cons]
have h1 := ih (p.1.foldl (fun (m : Std.HashMap Nat (List Nat)) x => m.insert x (m.getD x [] ++ [p.2])) m)
have h2 := sumInc_fold c hc p.2 p.1 m
simp only [List.map_cons, List.sum_cons]
omega
/-- The total incidence size is at most the total size of the items. -/
theorem sumInc_incidence (Qs : List Nat) (items : List (List Nat)) (c : List Nat)
(hc : c.Nodup) : sumInc c (connectorIncidence Qs items) ≤ (items.map List.length).sum := by
unfold connectorIncidence
have h := sumInc_outer c hc items.zipIdx ∅
have hz : ((items.zipIdx).map fun p => p.1.length).sum = (items.map List.length).sum := by
congr 1
have : (items.zipIdx).map (fun p => p.1.length) = items.map List.length := by
apply List.ext_getElem
· simp
· intro i h1 h2
simp
exact this
have h0 : sumInc c (∅ : Std.HashMap Nat (List Nat)) = 0 := by
unfold sumInc; simp
rw [h0, hz] at h
simpa using h
/-! ## The DFS `connDfs` -/
/-- One inner step of `connDfs`: add `z` unless it is assigned already. -/
def dfsF (acc : List Nat × Std.HashSet Nat × List Nat) (z : Nat) :
List Nat × Std.HashSet Nat × List Nat :=
if acc.2.1.contains z then acc else (z :: acc.1, acc.2.1.insert z, z :: acc.2.2)
/-- Invariant of the DFS state `(stk, A, M)` (`A` assigned, `M` members, `stk` pending),
for the root `root`, the set `A0` assigned before the call, and the universe `all`. -/
structure DBase (items : List (List Nat)) (all : List Nat) (A0 : Std.HashSet Nat)
(root : Nat) (stk : List Nat) (A : Std.HashSet Nat) (M : List Nat) : Prop where
nodup : M.Nodup
sub : ∀ w ∈ M, w ∈ all
asg : ∀ w ∈ M, A.contains w = true
stkM : ∀ w ∈ stk, w ∈ M
reach : ∀ w ∈ M, Reach items root w
base : ∀ w, A0.contains w = true → A.contains w = true
asgA0 : ∀ w, A.contains w = true → A0.contains w = true ∨ w ∈ M
freshA0 : ∀ w ∈ M, ¬ A0.contains w = true
shape : ∃ T, M = T ++ [root]
/-- `DBase` together with the pending condition: an item through a member is either
pending on the stack or fully assigned. -/
def DInv (items : List (List Nat)) (all : List Nat) (A0 : Std.HashSet Nat) (root : Nat)
(stk : List Nat) (A : Std.HashSet Nat) (M : List Nat) : Prop :=
DBase items all A0 root stk A M ∧
∀ y ∈ M, ∀ e ∈ items, y ∈ e → y ∈ stk ∨ ∀ z ∈ e, A.contains z = true
theorem dfsF_facts {items : List (List Nat)} {all : List Nat} {A0 : Std.HashSet Nat}
{root : Nat} (acc : List Nat × Std.HashSet Nat × List Nat)
(h : DBase items all A0 root acc.1 acc.2.1 acc.2.2) {z : Nat} (hz : z ∈ all)
(hr : Reach items root z) :
DBase items all A0 root (dfsF acc z).1 (dfsF acc z).2.1 (dfsF acc z).2.2 ∧
(∀ w ∈ acc.1, w ∈ (dfsF acc z).1) ∧
(∀ w, acc.2.1.contains w = true → (dfsF acc z).2.1.contains w = true) ∧
(∀ w ∈ acc.2.2, w ∈ (dfsF acc z).2.2) ∧
(∀ w ∈ (dfsF acc z).2.2, w ∈ acc.2.2 ∨ w ∈ (dfsF acc z).1) ∧
(dfsF acc z).1.length + acc.2.2.length = acc.1.length + (dfsF acc z).2.2.length ∧
(dfsF acc z).2.1.contains z = true := by
obtain ⟨s, A, M⟩ := acc
dsimp only at h ⊢
by_cases hA : A.contains z = true
· have e : dfsF (s, A, M) z = (s, A, M) := by unfold dfsF; simp [hA]
rw [e]
exact ⟨h, fun w hw => hw, fun w hw => hw, fun w hw => hw, fun w hw => Or.inl hw, by simp,
by simpa using hA⟩
· have hA' : A.contains z = false := by simpa using hA
have e : dfsF (s, A, M) z = (z :: s, A.insert z, z :: M) := by
unfold dfsF; simp [hA']
rw [e]
dsimp only
have hzM : z ∉ M := fun hm => by
have := h.asg z hm
rw [hA'] at this
exact Bool.false_ne_true this
have hD : DBase items all A0 root (z :: s) (A.insert z) (z :: M) := by
refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩
· exact List.nodup_cons.2 ⟨hzM, h.nodup⟩
· intro w hw
rcases List.mem_cons.1 hw with rfl | hw
· exact hz
· exact h.sub w hw
· intro w hw
rcases List.mem_cons.1 hw with rfl | hw
· rw [Std.HashSet.contains_insert]; simp
· rw [Std.HashSet.contains_insert]; simp [h.asg w hw]
· intro w hw
rcases List.mem_cons.1 hw with rfl | hw
· exact List.mem_cons_self
· exact List.mem_cons_of_mem _ (h.stkM w hw)
· intro w hw
rcases List.mem_cons.1 hw with rfl | hw
· exact hr
· exact h.reach w hw
· intro w hw
rw [Std.HashSet.contains_insert]
simp [h.base w hw]
· intro w hw
rw [Std.HashSet.contains_insert] at hw
simp only [Bool.or_eq_true, beq_iff_eq] at hw
rcases hw with rfl | hw
· exact Or.inr List.mem_cons_self
· rcases h.asgA0 w hw with h1 | h1
· exact Or.inl h1
· exact Or.inr (List.mem_cons_of_mem _ h1)
· intro w hw hA0
rcases List.mem_cons.1 hw with rfl | hw
· have := h.base _ hA0
rw [hA'] at this
exact Bool.false_ne_true this
· exact h.freshA0 w hw hA0
· obtain ⟨T, hT⟩ := h.shape
exact ⟨z :: T, by rw [hT]; simp⟩
refine ⟨hD, fun w hw => List.mem_cons_of_mem _ hw, fun w hw => ?_,
fun w hw => List.mem_cons_of_mem _ hw, fun w hw => ?_, ?_, ?_⟩
· rw [Std.HashSet.contains_insert]; simp [hw]
· rcases List.mem_cons.1 hw with rfl | hw
· exact Or.inr List.mem_cons_self
· exact Or.inl hw
· simp only [List.length_cons]; omega
· rw [Std.HashSet.contains_insert]; simp
/-- The inner fold over a list of elements. -/
theorem fold_dfsF {items : List (List Nat)} {all : List Nat} {A0 : Std.HashSet Nat}
{root : Nat} : ∀ (L : List Nat) (acc : List Nat × Std.HashSet Nat × List Nat),
(∀ z ∈ L, z ∈ all) → (∀ z ∈ L, Reach items root z) →
DBase items all A0 root acc.1 acc.2.1 acc.2.2 →
DBase items all A0 root (L.foldl dfsF acc).1 (L.foldl dfsF acc).2.1 (L.foldl dfsF acc).2.2 ∧
(∀ w ∈ acc.1, w ∈ (L.foldl dfsF acc).1) ∧
(∀ w, acc.2.1.contains w = true → (L.foldl dfsF acc).2.1.contains w = true) ∧
(∀ w ∈ acc.2.2, w ∈ (L.foldl dfsF acc).2.2) ∧
(∀ w ∈ (L.foldl dfsF acc).2.2, w ∈ acc.2.2 ∨ w ∈ (L.foldl dfsF acc).1) ∧
(L.foldl dfsF acc).1.length + acc.2.2.length = acc.1.length + (L.foldl dfsF acc).2.2.length ∧
(∀ z ∈ L, (L.foldl dfsF acc).2.1.contains z = true) := by
intro L
induction L with
| nil =>
intro acc _ _ h
exact ⟨h, fun _ hw => hw, fun _ hw => hw, fun _ hw => hw, fun w hw => Or.inl hw, rfl,
by simp⟩
| cons x L ih =>
intro acc hall hreach h
rw [List.foldl_cons]
obtain ⟨h1, h2, h3, h4, h5, h6, h7⟩ := dfsF_facts acc h (hall x List.mem_cons_self)
(hreach x List.mem_cons_self)
obtain ⟨i1, i2, i3, i4, i5, i6, i7⟩ := ih (dfsF acc x)
(fun z hz => hall z (List.mem_cons_of_mem _ hz))
(fun z hz => hreach z (List.mem_cons_of_mem _ hz)) h1
refine ⟨i1, fun w hw => i2 w (h2 w hw), fun w hw => i3 w (h3 w hw),
fun w hw => i4 w (h4 w hw), ?_, ?_, ?_⟩
· intro w hw
rcases i5 w hw with h8 | h8
· rcases h5 w h8 with h9 | h9
· exact Or.inl h9
· exact Or.inr (i2 w h9)
· exact Or.inr h8
· omega
· intro z hz
rcases List.mem_cons.1 hz with rfl | hz
· exact i3 _ h7
· exact i7 z hz
/-- One outer step of `connDfs`: fold over the elements of the item `items.getD k []`. -/
def dfsK (items : List (List Nat)) (acc : List Nat × Std.HashSet Nat × List Nat) (k : Nat) :
List Nat × Std.HashSet Nat × List Nat :=
(items.getD k []).foldl dfsF acc
theorem dfsK_facts {items : List (List Nat)} {all : List Nat} {A0 : Std.HashSet Nat}
{root : Nat} (hall : ∀ e ∈ items, ∀ z ∈ e, z ∈ all)
(acc : List Nat × Std.HashSet Nat × List Nat)
(h : DBase items all A0 root acc.1 acc.2.1 acc.2.2) (k y0 : Nat)
(hy : y0 ∈ items.getD k []) (hy0 : y0 ∈ acc.2.2) :
DBase items all A0 root (dfsK items acc k).1 (dfsK items acc k).2.1 (dfsK items acc k).2.2 ∧
(∀ w ∈ acc.1, w ∈ (dfsK items acc k).1) ∧
(∀ w, acc.2.1.contains w = true → (dfsK items acc k).2.1.contains w = true) ∧
(∀ w ∈ acc.2.2, w ∈ (dfsK items acc k).2.2) ∧
(∀ w ∈ (dfsK items acc k).2.2, w ∈ acc.2.2 ∨ w ∈ (dfsK items acc k).1) ∧
(dfsK items acc k).1.length + acc.2.2.length = acc.1.length + (dfsK items acc k).2.2.length ∧
(∀ z ∈ items.getD k [], (dfsK items acc k).2.1.contains z = true) := by
have hmem : items.getD k [] ∈ items := getD_mem_of_mem hy
have hreach0 : Reach items root y0 := h.reach y0 hy0
have hL1 : ∀ z ∈ items.getD k [], z ∈ all := fun z hz => hall _ hmem z hz
have hL2 : ∀ z ∈ items.getD k [], Reach items root z := fun z hz =>
Relation.ReflTransGen.tail hreach0 ⟨_, hmem, hy, hz⟩
unfold dfsK
exact fold_dfsF (items.getD k []) acc hL1 hL2 h
theorem fold_ks {items : List (List Nat)} {all : List Nat} {A0 : Std.HashSet Nat}
{root : Nat} (hall : ∀ e ∈ items, ∀ z ∈ e, z ∈ all) (y0 : Nat) :
∀ (ks : List Nat) (acc : List Nat × Std.HashSet Nat × List Nat),
y0 ∈ acc.2.2 → DBase items all A0 root acc.1 acc.2.1 acc.2.2 →
(∀ k ∈ ks, y0 ∈ items.getD k []) →
DBase items all A0 root (ks.foldl (dfsK items) acc).1 (ks.foldl (dfsK items) acc).2.1
(ks.foldl (dfsK items) acc).2.2 ∧
(∀ w ∈ acc.1, w ∈ (ks.foldl (dfsK items) acc).1) ∧
(∀ w, acc.2.1.contains w = true → (ks.foldl (dfsK items) acc).2.1.contains w = true) ∧
(∀ w ∈ acc.2.2, w ∈ (ks.foldl (dfsK items) acc).2.2) ∧
(∀ w ∈ (ks.foldl (dfsK items) acc).2.2, w ∈ acc.2.2 ∨ w ∈ (ks.foldl (dfsK items) acc).1) ∧
(ks.foldl (dfsK items) acc).1.length + acc.2.2.length =
acc.1.length + (ks.foldl (dfsK items) acc).2.2.length ∧
(∀ k ∈ ks, ∀ z ∈ items.getD k [], (ks.foldl (dfsK items) acc).2.1.contains z = true) := by
intro ks
induction ks with
| nil =>
intro acc _ h _
exact ⟨h, fun _ hw => hw, fun _ hw => hw, fun _ hw => hw, fun w hw => Or.inl hw, rfl,
fun k hk => absurd hk List.not_mem_nil⟩
| cons k ks ih =>
intro acc hy0 h hks
obtain ⟨g1, g2, g3, g4, g5, g6, g7⟩ := dfsK_facts hall acc h k y0 (hks k List.mem_cons_self) hy0
have hy1 : y0 ∈ (dfsK items acc k).2.2 := g4 y0 hy0
obtain ⟨i1, i2, i3, i4, i5, i6, i7⟩ := ih (dfsK items acc k) hy1 g1
(fun k' hk' => hks k' (List.mem_cons_of_mem _ hk'))
rw [List.foldl_cons]
refine ⟨i1, fun w hw => i2 w (g2 w hw), fun w hw => i3 w (g3 w hw),
fun w hw => i4 w (g4 w hw), ?_, ?_, ?_⟩
· intro w hw
rcases i5 w hw with h8 | h8
· rcases g5 w h8 with h9 | h9
· exact Or.inl h9
· exact Or.inr (i2 w h9)
· exact Or.inr h8
· omega
· intro k' hk' z hz
rcases List.mem_cons.1 hk' with rfl | hk'
· exact i3 z (g7 z hz)
· exact i7 k' hk' z hz
/-- The pop step of `connDfs`: the root of the popped item is removed, its item-neighbours
are pushed (through `inc`) and all their elements are assigned. -/
theorem dfs_pop {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{all : List Nat} {A0 : Std.HashSet Nat} {root : Nat}
(hall : ∀ e ∈ items, ∀ z ∈ e, z ∈ all)
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k [])
{y0 : Nat} {rest : List Nat} {A : Std.HashSet Nat} {M : List Nat}
(hD : DInv items all A0 root (y0 :: rest) A M) :
DInv items all A0 root ((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).1
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).2.1
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).2.2 ∧
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).1.length + M.length =
rest.length + ((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).2.2.length := by
obtain ⟨hB, hpend⟩ := hD
have hy0M : y0 ∈ M := hB.stkM y0 List.mem_cons_self
have hBr : DBase items all A0 root rest A M :=
{ hB with stkM := fun w hw => hB.stkM w (List.mem_cons_of_mem _ hw) }
obtain ⟨j1, j2, j3, j4, j5, j6, j7⟩ := fold_ks hall y0 (inc.getD y0 []) (rest, A, M) hy0M hBr
(fun k hk => (hinc k y0).1 hk)
refine ⟨⟨j1, ?_⟩, j6⟩
intro y hy e he hye
rcases j5 y hy with hyM | hyS
· rcases hpend y hyM e he hye with h1 | h1
· rcases List.mem_cons.1 h1 with rfl | h1
· obtain ⟨k, rfl⟩ : ∃ k, items.getD k [] = e := by
obtain ⟨k, hk, hke⟩ := List.mem_iff_getElem.1 he
exact ⟨k, by rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk]; simp [hke]⟩
exact Or.inr (j7 k ((hinc k y).2 hye))
· exact Or.inl (j2 y h1)
· exact Or.inr (fun z hz => j3 z (h1 z hz))
· exact Or.inl hyS
theorem dinv_init {items : List (List Nat)} {all : List Nat} (A0 : Std.HashSet Nat)
(root : Nat) (hr : root ∈ all) (hA0 : ¬ A0.contains root = true) :
DInv items all A0 root [root] (A0.insert root) [root] := by
refine ⟨⟨?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_, ?_⟩, ?_⟩
· simp
· intro w hw
rw [List.mem_singleton] at hw
rw [hw]; exact hr
· intro w hw
rw [List.mem_singleton] at hw
rw [hw, Std.HashSet.contains_insert]; simp
· intro w hw; exact hw
· intro w hw
rw [List.mem_singleton] at hw
rw [hw]; exact Relation.ReflTransGen.refl
· intro w hw
rw [Std.HashSet.contains_insert]
simp [hw]
· intro w hw
rw [Std.HashSet.contains_insert] at hw
simp only [Bool.or_eq_true, beq_iff_eq] at hw
rcases hw with rfl | hw
· exact Or.inr (List.mem_singleton_self _)
· exact Or.inl hw
· intro w hw
rw [List.mem_singleton] at hw
rw [hw]; exact hA0
· exact ⟨[], by simp⟩
· intro y hy _ _ _
exact Or.inl hy
/-- Completion of the DFS: from a state satisfying `DInv` with enough fuel
(`|stk| + |all| ≤ fuel + |M|`), `connDfs` empties its stack, and the invariant holds. -/
theorem dfs_complete {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{all : List Nat} {A0 : Std.HashSet Nat} {root : Nat}
(hall : ∀ e ∈ items, ∀ z ∈ e, z ∈ all)
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k []) :
∀ (f : Nat) (stk : List Nat) (A : Std.HashSet Nat) (M : List Nat),
DInv items all A0 root stk A M → stk.length + all.length ≤ f + M.length →
(connectorComponents.connDfs items inc f stk A M).1 = [] ∧
DInv items all A0 root (connectorComponents.connDfs items inc f stk A M).1
(connectorComponents.connDfs items inc f stk A M).2.1
(connectorComponents.connDfs items inc f stk A M).2.2 := by
intro f
induction f with
| zero =>
intro stk A M hD hpot
have hMl : M.length ≤ all.length :=
(List.subperm_of_subset hD.1.nodup (by intro w hw; exact hD.1.sub w hw)).length_le
have hstk : stk = [] := List.eq_nil_of_length_eq_zero (by omega)
subst hstk
rw [connectorComponents.connDfs]
exact ⟨rfl, hD⟩
| succ f ih =>
intro stk A M hD hpot
cases stk with
| nil =>
rw [connectorComponents.connDfs]
exact ⟨rfl, hD⟩
| cons y0 rest =>
have hpop := dfs_pop hall hinc hD
have key : connectorComponents.connDfs items inc (f + 1) (y0 :: rest) A M =
connectorComponents.connDfs items inc f
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).1
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).2.1
((inc.getD y0 []).foldl (dfsK items) (rest, A, M)).2.2 := by
rw [connectorComponents.connDfs]
try rfl
rw [key]
have hl := hpop.2
simp only [List.length_cons] at hpot
exact ih _ _ _ hpop.1 (by omega)
/-! ## The outer scan `connOuter` -/
/-- Invariant of the component list (newest first) and of the assigned set `A`: the
components are connected (from a root), closed under items, nodup, inside `all`,
pairwise disjoint, and `A` is exactly their union; `A` is closed under items. -/
structure OInv (items : List (List Nat)) (all : List Nat) (A : Std.HashSet Nat)
(comps : List (List Nat)) : Prop where
reach : ∀ c ∈ comps, ∃ r c', c = r :: c' ∧ ∀ w ∈ c, Reach items r w
nodup : ∀ c ∈ comps, c.Nodup
sub : ∀ c ∈ comps, ∀ y ∈ c, y ∈ all
closed : ∀ c ∈ comps, ∀ y ∈ c, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ c
disj : comps.Pairwise (fun c c' => ∀ y ∈ c, y ∉ c')
closedA : ∀ e ∈ items, (∃ z ∈ e, A.contains z = true) → ∀ z ∈ e, A.contains z = true
asgIff : ∀ w, A.contains w = true ↔ ∃ c ∈ comps, w ∈ c
theorem oinv_add {items : List (List Nat)} {all : List Nat} {A : Std.HashSet Nat}
{comps : List (List Nat)} {root : Nat} {Ad : Std.HashSet Nat} {M : List Nat}
(hO : OInv items all A comps) (hD : DInv items all A root [] Ad M) :
OInv items all Ad (M.reverse :: comps) := by
obtain ⟨hB, hpend⟩ := hD
obtain ⟨T, hT⟩ := hB.shape
have hrev : M.reverse = root :: T.reverse := by rw [hT]; simp
have hclM : ∀ y ∈ M, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ M := by
intro y hy e he hye z hz
rcases hpend y hy e he hye with h1 | h1
· simp at h1
· rcases hB.asgA0 z (h1 z hz) with h2 | h2
· exfalso
exact hB.freshA0 y hy (hO.closedA e he ⟨z, hz, h2⟩ y hye)
· exact h2
refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_⟩
· intro c hc
rcases List.mem_cons.1 hc with rfl | hc
· exact ⟨root, T.reverse, hrev, fun w hw => hB.reach w (List.mem_reverse.1 hw)⟩
· exact hO.reach c hc
· intro c hc
rcases List.mem_cons.1 hc with rfl | hc
· exact List.nodup_reverse.2 hB.nodup
· exact hO.nodup c hc
· intro c hc
rcases List.mem_cons.1 hc with rfl | hc
· exact fun y hy => hB.sub y (List.mem_reverse.1 hy)
· exact hO.sub c hc
· intro c hc y hy e he hye z hz
rcases List.mem_cons.1 hc with rfl | hc
· exact List.mem_reverse.2 (hclM y (List.mem_reverse.1 hy) e he hye z hz)
· exact hO.closed c hc y hy e he hye z hz
· refine List.pairwise_cons.2 ⟨fun c' hc' y hy hyc => ?_, hO.disj⟩
exact hB.freshA0 y (List.mem_reverse.1 hy) ((hO.asgIff y).2 ⟨c', hc', hyc⟩)
· intro e he ⟨z0, hz0, hA⟩
rcases hB.asgA0 z0 hA with h1 | h1
· intro z hz
exact hB.base z (hO.closedA e he ⟨z0, hz0, h1⟩ z hz)
· intro z hz
exact hB.asg z (hclM z0 h1 e he hz0 z hz)
· intro w
constructor
· intro hw
rcases hB.asgA0 w hw with h1 | h1
· obtain ⟨c, hc, hwc⟩ := (hO.asgIff w).1 h1
exact ⟨c, List.mem_cons_of_mem _ hc, hwc⟩
· exact ⟨M.reverse, List.mem_cons_self, List.mem_reverse.2 h1⟩
· rintro ⟨c, hc, hwc⟩
rcases List.mem_cons.1 hc with rfl | hc
· exact hB.asg w (List.mem_reverse.1 hwc)
· exact hB.base w ((hO.asgIff w).2 ⟨c, hc, hwc⟩)
theorem connOuter_spec {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{all : List Nat} {fuel : Nat}
(hall : ∀ e ∈ items, ∀ z ∈ e, z ∈ all)
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k [])
(hfuel : all.length ≤ fuel) :
∀ (qs : List Nat) (A : Std.HashSet Nat) (comps : List (List Nat)),
OInv items all A comps → (∀ y ∈ qs, y ∈ all) →
∃ (C : List (List Nat)) (A' : Std.HashSet Nat),
connectorComponents.connOuter items inc all fuel qs A comps = C.reverse ∧
OInv items all A' C ∧ (∀ c ∈ comps, c ∈ C) ∧ (∀ y ∈ qs, ∃ c ∈ C, y ∈ c) := by
intro qs
induction qs with
| nil =>
intro A comps hO _
refine ⟨comps, A, ?_, hO, fun c hc => hc, fun y hy => by simp at hy⟩
rw [connectorComponents.connOuter]
| cons x qs ih =>
intro A comps hO hqs
have hxall : x ∈ all := hqs x List.mem_cons_self
have hqs' : ∀ y ∈ qs, y ∈ all := fun y hy => hqs y (List.mem_cons_of_mem _ hy)
rw [connectorComponents.connOuter]
by_cases hx : A.contains x = true
· rw [if_pos hx]
obtain ⟨C, A', heq, hO', hsub, hcov⟩ := ih A comps hO hqs'
refine ⟨C, A', heq, hO', hsub, ?_⟩
intro y hy
rcases List.mem_cons.1 hy with rfl | hy
· obtain ⟨c, hc, hyc⟩ := (hO.asgIff y).1 hx
exact ⟨c, hsub c hc, hyc⟩
· exact hcov y hy
· have hx' : ¬ A.contains x = true := hx
rw [if_neg hx']
have hpot : [x].length + all.length ≤ fuel + [x].length := by simp; omega
obtain ⟨hempty, hDfin⟩ := dfs_complete hall hinc fuel [x] (A.insert x) [x]
(dinv_init A x hxall hx') hpot
generalize connectorComponents.connDfs items inc fuel [x] (A.insert x) [x] = r at hempty hDfin ⊢
have hxM : x ∈ r.2.2 := by
obtain ⟨T, hT⟩ := hDfin.1.shape
rw [hT]; simp
obtain ⟨C, A', heq, hO', hsub, hcov⟩ :=
ih r.2.1 (r.2.2.reverse :: comps) (oinv_add hO (by rw [hempty] at hDfin; exact hDfin)) hqs'
refine ⟨C, A', ?_, hO', fun c hc => hsub c (List.mem_cons_of_mem _ hc), ?_⟩
· dsimp only
rw [hempty]
simp only [List.isEmpty_nil, ite_true]
exact heq
· intro y hy
rcases List.mem_cons.1 hy with rfl | hy
· exact ⟨_, hsub _ List.mem_cons_self, List.mem_reverse.2 hxM⟩
· exact hcov y hy
/-! ## Pure list facts -/
theorem exists_getD {items : List (List Nat)} {e : List Nat} (he : e ∈ items) :
∃ k, items.getD k [] = e := by
obtain ⟨k, hk, rfl⟩ := List.mem_iff_getElem.1 he
exact ⟨k, by simp [List.getD_eq_getElem?_getD, hk]⟩
theorem add_cases (its : List Nat) (P : Nat → Bool) (z : Nat)
(hf : its.filter (fun w => !P w) = [z]) (hl : its.length = 2) :
z ∈ its ∧ P z = false ∧ (∀ w ∈ its, w = z ∨ P w = true) ∧ ∃ y ∈ its, P y = true := by
obtain ⟨a, b, rfl⟩ := List.length_eq_two.1 hl
cases ha : P a <;> cases hb : P b <;> simp_all [List.filter]
theorem discard_all (its : List Nat) (P : Nat → Bool) (hl : its.length ≤ 2)
(hex : ∃ y ∈ its, P y = true)
(hnot : ¬ ∃ z, its.filter (fun w => !P w) = [z] ∧ its.length = 2) :
∀ w ∈ its, P w = true := by
match its, hl, hex, hnot with
| [], _, ⟨y, hy, _⟩, _ => simp at hy
| [a], _, hex, hnot =>
obtain ⟨y, hy, hPy⟩ := hex
simp only [List.mem_singleton] at hy
subst hy
intro w hw
simp only [List.mem_singleton] at hw
subst hw
exact hPy
| [a, b], _, hex, hnot =>
intro w hw
by_cases hPa : P a = true <;> by_cases hPb : P b = true
· rcases List.mem_cons.1 hw with rfl | hw
· exact hPa
· rcases List.mem_cons.1 hw with rfl | hw
· exact hPb
· simp at hw
· exfalso
apply hnot
have hPb' : P b = false := by simpa using hPb
exact ⟨b, by simp [List.filter_cons, hPa, hPb'], rfl⟩
· exfalso
apply hnot
have hPa' : P a = false := by simpa using hPa
exact ⟨a, by simp [List.filter_cons, hPa', hPb], rfl⟩
· exfalso
obtain ⟨y, hy, hPy⟩ := hex
have hPa' : P a = false := by simpa using hPa
have hPb' : P b = false := by simpa using hPb
rcases List.mem_cons.1 hy with rfl | hy
· rw [hPa'] at hPy; exact Bool.false_ne_true hPy
· rcases List.mem_cons.1 hy with rfl | hy
· rw [hPb'] at hPy; exact Bool.false_ne_true hPy
· simp at hy
| _ :: _ :: _ :: _, hl, _, _ => simp at hl
/-! ## The greedy loop of `connectorGreedy` -/
theorem sumInc_le_of_sub (inc : Std.HashMap Nat (List Nat)) {l c : List Nat}
(hl : l.Nodup) (hsub : ∀ z ∈ l, z ∈ c) : sumInc l inc ≤ sumInc c inc := by
have hs : l.Subperm c := List.subperm_of_subset hl (by intro z hz; exact hsub z hz)
obtain ⟨m, hpm, hsl⟩ := hs
unfold sumInc
have hpe := (hpm.map (fun x => (inc.getD x []).length)).sum_eq
rw [← hpe]
exact (hsl.map (fun x => (inc.getD x []).length)).sum_le_sum (fun _ _ => Nat.zero_le _)
/-- Invariant of the greedy loop for the component `c` (`present` is the set of the
elements of `ordRev`; the output is `ordRev.reverse`). -/
structure GInv (items : List (List Nat)) (inc : Std.HashMap Nat (List Nat)) (c : List Nat)
(stk : List Nat) (P : Std.HashSet Nat) (ordRev : List Nat) : Prop where
mem : ∀ z, P.contains z = true ↔ z ∈ ordRev
nodup : ordRev.Nodup
subc : ∀ z ∈ ordRev, z ∈ c
pref : ∀ p, p ≤ ordRev.length → p ≤ cntIn items (ordRev.reverse.take p) + 1
pend : ∀ y, P.contains y = true → ∀ k, y ∈ items.getD k [] →
k ∈ stk ∨ ∀ z ∈ items.getD k [], P.contains z = true
stkItem : ∀ k ∈ stk, ∃ y ∈ items.getD k [], P.contains y = true
size : P.size = ordRev.length
theorem ginv_init {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)} {c : List Nat}
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k []) (start : Nat) (hstart : start ∈ c) :
GInv items inc c ((inc.getD start []).reverse) ((∅ : Std.HashSet Nat).insert start) [start] := by
refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_⟩
· intro z
rw [Std.HashSet.contains_insert]
simp only [Bool.or_eq_true, beq_iff_eq, Std.HashSet.contains_empty, Bool.false_eq_true,
or_false, List.mem_singleton]
exact eq_comm
· simp
· intro z hz
rw [List.mem_singleton] at hz
rw [hz]; exact hstart
· intro p hp
simp only [List.length_singleton] at hp
omega
· intro y hy k hk
rw [Std.HashSet.contains_insert] at hy
simp only [Bool.or_eq_true, beq_iff_eq, Std.HashSet.contains_empty, Bool.false_eq_true,
or_false] at hy
subst hy
exact Or.inl (List.mem_reverse.2 ((hinc k _).2 hk))
· intro k hk
refine ⟨start, ?_, ?_⟩
· exact (hinc k start).1 (List.mem_reverse.1 hk)
· rw [Std.HashSet.contains_insert]; simp
· rw [Std.HashSet.size_insert, if_neg (by simp), Std.HashSet.size_empty]
rfl
theorem ginv_add {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)} {c : List Nat}
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k [])
(hcl : ∀ y ∈ c, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ c)
{k : Nat} {rest : List Nat} {P : Std.HashSet Nat} {ordRev : List Nat} {z : Nat}
(hG : GInv items inc c (k :: rest) P ordRev)
(hf : (items.getD k []).filter (fun w => !P.contains w) = [z])
(hl : (items.getD k []).length = 2) :
GInv items inc c ((inc.getD z []).reverse ++ rest) (P.insert z) (z :: ordRev) := by
obtain ⟨hzI, hPz, hcases, hex⟩ := add_cases (items.getD k []) (fun w => P.contains w) z hf hl
replace hPz : P.contains z = false := hPz
have hitem : items.getD k [] ∈ items := getD_mem_of_mem hzI
have hzc : z ∈ c := by
obtain ⟨y, hyI, hPy⟩ := hex
have hyc : y ∈ c := hG.subc y ((hG.mem y).1 hPy)
exact hcl y hyc _ hitem hyI z hzI
have hznot : z ∉ ordRev := by
intro h
have := (hG.mem z).2 h
rw [hPz] at this
exact Bool.false_ne_true this
have hnotP : ¬ P.contains z = true := by rw [hPz]; decide
refine ⟨?_, ?_, ?_, ?_, ?_, ?_, ?_⟩
· intro w
rw [Std.HashSet.contains_insert]
simp only [Bool.or_eq_true, beq_iff_eq, List.mem_cons, hG.mem w]
constructor
· rintro (rfl | h)
· exact Or.inl rfl
· exact Or.inr h
· rintro (rfl | h)
· exact Or.inl rfl
· exact Or.inr h
· exact List.nodup_cons.2 ⟨hznot, hG.nodup⟩
· intro w hw
rcases List.mem_cons.1 hw with rfl | hw
· exact hzc
· exact hG.subc w hw
· intro p hp
have hLen : ordRev.reverse.length = ordRev.length := List.length_reverse
have hL : (z :: ordRev).reverse = ordRev.reverse ++ [z] := by simp
have hlt : cntIn items ordRev.reverse < cntIn items (ordRev.reverse ++ [z]) := by
apply cntIn_strict (e := items.getD k []) (fun w hw => List.mem_append_left _ hw) hitem
· intro w hw
rcases hcases w hw with rfl | hPw
· exact List.mem_append_right _ (List.mem_singleton_self _)
· exact List.mem_append_left _ (List.mem_reverse.2 ((hG.mem w).1 hPw))
· intro h
exact hznot (List.mem_reverse.1 (h z hzI))
rw [hL]
simp only [List.length_cons] at hp
by_cases hpo : p ≤ ordRev.length
· rw [List.take_append_of_le_length (by rw [hLen]; exact hpo)]
exact hG.pref p hpo
· have hp' : p = ordRev.length + 1 := by omega
rw [List.take_of_length_le (by simp; omega)]
have hG' := hG.pref ordRev.length le_rfl
rw [List.take_of_length_le (by simp)] at hG'
subst hp'
omega
· intro y hy k' hk'
rw [Std.HashSet.contains_insert] at hy
simp only [Bool.or_eq_true, beq_iff_eq] at hy
rcases hy with hyz | hy
· exact Or.inl (List.mem_append_left _ (List.mem_reverse.2
((hinc k' z).2 (by rw [hyz]; exact hk'))))
· rcases hG.pend y hy k' hk' with h | h
· rcases List.mem_cons.1 h with rfl | h
· right
intro w hw
rw [Std.HashSet.contains_insert]
rcases hcases w hw with rfl | hPw
· simp
· simp [hPw]
· exact Or.inl (List.mem_append_right _ h)
· right
intro w hw
rw [Std.HashSet.contains_insert]
simp [h w hw]
· intro k' hk'
rcases List.mem_append.1 hk' with h | h
· rw [List.mem_reverse] at h
refine ⟨z, (hinc k' z).1 h, ?_⟩
rw [Std.HashSet.contains_insert]; simp
· obtain ⟨y, hy, hPy⟩ := hG.stkItem k' (List.mem_cons_of_mem _ h)
refine ⟨y, hy, ?_⟩
rw [Std.HashSet.contains_insert]; simp [hPy]
· rw [Std.HashSet.size_insert, if_neg (by rw [Std.HashSet.mem_iff_contains]; exact hnotP)]
simp [hG.size]
theorem ginv_discard {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)} {c : List Nat}
(hlen : ∀ e ∈ items, e.length ≤ 2)
{k : Nat} {rest : List Nat} {P : Std.HashSet Nat} {ordRev : List Nat}
(hG : GInv items inc c (k :: rest) P ordRev)
(hnot : ¬ ∃ z, (items.getD k []).filter (fun w => !P.contains w) = [z] ∧
(items.getD k []).length = 2) :
GInv items inc c rest P ordRev := by
refine ⟨hG.mem, hG.nodup, hG.subc, hG.pref, ?_, ?_, hG.size⟩
· intro y hy k' hk'
rcases hG.pend y hy k' hk' with h | h
· rcases List.mem_cons.1 h with rfl | h
· right
obtain ⟨y0, hy0, hP0⟩ := hG.stkItem k' List.mem_cons_self
exact discard_all (items.getD k' []) (fun w => P.contains w)
(hlen _ (getD_mem_of_mem hk'))
⟨y0, hy0, hP0⟩ hnot
· exact Or.inl h
· exact Or.inr h
· intro k' hk'
obtain ⟨y, hy, hPy⟩ := hG.stkItem k' (List.mem_cons_of_mem _ hk')
exact ⟨y, hy, hPy⟩
theorem filter_full {c : List Nat} {P : Std.HashSet Nat} (h : ∀ z ∈ c, P.contains z = true) :
c.filter (fun z => !P.contains z) = [] :=
List.filter_eq_nil_iff.2 (fun a ha => by simp [h a ha])
theorem greedy_full {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{c : List Nat} {stk : List Nat} {P : Std.HashSet Nat} {ordRev : List Nat}
(hG : GInv items inc c stk P ordRev) (start : Nat) (hs : P.contains start = true)
(hconn : ∀ w ∈ c, Reach items start w) (hstk : stk = []) :
∀ z ∈ c, P.contains z = true := by
have hclosed : ∀ y, P.contains y = true → ∀ e ∈ items, y ∈ e → ∀ w ∈ e,
P.contains w = true := by
intro y hy e he hye w hw
obtain ⟨k, rfl⟩ := exists_getD he
rcases hG.pend y hy k hye with h | h
· rw [hstk] at h; simp at h
· exact h w hw
have hreach : ∀ w, Reach items start w → P.contains w = true := by
intro w hw
unfold Reach at hw
induction hw with
| refl => exact hs
| tail _ hadj ih =>
obtain ⟨e, he, hye, hwe⟩ := hadj
exact hclosed _ ih e he hye _ hwe
intro z hz
exact hreach z (hconn z hz)
theorem greedy_size {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{c : List Nat} {stk : List Nat} {P : Std.HashSet Nat} {ordRev : List Nat}
(hG : GInv items inc c stk P ordRev) (hsz : c.length ≤ P.size) :
∀ z ∈ c, P.contains z = true := by
have hsub : ordRev.Subperm c :=
List.subperm_of_subset hG.nodup (by intro z hz; exact hG.subc z hz)
have hperm : ordRev.Perm c := hsub.perm_of_length_le (by rw [hG.size] at hsz; exact hsz)
intro z hz
exact (hG.mem z).2 (hperm.symm.mem_iff.1 hz)
theorem greedy_loop_spec {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
{c : List Nat} (hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k [])
(hlen : ∀ e ∈ items, e.length ≤ 2)
(hcl : ∀ y ∈ c, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ c)
(hnodup : c.Nodup) (start : Nat) (hconn : ∀ w ∈ c, Reach items start w) :
∀ (f : Nat) (stk : List Nat) (P : Std.HashSet Nat) (ordRev : List Nat) (fl : Bool),
GInv items inc c stk P ordRev → P.contains start = true →
stk.length + sumInc c inc ≤ f + sumInc ordRev inc →
∃ (O : List Nat) (F : Bool),
connectorGreedy.loop items inc c
(fun present ordRev fl => (ordRev.reverse ++ c.filter (fun z => !present.contains z), fl))
f stk P ordRev fl = (O.reverse, F) ∧
O.Nodup ∧ (∀ z ∈ O, z ∈ c) ∧ (∀ z ∈ c, z ∈ O) ∧
(∀ p, p ≤ O.length → p ≤ cntIn items (O.reverse.take p) + 1) := by
intro f
induction f with
| zero =>
intro stk P ordRev fl hG hs hpot
have hsub := sumInc_le_of_sub inc hG.nodup hG.subc
have hstk : stk = [] := List.eq_nil_of_length_eq_zero (by omega)
have hfull := greedy_full hG start hs hconn hstk
refine ⟨ordRev, fl || decide (P.size < c.length), ?_, hG.nodup, hG.subc,
fun z hz => (hG.mem z).1 (hfull z hz), hG.pref⟩
rw [connectorGreedy.loop.eq_1]
try dsimp only
rw [filter_full hfull, List.append_nil]
| succ f ih =>
intro stk P ordRev fl hG hs hpot
cases stk with
| nil =>
have hfull := greedy_full hG start hs hconn rfl
refine ⟨ordRev, fl || decide (P.size < c.length), ?_, hG.nodup, hG.subc,
fun z hz => (hG.mem z).1 (hfull z hz), hG.pref⟩
rw [connectorGreedy.loop.eq_2]
try dsimp only
rw [filter_full hfull, List.append_nil]
| cons k rest =>
by_cases hsize : c.length ≤ P.size
· have hfull := greedy_size hG hsize
refine ⟨ordRev, fl, ?_, hG.nodup, hG.subc,
fun z hz => (hG.mem z).1 (hfull z hz), hG.pref⟩
rw [connectorGreedy.loop.eq_3, if_pos hsize]
try dsimp only
rw [filter_full hfull, List.append_nil]
· rw [connectorGreedy.loop.eq_3, if_neg hsize]
try dsimp only
split
· rename_i z hf hl
have hsz : sumInc (z :: ordRev) inc = (inc.getD z []).length + sumInc ordRev inc := by
unfold sumInc; simp [List.map_cons, List.sum_cons]
have hpot' : ((inc.getD z []).reverse ++ rest).length + sumInc c inc ≤
f + sumInc (z :: ordRev) inc := by
rw [hsz]
simp only [List.length_cons] at hpot
simp only [List.length_append, List.length_reverse]
omega
have hs' : (P.insert z).contains start = true := by
rw [Std.HashSet.contains_insert]; simp [hs]
exact ih _ _ _ fl (ginv_add hinc hcl hG hf hl) hs' hpot'
· rename_i hnot
have hnot' : ¬ ∃ z, (items.getD k []).filter (fun w => !P.contains w) = [z] ∧
(items.getD k []).length = 2 := by
rintro ⟨z, h1, h2⟩
exact hnot z h1 h2
have hpot' : rest.length + sumInc c inc ≤ f + sumInc ordRev inc := by
simp only [List.length_cons] at hpot
omega
exact ih _ _ _ fl (ginv_discard hlen hG hnot') hs hpot'
theorem greedy_out {items : List (List Nat)} {inc : Std.HashMap Nat (List Nat)}
(hinc : ∀ k y, k ∈ inc.getD y [] ↔ y ∈ items.getD k [])
(hlen : ∀ e ∈ items, e.length ≤ 2)
(start : Nat) (c' : List Nat) (hnodup : (start :: c').Nodup)
(hcl : ∀ y ∈ start :: c', ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ start :: c')
(hconn : ∀ w ∈ start :: c', Reach items start w)
(hsum : sumInc (start :: c') inc ≤ (items.map List.length).sum) :
(connectorGreedy items inc (start :: c')).1.Perm (start :: c') ∧
∀ p, p ≤ (connectorGreedy items inc (start :: c')).1.length →
p ≤ cntIn items ((connectorGreedy items inc (start :: c')).1.take p) + 1 := by
have hG := ginv_init (c := start :: c') hinc start List.mem_cons_self
have hpot : ((inc.getD start []).reverse).length + sumInc (start :: c') inc ≤
((items.map List.length).sum + 1) + sumInc [start] inc := by
have h1 : sumInc [start] inc = (inc.getD start []).length := by simp [sumInc]
simp only [List.length_reverse]
omega
obtain ⟨O, F, heq, hnd, hsubc, hcov, hpref⟩ :=
greedy_loop_spec hinc hlen hcl hnodup start hconn ((items.map List.length).sum + 1)
((inc.getD start []).reverse) ((∅ : Std.HashSet Nat).insert start) [start] false hG
(by simp [Std.HashSet.contains_insert]) hpot
have hres : connectorGreedy items inc (start :: c') = (O.reverse, F) := by
unfold connectorGreedy
exact heq
rw [hres]
dsimp only
refine ⟨?_, ?_⟩
· exact (List.perm_ext_iff_of_nodup (List.nodup_reverse.2 hnd) hnodup).2
(fun a => ⟨fun h => hsubc a (List.mem_reverse.1 h), fun h => List.mem_reverse.2 (hcov a h)⟩)
· intro p hp
rw [List.length_reverse] at hp
exact hpref p hp
/-! ## Components and excesses -/
theorem inner_getD (e : List Nat) (k x d : Nat) : ∀ (m : Std.HashMap Nat Nat),
(e.foldl (fun (m : Std.HashMap Nat Nat) y => m.insert y k) m).getD x d =
if x ∈ e then k else m.getD x d := by
induction e with
| nil => intro m; simp
| cons y e ih =>
intro m
rw [List.foldl_cons, ih]
by_cases hxy : y = x
· subst hxy
simp [Std.HashMap.getD_insert]
· have hne : (y == x) = false := by simpa using hxy
simp [Std.HashMap.getD_insert, hne, Ne.symm hxy]
theorem fold_idx_gen (j : Nat) (x d : Nat) : ∀ (L : List (List Nat × Nat)) (m : Std.HashMap Nat Nat),
(∀ p ∈ L, x ∈ p.1 → p.2 = j) → (m.getD x d = j ∨ ∃ p ∈ L, x ∈ p.1) →
(L.foldl (fun (m : Std.HashMap Nat Nat) (p : List Nat × Nat) =>
p.1.foldl (fun (m : Std.HashMap Nat Nat) y => m.insert y p.2) m) m).getD x d = j := by
intro L
induction L with
| nil =>
intro m _ h
rcases h with h | ⟨p, hp, _⟩
· simpa using h
· simp at hp
| cons p L ih =>
intro m hu h
rw [List.foldl_cons]
apply ih
· intro q hq hxq
exact hu q (List.mem_cons_of_mem _ hq) hxq
· by_cases hxp : x ∈ p.1
· left
rw [inner_getD, if_pos hxp]
exact hu p List.mem_cons_self hxp
· rw [inner_getD, if_neg hxp]
rcases h with h | ⟨q, hq, hxq⟩
· exact Or.inl h
· rcases List.mem_cons.1 hq with rfl | hq
· exact absurd hxq hxp
· exact Or.inr ⟨q, hq, hxq⟩
theorem fold_cnt (g : Nat → Nat) (i : Nat) : ∀ (hs : List Nat) (m : Std.HashMap Nat Nat),
(hs.foldl (fun (m : Std.HashMap Nat Nat) x => m.insert (g x) (m.getD (g x) 0 + 1)) m).getD i 0 =
m.getD i 0 + (hs.filter (fun x => g x == i)).length := by
intro hs
induction hs with
| nil => intro m; simp
| cons x hs ih =>
intro m
rw [List.foldl_cons, ih]
by_cases hgi : g x = i
· subst hgi
simp [Std.HashMap.getD_insert, List.filter_cons]
omega
· have hne : (g x == i) = false := by simpa using hgi
simp [Std.HashMap.getD_insert, hne, List.filter_cons, hgi]
theorem head_count (C : List Nat) (g : Nat → Nat) (i : Nat) : ∀ (L : List (List Nat)),
(∀ e ∈ L, e ≠ []) → (∀ e ∈ L, ∀ h, e.head? = some h → (g h == i) = subsetB e C) →
((L.filterMap List.head?).filter (fun x => g x == i)).length =
(L.filter (fun e => subsetB e C)).length := by
intro L
induction L with
| nil => simp
| cons e L ih =>
intro hne hc
have ih' := ih (fun e' he' => hne e' (List.mem_cons_of_mem _ he'))
(fun e' he' h hh => hc e' (List.mem_cons_of_mem _ he') h hh)
cases hh : e.head? with
| none =>
exfalso
cases e with
| nil => exact hne [] List.mem_cons_self rfl
| cons a t => simp at hh
| some h =>
have hcond := hc e List.mem_cons_self h hh
rw [List.filterMap_cons, hh, List.filter_cons, List.filter_cons, hcond]
by_cases hb : subsetB e C = true
· simp [hb, ih']
· simp [hb, ih']
/-- The map `x ↦ index of the component containing x` built by `connectorExcess`. -/
def idxMapL (comps : List (List Nat)) : Std.HashMap Nat Nat :=
comps.zipIdx.foldl (fun (m : Std.HashMap Nat Nat) (p : List Nat × Nat) =>
p.1.foldl (fun (m : Std.HashMap Nat Nat) y => m.insert y p.2) m) ∅
/-- The counts `cnt` built by `connectorExcess` (heads of the items, by component index). -/
def cntMapL (items : List (List Nat)) (comps : List (List Nat)) : Std.HashMap Nat Nat :=
(items.filterMap List.head?).foldl (fun (m : Std.HashMap Nat Nat) x =>
m.insert ((idxMapL comps).getD x 0) (m.getD ((idxMapL comps).getD x 0) 0 + 1)) ∅
theorem connectorExcess_eq (items : List (List Nat)) (comps : List (List Nat)) :
connectorExcess items comps = comps.zipIdx.map (fun p =>
((p.1.length : Nat) : ℤ) - (((cntMapL items comps).getD p.2 0 : Nat) : ℤ)) := rfl
/-- Index-form of `connectorExcess`: the component `c` has excess `|c| - #items in c`. -/
theorem excess_eq_gen (items : List (List Nat)) (comps : List (List Nat))
(hne : ∀ e ∈ items, e ≠ [])
(hdisj : comps.Pairwise (fun c c' => ∀ y ∈ c, y ∉ c'))
(hcov : ∀ e ∈ items, ∀ z ∈ e, ∃ c ∈ comps, z ∈ c)
(hclosed : ∀ c ∈ comps, ∀ y ∈ c, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ c) :
connectorExcess items comps =
comps.map (fun c => ((c.length : Nat) : ℤ) - ((cntIn items c : Nat) : ℤ)) := by
-- disjointness of components by index
have hdi : ∀ i j x, i ≠ j → x ∈ comps.getD i [] → x ∈ comps.getD j [] → False := by
intro i j x hij hi hj
rw [List.pairwise_iff_getElem] at hdisj
by_cases hi' : i < comps.length
· by_cases hj' : j < comps.length
· rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hi'] at hi
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hj'] at hj
simp only [Option.getD_some] at hi hj
rcases Nat.lt_or_gt_of_ne hij with h | h
· exact hdisj i j hi' hj' h x hi hj
· exact hdisj j i hj' hi' h x hj hi
· rw [List.getD_eq_getElem?_getD, List.getElem?_eq_none (by omega)] at hj
simp at hj
· rw [List.getD_eq_getElem?_getD, List.getElem?_eq_none (by omega)] at hi
simp at hi
-- the index map
have hidx : ∀ j, j < comps.length → ∀ x, x ∈ comps.getD j [] →
(idxMapL comps).getD x 0 = j := by
intro j hj x hx
unfold idxMapL
refine fold_idx_gen j x 0 comps.zipIdx ∅ ?_ (Or.inr ⟨(comps.getD j [], j), ?_, hx⟩)
· intro p hp hxp
have hp' : comps.getD p.2 [] = p.1 := by
rw [List.getD_eq_getElem?_getD, List.mem_zipIdx_iff_getElem?.1 hp]
simp
by_contra hne'
exact hdi p.2 j x hne' (by rw [hp']; exact hxp) hx
· rw [List.mem_zipIdx_iff_getElem?, List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hj]
simp
-- the counts
have hcomp_mem : ∀ j, j < comps.length → comps.getD j [] ∈ comps := by
intro j hj
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hj]
simpa using List.getElem_mem hj
have hcnt : ∀ j, j < comps.length →
(cntMapL items comps).getD j 0 = cntIn items (comps.getD j []) := by
intro j hj
have hC : comps.getD j [] ∈ comps := hcomp_mem j hj
have hhead : ∀ e ∈ items, ∀ h, e.head? = some h →
((idxMapL comps).getD h 0 == j) = subsetB e (comps.getD j []) := by
intro e he h hh
have hmem : h ∈ e := by
cases e with
| nil => simp at hh
| cons a t =>
simp only [List.head?_cons, Option.some.injEq] at hh
subst hh
exact List.mem_cons_self
apply Bool.eq_iff_iff.2
rw [subsetB_iff, beq_iff_eq]
constructor
· intro hidxj
obtain ⟨c, hc, hhc⟩ := hcov e he h hmem
obtain ⟨k, hk, hck⟩ := List.mem_iff_getElem.1 hc
have hget : comps.getD k [] = c := by
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hk]
simpa using hck
have hidxk : (idxMapL comps).getD h 0 = k := hidx k hk h (by rw [hget]; exact hhc)
have hkj : k = j := by rw [← hidxk]; exact hidxj
have hcC : h ∈ comps.getD j [] := by rw [← hkj, hget]; exact hhc
intro z hz
exact hclosed _ hC h hcC e he hmem z hz
· intro hsub
exact hidx j hj h (hsub h hmem)
have hne' : ∀ e ∈ items, e ≠ [] := hne
unfold cntMapL
rw [fold_cnt (fun x => (idxMapL comps).getD x 0) j _ ∅]
rw [Std.HashMap.getD_empty, zero_add, cntIn_eq_filter]
exact head_count (comps.getD j []) (fun x => (idxMapL comps).getD x 0) j items hne' hhead
rw [connectorExcess_eq]
conv_rhs => rw [← List.zipIdx_map_fst 0 comps]
rw [List.map_map]
apply List.map_congr_left
intro p hp
have hp1 := List.mem_zipIdx_iff_getElem?.1 hp
obtain ⟨hlt, _⟩ := List.getElem?_eq_some_iff.1 hp1
have hpc : p.1 = comps.getD p.2 [] := by
rw [List.getD_eq_getElem?_getD, hp1]
simp
show _ = ((p.1.length : Nat) : ℤ) - ((cntIn items p.1 : Nat) : ℤ)
rw [hcnt p.2 hlt, ← hpc]
/-! ## The main induction over the sorted blocks -/
/-- Excess of a block: `|C| - #items inside C`. -/
def exOf (items : List (List Nat)) (C : List Nat) : ℤ := (C.length : ℤ) - (cntIn items C : ℤ)
theorem max_aux (a : ℤ) (L : List ℤ) (h : ∀ x ∈ L, a ≤ x) :
a + max 0 L.sum ≤ max 0 (a + L.sum) := by
rcases le_total a 0 with ha | ha
· rcases le_total 0 L.sum with hs | hs
· rw [max_eq_right hs]
exact le_max_right _ _
· rw [max_eq_left hs]
linarith [le_max_left (0:ℤ) (a + L.sum)]
· have hS : 0 ≤ L.sum := List.sum_nonneg (fun x hx => le_trans ha (h x hx))
rw [max_eq_right hS, max_eq_right (by linarith : (0:ℤ) ≤ a + L.sum)]
theorem cntIn_nil {items : List (List Nat)} (hne : ∀ e ∈ items, e ≠ []) :
cntIn items [] = 0 := by
unfold cntIn
rw [List.countP_eq_zero]
intro e he h
rw [subsetB_iff] at h
obtain ⟨z, hz⟩ : ∃ z, z ∈ e := List.exists_mem_of_ne_nil _ (hne e he)
exact List.not_mem_nil (h z hz)
/-- Items inside a union of pairwise separated closed blocks: additivity. -/
theorem cntIn_flatten {items : List (List Nat)} (hne : ∀ e ∈ items, e ≠ []) :
∀ (bs : List (List Nat)),
(∀ C ∈ bs, ∀ e ∈ items, (∃ z ∈ e, z ∈ C) → ∀ w ∈ e, w ∈ C) →
bs.Pairwise (fun C D => ∀ y ∈ C, y ∉ D) →
cntIn items bs.flatten = (bs.map (cntIn items)).sum := by
intro bs
induction bs with
| nil => intro _ _; simp [cntIn_nil hne]
| cons B bs ih =>
intro hcl hdisj
have ih' := ih (fun C hC => hcl C (List.mem_cons_of_mem _ hC)) hdisj.of_cons
rw [List.flatten_cons, List.map_cons, List.sum_cons, ← ih']
apply cntIn_append hne
· intro x hx hxF
obtain ⟨l, hl, hxl⟩ := List.mem_flatten.1 hxF
exact (List.pairwise_cons.1 hdisj).1 l hl x hx hxl
· intro e he h
by_cases hz : ∃ z ∈ e, z ∈ B
· left
obtain ⟨z, hze, hzB⟩ := hz
exact fun w hw => hcl B List.mem_cons_self e he ⟨z, hze, hzB⟩ w hw
· right
intro w hw
rcases List.mem_append.1 (h w hw) with h1 | h1
· exact absurd ⟨w, hw, h1⟩ hz
· exact h1
/-- The main induction (`lem:connector-linear`, proof of Equation (8.3) (`eq:cubic-connector`)): along a
list of blocks sorted by excess, every prefix `P` of the output satisfies
`|P| - c(P) ≤ max 0 (total excess) + 1`. -/
theorem mc_ind (items : List (List Nat)) (hne : ∀ e ∈ items, e ≠ []) (g : List Nat → List Nat) :
∀ bs : List (List Nat),
(∀ C ∈ bs, (g C).Perm C ∧ (∀ p, p ≤ (g C).length → p ≤ cntIn items ((g C).take p) + 1) ∧
(∀ e ∈ items, (∃ z ∈ e, z ∈ C) → ∀ w ∈ e, w ∈ C)) →
bs.Pairwise (fun C D => ∀ y ∈ C, y ∉ D) →
bs.Pairwise (fun C D => exOf items C ≤ exOf items D) →
∀ j, j ≤ (bs.map g).flatten.length →
(j : ℤ) - (cntIn items ((bs.map g).flatten.take j) : ℤ) ≤
max 0 ((bs.map (exOf items)).sum) + 1 := by
intro bs
induction bs with
| nil =>
intro _ _ _ j hj
have hj0 : j = 0 := by simp at hj; exact hj
subst hj0
simp
| cons B bs ih =>
intro hgr hdisj hsorted j hj
obtain ⟨hperm, hpref, hclB⟩ := hgr B List.mem_cons_self
have ih' := ih (fun C hC => hgr C (List.mem_cons_of_mem _ hC)) hdisj.of_cons
hsorted.of_cons
have hhead : ∀ D ∈ bs, exOf items B ≤ exOf items D := (List.pairwise_cons.1 hsorted).1
have hF : ∀ x ∈ (bs.map g).flatten, ∃ D ∈ bs, x ∈ D := by
intro x hx
obtain ⟨l, hl, hxl⟩ := List.mem_flatten.1 hx
obtain ⟨D, hD, rfl⟩ := List.mem_map.1 hl
exact ⟨D, hD, (hgr D (List.mem_cons_of_mem _ hD)).1.mem_iff.1 hxl⟩
have hsumeq : (((B :: bs).map (exOf items)).sum) = exOf items B + (bs.map (exOf items)).sum := by
simp [List.map_cons, List.sum_cons]
rw [List.map_cons, List.flatten_cons] at hj ⊢
rw [hsumeq]
by_cases hj1 : j ≤ (g B).length
· rw [List.take_append_of_le_length hj1]
have h1 := hpref j hj1
have h2 : (j : ℤ) ≤ (cntIn items ((g B).take j) : ℤ) + 1 := by exact_mod_cast h1
have h3 : (0 : ℤ) ≤ max 0 (exOf items B + (bs.map (exOf items)).sum) := le_max_left _ _
linarith
· push_neg at hj1
set k := j - (g B).length with hk
have hjk : j = (g B).length + k := by omega
have hk_le : k ≤ ((bs.map g).flatten).length := by
rw [List.length_append] at hj; omega
have htake : ((g B) ++ (bs.map g).flatten).take j = (g B) ++ ((bs.map g).flatten).take k := by
rw [List.take_append, List.take_of_length_le (le_of_lt hj1), hk]
rw [htake]
have hdisjP : ∀ x ∈ g B, x ∉ ((bs.map g).flatten).take k := by
intro x hx hxT
obtain ⟨D, hD, hxD⟩ := hF x (List.mem_of_mem_take hxT)
have hxB : x ∈ B := (hperm.mem_iff).1 hx
exact (List.pairwise_cons.1 hdisj).1 D hD x hxB hxD
have hsplit : ∀ e ∈ items, (∀ z ∈ e, z ∈ g B ++ ((bs.map g).flatten).take k) →
(∀ z ∈ e, z ∈ g B) ∨ (∀ z ∈ e, z ∈ ((bs.map g).flatten).take k) := by
intro e he h
by_cases hz : ∃ z ∈ e, z ∈ g B
· left
obtain ⟨z, hze, hzg⟩ := hz
have hzB : z ∈ B := hperm.mem_iff.1 hzg
have hall := hclB e he ⟨z, hze, hzB⟩
intro w hw
rcases List.mem_append.1 (h w hw) with h1 | h1
· exact h1
· exfalso
obtain ⟨D, hD, hwD⟩ := hF w (List.mem_of_mem_take h1)
exact (List.pairwise_cons.1 hdisj).1 D hD w (hall w hw) hwD
· right
intro w hw
rcases List.mem_append.1 (h w hw) with h1 | h1
· exact absurd ⟨w, hw, h1⟩ hz
· exact h1
have hcnt := cntIn_append (items := items) (X := g B)
(Y := ((bs.map g).flatten).take k) hne hdisjP hsplit
rw [hcnt, cntIn_congr (fun z => hperm.mem_iff)]
have hIH := ih' k hk_le
have hlen : (g B).length = B.length := hperm.length_eq
have hA := max_aux (exOf items B) ((bs.map (exOf items))) (by
intro x hx
obtain ⟨D, hD, rfl⟩ := List.mem_map.1 hx
exact hhead D hD)
have hex : exOf items B = (B.length : ℤ) - (cntIn items B : ℤ) := rfl
have hmain : (j : ℤ) - ((cntIn items B + cntIn items (((bs.map g).flatten).take k) : Nat) : ℤ) =
exOf items B + ((k : ℤ) - (cntIn items (((bs.map g).flatten).take k) : ℤ)) := by
rw [hex, hjk, hlen]
push_cast
ring_nf
rw [hmain]
linarith [hA, hIH]
/-! ## Assembly -/
/-- The components of the connector (the list computed inside `connectorOrderF`). -/
def compsOf (Qs : List Nat) (items : List (List Nat)) : List (List Nat) :=
connectorComponents Qs items (connectorIncidence Qs items)
theorem compsOf_props {Qs : List Nat} {items : List (List Nat)}
(hitems : ∀ e ∈ items, ∀ z ∈ e, z ∈ Qs) :
(∀ c ∈ compsOf Qs items, ∃ r c', c = r :: c' ∧ ∀ w ∈ c, Reach items r w) ∧
(∀ c ∈ compsOf Qs items, c.Nodup) ∧
(∀ c ∈ compsOf Qs items, ∀ y ∈ c, y ∈ Qs) ∧
(∀ c ∈ compsOf Qs items, ∀ y ∈ c, ∀ e ∈ items, y ∈ e → ∀ z ∈ e, z ∈ c) ∧
(compsOf Qs items).Pairwise (fun c c' => ∀ y ∈ c, y ∉ c') ∧
(∀ x ∈ Qs, ∃ c ∈ compsOf Qs items, x ∈ c) := by
have hO0 : OInv items Qs (∅ : Std.HashSet Nat) [] :=
⟨by simp, by simp, by simp, by simp, List.Pairwise.nil,
fun e _ h => by obtain ⟨z, _, hz⟩ := h; simp at hz,
fun w => by simp⟩
have hinc : ∀ k y, k ∈ (connectorIncidence Qs items).getD y [] ↔ y ∈ items.getD k [] :=
fun k y => inc_mem Qs items y k
obtain ⟨C, A', heq, hO, hsub, hcov⟩ :=
connOuter_spec (fuel := Qs.length + (items.map List.length).sum + 1) hitems hinc
(by omega) Qs ∅ [] hO0 (fun y hy => hy)
have hcomps : compsOf Qs items = C.reverse := by
unfold compsOf connectorComponents
exact heq
rw [hcomps]
refine ⟨fun c hc => hO.reach c (List.mem_reverse.1 hc),
fun c hc => hO.nodup c (List.mem_reverse.1 hc),
fun c hc => hO.sub c (List.mem_reverse.1 hc),
fun c hc => hO.closed c (List.mem_reverse.1 hc), ?_, fun x hx => ?_⟩
· exact List.pairwise_reverse.2 (List.Pairwise.imp (fun h y hyb hya => h y hya hyb) hO.disj)
· obtain ⟨c, hc, hxc⟩ := hcov x hx
exact ⟨c, List.mem_reverse.2 hc, hxc⟩
theorem exOf_sum (items : List (List Nat)) : ∀ L : List (List Nat),
(L.map (exOf items)).sum = ((L.map List.length).sum : ℤ) - ((L.map (cntIn items)).sum : ℤ) := by
intro L
induction L with
| nil => simp
| cons C L ih =>
simp only [List.map_cons, List.sum_cons, ih, exOf]
push_cast
ring
theorem cntIn_all {items : List (List Nat)} {Qs : List Nat}
(h : ∀ e ∈ items, ∀ z ∈ e, z ∈ Qs) : cntIn items Qs = items.length := by
unfold cntIn
apply List.countP_eq_length.2
intro e he
rw [subsetB_iff]
exact fun z hz => h e he z hz
theorem connectorOrder_eq (Qs : List Nat) (items : List (List Nat)) :
connectorOrder Qs items =
((((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))).mergeSort
(fun a b => decide (a.2 ≤ b.2))).map
(fun p => (connectorGreedy items (connectorIncidence Qs items)
((compsOf Qs items).getD p.1 [])).1)).flatten := by
unfold connectorOrder connectorOrderF compsOf
dsimp only
rw [List.flatMap_map]
rfl
/-- Greedy order of one block. -/
def gOf (items : List (List Nat)) (Qs : List Nat) (C : List Nat) : List Nat :=
(connectorGreedy items (connectorIncidence Qs items) C).1
/-- The sorted list of (component index, excess). -/
def orderedOf (Qs : List Nat) (items : List (List Nat)) : List (Nat × Int) :=
((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))).mergeSort (fun a b => decide (a.2 ≤ b.2))
/-- The blocks of the connector, sorted by excess. -/
def blocksOf (Qs : List Nat) (items : List (List Nat)) : List (List Nat) :=
(orderedOf Qs items).map (fun p => (compsOf Qs items).getD p.1 [])
theorem connectorOrder_eq' (Qs : List Nat) (items : List (List Nat)) :
connectorOrder Qs items = ((blocksOf Qs items).map (gOf items Qs)).flatten := by
rw [connectorOrder_eq, blocksOf, List.map_map]
rfl
theorem connector_main (Qs : List Nat) (items : List (List Nat)) (hQ : Qs.Nodup)
(hitems : ∀ e ∈ items, e.Nodup ∧ 1 ≤ e.length ∧ e.length ≤ 2 ∧ ∀ z ∈ e, z ∈ Qs) :
(connectorOrder Qs items).Perm Qs ∧
∀ j, j ≤ Qs.length →
(j : ℤ) - ((items.filter fun e => subsetB e ((connectorOrder Qs items).take j)).length : ℤ)
≤ max 0 ((Qs.length : ℤ) - (items.length : ℤ)) + 1 := by
have hitQ : ∀ e ∈ items, ∀ z ∈ e, z ∈ Qs := fun e he z hz => (hitems e he).2.2.2 z hz
have hne : ∀ e ∈ items, e ≠ [] := by
intro e he h
have := (hitems e he).2.1
rw [h] at this
simp at this
have hlen2 : ∀ e ∈ items, e.length ≤ 2 := fun e he => (hitems e he).2.2.1
obtain ⟨hreach, hnodupC, hsubC, hclosedC, hdisjC, hcovC⟩ := compsOf_props hitQ
have hexc := excess_eq_gen items (compsOf Qs items) hne hdisjC
(fun e he z hz => hcovC z (hitQ e he z hz)) hclosedC
have hexc_len : (connectorExcess items (compsOf Qs items)).length = (compsOf Qs items).length := by
rw [hexc, List.length_map]
-- the blocks are a permutation of the components
have hnd_map : ((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))).map (fun p => (compsOf Qs items).getD p.1 []) =
compsOf Qs items := by
have h1 : ((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))).map Prod.fst =
List.range (compsOf Qs items).length :=
List.map_fst_zip (by simp [hexc_len])
calc _ = (((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))).map Prod.fst).map
(fun i => (compsOf Qs items).getD i []) := by
rw [List.map_map]; rfl
_ = (List.range (compsOf Qs items).length).map (fun i => (compsOf Qs items).getD i []) := by
rw [h1]
_ = compsOf Qs items := by
apply List.ext_getElem
· simp
· intro i h1 h2
simp [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem h2]
have hbl_perm : (blocksOf Qs items).Perm (compsOf Qs items) := by
have h := ((List.mergeSort_perm ((List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items))) (fun a b => decide (a.2 ≤ b.2))).map
(fun p => (compsOf Qs items).getD p.1 []))
rw [hnd_map] at h
exact h
have hbl_mem : ∀ C ∈ blocksOf Qs items, C ∈ compsOf Qs items :=
fun C hC => hbl_perm.mem_iff.1 hC
have hbl_disj : (blocksOf Qs items).Pairwise (fun C D => ∀ y ∈ C, y ∉ D) :=
(List.Perm.pairwise_iff (fun {x y} h z hz hzx => h z hzx hz) hbl_perm).2 hdisjC
-- excesses
have hexc_getD : ∀ k, (connectorExcess items (compsOf Qs items)).getD k 0 =
exOf items ((compsOf Qs items).getD k []) := by
intro k
rw [hexc]
by_cases hk : k < (compsOf Qs items).length
· rw [List.getD_eq_getElem?_getD, List.getElem?_map, List.getD_eq_getElem?_getD,
List.getElem?_eq_getElem hk]
simp [exOf]
· rw [List.getD_eq_getElem?_getD, List.getElem?_map, List.getD_eq_getElem?_getD,
List.getElem?_eq_none (by omega)]
simp [exOf, cntIn_nil hne]
have hval : ∀ p ∈ orderedOf Qs items, exOf items ((compsOf Qs items).getD p.1 []) = p.2 := by
intro p hp
have hp' : p ∈ (List.range (compsOf Qs items).length).zip
(connectorExcess items (compsOf Qs items)) := (List.mergeSort_perm _ _).mem_iff.1 hp
obtain ⟨k, hk, hkp⟩ := List.mem_iff_getElem.1 hp'
have hkn : k < (connectorExcess items (compsOf Qs items)).length := by
have h2 := hk
rw [List.length_zip, List.length_range] at h2
omega
have e1 : p.1 = k := by rw [← hkp]; simp
have e2 : p.2 = (connectorExcess items (compsOf Qs items)).getD k 0 := by
rw [List.getD_eq_getElem?_getD, List.getElem?_eq_getElem hkn, ← hkp]
simp
rw [e1, e2, hexc_getD]
have hsort : (orderedOf Qs items).Pairwise (fun a b => decide (a.2 ≤ b.2) = true) := by
apply List.pairwise_mergeSort
· intro a b c h1 h2
simp only [decide_eq_true_eq] at h1 h2 ⊢
exact le_trans h1 h2
· intro a b
simp only [Bool.or_eq_true, decide_eq_true_eq]
exact le_total _ _
have hbl_sorted : (blocksOf Qs items).Pairwise (fun C D => exOf items C ≤ exOf items D) := by
unfold blocksOf
rw [List.pairwise_map]
exact hsort.imp_of_mem (fun {a b} ha hb hab => by
show exOf items ((compsOf Qs items).getD a.1 []) ≤ exOf items ((compsOf Qs items).getD b.1 [])
rw [hval a ha, hval b hb]
simpa using hab)
-- blocks: greedy orders
have hblock : ∀ C ∈ compsOf Qs items, (gOf items Qs C).Perm C ∧
∀ p, p ≤ (gOf items Qs C).length → p ≤ cntIn items ((gOf items Qs C).take p) + 1 := by
intro C hC
obtain ⟨r, c', hCeq, hr⟩ := hreach C hC
subst hCeq
have hsum : sumInc (r :: c') (connectorIncidence Qs items) ≤ (items.map List.length).sum :=
sumInc_incidence Qs items (r :: c') (hnodupC _ hC)
exact greedy_out (fun k y => inc_mem Qs items y k) hlen2 r c' (hnodupC _ hC)
(hclosedC _ hC) hr hsum
have hgr : ∀ C ∈ blocksOf Qs items, (gOf items Qs C).Perm C ∧
(∀ p, p ≤ (gOf items Qs C).length → p ≤ cntIn items ((gOf items Qs C).take p) + 1) ∧
(∀ e ∈ items, (∃ z ∈ e, z ∈ C) → ∀ w ∈ e, w ∈ C) := by
intro C hC
obtain ⟨h1, h2⟩ := hblock C (hbl_mem C hC)
refine ⟨h1, h2, fun e he hex w hw => ?_⟩
obtain ⟨z, hz, hzC⟩ := hex
exact hclosedC C (hbl_mem C hC) z hzC e he hz w hw
have hmc := mc_ind items hne (gOf items Qs) (blocksOf Qs items) hgr hbl_disj hbl_sorted
-- the output is a permutation of Qs
have hnd : ((blocksOf Qs items).map (gOf items Qs)).flatten.Nodup := by
rw [List.nodup_flatten]
constructor
· intro l hl
obtain ⟨C, hC, rfl⟩ := List.mem_map.1 hl
exact (hblock C (hbl_mem C hC)).1.nodup_iff.2 (hnodupC C (hbl_mem C hC))
· rw [List.pairwise_map]
exact hbl_disj.imp_of_mem (fun {C D} hC hD hCD => by
intro a ha hb
exact hCD a ((hblock C (hbl_mem C hC)).1.mem_iff.1 ha)
((hblock D (hbl_mem D hD)).1.mem_iff.1 hb))
have hPerm : (connectorOrder Qs items).Perm Qs := by
rw [connectorOrder_eq']
exact (List.perm_ext_iff_of_nodup hnd hQ).2 (fun x => by
rw [← connectorOrder_eq']
exact connectorOrderMem Qs items hitQ x)
have hlenFlat : ((blocksOf Qs items).map (gOf items Qs)).flatten.length = Qs.length := by
rw [← connectorOrder_eq']
exact hPerm.length_eq
have hlenBl : ((blocksOf Qs items).map List.length).sum = Qs.length := by
rw [← hlenFlat, List.length_flatten, List.map_map]
congr 1
apply List.map_congr_left
intro C hC
exact ((hblock C (hbl_mem C hC)).1.length_eq).symm
-- sums of excesses
have hcntFlat : cntIn items (blocksOf Qs items).flatten = cntIn items Qs := by
apply cntIn_congr
intro z
rw [List.mem_flatten]
constructor
· rintro ⟨C, hC, hzC⟩
exact hsubC C (hbl_mem C hC) z hzC
· intro hz
obtain ⟨c, hc, hzc⟩ := hcovC z hz
exact ⟨c, hbl_perm.symm.mem_iff.1 hc, hzc⟩
have hsumEx : ((blocksOf Qs items).map (exOf items)).sum =
(Qs.length : ℤ) - (items.length : ℤ) := by
rw [exOf_sum, hlenBl]
have hc1 : ((blocksOf Qs items).map (cntIn items)).sum = items.length := by
rw [← cntIn_flatten hne (blocksOf Qs items) (fun C hC => (hgr C hC).2.2) hbl_disj,
hcntFlat, cntIn_all hitQ]
rw [hc1]
-- conclusion
refine ⟨hPerm, fun j hj => ?_⟩
rw [connectorOrder_eq', ← cntIn_eq_filter]
have hjl : j ≤ ((blocksOf Qs items).map (gOf items Qs)).flatten.length := by
rw [hlenFlat]; exact hj
have h := hmc j hjl
rw [hsumEx] at h
exact h
end L4
theorem connectorLemma10 : ConnectorLemma10 := by
intro Qs items hQ hitems
exact L4.connector_main Qs items hQ hitems
end CubicPathwidth.Alg
#print axioms CubicPathwidth.Alg.connectorLemma10
Back to top