module public import Langlib.Classes.DeterministicContextFree.Closure.Union public import Langlib.Utilities.ClosurePredicates /-! # Deterministic Context-Free Languages Are Not Closed Under Homomorphism If deterministic context-free languages were closed under arbitrary string homomorphisms, then erasing the first symbol of a prefix-marked disjoint union would make them closed under union. -/ open PDA namespace DCFHomomorphism variable {T : Type} [Fintype T] private inductive MarkState (Q₁ Q₂ : Type) where | start | left : Q₁ → MarkState Q₁ Q₂ | right : Q₂ → MarkState Q₁ Q₂ deriving Fintype private inductive MarkStack (S₁ S₂ : Type) where | bottom | left : S₁ → MarkStack S₁ S₂ | right : S₂ → MarkStack S₁ S₂ deriving Fintype namespace DPDA variable {Q₁ Q₂ S₁ S₂ : Type} variable [Fintype Q₁] [Fintype Q₂] [Fintype S₁] [Fintype S₂] public abbrev LeftQuotientState (Q : Type) := Bool × Q public def leftQuotientSingleton {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) : DPDA (LeftQuotientState Q) A S where initial_state := (false, M.initial_state) start_symbol := M.start_symbol final_states := {q | q.1 = true ∧ q.2 ∈ M.final_states} transition q x Z := match q with | (true, q) => (M.transition q x Z).map fun p => ((true, p.1), p.2) | (false, _) => none epsilon_transition q Z := match q with | (true, q) => (M.epsilon_transition q Z).map fun p => ((true, p.1), p.2) | (false, q) => match M.epsilon_transition q Z with | some p => some ((false, p.1), p.2) | none => (M.transition q a Z).map fun p => ((true, p.1), p.2) no_mixed := by rintro ⟨mode, q⟩ Z hε x cases mode <;> simp [LeftQuotientState] at hε ⊢ exact M.no_mixed q Z (by simpa using hε) x private def leftQuotientConf {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) : PDA.conf (leftQuotientSingleton M a).toPDA → PDA.conf M.toPDA | ⟨(false, q), input, stack⟩ => ⟨q, a :: input, stack⟩ | ⟨(true, q), input, stack⟩ => ⟨q, input, stack⟩ private lemma leftQuotient_step_sound {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) {c c' : PDA.conf (leftQuotientSingleton M a).toPDA} (hstep : PDA.Reaches₁ c c') : PDA.Reaches (leftQuotientConf M a c) (leftQuotientConf M a c') := by rcases c with ⟨⟨mode, q⟩, input, stack⟩ cases stack with | nil => simp [PDA.Reaches₁, PDA.step] at hstep | cons Z γ => cases mode · cases input with | nil => simp [PDA.Reaches₁, PDA.step, leftQuotientSingleton, DPDA.toPDA] at hstep cases hε : M.epsilon_transition q Z with | some eps => rcases hstep with hstep | hbad · rcases hstep with ⟨p, β, hp, rfl⟩ have hp' : ((false, p), β) = ((false, eps.1), eps.2) := by simpa [hε] using hp injection hp' with hpq hβ cases hpq cases hβ exact Relation.ReflTransGen.single (Set.mem_union_right _ ⟨eps.1, eps.2, by simp [DPDA.toPDA, hε], rfl⟩) · simp [hε] at hbad | none => cases ht : M.transition q a Z with | none => simp [hε, ht] at hstep | some tr => simp [hε, ht] at hstep rcases hstep with ⟨β, rfl, rfl⟩ exact Relation.ReflTransGen.single (Set.mem_union_left _ ⟨tr.1, tr.2, by simp [DPDA.toPDA, ht], rfl⟩) | cons x xs => simp [PDA.Reaches₁, PDA.step, leftQuotientSingleton, DPDA.toPDA] at hstep cases hε : M.epsilon_transition q Z with | some eps => rcases hstep with hstep | hbad · rcases hstep with ⟨p, β, hp, rfl⟩ have hp' : ((false, p), β) = ((false, eps.1), eps.2) := by simpa [hε] using hp injection hp' with hpq hβ cases hpq cases hβ exact Relation.ReflTransGen.single (Set.mem_union_right _ ⟨eps.1, eps.2, by simp [DPDA.toPDA, hε], rfl⟩) · simp [hε] at hbad | none => cases ht : M.transition q a Z with | none => simp [hε, ht] at hstep | some tr => simp [hε, ht] at hstep rcases hstep with ⟨β, rfl, rfl⟩ exact Relation.ReflTransGen.single (Set.mem_union_left _ ⟨tr.1, tr.2, by simp [DPDA.toPDA, ht], rfl⟩) · cases input with | nil => simp [PDA.Reaches₁, PDA.step, leftQuotientSingleton, DPDA.toPDA] at hstep cases hε : M.epsilon_transition q Z with | none => simp [hε] at hstep | some eps => simp [hε] at hstep rcases hstep with ⟨β, rfl, rfl⟩ exact Relation.ReflTransGen.single ⟨eps.1, eps.2, by simp [DPDA.toPDA, hε], rfl⟩ | cons x xs => simp [PDA.Reaches₁, PDA.step, leftQuotientSingleton, DPDA.toPDA] at hstep rcases hstep with hstep | hstep · cases ht : M.transition q x Z with | none => simp [ht] at hstep | some tr => simp [ht] at hstep rcases hstep with ⟨β, rfl, rfl⟩ exact Relation.ReflTransGen.single (Set.mem_union_left _ ⟨tr.1, tr.2, by simp [DPDA.toPDA, ht], rfl⟩) · cases hε : M.epsilon_transition q Z with | none => simp [hε] at hstep | some eps => simp [hε] at hstep rcases hstep with ⟨β, rfl, rfl⟩ exact Relation.ReflTransGen.single (Set.mem_union_right _ ⟨eps.1, eps.2, by simp [DPDA.toPDA, hε], rfl⟩) private lemma leftQuotient_reaches_sound {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) {c c' : PDA.conf (leftQuotientSingleton M a).toPDA} (hreach : PDA.Reaches c c') : PDA.Reaches (leftQuotientConf M a c) (leftQuotientConf M a c') := by induction hreach with | refl => exact Relation.ReflTransGen.refl | tail _ hstep ih => exact Relation.ReflTransGen.trans ih (leftQuotient_step_sound M a hstep) private lemma leftQuotient_step_complete {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) {c : PDA.conf (leftQuotientSingleton M a).toPDA} {d' : PDA.conf M.toPDA} (hstep : PDA.Reaches₁ (leftQuotientConf M a c) d') : ∃ c' : PDA.conf (leftQuotientSingleton M a).toPDA, PDA.Reaches c c' ∧ leftQuotientConf M a c' = d' := by rcases c with ⟨⟨mode, q⟩, input, stack⟩ cases stack with | nil => cases mode <;> simp [PDA.Reaches₁, PDA.step, leftQuotientConf] at hstep | cons Z γ => cases mode · cases input with | nil => simp [PDA.Reaches₁, PDA.step, leftQuotientConf, DPDA.toPDA] at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | some eps => have hnone := M.no_mixed q Z (by simp [hε]) a simp [hnone] at hp | none => cases ht : M.transition q a Z with | none => simp [ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(true, p), [], β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step refine ⟨(true, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε, ht]) · rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | none => simp [hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(false, p), [], β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step refine ⟨(false, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε]) | cons x xs => simp [PDA.Reaches₁, PDA.step, leftQuotientConf, DPDA.toPDA] at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | some eps => have hnone := M.no_mixed q Z (by simp [hε]) a simp [hnone] at hp | none => cases ht : M.transition q a Z with | none => simp [ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(true, p), x :: xs, β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step right refine ⟨(true, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε, ht]) · rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | none => simp [hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(false, p), x :: xs, β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step right refine ⟨(false, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε]) · cases input with | nil => simp [PDA.Reaches₁, PDA.step, leftQuotientConf, DPDA.toPDA] at hstep rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | none => simp [hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(true, p), [], β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step refine ⟨(true, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε]) | cons x xs => simp [PDA.Reaches₁, PDA.step, leftQuotientConf, DPDA.toPDA] at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, rfl⟩ cases ht : M.transition q x Z with | none => simp [ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(true, p), xs, β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step left refine ⟨(true, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, ht]) · rcases hstep with ⟨p, β, hp, rfl⟩ cases hε : M.epsilon_transition q Z with | none => simp [hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨(true, p), x :: xs, β ++ γ⟩, ?_, rfl⟩ exact Relation.ReflTransGen.single (by unfold PDA.Reaches₁ PDA.step right refine ⟨(true, p), β, ?_, rfl⟩ simp [leftQuotientSingleton, DPDA.toPDA, hε]) private lemma leftQuotient_reaches_complete_aux {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) {d d' : PDA.conf M.toPDA} (hreach : PDA.Reaches d d') : ∀ c : PDA.conf (leftQuotientSingleton M a).toPDA, leftQuotientConf M a c = d → ∃ c' : PDA.conf (leftQuotientSingleton M a).toPDA, PDA.Reaches c c' ∧ leftQuotientConf M a c' = d' := by induction hreach with | refl => intro c hsrc exact ⟨c, Relation.ReflTransGen.refl, hsrc⟩ | tail _ hstep ih => intro c hsrc rcases ih c hsrc with ⟨cMid, hMid, hmap⟩ rcases leftQuotient_step_complete M a (c := cMid) (by rw [hmap] exact hstep) with ⟨cEnd, hEnd, hmapEnd⟩ exact ⟨cEnd, Relation.ReflTransGen.trans hMid hEnd, hmapEnd⟩ private lemma leftQuotient_reaches_complete {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) {c : PDA.conf (leftQuotientSingleton M a).toPDA} {d' : PDA.conf M.toPDA} (hreach : PDA.Reaches (leftQuotientConf M a c) d') : ∃ c' : PDA.conf (leftQuotientSingleton M a).toPDA, PDA.Reaches c c' ∧ leftQuotientConf M a c' = d' := leftQuotient_reaches_complete_aux M a hreach c rfl public theorem leftQuotientSingleton_accepts {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) : (leftQuotientSingleton M a).acceptsByFinalState = [a] \\ M.acceptsByFinalState := by ext w constructor · rintro ⟨q, hq, γ, hreach⟩ rcases q with ⟨mode, q⟩ rcases hq with ⟨hmode, hq⟩ simp at hmode subst mode change a :: w ∈ M.acceptsByFinalState exact ⟨q, hq, γ, leftQuotient_reaches_sound M a hreach⟩ · intro hw change a :: w ∈ M.acceptsByFinalState at hw rcases hw with ⟨q, hq, γ, hreach⟩ rcases leftQuotient_reaches_complete M a (c := ⟨(false, M.initial_state), w, [M.start_symbol]⟩) (d' := ⟨q, [], γ⟩) hreach with ⟨c, hc, hmap⟩ rcases c with ⟨⟨mode, q'⟩, input, stack⟩ cases mode · simp [leftQuotientConf] at hmap · simp [leftQuotientConf] at hmap rcases hmap with ⟨rfl, rfl, rfl⟩ exact ⟨(true, q'), by constructor · rfl · change q' ∈ M.final_states at hq exact hq, stack, hc⟩ public theorem is_DCF_leftQuotient_singleton {Q A S : Type} [Fintype Q] [Fintype A] [Fintype S] (M : DPDA Q A S) (a : A) : is_DCF ([a] \\ M.acceptsByFinalState) := ⟨_, _, inferInstance, inferInstance, leftQuotientSingleton M a, leftQuotientSingleton_accepts M a⟩ private def markTransitionLeft (M : DPDA Q₁ T S₁) (q : Q₁) (a : T) (Z : S₁) : Option (MarkState Q₁ Q₂ × List (MarkStack S₁ S₂)) := match M.transition q a Z with | none => none | some (q', γ) => some (MarkState.left q', γ.map MarkStack.left) private def markTransitionRight (M : DPDA Q₂ T S₂) (q : Q₂) (a : T) (Z : S₂) : Option (MarkState Q₁ Q₂ × List (MarkStack S₁ S₂)) := match M.transition q a Z with | none => none | some (q', γ) => some (MarkState.right q', γ.map MarkStack.right) private def markEpsilonLeft (M : DPDA Q₁ T S₁) (q : Q₁) (Z : S₁) : Option (MarkState Q₁ Q₂ × List (MarkStack S₁ S₂)) := match M.epsilon_transition q Z with | none => none | some (q', γ) => some (MarkState.left q', γ.map MarkStack.left) private def markEpsilonRight (M : DPDA Q₂ T S₂) (q : Q₂) (Z : S₂) : Option (MarkState Q₁ Q₂ × List (MarkStack S₁ S₂)) := match M.epsilon_transition q Z with | none => none | some (q', γ) => some (MarkState.right q', γ.map MarkStack.right) private def markedUnion (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : DPDA (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) where initial_state := MarkState.start start_symbol := MarkStack.bottom final_states := {q | (∃ q₁ ∈ M₁.final_states, q = MarkState.left q₁) ∨ (∃ q₂ ∈ M₂.final_states, q = MarkState.right q₂)} transition q a Z := match q, a, Z with | MarkState.start, Sum.inl false, MarkStack.bottom => some (MarkState.left M₁.initial_state, [MarkStack.left M₁.start_symbol]) | MarkState.start, Sum.inl true, MarkStack.bottom => some (MarkState.right M₂.initial_state, [MarkStack.right M₂.start_symbol]) | MarkState.left q₁, Sum.inr a, MarkStack.left Z₁ => markTransitionLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ a Z₁ | MarkState.right q₂, Sum.inr a, MarkStack.right Z₂ => markTransitionRight (Q₁ := Q₁) (S₁ := S₁) M₂ q₂ a Z₂ | _, _, _ => none epsilon_transition q Z := match q, Z with | MarkState.left q₁, MarkStack.left Z₁ => markEpsilonLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ Z₁ | MarkState.right q₂, MarkStack.right Z₂ => markEpsilonRight (Q₁ := Q₁) (S₁ := S₁) M₂ q₂ Z₂ | _, _ => none no_mixed := by intro q Z hε a cases q <;> cases Z <;> cases a <;> simp [markEpsilonLeft, markEpsilonRight, markTransitionLeft, markTransitionRight] at hε ⊢ · rename_i q₁ Z₁ t split at hε · exact False.elim (hε rfl) · rename_i q' γ hε₁ have hnone := M₁.no_mixed q₁ Z₁ (by simp [hε₁]) t simp [hnone] · rename_i q₂ Z₂ t split at hε · exact False.elim (hε rfl) · rename_i q' γ hε₁ have hnone := M₂.no_mixed q₂ Z₂ (by simp [hε₁]) t simp [hnone] private def leftConf (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : PDA.conf M₁.toPDA → PDA.conf (markedUnion M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨MarkState.left q, w.map Sum.inr, γ.map MarkStack.left⟩ private def rightConf (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : PDA.conf M₂.toPDA → PDA.conf (markedUnion M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨MarkState.right q, w.map Sum.inr, γ.map MarkStack.right⟩ private lemma left_reaches₁_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c c' : PDA.conf M₁.toPDA} : PDA.Reaches₁ c c' → @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (leftConf M₁ M₂ c) (leftConf M₁ M₂ c') := by intro h rcases c with ⟨q, w, γ⟩ rcases c' with ⟨q', w', γ'⟩ cases γ with | nil => have h' : @PDA.ReachesIn Q₁ T S₁ _ _ _ M₁.toPDA 1 ⟨q, w, []⟩ ⟨q', w', γ'⟩ := (PDA.reaches₁_iff_reachesIn_one).mp h exact False.elim (PDA.reachesIn_one_on_empty_stack h') | cons Z γ => cases w with | nil => unfold PDA.Reaches₁ PDA.step at h ⊢ rcases h with ⟨p, β, hp, hcfg⟩ refine ⟨MarkState.left p, β.map MarkStack.left, ?_, ?_⟩ · cases hε : M₁.epsilon_transition q Z with | none => simp [DPDA.toPDA, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, hε] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] · simpa [leftConf, List.map_append] using congrArg (leftConf M₁ M₂) hcfg | cons a w => unfold PDA.Reaches₁ PDA.step at h ⊢ rcases h with h | h · rcases h with ⟨p, β, hp, hcfg⟩ left refine ⟨MarkState.left p, β.map MarkStack.left, ?_, ?_⟩ · cases ht : M₁.transition q a Z with | none => simp [DPDA.toPDA, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, ht] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markTransitionLeft, ht] · simpa [leftConf, List.map_append] using congrArg (leftConf M₁ M₂) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨MarkState.left p, β.map MarkStack.left, ?_, ?_⟩ · cases hε : M₁.epsilon_transition q Z with | none => simp [DPDA.toPDA, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, hε] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] · simpa [leftConf, List.map_append] using congrArg (leftConf M₁ M₂) hcfg private lemma left_reaches_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c c' : PDA.conf M₁.toPDA} : PDA.Reaches c c' → @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (leftConf M₁ M₂ c) (leftConf M₁ M₂ c') := by intro h induction h with | refl => rfl | tail _ hstep ih => exact Relation.ReflTransGen.tail ih (left_reaches₁_map M₁ M₂ hstep) private lemma right_reaches₁_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c c' : PDA.conf M₂.toPDA} : PDA.Reaches₁ c c' → @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (rightConf M₁ M₂ c) (rightConf M₁ M₂ c') := by intro h rcases c with ⟨q, w, γ⟩ rcases c' with ⟨q', w', γ'⟩ cases γ with | nil => have h' : @PDA.ReachesIn Q₂ T S₂ _ _ _ M₂.toPDA 1 ⟨q, w, []⟩ ⟨q', w', γ'⟩ := (PDA.reaches₁_iff_reachesIn_one).mp h exact False.elim (PDA.reachesIn_one_on_empty_stack h') | cons Z γ => cases w with | nil => unfold PDA.Reaches₁ PDA.step at h ⊢ rcases h with ⟨p, β, hp, hcfg⟩ refine ⟨MarkState.right p, β.map MarkStack.right, ?_, ?_⟩ · cases hε : M₂.epsilon_transition q Z with | none => simp [DPDA.toPDA, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, hε] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] · simpa [rightConf, List.map_append] using congrArg (rightConf M₁ M₂) hcfg | cons a w => unfold PDA.Reaches₁ PDA.step at h ⊢ rcases h with h | h · rcases h with ⟨p, β, hp, hcfg⟩ left refine ⟨MarkState.right p, β.map MarkStack.right, ?_, ?_⟩ · cases ht : M₂.transition q a Z with | none => simp [DPDA.toPDA, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, ht] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markTransitionRight, ht] · simpa [rightConf, List.map_append] using congrArg (rightConf M₁ M₂) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨MarkState.right p, β.map MarkStack.right, ?_, ?_⟩ · cases hε : M₂.epsilon_transition q Z with | none => simp [DPDA.toPDA, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, hε] at hp rcases hp with ⟨rfl, rfl⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] · simpa [rightConf, List.map_append] using congrArg (rightConf M₁ M₂) hcfg private lemma right_reaches_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c c' : PDA.conf M₂.toPDA} : PDA.Reaches c c' → @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (rightConf M₁ M₂ c) (rightConf M₁ M₂ c') := by intro h induction h with | refl => rfl | tail _ hstep ih => exact Relation.ReflTransGen.tail ih (right_reaches₁_map M₁ M₂ hstep) private lemma left_reaches₁_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₁.toPDA} {d : PDA.conf (markedUnion M₁ M₂).toPDA} : @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (leftConf M₁ M₂ c) d → ∃ c' : PDA.conf M₁.toPDA, d = leftConf M₁ M₂ c' ∧ PDA.Reaches₁ c c' := by intro h rcases c with ⟨q, w, γ⟩ rcases d with ⟨qd, wd, γd⟩ cases γ with | nil => have h' : @PDA.ReachesIn (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA 1 (leftConf M₁ M₂ ⟨q, w, []⟩) ⟨qd, wd, γd⟩ := (PDA.reaches₁_iff_reachesIn_one).mp h exact False.elim (PDA.reachesIn_one_on_empty_stack h') | cons Z γ => cases w with | nil => unfold PDA.Reaches₁ PDA.step at h rcases h with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z with | none => simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', [], β' ++ γ⟩, ?_, ?_⟩ · simpa [leftConf, List.map_append] using hcfg · refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] | cons a w => unfold PDA.Reaches₁ PDA.step at h rcases h with h | h · rcases h with ⟨p, β, hp, hcfg⟩ cases ht : M₁.transition q a Z with | none => simp [DPDA.toPDA, markedUnion, markTransitionLeft, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markTransitionLeft, ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', w, β' ++ γ⟩, ?_, ?_⟩ · simpa [leftConf, List.map_append] using hcfg · left refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, ht] · rcases h with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z with | none => simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', a :: w, β' ++ γ⟩, ?_, ?_⟩ · simpa [leftConf, List.map_append] using hcfg · right refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] private lemma left_reaches_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₁.toPDA} {d : PDA.conf (markedUnion M₁ M₂).toPDA} : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (leftConf M₁ M₂ c) d → ∃ c' : PDA.conf M₁.toPDA, d = leftConf M₁ M₂ c' ∧ PDA.Reaches c c' := by intro h induction h with | refl => exact ⟨c, rfl, PDA.Reaches.refl _⟩ | tail _ hstep ih => rcases ih with ⟨c', rfl, hc'⟩ rcases left_reaches₁_unmap M₁ M₂ hstep with ⟨c'', rfl, hstep'⟩ exact ⟨c'', rfl, Relation.ReflTransGen.tail hc' hstep'⟩ private lemma right_reaches₁_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₂.toPDA} {d : PDA.conf (markedUnion M₁ M₂).toPDA} : @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (rightConf M₁ M₂ c) d → ∃ c' : PDA.conf M₂.toPDA, d = rightConf M₁ M₂ c' ∧ PDA.Reaches₁ c c' := by intro h rcases c with ⟨q, w, γ⟩ rcases d with ⟨qd, wd, γd⟩ cases γ with | nil => have h' : @PDA.ReachesIn (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA 1 (rightConf M₁ M₂ ⟨q, w, []⟩) ⟨qd, wd, γd⟩ := (PDA.reaches₁_iff_reachesIn_one).mp h exact False.elim (PDA.reachesIn_one_on_empty_stack h') | cons Z γ => cases w with | nil => unfold PDA.Reaches₁ PDA.step at h rcases h with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z with | none => simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', [], β' ++ γ⟩, ?_, ?_⟩ · simpa [rightConf, List.map_append] using hcfg · refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] | cons a w => unfold PDA.Reaches₁ PDA.step at h rcases h with h | h · rcases h with ⟨p, β, hp, hcfg⟩ cases ht : M₂.transition q a Z with | none => simp [DPDA.toPDA, markedUnion, markTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markTransitionRight, ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', w, β' ++ γ⟩, ?_, ?_⟩ · simpa [rightConf, List.map_append] using hcfg · left refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, ht] · rcases h with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z with | none => simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', a :: w, β' ++ γ⟩, ?_, ?_⟩ · simpa [rightConf, List.map_append] using hcfg · right refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] private lemma right_reaches_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₂.toPDA} {d : PDA.conf (markedUnion M₁ M₂).toPDA} : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (rightConf M₁ M₂ c) d → ∃ c' : PDA.conf M₂.toPDA, d = rightConf M₁ M₂ c' ∧ PDA.Reaches c c' := by intro h induction h with | refl => exact ⟨c, rfl, PDA.Reaches.refl _⟩ | tail _ hstep ih => rcases ih with ⟨c', rfl, hc'⟩ rcases right_reaches₁_unmap M₁ M₂ hstep with ⟨c'', rfl, hstep'⟩ exact ⟨c'', rfl, Relation.ReflTransGen.tail hc' hstep'⟩ private lemma left_reachesIn_empty_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : ∀ n {q : Q₁} {sf : MarkState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (MarkStack S₁ S₂)}, @PDA.ReachesIn (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA n ⟨MarkState.left q, x, γ⟩ ⟨sf, [], δ⟩ → ∃ w : List T, x = w.map Sum.inr | 0, q, sf, x, γ, δ, h => by have heq := PDA.reachesIn_zero h cases heq exact ⟨[], rfl⟩ | n + 1, q, sf, x, γ, δ, h => by obtain ⟨c, hstep, hrest⟩ := PDA.reachesIn_iff_split_first.mpr h rcases c with ⟨s, y, stack⟩ have hstep' := PDA.reachesIn_one.mp hstep rcases γ with _ | ⟨Z, γ'⟩ · simp [PDA.step] at hstep' · cases Z with | bottom => cases x <;> simp [PDA.step, DPDA.toPDA, markedUnion] at hstep' | right Z₂ => cases x <;> simp [PDA.step, DPDA.toPDA, markedUnion] at hstep' | left Z₁ => cases x with | nil => unfold PDA.step at hstep' rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z₁ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg exact ⟨[], rfl⟩ | cons a x' => cases a with | inl b => unfold PDA.step at hstep' rcases hstep' with hstep' | hstep' · rcases hstep' with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z₁ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hw⟩ := left_reachesIn_empty_payload M₁ M₂ n hrest cases w <;> simp at hw | inr a => unfold PDA.step at hstep' rcases hstep' with hstep' | hstep' · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases ht : M₁.transition q a Z₁ with | none => simp [DPDA.toPDA, markedUnion, markTransitionLeft, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markTransitionLeft, ht] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hw⟩ := left_reachesIn_empty_payload M₁ M₂ n hrest exact ⟨a :: w, by simp [hw]⟩ · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z₁ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg exact left_reachesIn_empty_payload M₁ M₂ n hrest private lemma left_reaches_empty_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {q : Q₁} {sf : MarkState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (MarkStack S₁ S₂)} : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA ⟨MarkState.left q, x, γ⟩ ⟨sf, [], δ⟩ → ∃ w : List T, x = w.map Sum.inr := by intro h obtain ⟨n, hn⟩ := PDA.reaches_iff_reachesIn.mp h exact left_reachesIn_empty_payload M₁ M₂ n hn private lemma right_reachesIn_empty_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : ∀ n {q : Q₂} {sf : MarkState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (MarkStack S₁ S₂)}, @PDA.ReachesIn (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA n ⟨MarkState.right q, x, γ⟩ ⟨sf, [], δ⟩ → ∃ w : List T, x = w.map Sum.inr | 0, q, sf, x, γ, δ, h => by have heq := PDA.reachesIn_zero h cases heq exact ⟨[], rfl⟩ | n + 1, q, sf, x, γ, δ, h => by obtain ⟨c, hstep, hrest⟩ := PDA.reachesIn_iff_split_first.mpr h rcases c with ⟨s, y, stack⟩ have hstep' := PDA.reachesIn_one.mp hstep rcases γ with _ | ⟨Z, γ'⟩ · simp [PDA.step] at hstep' · cases Z with | bottom => cases x <;> simp [PDA.step, DPDA.toPDA, markedUnion] at hstep' | left Z₁ => cases x <;> simp [PDA.step, DPDA.toPDA, markedUnion] at hstep' | right Z₂ => cases x with | nil => unfold PDA.step at hstep' rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z₂ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg exact ⟨[], rfl⟩ | cons a x' => cases a with | inl b => unfold PDA.step at hstep' rcases hstep' with hstep' | hstep' · rcases hstep' with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z₂ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hw⟩ := right_reachesIn_empty_payload M₁ M₂ n hrest cases w <;> simp at hw | inr a => unfold PDA.step at hstep' rcases hstep' with hstep' | hstep' · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases ht : M₂.transition q a Z₂ with | none => simp [DPDA.toPDA, markedUnion, markTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markTransitionRight, ht] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hw⟩ := right_reachesIn_empty_payload M₁ M₂ n hrest exact ⟨a :: w, by simp [hw]⟩ · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z₂ with | none => simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, markedUnion, markEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg exact right_reachesIn_empty_payload M₁ M₂ n hrest private lemma right_reaches_empty_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {q : Q₂} {sf : MarkState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (MarkStack S₁ S₂)} : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA ⟨MarkState.right q, x, γ⟩ ⟨sf, [], δ⟩ → ∃ w : List T, x = w.map Sum.inr := by intro h obtain ⟨n, hn⟩ := PDA.reaches_iff_reachesIn.mp h exact right_reachesIn_empty_payload M₁ M₂ n hn private lemma markedUnion_accepts_left (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : w ∈ M₁.acceptsByFinalState → Sum.inl false :: w.map Sum.inr ∈ (markedUnion M₁ M₂).acceptsByFinalState := by rintro ⟨q, hq, γ, hreach⟩ refine ⟨MarkState.left q, ?_, γ.map MarkStack.left, ?_⟩ · left exact ⟨q, hq, rfl⟩ · let c0 : PDA.conf (markedUnion M₁ M₂).toPDA := leftConf M₁ M₂ ⟨M₁.initial_state, w, [M₁.start_symbol]⟩ have hfirst : @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA ⟨(markedUnion M₁ M₂).initial_state, Sum.inl false :: w.map Sum.inr, [(markedUnion M₁ M₂).start_symbol]⟩ c0 := by unfold c0 PDA.Reaches₁ PDA.step left refine ⟨MarkState.left M₁.initial_state, [MarkStack.left M₁.start_symbol], ?_, ?_⟩ · simp [DPDA.toPDA, markedUnion] · simp [leftConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (left_reaches_map M₁ M₂ hreach) private lemma markedUnion_accepts_right (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : w ∈ M₂.acceptsByFinalState → Sum.inl true :: w.map Sum.inr ∈ (markedUnion M₁ M₂).acceptsByFinalState := by rintro ⟨q, hq, γ, hreach⟩ refine ⟨MarkState.right q, ?_, γ.map MarkStack.right, ?_⟩ · right exact ⟨q, hq, rfl⟩ · let c0 : PDA.conf (markedUnion M₁ M₂).toPDA := rightConf M₁ M₂ ⟨M₂.initial_state, w, [M₂.start_symbol]⟩ have hfirst : @PDA.Reaches₁ (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA ⟨(markedUnion M₁ M₂).initial_state, Sum.inl true :: w.map Sum.inr, [(markedUnion M₁ M₂).start_symbol]⟩ c0 := by unfold c0 PDA.Reaches₁ PDA.step left refine ⟨MarkState.right M₂.initial_state, [MarkStack.right M₂.start_symbol], ?_, ?_⟩ · simp [DPDA.toPDA, markedUnion] · simp [rightConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (right_reaches_map M₁ M₂ hreach) private lemma markedUnion_accepts_iff (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {x : List (Bool ⊕ T)} : x ∈ (markedUnion M₁ M₂).acceptsByFinalState ↔ (∃ w, x = Sum.inl false :: w.map Sum.inr ∧ w ∈ M₁.acceptsByFinalState) ∨ (∃ w, x = Sum.inl true :: w.map Sum.inr ∧ w ∈ M₂.acceptsByFinalState) := by constructor · rintro ⟨qf, hfinal, γ, hreach⟩ rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, hrest⟩ · cases heq simp [DPDA.toPDA, markedUnion] at hfinal · rcases c with ⟨s, y, stack⟩ cases x with | nil => unfold PDA.Reaches₁ PDA.step at hstep simp [DPDA.toPDA, markedUnion] at hstep | cons a xs => cases a with | inr a => unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp | inl b => cases b · unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hxs⟩ := left_reaches_empty_payload M₁ M₂ hrest have hrest' : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (leftConf M₁ M₂ ⟨M₁.initial_state, w, [M₁.start_symbol]⟩) ⟨qf, [], γ⟩ := by simpa [leftConf, hxs] using hrest rcases left_reaches_unmap M₁ M₂ hrest' with ⟨c', hc', hM⟩ rcases c' with ⟨q', w', γ'⟩ simp [leftConf] at hc' rcases hc' with ⟨rfl, rfl, rfl⟩ have hq' : q' ∈ M₁.final_states := by simpa [DPDA.toPDA, markedUnion] using hfinal left exact ⟨w, by simp [hxs], ⟨q', hq', γ', hM⟩⟩ · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp · unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hxs⟩ := right_reaches_empty_payload M₁ M₂ hrest have hrest' : @PDA.Reaches (MarkState Q₁ Q₂) (Bool ⊕ T) (MarkStack S₁ S₂) _ _ _ (markedUnion M₁ M₂).toPDA (rightConf M₁ M₂ ⟨M₂.initial_state, w, [M₂.start_symbol]⟩) ⟨qf, [], γ⟩ := by simpa [rightConf, hxs] using hrest rcases right_reaches_unmap M₁ M₂ hrest' with ⟨c', hc', hM⟩ rcases c' with ⟨q', w', γ'⟩ simp [rightConf] at hc' rcases hc' with ⟨rfl, rfl, rfl⟩ have hq' : q' ∈ M₂.final_states := by simpa [DPDA.toPDA, markedUnion] using hfinal right exact ⟨w, by simp [hxs], ⟨q', hq', γ', hM⟩⟩ · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, markedUnion] at hp · rintro (⟨w, rfl, hw⟩ | ⟨w, rfl, hw⟩) · exact markedUnion_accepts_left M₁ M₂ hw · exact markedUnion_accepts_right M₁ M₂ hw private lemma mem_prod_singletons_iff {α β : Type} (h : α → List β) (x : List α) (u : List β) : u ∈ (x.map (fun a => ({h a} : Language β))).prod ↔ u = x.flatMap h := by induction x generalizing u with | nil => exact Set.mem_singleton_iff | cons a x ih => rw [List.map_cons, List.prod_cons, Language.mul_def, Set.mem_image2] constructor · rintro ⟨u₁, hu₁, u₂, hu₂, rfl⟩ rw [Set.mem_singleton_iff] at hu₁ subst u₁ rw [(ih u₂).mp hu₂] rfl · intro hu subst hu exact ⟨h a, Set.mem_singleton (h a), x.flatMap h, (ih _).mpr rfl, rfl⟩ private lemma mem_homomorphicImage_iff_flatMap {α β : Type} (L : Language α) (h : α → List β) (u : List β) : u ∈ L.homomorphicImage h ↔ ∃ x ∈ L, u = x.flatMap h := by constructor · rintro ⟨x, hx, hu⟩ exact ⟨x, hx, (mem_prod_singletons_iff h x u).mp hu⟩ · rintro ⟨x, hx, rfl⟩ exact ⟨x, hx, (mem_prod_singletons_iff h x (x.flatMap h)).mpr rfl⟩ private def eraseMarker : Bool ⊕ T → List T | Sum.inl _ => [] | Sum.inr a => [a] omit [Fintype T] in private lemma flatMap_erase_payload (w : List T) : (w.map Sum.inr).flatMap eraseMarker = w := by induction w with | nil => rfl | cons a w ih => simp [eraseMarker, ih] private lemma markedUnion_hom_eq_union (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : ((markedUnion M₁ M₂).acceptsByFinalState).homomorphicImage eraseMarker = M₁.acceptsByFinalState + M₂.acceptsByFinalState := by ext w constructor · intro hw rw [mem_homomorphicImage_iff_flatMap] at hw rcases hw with ⟨x, hx, rfl⟩ rw [markedUnion_accepts_iff M₁ M₂] at hx rcases hx with ⟨u, rfl, hu⟩ | ⟨u, rfl, hu⟩ · simpa [eraseMarker, Language.add_def, flatMap_erase_payload] using Or.inl hu · simpa [eraseMarker, Language.add_def, flatMap_erase_payload] using Or.inr hu · intro hw rw [mem_homomorphicImage_iff_flatMap] rw [Language.add_def] at hw rcases hw with hw | hw · exact ⟨Sum.inl false :: w.map Sum.inr, markedUnion_accepts_left M₁ M₂ hw, by simp [eraseMarker, flatMap_erase_payload]⟩ · exact ⟨Sum.inl true :: w.map Sum.inr, markedUnion_accepts_right M₁ M₂ hw, by simp [eraseMarker, flatMap_erase_payload]⟩ private def mergeMarker (marker : T) : Bool ⊕ T → List T | Sum.inl _ => [marker] | Sum.inr a => [a] omit [Fintype T] in private lemma mergeMarker_epsfree (marker : T) : IsEpsFreeHomomorphism (mergeMarker marker) := by intro a cases a <;> simp [mergeMarker] omit [Fintype T] in private lemma flatMap_merge_payload (marker : T) (w : List T) : (w.map Sum.inr).flatMap (mergeMarker marker) = w := by induction w with | nil => rfl | cons a w ih => simp [mergeMarker, ih] private lemma markedUnion_merge_leftQuotient_eq_union (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (marker : T) : [marker] \\ (((markedUnion M₁ M₂).acceptsByFinalState).homomorphicImage (mergeMarker marker)) = M₁.acceptsByFinalState + M₂.acceptsByFinalState := by ext w constructor · intro hw change marker :: w ∈ ((markedUnion M₁ M₂).acceptsByFinalState).homomorphicImage (mergeMarker marker) at hw rw [mem_homomorphicImage_iff_flatMap] at hw rcases hw with ⟨x, hx, hflat⟩ rw [markedUnion_accepts_iff M₁ M₂] at hx rcases hx with ⟨u, rfl, hu⟩ | ⟨u, rfl, hu⟩ · simp [mergeMarker, flatMap_merge_payload] at hflat subst w exact Or.inl hu · simp [mergeMarker, flatMap_merge_payload] at hflat subst w exact Or.inr hu · intro hw change marker :: w ∈ ((markedUnion M₁ M₂).acceptsByFinalState).homomorphicImage (mergeMarker marker) rw [mem_homomorphicImage_iff_flatMap] rw [Language.add_def] at hw rcases hw with hw | hw · exact ⟨Sum.inl false :: w.map Sum.inr, markedUnion_accepts_left M₁ M₂ hw, by simp [mergeMarker, flatMap_merge_payload]⟩ · exact ⟨Sum.inl true :: w.map Sum.inr, markedUnion_accepts_right M₁ M₂ hw, by simp [mergeMarker, flatMap_merge_payload]⟩ end DPDA public theorem DCF_leftQuotient_singleton {A : Type} [Fintype A] {L : Language A} (a : A) (hL : is_DCF L) : is_DCF ([a] \\ L) := by rcases hL with ⟨Q, S, hQ, hS, M, hM⟩ have h := DPDA.is_DCF_leftQuotient_singleton M a rw [hM] at h exact h public theorem DCF_leftQuotient_word {A : Type} [Fintype A] (x : List A) {L : Language A} (hL : is_DCF L) : is_DCF (x \\ L) := by induction x generalizing L with | nil => simpa using hL | cons a x ih => rw [Language.cons_leftQuotient] exact ih (DCF_leftQuotient_singleton a hL) public theorem DCF_notClosedUnderHomomorphism : ¬ ClosedUnderHomomorphism is_DCF := by intro hhom apply DCF_notClosedUnderUnion intro L₁ L₂ hL₁ hL₂ rcases hL₁ with ⟨Q₁, S₁, hQ₁, hS₁, M₁, hM₁⟩ rcases hL₂ with ⟨Q₂, S₂, hQ₂, hS₂, M₂, hM₂⟩ let M := DPDA.markedUnion M₁ M₂ have hMDCF : is_DCF M.acceptsByFinalState := by exact ⟨_, _, inferInstance, inferInstance, M, rfl⟩ have hImage := hhom (α := Bool ⊕ Fin 3) (β := Fin 3) M.acceptsByFinalState DPDA.eraseMarker hMDCF simpa [M, DPDA.markedUnion_hom_eq_union, hM₁, hM₂] using hImage public theorem DCF_notClosedUnderEpsFreeHomomorphism : ¬ ClosedUnderEpsFreeHomomorphism is_DCF := by intro heps apply DCF_notClosedUnderUnion_of_three (Sum.inl false : Bool ⊕ Fin 3) (Sum.inl true : Bool ⊕ Fin 3) (Sum.inr 0) (by decide) (by decide) (by decide) intro L₁ L₂ hL₁ hL₂ rcases hL₁ with ⟨Q₁, S₁, hQ₁, hS₁, M₁, hM₁⟩ rcases hL₂ with ⟨Q₂, S₂, hQ₂, hS₂, M₂, hM₂⟩ let marker : Bool ⊕ Fin 3 := Sum.inl false let M := DPDA.markedUnion M₁ M₂ have hMDCF : is_DCF M.acceptsByFinalState := by exact ⟨_, _, inferInstance, inferInstance, M, rfl⟩ have hImage := heps M.acceptsByFinalState (DPDA.mergeMarker marker) (DPDA.mergeMarker_epsfree marker) hMDCF rcases hImage with ⟨Q, S, hQ, hS, MImage, hMImage⟩ have hQuot : is_DCF ([marker] \\ (M.acceptsByFinalState.homomorphicImage (DPDA.mergeMarker marker))) := by have h := DPDA.is_DCF_leftQuotient_singleton MImage marker rw [hMImage] at h exact h dsimp [M] at hQuot change is_DCF ([marker] \\ (((DPDA.markedUnion M₁ M₂).acceptsByFinalState).homomorphicImage (DPDA.mergeMarker marker))) at hQuot rw [DPDA.markedUnion_merge_leftQuotient_eq_union M₁ M₂ marker, hM₁, hM₂] at hQuot exact hQuot end DCFHomomorphism