module public import Langlib.Classes.DeterministicContextFree.Closure.Bijection public import Langlib.Classes.DeterministicContextFree.Closure.Homomorphism public import Langlib.Classes.DeterministicContextFree.Closure.IntersectionRegular public import Langlib.Classes.DeterministicContextFree.Closure.Union public import Langlib.Classes.DeterministicContextFree.Examples.AnBmCm public import Langlib.Classes.Regular.Closure.Concatenation public import Langlib.Classes.Regular.Closure.Homomorphism public import Langlib.Classes.Regular.Closure.Star public import Langlib.Classes.Regular.Closure.Union public import Langlib.Classes.Regular.Examples.TopBot public import Langlib.Classes.Regular.Inclusion.DeterministicContextFree public import Langlib.Automata.FiniteState.Equivalence.Regular public import Langlib.Utilities.ClosurePredicates /-! # Deterministic Context-Free Languages Are Not Closed Under Concatenation The reduction uses the standard DCFL union witnesses. If DCFLs were closed under concatenation, then prefixing one branch by a fresh marker and concatenating with the marker star would let an intersection and a left quotient recover the union of the two witness languages. -/ open PDA namespace DCFConcatenation private inductive OptionalState (Q₁ Q₂ : Type) where | start | left : Q₁ → OptionalState Q₁ Q₂ | right : Q₂ → OptionalState Q₁ Q₂ deriving Fintype private inductive OptionalStack (S₁ S₂ : Type) where | bottom | left : S₁ → OptionalStack S₁ S₂ | right : S₂ → OptionalStack S₁ S₂ deriving Fintype namespace DPDA variable {T Q₁ Q₂ S₁ S₂ : Type} variable [Fintype T] [Fintype Q₁] [Fintype Q₂] [Fintype S₁] [Fintype S₂] private def optTransitionLeft (M : DPDA Q₁ T S₁) (q : Q₁) (a : T) (Z : S₁) : Option (OptionalState Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.transition q a Z with | none => none | some (q', γ) => some (OptionalState.left q', γ.map OptionalStack.left) private def optTransitionRight (M : DPDA Q₂ T S₂) (q : Q₂) (a : T) (Z : S₂) : Option (OptionalState Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.transition q a Z with | none => none | some (q', γ) => some (OptionalState.right q', γ.map OptionalStack.right) private def optEpsilonLeft (M : DPDA Q₁ T S₁) (q : Q₁) (Z : S₁) : Option (OptionalState Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.epsilon_transition q Z with | none => none | some (q', γ) => some (OptionalState.left q', γ.map OptionalStack.left) private def optEpsilonRight (M : DPDA Q₂ T S₂) (q : Q₂) (Z : S₂) : Option (OptionalState Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.epsilon_transition q Z with | none => none | some (q', γ) => some (OptionalState.right q', γ.map OptionalStack.right) private def optionalMarkedUnion (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : DPDA (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) where initial_state := OptionalState.start start_symbol := OptionalStack.bottom final_states := {q | q = OptionalState.start ∧ M₂.initial_state ∈ M₂.final_states ∨ (∃ q₁ ∈ M₁.final_states, q = OptionalState.left q₁) ∨ (∃ q₂ ∈ M₂.final_states, q = OptionalState.right q₂)} transition q a Z := match q, a, Z with | OptionalState.start, Sum.inl false, OptionalStack.bottom => some (OptionalState.left M₁.initial_state, [OptionalStack.left M₁.start_symbol]) | OptionalState.start, Sum.inr a, OptionalStack.bottom => optTransitionRight (Q₁ := Q₁) (S₁ := S₁) M₂ M₂.initial_state a M₂.start_symbol | OptionalState.left q₁, Sum.inr a, OptionalStack.left Z₁ => optTransitionLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ a Z₁ | OptionalState.right q₂, Sum.inr a, OptionalStack.right Z₂ => optTransitionRight (Q₁ := Q₁) (S₁ := S₁) M₂ q₂ a Z₂ | _, _, _ => none epsilon_transition q Z := match q, Z with | OptionalState.left q₁, OptionalStack.left Z₁ => optEpsilonLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ Z₁ | OptionalState.right q₂, OptionalStack.right Z₂ => optEpsilonRight (Q₁ := Q₁) (S₁ := S₁) M₂ q₂ Z₂ | _, _ => none no_mixed := by intro q Z hε a cases q <;> cases Z <;> cases a <;> simp [optEpsilonLeft, optEpsilonRight, optTransitionLeft, optTransitionRight] 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 (optionalMarkedUnion M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨OptionalState.left q, w.map Sum.inr, γ.map OptionalStack.left⟩ private def rightConf (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : PDA.conf M₂.toPDA → PDA.conf (optionalMarkedUnion M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨OptionalState.right q, w.map Sum.inr, γ.map OptionalStack.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₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 ⟨OptionalState.left p, β.map OptionalStack.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, optionalMarkedUnion, optEpsilonLeft, 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 ⟨OptionalState.left p, β.map OptionalStack.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, optionalMarkedUnion, optTransitionLeft, ht] · simpa [leftConf, List.map_append] using congrArg (leftConf M₁ M₂) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨OptionalState.left p, β.map OptionalStack.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, optionalMarkedUnion, optEpsilonLeft, 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 (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 ⟨OptionalState.right p, β.map OptionalStack.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, optionalMarkedUnion, optEpsilonRight, 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 ⟨OptionalState.right p, β.map OptionalStack.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, optionalMarkedUnion, optTransitionRight, ht] · simpa [rightConf, List.map_append] using congrArg (rightConf M₁ M₂) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨OptionalState.right p, β.map OptionalStack.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, optionalMarkedUnion, optEpsilonRight, 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 (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 (optionalMarkedUnion M₁ M₂).toPDA} : @PDA.Reaches₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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, optionalMarkedUnion, optEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, 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, optionalMarkedUnion, optTransitionLeft, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optTransitionLeft, 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, optionalMarkedUnion, optEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, 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 (optionalMarkedUnion M₁ M₂).toPDA} : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 (optionalMarkedUnion M₁ M₂).toPDA} : @PDA.Reaches₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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, optionalMarkedUnion, optEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, 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, optionalMarkedUnion, optTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optTransitionRight, 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, optionalMarkedUnion, optEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, 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 (optionalMarkedUnion M₁ M₂).toPDA} : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 : OptionalState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (OptionalStack S₁ S₂)}, @PDA.ReachesIn (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA n ⟨OptionalState.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, optionalMarkedUnion] at hstep' | right Z₂ => cases x <;> simp [PDA.step, DPDA.toPDA, optionalMarkedUnion] 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, optionalMarkedUnion, optEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, 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, optionalMarkedUnion] at hp · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₁.epsilon_transition q Z₁ with | none => simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, 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, optionalMarkedUnion, optTransitionLeft, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optTransitionLeft, 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, optionalMarkedUnion, optEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonLeft, 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 : OptionalState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (OptionalStack S₁ S₂)} : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA ⟨OptionalState.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 : OptionalState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (OptionalStack S₁ S₂)}, @PDA.ReachesIn (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA n ⟨OptionalState.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, optionalMarkedUnion] at hstep' | left Z₁ => cases x <;> simp [PDA.step, DPDA.toPDA, optionalMarkedUnion] 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, optionalMarkedUnion, optEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, 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, optionalMarkedUnion] at hp · rcases hstep' with ⟨p, β, hp, hcfg⟩ cases hε : M₂.epsilon_transition q Z₂ with | none => simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, 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, optionalMarkedUnion, optTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optTransitionRight, 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, optionalMarkedUnion, optEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnion, optEpsilonRight, 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 : OptionalState Q₁ Q₂} {x : List (Bool ⊕ T)} {γ δ : List (OptionalStack S₁ S₂)} : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA ⟨OptionalState.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 optional_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 ∈ (optionalMarkedUnion M₁ M₂).acceptsByFinalState := by rintro ⟨q, hq, γ, hreach⟩ refine ⟨OptionalState.left q, ?_, γ.map OptionalStack.left, ?_⟩ · right left exact ⟨q, hq, rfl⟩ · let c0 : PDA.conf (optionalMarkedUnion M₁ M₂).toPDA := leftConf M₁ M₂ ⟨M₁.initial_state, w, [M₁.start_symbol]⟩ have hfirst : @PDA.Reaches₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA ⟨(optionalMarkedUnion M₁ M₂).initial_state, Sum.inl false :: w.map Sum.inr, [(optionalMarkedUnion M₁ M₂).start_symbol]⟩ c0 := by unfold c0 PDA.Reaches₁ PDA.step left refine ⟨OptionalState.left M₁.initial_state, [OptionalStack.left M₁.start_symbol], ?_, ?_⟩ · simp [DPDA.toPDA, optionalMarkedUnion] · simp [leftConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (left_reaches_map M₁ M₂ hreach) private lemma optional_accepts_right (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (hnoε : M₂.epsilon_transition M₂.initial_state M₂.start_symbol = none) {w : List T} : w ∈ M₂.acceptsByFinalState → w.map Sum.inr ∈ (optionalMarkedUnion M₁ M₂).acceptsByFinalState := by rintro ⟨q, hq, γ, hreach⟩ cases w with | nil => rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, _⟩ · cases heq exact ⟨OptionalState.start, by left exact ⟨rfl, hq⟩, [OptionalStack.bottom], Relation.ReflTransGen.refl⟩ · unfold PDA.Reaches₁ PDA.step at hstep simp [DPDA.toPDA, hnoε] at hstep | cons a w => rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, hrest⟩ · cases heq · rcases c with ⟨p, input, stack⟩ unfold PDA.Reaches₁ PDA.step at hstep simp [DPDA.toPDA, hnoε] at hstep rcases hstep with ⟨hp, hinput⟩ subst input cases ht : M₂.transition M₂.initial_state a M₂.start_symbol with | none => simp [ht] at hp | some pβ => rcases pβ with ⟨p0, β0⟩ simp [ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨OptionalState.right q, ?_, γ.map OptionalStack.right, ?_⟩ · right right exact ⟨q, hq, rfl⟩ · have hfirst : @PDA.Reaches₁ (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA ⟨(optionalMarkedUnion M₁ M₂).initial_state, Sum.inr a :: w.map Sum.inr, [(optionalMarkedUnion M₁ M₂).start_symbol]⟩ (rightConf M₁ M₂ ⟨p, w, stack⟩) := by unfold PDA.Reaches₁ PDA.step left refine ⟨OptionalState.right p, stack.map OptionalStack.right, ?_, ?_⟩ · simp [DPDA.toPDA, optionalMarkedUnion, optTransitionRight, ht] · simp [rightConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (right_reaches_map M₁ M₂ hrest) private theorem optional_accepts_iff (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (hnoε : M₂.epsilon_transition M₂.initial_state M₂.start_symbol = none) {x : List (Bool ⊕ T)} : x ∈ (optionalMarkedUnion M₁ M₂).acceptsByFinalState ↔ (∃ w, x = Sum.inl false :: w.map Sum.inr ∧ w ∈ M₁.acceptsByFinalState) ∨ (∃ w, x = 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 rcases hfinal with ⟨_, hinit⟩ | ⟨⟨_, _, hbad⟩ | ⟨_, _, hbad⟩⟩ · right exact ⟨[], rfl, ⟨M₂.initial_state, hinit, [M₂.start_symbol], Relation.ReflTransGen.refl⟩⟩ · cases hbad · cases hbad · rcases c with ⟨s, y, stack⟩ cases x with | nil => unfold PDA.Reaches₁ PDA.step at hstep simp [DPDA.toPDA, optionalMarkedUnion] at hstep | cons a xs => cases a with | 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, optionalMarkedUnion] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hxs⟩ := left_reaches_empty_payload M₁ M₂ hrest have hrest' : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion 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 rcases hfinal with hstart | hfinal · rcases hstart with ⟨hstart, _⟩ simp at hstart · rcases hfinal with hleft | hright · rcases hleft with ⟨q1, hq1, hqeq⟩ simp at hqeq rcases hqeq with rfl exact hq1 · rcases hright with ⟨q2, _, hqeq⟩ simp at hqeq left exact ⟨w, by simp [hxs], ⟨q', hq', γ', hM⟩⟩ · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnion] at hp · unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnion] at hp · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnion] at hp | inr t => unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ cases ht : M₂.transition M₂.initial_state t M₂.start_symbol with | none => simp [DPDA.toPDA, optionalMarkedUnion, optTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p0, β0⟩ simp [DPDA.toPDA, optionalMarkedUnion, optTransitionRight, ht] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg obtain ⟨w, hxs⟩ := right_reaches_empty_payload M₁ M₂ hrest have hrest' : @PDA.Reaches (OptionalState Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnion M₁ M₂).toPDA (rightConf M₁ M₂ ⟨p0, w, β0⟩) ⟨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 rcases hfinal with hstart | hfinal · rcases hstart with ⟨hstart, _⟩ simp at hstart · rcases hfinal with hleft | hright · rcases hleft with ⟨q1, _, hqeq⟩ simp at hqeq · rcases hright with ⟨q2, hq2, hqeq⟩ simp at hqeq rcases hqeq with rfl exact hq2 right refine ⟨t :: w, ?_, ?_⟩ · simp [hxs] · refine ⟨q', hq', γ', ?_⟩ exact Relation.ReflTransGen.tail (Relation.ReflTransGen.refl : PDA.Reaches (⟨M₂.initial_state, t :: w, [M₂.start_symbol]⟩ : PDA.conf M₂.toPDA) ⟨M₂.initial_state, t :: w, [M₂.start_symbol]⟩) (by unfold PDA.Reaches₁ PDA.step left refine ⟨p0, β0, ?_, by simp⟩ simp [DPDA.toPDA, ht]) |>.trans hM · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnion] at hp · rintro (⟨w, rfl, hw⟩ | ⟨w, rfl, hw⟩) · exact optional_accepts_left M₁ M₂ hw · exact optional_accepts_right M₁ M₂ hnoε hw public theorem is_DCF_optional_marked_union_no_initial_epsilon (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (hnoε : M₂.epsilon_transition M₂.initial_state M₂.start_symbol = none) : is_DCF ({x | (∃ w, x = Sum.inl false :: w.map Sum.inr ∧ w ∈ M₁.acceptsByFinalState) ∨ (∃ w, x = w.map Sum.inr ∧ w ∈ M₂.acceptsByFinalState)} : Language (Bool ⊕ T)) := by refine ⟨OptionalState Q₁ Q₂, OptionalStack S₁ S₂, inferInstance, inferInstance, optionalMarkedUnion M₁ M₂, ?_⟩ ext x exact optional_accepts_iff M₁ M₂ hnoε end DPDA private inductive OptionalQuotState (T Q₁ Q₂ : Type) where | start | left : Q₁ → OptionalQuotState T Q₁ Q₂ | right : T → DCFHomomorphism.DPDA.LeftQuotientState Q₂ → OptionalQuotState T Q₁ Q₂ deriving DecidableEq private def optionalQuotStateEquiv (T Q₁ Q₂ : Type) : OptionalQuotState T Q₁ Q₂ ≃ Unit ⊕ Q₁ ⊕ T × DCFHomomorphism.DPDA.LeftQuotientState Q₂ where toFun | OptionalQuotState.start => Sum.inl () | OptionalQuotState.left q => Sum.inr (Sum.inl q) | OptionalQuotState.right a q => Sum.inr (Sum.inr (a, q)) invFun | Sum.inl _ => OptionalQuotState.start | Sum.inr (Sum.inl q) => OptionalQuotState.left q | Sum.inr (Sum.inr (a, q)) => OptionalQuotState.right a q left_inv := by intro q cases q <;> rfl right_inv := by intro q rcases q with (_ | (_ | ⟨a, q⟩)) <;> rfl private instance optionalQuotStateFintype {T Q₁ Q₂ : Type} [Fintype T] [Fintype Q₁] [Fintype Q₂] : Fintype (OptionalQuotState T Q₁ Q₂) := Fintype.ofEquiv _ (optionalQuotStateEquiv T Q₁ Q₂).symm namespace DPDA variable {T Q₁ Q₂ S₁ S₂ : Type} variable [Fintype T] [Fintype Q₁] [Fintype Q₂] [Fintype S₁] [Fintype S₂] private abbrev quotientM (M : DPDA Q₂ T S₂) (a : T) := DCFHomomorphism.DPDA.leftQuotientSingleton M a private def optQuotTransitionLeft (M : DPDA Q₁ T S₁) (q : Q₁) (a : T) (Z : S₁) : Option (OptionalQuotState T Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.transition q a Z with | none => none | some (q', γ) => some (OptionalQuotState.left q', γ.map OptionalStack.left) private def optQuotTransitionRight (M : DPDA Q₂ T S₂) (mark : T) (q : DCFHomomorphism.DPDA.LeftQuotientState Q₂) (a : T) (Z : S₂) : Option (OptionalQuotState T Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match (quotientM M mark).transition q a Z with | none => none | some (q', γ) => some (OptionalQuotState.right mark q', γ.map OptionalStack.right) private def optQuotEpsilonLeft (M : DPDA Q₁ T S₁) (q : Q₁) (Z : S₁) : Option (OptionalQuotState T Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match M.epsilon_transition q Z with | none => none | some (q', γ) => some (OptionalQuotState.left q', γ.map OptionalStack.left) private def optQuotEpsilonRight (M : DPDA Q₂ T S₂) (mark : T) (q : DCFHomomorphism.DPDA.LeftQuotientState Q₂) (Z : S₂) : Option (OptionalQuotState T Q₁ Q₂ × List (OptionalStack S₁ S₂)) := match (quotientM M mark).epsilon_transition q Z with | none => none | some (q', γ) => some (OptionalQuotState.right mark q', γ.map OptionalStack.right) private def optionalMarkedUnionQuot (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : DPDA (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) where initial_state := OptionalQuotState.start start_symbol := OptionalStack.bottom final_states := {q | q = OptionalQuotState.start ∧ ([] : List T) ∈ M₂.acceptsByFinalState ∨ (∃ q₁ ∈ M₁.final_states, q = OptionalQuotState.left q₁) ∨ (∃ mark q₂, q₂ ∈ (quotientM M₂ mark).final_states ∧ q = OptionalQuotState.right mark q₂)} transition q a Z := match q, a, Z with | OptionalQuotState.start, Sum.inl false, OptionalStack.bottom => some (OptionalQuotState.left M₁.initial_state, [OptionalStack.left M₁.start_symbol]) | OptionalQuotState.start, Sum.inr a, OptionalStack.bottom => some (OptionalQuotState.right a (quotientM M₂ a).initial_state, [OptionalStack.right (quotientM M₂ a).start_symbol]) | OptionalQuotState.left q₁, Sum.inr a, OptionalStack.left Z₁ => optQuotTransitionLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ a Z₁ | OptionalQuotState.right mark q₂, Sum.inr a, OptionalStack.right Z₂ => optQuotTransitionRight (Q₁ := Q₁) (S₁ := S₁) M₂ mark q₂ a Z₂ | _, _, _ => none epsilon_transition q Z := match q, Z with | OptionalQuotState.left q₁, OptionalStack.left Z₁ => optQuotEpsilonLeft (Q₂ := Q₂) (S₂ := S₂) M₁ q₁ Z₁ | OptionalQuotState.right mark q₂, OptionalStack.right Z₂ => optQuotEpsilonRight (Q₁ := Q₁) (S₁ := S₁) M₂ mark q₂ Z₂ | _, _ => none no_mixed := by intro q Z hε a cases q <;> cases Z <;> cases a <;> simp [optQuotEpsilonLeft, optQuotEpsilonRight, optQuotTransitionLeft, optQuotTransitionRight] 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 mark q₂ Z₂ t split at hε · exact False.elim (hε rfl) · rename_i q' γ hε₂ have hnone := (quotientM M₂ mark).no_mixed q₂ Z₂ (by simp [hε₂]) t simp [hnone] private def quotLeftConf (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : PDA.conf M₁.toPDA → PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨OptionalQuotState.left q, w.map Sum.inr, γ.map OptionalStack.left⟩ private def quotRightConf (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (mark : T) : PDA.conf (quotientM M₂ mark).toPDA → PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA | ⟨q, w, γ⟩ => ⟨OptionalQuotState.right mark q, w.map Sum.inr, γ.map OptionalStack.right⟩ private lemma quot_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₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotLeftConf M₁ M₂ c) (quotLeftConf 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 ⟨OptionalQuotState.left p, β.map OptionalStack.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, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] · simpa [quotLeftConf, List.map_append] using congrArg (quotLeftConf 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 ⟨OptionalQuotState.left p, β.map OptionalStack.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, optionalMarkedUnionQuot, optQuotTransitionLeft, ht] · simpa [quotLeftConf, List.map_append] using congrArg (quotLeftConf M₁ M₂) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨OptionalQuotState.left p, β.map OptionalStack.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, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] · simpa [quotLeftConf, List.map_append] using congrArg (quotLeftConf M₁ M₂) hcfg private lemma quot_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 (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotLeftConf M₁ M₂ c) (quotLeftConf M₁ M₂ c') := by intro h induction h with | refl => rfl | tail _ hstep ih => exact Relation.ReflTransGen.tail ih (quot_left_reaches₁_map M₁ M₂ hstep) private lemma quot_right_reaches₁_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (mark : T) {c c' : PDA.conf (quotientM M₂ mark).toPDA} : PDA.Reaches₁ c c' → @PDA.Reaches₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotRightConf M₁ M₂ mark c) (quotRightConf M₁ M₂ mark c') := by intro h rcases c with ⟨q, w, γ⟩ rcases c' with ⟨q', w', γ'⟩ cases γ with | nil => have h' : @PDA.ReachesIn (DCFHomomorphism.DPDA.LeftQuotientState Q₂) T S₂ _ _ _ (quotientM M₂ mark).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 ⟨OptionalQuotState.right mark p, β.map OptionalStack.right, ?_, ?_⟩ · cases hε : (quotientM M₂ mark).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, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] · simpa [quotRightConf, List.map_append] using congrArg (quotRightConf M₁ M₂ mark) hcfg | cons a w => unfold PDA.Reaches₁ PDA.step at h ⊢ rcases h with h | h · rcases h with ⟨p, β, hp, hcfg⟩ left refine ⟨OptionalQuotState.right mark p, β.map OptionalStack.right, ?_, ?_⟩ · cases ht : (quotientM M₂ mark).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, optionalMarkedUnionQuot, optQuotTransitionRight, ht] · simpa [quotRightConf, List.map_append] using congrArg (quotRightConf M₁ M₂ mark) hcfg · rcases h with ⟨p, β, hp, hcfg⟩ right refine ⟨OptionalQuotState.right mark p, β.map OptionalStack.right, ?_, ?_⟩ · cases hε : (quotientM M₂ mark).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, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] · simpa [quotRightConf, List.map_append] using congrArg (quotRightConf M₁ M₂ mark) hcfg private lemma quot_right_reaches_map (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (mark : T) {c c' : PDA.conf (quotientM M₂ mark).toPDA} : PDA.Reaches c c' → @PDA.Reaches (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotRightConf M₁ M₂ mark c) (quotRightConf M₁ M₂ mark c') := by intro h induction h with | refl => rfl | tail _ hstep ih => exact Relation.ReflTransGen.tail ih (quot_right_reaches₁_map M₁ M₂ mark hstep) private lemma quot_left_reaches₁_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₁.toPDA} {d : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA} : @PDA.Reaches₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotLeftConf M₁ M₂ c) d → ∃ c' : PDA.conf M₁.toPDA, d = quotLeftConf 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 (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA 1 (quotLeftConf 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, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', [], β' ++ γ⟩, ?_, ?_⟩ · simpa [quotLeftConf, 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, optionalMarkedUnionQuot, optQuotTransitionLeft, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotTransitionLeft, ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', w, β' ++ γ⟩, ?_, ?_⟩ · simpa [quotLeftConf, 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, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonLeft, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', a :: w, β' ++ γ⟩, ?_, ?_⟩ · simpa [quotLeftConf, List.map_append] using hcfg · right refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] private lemma quot_left_reaches_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {c : PDA.conf M₁.toPDA} {d : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA} : @PDA.Reaches (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotLeftConf M₁ M₂ c) d → ∃ c' : PDA.conf M₁.toPDA, d = quotLeftConf 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 quot_left_reaches₁_unmap M₁ M₂ hstep with ⟨c'', rfl, hstep'⟩ exact ⟨c'', rfl, Relation.ReflTransGen.tail hc' hstep'⟩ private lemma quot_right_reaches₁_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (mark : T) {c : PDA.conf (quotientM M₂ mark).toPDA} {d : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA} : @PDA.Reaches₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotRightConf M₁ M₂ mark c) d → ∃ c' : PDA.conf (quotientM M₂ mark).toPDA, d = quotRightConf M₁ M₂ mark 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 (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA 1 (quotRightConf M₁ M₂ mark ⟨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ε : (quotientM M₂ mark).epsilon_transition q Z with | none => simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', [], β' ++ γ⟩, ?_, ?_⟩ · simpa [quotRightConf, 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 : (quotientM M₂ mark).transition q a Z with | none => simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotTransitionRight, ht] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotTransitionRight, ht] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', w, β' ++ γ⟩, ?_, ?_⟩ · simpa [quotRightConf, List.map_append] using hcfg · left refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, ht] · rcases h with ⟨p, β, hp, hcfg⟩ cases hε : (quotientM M₂ mark).epsilon_transition q Z with | none => simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] at hp | some pβ => rcases pβ with ⟨p', β'⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot, optQuotEpsilonRight, hε] at hp rcases hp with ⟨rfl, rfl⟩ refine ⟨⟨p', a :: w, β' ++ γ⟩, ?_, ?_⟩ · simpa [quotRightConf, List.map_append] using hcfg · right refine ⟨p', β', ?_, rfl⟩ simp [DPDA.toPDA, hε] private lemma quot_right_reaches_unmap (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) (mark : T) {c : PDA.conf (quotientM M₂ mark).toPDA} {d : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA} : @PDA.Reaches (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotRightConf M₁ M₂ mark c) d → ∃ c' : PDA.conf (quotientM M₂ mark).toPDA, d = quotRightConf M₁ M₂ mark 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 quot_right_reaches₁_unmap M₁ M₂ mark hstep with ⟨c'', rfl, hstep'⟩ exact ⟨c'', rfl, Relation.ReflTransGen.tail hc' hstep'⟩ private lemma optionalQuot_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 ∈ (optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState := by rintro ⟨q, hq, γ, hreach⟩ refine ⟨OptionalQuotState.left q, ?_, γ.map OptionalStack.left, ?_⟩ · right left exact ⟨q, hq, rfl⟩ · let c0 : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA := quotLeftConf M₁ M₂ ⟨M₁.initial_state, w, [M₁.start_symbol]⟩ have hfirst : @PDA.Reaches₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA ⟨(optionalMarkedUnionQuot M₁ M₂).initial_state, Sum.inl false :: w.map Sum.inr, [(optionalMarkedUnionQuot M₁ M₂).start_symbol]⟩ c0 := by unfold c0 PDA.Reaches₁ PDA.step left refine ⟨OptionalQuotState.left M₁.initial_state, [OptionalStack.left M₁.start_symbol], ?_, ?_⟩ · simp [DPDA.toPDA, optionalMarkedUnionQuot] · simp [quotLeftConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (quot_left_reaches_map M₁ M₂ hreach) private lemma optionalQuot_accepts_right (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : w ∈ M₂.acceptsByFinalState → w.map Sum.inr ∈ (optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState := by intro hw cases w with | nil => exact ⟨OptionalQuotState.start, by left exact ⟨rfl, hw⟩, [OptionalStack.bottom], Relation.ReflTransGen.refl⟩ | cons a w => have hquot : w ∈ (quotientM M₂ a).acceptsByFinalState := by rw [DCFHomomorphism.DPDA.leftQuotientSingleton_accepts] change a :: w ∈ M₂.acceptsByFinalState exact hw rcases hquot with ⟨q, hq, γ, hreach⟩ refine ⟨OptionalQuotState.right a q, ?_, γ.map OptionalStack.right, ?_⟩ · right right exact ⟨a, q, hq, rfl⟩ · let c0 : PDA.conf (optionalMarkedUnionQuot M₁ M₂).toPDA := quotRightConf M₁ M₂ a ⟨(quotientM M₂ a).initial_state, w, [(quotientM M₂ a).start_symbol]⟩ have hfirst : @PDA.Reaches₁ (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA ⟨(optionalMarkedUnionQuot M₁ M₂).initial_state, Sum.inr a :: w.map Sum.inr, [(optionalMarkedUnionQuot M₁ M₂).start_symbol]⟩ c0 := by unfold c0 PDA.Reaches₁ PDA.step left refine ⟨OptionalQuotState.right a (quotientM M₂ a).initial_state, [OptionalStack.right (quotientM M₂ a).start_symbol], ?_, ?_⟩ · simp [DPDA.toPDA, optionalMarkedUnionQuot] · simp [quotRightConf] exact Relation.ReflTransGen.trans (Relation.ReflTransGen.single hfirst) (quot_right_reaches_map M₁ M₂ a hreach) private lemma optionalQuot_accepts_marker_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : Sum.inl false :: w.map Sum.inr ∈ (optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState ↔ w ∈ M₁.acceptsByFinalState := by constructor · rintro ⟨qf, hfinal, γ, hreach⟩ rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, hrest⟩ · cases heq · rcases c with ⟨s, y, stack⟩ unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg have hrest' : @PDA.Reaches (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotLeftConf M₁ M₂ ⟨M₁.initial_state, w, [M₁.start_symbol]⟩) ⟨qf, [], γ⟩ := by simpa [quotLeftConf] using hrest rcases quot_left_reaches_unmap M₁ M₂ hrest' with ⟨c', hc', hM⟩ rcases c' with ⟨q', w', γ'⟩ simp [quotLeftConf] at hc' rcases hc' with ⟨rfl, rfl, rfl⟩ have hq' : q' ∈ M₁.final_states := by rcases hfinal with hstart | hfinal · rcases hstart with ⟨hstart, _⟩ simp at hstart · rcases hfinal with hleft | hright · rcases hleft with ⟨q1, hq1, hqeq⟩ simp at hqeq rcases hqeq with rfl exact hq1 · rcases hright with ⟨mark, q2, _, hqeq⟩ simp at hqeq exact ⟨q', hq', γ', hM⟩ · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot] at hp · exact optionalQuot_accepts_left M₁ M₂ private lemma optionalQuot_accepts_payload (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : w.map Sum.inr ∈ (optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState ↔ w ∈ M₂.acceptsByFinalState := by constructor · intro h cases w with | nil => rcases h with ⟨qf, hfinal, γ, hreach⟩ rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, _⟩ · cases heq rcases hfinal with hstart | hfinal · exact hstart.2 · rcases hfinal with hleft | hright · rcases hleft with ⟨_, _, hbad⟩ cases hbad · rcases hright with ⟨_, _, _, hbad⟩ cases hbad · unfold PDA.Reaches₁ PDA.step at hstep simp [DPDA.toPDA, optionalMarkedUnionQuot] at hstep | cons a w => rcases h with ⟨qf, hfinal, γ, hreach⟩ rcases Relation.ReflTransGen.cases_head hreach with heq | ⟨c, hstep, hrest⟩ · cases heq · rcases c with ⟨s, y, stack⟩ unfold PDA.Reaches₁ PDA.step at hstep rcases hstep with hstep | hstep · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot] at hp rcases hp with ⟨rfl, rfl⟩ cases hcfg have hrest' : @PDA.Reaches (OptionalQuotState T Q₁ Q₂) (Bool ⊕ T) (OptionalStack S₁ S₂) _ _ _ (optionalMarkedUnionQuot M₁ M₂).toPDA (quotRightConf M₁ M₂ a ⟨(quotientM M₂ a).initial_state, w, [(quotientM M₂ a).start_symbol]⟩) ⟨qf, [], γ⟩ := by simpa [quotRightConf] using hrest rcases quot_right_reaches_unmap M₁ M₂ a hrest' with ⟨c', hc', hM⟩ rcases c' with ⟨q', w', γ'⟩ simp [quotRightConf] at hc' rcases hc' with ⟨rfl, rfl, rfl⟩ have hq' : q' ∈ (quotientM M₂ a).final_states := by rcases hfinal with hstart | hfinal · rcases hstart with ⟨hstart, _⟩ simp at hstart · rcases hfinal with hleft | hright · rcases hleft with ⟨q1, _, hqeq⟩ simp at hqeq · rcases hright with ⟨mark, q2, hq2, hqeq⟩ simp at hqeq rcases hqeq with ⟨rfl, rfl⟩ exact hq2 have hquot : w ∈ (quotientM M₂ a).acceptsByFinalState := ⟨q', hq', γ', hM⟩ rw [DCFHomomorphism.DPDA.leftQuotientSingleton_accepts] at hquot change a :: w ∈ M₂.acceptsByFinalState at hquot exact hquot · rcases hstep with ⟨p, β, hp, hcfg⟩ simp [DPDA.toPDA, optionalMarkedUnionQuot] at hp · exact optionalQuot_accepts_right M₁ M₂ private theorem is_DCF_optional_marked_union_machine (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : is_DCF (optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState := ⟨OptionalQuotState T Q₁ Q₂, OptionalStack S₁ S₂, inferInstance, inferInstance, optionalMarkedUnionQuot M₁ M₂, rfl⟩ end DPDA private abbrev marker {T : Type} : Bool ⊕ T := Sum.inl false private abbrev markerStar (T : Type) : Language (Bool ⊕ T) := KStar.kstar ({[marker]} : Language (Bool ⊕ T)) private abbrev payloadOnly (T : Type) : Language (Bool ⊕ T) := Language.map Sum.inr (⊤ : Language T) private abbrev oneMarkerPayload (T : Type) : Language (Bool ⊕ T) := ({[marker]} : Language (Bool ⊕ T)) * payloadOnly T private lemma markerStar_nil_mem {T : Type} : ([] : List (Bool ⊕ T)) ∈ markerStar T := Language.nil_mem_kstar _ private lemma markerStar_singleton_mem {T : Type} : ([marker] : List (Bool ⊕ T)) ∈ markerStar T := by rw [markerStar, Language.mem_kstar] refine ⟨[[marker]], by simp, ?_⟩ intro y hy simpa using hy private lemma markerStar_all_marker {T : Type} {p : List (Bool ⊕ T)} (hp : p ∈ markerStar T) : ∀ a ∈ p, a = marker := by rw [markerStar, Language.mem_kstar] at hp rcases hp with ⟨blocks, rfl, hblocks⟩ intro a ha rw [List.mem_flatten] at ha rcases ha with ⟨block, hblock, ha⟩ have hblock' := hblocks block hblock rw [Set.mem_singleton_iff] at hblock' subst block simpa using ha private lemma marker_not_mem_payload {T : Type} (w : List T) : (marker : Bool ⊕ T) ∉ w.map Sum.inr := by intro h rw [List.mem_map] at h rcases h with ⟨a, _ha, h⟩ cases h private lemma mem_oneMarkerPayload_iff {T : Type} {x : List (Bool ⊕ T)} : x ∈ oneMarkerPayload T ↔ ∃ w : List T, x = marker :: w.map Sum.inr := by rw [oneMarkerPayload, Language.mem_mul] constructor · rintro ⟨u, hu, v, hv, rfl⟩ rw [Set.mem_singleton_iff] at hu subst u rcases hv with ⟨w, _hw, rfl⟩ exact ⟨w, rfl⟩ · rintro ⟨w, rfl⟩ exact ⟨[marker], Set.mem_singleton _, w.map Sum.inr, ⟨w, trivial, rfl⟩, rfl⟩ private lemma oneMarkerPayload_regular {T : Type} [Fintype T] : (oneMarkerPayload T).IsRegular := by exact (Language.isRegular_singleton_word ([marker] : List (Bool ⊕ T))).mul' (Language.isRegular_top.map Sum.inr) private lemma payloadOnly_regular {T : Type} [Fintype T] : (payloadOnly T).IsRegular := Language.isRegular_top.map Sum.inr /-- If `L₁` and `L₂` are deterministic context-free, so is the language that contains either a marked payload from `L₁` or an unmarked payload from `L₂`. -/ public theorem DCF_optional_marked_union {T : Type} [Fintype T] {L₁ L₂ : Language T} (hL₁ : is_DCF L₁) (hL₂ : is_DCF L₂) : is_DCF ({x | (∃ w, x = (Sum.inl false : Bool ⊕ T) :: w.map Sum.inr ∧ w ∈ L₁) ∨ (∃ w, x = w.map Sum.inr ∧ w ∈ L₂)} : Language (Bool ⊕ T)) := by rcases hL₁ with ⟨Q₁, S₁, hQ₁, hS₁, M₁, hM₁⟩ rcases hL₂ with ⟨Q₂, S₂, hQ₂, hS₂, M₂, hM₂⟩ let O : Language (Bool ⊕ T) := (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState let validShape : Language (Bool ⊕ T) := oneMarkerPayload T + payloadOnly T have hO : is_DCF O := DPDA.is_DCF_optional_marked_union_machine M₁ M₂ have hShape : validShape.IsRegular := (oneMarkerPayload_regular (T := T)).add' (payloadOnly_regular (T := T)) have hFiltered : is_DCF (O ⊓ validShape) := DCF_inter_regular O validShape hO hShape have hEq : O ⊓ validShape = ({x | (∃ w, x = (Sum.inl false : Bool ⊕ T) :: w.map Sum.inr ∧ w ∈ L₁) ∨ (∃ w, x = w.map Sum.inr ∧ w ∈ L₂)} : Language (Bool ⊕ T)) := by ext x constructor · rintro ⟨hOmem, hshape⟩ change x ∈ oneMarkerPayload T + payloadOnly T at hshape rw [Language.add_def] at hshape rcases hshape with hmarked | hunmarked · rw [mem_oneMarkerPayload_iff] at hmarked rcases hmarked with ⟨w, rfl⟩ left refine ⟨w, rfl, ?_⟩ simpa [hM₁, marker] using (DPDA.optionalQuot_accepts_marker_payload M₁ M₂).mp hOmem · rcases hunmarked with ⟨w, _hw, rfl⟩ right refine ⟨w, rfl, ?_⟩ simpa [hM₂] using (DPDA.optionalQuot_accepts_payload M₁ M₂).mp hOmem · rintro (⟨w, rfl, hw⟩ | ⟨w, rfl, hw⟩) · constructor · exact (DPDA.optionalQuot_accepts_marker_payload M₁ M₂).mpr (by simpa [hM₁] using hw) · change marker :: w.map Sum.inr ∈ oneMarkerPayload T + payloadOnly T rw [Language.add_def] left rw [mem_oneMarkerPayload_iff] exact ⟨w, rfl⟩ · constructor · exact (DPDA.optionalQuot_accepts_payload M₁ M₂).mpr (by simpa [hM₂] using hw) · change w.map Sum.inr ∈ oneMarkerPayload T + payloadOnly T rw [Language.add_def] right exact ⟨w, trivial, rfl⟩ rwa [← hEq] private lemma markerStar_regular {T : Type} [Fintype T] : (markerStar T).IsRegular := by exact (Language.isRegular_singleton_word ([marker] : List (Bool ⊕ T))).kstar' private lemma marker_payload_mem_concat_iff {T Q₁ Q₂ S₁ S₂ : Type} [Fintype T] [Fintype Q₁] [Fintype Q₂] [Fintype S₁] [Fintype S₂] (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) {w : List T} : (marker : Bool ⊕ T) :: w.map Sum.inr ∈ markerStar T * (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState ↔ w ∈ M₁.acceptsByFinalState ∨ w ∈ M₂.acceptsByFinalState := by constructor · intro h rw [Language.mem_mul] at h rcases h with ⟨p, hp, q, hq, hpq⟩ have hall := markerStar_all_marker hp cases p with | nil => simp at hpq subst q exact Or.inl ((DPDA.optionalQuot_accepts_marker_payload M₁ M₂).mp hq) | cons a p' => have ha : a = marker := hall a (by simp) subst a cases p' with | nil => simp at hpq subst q exact Or.inr ((DPDA.optionalQuot_accepts_payload M₁ M₂).mp hq) | cons b p'' => have hb : b = marker := hall b (by simp) simp at hpq have hbmem : b ∈ w.map Sum.inr := by rw [← hpq] simp exact False.elim (marker_not_mem_payload w (by simp [hb] at hbmem)) · intro h rw [Language.mem_mul] rcases h with h | h · refine ⟨[], markerStar_nil_mem, marker :: w.map Sum.inr, (DPDA.optionalQuot_accepts_marker_payload M₁ M₂).mpr h, ?_⟩ simp · refine ⟨[marker], markerStar_singleton_mem, w.map Sum.inr, (DPDA.optionalQuot_accepts_payload M₁ M₂).mpr h, ?_⟩ simp private theorem optional_concat_slice_eq_union {T Q₁ Q₂ S₁ S₂ : Type} [Fintype T] [Fintype Q₁] [Fintype Q₂] [Fintype S₁] [Fintype S₂] (M₁ : DPDA Q₁ T S₁) (M₂ : DPDA Q₂ T S₂) : [marker] \\ ((markerStar T * (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState) ⊓ oneMarkerPayload T) = Language.map Sum.inr (M₁.acceptsByFinalState + M₂.acceptsByFinalState) := by ext x constructor · intro hx change (marker : Bool ⊕ T) :: x ∈ ((markerStar T * (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState) ⊓ oneMarkerPayload T) at hx rcases hx with ⟨hcat, hone⟩ rw [mem_oneMarkerPayload_iff] at hone rcases hone with ⟨w, hshape⟩ cases hshape rw [marker_payload_mem_concat_iff M₁ M₂] at hcat exact ⟨w, by simpa [Language.add_def] using hcat, rfl⟩ · rintro ⟨w, hw, rfl⟩ change (marker : Bool ⊕ T) :: w.map Sum.inr ∈ ((markerStar T * (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState) ⊓ oneMarkerPayload T) constructor · rw [marker_payload_mem_concat_iff M₁ M₂] simpa [Language.add_def] using hw · rw [mem_oneMarkerPayload_iff] exact ⟨w, rfl⟩ /-- Deterministic context-free languages over `Bool ⊕ Fin 3` are not closed under concatenation. -/ public theorem DCF_notClosedUnderConcatenation : ¬ ClosedUnderConcatenation (α := Bool ⊕ Fin 3) is_DCF := by intro hconcat 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 O : Language (Bool ⊕ Fin 3) := (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState have hO : is_DCF O := DPDA.is_DCF_optional_marked_union_machine M₁ M₂ have hStar : is_DCF (markerStar (Fin 3)) := is_DCF_of_is_RG (is_RG_of_isRegular markerStar_regular) have hCat : is_DCF (markerStar (Fin 3) * O) := hconcat (markerStar (Fin 3)) O hStar hO have hFiltered : is_DCF ((markerStar (Fin 3) * O) ⊓ oneMarkerPayload (Fin 3)) := DCF_inter_regular (markerStar (Fin 3) * O) (oneMarkerPayload (Fin 3)) hCat oneMarkerPayload_regular have hQuot : is_DCF ([marker] \\ ((markerStar (Fin 3) * O) ⊓ oneMarkerPayload (Fin 3))) := DCFHomomorphism.DCF_leftQuotient_singleton marker hFiltered change is_DCF ([marker] \\ ((markerStar (Fin 3) * (DPDA.optionalMarkedUnionQuot M₁ M₂).acceptsByFinalState) ⊓ oneMarkerPayload (Fin 3))) at hQuot rw [optional_concat_slice_eq_union M₁ M₂] at hQuot have hUnion := DCF_of_map_injective_DCF_rev (f := Sum.inr) (by intro a b h exact Sum.inr.inj h) (M₁.acceptsByFinalState + M₂.acceptsByFinalState) hQuot simpa [hM₁, hM₂] using hUnion end DCFConcatenation