From 4a941eb9471798ba849f203f03a7c5dab21da509 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Mon, 9 Dec 2024 11:52:32 +0300 Subject: [PATCH 01/34] make Seq.recOn cases_eliminator --- Mathlib/Data/Seq/Parallel.lean | 4 +- Mathlib/Data/Seq/Seq.lean | 130 +++++++++++++++------------------ 2 files changed, 60 insertions(+), 74 deletions(-) diff --git a/Mathlib/Data/Seq/Parallel.lean b/Mathlib/Data/Seq/Parallel.lean index ee4bdac0f33db6..16ea3f436d55d5 100644 --- a/Mathlib/Data/Seq/Parallel.lean +++ b/Mathlib/Data/Seq/Parallel.lean @@ -157,9 +157,7 @@ theorem terminates_parallel {S : WSeq (Computation α)} {c} (h : c ∈ S) [T : T have TT : ∀ l', Terminates (corec parallel.aux1 (l', S.tail)) := by intro apply IH _ _ _ (Or.inr _) T - rw [a] - cases' S with f al - rfl + rw [a, Seq.get?_tail] induction' e : Seq.get? S 0 with o · have D : Seq.destruct S = none := by dsimp [Seq.destruct] diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 8b25a1d0c7c0a5..d45af9a0910ef3 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -150,8 +150,10 @@ theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) have : s.get? m ≠ none := mt (s.le_stable m_le_n) this Option.ne_none_iff_exists'.mp this +@[simp] theorem not_mem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h +@[simp] theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s | ⟨_, _⟩ => Stream'.mem_cons (some a) _ @@ -230,14 +232,16 @@ theorem get?_tail (s : Seq α) (n) : get? (tail s) n = get? s (n + 1) := rfl /-- Recursion principle for sequences, compare with `List.recOn`. -/ -def recOn {C : Seq α → Sort v} (s : Seq α) (h1 : C nil) (h2 : ∀ x s, C (cons x s)) : - C s := by +@[cases_eliminator] +def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) + (cons : ∀ x s, motive (cons x s)) : + motive s := by cases' H : destruct s with v · rw [destruct_eq_nil H] - apply h1 + apply nil · cases' v with a s' rw [destruct_eq_cons H] - apply h2 + apply cons theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by @@ -251,10 +255,9 @@ theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) rw [TH] apply h1 _ _ (Or.inl rfl) -- Porting note: had to reshuffle `intro` - revert e; apply s.recOn _ fun b s' => _ - · intro e; injection e - · intro b s' e - have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s'; rfl + cases' s with b s' + · injection e + · have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s' using Subtype.recOn; rfl rw [h_eq] at e apply h1 _ _ (Or.inr (IH e)) @@ -331,20 +334,21 @@ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s match t₁, t₂, e with | _, _, ⟨s, s', rfl, rfl, r⟩ => by suffices head s = head s' ∧ R (tail s) (tail s') from - And.imp id (fun r => ⟨tail s, tail s', by cases s; rfl, by cases s'; rfl, r⟩) this + And.imp id (fun r => ⟨tail s, tail s', by cases s using Subtype.recOn; rfl, + by cases s' using Subtype.recOn; rfl, r⟩) this have := bisim r; revert r this - apply recOn s _ _ <;> apply recOn s' _ _ + cases' s with x s <;> cases' s' with x' s' · intro r _ constructor · rfl · assumption - · intro x s _ this + · intro _ this rw [destruct_nil, destruct_cons] at this exact False.elim this - · intro x s _ this + · intro _ this rw [destruct_nil, destruct_cons] at this exact False.elim this - · intro x s x' s' _ this + · intro _ this rw [destruct_cons, destruct_cons] at this rw [head_cons, head_cons, tail_cons, tail_cons] cases' this with h1 h2 @@ -568,10 +572,10 @@ def toListOrStream (s : Seq α) [Decidable s.Terminates] : List α ⊕ Stream' theorem nil_append (s : Seq α) : append nil s = s := by apply coinduction2; intro s dsimp [append]; rw [corec_eq] - dsimp [append]; apply recOn s _ _ + dsimp [append] + cases' s with x s · trivial - · intro x s - rw [destruct_cons] + · rw [destruct_cons] dsimp exact ⟨rfl, s, rfl, rfl⟩ @@ -718,10 +722,9 @@ theorem cons_append (a : α) (s t) : append (cons a s) t = cons a (append s t) : @[simp] theorem append_nil (s : Seq α) : append s nil = s := by apply coinduction2 s; intro s - apply recOn s _ _ + cases' s with x s · trivial - · intro x s - rw [cons_append, destruct_cons, destruct_cons] + · rw [cons_append, destruct_cons, destruct_cons] dsimp exact ⟨rfl, s, rfl, rfl⟩ @@ -732,15 +735,12 @@ theorem append_assoc (s t u : Seq α) : append (append s t) u = append s (append exact match s1, s2, h with | _, _, ⟨s, t, u, rfl, rfl⟩ => by - apply recOn s <;> simp - · apply recOn t <;> simp - · apply recOn u <;> simp - · intro _ u - refine ⟨nil, nil, u, ?_, ?_⟩ <;> simp - · intro _ t - refine ⟨nil, t, u, ?_, ?_⟩ <;> simp - · intro _ s - exact ⟨s, t, u, rfl, rfl⟩ + cases' s with _ s <;> simp + · cases' t with _ t <;> simp + · cases' u with _ u <;> simp + · refine ⟨nil, nil, u, ?_, ?_⟩ <;> simp + · refine ⟨nil, t, u, ?_, ?_⟩ <;> simp + · exact ⟨s, t, u, rfl, rfl⟩ · exact ⟨s, t, u, rfl, rfl⟩ @[simp] @@ -776,12 +776,10 @@ theorem map_append (f : α → β) (s t) : map f (append s t) = append (map f s) exact match s1, s2, h with | _, _, ⟨s, t, rfl, rfl⟩ => by - apply recOn s <;> simp - · apply recOn t <;> simp - · intro _ t - refine ⟨nil, t, ?_, ?_⟩ <;> simp - · intro _ s - exact ⟨s, t, rfl, rfl⟩ + cases' s with _ s <;> simp + · cases' t with _ t <;> simp + · refine ⟨nil, t, ?_, ?_⟩ <;> simp + · exact ⟨s, t, rfl, rfl⟩ @[simp] theorem map_get? (f : α → β) : ∀ s n, get? (map f s) n = (get? s n).map f @@ -835,15 +833,13 @@ theorem join_cons (a : α) (s S) : join (cons (a, s) S) = cons a (append s (join exact match s1, s2, h with | s, _, Or.inl <| Eq.refl s => by - apply recOn s; · trivial - · intro x s - rw [destruct_cons] + cases' s with x s; · trivial + · rw [destruct_cons] exact ⟨rfl, Or.inl rfl⟩ | _, _, Or.inr ⟨a, s, S, rfl, rfl⟩ => by - apply recOn s + cases' s with x s · simp [join_cons_cons, join_cons_nil] - · intro x s - simpa [join_cons_cons, join_cons_nil] using Or.inr ⟨x, s, S, rfl, rfl⟩ + · simpa [join_cons_cons, join_cons_nil] using Or.inr ⟨x, s, S, rfl, rfl⟩ @[simp] theorem join_append (S T : Seq (Seq1 α)) : join (append S T) = append (join S) (join T) := by @@ -854,18 +850,15 @@ theorem join_append (S T : Seq (Seq1 α)) : join (append S T) = append (join S) exact match s1, s2, h with | _, _, ⟨s, S, T, rfl, rfl⟩ => by - apply recOn s <;> simp - · apply recOn S <;> simp - · apply recOn T + cases' s with _ s <;> simp + · cases' S with s S <;> simp + · cases' T with s T · simp - · intro s T - cases' s with a s; simp only [join_cons, destruct_cons, true_and] + · cases' s with a s; simp only [join_cons, destruct_cons, true_and] refine ⟨s, nil, T, ?_, ?_⟩ <;> simp - · intro s S - cases' s with a s + · cases' s with a s simpa using ⟨s, S, T, rfl, rfl⟩ - · intro _ s - exact ⟨s, S, T, rfl, rfl⟩ + · exact ⟨s, S, T, rfl, rfl⟩ · refine ⟨nil, S, T, ?_, ?_⟩ <;> simp @[simp] @@ -920,11 +913,11 @@ theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) generalize e : append s₁ s₂ = ss; intro h; revert s₁ apply mem_rec_on h _ intro b s' o s₁ - apply s₁.recOn _ fun c t₁ => _ + cases' s₁ with c t₁ · intro m _ apply Or.inr simpa using m - · intro c t₁ m e + · intro m e have this := congr_arg destruct e cases' show a = c ∨ a ∈ append t₁ s₂ by simpa using m with e' m · rw [e'] @@ -1002,7 +995,7 @@ def bind (s : Seq1 α) (f : α → Seq1 β) : Seq1 β := @[simp] theorem join_map_ret (s : Seq α) : Seq.join (Seq.map ret s) = s := by - apply coinduction2 s; intro s; apply recOn s <;> simp [ret] + apply coinduction2 s; intro s; cases s <;> simp [ret] @[simp] theorem bind_ret (f : α → β) : ∀ s, bind s (ret ∘ f) = map f s @@ -1016,7 +1009,7 @@ theorem bind_ret (f : α → β) : ∀ s, bind s (ret ∘ f) = map f s theorem ret_bind (a : α) (f : α → Seq1 β) : bind (ret a) f = f a := by simp only [bind, map, ret.eq_1, map_nil] cases' f a with a s - apply recOn s <;> intros <;> simp + cases s <;> simp @[simp] theorem map_join' (f : α → β) (S) : Seq.map f (Seq.join S) = Seq.join (Seq.map (map f) S) := by @@ -1028,18 +1021,16 @@ theorem map_join' (f : α → β) (S) : Seq.map f (Seq.join S) = Seq.join (Seq.m exact match s1, s2, h with | _, _, ⟨s, S, rfl, rfl⟩ => by - apply recOn s <;> simp - · apply recOn S <;> simp - · intro x S - cases' x with a s + cases' s with _ s <;> simp + · cases' S with x S <;> simp + · cases' x with a s simpa [map] using ⟨_, _, rfl, rfl⟩ - · intro _ s - exact ⟨s, S, rfl, rfl⟩ + · exact ⟨s, S, rfl, rfl⟩ · refine ⟨nil, S, ?_, ?_⟩ <;> simp @[simp] theorem map_join (f : α → β) : ∀ S, map f (join S) = join (map (map f) S) - | ((a, s), S) => by apply recOn s <;> intros <;> simp [map] + | ((a, s), S) => by cases s <;> simp [map] @[simp] theorem join_join (SS : Seq (Seq1 (Seq1 α))) : @@ -1052,24 +1043,21 @@ theorem join_join (SS : Seq (Seq1 (Seq1 α))) : exact match s1, s2, h with | _, _, ⟨s, SS, rfl, rfl⟩ => by - apply recOn s <;> simp - · apply recOn SS <;> simp - · intro S SS - cases' S with s S; cases' s with x s + cases' s with _ s <;> simp + · cases' SS with S SS <;> simp + · cases' S with s S; cases' s with x s simp only [Seq.join_cons, join_append, destruct_cons] - apply recOn s <;> simp + cases' s with x s <;> simp · exact ⟨_, _, rfl, rfl⟩ - · intro x s - refine ⟨Seq.cons x (append s (Seq.join S)), SS, ?_, ?_⟩ <;> simp - · intro _ s - exact ⟨s, SS, rfl, rfl⟩ + · refine ⟨Seq.cons x (append s (Seq.join S)), SS, ?_, ?_⟩ <;> simp + · exact ⟨s, SS, rfl, rfl⟩ · refine ⟨nil, SS, ?_, ?_⟩ <;> simp @[simp] theorem bind_assoc (s : Seq1 α) (f : α → Seq1 β) (g : β → Seq1 γ) : bind (bind s f) g = bind s fun x : α => bind (f x) g := by cases' s with a s - -- Porting note (https://github.com/leanprover-community/mathlib4/issues/10745): was `simp [bind, map]`. + -- porting note (#10745): was `simp [bind, map]`. simp only [bind, map_pair, map_join] rw [← map_comp] simp only [show (fun x => join (map g (f x))) = join ∘ (map g ∘ f) from rfl] @@ -1080,7 +1068,7 @@ theorem bind_assoc (s : Seq1 α) (f : α → Seq1 β) (g : β → Seq1 γ) : -- give names to variables. induction' s using recOn with x s_1 <;> induction' S using recOn with x_1 s_2 <;> simp · cases' x_1 with x t - apply recOn t <;> intros <;> simp + cases t <;> simp · cases' x_1 with y t; simp instance monad : Monad Seq1 where From e7dd2f69282018159226b0473c04f7ac57ed8de0 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Mon, 9 Dec 2024 12:41:09 +0300 Subject: [PATCH 02/34] revert some --- Mathlib/Data/Seq/Seq.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index d45af9a0910ef3..78a7c424856dbd 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -150,10 +150,8 @@ theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) have : s.get? m ≠ none := mt (s.le_stable m_le_n) this Option.ne_none_iff_exists'.mp this -@[simp] theorem not_mem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h -@[simp] theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s | ⟨_, _⟩ => Stream'.mem_cons (some a) _ @@ -1057,7 +1055,7 @@ theorem join_join (SS : Seq (Seq1 (Seq1 α))) : theorem bind_assoc (s : Seq1 α) (f : α → Seq1 β) (g : β → Seq1 γ) : bind (bind s f) g = bind s fun x : α => bind (f x) g := by cases' s with a s - -- porting note (#10745): was `simp [bind, map]`. + -- Porting note (https://github.com/leanprover-community/mathlib4/issues/10745): was `simp [bind, map]`. simp only [bind, map_pair, map_join] rw [← map_comp] simp only [show (fun x => join (map g (f x))) = join ∘ (map g ∘ f) from rfl] From db575e957c7fa02b89e533cd0924b7bf3bc01066 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:01:07 +0300 Subject: [PATCH 03/34] general lemmas --- Mathlib/Data/Seq/Seq.lean | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 78a7c424856dbd..19e06221571b5f 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -5,6 +5,7 @@ Authors: Mario Carneiro -/ import Mathlib.Data.Option.NAry import Mathlib.Data.Seq.Computation +import Mathlib.Tactic.ApplyFun /-! # Possibly infinite lists @@ -64,6 +65,10 @@ theorem val_cons (s : Seq α) (x : α) : (cons x s).val = some x::s.val := def get? : Seq α → ℕ → Option α := Subtype.val +@[simp] +theorem val_eq_get {α : Type u} (li : Seq α) (n : ℕ) : li.val n = li.get? n := by + rfl + @[simp] theorem get?_mk (f hf) : @get? α ⟨f, hf⟩ = f := rfl @@ -165,6 +170,11 @@ theorem eq_or_mem_of_mem_cons {a b : α} : ∀ {s : Seq α}, a ∈ cons b s → theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ∈ s := ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ +@[simp] +theorem get?_mem {α : Type u} {li : Seq α} {n : ℕ} {x : α} (h : li.get? n = .some x) : x ∈ li := by + simp [Membership.mem, Seq.Mem, Any] + exact ⟨n, h.symm⟩ + /-- Destructor for a sequence, resulting in either `none` (for `nil`) or `some (a, s)` (for `cons a s`). -/ def destruct (s : Seq α) : Option (Seq1 α) := @@ -241,6 +251,46 @@ def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) rw [destruct_eq_cons H] apply cons +@[simp] +theorem noConfusion {α : Type u} {hd : α} {tl : Seq α} : (cons hd tl) ≠ .nil := by + intro h + apply_fun head at h + simp at h + +@[simp] +theorem noConfusion_symm {α : Type u} {hd : α} {tl : Seq α} : .nil ≠ (cons hd tl) := by + symm + simp + +theorem cons_eq_cons {α : Type u} {hd hd' : α} {tl tl' : Seq α} : + (cons hd tl = cons hd' tl') ↔ (hd = hd' ∧ tl = tl') := by + constructor + · intro h + constructor + · apply_fun head at h + simpa using h + · apply_fun tail at h + simpa using h + · rintro ⟨h_hd, h_tl⟩ + congr + +theorem head_eq_some {α : Type u} {li : Seq α} {hd : α} (h : li.head = some hd) : + li = cons hd li.tail := by + cases' li with hd' tl <;> simp at h + simpa [cons_eq_cons] + +theorem head_eq_none {α : Type u} {li : Seq α} (h : li.head = none) : li = nil := by + cases' li with hd tl + · rfl + · simp at h + +@[simp] +theorem head_eq_none_iff {α : Type u} {li : Seq α} : li.head = none ↔ li = nil := by + constructor + · apply head_eq_none + · intro h + simp [h] + theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by cases' M with k e; unfold Stream'.get at e @@ -303,6 +353,16 @@ theorem corec_eq (f : β → Option (α × β)) (b : β) : rw [Stream'.corec'_eq, Stream'.tail_cons] dsimp [Corec.f]; rw [h] +theorem corec_nil {α : Type u} {β : Type u} (g : β → Option (α × β)) (b : β) + (h : g b = .none) : corec g b = nil := by + apply destruct_eq_nil + simp [h] + +theorem corec_cons {α : Type u} {β : Type u} {g : β → Option (α × β)} {b : β} {hd : α} {tl : β} + (h : g b = .some (hd, tl)) : corec g b = cons hd (corec g tl) := by + apply destruct_eq_cons + simp [h] + section Bisim variable (R : Seq α → Seq α → Prop) From 6c0d1eb014a4c55e1b4a6fe109709435315dea4a Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:04:47 +0300 Subject: [PATCH 04/34] drop lemmas --- Mathlib/Data/Seq/Seq.lean | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 19e06221571b5f..75c81e9ee5c551 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -943,6 +943,15 @@ def toList' {α} (s : Seq α) : Computation (List α) := | some (a, s') => Sum.inr (a::l, s')) ([], s) +@[simp] +theorem drop_get? {α : Type u} {n m : ℕ} {li : Seq α} : (li.drop n).get? m = li.get? (n + m) := by + induction n generalizing m with + | zero => simp + | succ k ih => + simp [Seq.get?_tail] + rw [show k + 1 + m = k + (m + 1) by omega] + apply ih + theorem dropn_add (s : Seq α) (m) : ∀ n, drop s (m + n) = drop (drop s m) n | 0 => rfl | n + 1 => congr_arg tail (dropn_add s _ n) @@ -955,6 +964,19 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by induction' n with n IH generalizing s; · rfl rw [← get?_tail, ← dropn_tail]; apply IH +theorem drop_succ_cons {α : Type u} {hd : α} {tl : Seq α} {n : ℕ} : + (cons hd tl).drop (n + 1) = tl.drop n := by + rw [← dropn_tail] + simp + +@[simp] +theorem drop_nil {α : Type u} {n : ℕ} : (@nil α).drop n = nil := by + induction n with + | zero => + simp + | succ m ih => + simp [← dropn_tail, ih] + theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s | ⟨_, _⟩ => Stream'.mem_map (Option.map f) From de0847bf0a797b63ce3ebd9a528c0e4068d21831 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:15:12 +0300 Subject: [PATCH 05/34] termination lemmas --- Mathlib/Data/Seq/Seq.lean | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 75c81e9ee5c551..70281770063562 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -661,11 +661,29 @@ theorem terminatedAt_ofList (l : List α) : theorem terminates_ofList (l : List α) : (ofList l).Terminates := ⟨_, terminatedAt_ofList l⟩ -theorem terminatedAt_nil : TerminatedAt (nil : Seq α) 0 := rfl +@[simp] +theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl + +@[simp] +theorem cons_not_terminatedAt_zero {α : Type u} {hd : α} {tl : Seq α} : + ¬(cons hd tl).TerminatedAt 0 := by + simp [TerminatedAt] + +@[simp] +theorem cons_terminatedAt_succ_iff {α : Type u} {hd : α} {tl : Seq α} {n : ℕ} : + (cons hd tl).TerminatedAt (n + 1) ↔ tl.TerminatedAt n := by + simp [TerminatedAt] @[simp] theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ +@[simp] +theorem terminates_cons_iff {hd : α} {tl : Seq α} : + (cons hd tl).Terminates ↔ tl.Terminates := by + constructor <;> intro ⟨n, h⟩ + · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ + · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ + @[simp] theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl From d0c4d00d80d7ad37819a71180bf3f274e0c09b1d Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:21:29 +0300 Subject: [PATCH 06/34] take lemmas --- Mathlib/Data/Seq/Seq.lean | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 70281770063562..192ef92a630de4 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -637,6 +637,19 @@ theorem nil_append (s : Seq α) : append nil s = s := by dsimp exact ⟨rfl, s, rfl, rfl⟩ +@[simp] +theorem take_nil {α : Type u} {n : ℕ} : (nil (α := α)).take n = List.nil := by + cases n <;> rfl + +@[simp] +theorem take_zero {α : Type u} {li : Seq α} : li.take 0 = [] := by + cases li <;> rfl + +@[simp] +theorem take_succ_cons {α : Type u} {n : ℕ} {hd : α} {tl : Seq α} : + (cons hd tl).take (n + 1) = hd :: tl.take n := by + rfl + @[simp] theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), (s.take k)[n]? = if n < k then s.get? n else none @@ -654,6 +667,27 @@ theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), | 0 => simp | n+1 => simp [List.get?_cons_succ, Nat.add_lt_add_iff_right, get?_cons_succ, getElem?_take] +theorem get?_mem_take {α : Type u} {li : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} + (h_get : li.get? m = .some x) : x ∈ li.take n := by + induction m generalizing n li with + | zero => + obtain ⟨l, hl⟩ := Nat.exists_add_one_eq.mpr h_mn + rw [← hl] + rw [take, head_eq_some h_get] + simp + | succ k ih => + obtain ⟨l, hl⟩ := Nat.exists_eq_add_of_lt h_mn + subst hl + have : ∃ y, li.get? 0 = .some y := by + apply ge_stable _ _ h_get + simp + obtain ⟨y, hy⟩ := this + rw [take, head_eq_some hy] + simp + right + apply ih (by omega) + rwa [get?_tail] + theorem terminatedAt_ofList (l : List α) : (ofList l).TerminatedAt l.length := by simp [ofList, TerminatedAt] @@ -741,6 +775,18 @@ theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : n < s.length h ↔ ∃ a, a ∈ s.get? n := by rw [← lt_length_iff']; simp [h] +theorem length_take_le {α : Type u} {li : Seq α} {n : ℕ} : (li.take n).length ≤ n := by + induction n generalizing li with + | zero => simp + | succ m ih => + rw [take] + cases li.destruct with + | none => simp + | some v => + obtain ⟨x, r⟩ := v + simp + apply ih + theorem length_take_of_le_length {s : Seq α} {n : ℕ} (hle : ∀ h : s.Terminates, n ≤ s.length h) : (s.take n).length = n := by induction n generalizing s with @@ -982,6 +1028,7 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by induction' n with n IH generalizing s; · rfl rw [← get?_tail, ← dropn_tail]; apply IH +@[simp] theorem drop_succ_cons {α : Type u} {hd : α} {tl : Seq α} {n : ℕ} : (cons hd tl).drop (n + 1) = tl.drop n := by rw [← dropn_tail] @@ -995,6 +1042,21 @@ theorem drop_nil {α : Type u} {n : ℕ} : (@nil α).drop n = nil := by | succ m ih => simp [← dropn_tail, ih] +theorem take_drop {α : Type u} {li : Seq α} {n m : ℕ} : + (li.take n).drop m = (li.drop m).take (n - m) := by + induction m generalizing n li with + | zero => simp + | succ k ih => + cases' li with hd tl + · simp + cases n with + | zero => simp + | succ l => + simp only [take, destruct_cons, List.drop_succ_cons, Nat.reduceSubDiff] + rw [ih] + congr 1 + rw [drop_succ_cons] + theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s | ⟨_, _⟩ => Stream'.mem_map (Option.map f) From b4b2f7b07f8929b0a65e3e01390a62221b6afddf Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:35:00 +0300 Subject: [PATCH 07/34] fold lemmas --- Mathlib/Data/Seq/Seq.lean | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 192ef92a630de4..781ce7b64181a3 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -570,6 +570,15 @@ def splitAt : ℕ → Seq α → List α × Seq α let (l, r) := splitAt n s' (List.cons x l, r) +/-- Folds a sequence using `f`, producing sequence of intermedieate values, i.e. +`[init, f init li.head, f (f init li.head) li.tail.head, ...]`. -/ +def fold {α : Type u} {β : Type v} (li : Seq α) (init : β) (f : β → α → β) : Seq β := + let g : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => + match destruct x with + | none => .none + | some (hd, tl) => .some (f acc hd, f acc hd, tl) + cons init <| corec g (init, li) + section ZipWith /-- Combine two sequences with a function -/ @@ -1098,6 +1107,26 @@ theorem enum_cons (s : Seq α) (x : α) : · simp only [get?_enum, get?_cons_succ, map_get?, Option.map_map] congr +@[simp] +theorem fold_nil {α : Type u} {β : Type u} (init : β) (f : β → α → β) : + nil.fold init f = cons init nil := by + unfold fold + simp [corec_nil] + +@[simp] +theorem fold_cons {α : Type u} {β : Type u} (init : β) (f : β → α → β) (hd : α) (tl : Seq α) : + (cons hd tl).fold init f = cons init (tl.fold (f init hd) f) := by + unfold fold + simp only + congr + rw [corec_cons] + simp + +@[simp] +theorem fold_head {α : Type u} {β : Type u} (init : β) (f : β → α → β) (li : Seq α) : + (li.fold init f).head = init := by + simp [fold] + end Seq namespace Seq1 From 620f8cab1c29d6913da47603fd4ce91bd5c60fe3 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:45:35 +0300 Subject: [PATCH 08/34] rename --- Mathlib/Data/Seq/Seq.lean | 106 +++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 781ce7b64181a3..1417854243361f 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -66,7 +66,7 @@ def get? : Seq α → ℕ → Option α := Subtype.val @[simp] -theorem val_eq_get {α : Type u} (li : Seq α) (n : ℕ) : li.val n = li.get? n := by +theorem val_eq_get (s : Seq α) (n : ℕ) : s.val n = s.get? n := by rfl @[simp] @@ -171,7 +171,7 @@ theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ @[simp] -theorem get?_mem {α : Type u} {li : Seq α} {n : ℕ} {x : α} (h : li.get? n = .some x) : x ∈ li := by +theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := by simp [Membership.mem, Seq.Mem, Any] exact ⟨n, h.symm⟩ @@ -252,18 +252,18 @@ def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) apply cons @[simp] -theorem noConfusion {α : Type u} {hd : α} {tl : Seq α} : (cons hd tl) ≠ .nil := by +theorem noConfusion {x : α} {s : Seq α} : (cons x s) ≠ .nil := by intro h apply_fun head at h simp at h @[simp] -theorem noConfusion_symm {α : Type u} {hd : α} {tl : Seq α} : .nil ≠ (cons hd tl) := by +theorem noConfusion_symm {x : α} {s : Seq α} : .nil ≠ (cons x s) := by symm simp -theorem cons_eq_cons {α : Type u} {hd hd' : α} {tl tl' : Seq α} : - (cons hd tl = cons hd' tl') ↔ (hd = hd' ∧ tl = tl') := by +theorem cons_eq_cons {x x' : α} {s s' : Seq α} : + (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by constructor · intro h constructor @@ -271,21 +271,21 @@ theorem cons_eq_cons {α : Type u} {hd hd' : α} {tl tl' : Seq α} : simpa using h · apply_fun tail at h simpa using h - · rintro ⟨h_hd, h_tl⟩ + · rintro ⟨hx, hs⟩ congr -theorem head_eq_some {α : Type u} {li : Seq α} {hd : α} (h : li.head = some hd) : - li = cons hd li.tail := by - cases' li with hd' tl <;> simp at h +theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : + s = cons x s.tail := by + cases' s with x' tl <;> simp at h simpa [cons_eq_cons] -theorem head_eq_none {α : Type u} {li : Seq α} (h : li.head = none) : li = nil := by - cases' li with hd tl +theorem head_eq_none {s : Seq α} (h : s.head = none) : s = nil := by + cases' s with x tl · rfl · simp at h @[simp] -theorem head_eq_none_iff {α : Type u} {li : Seq α} : li.head = none ↔ li = nil := by +theorem head_eq_none_iff {s : Seq α} : s.head = none ↔ s = nil := by constructor · apply head_eq_none · intro h @@ -353,13 +353,13 @@ theorem corec_eq (f : β → Option (α × β)) (b : β) : rw [Stream'.corec'_eq, Stream'.tail_cons] dsimp [Corec.f]; rw [h] -theorem corec_nil {α : Type u} {β : Type u} (g : β → Option (α × β)) (b : β) - (h : g b = .none) : corec g b = nil := by +theorem corec_nil (f : β → Option (α × β)) (b : β) + (h : f b = .none) : corec f b = nil := by apply destruct_eq_nil simp [h] -theorem corec_cons {α : Type u} {β : Type u} {g : β → Option (α × β)} {b : β} {hd : α} {tl : β} - (h : g b = .some (hd, tl)) : corec g b = cons hd (corec g tl) := by +theorem corec_cons {f : β → Option (α × β)} {b : β} {x : α} {s : β} + (h : f b = .some (x, s)) : corec f b = cons x (corec f s) := by apply destruct_eq_cons simp [h] @@ -571,13 +571,13 @@ def splitAt : ℕ → Seq α → List α × Seq α (List.cons x l, r) /-- Folds a sequence using `f`, producing sequence of intermedieate values, i.e. -`[init, f init li.head, f (f init li.head) li.tail.head, ...]`. -/ -def fold {α : Type u} {β : Type v} (li : Seq α) (init : β) (f : β → α → β) : Seq β := - let g : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => +`[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ +def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := + let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => match destruct x with | none => .none - | some (hd, tl) => .some (f acc hd, f acc hd, tl) - cons init <| corec g (init, li) + | some (x, s) => .some (f acc x, f acc x, s) + cons init <| corec f (init, s) section ZipWith @@ -647,16 +647,16 @@ theorem nil_append (s : Seq α) : append nil s = s := by exact ⟨rfl, s, rfl, rfl⟩ @[simp] -theorem take_nil {α : Type u} {n : ℕ} : (nil (α := α)).take n = List.nil := by +theorem take_nil {n : ℕ} : (nil (α := α)).take n = List.nil := by cases n <;> rfl @[simp] -theorem take_zero {α : Type u} {li : Seq α} : li.take 0 = [] := by - cases li <;> rfl +theorem take_zero {s : Seq α} : s.take 0 = [] := by + cases s <;> rfl @[simp] -theorem take_succ_cons {α : Type u} {n : ℕ} {hd : α} {tl : Seq α} : - (cons hd tl).take (n + 1) = hd :: tl.take n := by +theorem take_succ_cons {n : ℕ} {x : α} {s : Seq α} : + (cons x s).take (n + 1) = x :: s.take n := by rfl @[simp] @@ -676,9 +676,9 @@ theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), | 0 => simp | n+1 => simp [List.get?_cons_succ, Nat.add_lt_add_iff_right, get?_cons_succ, getElem?_take] -theorem get?_mem_take {α : Type u} {li : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} - (h_get : li.get? m = .some x) : x ∈ li.take n := by - induction m generalizing n li with +theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} + (h_get : s.get? m = .some x) : x ∈ s.take n := by + induction m generalizing n s with | zero => obtain ⟨l, hl⟩ := Nat.exists_add_one_eq.mpr h_mn rw [← hl] @@ -687,7 +687,7 @@ theorem get?_mem_take {α : Type u} {li : Seq α} {m n : ℕ} (h_mn : m < n) {x | succ k ih => obtain ⟨l, hl⟩ := Nat.exists_eq_add_of_lt h_mn subst hl - have : ∃ y, li.get? 0 = .some y := by + have : ∃ y, s.get? 0 = .some y := by apply ge_stable _ _ h_get simp obtain ⟨y, hy⟩ := this @@ -708,21 +708,21 @@ theorem terminates_ofList (l : List α) : (ofList l).Terminates := theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl @[simp] -theorem cons_not_terminatedAt_zero {α : Type u} {hd : α} {tl : Seq α} : - ¬(cons hd tl).TerminatedAt 0 := by +theorem cons_not_terminatedAt_zero {x : α} {s : Seq α} : + ¬(cons x s).TerminatedAt 0 := by simp [TerminatedAt] @[simp] -theorem cons_terminatedAt_succ_iff {α : Type u} {hd : α} {tl : Seq α} {n : ℕ} : - (cons hd tl).TerminatedAt (n + 1) ↔ tl.TerminatedAt n := by +theorem cons_terminatedAt_succ_iff {x : α} {s : Seq α} {n : ℕ} : + (cons x s).TerminatedAt (n + 1) ↔ s.TerminatedAt n := by simp [TerminatedAt] @[simp] theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ @[simp] -theorem terminates_cons_iff {hd : α} {tl : Seq α} : - (cons hd tl).Terminates ↔ tl.Terminates := by +theorem terminates_cons_iff {x : α} {s : Seq α} : + (cons x s).Terminates ↔ s.Terminates := by constructor <;> intro ⟨n, h⟩ · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ @@ -784,12 +784,12 @@ theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : n < s.length h ↔ ∃ a, a ∈ s.get? n := by rw [← lt_length_iff']; simp [h] -theorem length_take_le {α : Type u} {li : Seq α} {n : ℕ} : (li.take n).length ≤ n := by - induction n generalizing li with +theorem length_take_le {s : Seq α} {n : ℕ} : (s.take n).length ≤ n := by + induction n generalizing s with | zero => simp | succ m ih => rw [take] - cases li.destruct with + cases s.destruct with | none => simp | some v => obtain ⟨x, r⟩ := v @@ -1017,7 +1017,7 @@ def toList' {α} (s : Seq α) : Computation (List α) := ([], s) @[simp] -theorem drop_get? {α : Type u} {n m : ℕ} {li : Seq α} : (li.drop n).get? m = li.get? (n + m) := by +theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) := by induction n generalizing m with | zero => simp | succ k ih => @@ -1038,25 +1038,25 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by rw [← get?_tail, ← dropn_tail]; apply IH @[simp] -theorem drop_succ_cons {α : Type u} {hd : α} {tl : Seq α} {n : ℕ} : - (cons hd tl).drop (n + 1) = tl.drop n := by +theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : + (cons x s).drop (n + 1) = s.drop n := by rw [← dropn_tail] simp @[simp] -theorem drop_nil {α : Type u} {n : ℕ} : (@nil α).drop n = nil := by +theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by induction n with | zero => simp | succ m ih => simp [← dropn_tail, ih] -theorem take_drop {α : Type u} {li : Seq α} {n m : ℕ} : - (li.take n).drop m = (li.drop m).take (n - m) := by - induction m generalizing n li with +theorem take_drop {s : Seq α} {n m : ℕ} : + (s.take n).drop m = (s.drop m).take (n - m) := by + induction m generalizing n s with | zero => simp | succ k ih => - cases' li with hd tl + cases' s with x tl · simp cases n with | zero => simp @@ -1108,14 +1108,14 @@ theorem enum_cons (s : Seq α) (x : α) : congr @[simp] -theorem fold_nil {α : Type u} {β : Type u} (init : β) (f : β → α → β) : +theorem fold_nil (init : β) (f : β → α → β) : nil.fold init f = cons init nil := by unfold fold simp [corec_nil] @[simp] -theorem fold_cons {α : Type u} {β : Type u} (init : β) (f : β → α → β) (hd : α) (tl : Seq α) : - (cons hd tl).fold init f = cons init (tl.fold (f init hd) f) := by +theorem fold_cons (init : β) (f : β → α → β) (x : α) (s : Seq α) : + (cons x s).fold init f = cons init (s.fold (f init x) f) := by unfold fold simp only congr @@ -1123,8 +1123,8 @@ theorem fold_cons {α : Type u} {β : Type u} (init : β) (f : β → α → β) simp @[simp] -theorem fold_head {α : Type u} {β : Type u} (init : β) (f : β → α → β) (li : Seq α) : - (li.fold init f).head = init := by +theorem fold_head (init : β) (f : β → α → β) (s : Seq α) : + (s.fold init f).head = init := by simp [fold] end Seq From d5c4cb61f38662f96c290c0f7d7b6bdb217c57a4 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 12:59:57 +0300 Subject: [PATCH 09/34] fix --- Mathlib/Data/Seq/Seq.lean | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 1417854243361f..809240ca0fe494 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -549,7 +549,7 @@ def drop (s : Seq α) : ℕ → Seq α | 0 => s | n + 1 => tail (drop s n) -attribute [simp] drop +-- attribute [simp] drop /-- Take the first `n` elements of the sequence (producing a list) -/ def take : ℕ → Seq α → List α @@ -1019,9 +1019,9 @@ def toList' {α} (s : Seq α) : Computation (List α) := @[simp] theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) := by induction n generalizing m with - | zero => simp + | zero => simp [drop] | succ k ih => - simp [Seq.get?_tail] + simp [Seq.get?_tail, drop] rw [show k + 1 + m = k + (m + 1) by omega] apply ih @@ -1040,21 +1040,18 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by @[simp] theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : (cons x s).drop (n + 1) = s.drop n := by - rw [← dropn_tail] - simp + simp [← dropn_tail] @[simp] theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by induction n with - | zero => - simp - | succ m ih => - simp [← dropn_tail, ih] + | zero => simp [drop] + | succ m ih => simp [← dropn_tail, ih] theorem take_drop {s : Seq α} {n m : ℕ} : (s.take n).drop m = (s.drop m).take (n - m) := by induction m generalizing n s with - | zero => simp + | zero => simp [drop] | succ k ih => cases' s with x tl · simp From 6204386651cd35675c32dfff48c018bd93ab40fb Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Tue, 10 Dec 2024 13:11:36 +0300 Subject: [PATCH 10/34] golf --- Mathlib/Data/Seq/Seq.lean | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 809240ca0fe494..d4469299bf2988 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -171,9 +171,7 @@ theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ @[simp] -theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := by - simp [Membership.mem, Seq.Mem, Any] - exact ⟨n, h.symm⟩ +theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ /-- Destructor for a sequence, resulting in either `none` (for `nil`) or `some (a, s)` (for `cons a s`). -/ @@ -258,9 +256,7 @@ theorem noConfusion {x : α} {s : Seq α} : (cons x s) ≠ .nil := by simp at h @[simp] -theorem noConfusion_symm {x : α} {s : Seq α} : .nil ≠ (cons x s) := by - symm - simp +theorem noConfusion_symm {x : α} {s : Seq α} : .nil ≠ (cons x s) := noConfusion.symm theorem cons_eq_cons {x x' : α} {s s' : Seq α} : (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by @@ -271,7 +267,7 @@ theorem cons_eq_cons {x x' : α} {s s' : Seq α} : simpa using h · apply_fun tail at h simpa using h - · rintro ⟨hx, hs⟩ + · intro ⟨hx, hs⟩ congr theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : @@ -681,8 +677,7 @@ theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} induction m generalizing n s with | zero => obtain ⟨l, hl⟩ := Nat.exists_add_one_eq.mpr h_mn - rw [← hl] - rw [take, head_eq_some h_get] + rw [← hl, take, head_eq_some h_get] simp | succ k ih => obtain ⟨l, hl⟩ := Nat.exists_eq_add_of_lt h_mn @@ -793,8 +788,7 @@ theorem length_take_le {s : Seq α} {n : ℕ} : (s.take n).length ≤ n := by | none => simp | some v => obtain ⟨x, r⟩ := v - simp - apply ih + simpa using ih theorem length_take_of_le_length {s : Seq α} {n : ℕ} (hle : ∀ h : s.Terminates, n ≤ s.length h) : (s.take n).length = n := by @@ -1022,8 +1016,8 @@ theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) | zero => simp [drop] | succ k ih => simp [Seq.get?_tail, drop] - rw [show k + 1 + m = k + (m + 1) by omega] - apply ih + convert ih using 2 + omega theorem dropn_add (s : Seq α) (m) : ∀ n, drop s (m + n) = drop (drop s m) n | 0 => rfl @@ -1114,7 +1108,7 @@ theorem fold_nil (init : β) (f : β → α → β) : theorem fold_cons (init : β) (f : β → α → β) (x : α) (s : Seq α) : (cons x s).fold init f = cons init (s.fold (f init x) f) := by unfold fold - simp only + dsimp only congr rw [corec_cons] simp From f47f46318f061d864e0e667bfd78c9fc000fa9fe Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 14 Dec 2024 03:13:07 +0300 Subject: [PATCH 11/34] suggestions --- Mathlib/Data/Seq/Seq.lean | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index d4469299bf2988..8b1138ab8e8ad8 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -250,13 +250,13 @@ def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) apply cons @[simp] -theorem noConfusion {x : α} {s : Seq α} : (cons x s) ≠ .nil := by +theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by intro h apply_fun head at h simp at h @[simp] -theorem noConfusion_symm {x : α} {s : Seq α} : .nil ≠ (cons x s) := noConfusion.symm +theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm theorem cons_eq_cons {x x' : α} {s s' : Seq α} : (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by @@ -267,7 +267,7 @@ theorem cons_eq_cons {x x' : α} {s s' : Seq α} : simpa using h · apply_fun tail at h simpa using h - · intro ⟨hx, hs⟩ + · intro ⟨_, _⟩ congr theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : @@ -545,8 +545,6 @@ def drop (s : Seq α) : ℕ → Seq α | 0 => s | n + 1 => tail (drop s n) --- attribute [simp] drop - /-- Take the first `n` elements of the sequence (producing a list) -/ def take : ℕ → Seq α → List α | 0, _ => [] @@ -566,7 +564,7 @@ def splitAt : ℕ → Seq α → List α × Seq α let (l, r) := splitAt n s' (List.cons x l, r) -/-- Folds a sequence using `f`, producing sequence of intermedieate values, i.e. +/-- Folds a sequence using `f`, producing a sequence of intermediate values, i.e. `[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => From 65a2c3f82b1be79979c47bda66a38131c6b40d91 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Thu, 19 Dec 2024 14:32:04 +0300 Subject: [PATCH 12/34] rearrange --- Mathlib/Data/Seq/Parallel.lean | 2 +- Mathlib/Data/Seq/Seq.lean | 806 ++++++++++++++++++--------------- 2 files changed, 431 insertions(+), 377 deletions(-) diff --git a/Mathlib/Data/Seq/Parallel.lean b/Mathlib/Data/Seq/Parallel.lean index 16ea3f436d55d5..5bc0a9d1e2e954 100644 --- a/Mathlib/Data/Seq/Parallel.lean +++ b/Mathlib/Data/Seq/Parallel.lean @@ -166,7 +166,7 @@ theorem terminates_parallel {S : WSeq (Computation α)} {c} (h : c ∈ S) [T : T rw [D] simp only have TT := TT l' - rwa [Seq.destruct_eq_nil D, Seq.tail_nil] at TT + rwa [Seq.destruct_eq_none D, Seq.tail_nil] at TT · have D : Seq.destruct S = some (o, S.tail) := by dsimp [Seq.destruct] rw [e] diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 8b1138ab8e8ad8..c088739361c4d1 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -43,6 +43,40 @@ namespace Seq variable {α : Type u} {β : Type v} {γ : Type w} +/-- Get the nth element of a sequence (if it exists) -/ +def get? : Seq α → ℕ → Option α := + Subtype.val + +@[simp] +theorem val_eq_get (s : Seq α) (n : ℕ) : s.val n = s.get? n := by + rfl + +@[simp] +theorem get?_mk (f hf) : @get? α ⟨f, hf⟩ = f := + rfl + +theorem le_stable (s : Seq α) {m n} (h : m ≤ n) : s.get? m = none → s.get? n = none := by + cases' s with f al + induction' h with n _ IH + exacts [id, fun h2 => al (IH h2)] + +/-- If `s.get? n = some aₙ` for some value `aₙ`, then there is also some value `aₘ` such +that `s.get? = some aₘ` for `m ≤ n`. +-/ +theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) + (s_nth_eq_some : s.get? n = some aₙ) : ∃ aₘ : α, s.get? m = some aₘ := + have : s.get? n ≠ none := by simp [s_nth_eq_some] + have : s.get? m ≠ none := mt (s.le_stable m_le_n) this + Option.ne_none_iff_exists'.mp this + +@[ext] +protected theorem ext {s t : Seq α} (h : ∀ n : ℕ, s.get? n = t.get? n) : s = t := + Subtype.eq <| funext h + +/-! +### Constructors +-/ + /-- The empty sequence -/ def nil : Seq α := ⟨Stream'.const none, fun {_} _ => rfl⟩ @@ -61,21 +95,15 @@ def cons (a : α) (s : Seq α) : Seq α := theorem val_cons (s : Seq α) (x : α) : (cons x s).val = some x::s.val := rfl -/-- Get the nth element of a sequence (if it exists) -/ -def get? : Seq α → ℕ → Option α := - Subtype.val - -@[simp] -theorem val_eq_get (s : Seq α) (n : ℕ) : s.val n = s.get? n := by - rfl - @[simp] -theorem get?_mk (f hf) : @get? α ⟨f, hf⟩ = f := +theorem get?_nil (n : ℕ) : (@nil α).get? n = none := rfl @[simp] -theorem get?_nil (n : ℕ) : (@nil α).get? n = none := - rfl +theorem get?_zero_eq_none {s : Seq α} : s.get? 0 = none ↔ s = nil := by + refine ⟨fun h => ?_, fun h => h ▸ rfl⟩ + ext1 n + exact le_stable s (Nat.zero_le _) h @[simp] theorem get?_cons_zero (a : α) (s : Seq α) : (cons a s).get? 0 = some a := @@ -85,9 +113,14 @@ theorem get?_cons_zero (a : α) (s : Seq α) : (cons a s).get? 0 = some a := theorem get?_cons_succ (a : α) (s : Seq α) (n : ℕ) : (cons a s).get? (n + 1) = s.get? n := rfl -@[ext] -protected theorem ext {s t : Seq α} (h : ∀ n : ℕ, s.get? n = t.get? n) : s = t := - Subtype.eq <| funext h +@[simp] +theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by + intro h + apply_fun (·.get? 0) at h + simp at h + +@[simp] +theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm theorem cons_injective2 : Function.Injective2 (cons : α → Seq α → Seq α) := fun x y s t h => ⟨by rw [← Option.some_inj, ← get?_cons_zero, h, get?_cons_zero], @@ -99,26 +132,16 @@ theorem cons_left_injective (s : Seq α) : Function.Injective fun x => cons x s theorem cons_right_injective (x : α) : Function.Injective (cons x) := cons_injective2.right _ -/-- A sequence has terminated at position `n` if the value at position `n` equals `none`. -/ -def TerminatedAt (s : Seq α) (n : ℕ) : Prop := - s.get? n = none - -/-- It is decidable whether a sequence terminates at a given position. -/ -instance terminatedAtDecidable (s : Seq α) (n : ℕ) : Decidable (s.TerminatedAt n) := - decidable_of_iff' (s.get? n).isNone <| by unfold TerminatedAt; cases s.get? n <;> simp - -/-- A sequence terminates if there is some position `n` at which it has terminated. -/ -def Terminates (s : Seq α) : Prop := - ∃ n : ℕ, s.TerminatedAt n - -theorem not_terminates_iff {s : Seq α} : ¬s.Terminates ↔ ∀ n, (s.get? n).isSome := by - simp only [Terminates, TerminatedAt, ← Ne.eq_def, Option.ne_none_iff_isSome, not_exists, iff_self] +theorem cons_eq_cons {x x' : α} {s s' : Seq α} : + (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by + constructor + · apply cons_injective2 + · intro ⟨_, _⟩ + congr -/-- Functorial action of the functor `Option (α × _)` -/ -@[simp] -def omap (f : β → γ) : Option (α × β) → Option (α × γ) - | none => none - | some (a, b) => some (a, f b) +/-! +### Destructors +-/ /-- Get the first element of a sequence -/ def head (s : Seq α) : Option α := @@ -130,55 +153,31 @@ def tail (s : Seq α) : Seq α := cases' s with f al exact al n'⟩ -/-- member definition for `Seq`-/ -protected def Mem (s : Seq α) (a : α) := - some a ∈ s.1 - -instance : Membership α (Seq α) := - ⟨Seq.Mem⟩ - -theorem le_stable (s : Seq α) {m n} (h : m ≤ n) : s.get? m = none → s.get? n = none := by - cases' s with f al - induction' h with n _ IH - exacts [id, fun h2 => al (IH h2)] - -/-- If a sequence terminated at position `n`, it also terminated at `m ≥ n`. -/ -theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.TerminatedAt m → s.TerminatedAt n := - le_stable - -/-- If `s.get? n = some aₙ` for some value `aₙ`, then there is also some value `aₘ` such -that `s.get? = some aₘ` for `m ≤ n`. --/ -theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) - (s_nth_eq_some : s.get? n = some aₙ) : ∃ aₘ : α, s.get? m = some aₘ := - have : s.get? n ≠ none := by simp [s_nth_eq_some] - have : s.get? m ≠ none := mt (s.le_stable m_le_n) this - Option.ne_none_iff_exists'.mp this - -theorem not_mem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h - -theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s - | ⟨_, _⟩ => Stream'.mem_cons (some a) _ - -theorem mem_cons_of_mem (y : α) {a : α} : ∀ {s : Seq α}, a ∈ s → a ∈ cons y s - | ⟨_, _⟩ => Stream'.mem_cons_of_mem (some y) +/-- Destructor for a sequence, resulting in either `none` (for `nil`) or + `some (a, s)` (for `cons a s`). -/ +def destruct (s : Seq α) : Option (Seq1 α) := + (fun a' => (a', s.tail)) <$> get? s 0 -theorem eq_or_mem_of_mem_cons {a b : α} : ∀ {s : Seq α}, a ∈ cons b s → a = b ∨ a ∈ s - | ⟨_, _⟩, h => (Stream'.eq_or_mem_of_mem_cons h).imp_left fun h => by injection h +-- Porting note: needed universe annotation to avoid universe issues +theorem head_eq_destruct (s : Seq α) : head.{u} s = Prod.fst.{u} <$> destruct.{u} s := by + unfold destruct head; cases get? s 0 <;> rfl @[simp] -theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ∈ s := - ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ +theorem get?_tail (s : Seq α) (n) : get? (tail s) n = get? s (n + 1) := + rfl @[simp] -theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ +theorem destruct_nil : destruct (nil : Seq α) = none := + rfl -/-- Destructor for a sequence, resulting in either `none` (for `nil`) or - `some (a, s)` (for `cons a s`). -/ -def destruct (s : Seq α) : Option (Seq1 α) := - (fun a' => (a', s.tail)) <$> get? s 0 +@[simp] +theorem destruct_cons (a : α) : ∀ s, destruct (cons a s) = some (a, s) + | ⟨f, al⟩ => by + unfold cons destruct Functor.map + apply congr_arg fun s => some (a, s) + apply Subtype.eq; dsimp [tail] -theorem destruct_eq_nil {s : Seq α} : destruct s = none → s = nil := by +theorem destruct_eq_none {s : Seq α} : destruct s = none → s = nil := by dsimp [destruct] induction' f0 : get? s 0 <;> intro h · apply Subtype.eq @@ -200,21 +199,6 @@ theorem destruct_eq_cons {s : Seq α} {a s'} : destruct s = some (a, s') → s = rw [← f0] exact (Stream'.eta f).symm -@[simp] -theorem destruct_nil : destruct (nil : Seq α) = none := - rfl - -@[simp] -theorem destruct_cons (a : α) : ∀ s, destruct (cons a s) = some (a, s) - | ⟨f, al⟩ => by - unfold cons destruct Functor.map - apply congr_arg fun s => some (a, s) - apply Subtype.eq; dsimp [tail] - --- Porting note: needed universe annotation to avoid universe issues -theorem head_eq_destruct (s : Seq α) : head.{u} s = Prod.fst.{u} <$> destruct.{u} s := by - unfold destruct head; cases get? s 0 <;> rfl - @[simp] theorem head_nil : head (nil : Seq α) = none := rfl @@ -233,9 +217,25 @@ theorem tail_cons (a : α) (s) : tail (cons a s) = s := by apply Subtype.eq dsimp [tail, cons] +theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : + s = cons x s.tail := by + ext1 n + cases' n <;> simp + exact h + +theorem head_eq_none {s : Seq α} (h : s.head = none) : s = nil := + get?_zero_eq_none.mp h + @[simp] -theorem get?_tail (s : Seq α) (n) : get? (tail s) n = get? s (n + 1) := - rfl +theorem head_eq_none_iff {s : Seq α} : s.head = none ↔ s = nil := by + constructor + · apply head_eq_none + · intro h + simp [h] + +/-! +### Recursion and corecursion principles +-/ /-- Recursion principle for sequences, compare with `List.recOn`. -/ @[cases_eliminator] @@ -243,67 +243,17 @@ def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) (cons : ∀ x s, motive (cons x s)) : motive s := by cases' H : destruct s with v - · rw [destruct_eq_nil H] + · rw [destruct_eq_none H] apply nil · cases' v with a s' rw [destruct_eq_cons H] apply cons +/-- Functorial action of the functor `Option (α × _)` -/ @[simp] -theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by - intro h - apply_fun head at h - simp at h - -@[simp] -theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm - -theorem cons_eq_cons {x x' : α} {s s' : Seq α} : - (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by - constructor - · intro h - constructor - · apply_fun head at h - simpa using h - · apply_fun tail at h - simpa using h - · intro ⟨_, _⟩ - congr - -theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : - s = cons x s.tail := by - cases' s with x' tl <;> simp at h - simpa [cons_eq_cons] - -theorem head_eq_none {s : Seq α} (h : s.head = none) : s = nil := by - cases' s with x tl - · rfl - · simp at h - -@[simp] -theorem head_eq_none_iff {s : Seq α} : s.head = none ↔ s = nil := by - constructor - · apply head_eq_none - · intro h - simp [h] - -theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) - (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by - cases' M with k e; unfold Stream'.get at e - induction' k with k IH generalizing s - · have TH : s = cons a (tail s) := by - apply destruct_eq_cons - unfold destruct get? Functor.map - rw [← e] - rfl - rw [TH] - apply h1 _ _ (Or.inl rfl) - -- Porting note: had to reshuffle `intro` - cases' s with b s' - · injection e - · have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s' using Subtype.recOn; rfl - rw [h_eq] at e - apply h1 _ _ (Or.inr (IH e)) +def omap (f : β → γ) : Option (α × β) → Option (α × γ) + | none => none + | some (a, b) => some (a, f b) /-- Corecursor over pairs of `Option` values -/ def Corec.f (f : β → Option (α × β)) : Option β → Option α × Option β @@ -351,7 +301,7 @@ theorem corec_eq (f : β → Option (α × β)) (b : β) : theorem corec_nil (f : β → Option (α × β)) (b : β) (h : f b = .none) : corec f b = nil := by - apply destruct_eq_nil + apply destruct_eq_none simp [h] theorem corec_cons {f : β → Option (α × β)} {b : β} {x : α} {s : β} @@ -359,6 +309,10 @@ theorem corec_cons {f : β → Option (α × β)} {b : β} {x : α} {s : β} apply destruct_eq_cons simp [h] +/-! +### Bisimulation +-/ + section Bisim variable (R : Seq α → Seq α → Prop) @@ -430,6 +384,158 @@ theorem coinduction2 (s) (f g : Seq α → Seq β) intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ rw [h1, h2]; apply H +/-! +### Termination +-/ + +/-- A sequence has terminated at position `n` if the value at position `n` equals `none`. -/ +def TerminatedAt (s : Seq α) (n : ℕ) : Prop := + s.get? n = none + +/-- It is decidable whether a sequence terminates at a given position. -/ +instance terminatedAtDecidable (s : Seq α) (n : ℕ) : Decidable (s.TerminatedAt n) := + decidable_of_iff' (s.get? n).isNone <| by unfold TerminatedAt; cases s.get? n <;> simp + +/-- A sequence terminates if there is some position `n` at which it has terminated. -/ +def Terminates (s : Seq α) : Prop := + ∃ n : ℕ, s.TerminatedAt n + +/-- The length of a terminating sequence. -/ +def length (s : Seq α) (h : s.Terminates) : ℕ := + Nat.find h + +/-- If a sequence terminated at position `n`, it also terminated at `m ≥ n`. -/ +theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.TerminatedAt m → s.TerminatedAt n := + le_stable + +theorem not_terminates_iff {s : Seq α} : ¬s.Terminates ↔ ∀ n, (s.get? n).isSome := by + simp only [Terminates, TerminatedAt, ← Ne.eq_def, Option.ne_none_iff_isSome, not_exists, iff_self] + +@[simp] +theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl + +@[simp] +theorem cons_not_terminatedAt_zero {x : α} {s : Seq α} : + ¬(cons x s).TerminatedAt 0 := by + simp [TerminatedAt] + +@[simp] +theorem cons_terminatedAt_succ_iff {x : α} {s : Seq α} {n : ℕ} : + (cons x s).TerminatedAt (n + 1) ↔ s.TerminatedAt n := by + simp [TerminatedAt] + +@[simp] +theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ + +@[simp] +theorem terminates_cons_iff {x : α} {s : Seq α} : + (cons x s).Terminates ↔ s.Terminates := by + constructor <;> intro ⟨n, h⟩ + · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ + · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ + +@[simp] +theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl + +@[simp] theorem length_eq_zero {s : Seq α} {h : s.Terminates} : + s.length h = 0 ↔ s = nil := by + simp [length, TerminatedAt] + +theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by + refine ⟨?_, ?_⟩ + · intro h + ext n + rw [le_stable _ (Nat.zero_le _) h] + simp + · rintro rfl + simp [TerminatedAt] + +/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ +theorem length_le_iff' {s : Seq α} {n : ℕ} : + (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by + simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] + refine ⟨?_, ?_⟩ + · rintro ⟨_, k, hkn, hk⟩ + exact le_stable s hkn hk + · intro hn + exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + s.length h ≤ n ↔ s.TerminatedAt n := by + rw [← length_le_iff']; simp [h] + +/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ +theorem lt_length_iff' {s : Seq α} {n : ℕ} : + (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by + simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, + ← Option.ne_none_iff_exists', ne_eq] + refine ⟨?_, ?_⟩ + · intro h hn + exact h n hn n le_rfl hn + · intro hn _ _ k hkn hk + exact hn <| le_stable s hkn hk + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + n < s.length h ↔ ∃ a, a ∈ s.get? n := by + rw [← lt_length_iff']; simp [h] + +/-! +### Membership +-/ + +/-- member definition for `Seq`-/ +protected def Mem (s : Seq α) (a : α) := + some a ∈ s.1 + +instance : Membership α (Seq α) := + ⟨Seq.Mem⟩ + +@[simp] +theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ + +theorem not_mem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h + +theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s + | ⟨_, _⟩ => Stream'.mem_cons (some a) _ + +theorem mem_cons_of_mem (y : α) {a : α} : ∀ {s : Seq α}, a ∈ s → a ∈ cons y s + | ⟨_, _⟩ => Stream'.mem_cons_of_mem (some y) + +theorem eq_or_mem_of_mem_cons {a b : α} : ∀ {s : Seq α}, a ∈ cons b s → a = b ∨ a ∈ s + | ⟨_, _⟩, h => (Stream'.eq_or_mem_of_mem_cons h).imp_left fun h => by injection h + +@[simp] +theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ∈ s := + ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ + +theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) + (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by + cases' M with k e; unfold Stream'.get at e + induction' k with k IH generalizing s + · have TH : s = cons a (tail s) := by + apply destruct_eq_cons + unfold destruct get? Functor.map + rw [← e] + rfl + rw [TH] + apply h1 _ _ (Or.inl rfl) + -- Porting note: had to reshuffle `intro` + cases' s with b s' + · injection e + · have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s' using Subtype.recOn; rfl + rw [h_eq] at e + apply h1 _ _ (Or.inr (IH e)) + +/-! +### Converting from/to other types +-/ + /-- Embed a list as a sequence -/ @[coe] def ofList (l : List α) : Seq α := @@ -497,13 +603,42 @@ end MLList unsafe def forceToList (s : Seq α) : List α := (toMLList s).force -/-- The sequence of natural numbers some 0, some 1, ... -/ -def nats : Seq ℕ := - Stream'.nats +/-- Take the first `n` elements of the sequence (producing a list) -/ +def take : ℕ → Seq α → List α + | 0, _ => [] + | n + 1, s => + match destruct s with + | none => [] + | some (x, r) => List.cons x (take n r) -@[simp] -theorem nats_get? (n : ℕ) : nats.get? n = some n := - rfl +/-- Convert a sequence which is known to terminate into a list -/ +def toList (s : Seq α) (h : s.Terminates) : List α := + take (length s h) s + +/-- Convert a sequence which is known not to terminate into a stream -/ +def toStream (s : Seq α) (h : ¬s.Terminates) : Stream' α := fun n => + Option.get _ <| not_terminates_iff.1 h n + +/-- Convert a sequence into either a list or a stream depending on whether + it is finite or infinite. (Without decidability of the infiniteness predicate, + this is not constructively possible.) -/ +def toListOrStream (s : Seq α) [Decidable s.Terminates] : List α ⊕ Stream' α := + if h : s.Terminates then Sum.inl (toList s h) else Sum.inr (toStream s h) + +/-- Convert a sequence into a list, embedded in a computation to allow for + the possibility of infinite sequences (in which case the computation + never returns anything). -/ +def toList' {α} (s : Seq α) : Computation (List α) := + @Computation.corec (List α) (List α × Seq α) + (fun ⟨l, s⟩ => + match destruct s with + | none => Sum.inl l.reverse + | some (a, s') => Sum.inr (a::l, s')) + ([], s) + +/-! +### Operations on sequences +-/ /-- Append two sequences. If `s₁` is infinite, then `s₁ ++ s₂ = s₁`, otherwise it puts `s₂` at the location of the `nil` in `s₁`. -/ @@ -545,14 +680,6 @@ def drop (s : Seq α) : ℕ → Seq α | 0 => s | n + 1 => tail (drop s n) -/-- Take the first `n` elements of the sequence (producing a list) -/ -def take : ℕ → Seq α → List α - | 0, _ => [] - | n + 1, s => - match destruct s with - | none => [] - | some (x, r) => List.cons x (take n r) - /-- Split a sequence at `n`, producing a finite initial segment and an infinite tail. -/ def splitAt : ℕ → Seq α → List α × Seq α @@ -564,81 +691,56 @@ def splitAt : ℕ → Seq α → List α × Seq α let (l, r) := splitAt n s' (List.cons x l, r) -/-- Folds a sequence using `f`, producing a sequence of intermediate values, i.e. -`[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ -def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := - let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => - match destruct x with - | none => .none - | some (x, s) => .some (f acc x, f acc x, s) - cons init <| corec f (init, s) - -section ZipWith - /-- Combine two sequences with a function -/ def zipWith (f : α → β → γ) (s₁ : Seq α) (s₂ : Seq β) : Seq γ := ⟨fun n => Option.map₂ f (s₁.get? n) (s₂.get? n), fun {_} hn => Option.map₂_eq_none_iff.2 <| (Option.map₂_eq_none_iff.1 hn).imp s₁.2 s₂.2⟩ -@[simp] -theorem get?_zipWith (f : α → β → γ) (s s' n) : - (zipWith f s s').get? n = Option.map₂ f (s.get? n) (s'.get? n) := - rfl - -end ZipWith - /-- Pair two sequences into a sequence of pairs -/ def zip : Seq α → Seq β → Seq (α × β) := zipWith Prod.mk -theorem get?_zip (s : Seq α) (t : Seq β) (n : ℕ) : - get? (zip s t) n = Option.map₂ Prod.mk (get? s n) (get? t n) := - get?_zipWith _ _ _ _ - /-- Separate a sequence of pairs into two sequences -/ def unzip (s : Seq (α × β)) : Seq α × Seq β := (map Prod.fst s, map Prod.snd s) +/-- The sequence of natural numbers some 0, some 1, ... -/ +def nats : Seq ℕ := + Stream'.nats + /-- Enumerate a sequence by tagging each element with its index. -/ def enum (s : Seq α) : Seq (ℕ × α) := Seq.zip nats s -@[simp] -theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := - get?_zip _ _ _ +/-- Folds a sequence using `f`, producing a sequence of intermediate values, i.e. +`[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ +def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := + let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => + match destruct x with + | none => .none + | some (x, s) => .some (f acc x, f acc x, s) + cons init <| corec f (init, s) + +section OfStream @[simp] -theorem enum_nil : enum (nil : Seq α) = nil := - rfl +theorem ofStream_cons (a : α) (s) : ofStream (a::s) = cons a (ofStream s) := by + apply Subtype.eq; simp only [ofStream, cons]; rw [Stream'.map_cons] -/-- The length of a terminating sequence. -/ -def length (s : Seq α) (h : s.Terminates) : ℕ := - Nat.find h +end OfStream -/-- Convert a sequence which is known to terminate into a list -/ -def toList (s : Seq α) (h : s.Terminates) : List α := - take (length s h) s +section OfList -/-- Convert a sequence which is known not to terminate into a stream -/ -def toStream (s : Seq α) (h : ¬s.Terminates) : Stream' α := fun n => - Option.get _ <| not_terminates_iff.1 h n +theorem terminatedAt_ofList (l : List α) : + (ofList l).TerminatedAt l.length := by + simp [ofList, TerminatedAt] -/-- Convert a sequence into either a list or a stream depending on whether - it is finite or infinite. (Without decidability of the infiniteness predicate, - this is not constructively possible.) -/ -def toListOrStream (s : Seq α) [Decidable s.Terminates] : List α ⊕ Stream' α := - if h : s.Terminates then Sum.inl (toList s h) else Sum.inr (toStream s h) +theorem terminates_ofList (l : List α) : (ofList l).Terminates := + ⟨_, terminatedAt_ofList l⟩ -@[simp] -theorem nil_append (s : Seq α) : append nil s = s := by - apply coinduction2; intro s - dsimp [append]; rw [corec_eq] - dsimp [append] - cases' s with x s - · trivial - · rw [destruct_cons] - dsimp - exact ⟨rfl, s, rfl, rfl⟩ +end OfList + +section Take @[simp] theorem take_nil {n : ℕ} : (nil (α := α)).take n = List.nil := by @@ -661,7 +763,7 @@ theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), rw [take] cases h : destruct s with | none => - simp [destruct_eq_nil h] + simp [destruct_eq_none h] | some a => match a with | (x, r) => @@ -690,93 +792,6 @@ theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} apply ih (by omega) rwa [get?_tail] -theorem terminatedAt_ofList (l : List α) : - (ofList l).TerminatedAt l.length := by - simp [ofList, TerminatedAt] - -theorem terminates_ofList (l : List α) : (ofList l).Terminates := - ⟨_, terminatedAt_ofList l⟩ - -@[simp] -theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl - -@[simp] -theorem cons_not_terminatedAt_zero {x : α} {s : Seq α} : - ¬(cons x s).TerminatedAt 0 := by - simp [TerminatedAt] - -@[simp] -theorem cons_terminatedAt_succ_iff {x : α} {s : Seq α} {n : ℕ} : - (cons x s).TerminatedAt (n + 1) ↔ s.TerminatedAt n := by - simp [TerminatedAt] - -@[simp] -theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ - -@[simp] -theorem terminates_cons_iff {x : α} {s : Seq α} : - (cons x s).Terminates ↔ s.Terminates := by - constructor <;> intro ⟨n, h⟩ - · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ - · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ - -@[simp] -theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl - -@[simp] -theorem get?_zero_eq_none {s : Seq α} : s.get? 0 = none ↔ s = nil := by - refine ⟨fun h => ?_, fun h => h ▸ rfl⟩ - ext1 n - exact le_stable s (Nat.zero_le _) h - -@[simp] theorem length_eq_zero {s : Seq α} {h : s.Terminates} : - s.length h = 0 ↔ s = nil := by - simp [length, TerminatedAt] - -theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by - refine ⟨?_, ?_⟩ - · intro h - ext n - rw [le_stable _ (Nat.zero_le _) h] - simp - · rintro rfl - simp [TerminatedAt] - -/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ -theorem length_le_iff' {s : Seq α} {n : ℕ} : - (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by - simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] - refine ⟨?_, ?_⟩ - · rintro ⟨_, k, hkn, hk⟩ - exact le_stable s hkn hk - · intro hn - exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - s.length h ≤ n ↔ s.TerminatedAt n := by - rw [← length_le_iff']; simp [h] - -/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ -theorem lt_length_iff' {s : Seq α} {n : ℕ} : - (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by - simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, - ← Option.ne_none_iff_exists', ne_eq] - refine ⟨?_, ?_⟩ - · intro h hn - exact h n hn n le_rfl hn - · intro hn _ _ k hkn hk - exact hn <| le_stable s hkn hk - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - n < s.length h ↔ ∃ a, a ∈ s.get? n := by - rw [← lt_length_iff']; simp [h] - theorem length_take_le {s : Seq α} {n : ℕ} : (s.take n).length ≤ n := by induction n generalizing s with | zero => simp @@ -804,6 +819,10 @@ theorem length_take_of_le_length {s : Seq α} {n : ℕ} rw [le_stable s (Nat.succ_le_of_lt hmn) hs] at this simp at this +end Take + +section ToList + @[simp] theorem length_toList (s : Seq α) (h : s.Terminates) : (toList s h).length = length s h := by rw [toList, length_take_of_le_length] @@ -836,12 +855,27 @@ theorem getLast?_toList (s : Seq α) (h : s.Terminates) : (toList s h).getLast? = s.get? (s.length h - 1) := by rw [List.getLast?_eq_getElem?, getElem?_toList, length_toList] +end ToList + +section Append + @[simp] theorem cons_append (a : α) (s t) : append (cons a s) t = cons a (append s t) := destruct_eq_cons <| by dsimp [append]; rw [corec_eq] dsimp [append]; rw [destruct_cons] +@[simp] +theorem nil_append (s : Seq α) : append nil s = s := by + apply coinduction2; intro s + dsimp [append]; rw [corec_eq] + dsimp [append] + cases' s with x s + · trivial + · rw [destruct_cons] + dsimp + exact ⟨rfl, s, rfl, rfl⟩ + @[simp] theorem append_nil (s : Seq α) : append s nil = s := by apply coinduction2 s; intro s @@ -866,6 +900,36 @@ theorem append_assoc (s t u : Seq α) : append (append s t) u = append s (append · exact ⟨s, t, u, rfl, rfl⟩ · exact ⟨s, t, u, rfl, rfl⟩ +theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) : a ∈ s₁ ∨ a ∈ s₂ := by + have := h; revert this + generalize e : append s₁ s₂ = ss; intro h; revert s₁ + apply mem_rec_on h _ + intro b s' o s₁ + cases' s₁ with c t₁ + · intro m _ + apply Or.inr + simpa using m + · intro m e + have this := congr_arg destruct e + cases' show a = c ∨ a ∈ append t₁ s₂ by simpa using m with e' m + · rw [e'] + exact Or.inl (mem_cons _ _) + · cases' show c = b ∧ append t₁ s₂ = s' by simpa with i1 i2 + cases' o with e' IH + · simp [i1, e'] + · exact Or.imp_left (mem_cons_of_mem _) (IH m i2) + +theorem mem_append_left {s₁ s₂ : Seq α} {a : α} (h : a ∈ s₁) : a ∈ append s₁ s₂ := by + apply mem_rec_on h; intros; simp [*] + +end Append + +section Map + +@[simp] +theorem map_get? (f : α → β) : ∀ s n, get? (map f s) n = (get? s n).map f + | ⟨_, _⟩, _ => rfl + @[simp] theorem map_nil (f : α → β) : map f nil = nil := rfl @@ -890,24 +954,6 @@ theorem map_comp (f : α → β) (g : β → γ) : ∀ s : Seq α, map (g ∘ f) apply congr_arg fun f : _ → Option γ => Stream'.map f s ext ⟨⟩ <;> rfl -@[simp] -theorem map_append (f : α → β) (s t) : map f (append s t) = append (map f s) (map f t) := by - apply - eq_of_bisim (fun s1 s2 => ∃ s t, s1 = map f (append s t) ∧ s2 = append (map f s) (map f t)) _ - ⟨s, t, rfl, rfl⟩ - intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, t, rfl, rfl⟩ => by - cases' s with _ s <;> simp - · cases' t with _ t <;> simp - · refine ⟨nil, t, ?_, ?_⟩ <;> simp - · exact ⟨s, t, rfl, rfl⟩ - -@[simp] -theorem map_get? (f : α → β) : ∀ s n, get? (map f s) n = (get? s n).map f - | ⟨_, _⟩, _ => rfl - @[simp] theorem terminatedAt_map_iff {f : α → β} {s : Seq α} {n : ℕ} : (map f s).TerminatedAt n ↔ s.TerminatedAt n := by @@ -926,16 +972,39 @@ theorem length_map {s : Seq α} {f : α → β} (h : (s.map f).Terminates) : ext simp -instance : Functor Seq where map := @map +theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s + | ⟨_, _⟩ => Stream'.mem_map (Option.map f) + +theorem exists_of_mem_map {f} {b : β} : ∀ {s : Seq α}, b ∈ map f s → ∃ a, a ∈ s ∧ f a = b := + fun {s} h => by match s with + | ⟨g, al⟩ => + let ⟨o, om, oe⟩ := @Stream'.exists_of_mem_map _ _ (Option.map f) (some b) g h + cases' o with a + · injection oe + · injection oe with h'; exact ⟨a, om, h'⟩ + +@[simp] +theorem map_append (f : α → β) (s t) : map f (append s t) = append (map f s) (map f t) := by + apply + eq_of_bisim (fun s1 s2 => ∃ s t, s1 = map f (append s t) ∧ s2 = append (map f s) (map f t)) _ + ⟨s, t, rfl, rfl⟩ + intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, t, rfl, rfl⟩ => by + cases' s with _ s <;> simp + · cases' t with _ t <;> simp + · refine ⟨nil, t, ?_, ?_⟩ <;> simp + · exact ⟨s, t, rfl, rfl⟩ + +end Map + +section Join -instance : LawfulFunctor Seq where - id_map := @map_id - comp_map := @map_comp - map_const := rfl @[simp] theorem join_nil : join nil = (nil : Seq α) := - destruct_eq_nil rfl + destruct_eq_none rfl --@[simp] -- Porting note: simp can prove: `join_cons` is more general theorem join_cons_nil (a : α) (S) : join (cons (a, nil) S) = cons a (join S) := @@ -984,29 +1053,9 @@ theorem join_append (S T : Seq (Seq1 α)) : join (append S T) = append (join S) · exact ⟨s, S, T, rfl, rfl⟩ · refine ⟨nil, S, T, ?_, ?_⟩ <;> simp -@[simp] -theorem ofStream_cons (a : α) (s) : ofStream (a::s) = cons a (ofStream s) := by - apply Subtype.eq; simp only [ofStream, cons]; rw [Stream'.map_cons] - -@[simp] -theorem ofList_append (l l' : List α) : ofList (l ++ l') = append (ofList l) (ofList l') := by - induction l <;> simp [*] - -@[simp] -theorem ofStream_append (l : List α) (s : Stream' α) : - ofStream (l ++ₛ s) = append (ofList l) (ofStream s) := by - induction l <;> simp [*, Stream'.nil_append_stream, Stream'.cons_append_stream] +end Join -/-- Convert a sequence into a list, embedded in a computation to allow for - the possibility of infinite sequences (in which case the computation - never returns anything). -/ -def toList' {α} (s : Seq α) : Computation (List α) := - @Computation.corec (List α) (List α × Seq α) - (fun ⟨l, s⟩ => - match destruct s with - | none => Sum.inl l.reverse - | some (a, s') => Sum.inr (a::l, s')) - ([], s) +section Drop @[simp] theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) := by @@ -1055,38 +1104,30 @@ theorem take_drop {s : Seq α} {n m : ℕ} : congr 1 rw [drop_succ_cons] -theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s - | ⟨_, _⟩ => Stream'.mem_map (Option.map f) +end Drop -theorem exists_of_mem_map {f} {b : β} : ∀ {s : Seq α}, b ∈ map f s → ∃ a, a ∈ s ∧ f a = b := - fun {s} h => by match s with - | ⟨g, al⟩ => - let ⟨o, om, oe⟩ := @Stream'.exists_of_mem_map _ _ (Option.map f) (some b) g h - cases' o with a - · injection oe - · injection oe with h'; exact ⟨a, om, h'⟩ +section ZipWith -theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) : a ∈ s₁ ∨ a ∈ s₂ := by - have := h; revert this - generalize e : append s₁ s₂ = ss; intro h; revert s₁ - apply mem_rec_on h _ - intro b s' o s₁ - cases' s₁ with c t₁ - · intro m _ - apply Or.inr - simpa using m - · intro m e - have this := congr_arg destruct e - cases' show a = c ∨ a ∈ append t₁ s₂ by simpa using m with e' m - · rw [e'] - exact Or.inl (mem_cons _ _) - · cases' show c = b ∧ append t₁ s₂ = s' by simpa with i1 i2 - cases' o with e' IH - · simp [i1, e'] - · exact Or.imp_left (mem_cons_of_mem _) (IH m i2) +@[simp] +theorem get?_zipWith (f : α → β → γ) (s s' n) : + (zipWith f s s').get? n = Option.map₂ f (s.get? n) (s'.get? n) := + rfl -theorem mem_append_left {s₁ s₂ : Seq α} {a : α} (h : a ∈ s₁) : a ∈ append s₁ s₂ := by - apply mem_rec_on h; intros; simp [*] +theorem get?_zip (s : Seq α) (t : Seq β) (n : ℕ) : + get? (zip s t) n = Option.map₂ Prod.mk (get? s n) (get? t n) := + get?_zipWith _ _ _ _ + +@[simp] +theorem nats_get? (n : ℕ) : nats.get? n = some n := + rfl + +@[simp] +theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := + get?_zip _ _ _ + +@[simp] +theorem enum_nil : enum (nil : Seq α) = nil := + rfl @[simp] theorem enum_cons (s : Seq α) (x : α) : @@ -1096,6 +1137,10 @@ theorem enum_cons (s : Seq α) (x : α) : · simp only [get?_enum, get?_cons_succ, map_get?, Option.map_map] congr +end ZipWith + +section Fold + @[simp] theorem fold_nil (init : β) (f : β → α → β) : nil.fold init f = cons init nil := by @@ -1116,6 +1161,15 @@ theorem fold_head (init : β) (f : β → α → β) (s : Seq α) : (s.fold init f).head = init := by simp [fold] +end Fold + +instance : Functor Seq where map := @map + +instance : LawfulFunctor Seq where + id_map := @map_id + comp_map := @map_comp + map_const := rfl + end Seq namespace Seq1 From 30befcfacded4e20581a5624a1876990f57ff9e9 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Thu, 19 Dec 2024 14:57:29 +0300 Subject: [PATCH 13/34] return lost lemmas --- Mathlib/Data/Seq/Seq.lean | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index c088739361c4d1..d8d1dcf257db13 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -922,6 +922,15 @@ theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) theorem mem_append_left {s₁ s₂ : Seq α} {a : α} (h : a ∈ s₁) : a ∈ append s₁ s₂ := by apply mem_rec_on h; intros; simp [*] +@[simp] +theorem ofList_append (l l' : List α) : ofList (l ++ l') = append (ofList l) (ofList l') := by + induction l <;> simp [*] + +@[simp] +theorem ofStream_append (l : List α) (s : Stream' α) : + ofStream (l ++ₛ s) = append (ofList l) (ofStream s) := by + induction l <;> simp [*, Stream'.nil_append_stream, Stream'.cons_append_stream] + end Append section Map From c43d4ad183888698957fea96023b7aa918ad54c4 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 21 Dec 2024 20:15:32 +0300 Subject: [PATCH 14/34] zip lemmas --- Mathlib/Data/Seq/Seq.lean | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index d8d1dcf257db13..40fd582594e4b2 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -1134,6 +1134,38 @@ theorem nats_get? (n : ℕ) : nats.get? n = some n := theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := get?_zip _ _ _ +@[simp] +theorem zipWith_nil_left {f : α → β → γ} {s} : + zipWith f nil s = nil := + rfl + +@[simp] +theorem zipWith_nil_right {f : α → β → γ} {s} : + zipWith f s nil = nil := by + ext1 + simp + +@[simp] +theorem zipWith_cons_cons {f : α → β → γ} {x s x' s'} : + zipWith f (cons x s) (cons x' s') = cons (f x x') (zipWith f s s') := by + ext1 n + cases' n <;> simp + +@[simp] +theorem zip_nil_left {s : Seq α} : + zip (@nil α) s = nil := + rfl + +@[simp] +theorem zip_nil_right {s : Seq α} : + zip s (@nil α) = nil := + zipWith_nil_right + +@[simp] +theorem zip_cons_cons {s s' : Seq α} {x x'} : + zip (cons x s) (cons x' s') = cons (x, x') (zip s s') := + zipWith_cons_cons + @[simp] theorem enum_nil : enum (nil : Seq α) = nil := rfl From 57dda42cf3c37160b038313722ee657924473d2f Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 21 Dec 2024 20:37:42 +0300 Subject: [PATCH 15/34] modify + set --- Mathlib/Data/Seq/Seq.lean | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 40fd582594e4b2..d9aa56696bfdf2 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -721,6 +721,31 @@ def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := | some (x, s) => .some (f acc x, f acc x, s) cons init <| corec f (init, s) +/-- Apply `f` to the nth element of the list, if it exists, replacing that element +with the result. -/ +def modify (s : Seq α) (n : ℕ) (f : α → α) : Seq α where + val := fun i => + if i = n then + (s.val i).map f + else + s.val i + property := by + simp only [IsSeq] + intro i h + split_ifs with h_if + · split_ifs at h + · omega + · rw [s.property h] + rfl + · split_ifs at h with h_if' + · simp only [Option.map_eq_none'] at h + exact s.property h + · exact s.property h + +/-- `s.set n a` sets the value of sequence `s` at (zero-based) index `n` to `a`. -/ +def set (s : Seq α) (n : ℕ) (a : α) : Seq α := + modify s n (fun _ ↦ a) + section OfStream @[simp] @@ -1204,6 +1229,73 @@ theorem fold_head (init : β) (f : β → α → β) (s : Seq α) : end Fold +section Modify + +@[simp] +theorem modify_nil {f : α → α} {n} : + modify nil n f = nil := by + simp [modify] + rfl + +@[simp] +theorem set_nil {n : ℕ} {x : α} : + set nil n x = nil := + modify_nil + +@[simp] +theorem modify_cons_zero {f : α → α} {hd : α} {tl : Seq α} : + (cons hd tl).modify 0 f = cons (f hd) tl := by + ext1 n + cases n <;> simp [modify] + +@[simp] +theorem set_cons_zero {hd hd' : α} {tl : Seq α} : + (cons hd tl).set 0 hd' = cons hd' tl := + modify_cons_zero + +@[simp] +theorem modify_cons_succ {hd : α} {f : α → α} {n : ℕ} {tl : Seq α} : + (cons hd tl).modify (n + 1) f = cons hd (tl.modify n f) := by + ext1 n + cases n <;> simp [modify] + +@[simp] +theorem set_cons_succ {hd x : α} {n : ℕ} {tl : Seq α} : + (cons hd tl).set (n + 1) x = cons hd (tl.set n x) := + modify_cons_succ + +theorem set_get_of_not_terminated {s : Seq α} {x : α} {n : ℕ} + (h_not_terminated : ¬ s.TerminatedAt n) : + (s.set n x).get? n = x := by + simp [set, modify] + simp [TerminatedAt] at h_not_terminated + cases h : s.get? n with + | none => simp [h] at h_not_terminated + | some => simp + +theorem set_get_of_terminated {s : Seq α} {x : α} {n : ℕ} + (h_terminated : s.TerminatedAt n) : + (s.set n x).get? n = .none := by + simp [set, modify] + simpa [TerminatedAt] using h_terminated + +theorem set_get_stable {s : Seq α} {x : α} {n m : ℕ} + (h : n ≠ m) : + (s.set m x).get? n = s.get? n := by + simp [set, modify] + intro h' + exact (h h').elim + +theorem set_dropn_stable_of_lt {s : Seq α} {m n : ℕ} {x : α} + (h : m < n) : + (s.set m x).drop n = s.drop n := by + ext1 i + simp + rw [set_get_stable] + omega + +end Modify + instance : Functor Seq where map := @map instance : LawfulFunctor Seq where From 55b86cbb08696fae43bb8a2b5e72bfdf8306db9c Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 21 Dec 2024 21:02:47 +0300 Subject: [PATCH 16/34] zip lemmas --- Mathlib/Data/Seq/Seq.lean | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index d8d1dcf257db13..40fd582594e4b2 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -1134,6 +1134,38 @@ theorem nats_get? (n : ℕ) : nats.get? n = some n := theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := get?_zip _ _ _ +@[simp] +theorem zipWith_nil_left {f : α → β → γ} {s} : + zipWith f nil s = nil := + rfl + +@[simp] +theorem zipWith_nil_right {f : α → β → γ} {s} : + zipWith f s nil = nil := by + ext1 + simp + +@[simp] +theorem zipWith_cons_cons {f : α → β → γ} {x s x' s'} : + zipWith f (cons x s) (cons x' s') = cons (f x x') (zipWith f s s') := by + ext1 n + cases' n <;> simp + +@[simp] +theorem zip_nil_left {s : Seq α} : + zip (@nil α) s = nil := + rfl + +@[simp] +theorem zip_nil_right {s : Seq α} : + zip s (@nil α) = nil := + zipWith_nil_right + +@[simp] +theorem zip_cons_cons {s s' : Seq α} {x x'} : + zip (cons x s) (cons x' s') = cons (x, x') (zip s s') := + zipWith_cons_cons + @[simp] theorem enum_nil : enum (nil : Seq α) = nil := rfl From 66a3e875f0887aaddd5897b8342bcfcacaef4f23 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 21 Dec 2024 21:48:55 +0300 Subject: [PATCH 17/34] zip_map lemmas --- Mathlib/Data/Seq/Seq.lean | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 40fd582594e4b2..5730689c46564e 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -1122,6 +1122,7 @@ theorem get?_zipWith (f : α → β → γ) (s s' n) : (zipWith f s s').get? n = Option.map₂ f (s.get? n) (s'.get? n) := rfl +@[simp] theorem get?_zip (s : Seq α) (t : Seq β) (n : ℕ) : get? (zip s t) n = Option.map₂ Prod.mk (get? s n) (get? t n) := get?_zipWith _ _ _ _ @@ -1178,6 +1179,41 @@ theorem enum_cons (s : Seq α) (x : α) : · simp only [get?_enum, get?_cons_succ, map_get?, Option.map_map] congr +universe u' v' +variable {α' : Type u'} {β' : Type v'} + +theorem zipWith_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') (g : α' → β' → γ) : + zipWith g (s₁.map f₁) (s₂.map f₂) = zipWith (fun a b ↦ g (f₁ a) (f₂ b)) s₁ s₂ := by + ext1 n + simp only [get?_zipWith, map_get?] + cases s₁.get? n <;> cases s₂.get? n <;> simp + +theorem zipWith_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') (g : α' → β → γ) : + zipWith g (s₁.map f) s₂ = zipWith (fun a b ↦ g (f a) b) s₁ s₂ := by + convert zipWith_map _ _ _ (@id β) _ + simp + +theorem zipWith_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') (g : α → β' → γ) : + zipWith g s₁ (s₂.map f) = zipWith (fun a b ↦ g a (f b)) s₁ s₂ := by + convert zipWith_map _ _ (@id α) _ _ + simp + +theorem zip_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') : + (s₁.map f₁).zip (s₂.map f₂) = (s₁.zip s₂).map (Prod.map f₁ f₂) := by + ext1 n + simp + cases s₁.get? n <;> cases s₂.get? n <;> simp + +theorem zip_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') : + (s₁.map f).zip s₂ = (s₁.zip s₂).map (Prod.map f id) := by + convert zip_map _ _ _ _ + simp + +theorem zip_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') : + s₁.zip (s₂.map f) = (s₁.zip s₂).map (Prod.map id f) := by + convert zip_map _ _ _ _ + simp + end ZipWith section Fold From 13e7e0f533e7928f349ca2e87515164ae942d3e8 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Wed, 25 Dec 2024 14:25:56 +0300 Subject: [PATCH 18/34] predicates --- Mathlib/Data/Seq/Seq.lean | 357 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index c8ceb23214ac77..a5c9edd1f3eedb 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -499,6 +499,7 @@ instance : Membership α (Seq α) := @[simp] theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ +@[simp] theorem not_mem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s @@ -746,6 +747,35 @@ def modify (s : Seq α) (n : ℕ) (f : α → α) : Seq α where def set (s : Seq α) (n : ℕ) (a : α) : Seq α := modify s n (fun _ ↦ a) +/-! +### Predicates on sequences +-/ + +-- Note: without `irreducible` attribute it is inconvenient to apply lemmas about it, because Lean +-- eagerly unfolds `All` and unifyes `p x` with the goal (even if the goal is in form `s.All p`). +/-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ +@[irreducible] +def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x + +-- Note: `irreducible` here is necessary for the same reason as for `All` above +/-- +`Pairwise R s` means that all the elements with earlier indexes are +`R`-related to all the elements with later indexes. +``` +Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 +``` +For example if `R = (·≠·)` then it asserts `s` has no duplicates, +and if `R = (·<·)` then it asserts that `s` is (strictly) sorted. +-/ +@[irreducible] +def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := + ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y + +/-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. +In particular, they both may be infinite. -/ +def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := + ∀ n, a.TerminatedAt n → b.TerminatedAt n + section OfStream @[simp] @@ -1112,6 +1142,10 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by induction' n with n IH generalizing s; · rfl rw [← get?_tail, ← dropn_tail]; apply IH +@[simp] +theorem drop_zero {s : Seq α} : s.drop 0 = s := by + rfl + @[simp] theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : (cons x s).drop (n + 1) = s.drop n := by @@ -1332,6 +1366,329 @@ theorem set_dropn_stable_of_lt {s : Seq α} {m n : ℕ} {x : α} end Modify +section All + +@[simp] +theorem All.nil {p : α → Prop} : nil.All p := by + simp [All] + +theorem All.cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : tl.All p) : + ((cons hd tl).All p) := by + simp only [All, mem_cons_iff, forall_eq_or_imp] at * + exact ⟨h_hd, h_tl⟩ + +@[simp] +theorem All_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : + ((cons hd tl).All p) ↔ p hd ∧ tl.All p := by + simp [All] + +theorem All_get {p : α → Prop} {s : Seq α} (h : s.All p) {n : ℕ} {x : α} (hx : s.get? n = .some x) : + p x := by + unfold All at h + exact h _ (get?_mem hx) + +theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : + s.All p := by + simp [All, Membership.mem, Seq.Mem, Any, get] + intro x i hx + simpa [← hx] using h i + +theorem All.coind {s : Seq α} {p : α → Prop} + (motive : Seq α → Prop) (h_base : motive s) + (h_cons : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) + : s.All p := by + apply All_of_get + intro n + have : (s.get? n).elim True p ∧ motive (s.drop n) := by + induction n with + | zero => + cases h1 : get? s 0 with + | none => + constructor + · simp + · simpa + | some hd => + simp + have := head_eq_some h1 + specialize h_cons hd s.tail (this ▸ h_base) + constructor + · exact h_cons.left + · exact h_base + | succ m ih => + simp at ih + simp only [drop, ← head_dropn] + generalize s.drop m = t at ih + cases' t with hd tl + · simp [ih.right] + · simp + obtain ⟨h1, h2⟩ := ih + have : motive tl := by + specialize h_cons hd tl h2 + exact h_cons.right + constructor + · cases h_head : tl.head with + | none => simp + | some tl_hd => + have h_tl_cons := head_eq_some h_head + specialize h_cons tl_hd tl.tail (h_tl_cons ▸ this) + simp + exact h_cons.left + · assumption + intro x hx + simp only [hx, Option.elim_some] at this + exact this.left + +theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : + s.All q := by + simp only [All] at hp ⊢ + tauto + +theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : + (s.map f).All p ↔ s.All (p ∘ f) := by + simp [All] + refine ⟨fun _ _ hx ↦ ?_, fun _ _ hx ↦ ?_⟩ + · solve_by_elim [mem_map f hx] + · obtain ⟨_, _, hx'⟩ := exists_of_mem_map hx + rw [← hx'] + solve_by_elim + +theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} : + ∀ x ∈ s.take n, p x := by + intro x hx + induction n generalizing s with + | zero => simp [take] at hx + | succ m ih => + cases' s with hd tl + · simp at hx + · simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all + rcases hx with (hx | hx) + · exact hx ▸ h_all.left + · exact ih h_all.right hx + +theorem set_All {p : α → Prop} {s : Seq α} (h_all : s.All p) {n : ℕ} {x : α} + (hx : p x) : (s.set n x).All p := by + apply All_of_get + intro m + by_cases h_nm : n = m + · subst h_nm + by_cases h_term : s.TerminatedAt n + · simp [set_get_of_terminated h_term] + · simpa [set_get_of_not_terminated h_term] + · rw [set_get_stable] + · intro x hx + exact All_get h_all hx + · omega + +end All + +section Pairwise + +@[simp] +theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by + simp [Pairwise] + +theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} + (h_lt : tl.All (R hd ·)) + (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by + simp [Pairwise] at * + intro i j x y h_ij hx hy + cases j with + | zero => + simp at h_ij + | succ k => + simp at hy + cases i with + | zero => + simp at hx + rw [← hx] + exact All_get h_lt hy + | succ n => + exact h_tl n k x y (by omega) hx hy + +theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} + (h : Pairwise R (.cons hd tl)) : tl.All (R hd ·) ∧ Pairwise R tl := by + simp only [Pairwise] at h + constructor + · apply All_of_get + intro n + specialize h 0 (n + 1) hd + simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h + cases' h_tl : tl.get? n with y + · simp + · simp [h y h_tl] + · simp [Pairwise] + exact fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy + +@[simp] +theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by + apply Pairwise.cons <;> simp + +theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl : Seq α} + (h : Pairwise R (cons hd (cons tl_hd tl_tl))) : + R hd tl_hd := by + simp only [Pairwise] at h + simpa using h 0 1 hd tl_hd Nat.one_pos + +theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α} + (h_lt : R hd tl_hd) + (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by + apply Pairwise.cons _ h_tl + simp only [All_cons_iff] + refine ⟨h_lt, ?_⟩ + apply All_mp _ h_tl.cons_elim.left + intro x h + exact trans_of _ h_lt h + +theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} + (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) + : Pairwise R s := by + have h_all : ∀ n, motive (s.drop n) := by + intro n + induction n with + | zero => simpa + | succ m ih => + simp only [drop] + generalize s.drop m = t at * + cases' t with hd tl + · simpa + · exact (h_step hd tl ih).right + simp only [Pairwise] + intro i j x y h_ij hx hy + replace h_ij := Nat.exists_eq_add_of_lt h_ij + obtain ⟨k, hj⟩ := h_ij + rw [Nat.add_assoc, Nat.add_comm] at hj + subst hj + rw [show k + 1 + i = i + 1 + k by omega] at hy + simp only [← head_dropn] at hx + rw [← head_dropn, dropn_add, drop, head_dropn] at hy + have := (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left + exact All_get this hy + +theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans _ R] {s : Seq α} + (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → tl.head.elim True (R hd ·) ∧ motive tl) + : Pairwise R s := by + have h_all : ∀ n, motive (s.drop n) := by + intro n + induction n with + | zero => simpa + | succ m ih => + simp only [drop] + generalize s.drop m = t at * + cases' t with hd tl + · simpa + · exact (h_step hd tl ih).right + simp only [Pairwise] + intro i j x y h_ij hx hy + replace h_ij := Nat.exists_eq_add_of_lt h_ij + obtain ⟨k, hj⟩ := h_ij + rw [Nat.add_assoc, Nat.add_comm] at hj + subst hj + induction k generalizing i x with + | zero => + simp only [← head_dropn] at hx + rw [Nat.zero_add, Nat.add_comm, ← head_dropn, drop] at hy + have := (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left + simpa only [hy, Option.elim_some] using this + | succ k ih => + obtain ⟨z, hz⟩ := ge_stable (m := i + 1) _ (by omega) hy + trans z + · simp only [← head_dropn, drop] at hx hz + simpa [hz] using + (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left + · exact ih (i + 1) z hz (by convert hy using 2; omega) + +theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) : + s.tail.Pairwise R := by + cases' s with hd tl + · simp + · simp only [tail_cons] + exact h.cons_elim.right + +theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) {n : ℕ} : + (s.drop n).Pairwise R := by + induction n with + | zero => simpa + | succ m ih => + simp only [drop] + exact Pairwise_tail ih + +end Pairwise + +section AtLeastAsLongAs + +theorem AtLeastAsLongAs.nil {a : Seq α} : + a.AtLeastAsLongAs (@nil β) := by + unfold AtLeastAsLongAs + simp + +theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} + (h : a_tl.AtLeastAsLongAs b_tl) : + (Seq.cons a_hd a_tl).AtLeastAsLongAs (Seq.cons b_hd b_tl) := by + simp only [AtLeastAsLongAs] at * + intro n + cases n with + | zero => simp + | succ m => simpa using h m + +theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} + (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by + cases' a with hd' tl' + · unfold AtLeastAsLongAs at h + simp only [terminatedAt_nil, forall_const] at h + specialize h 0 + simp [TerminatedAt] at h + · use hd', tl' + +@[simp] +theorem cons_AtLeastAsLongAs_cons {a_hd : α} {a_tl : Seq α} {b_hd : β} + {b_tl : Seq β} : + (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl := by + refine ⟨fun h ↦ ?_, fun h ↦ AtLeastAsLongAs.cons h⟩ + simp [AtLeastAsLongAs] at * + intro n + specialize h (n + 1) + simpa using h + +theorem AtLeastAsLongAs_map {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} + {b : Seq β} (h : a.AtLeastAsLongAs b): + a.AtLeastAsLongAs (b.map f) := by + simp only [AtLeastAsLongAs, terminatedAt_map_iff] at h ⊢ + intro n ha + simpa [TerminatedAt] using h n ha + +theorem atLeastAsLong.coind {a : Seq α} {b : Seq β} + (motive : Seq α → Seq β → Prop) (h_base : motive a b) + (h_step : ∀ a b, motive a b → + (∀ b_hd b_tl, (b = cons b_hd b_tl) → ∃ a_hd a_tl, a = cons a_hd a_tl ∧ motive a_tl b_tl)) + : a.AtLeastAsLongAs b := by + simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] + intro n + have : b.drop n ≠ .nil → motive (a.drop n) (b.drop n) := by + intro hb + induction n with + | zero => simpa + | succ m ih => + simp only [drop] at hb ⊢ + generalize b.drop m = tb at * + cases' tb with tb_hd tb_tl + · simp at hb + · simp at ih + obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (cons tb_hd tb_tl) ih _ _ (by rfl) + rw [ha] + simpa + contrapose + intro hb + rw [head_eq_none_iff] at hb + generalize b.drop n = tb at * + cases' tb with tb_hd tb_tl + · simp at hb + · obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) + simp [ha] + +end AtLeastAsLongAs + instance : Functor Seq where map := @map instance : LawfulFunctor Seq where From 368f23704d4b6329328cac8e459adc5ccc5b8e07 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Wed, 25 Dec 2024 15:26:26 +0300 Subject: [PATCH 19/34] bisimulation --- Mathlib/Data/Seq/Seq.lean | 64 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index a5c9edd1f3eedb..e576f4bc81a1d3 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -15,6 +15,8 @@ This file provides a `Seq α` type representing possibly infinite lists (referre `f m = none` for all `m ≥ n`. -/ +set_option linter.style.longFile 2000 + namespace Stream' universe u v w @@ -332,7 +334,7 @@ attribute [nolint simpNF] BisimO.eq_3 def IsBisimulation := ∀ ⦃s₁ s₂⦄, s₁ ~ s₂ → BisimO R (destruct s₁) (destruct s₂) --- If two streams are bisimilar, then they are equal +/-- If two streams are bisimilar, then they are equal. -/ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by apply Subtype.eq apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s' @@ -384,6 +386,54 @@ theorem coinduction2 (s) (f g : Seq α → Seq β) intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ rw [h1, h2]; apply H +/-- Version of `eq_of_bisim` that looks more like an induction principle. -/ +theorem eq_of_bisim' {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (h_base : motive s₁ s₂) + (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ + (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by + apply eq_of_bisim motive _ h_base + intro s₁ s₂ h + specialize h_step s₁ s₂ h + rcases h_step with (h_cons | h_nil) + · obtain ⟨hd, tl₁, tl₂, h₁, h₂, h_tl⟩ := h_cons + simpa [h₁, h₂] + · simp [h_nil.left, h_nil.right] + +/-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` +instead of `s₁ = nil ∧ s₂ = nil` in `h_step`. -/ +theorem eq_of_bisim_strong {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (h_base : motive s₁ s₂) + (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (s₁ = s₂) ∨ + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))): s₁ = s₂ := by + let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ + apply eq_of_bisim' motive' + · simp [motive'] + tauto + intro s₁ s₂ ih + simp only [motive'] at ih ⊢ + rcases ih with (h_eq | ih) + · subst h_eq + cases' s₁ with x tl + · simp + · simp only [cons_ne_nil, and_self, or_false] + use x, tl, tl + simp + rcases h_step s₁ s₂ ih with (h_eq | h_cons) + · subst h_eq + cases' s₁ with x tl + · simp + · simp only [cons_ne_nil, and_self, or_false] + use x, tl, tl + simp + · left + obtain ⟨hd, s₁', s₂', _⟩ := h_cons + use hd, s₁', s₂' + tauto + /-! ### Termination -/ @@ -1393,6 +1443,7 @@ theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some intro x i hx simpa [← hx] using h i +/-- Coinductive principle for `All`. -/ theorem All.coind {s : Seq α} {p : α → Prop} (motive : Seq α → Prop) (h_base : motive s) (h_cons : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) @@ -1539,6 +1590,7 @@ theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd t intro x h exact trans_of _ h_lt h +/-- Coinductive principle for `Pairwise`. -/ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) @@ -1565,6 +1617,9 @@ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} have := (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left exact All_get this hy + +/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove +`R hd tl.head` instead of `tl.All (R hd ·)` in `h_step`. -/ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans _ R] {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) (h_step : ∀ hd tl, motive (.cons hd tl) → tl.head.elim True (R hd ·) ∧ motive tl) @@ -1658,10 +1713,11 @@ theorem AtLeastAsLongAs_map {α : Type v} {γ : Type w} {f : β → γ} {a : Seq intro n ha simpa [TerminatedAt] using h n ha -theorem atLeastAsLong.coind {a : Seq α} {b : Seq β} +/-- Coinductive principle for `AtLeastAsLongAs`. -/ +theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} (motive : Seq α → Seq β → Prop) (h_base : motive a b) (h_step : ∀ a b, motive a b → - (∀ b_hd b_tl, (b = cons b_hd b_tl) → ∃ a_hd a_tl, a = cons a_hd a_tl ∧ motive a_tl b_tl)) + (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) : a.AtLeastAsLongAs b := by simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] intro n @@ -1675,7 +1731,7 @@ theorem atLeastAsLong.coind {a : Seq α} {b : Seq β} cases' tb with tb_hd tb_tl · simp at hb · simp at ih - obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (cons tb_hd tb_tl) ih _ _ (by rfl) + obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) rw [ha] simpa contrapose From 4d9622da0bbf2427f2d2c1e3b2e0385948286277 Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Wed, 25 Dec 2024 15:33:10 +0300 Subject: [PATCH 20/34] move --- Mathlib/Data/Seq/Seq.lean | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index e576f4bc81a1d3..0c7fe6276472d8 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -367,25 +367,6 @@ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s · exact h2 · exact ⟨s₁, s₂, rfl, rfl, r⟩ -end Bisim - -theorem coinduction : - ∀ {s₁ s₂ : Seq α}, - head s₁ = head s₂ → - (∀ (β : Type u) (fr : Seq α → β), fr s₁ = fr s₂ → fr (tail s₁) = fr (tail s₂)) → s₁ = s₂ - | _, _, hh, ht => - Subtype.eq (Stream'.coinduction hh fun β fr => ht β fun s => fr s.1) - -theorem coinduction2 (s) (f g : Seq α → Seq β) - (H : - ∀ s, - BisimO (fun s1 s2 : Seq β => ∃ s : Seq α, s1 = f s ∧ s2 = g s) (destruct (f s)) - (destruct (g s))) : - f s = g s := by - refine eq_of_bisim (fun s1 s2 => ∃ s, s1 = f s ∧ s2 = g s) ?_ ⟨s, rfl, rfl⟩ - intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ - rw [h1, h2]; apply H - /-- Version of `eq_of_bisim` that looks more like an induction principle. -/ theorem eq_of_bisim' {s₁ s₂ : Seq α} (motive : Seq α → Seq α → Prop) @@ -434,6 +415,25 @@ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} use hd, s₁', s₂' tauto +end Bisim + +theorem coinduction : + ∀ {s₁ s₂ : Seq α}, + head s₁ = head s₂ → + (∀ (β : Type u) (fr : Seq α → β), fr s₁ = fr s₂ → fr (tail s₁) = fr (tail s₂)) → s₁ = s₂ + | _, _, hh, ht => + Subtype.eq (Stream'.coinduction hh fun β fr => ht β fun s => fr s.1) + +theorem coinduction2 (s) (f g : Seq α → Seq β) + (H : + ∀ s, + BisimO (fun s1 s2 : Seq β => ∃ s : Seq α, s1 = f s ∧ s2 = g s) (destruct (f s)) + (destruct (g s))) : + f s = g s := by + refine eq_of_bisim (fun s1 s2 => ∃ s, s1 = f s ∧ s2 = g s) ?_ ⟨s, rfl, rfl⟩ + intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ + rw [h1, h2]; apply H + /-! ### Termination -/ From 69496e602be7d9750d28424604f83bcacbbf413f Mon Sep 17 00:00:00 2001 From: Vasily Nesterov Date: Sat, 18 Jan 2025 17:33:47 +0300 Subject: [PATCH 21/34] remove import --- Mathlib/Data/Seq/Seq.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 5730689c46564e..ec72992f0c6f2c 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -5,7 +5,6 @@ Authors: Mario Carneiro -/ import Mathlib.Data.Option.NAry import Mathlib.Data.Seq.Computation -import Mathlib.Tactic.ApplyFun /-! # Possibly infinite lists @@ -116,8 +115,7 @@ theorem get?_cons_succ (a : α) (s : Seq α) (n : ℕ) : (cons a s).get? (n + 1) @[simp] theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by intro h - apply_fun (·.get? 0) at h - simp at h + simpa using congrArg (·.get? 0) h @[simp] theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm From 78dfb8f8ddb8c92ef5b7d1e45ee754b8f1ff00b1 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Tue, 13 May 2025 15:38:56 +0300 Subject: [PATCH 22/34] remove cases' --- Mathlib/Data/Seq/Seq.lean | 60 +++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 769c9de387950c..6cacb301bb6a3c 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -394,16 +394,18 @@ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} simp only [motive'] at ih ⊢ rcases ih with (h_eq | ih) · subst h_eq - cases' s₁ with x tl - · simp - · simp only [cons_ne_nil, and_self, or_false] + cases s₁ with + | nil => simp + | cons x tl => + simp only [cons_ne_nil, and_self, or_false] use x, tl, tl simp rcases h_step s₁ s₂ ih with (h_eq | h_cons) · subst h_eq - cases' s₁ with x tl - · simp - · simp only [cons_ne_nil, and_self, or_false] + cases s₁ with + | nil => simp + | cons x tl => + simp only [cons_ne_nil, and_self, or_false] use x, tl, tl simp · left @@ -1472,9 +1474,10 @@ theorem All.coind {s : Seq α} {p : α → Prop} simp at ih simp only [drop, ← head_dropn] generalize s.drop m = t at ih - cases' t with hd tl - · simp [ih.right] - · simp + cases t with + | nil => simp [ih.right] + | cons hd tl => + simp obtain ⟨h1, h2⟩ := ih have : motive tl := by specialize h_cons hd tl h2 @@ -1512,9 +1515,10 @@ theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} : induction n generalizing s with | zero => simp [take] at hx | succ m ih => - cases' s with hd tl - · simp at hx - · simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all + cases s with + | nil => simp at hx + | cons hd tl => + simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all rcases hx with (hx | hx) · exact hx ▸ h_all.left · exact ih h_all.right hx @@ -1567,9 +1571,9 @@ theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} intro n specialize h 0 (n + 1) hd simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h - cases' h_tl : tl.get? n with y - · simp - · simp [h y h_tl] + cases h_tl : tl.get? n with + | none => simp + | some y => simp [h y h_tl] · simp [Pairwise] exact fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy @@ -1605,9 +1609,9 @@ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} | succ m ih => simp only [drop] generalize s.drop m = t at * - cases' t with hd tl + cases t · simpa - · exact (h_step hd tl ih).right + · exact (h_step _ _ ih).right simp only [Pairwise] intro i j x y h_ij hx hy replace h_ij := Nat.exists_eq_add_of_lt h_ij @@ -1634,9 +1638,9 @@ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans _ R] {s : Seq α} | succ m ih => simp only [drop] generalize s.drop m = t at * - cases' t with hd tl + cases t · simpa - · exact (h_step hd tl ih).right + · exact (h_step _ _ ih).right simp only [Pairwise] intro i j x y h_ij hx hy replace h_ij := Nat.exists_eq_add_of_lt h_ij @@ -1659,7 +1663,7 @@ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans _ R] {s : Seq α} theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) : s.tail.Pairwise R := by - cases' s with hd tl + cases s · simp · simp only [tail_cons] exact h.cons_elim.right @@ -1692,12 +1696,13 @@ theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by - cases' a with hd' tl' - · unfold AtLeastAsLongAs at h + cases a with + | nil => + unfold AtLeastAsLongAs at h simp only [terminatedAt_nil, forall_const] at h specialize h 0 simp [TerminatedAt] at h - · use hd', tl' + | cons hd' tl' => use hd', tl' @[simp] theorem cons_AtLeastAsLongAs_cons {a_hd : α} {a_tl : Seq α} {b_hd : β} @@ -1731,9 +1736,10 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} | succ m ih => simp only [drop] at hb ⊢ generalize b.drop m = tb at * - cases' tb with tb_hd tb_tl - · simp at hb - · simp at ih + cases tb with + | nil => simp at hb + | cons tb_hd tb_tl => + simp at ih obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) rw [ha] simpa @@ -1741,7 +1747,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} intro hb rw [head_eq_none_iff] at hb generalize b.drop n = tb at * - cases' tb with tb_hd tb_tl + cases tb · simp at hb · obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) simp [ha] From 323d6efac762794f0539f5f7406fed07d9de3e64 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Sun, 24 Aug 2025 23:59:32 +0300 Subject: [PATCH 23/34] fix --- Mathlib/Data/Seq/Seq.lean | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index bba632550ffe02..98a8928d6741a8 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -453,7 +453,6 @@ theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.Terminat theorem not_terminates_iff {s : Seq α} : ¬s.Terminates ↔ ∀ n, (s.get? n).isSome := by simp only [Terminates, TerminatedAt, ← Ne.eq_def, Option.ne_none_iff_isSome, not_exists, iff_self] -@[simp] theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl @[simp] @@ -1656,7 +1655,7 @@ section AtLeastAsLongAs theorem AtLeastAsLongAs.nil {a : Seq α} : a.AtLeastAsLongAs (@nil β) := by unfold AtLeastAsLongAs - simp + simp [terminatedAt_nil] theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} (h : a_tl.AtLeastAsLongAs b_tl) : From 3eb1eaf88d41a81e9f268c2012f5a1514a782c8a Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Tue, 26 Aug 2025 14:41:28 +0300 Subject: [PATCH 24/34] golf --- Mathlib/Data/Seq/Seq.lean | 324 +++++++++++++--------------------- Mathlib/Data/Stream/Init.lean | 4 + 2 files changed, 123 insertions(+), 205 deletions(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 98a8928d6741a8..78ebc42cf04e04 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -14,8 +14,6 @@ This file provides a `Seq α` type representing possibly infinite lists (referre `f m = none` for all `m ≥ n`. -/ -set_option linter.style.longFile 2000 - namespace Stream' universe u v w @@ -366,11 +364,9 @@ theorem eq_of_bisim' {s₁ s₂ : Seq α} (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by apply eq_of_bisim motive _ h_base intro s₁ s₂ h - specialize h_step s₁ s₂ h - rcases h_step with (h_cons | h_nil) - · obtain ⟨hd, tl₁, tl₂, h₁, h₂, h_tl⟩ := h_cons - simpa [h₁, h₂] - · simp [h_nil.left, h_nil.right] + rcases h_step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) + · simpa [h₁, h₂] + · simp [h_nil₁, h_nil₂] /-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` instead of `s₁ = nil ∧ s₂ = nil` in `h_step`. -/ @@ -381,31 +377,14 @@ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} (s₁ = s₂) ∨ (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))) : s₁ = s₂ := by let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ - apply eq_of_bisim' motive' - · simp [motive'] - tauto + apply eq_of_bisim' motive' (by grind) intro s₁ s₂ ih simp only [motive'] at ih ⊢ - rcases ih with (h_eq | ih) - · subst h_eq - cases s₁ with - | nil => simp - | cons x tl => - simp only [cons_ne_nil, and_self, or_false] - use x, tl, tl - simp - rcases h_step s₁ s₂ ih with (h_eq | h_cons) - · subst h_eq - cases s₁ with - | nil => simp - | cons x tl => - simp only [cons_ne_nil, and_self, or_false] - use x, tl, tl - simp - · left - obtain ⟨hd, s₁', s₂', _⟩ := h_cons - use hd, s₁', s₂' - tauto + rcases ih with (rfl | ih) + · cases s₁ <;> grind + rcases h_step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) + · cases s₁ <;> grind + · grind end Bisim @@ -540,6 +519,12 @@ instance : Membership α (Seq α) := -- Cannot be @[simp] because `n` can not be inferred by `simp`. theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ +theorem mem_iff_exists_get? {s : Seq α} {x : α} : x ∈ s ↔ ∃ i, some x = s.get? i where + mp h := by + change (some x ∈ s.1) at h + rwa [Stream'.mem_iff_exists_get_eq] at h + mpr h := get?_mem h.choose_spec.symm + @[simp] theorem notMem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h @@ -781,23 +766,18 @@ def set (s : Seq α) (n : ℕ) (a : α) : Seq α := ### Predicates on sequences -/ --- Note: without `irreducible` attribute it is inconvenient to apply lemmas about it, because Lean --- eagerly unfolds `All` and unifyes `p x` with the goal (even if the goal is in form `s.All p`). /-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ -@[irreducible] def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x --- Note: `irreducible` here is necessary for the same reason as for `All` above /-- `Pairwise R s` means that all the elements with earlier indexes are `R`-related to all the elements with later indexes. ``` Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 ``` -For example if `R = (·≠·)` then it asserts `s` has no duplicates, -and if `R = (·<·)` then it asserts that `s` is (strictly) sorted. +For example if `R = (· ≠ ·)` then it asserts `s` has no duplicates, +and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. -/ -@[irreducible] def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y @@ -1183,8 +1163,7 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by rw [← get?_tail, ← dropn_tail]; apply IH @[simp] -theorem drop_zero {s : Seq α} : s.drop 0 = s := by - rfl +theorem drop_zero {s : Seq α} : s.drop 0 = s := rfl @[simp] theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : @@ -1396,7 +1375,7 @@ end Update section All @[simp] -theorem All.nil {p : α → Prop} : nil.All p := by +theorem All.nil (p : α → Prop) : nil.All p := by simp [All] theorem All.cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : tl.All p) : @@ -1416,61 +1395,39 @@ theorem All_get {p : α → Prop} {s : Seq α} (h : s.All p) {n : ℕ} {x : α} theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : s.All p := by - simp [All, Membership.mem, Seq.Mem, Any, get] - intro x i hx - simpa [← hx] using h i + simp only [All, mem_iff_exists_get?] + grind + +private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : + motive (s.drop n) := by + induction n with + | zero => simpa + | succ m ih => + simp only [drop] + generalize s.drop m = t at * + cases t + · simpa + · exact h_step _ _ ih /-- Coinductive principle for `All`. -/ theorem All.coind {s : Seq α} {p : α → Prop} (motive : Seq α → Prop) (h_base : motive s) - (h_cons : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) + (h_step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) : s.All p := by apply All_of_get intro n - have : (s.get? n).elim True p ∧ motive (s.drop n) := by - induction n with - | zero => - cases h1 : get? s 0 with - | none => - constructor - · simp - · simpa - | some hd => - simp - have := head_eq_some h1 - specialize h_cons hd s.tail (this ▸ h_base) - constructor - · exact h_cons.left - · exact h_base - | succ m ih => - simp at ih - simp only [drop, ← head_dropn] - generalize s.drop m = t at ih - cases t with - | nil => simp [ih.right] - | cons hd tl => - simp - obtain ⟨h1, h2⟩ := ih - have : motive tl := by - specialize h_cons hd tl h2 - exact h_cons.right - constructor - · cases h_head : tl.head with - | none => simp - | some tl_hd => - have h_tl_cons := head_eq_some h_head - specialize h_cons tl_hd tl.tail (h_tl_cons ▸ this) - simp - exact h_cons.left - · assumption - intro x hx - simp only [hx, Option.elim_some] at this - exact this.left + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) n + rw [← head_dropn] + generalize s.drop n = s' at this + cases s' with + | nil => simp + | cons hd tl => simp [(h_step hd tl this).left] theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : s.All q := by - simp only [All] at hp ⊢ - tauto + simp only [All] at * + grind theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : (s.map f).All p ↔ s.All (p ∘ f) := by @@ -1481,9 +1438,8 @@ theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} rw [← hx'] solve_by_elim -theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} : - ∀ x ∈ s.take n, p x := by - intro x hx +theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} {x : α} (hx : x ∈ s.take n) : + p x := by induction n generalizing s with | zero => simp [take] at hx | succ m ih => @@ -1491,23 +1447,19 @@ theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} : | nil => simp at hx | cons hd tl => simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all - rcases hx with (hx | hx) - · exact hx ▸ h_all.left - · exact ih h_all.right hx + rcases hx with (rfl | hx) + exacts [h_all.left, ih h_all.right hx] theorem set_All {p : α → Prop} {s : Seq α} (h_all : s.All p) {n : ℕ} {x : α} (hx : p x) : (s.set n x).All p := by apply All_of_get intro m - by_cases h_nm : n = m - · subst h_nm - by_cases h_term : s.TerminatedAt n + rcases eq_or_ne n m with (rfl | h_nm) + · by_cases h_term : s.TerminatedAt n · simp [get?_set_of_terminatedAt _ h_term] · simpa [get?_set_of_not_terminatedAt _ h_term] · rw [get?_set_of_ne] - · intro x hx - exact All_get h_all hx - · omega + exacts [fun x hx ↦ All_get h_all hx, h_nm.symm] end All @@ -1520,34 +1472,29 @@ theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} (h_lt : tl.All (R hd ·)) (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by - simp [Pairwise] at * + simp only [Pairwise] at * intro i j x y h_ij hx hy cases j with - | zero => - simp at h_ij + | zero => simp at h_ij | succ k => - simp at hy + simp only [get?_cons_succ] at hy cases i with | zero => - simp at hx - rw [← hx] - exact All_get h_lt hy - | succ n => - exact h_tl n k x y (by omega) hx hy + simp only [get?_cons_zero, Option.some.injEq] at hx + exact hx ▸ All_get h_lt hy + | succ n => exact h_tl n k x y (by omega) hx hy theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} (h : Pairwise R (.cons hd tl)) : tl.All (R hd ·) ∧ Pairwise R tl := by - simp only [Pairwise] at h - constructor - · apply All_of_get - intro n - specialize h 0 (n + 1) hd - simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h - cases h_tl : tl.get? n with - | none => simp - | some y => simp [h y h_tl] - · simp [Pairwise] - exact fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy + simp only [Pairwise] at * + refine ⟨?_, fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy⟩ + apply All_of_get + intro n + specialize h 0 (n + 1) hd + simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h + cases h_tl : tl.get? n with + | none => simp + | some y => simp [h y h_tl] @[simp] theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by @@ -1560,93 +1507,60 @@ theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl simpa using h 0 1 hd tl_hd Nat.one_pos theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α} - (h_lt : R hd tl_hd) + (h_hd : R hd tl_hd) (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by apply Pairwise.cons _ h_tl - simp only [All_cons_iff] - refine ⟨h_lt, ?_⟩ - apply All_mp _ h_tl.cons_elim.left - intro x h - exact trans_of _ h_lt h + rw [All_cons_iff] + exact ⟨h_hd, All_mp (fun x h ↦ trans_of _ h_hd h) h_tl.cons_elim.left⟩ /-- Coinductive principle for `Pairwise`. -/ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) - : Pairwise R s := by - have h_all : ∀ n, motive (s.drop n) := by - intro n - induction n with - | zero => simpa - | succ m ih => - simp only [drop] - generalize s.drop m = t at * - cases t - · simpa - · exact (h_step _ _ ih).right + (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by simp only [Pairwise] intro i j x y h_ij hx hy - replace h_ij := Nat.exists_eq_add_of_lt h_ij - obtain ⟨k, hj⟩ := h_ij - rw [Nat.add_assoc, Nat.add_comm] at hj - subst hj - rw [show k + 1 + i = i + 1 + k by omega] at hy - simp only [← head_dropn] at hx - rw [← head_dropn, dropn_add, drop, head_dropn] at hy - have := (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left - exact All_get this hy - + obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij + rw [← head_dropn] at hx + rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) i + generalize s.drop i = s' at * + cases s' with + | nil => simp at hx + | cons hd tl => + simp at hx hy + exact hx ▸ All_get (h_step hd tl this).left hy /-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove `R hd tl.head` instead of `tl.All (R hd ·)` in `h_step`. -/ -theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans _ R] {s : Seq α} +theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → tl.head.elim True (R hd ·) ∧ motive tl) - : Pairwise R s := by - have h_all : ∀ n, motive (s.drop n) := by - intro n - induction n with - | zero => simpa - | succ m ih => - simp only [drop] - generalize s.drop m = t at * - cases t - · simpa - · exact (h_step _ _ ih).right + (h_step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : + Pairwise R s := by + have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by + rw [← head_dropn] at hx + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) + exact (h_step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) simp only [Pairwise] intro i j x y h_ij hx hy - replace h_ij := Nat.exists_eq_add_of_lt h_ij - obtain ⟨k, hj⟩ := h_ij - rw [Nat.add_assoc, Nat.add_comm] at hj - subst hj - induction k generalizing i x with - | zero => - simp only [← head_dropn] at hx - rw [Nat.zero_add, Nat.add_comm, ← head_dropn, drop] at hy - have := (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left - simpa only [hy, Option.elim_some] using this + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij + clear h_ij + induction k generalizing y with + | zero => exact h_succ hx hy | succ k ih => - obtain ⟨z, hz⟩ := ge_stable (m := i + 1) _ (by omega) hy - trans z - · simp only [← head_dropn, drop] at hx hz - simpa [hz] using - (h_step x (s.drop i).tail (by convert h_all i; rw [head_eq_some hx, tail_cons])).left - · exact ih (i + 1) z hz (by convert hy using 2; omega) + obtain ⟨z, hz⟩ := ge_stable (m := i + k + 1) _ (by omega) hy + exact _root_.trans (ih z hz) <| h_succ hz hy theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) : s.tail.Pairwise R := by cases s · simp - · simp only [tail_cons] - exact h.cons_elim.right + · simp [h.cons_elim.right] theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) {n : ℕ} : (s.drop n).Pairwise R := by induction n with | zero => simpa - | succ m ih => - simp only [drop] - exact Pairwise_tail ih + | succ m ih => simp [drop, Pairwise_tail ih] end Pairwise @@ -1676,23 +1590,6 @@ theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} simp [TerminatedAt] at h | cons hd' tl' => use hd', tl' -@[simp] -theorem cons_AtLeastAsLongAs_cons {a_hd : α} {a_tl : Seq α} {b_hd : β} - {b_tl : Seq β} : - (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl := by - refine ⟨fun h ↦ ?_, fun h ↦ AtLeastAsLongAs.cons h⟩ - simp [AtLeastAsLongAs] at * - intro n - specialize h (n + 1) - simpa using h - -theorem AtLeastAsLongAs_map {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} - {b : Seq β} (h : a.AtLeastAsLongAs b) : - a.AtLeastAsLongAs (b.map f) := by - simp only [AtLeastAsLongAs, terminatedAt_map_iff] at h ⊢ - intro n ha - simpa [TerminatedAt] using h n ha - /-- Coinductive principle for `AtLeastAsLongAs`. -/ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} (motive : Seq α → Seq β → Prop) (h_base : motive a b) @@ -1701,8 +1598,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} : a.AtLeastAsLongAs b := by simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] intro n - have : b.drop n ≠ .nil → motive (a.drop n) (b.drop n) := by - intro hb + have (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by induction n with | zero => simpa | succ m ih => @@ -1711,19 +1607,35 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} cases tb with | nil => simp at hb | cons tb_hd tb_tl => - simp at ih + simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) - rw [ha] - simpa + simpa [ha] contrapose - intro hb - rw [head_eq_none_iff] at hb + rw [head_eq_none_iff] generalize b.drop n = tb at * cases tb - · simp at hb - · obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) + · simp + · intro hb + obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) simp [ha] +@[simp] +theorem cons_AtLeastAsLongAs_cons_iff {a_hd : α} {a_tl : Seq α} {b_hd : β} + {b_tl : Seq β} : + (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl where + mp h := by + simp [AtLeastAsLongAs] at * + intro n + simpa using h (n + 1) + mpr := AtLeastAsLongAs.cons + +theorem map_AtLeastAsLongAs_self {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} + {b : Seq β} (h : a.AtLeastAsLongAs b) : + a.AtLeastAsLongAs (b.map f) := by + simp only [AtLeastAsLongAs, terminatedAt_map_iff] at * + intro n ha + simpa [TerminatedAt] using h n ha + end AtLeastAsLongAs instance : Functor Seq where map := @map @@ -1877,3 +1789,5 @@ instance lawfulMonad : LawfulMonad Seq1 := LawfulMonad.mk' end Seq1 end Stream' + +set_option linter.style.longFile 2000 diff --git a/Mathlib/Data/Stream/Init.lean b/Mathlib/Data/Stream/Init.lean index 8b4e590f6ec208..3ec82aa69b5dd8 100644 --- a/Mathlib/Data/Stream/Init.lean +++ b/Mathlib/Data/Stream/Init.lean @@ -120,6 +120,10 @@ theorem eq_or_mem_of_mem_cons {a b : α} {s : Stream' α} : (a ∈ b::s) → a = theorem mem_of_get_eq {n : ℕ} {s : Stream' α} {a : α} : a = get s n → a ∈ s := fun h => Exists.intro n h +theorem mem_iff_exists_get_eq {s : Stream' α} {a : α} : a ∈ s ↔ ∃ n, a = s.get n where + mp := by simp [Membership.mem, any_def] + mpr h := mem_of_get_eq h.choose_spec + section Map variable (f : α → β) From ba691a21dbb6ae1cc1b54a262d67ec6da07db399 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Tue, 26 Aug 2025 14:44:07 +0300 Subject: [PATCH 25/34] fix --- Mathlib/Data/Seq/Seq.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 78ebc42cf04e04..084bc80785dab3 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -1398,6 +1398,7 @@ theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some simp only [All, mem_iff_exists_get?] grind +set_option linter.dupNamespace false in private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) (h_step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : motive (s.drop n) := by @@ -1790,4 +1791,4 @@ end Seq1 end Stream' -set_option linter.style.longFile 2000 +set_option linter.style.longFile 1900 From 5fd2b9a80baeaf675b3ad80413ab9ff5931fcf35 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Wed, 27 Aug 2025 10:51:15 +0300 Subject: [PATCH 26/34] before merge --- Mathlib/Data/Seq/Basic.lean | 1794 ++++++++++++++++++++++++++++++++++ Mathlib/Data/Seq/Seq.lean | 1795 +---------------------------------- 2 files changed, 1796 insertions(+), 1793 deletions(-) create mode 100644 Mathlib/Data/Seq/Basic.lean diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean new file mode 100644 index 00000000000000..084bc80785dab3 --- /dev/null +++ b/Mathlib/Data/Seq/Basic.lean @@ -0,0 +1,1794 @@ +/- +Copyright (c) 2017 Mario Carneiro. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Mario Carneiro +-/ +import Mathlib.Data.Option.NAry +import Mathlib.Data.Seq.Computation + +/-! +# Possibly infinite lists + +This file provides a `Seq α` type representing possibly infinite lists (referred here as sequences). + It is encoded as an infinite stream of options such that if `f n = none`, then + `f m = none` for all `m ≥ n`. +-/ + +namespace Stream' + +universe u v w + +/- +coinductive seq (α : Type u) : Type u +| nil : seq α +| cons : α → seq α → seq α +-/ +/-- A stream `s : Option α` is a sequence if `s.get n = none` implies `s.get (n + 1) = none`. +-/ +def IsSeq {α : Type u} (s : Stream' (Option α)) : Prop := + ∀ {n : ℕ}, s n = none → s (n + 1) = none + +/-- `Seq α` is the type of possibly infinite lists (referred here as sequences). + It is encoded as an infinite stream of options such that if `f n = none`, then + `f m = none` for all `m ≥ n`. -/ +def Seq (α : Type u) : Type u := + { f : Stream' (Option α) // f.IsSeq } + +/-- `Seq1 α` is the type of nonempty sequences. -/ +def Seq1 (α) := + α × Seq α + +namespace Seq + +variable {α : Type u} {β : Type v} {γ : Type w} + +/-- Get the nth element of a sequence (if it exists) -/ +def get? : Seq α → ℕ → Option α := + Subtype.val + +@[simp] +theorem val_eq_get (s : Seq α) (n : ℕ) : s.val n = s.get? n := + rfl + +@[simp] +theorem get?_mk (f hf) : @get? α ⟨f, hf⟩ = f := + rfl + +theorem le_stable (s : Seq α) {m n} (h : m ≤ n) : s.get? m = none → s.get? n = none := by + obtain ⟨f, al⟩ := s + induction' h with n _ IH + exacts [id, fun h2 => al (IH h2)] + +/-- If `s.get? n = some aₙ` for some value `aₙ`, then there is also some value `aₘ` such +that `s.get? = some aₘ` for `m ≤ n`. +-/ +theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) + (s_nth_eq_some : s.get? n = some aₙ) : ∃ aₘ : α, s.get? m = some aₘ := + have : s.get? n ≠ none := by simp [s_nth_eq_some] + have : s.get? m ≠ none := mt (s.le_stable m_le_n) this + Option.ne_none_iff_exists'.mp this + +@[ext] +protected theorem ext {s t : Seq α} (h : ∀ n : ℕ, s.get? n = t.get? n) : s = t := + Subtype.eq <| funext h + +/-! +### Constructors +-/ + +/-- The empty sequence -/ +def nil : Seq α := + ⟨Stream'.const none, fun {_} _ => rfl⟩ + +instance : Inhabited (Seq α) := + ⟨nil⟩ + +/-- Prepend an element to a sequence -/ +def cons (a : α) (s : Seq α) : Seq α := + ⟨some a::s.1, by + rintro (n | _) h + · contradiction + · exact s.2 h⟩ + +@[simp] +theorem val_cons (s : Seq α) (x : α) : (cons x s).val = some x::s.val := + rfl + +@[simp] +theorem get?_nil (n : ℕ) : (@nil α).get? n = none := + rfl + +@[simp] +theorem get?_zero_eq_none {s : Seq α} : s.get? 0 = none ↔ s = nil := by + refine ⟨fun h => ?_, fun h => h ▸ rfl⟩ + ext1 n + exact le_stable s (Nat.zero_le _) h + +@[simp] +theorem get?_cons_zero (a : α) (s : Seq α) : (cons a s).get? 0 = some a := + rfl + +@[simp] +theorem get?_cons_succ (a : α) (s : Seq α) (n : ℕ) : (cons a s).get? (n + 1) = s.get? n := + rfl + +@[simp] +theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by + intro h + simpa using congrArg (·.get? 0) h + +@[simp] +theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm + +theorem cons_injective2 : Function.Injective2 (cons : α → Seq α → Seq α) := fun x y s t h => + ⟨by rw [← Option.some_inj, ← get?_cons_zero, h, get?_cons_zero], + Seq.ext fun n => by simp_rw [← get?_cons_succ x s n, h, get?_cons_succ]⟩ + +theorem cons_left_injective (s : Seq α) : Function.Injective fun x => cons x s := + cons_injective2.left _ + +theorem cons_right_injective (x : α) : Function.Injective (cons x) := + cons_injective2.right _ + +theorem cons_eq_cons {x x' : α} {s s' : Seq α} : + (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by + constructor + · apply cons_injective2 + · intro ⟨_, _⟩ + congr + +/-! +### Destructors +-/ + +/-- Get the first element of a sequence -/ +def head (s : Seq α) : Option α := + get? s 0 + +/-- Get the tail of a sequence (or `nil` if the sequence is `nil`) -/ +def tail (s : Seq α) : Seq α := + ⟨s.1.tail, fun n' => by + obtain ⟨f, al⟩ := s + exact al n'⟩ + +/-- Destructor for a sequence, resulting in either `none` (for `nil`) or + `some (a, s)` (for `cons a s`). -/ +def destruct (s : Seq α) : Option (Seq1 α) := + (fun a' => (a', s.tail)) <$> get? s 0 + +-- Porting note: needed universe annotation to avoid universe issues +theorem head_eq_destruct (s : Seq α) : head.{u} s = Prod.fst.{u} <$> destruct.{u} s := by + unfold destruct head; cases get? s 0 <;> rfl + +@[simp] +theorem get?_tail (s : Seq α) (n) : get? (tail s) n = get? s (n + 1) := + rfl + +@[simp] +theorem destruct_nil : destruct (nil : Seq α) = none := + rfl + +@[simp] +theorem destruct_cons (a : α) : ∀ s, destruct (cons a s) = some (a, s) + | ⟨f, al⟩ => by + unfold cons destruct Functor.map + apply congr_arg fun s => some (a, s) + apply Subtype.eq; dsimp [tail] + +theorem destruct_eq_none {s : Seq α} : destruct s = none → s = nil := by + dsimp [destruct] + induction' f0 : get? s 0 <;> intro h + · apply Subtype.eq + funext n + induction' n with n IH + exacts [f0, s.2 IH] + · contradiction + +theorem destruct_eq_cons {s : Seq α} {a s'} : destruct s = some (a, s') → s = cons a s' := by + dsimp [destruct] + induction' f0 : get? s 0 with a' <;> intro h + · contradiction + · obtain ⟨f, al⟩ := s + injections _ h1 h2 + rw [← h2] + apply Subtype.eq + dsimp [tail, cons] + rw [h1] at f0 + rw [← f0] + exact (Stream'.eta f).symm + +@[simp] +theorem head_nil : head (nil : Seq α) = none := + rfl + +@[simp] +theorem head_cons (a : α) (s) : head (cons a s) = some a := by + rw [head_eq_destruct, destruct_cons, Option.map_eq_map, Option.map_some] + +@[simp] +theorem tail_nil : tail (nil : Seq α) = nil := + rfl + +@[simp] +theorem tail_cons (a : α) (s) : tail (cons a s) = s := by + obtain ⟨f, al⟩ := s + apply Subtype.eq + dsimp [tail, cons] + +theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : + s = cons x s.tail := by + ext1 n + cases n <;> simp only [get?_cons_zero, get?_cons_succ, get?_tail] + exact h + +theorem head_eq_none {s : Seq α} (h : s.head = none) : s = nil := + get?_zero_eq_none.mp h + +@[simp] +theorem head_eq_none_iff {s : Seq α} : s.head = none ↔ s = nil := by + constructor + · apply head_eq_none + · intro h + simp [h] + +/-! +### Recursion and corecursion principles +-/ + +/-- Recursion principle for sequences, compare with `List.recOn`. -/ +@[cases_eliminator] +def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) + (cons : ∀ x s, motive (cons x s)) : + motive s := by + rcases H : destruct s with - | v + · rw [destruct_eq_none H] + apply nil + · obtain ⟨a, s'⟩ := v + rw [destruct_eq_cons H] + apply cons + +/-- Functorial action of the functor `Option (α × _)` -/ +@[simp] +def omap (f : β → γ) : Option (α × β) → Option (α × γ) + | none => none + | some (a, b) => some (a, f b) + +/-- Corecursor over pairs of `Option` values -/ +def Corec.f (f : β → Option (α × β)) : Option β → Option α × Option β + | none => (none, none) + | some b => + match f b with + | none => (none, none) + | some (a, b') => (some a, some b') + +/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements + of the sequence until `none` is obtained. -/ +def corec (f : β → Option (α × β)) (b : β) : Seq α := by + refine ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => ?_⟩ + rw [Stream'.corec'_eq] + change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none + revert h; generalize some b = o; revert o + induction' n with n IH <;> intro o + · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none + rcases o with - | b <;> intro h + · rfl + dsimp [Corec.f] at h + dsimp [Corec.f] + revert h; rcases h₁ : f b with - | s <;> intro h + · rfl + · obtain ⟨a, b'⟩ := s + contradiction + · rw [Stream'.corec'_eq (Corec.f f) (Corec.f f o).2, Stream'.corec'_eq (Corec.f f) o] + exact IH (Corec.f f o).2 + +@[simp] +theorem corec_eq (f : β → Option (α × β)) (b : β) : + destruct (corec f b) = omap (corec f) (f b) := by + dsimp [corec, destruct, get] + rw [show Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 from rfl] + dsimp [Corec.f] + induction' h : f b with s; · rfl + obtain ⟨a, b'⟩ := s; dsimp [Corec.f] + apply congr_arg fun b' => some (a, b') + apply Subtype.eq + dsimp [corec, tail] + rw [Stream'.corec'_eq, Stream'.tail_cons] + dsimp [Corec.f]; rw [h] + +theorem corec_nil (f : β → Option (α × β)) (b : β) + (h : f b = .none) : corec f b = nil := by + apply destruct_eq_none + simp [h] + +theorem corec_cons {f : β → Option (α × β)} {b : β} {x : α} {s : β} + (h : f b = .some (x, s)) : corec f b = cons x (corec f s) := by + apply destruct_eq_cons + simp [h] + +/-! +### Bisimulation +-/ + +section Bisim + +variable (R : Seq α → Seq α → Prop) + +local infixl:50 " ~ " => R + +/-- Bisimilarity relation over `Option` of `Seq1 α` -/ +def BisimO : Option (Seq1 α) → Option (Seq1 α) → Prop + | none, none => True + | some (a, s), some (a', s') => a = a' ∧ R s s' + | _, _ => False + +attribute [simp] BisimO +attribute [nolint simpNF] BisimO.eq_3 + +/-- a relation is bisimilar if it meets the `BisimO` test -/ +def IsBisimulation := + ∀ ⦃s₁ s₂⦄, s₁ ~ s₂ → BisimO R (destruct s₁) (destruct s₂) + +/-- If two streams are bisimilar, then they are equal. -/ +theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by + apply Subtype.eq + apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s' + · dsimp [Stream'.IsBisimulation] + intro t₁ t₂ e + exact + match t₁, t₂, e with + | _, _, ⟨s, s', rfl, rfl, r⟩ => by + suffices head s = head s' ∧ R (tail s) (tail s') from + And.imp id (fun r => ⟨tail s, tail s', by cases s using Subtype.recOn; rfl, + by cases s' using Subtype.recOn; rfl, r⟩) this + have := bisim r; revert r this + cases s <;> cases s' + · intro r _ + constructor + · rfl + · assumption + · intro _ this + rw [destruct_nil, destruct_cons] at this + exact False.elim this + · intro _ this + rw [destruct_nil, destruct_cons] at this + exact False.elim this + · simp + · exact ⟨s₁, s₂, rfl, rfl, r⟩ + +/-- Version of `eq_of_bisim` that looks more like an induction principle. -/ +theorem eq_of_bisim' {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (h_base : motive s₁ s₂) + (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ + (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by + apply eq_of_bisim motive _ h_base + intro s₁ s₂ h + rcases h_step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) + · simpa [h₁, h₂] + · simp [h_nil₁, h_nil₂] + +/-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` +instead of `s₁ = nil ∧ s₂ = nil` in `h_step`. -/ +theorem eq_of_bisim_strong {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (h_base : motive s₁ s₂) + (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (s₁ = s₂) ∨ + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))) : s₁ = s₂ := by + let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ + apply eq_of_bisim' motive' (by grind) + intro s₁ s₂ ih + simp only [motive'] at ih ⊢ + rcases ih with (rfl | ih) + · cases s₁ <;> grind + rcases h_step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) + · cases s₁ <;> grind + · grind + +end Bisim + +theorem coinduction : + ∀ {s₁ s₂ : Seq α}, + head s₁ = head s₂ → + (∀ (β : Type u) (fr : Seq α → β), fr s₁ = fr s₂ → fr (tail s₁) = fr (tail s₂)) → s₁ = s₂ + | _, _, hh, ht => + Subtype.eq (Stream'.coinduction hh fun β fr => ht β fun s => fr s.1) + +theorem coinduction2 (s) (f g : Seq α → Seq β) + (H : + ∀ s, + BisimO (fun s1 s2 : Seq β => ∃ s : Seq α, s1 = f s ∧ s2 = g s) (destruct (f s)) + (destruct (g s))) : + f s = g s := by + refine eq_of_bisim (fun s1 s2 => ∃ s, s1 = f s ∧ s2 = g s) ?_ ⟨s, rfl, rfl⟩ + intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ + rw [h1, h2]; apply H + +/-! +### Termination +-/ + +/-- A sequence has terminated at position `n` if the value at position `n` equals `none`. -/ +def TerminatedAt (s : Seq α) (n : ℕ) : Prop := + s.get? n = none + +/-- It is decidable whether a sequence terminates at a given position. -/ +instance terminatedAtDecidable (s : Seq α) (n : ℕ) : Decidable (s.TerminatedAt n) := + decidable_of_iff' (s.get? n).isNone <| by unfold TerminatedAt; cases s.get? n <;> simp + +/-- A sequence terminates if there is some position `n` at which it has terminated. -/ +def Terminates (s : Seq α) : Prop := + ∃ n : ℕ, s.TerminatedAt n + +/-- The length of a terminating sequence. -/ +def length (s : Seq α) (h : s.Terminates) : ℕ := + Nat.find h + +/-- If a sequence terminated at position `n`, it also terminated at `m ≥ n`. -/ +theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.TerminatedAt m → s.TerminatedAt n := + le_stable + +theorem not_terminates_iff {s : Seq α} : ¬s.Terminates ↔ ∀ n, (s.get? n).isSome := by + simp only [Terminates, TerminatedAt, ← Ne.eq_def, Option.ne_none_iff_isSome, not_exists, iff_self] + +theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl + +@[simp] +theorem cons_not_terminatedAt_zero {x : α} {s : Seq α} : + ¬(cons x s).TerminatedAt 0 := by + simp [TerminatedAt] + +@[simp] +theorem cons_terminatedAt_succ_iff {x : α} {s : Seq α} {n : ℕ} : + (cons x s).TerminatedAt (n + 1) ↔ s.TerminatedAt n := by + simp [TerminatedAt] + +@[simp] +theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ + +@[simp] +theorem terminates_cons_iff {x : α} {s : Seq α} : + (cons x s).Terminates ↔ s.Terminates := by + constructor <;> intro ⟨n, h⟩ + · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ + · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ + +@[simp] +theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl + +@[simp] theorem length_eq_zero {s : Seq α} {h : s.Terminates} : + s.length h = 0 ↔ s = nil := by + simp [length, TerminatedAt] + +theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by + refine ⟨?_, ?_⟩ + · intro h + ext n + rw [le_stable _ (Nat.zero_le _) h] + simp + · rintro rfl + simp [TerminatedAt] + +/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ +theorem length_le_iff' {s : Seq α} {n : ℕ} : + (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by + simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] + refine ⟨?_, ?_⟩ + · rintro ⟨_, k, hkn, hk⟩ + exact le_stable s hkn hk + · intro hn + exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + s.length h ≤ n ↔ s.TerminatedAt n := by + rw [← length_le_iff']; simp [h] + +/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ +theorem lt_length_iff' {s : Seq α} {n : ℕ} : + (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by + simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, + ← Option.ne_none_iff_exists', ne_eq] + refine ⟨?_, ?_⟩ + · intro h hn + exact h n hn n le_rfl hn + · intro hn _ _ k hkn hk + exact hn <| le_stable s hkn hk + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + n < s.length h ↔ ∃ a, a ∈ s.get? n := by + rw [← lt_length_iff']; simp [h] + +/-! +### Membership +-/ + +/-- member definition for `Seq` -/ +protected def Mem (s : Seq α) (a : α) := + some a ∈ s.1 + +instance : Membership α (Seq α) := + ⟨Seq.Mem⟩ + +-- Cannot be @[simp] because `n` can not be inferred by `simp`. +theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ + +theorem mem_iff_exists_get? {s : Seq α} {x : α} : x ∈ s ↔ ∃ i, some x = s.get? i where + mp h := by + change (some x ∈ s.1) at h + rwa [Stream'.mem_iff_exists_get_eq] at h + mpr h := get?_mem h.choose_spec.symm + +@[simp] +theorem notMem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h + +@[deprecated (since := "2025-05-23")] alias not_mem_nil := notMem_nil + +theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s + | ⟨_, _⟩ => Stream'.mem_cons (some a) _ + +theorem mem_cons_of_mem (y : α) {a : α} : ∀ {s : Seq α}, a ∈ s → a ∈ cons y s + | ⟨_, _⟩ => Stream'.mem_cons_of_mem (some y) + +theorem eq_or_mem_of_mem_cons {a b : α} : ∀ {s : Seq α}, a ∈ cons b s → a = b ∨ a ∈ s + | ⟨_, _⟩, h => (Stream'.eq_or_mem_of_mem_cons h).imp_left fun h => by injection h + +@[simp] +theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ∈ s := + ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ + +theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) + (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by + obtain ⟨k, e⟩ := M; unfold Stream'.get at e + induction' k with k IH generalizing s + · have TH : s = cons a (tail s) := by + apply destruct_eq_cons + unfold destruct get? Functor.map + rw [← e] + rfl + rw [TH] + apply h1 _ _ (Or.inl rfl) + cases s with + | nil => injection e + | cons b s' => + have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s' using Subtype.recOn; rfl + rw [h_eq] at e + apply h1 _ _ (Or.inr (IH e)) + +/-! +### Converting from/to other types +-/ + +/-- Embed a list as a sequence -/ +@[coe] +def ofList (l : List α) : Seq α := + ⟨(l[·]?), fun {n} h => by + rw [List.getElem?_eq_none_iff] at h ⊢ + exact Nat.le_succ_of_le h⟩ + +instance coeList : Coe (List α) (Seq α) := + ⟨ofList⟩ + +@[simp] +theorem ofList_nil : ofList [] = (nil : Seq α) := + rfl + +@[simp] +theorem ofList_get? (l : List α) (n : ℕ) : (ofList l).get? n = l[n]? := + rfl + +@[deprecated (since := "2025-02-21")] +alias ofList_get := ofList_get? + +@[simp] +theorem ofList_cons (a : α) (l : List α) : ofList (a::l) = cons a (ofList l) := by + ext1 (_ | n) <;> simp + +theorem ofList_injective : Function.Injective (ofList : List α → _) := + fun _ _ h => List.ext_getElem? fun _ => congr_fun (Subtype.ext_iff.1 h) _ + +/-- Embed an infinite stream as a sequence -/ +@[coe] +def ofStream (s : Stream' α) : Seq α := + ⟨s.map some, fun {n} h => by contradiction⟩ + +instance coeStream : Coe (Stream' α) (Seq α) := + ⟨ofStream⟩ + +section MLList + +/-- Embed a `MLList α` as a sequence. Note that even though this + is non-meta, it will produce infinite sequences if used with + cyclic `MLList`s created by meta constructions. -/ +def ofMLList : MLList Id α → Seq α := + corec fun l => + match l.uncons with + | .none => none + | .some (a, l') => some (a, l') + +instance coeMLList : Coe (MLList Id α) (Seq α) := + ⟨ofMLList⟩ + +/-- Translate a sequence into a `MLList`. -/ +unsafe def toMLList : Seq α → MLList Id α + | s => + match destruct s with + | none => .nil + | some (a, s') => .cons a (toMLList s') + +end MLList + +/-- Translate a sequence to a list. This function will run forever if + run on an infinite sequence. -/ +unsafe def forceToList (s : Seq α) : List α := + (toMLList s).force + +/-- Take the first `n` elements of the sequence (producing a list) -/ +def take : ℕ → Seq α → List α + | 0, _ => [] + | n + 1, s => + match destruct s with + | none => [] + | some (x, r) => List.cons x (take n r) + +/-- Convert a sequence which is known to terminate into a list -/ +def toList (s : Seq α) (h : s.Terminates) : List α := + take (length s h) s + +/-- Convert a sequence which is known not to terminate into a stream -/ +def toStream (s : Seq α) (h : ¬s.Terminates) : Stream' α := fun n => + Option.get _ <| not_terminates_iff.1 h n + +/-- Convert a sequence into either a list or a stream depending on whether + it is finite or infinite. (Without decidability of the infiniteness predicate, + this is not constructively possible.) -/ +def toListOrStream (s : Seq α) [Decidable s.Terminates] : List α ⊕ Stream' α := + if h : s.Terminates then Sum.inl (toList s h) else Sum.inr (toStream s h) + +/-- Convert a sequence into a list, embedded in a computation to allow for + the possibility of infinite sequences (in which case the computation + never returns anything). -/ +def toList' {α} (s : Seq α) : Computation (List α) := + @Computation.corec (List α) (List α × Seq α) + (fun ⟨l, s⟩ => + match destruct s with + | none => Sum.inl l.reverse + | some (a, s') => Sum.inr (a::l, s')) + ([], s) + +/-! +### Operations on sequences +-/ + +/-- Append two sequences. If `s₁` is infinite, then `s₁ ++ s₂ = s₁`, + otherwise it puts `s₂` at the location of the `nil` in `s₁`. -/ +def append (s₁ s₂ : Seq α) : Seq α := + @corec α (Seq α × Seq α) + (fun ⟨s₁, s₂⟩ => + match destruct s₁ with + | none => omap (fun s₂ => (nil, s₂)) (destruct s₂) + | some (a, s₁') => some (a, s₁', s₂)) + (s₁, s₂) + +/-- Map a function over a sequence. -/ +def map (f : α → β) : Seq α → Seq β + | ⟨s, al⟩ => + ⟨s.map (Option.map f), fun {n} => by + dsimp [Stream'.map, Stream'.get] + induction' e : s n with e <;> intro + · rw [al e] + assumption + · contradiction⟩ + +/-- Flatten a sequence of sequences. (It is required that the + sequences be nonempty to ensure productivity; in the case + of an infinite sequence of `nil`, the first element is never + generated.) -/ +def join : Seq (Seq1 α) → Seq α := + corec fun S => + match destruct S with + | none => none + | some ((a, s), S') => + some + (a, + match destruct s with + | none => S' + | some s' => cons s' S') + +/-- Remove the first `n` elements from the sequence. -/ +def drop (s : Seq α) : ℕ → Seq α + | 0 => s + | n + 1 => tail (drop s n) + +/-- Split a sequence at `n`, producing a finite initial segment + and an infinite tail. -/ +def splitAt : ℕ → Seq α → List α × Seq α + | 0, s => ([], s) + | n + 1, s => + match destruct s with + | none => ([], nil) + | some (x, s') => + let (l, r) := splitAt n s' + (List.cons x l, r) + +/-- Combine two sequences with a function -/ +def zipWith (f : α → β → γ) (s₁ : Seq α) (s₂ : Seq β) : Seq γ := + ⟨fun n => Option.map₂ f (s₁.get? n) (s₂.get? n), fun {_} hn => + Option.map₂_eq_none_iff.2 <| (Option.map₂_eq_none_iff.1 hn).imp s₁.2 s₂.2⟩ + +/-- Pair two sequences into a sequence of pairs -/ +def zip : Seq α → Seq β → Seq (α × β) := + zipWith Prod.mk + +/-- Separate a sequence of pairs into two sequences -/ +def unzip (s : Seq (α × β)) : Seq α × Seq β := + (map Prod.fst s, map Prod.snd s) + +/-- The sequence of natural numbers some 0, some 1, ... -/ +def nats : Seq ℕ := + Stream'.nats + +/-- Enumerate a sequence by tagging each element with its index. -/ +def enum (s : Seq α) : Seq (ℕ × α) := + Seq.zip nats s + +/-- Folds a sequence using `f`, producing a sequence of intermediate values, i.e. +`[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ +def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := + let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => + match destruct x with + | none => .none + | some (x, s) => .some (f acc x, f acc x, s) + cons init <| corec f (init, s) + +/-- Applies `f` to the `n`th element of the sequence, if it exists, replacing that element +with the result. -/ +def update (s : Seq α) (n : ℕ) (f : α → α) : Seq α where + val := Function.update s.val n ((s.val n).map f) + property := by + have (i : ℕ) : Function.update s.val n ((s.get? n).map f) i = none ↔ s.get? i = none := by + by_cases hi : i = n <;> simp [Function.update, hi] + simp only [IsSeq, val_eq_get, this] + exact @s.prop + +/-- Sets the value of sequence `s` at index `n` to `a`. If the `n`th element does not exist +(`s` terminates earlier), the sequence is left unchanged. -/ +def set (s : Seq α) (n : ℕ) (a : α) : Seq α := + update s n fun _ ↦ a + +/-! +### Predicates on sequences +-/ + +/-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ +def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x + +/-- +`Pairwise R s` means that all the elements with earlier indexes are +`R`-related to all the elements with later indexes. +``` +Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 +``` +For example if `R = (· ≠ ·)` then it asserts `s` has no duplicates, +and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. +-/ +def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := + ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y + +/-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. +In particular, they both may be infinite. -/ +def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := + ∀ n, a.TerminatedAt n → b.TerminatedAt n + +section OfStream + +@[simp] +theorem ofStream_cons (a : α) (s) : ofStream (a::s) = cons a (ofStream s) := by + apply Subtype.eq; simp only [ofStream, cons]; rw [Stream'.map_cons] + +end OfStream + +section OfList + +theorem terminatedAt_ofList (l : List α) : + (ofList l).TerminatedAt l.length := by + simp [ofList, TerminatedAt] + +theorem terminates_ofList (l : List α) : (ofList l).Terminates := + ⟨_, terminatedAt_ofList l⟩ + +end OfList + +section Take + +@[simp] +theorem take_nil {n : ℕ} : (nil (α := α)).take n = List.nil := by + cases n <;> rfl + +@[simp] +theorem take_zero {s : Seq α} : s.take 0 = [] := by + cases s <;> rfl + +@[simp] +theorem take_succ_cons {n : ℕ} {x : α} {s : Seq α} : + (cons x s).take (n + 1) = x :: s.take n := by + rfl + +@[simp] +theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), + (s.take k)[n]? = if n < k then s.get? n else none + | n, 0, s => by simp [take] + | n, k+1, s => by + rw [take] + cases h : destruct s with + | none => + simp [destruct_eq_none h] + | some a => + match a with + | (x, r) => + rw [destruct_eq_cons h] + match n with + | 0 => simp + | n+1 => + simp [List.getElem?_cons_succ, Nat.add_lt_add_iff_right, getElem?_take] + +theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} + (h_get : s.get? m = .some x) : x ∈ s.take n := by + induction m generalizing n s with + | zero => + obtain ⟨l, hl⟩ := Nat.exists_add_one_eq.mpr h_mn + rw [← hl, take, head_eq_some h_get] + simp + | succ k ih => + obtain ⟨l, hl⟩ := Nat.exists_eq_add_of_lt h_mn + subst hl + have : ∃ y, s.get? 0 = .some y := by + apply ge_stable _ _ h_get + simp + obtain ⟨y, hy⟩ := this + rw [take, head_eq_some hy] + simp + right + apply ih (by omega) + rwa [get?_tail] + +theorem length_take_le {s : Seq α} {n : ℕ} : (s.take n).length ≤ n := by + induction n generalizing s with + | zero => simp + | succ m ih => + rw [take] + cases s.destruct with + | none => simp + | some v => + obtain ⟨x, r⟩ := v + simpa using ih + +theorem length_take_of_le_length {s : Seq α} {n : ℕ} + (hle : ∀ h : s.Terminates, n ≤ s.length h) : (s.take n).length = n := by + induction n generalizing s with + | zero => simp [take] + | succ n ih => + rw [take, destruct] + let ⟨a, ha⟩ := lt_length_iff'.1 (fun ht => lt_of_lt_of_le (Nat.succ_pos _) (hle ht)) + simp [Option.mem_def.1 ha] + rw [ih] + intro h + simp only [length, tail, Nat.le_find_iff, TerminatedAt, get?_mk, Stream'.tail] + intro m hmn hs + have := lt_length_iff'.1 (fun ht => (Nat.lt_of_succ_le (hle ht))) + rw [le_stable s (Nat.succ_le_of_lt hmn) hs] at this + simp at this + +end Take + +section ToList + +@[simp] +theorem length_toList (s : Seq α) (h : s.Terminates) : (toList s h).length = length s h := by + rw [toList, length_take_of_le_length] + intro _ + exact le_rfl + +@[simp] +theorem getElem?_toList (s : Seq α) (h : s.Terminates) (n : ℕ) : (toList s h)[n]? = s.get? n := by + ext k + simp only [toList, getElem?_take, Nat.lt_find_iff, length, + Option.ite_none_right_eq_some, and_iff_right_iff_imp, TerminatedAt] + intro h m hmn + let ⟨a, ha⟩ := ge_stable s hmn h + simp [ha] + +@[simp] +theorem ofList_toList (s : Seq α) (h : s.Terminates) : + ofList (toList s h) = s := by + ext n; simp [ofList] + +@[simp] +theorem toList_ofList (l : List α) : toList (ofList l) (terminates_ofList l) = l := + ofList_injective (by simp) + +@[simp] +theorem toList_nil : toList (nil : Seq α) ⟨0, terminatedAt_zero_iff.2 rfl⟩ = [] := by + ext; simp [nil, toList, const] + +theorem getLast?_toList (s : Seq α) (h : s.Terminates) : + (toList s h).getLast? = s.get? (s.length h - 1) := by + rw [List.getLast?_eq_getElem?, getElem?_toList, length_toList] + +end ToList + +section Append + +@[simp] +theorem cons_append (a : α) (s t) : append (cons a s) t = cons a (append s t) := + destruct_eq_cons <| by + dsimp [append]; rw [corec_eq] + dsimp [append]; rw [destruct_cons] + +@[simp] +theorem nil_append (s : Seq α) : append nil s = s := by + apply coinduction2; intro s + dsimp [append]; rw [corec_eq] + dsimp [append] + cases s + · trivial + · rw [destruct_cons] + dsimp + exact ⟨rfl, _, rfl, rfl⟩ + +@[simp] +theorem append_nil (s : Seq α) : append s nil = s := by + apply coinduction2 s; intro s + cases s + · trivial + · rw [cons_append, destruct_cons, destruct_cons] + dsimp + exact ⟨rfl, _, rfl, rfl⟩ + +@[simp] +theorem append_assoc (s t u : Seq α) : append (append s t) u = append s (append t u) := by + apply eq_of_bisim fun s1 s2 => ∃ s t u, s1 = append (append s t) u ∧ s2 = append s (append t u) + · intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, t, u, rfl, rfl⟩ => by + cases s <;> simp + case nil => + cases t <;> simp + case nil => + cases u <;> simp + case cons _ u => refine ⟨nil, nil, u, ?_, ?_⟩ <;> simp + case cons _ t => refine ⟨nil, t, u, ?_, ?_⟩ <;> simp + case cons _ s => exact ⟨s, t, u, rfl, rfl⟩ + · exact ⟨s, t, u, rfl, rfl⟩ + +theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) : a ∈ s₁ ∨ a ∈ s₂ := by + have := h; revert this + generalize e : append s₁ s₂ = ss; intro h; revert s₁ + apply mem_rec_on h _ + intro b s' o s₁ + cases s₁ with + | nil => + intro m _ + apply Or.inr + simpa using m + | cons c t₁ => + intro m e + have this := congr_arg destruct e + rcases show a = c ∨ a ∈ append t₁ s₂ by simpa using m with e' | m + · rw [e'] + exact Or.inl (mem_cons _ _) + · obtain ⟨i1, i2⟩ := show c = b ∧ append t₁ s₂ = s' by simpa + rcases o with e' | IH + · simp [i1, e'] + · exact Or.imp_left (mem_cons_of_mem _) (IH m i2) + +theorem mem_append_left {s₁ s₂ : Seq α} {a : α} (h : a ∈ s₁) : a ∈ append s₁ s₂ := by + apply mem_rec_on h; intros; simp [*] + +@[simp] +theorem ofList_append (l l' : List α) : ofList (l ++ l') = append (ofList l) (ofList l') := by + induction l <;> simp [*] + +@[simp] +theorem ofStream_append (l : List α) (s : Stream' α) : + ofStream (l ++ₛ s) = append (ofList l) (ofStream s) := by + induction l <;> simp [*, Stream'.nil_append_stream, Stream'.cons_append_stream] + +end Append + +section Map + +@[simp] +theorem map_get? (f : α → β) : ∀ s n, get? (map f s) n = (get? s n).map f + | ⟨_, _⟩, _ => rfl + +@[simp] +theorem map_nil (f : α → β) : map f nil = nil := + rfl + +@[simp] +theorem map_cons (f : α → β) (a) : ∀ s, map f (cons a s) = cons (f a) (map f s) + | ⟨s, al⟩ => by apply Subtype.eq; dsimp [cons, map]; rw [Stream'.map_cons]; rfl + +@[simp] +theorem map_id : ∀ s : Seq α, map id s = s + | ⟨s, al⟩ => by + apply Subtype.eq; dsimp [map] + rw [Option.map_id, Stream'.map_id] + +@[simp] +theorem map_tail (f : α → β) : ∀ s, map f (tail s) = tail (map f s) + | ⟨s, al⟩ => by apply Subtype.eq; dsimp [tail, map] + +theorem map_comp (f : α → β) (g : β → γ) : ∀ s : Seq α, map (g ∘ f) s = map g (map f s) + | ⟨s, al⟩ => by + apply Subtype.eq; dsimp [map] + apply congr_arg fun f : _ → Option γ => Stream'.map f s + ext ⟨⟩ <;> rfl + +@[simp] +theorem terminatedAt_map_iff {f : α → β} {s : Seq α} {n : ℕ} : + (map f s).TerminatedAt n ↔ s.TerminatedAt n := by + simp [TerminatedAt] + +@[simp] +theorem terminates_map_iff {f : α → β} {s : Seq α} : + (map f s).Terminates ↔ s.Terminates := by + simp [Terminates] + +@[simp] +theorem length_map {s : Seq α} {f : α → β} (h : (s.map f).Terminates) : + (s.map f).length h = s.length (terminates_map_iff.1 h) := by + rw [length] + congr + ext + simp + +theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s + | ⟨_, _⟩ => Stream'.mem_map (Option.map f) + +theorem exists_of_mem_map {f} {b : β} : ∀ {s : Seq α}, b ∈ map f s → ∃ a, a ∈ s ∧ f a = b := + fun {s} h => by match s with + | ⟨g, al⟩ => + let ⟨o, om, oe⟩ := @Stream'.exists_of_mem_map _ _ (Option.map f) (some b) g h + rcases o with - | a + · injection oe + · injection oe with h'; exact ⟨a, om, h'⟩ + +@[simp] +theorem map_append (f : α → β) (s t) : map f (append s t) = append (map f s) (map f t) := by + apply + eq_of_bisim (fun s1 s2 => ∃ s t, s1 = map f (append s t) ∧ s2 = append (map f s) (map f t)) _ + ⟨s, t, rfl, rfl⟩ + intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, t, rfl, rfl⟩ => by + cases s <;> simp + case nil => + cases t <;> simp + case cons _ t => refine ⟨nil, t, ?_, ?_⟩ <;> simp + case cons _ s => exact ⟨s, t, rfl, rfl⟩ + +end Map + +section Join + + +@[simp] +theorem join_nil : join nil = (nil : Seq α) := + destruct_eq_none rfl + +-- Not a simp lemmas as `join_cons` is more general +theorem join_cons_nil (a : α) (S) : join (cons (a, nil) S) = cons a (join S) := + destruct_eq_cons <| by simp [join] + +-- Not a simp lemmas as `join_cons` is more general +theorem join_cons_cons (a b : α) (s S) : + join (cons (a, cons b s) S) = cons a (join (cons (b, s) S)) := + destruct_eq_cons <| by simp [join] + +@[simp] +theorem join_cons (a : α) (s S) : join (cons (a, s) S) = cons a (append s (join S)) := by + apply + eq_of_bisim + (fun s1 s2 => s1 = s2 ∨ ∃ a s S, s1 = join (cons (a, s) S) ∧ s2 = cons a (append s (join S))) + _ (Or.inr ⟨a, s, S, rfl, rfl⟩) + intro s1 s2 h + exact + match s1, s2, h with + | s, _, Or.inl <| Eq.refl s => by + cases s; · trivial + · rw [destruct_cons] + exact ⟨rfl, Or.inl rfl⟩ + | _, _, Or.inr ⟨a, s, S, rfl, rfl⟩ => by + cases s + · simp [join_cons_nil] + · simpa [join_cons_cons, join_cons_nil] using Or.inr ⟨_, _, S, rfl, rfl⟩ + +@[simp] +theorem join_append (S T : Seq (Seq1 α)) : join (append S T) = append (join S) (join T) := by + apply + eq_of_bisim fun s1 s2 => + ∃ s S T, s1 = append s (join (append S T)) ∧ s2 = append s (append (join S) (join T)) + · intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, S, T, rfl, rfl⟩ => by + cases s <;> simp + case nil => + cases S <;> simp + case nil => + cases T with + | nil => simp + | cons s T => + obtain ⟨a, s⟩ := s; simp only [join_cons, destruct_cons, true_and] + refine ⟨s, nil, T, ?_, ?_⟩ <;> simp + case cons s S => + obtain ⟨a, s⟩ := s + simpa using ⟨s, S, T, rfl, rfl⟩ + case cons _ s => exact ⟨s, S, T, rfl, rfl⟩ + · refine ⟨nil, S, T, ?_, ?_⟩ <;> simp + +end Join + +section Drop + +@[simp] +theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) := by + induction n generalizing m with + | zero => simp [drop] + | succ k ih => + simp [Seq.get?_tail, drop] + convert ih using 2 + omega + +theorem dropn_add (s : Seq α) (m) : ∀ n, drop s (m + n) = drop (drop s m) n + | 0 => rfl + | n + 1 => congr_arg tail (dropn_add s _ n) + +theorem dropn_tail (s : Seq α) (n) : drop (tail s) n = drop s (n + 1) := by + rw [Nat.add_comm]; symm; apply dropn_add + +@[simp] +theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by + induction' n with n IH generalizing s; · rfl + rw [← get?_tail, ← dropn_tail]; apply IH + +@[simp] +theorem drop_zero {s : Seq α} : s.drop 0 = s := rfl + +@[simp] +theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : + (cons x s).drop (n + 1) = s.drop n := by + simp [← dropn_tail] + +@[simp] +theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by + induction n with + | zero => simp [drop] + | succ m ih => simp [← dropn_tail, ih] + +theorem take_drop {s : Seq α} {n m : ℕ} : + (s.take n).drop m = (s.drop m).take (n - m) := by + induction m generalizing n s with + | zero => simp [drop] + | succ k ih => + cases s + · simp + cases n with + | zero => simp + | succ l => + simp only [take, destruct_cons, List.drop_succ_cons, Nat.reduceSubDiff] + rw [ih] + congr 1 + rw [drop_succ_cons] + +end Drop + +section ZipWith + +@[simp] +theorem get?_zipWith (f : α → β → γ) (s s' n) : + (zipWith f s s').get? n = Option.map₂ f (s.get? n) (s'.get? n) := + rfl + +@[simp] +theorem get?_zip (s : Seq α) (t : Seq β) (n : ℕ) : + get? (zip s t) n = Option.map₂ Prod.mk (get? s n) (get? t n) := + get?_zipWith _ _ _ _ + +@[simp] +theorem nats_get? (n : ℕ) : nats.get? n = some n := + rfl + +@[simp] +theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := + get?_zip _ _ _ + +@[simp] +theorem zipWith_nil_left {f : α → β → γ} {s} : + zipWith f nil s = nil := + rfl + +@[simp] +theorem zipWith_nil_right {f : α → β → γ} {s} : + zipWith f s nil = nil := by + ext1 + simp + +@[simp] +theorem zipWith_cons_cons {f : α → β → γ} {x s x' s'} : + zipWith f (cons x s) (cons x' s') = cons (f x x') (zipWith f s s') := by + ext1 n + cases n <;> simp + +@[simp] +theorem zip_nil_left {s : Seq α} : + zip (@nil α) s = nil := + rfl + +@[simp] +theorem zip_nil_right {s : Seq α} : + zip s (@nil α) = nil := + zipWith_nil_right + +@[simp] +theorem zip_cons_cons {s s' : Seq α} {x x'} : + zip (cons x s) (cons x' s') = cons (x, x') (zip s s') := + zipWith_cons_cons + +@[simp] +theorem enum_nil : enum (nil : Seq α) = nil := + rfl + +@[simp] +theorem enum_cons (s : Seq α) (x : α) : + enum (cons x s) = cons (0, x) (map (Prod.map Nat.succ id) (enum s)) := by + ext ⟨n⟩ : 1 + · simp + · simp only [get?_enum, get?_cons_succ, map_get?, Option.map_map] + congr + +universe u' v' +variable {α' : Type u'} {β' : Type v'} + +theorem zipWith_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') (g : α' → β' → γ) : + zipWith g (s₁.map f₁) (s₂.map f₂) = zipWith (fun a b ↦ g (f₁ a) (f₂ b)) s₁ s₂ := by + ext1 n + simp only [get?_zipWith, map_get?] + cases s₁.get? n <;> cases s₂.get? n <;> simp + +theorem zipWith_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') (g : α' → β → γ) : + zipWith g (s₁.map f) s₂ = zipWith (fun a b ↦ g (f a) b) s₁ s₂ := by + convert zipWith_map _ _ _ (@id β) _ + simp + +theorem zipWith_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') (g : α → β' → γ) : + zipWith g s₁ (s₂.map f) = zipWith (fun a b ↦ g a (f b)) s₁ s₂ := by + convert zipWith_map _ _ (@id α) _ _ + simp + +theorem zip_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') : + (s₁.map f₁).zip (s₂.map f₂) = (s₁.zip s₂).map (Prod.map f₁ f₂) := by + ext1 n + simp + cases s₁.get? n <;> cases s₂.get? n <;> simp + +theorem zip_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') : + (s₁.map f).zip s₂ = (s₁.zip s₂).map (Prod.map f id) := by + convert zip_map _ _ _ _ + simp + +theorem zip_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') : + s₁.zip (s₂.map f) = (s₁.zip s₂).map (Prod.map id f) := by + convert zip_map _ _ _ _ + simp + +end ZipWith + +section Fold + +@[simp] +theorem fold_nil (init : β) (f : β → α → β) : + nil.fold init f = cons init nil := by + unfold fold + simp [corec_nil] + +@[simp] +theorem fold_cons (init : β) (f : β → α → β) (x : α) (s : Seq α) : + (cons x s).fold init f = cons init (s.fold (f init x) f) := by + unfold fold + dsimp only + congr + rw [corec_cons] + simp + +@[simp] +theorem fold_head (init : β) (f : β → α → β) (s : Seq α) : + (s.fold init f).head = init := by + simp [fold] + +end Fold + +section Update + +variable (hd x : α) (tl : Seq α) (f : α → α) + +theorem get?_update (s : Seq α) (n : ℕ) (m : ℕ) : + (s.update n f).get? m = if m = n then (s.get? m).map f else s.get? m := by + simp [update, Function.update] + split_ifs with h_if + · simp [h_if] + · rfl + +@[simp] +theorem update_nil (n : ℕ) : update nil n f = nil := by + ext1 m + simp [get?_update] + +@[simp] +theorem set_nil (n : ℕ) (x : α) : set nil n x = nil := update_nil _ _ + +@[simp] +theorem update_cons_zero : (cons hd tl).update 0 f = cons (f hd) tl := by + ext1 n + cases n <;> simp [get?_update] + +@[simp] +theorem set_cons_zero (hd' : α) : (cons hd tl).set 0 hd' = cons hd' tl := + update_cons_zero _ _ _ + +@[simp] +theorem update_cons_succ (n : ℕ) : (cons hd tl).update (n + 1) f = cons hd (tl.update n f) := by + ext1 n + cases n <;> simp [get?_update] + +@[simp] +theorem set_cons_succ (n : ℕ) : (cons hd tl).set (n + 1) x = cons hd (tl.set n x) := + update_cons_succ _ _ _ _ + +theorem get?_set_of_not_terminatedAt {s : Seq α} {n : ℕ} (h_not_terminated : ¬ s.TerminatedAt n) : + (s.set n x).get? n = x := by + simpa [set, update, ← Option.ne_none_iff_exists'] using h_not_terminated + +theorem get?_set_of_terminatedAt {s : Seq α} {n : ℕ} (h_terminated : s.TerminatedAt n) : + (s.set n x).get? n = .none := by + simpa [set, get?_update] using h_terminated + +theorem get?_set_of_ne (s : Seq α) {m n : ℕ} (h : n ≠ m) : (s.set m x).get? n = s.get? n := by + simp [set, get?_update, h] + +theorem drop_set_of_lt (s : Seq α) {m n : ℕ} (h : m < n) : (s.set m x).drop n = s.drop n := by + ext1 i + simp [get?_set_of_ne _ _ (show n + i ≠ m by omega)] + +end Update + +section All + +@[simp] +theorem All.nil (p : α → Prop) : nil.All p := by + simp [All] + +theorem All.cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : tl.All p) : + ((cons hd tl).All p) := by + simp only [All, mem_cons_iff, forall_eq_or_imp] at * + exact ⟨h_hd, h_tl⟩ + +@[simp] +theorem All_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : + ((cons hd tl).All p) ↔ p hd ∧ tl.All p := by + simp [All] + +theorem All_get {p : α → Prop} {s : Seq α} (h : s.All p) {n : ℕ} {x : α} (hx : s.get? n = .some x) : + p x := by + unfold All at h + exact h _ (get?_mem hx) + +theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : + s.All p := by + simp only [All, mem_iff_exists_get?] + grind + +set_option linter.dupNamespace false in +private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : + motive (s.drop n) := by + induction n with + | zero => simpa + | succ m ih => + simp only [drop] + generalize s.drop m = t at * + cases t + · simpa + · exact h_step _ _ ih + +/-- Coinductive principle for `All`. -/ +theorem All.coind {s : Seq α} {p : α → Prop} + (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) + : s.All p := by + apply All_of_get + intro n + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) n + rw [← head_dropn] + generalize s.drop n = s' at this + cases s' with + | nil => simp + | cons hd tl => simp [(h_step hd tl this).left] + +theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : + s.All q := by + simp only [All] at * + grind + +theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : + (s.map f).All p ↔ s.All (p ∘ f) := by + simp [All] + refine ⟨fun _ _ hx ↦ ?_, fun _ _ hx ↦ ?_⟩ + · solve_by_elim [mem_map f hx] + · obtain ⟨_, _, hx'⟩ := exists_of_mem_map hx + rw [← hx'] + solve_by_elim + +theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} {x : α} (hx : x ∈ s.take n) : + p x := by + induction n generalizing s with + | zero => simp [take] at hx + | succ m ih => + cases s with + | nil => simp at hx + | cons hd tl => + simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all + rcases hx with (rfl | hx) + exacts [h_all.left, ih h_all.right hx] + +theorem set_All {p : α → Prop} {s : Seq α} (h_all : s.All p) {n : ℕ} {x : α} + (hx : p x) : (s.set n x).All p := by + apply All_of_get + intro m + rcases eq_or_ne n m with (rfl | h_nm) + · by_cases h_term : s.TerminatedAt n + · simp [get?_set_of_terminatedAt _ h_term] + · simpa [get?_set_of_not_terminatedAt _ h_term] + · rw [get?_set_of_ne] + exacts [fun x hx ↦ All_get h_all hx, h_nm.symm] + +end All + +section Pairwise + +@[simp] +theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by + simp [Pairwise] + +theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} + (h_lt : tl.All (R hd ·)) + (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by + simp only [Pairwise] at * + intro i j x y h_ij hx hy + cases j with + | zero => simp at h_ij + | succ k => + simp only [get?_cons_succ] at hy + cases i with + | zero => + simp only [get?_cons_zero, Option.some.injEq] at hx + exact hx ▸ All_get h_lt hy + | succ n => exact h_tl n k x y (by omega) hx hy + +theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} + (h : Pairwise R (.cons hd tl)) : tl.All (R hd ·) ∧ Pairwise R tl := by + simp only [Pairwise] at * + refine ⟨?_, fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy⟩ + apply All_of_get + intro n + specialize h 0 (n + 1) hd + simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h + cases h_tl : tl.get? n with + | none => simp + | some y => simp [h y h_tl] + +@[simp] +theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by + apply Pairwise.cons <;> simp + +theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl : Seq α} + (h : Pairwise R (cons hd (cons tl_hd tl_tl))) : + R hd tl_hd := by + simp only [Pairwise] at h + simpa using h 0 1 hd tl_hd Nat.one_pos + +theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α} + (h_hd : R hd tl_hd) + (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by + apply Pairwise.cons _ h_tl + rw [All_cons_iff] + exact ⟨h_hd, All_mp (fun x h ↦ trans_of _ h_hd h) h_tl.cons_elim.left⟩ + +/-- Coinductive principle for `Pairwise`. -/ +theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} + (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by + simp only [Pairwise] + intro i j x y h_ij hx hy + obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij + rw [← head_dropn] at hx + rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) i + generalize s.drop i = s' at * + cases s' with + | nil => simp at hx + | cons hd tl => + simp at hx hy + exact hx ▸ All_get (h_step hd tl this).left hy + +/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove +`R hd tl.head` instead of `tl.All (R hd ·)` in `h_step`. -/ +theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α} + (motive : Seq α → Prop) (h_base : motive s) + (h_step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : + Pairwise R s := by + have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by + rw [← head_dropn] at hx + have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) + exact (h_step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) + simp only [Pairwise] + intro i j x y h_ij hx hy + obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij + clear h_ij + induction k generalizing y with + | zero => exact h_succ hx hy + | succ k ih => + obtain ⟨z, hz⟩ := ge_stable (m := i + k + 1) _ (by omega) hy + exact _root_.trans (ih z hz) <| h_succ hz hy + +theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) : + s.tail.Pairwise R := by + cases s + · simp + · simp [h.cons_elim.right] + +theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) {n : ℕ} : + (s.drop n).Pairwise R := by + induction n with + | zero => simpa + | succ m ih => simp [drop, Pairwise_tail ih] + +end Pairwise + +section AtLeastAsLongAs + +theorem AtLeastAsLongAs.nil {a : Seq α} : + a.AtLeastAsLongAs (@nil β) := by + unfold AtLeastAsLongAs + simp [terminatedAt_nil] + +theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} + (h : a_tl.AtLeastAsLongAs b_tl) : + (Seq.cons a_hd a_tl).AtLeastAsLongAs (Seq.cons b_hd b_tl) := by + simp only [AtLeastAsLongAs] at * + intro n + cases n with + | zero => simp + | succ m => simpa using h m + +theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} + (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by + cases a with + | nil => + unfold AtLeastAsLongAs at h + simp only [terminatedAt_nil, forall_const] at h + specialize h 0 + simp [TerminatedAt] at h + | cons hd' tl' => use hd', tl' + +/-- Coinductive principle for `AtLeastAsLongAs`. -/ +theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} + (motive : Seq α → Seq β → Prop) (h_base : motive a b) + (h_step : ∀ a b, motive a b → + (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) + : a.AtLeastAsLongAs b := by + simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] + intro n + have (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by + induction n with + | zero => simpa + | succ m ih => + simp only [drop] at hb ⊢ + generalize b.drop m = tb at * + cases tb with + | nil => simp at hb + | cons tb_hd tb_tl => + simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih + obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) + simpa [ha] + contrapose + rw [head_eq_none_iff] + generalize b.drop n = tb at * + cases tb + · simp + · intro hb + obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) + simp [ha] + +@[simp] +theorem cons_AtLeastAsLongAs_cons_iff {a_hd : α} {a_tl : Seq α} {b_hd : β} + {b_tl : Seq β} : + (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl where + mp h := by + simp [AtLeastAsLongAs] at * + intro n + simpa using h (n + 1) + mpr := AtLeastAsLongAs.cons + +theorem map_AtLeastAsLongAs_self {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} + {b : Seq β} (h : a.AtLeastAsLongAs b) : + a.AtLeastAsLongAs (b.map f) := by + simp only [AtLeastAsLongAs, terminatedAt_map_iff] at * + intro n ha + simpa [TerminatedAt] using h n ha + +end AtLeastAsLongAs + +instance : Functor Seq where map := @map + +instance : LawfulFunctor Seq where + id_map := @map_id + comp_map := @map_comp + map_const := rfl + +end Seq + +namespace Seq1 + +variable {α : Type u} {β : Type v} {γ : Type w} + +open Stream'.Seq + +/-- Convert a `Seq1` to a sequence. -/ +def toSeq : Seq1 α → Seq α + | (a, s) => Seq.cons a s + +instance coeSeq : Coe (Seq1 α) (Seq α) := + ⟨toSeq⟩ + +/-- Map a function on a `Seq1` -/ +def map (f : α → β) : Seq1 α → Seq1 β + | (a, s) => (f a, Seq.map f s) + +theorem map_pair {f : α → β} {a s} : map f (a, s) = (f a, Seq.map f s) := rfl + +theorem map_id : ∀ s : Seq1 α, map id s = s + | ⟨a, s⟩ => by simp [map] + +/-- Flatten a nonempty sequence of nonempty sequences -/ +def join : Seq1 (Seq1 α) → Seq1 α + | ((a, s), S) => + match destruct s with + | none => (a, Seq.join S) + | some s' => (a, Seq.join (Seq.cons s' S)) + +@[simp] +theorem join_nil (a : α) (S) : join ((a, nil), S) = (a, Seq.join S) := + rfl + +@[simp] +theorem join_cons (a b : α) (s S) : + join ((a, Seq.cons b s), S) = (a, Seq.join (Seq.cons (b, s) S)) := by + dsimp [join]; rw [destruct_cons] + +/-- The `return` operator for the `Seq1` monad, + which produces a singleton sequence. -/ +def ret (a : α) : Seq1 α := + (a, nil) + +instance [Inhabited α] : Inhabited (Seq1 α) := + ⟨ret default⟩ + +/-- The `bind` operator for the `Seq1` monad, + which maps `f` on each element of `s` and appends the results together. + (Not all of `s` may be evaluated, because the first few elements of `s` + may already produce an infinite result.) -/ +def bind (s : Seq1 α) (f : α → Seq1 β) : Seq1 β := + join (map f s) + +@[simp] +theorem join_map_ret (s : Seq α) : Seq.join (Seq.map ret s) = s := by + apply coinduction2 s; intro s; cases s <;> simp [ret] + +@[simp] +theorem bind_ret (f : α → β) : ∀ s, bind s (ret ∘ f) = map f s + | ⟨a, s⟩ => by simp [bind, map, map_comp, ret] + +@[simp] +theorem ret_bind (a : α) (f : α → Seq1 β) : bind (ret a) f = f a := by + simp only [bind, map, ret.eq_1, map_nil] + obtain ⟨a, s⟩ := f a + cases s <;> simp + +@[simp] +theorem map_join' (f : α → β) (S) : Seq.map f (Seq.join S) = Seq.join (Seq.map (map f) S) := by + apply + Seq.eq_of_bisim fun s1 s2 => + ∃ s S, + s1 = Seq.append s (Seq.map f (Seq.join S)) ∧ s2 = append s (Seq.join (Seq.map (map f) S)) + · intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, S, rfl, rfl⟩ => by + cases s <;> simp + case nil => + cases S <;> simp + case cons x S => + obtain ⟨a, s⟩ := x + simpa [map] using ⟨_, _, rfl, rfl⟩ + case cons _ s => exact ⟨s, S, rfl, rfl⟩ + · refine ⟨nil, S, ?_, ?_⟩ <;> simp + +@[simp] +theorem map_join (f : α → β) : ∀ S, map f (join S) = join (map (map f) S) + | ((a, s), S) => by cases s <;> simp [map] + +@[simp] +theorem join_join (SS : Seq (Seq1 (Seq1 α))) : + Seq.join (Seq.join SS) = Seq.join (Seq.map join SS) := by + apply + Seq.eq_of_bisim fun s1 s2 => + ∃ s SS, + s1 = Seq.append s (Seq.join (Seq.join SS)) ∧ s2 = Seq.append s (Seq.join (Seq.map join SS)) + · intro s1 s2 h + exact + match s1, s2, h with + | _, _, ⟨s, SS, rfl, rfl⟩ => by + cases s <;> simp + case nil => + cases SS <;> simp + case cons S SS => + obtain ⟨s, S⟩ := S; obtain ⟨x, s⟩ := s + simp only [Seq.join_cons, join_append, destruct_cons] + cases s <;> simp + case nil => exact ⟨_, _, rfl, rfl⟩ + case cons x s => refine ⟨Seq.cons x (append s (Seq.join S)), SS, ?_, ?_⟩ <;> simp + case cons _ s => exact ⟨s, SS, rfl, rfl⟩ + · refine ⟨nil, SS, ?_, ?_⟩ <;> simp + +@[simp] +theorem bind_assoc (s : Seq1 α) (f : α → Seq1 β) (g : β → Seq1 γ) : + bind (bind s f) g = bind s fun x : α => bind (f x) g := by + obtain ⟨a, s⟩ := s + simp only [bind, map_pair, map_join] + rw [← map_comp] + simp only [show (fun x => join (map g (f x))) = join ∘ (map g ∘ f) from rfl] + rw [map_comp _ join] + generalize Seq.map (map g ∘ f) s = SS + rcases map g (f a) with ⟨⟨a, s⟩, S⟩ + induction' s using recOn with x s_1 <;> induction' S using recOn with x_1 s_2 <;> simp + · obtain ⟨x, t⟩ := x_1 + cases t <;> simp + · obtain ⟨y, t⟩ := x_1; simp + +instance monad : Monad Seq1 where + map := @map + pure := @ret + bind := @bind + +instance lawfulMonad : LawfulMonad Seq1 := LawfulMonad.mk' + (id_map := @map_id) + (bind_pure_comp := @bind_ret) + (pure_bind := @ret_bind) + (bind_assoc := @bind_assoc) + +end Seq1 + +end Stream' + +set_option linter.style.longFile 1900 diff --git a/Mathlib/Data/Seq/Seq.lean b/Mathlib/Data/Seq/Seq.lean index 084bc80785dab3..ca23e2dcce198f 100644 --- a/Mathlib/Data/Seq/Seq.lean +++ b/Mathlib/Data/Seq/Seq.lean @@ -1,1794 +1,3 @@ -/- -Copyright (c) 2017 Mario Carneiro. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Mario Carneiro --/ -import Mathlib.Data.Option.NAry -import Mathlib.Data.Seq.Computation +import Mathlib.Data.Seq.Basic -/-! -# Possibly infinite lists - -This file provides a `Seq α` type representing possibly infinite lists (referred here as sequences). - It is encoded as an infinite stream of options such that if `f n = none`, then - `f m = none` for all `m ≥ n`. --/ - -namespace Stream' - -universe u v w - -/- -coinductive seq (α : Type u) : Type u -| nil : seq α -| cons : α → seq α → seq α --/ -/-- A stream `s : Option α` is a sequence if `s.get n = none` implies `s.get (n + 1) = none`. --/ -def IsSeq {α : Type u} (s : Stream' (Option α)) : Prop := - ∀ {n : ℕ}, s n = none → s (n + 1) = none - -/-- `Seq α` is the type of possibly infinite lists (referred here as sequences). - It is encoded as an infinite stream of options such that if `f n = none`, then - `f m = none` for all `m ≥ n`. -/ -def Seq (α : Type u) : Type u := - { f : Stream' (Option α) // f.IsSeq } - -/-- `Seq1 α` is the type of nonempty sequences. -/ -def Seq1 (α) := - α × Seq α - -namespace Seq - -variable {α : Type u} {β : Type v} {γ : Type w} - -/-- Get the nth element of a sequence (if it exists) -/ -def get? : Seq α → ℕ → Option α := - Subtype.val - -@[simp] -theorem val_eq_get (s : Seq α) (n : ℕ) : s.val n = s.get? n := - rfl - -@[simp] -theorem get?_mk (f hf) : @get? α ⟨f, hf⟩ = f := - rfl - -theorem le_stable (s : Seq α) {m n} (h : m ≤ n) : s.get? m = none → s.get? n = none := by - obtain ⟨f, al⟩ := s - induction' h with n _ IH - exacts [id, fun h2 => al (IH h2)] - -/-- If `s.get? n = some aₙ` for some value `aₙ`, then there is also some value `aₘ` such -that `s.get? = some aₘ` for `m ≤ n`. --/ -theorem ge_stable (s : Seq α) {aₙ : α} {n m : ℕ} (m_le_n : m ≤ n) - (s_nth_eq_some : s.get? n = some aₙ) : ∃ aₘ : α, s.get? m = some aₘ := - have : s.get? n ≠ none := by simp [s_nth_eq_some] - have : s.get? m ≠ none := mt (s.le_stable m_le_n) this - Option.ne_none_iff_exists'.mp this - -@[ext] -protected theorem ext {s t : Seq α} (h : ∀ n : ℕ, s.get? n = t.get? n) : s = t := - Subtype.eq <| funext h - -/-! -### Constructors --/ - -/-- The empty sequence -/ -def nil : Seq α := - ⟨Stream'.const none, fun {_} _ => rfl⟩ - -instance : Inhabited (Seq α) := - ⟨nil⟩ - -/-- Prepend an element to a sequence -/ -def cons (a : α) (s : Seq α) : Seq α := - ⟨some a::s.1, by - rintro (n | _) h - · contradiction - · exact s.2 h⟩ - -@[simp] -theorem val_cons (s : Seq α) (x : α) : (cons x s).val = some x::s.val := - rfl - -@[simp] -theorem get?_nil (n : ℕ) : (@nil α).get? n = none := - rfl - -@[simp] -theorem get?_zero_eq_none {s : Seq α} : s.get? 0 = none ↔ s = nil := by - refine ⟨fun h => ?_, fun h => h ▸ rfl⟩ - ext1 n - exact le_stable s (Nat.zero_le _) h - -@[simp] -theorem get?_cons_zero (a : α) (s : Seq α) : (cons a s).get? 0 = some a := - rfl - -@[simp] -theorem get?_cons_succ (a : α) (s : Seq α) (n : ℕ) : (cons a s).get? (n + 1) = s.get? n := - rfl - -@[simp] -theorem cons_ne_nil {x : α} {s : Seq α} : (cons x s) ≠ .nil := by - intro h - simpa using congrArg (·.get? 0) h - -@[simp] -theorem nil_ne_cons {x : α} {s : Seq α} : .nil ≠ (cons x s) := cons_ne_nil.symm - -theorem cons_injective2 : Function.Injective2 (cons : α → Seq α → Seq α) := fun x y s t h => - ⟨by rw [← Option.some_inj, ← get?_cons_zero, h, get?_cons_zero], - Seq.ext fun n => by simp_rw [← get?_cons_succ x s n, h, get?_cons_succ]⟩ - -theorem cons_left_injective (s : Seq α) : Function.Injective fun x => cons x s := - cons_injective2.left _ - -theorem cons_right_injective (x : α) : Function.Injective (cons x) := - cons_injective2.right _ - -theorem cons_eq_cons {x x' : α} {s s' : Seq α} : - (cons x s = cons x' s') ↔ (x = x' ∧ s = s') := by - constructor - · apply cons_injective2 - · intro ⟨_, _⟩ - congr - -/-! -### Destructors --/ - -/-- Get the first element of a sequence -/ -def head (s : Seq α) : Option α := - get? s 0 - -/-- Get the tail of a sequence (or `nil` if the sequence is `nil`) -/ -def tail (s : Seq α) : Seq α := - ⟨s.1.tail, fun n' => by - obtain ⟨f, al⟩ := s - exact al n'⟩ - -/-- Destructor for a sequence, resulting in either `none` (for `nil`) or - `some (a, s)` (for `cons a s`). -/ -def destruct (s : Seq α) : Option (Seq1 α) := - (fun a' => (a', s.tail)) <$> get? s 0 - --- Porting note: needed universe annotation to avoid universe issues -theorem head_eq_destruct (s : Seq α) : head.{u} s = Prod.fst.{u} <$> destruct.{u} s := by - unfold destruct head; cases get? s 0 <;> rfl - -@[simp] -theorem get?_tail (s : Seq α) (n) : get? (tail s) n = get? s (n + 1) := - rfl - -@[simp] -theorem destruct_nil : destruct (nil : Seq α) = none := - rfl - -@[simp] -theorem destruct_cons (a : α) : ∀ s, destruct (cons a s) = some (a, s) - | ⟨f, al⟩ => by - unfold cons destruct Functor.map - apply congr_arg fun s => some (a, s) - apply Subtype.eq; dsimp [tail] - -theorem destruct_eq_none {s : Seq α} : destruct s = none → s = nil := by - dsimp [destruct] - induction' f0 : get? s 0 <;> intro h - · apply Subtype.eq - funext n - induction' n with n IH - exacts [f0, s.2 IH] - · contradiction - -theorem destruct_eq_cons {s : Seq α} {a s'} : destruct s = some (a, s') → s = cons a s' := by - dsimp [destruct] - induction' f0 : get? s 0 with a' <;> intro h - · contradiction - · obtain ⟨f, al⟩ := s - injections _ h1 h2 - rw [← h2] - apply Subtype.eq - dsimp [tail, cons] - rw [h1] at f0 - rw [← f0] - exact (Stream'.eta f).symm - -@[simp] -theorem head_nil : head (nil : Seq α) = none := - rfl - -@[simp] -theorem head_cons (a : α) (s) : head (cons a s) = some a := by - rw [head_eq_destruct, destruct_cons, Option.map_eq_map, Option.map_some] - -@[simp] -theorem tail_nil : tail (nil : Seq α) = nil := - rfl - -@[simp] -theorem tail_cons (a : α) (s) : tail (cons a s) = s := by - obtain ⟨f, al⟩ := s - apply Subtype.eq - dsimp [tail, cons] - -theorem head_eq_some {s : Seq α} {x : α} (h : s.head = some x) : - s = cons x s.tail := by - ext1 n - cases n <;> simp only [get?_cons_zero, get?_cons_succ, get?_tail] - exact h - -theorem head_eq_none {s : Seq α} (h : s.head = none) : s = nil := - get?_zero_eq_none.mp h - -@[simp] -theorem head_eq_none_iff {s : Seq α} : s.head = none ↔ s = nil := by - constructor - · apply head_eq_none - · intro h - simp [h] - -/-! -### Recursion and corecursion principles --/ - -/-- Recursion principle for sequences, compare with `List.recOn`. -/ -@[cases_eliminator] -def recOn {motive : Seq α → Sort v} (s : Seq α) (nil : motive nil) - (cons : ∀ x s, motive (cons x s)) : - motive s := by - rcases H : destruct s with - | v - · rw [destruct_eq_none H] - apply nil - · obtain ⟨a, s'⟩ := v - rw [destruct_eq_cons H] - apply cons - -/-- Functorial action of the functor `Option (α × _)` -/ -@[simp] -def omap (f : β → γ) : Option (α × β) → Option (α × γ) - | none => none - | some (a, b) => some (a, f b) - -/-- Corecursor over pairs of `Option` values -/ -def Corec.f (f : β → Option (α × β)) : Option β → Option α × Option β - | none => (none, none) - | some b => - match f b with - | none => (none, none) - | some (a, b') => (some a, some b') - -/-- Corecursor for `Seq α` as a coinductive type. Iterates `f` to produce new elements - of the sequence until `none` is obtained. -/ -def corec (f : β → Option (α × β)) (b : β) : Seq α := by - refine ⟨Stream'.corec' (Corec.f f) (some b), fun {n} h => ?_⟩ - rw [Stream'.corec'_eq] - change Stream'.corec' (Corec.f f) (Corec.f f (some b)).2 n = none - revert h; generalize some b = o; revert o - induction' n with n IH <;> intro o - · change (Corec.f f o).1 = none → (Corec.f f (Corec.f f o).2).1 = none - rcases o with - | b <;> intro h - · rfl - dsimp [Corec.f] at h - dsimp [Corec.f] - revert h; rcases h₁ : f b with - | s <;> intro h - · rfl - · obtain ⟨a, b'⟩ := s - contradiction - · rw [Stream'.corec'_eq (Corec.f f) (Corec.f f o).2, Stream'.corec'_eq (Corec.f f) o] - exact IH (Corec.f f o).2 - -@[simp] -theorem corec_eq (f : β → Option (α × β)) (b : β) : - destruct (corec f b) = omap (corec f) (f b) := by - dsimp [corec, destruct, get] - rw [show Stream'.corec' (Corec.f f) (some b) 0 = (Corec.f f (some b)).1 from rfl] - dsimp [Corec.f] - induction' h : f b with s; · rfl - obtain ⟨a, b'⟩ := s; dsimp [Corec.f] - apply congr_arg fun b' => some (a, b') - apply Subtype.eq - dsimp [corec, tail] - rw [Stream'.corec'_eq, Stream'.tail_cons] - dsimp [Corec.f]; rw [h] - -theorem corec_nil (f : β → Option (α × β)) (b : β) - (h : f b = .none) : corec f b = nil := by - apply destruct_eq_none - simp [h] - -theorem corec_cons {f : β → Option (α × β)} {b : β} {x : α} {s : β} - (h : f b = .some (x, s)) : corec f b = cons x (corec f s) := by - apply destruct_eq_cons - simp [h] - -/-! -### Bisimulation --/ - -section Bisim - -variable (R : Seq α → Seq α → Prop) - -local infixl:50 " ~ " => R - -/-- Bisimilarity relation over `Option` of `Seq1 α` -/ -def BisimO : Option (Seq1 α) → Option (Seq1 α) → Prop - | none, none => True - | some (a, s), some (a', s') => a = a' ∧ R s s' - | _, _ => False - -attribute [simp] BisimO -attribute [nolint simpNF] BisimO.eq_3 - -/-- a relation is bisimilar if it meets the `BisimO` test -/ -def IsBisimulation := - ∀ ⦃s₁ s₂⦄, s₁ ~ s₂ → BisimO R (destruct s₁) (destruct s₂) - -/-- If two streams are bisimilar, then they are equal. -/ -theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by - apply Subtype.eq - apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s' - · dsimp [Stream'.IsBisimulation] - intro t₁ t₂ e - exact - match t₁, t₂, e with - | _, _, ⟨s, s', rfl, rfl, r⟩ => by - suffices head s = head s' ∧ R (tail s) (tail s') from - And.imp id (fun r => ⟨tail s, tail s', by cases s using Subtype.recOn; rfl, - by cases s' using Subtype.recOn; rfl, r⟩) this - have := bisim r; revert r this - cases s <;> cases s' - · intro r _ - constructor - · rfl - · assumption - · intro _ this - rw [destruct_nil, destruct_cons] at this - exact False.elim this - · intro _ this - rw [destruct_nil, destruct_cons] at this - exact False.elim this - · simp - · exact ⟨s₁, s₂, rfl, rfl, r⟩ - -/-- Version of `eq_of_bisim` that looks more like an induction principle. -/ -theorem eq_of_bisim' {s₁ s₂ : Seq α} - (motive : Seq α → Seq α → Prop) - (h_base : motive s₁ s₂) - (h_step : ∀ s₁ s₂, motive s₁ s₂ → - (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ - (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by - apply eq_of_bisim motive _ h_base - intro s₁ s₂ h - rcases h_step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) - · simpa [h₁, h₂] - · simp [h_nil₁, h_nil₂] - -/-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` -instead of `s₁ = nil ∧ s₂ = nil` in `h_step`. -/ -theorem eq_of_bisim_strong {s₁ s₂ : Seq α} - (motive : Seq α → Seq α → Prop) - (h_base : motive s₁ s₂) - (h_step : ∀ s₁ s₂, motive s₁ s₂ → - (s₁ = s₂) ∨ - (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))) : s₁ = s₂ := by - let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ - apply eq_of_bisim' motive' (by grind) - intro s₁ s₂ ih - simp only [motive'] at ih ⊢ - rcases ih with (rfl | ih) - · cases s₁ <;> grind - rcases h_step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) - · cases s₁ <;> grind - · grind - -end Bisim - -theorem coinduction : - ∀ {s₁ s₂ : Seq α}, - head s₁ = head s₂ → - (∀ (β : Type u) (fr : Seq α → β), fr s₁ = fr s₂ → fr (tail s₁) = fr (tail s₂)) → s₁ = s₂ - | _, _, hh, ht => - Subtype.eq (Stream'.coinduction hh fun β fr => ht β fun s => fr s.1) - -theorem coinduction2 (s) (f g : Seq α → Seq β) - (H : - ∀ s, - BisimO (fun s1 s2 : Seq β => ∃ s : Seq α, s1 = f s ∧ s2 = g s) (destruct (f s)) - (destruct (g s))) : - f s = g s := by - refine eq_of_bisim (fun s1 s2 => ∃ s, s1 = f s ∧ s2 = g s) ?_ ⟨s, rfl, rfl⟩ - intro s1 s2 h; rcases h with ⟨s, h1, h2⟩ - rw [h1, h2]; apply H - -/-! -### Termination --/ - -/-- A sequence has terminated at position `n` if the value at position `n` equals `none`. -/ -def TerminatedAt (s : Seq α) (n : ℕ) : Prop := - s.get? n = none - -/-- It is decidable whether a sequence terminates at a given position. -/ -instance terminatedAtDecidable (s : Seq α) (n : ℕ) : Decidable (s.TerminatedAt n) := - decidable_of_iff' (s.get? n).isNone <| by unfold TerminatedAt; cases s.get? n <;> simp - -/-- A sequence terminates if there is some position `n` at which it has terminated. -/ -def Terminates (s : Seq α) : Prop := - ∃ n : ℕ, s.TerminatedAt n - -/-- The length of a terminating sequence. -/ -def length (s : Seq α) (h : s.Terminates) : ℕ := - Nat.find h - -/-- If a sequence terminated at position `n`, it also terminated at `m ≥ n`. -/ -theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.TerminatedAt m → s.TerminatedAt n := - le_stable - -theorem not_terminates_iff {s : Seq α} : ¬s.Terminates ↔ ∀ n, (s.get? n).isSome := by - simp only [Terminates, TerminatedAt, ← Ne.eq_def, Option.ne_none_iff_isSome, not_exists, iff_self] - -theorem terminatedAt_nil {n : ℕ} : TerminatedAt (nil : Seq α) n := rfl - -@[simp] -theorem cons_not_terminatedAt_zero {x : α} {s : Seq α} : - ¬(cons x s).TerminatedAt 0 := by - simp [TerminatedAt] - -@[simp] -theorem cons_terminatedAt_succ_iff {x : α} {s : Seq α} {n : ℕ} : - (cons x s).TerminatedAt (n + 1) ↔ s.TerminatedAt n := by - simp [TerminatedAt] - -@[simp] -theorem terminates_nil : Terminates (nil : Seq α) := ⟨0, rfl⟩ - -@[simp] -theorem terminates_cons_iff {x : α} {s : Seq α} : - (cons x s).Terminates ↔ s.Terminates := by - constructor <;> intro ⟨n, h⟩ - · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ - · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ - -@[simp] -theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl - -@[simp] theorem length_eq_zero {s : Seq α} {h : s.Terminates} : - s.length h = 0 ↔ s = nil := by - simp [length, TerminatedAt] - -theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by - refine ⟨?_, ?_⟩ - · intro h - ext n - rw [le_stable _ (Nat.zero_le _) h] - simp - · rintro rfl - simp [TerminatedAt] - -/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ -theorem length_le_iff' {s : Seq α} {n : ℕ} : - (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by - simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] - refine ⟨?_, ?_⟩ - · rintro ⟨_, k, hkn, hk⟩ - exact le_stable s hkn hk - · intro hn - exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - s.length h ≤ n ↔ s.TerminatedAt n := by - rw [← length_le_iff']; simp [h] - -/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ -theorem lt_length_iff' {s : Seq α} {n : ℕ} : - (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by - simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, - ← Option.ne_none_iff_exists', ne_eq] - refine ⟨?_, ?_⟩ - · intro h hn - exact h n hn n le_rfl hn - · intro hn _ _ k hkn hk - exact hn <| le_stable s hkn hk - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - n < s.length h ↔ ∃ a, a ∈ s.get? n := by - rw [← lt_length_iff']; simp [h] - -/-! -### Membership --/ - -/-- member definition for `Seq` -/ -protected def Mem (s : Seq α) (a : α) := - some a ∈ s.1 - -instance : Membership α (Seq α) := - ⟨Seq.Mem⟩ - --- Cannot be @[simp] because `n` can not be inferred by `simp`. -theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ - -theorem mem_iff_exists_get? {s : Seq α} {x : α} : x ∈ s ↔ ∃ i, some x = s.get? i where - mp h := by - change (some x ∈ s.1) at h - rwa [Stream'.mem_iff_exists_get_eq] at h - mpr h := get?_mem h.choose_spec.symm - -@[simp] -theorem notMem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h - -@[deprecated (since := "2025-05-23")] alias not_mem_nil := notMem_nil - -theorem mem_cons (a : α) : ∀ s : Seq α, a ∈ cons a s - | ⟨_, _⟩ => Stream'.mem_cons (some a) _ - -theorem mem_cons_of_mem (y : α) {a : α} : ∀ {s : Seq α}, a ∈ s → a ∈ cons y s - | ⟨_, _⟩ => Stream'.mem_cons_of_mem (some y) - -theorem eq_or_mem_of_mem_cons {a b : α} : ∀ {s : Seq α}, a ∈ cons b s → a = b ∨ a ∈ s - | ⟨_, _⟩, h => (Stream'.eq_or_mem_of_mem_cons h).imp_left fun h => by injection h - -@[simp] -theorem mem_cons_iff {a b : α} {s : Seq α} : a ∈ cons b s ↔ a = b ∨ a ∈ s := - ⟨eq_or_mem_of_mem_cons, by rintro (rfl | m) <;> [apply mem_cons; exact mem_cons_of_mem _ m]⟩ - -theorem mem_rec_on {C : Seq α → Prop} {a s} (M : a ∈ s) - (h1 : ∀ b s', a = b ∨ C s' → C (cons b s')) : C s := by - obtain ⟨k, e⟩ := M; unfold Stream'.get at e - induction' k with k IH generalizing s - · have TH : s = cons a (tail s) := by - apply destruct_eq_cons - unfold destruct get? Functor.map - rw [← e] - rfl - rw [TH] - apply h1 _ _ (Or.inl rfl) - cases s with - | nil => injection e - | cons b s' => - have h_eq : (cons b s').val (Nat.succ k) = s'.val k := by cases s' using Subtype.recOn; rfl - rw [h_eq] at e - apply h1 _ _ (Or.inr (IH e)) - -/-! -### Converting from/to other types --/ - -/-- Embed a list as a sequence -/ -@[coe] -def ofList (l : List α) : Seq α := - ⟨(l[·]?), fun {n} h => by - rw [List.getElem?_eq_none_iff] at h ⊢ - exact Nat.le_succ_of_le h⟩ - -instance coeList : Coe (List α) (Seq α) := - ⟨ofList⟩ - -@[simp] -theorem ofList_nil : ofList [] = (nil : Seq α) := - rfl - -@[simp] -theorem ofList_get? (l : List α) (n : ℕ) : (ofList l).get? n = l[n]? := - rfl - -@[deprecated (since := "2025-02-21")] -alias ofList_get := ofList_get? - -@[simp] -theorem ofList_cons (a : α) (l : List α) : ofList (a::l) = cons a (ofList l) := by - ext1 (_ | n) <;> simp - -theorem ofList_injective : Function.Injective (ofList : List α → _) := - fun _ _ h => List.ext_getElem? fun _ => congr_fun (Subtype.ext_iff.1 h) _ - -/-- Embed an infinite stream as a sequence -/ -@[coe] -def ofStream (s : Stream' α) : Seq α := - ⟨s.map some, fun {n} h => by contradiction⟩ - -instance coeStream : Coe (Stream' α) (Seq α) := - ⟨ofStream⟩ - -section MLList - -/-- Embed a `MLList α` as a sequence. Note that even though this - is non-meta, it will produce infinite sequences if used with - cyclic `MLList`s created by meta constructions. -/ -def ofMLList : MLList Id α → Seq α := - corec fun l => - match l.uncons with - | .none => none - | .some (a, l') => some (a, l') - -instance coeMLList : Coe (MLList Id α) (Seq α) := - ⟨ofMLList⟩ - -/-- Translate a sequence into a `MLList`. -/ -unsafe def toMLList : Seq α → MLList Id α - | s => - match destruct s with - | none => .nil - | some (a, s') => .cons a (toMLList s') - -end MLList - -/-- Translate a sequence to a list. This function will run forever if - run on an infinite sequence. -/ -unsafe def forceToList (s : Seq α) : List α := - (toMLList s).force - -/-- Take the first `n` elements of the sequence (producing a list) -/ -def take : ℕ → Seq α → List α - | 0, _ => [] - | n + 1, s => - match destruct s with - | none => [] - | some (x, r) => List.cons x (take n r) - -/-- Convert a sequence which is known to terminate into a list -/ -def toList (s : Seq α) (h : s.Terminates) : List α := - take (length s h) s - -/-- Convert a sequence which is known not to terminate into a stream -/ -def toStream (s : Seq α) (h : ¬s.Terminates) : Stream' α := fun n => - Option.get _ <| not_terminates_iff.1 h n - -/-- Convert a sequence into either a list or a stream depending on whether - it is finite or infinite. (Without decidability of the infiniteness predicate, - this is not constructively possible.) -/ -def toListOrStream (s : Seq α) [Decidable s.Terminates] : List α ⊕ Stream' α := - if h : s.Terminates then Sum.inl (toList s h) else Sum.inr (toStream s h) - -/-- Convert a sequence into a list, embedded in a computation to allow for - the possibility of infinite sequences (in which case the computation - never returns anything). -/ -def toList' {α} (s : Seq α) : Computation (List α) := - @Computation.corec (List α) (List α × Seq α) - (fun ⟨l, s⟩ => - match destruct s with - | none => Sum.inl l.reverse - | some (a, s') => Sum.inr (a::l, s')) - ([], s) - -/-! -### Operations on sequences --/ - -/-- Append two sequences. If `s₁` is infinite, then `s₁ ++ s₂ = s₁`, - otherwise it puts `s₂` at the location of the `nil` in `s₁`. -/ -def append (s₁ s₂ : Seq α) : Seq α := - @corec α (Seq α × Seq α) - (fun ⟨s₁, s₂⟩ => - match destruct s₁ with - | none => omap (fun s₂ => (nil, s₂)) (destruct s₂) - | some (a, s₁') => some (a, s₁', s₂)) - (s₁, s₂) - -/-- Map a function over a sequence. -/ -def map (f : α → β) : Seq α → Seq β - | ⟨s, al⟩ => - ⟨s.map (Option.map f), fun {n} => by - dsimp [Stream'.map, Stream'.get] - induction' e : s n with e <;> intro - · rw [al e] - assumption - · contradiction⟩ - -/-- Flatten a sequence of sequences. (It is required that the - sequences be nonempty to ensure productivity; in the case - of an infinite sequence of `nil`, the first element is never - generated.) -/ -def join : Seq (Seq1 α) → Seq α := - corec fun S => - match destruct S with - | none => none - | some ((a, s), S') => - some - (a, - match destruct s with - | none => S' - | some s' => cons s' S') - -/-- Remove the first `n` elements from the sequence. -/ -def drop (s : Seq α) : ℕ → Seq α - | 0 => s - | n + 1 => tail (drop s n) - -/-- Split a sequence at `n`, producing a finite initial segment - and an infinite tail. -/ -def splitAt : ℕ → Seq α → List α × Seq α - | 0, s => ([], s) - | n + 1, s => - match destruct s with - | none => ([], nil) - | some (x, s') => - let (l, r) := splitAt n s' - (List.cons x l, r) - -/-- Combine two sequences with a function -/ -def zipWith (f : α → β → γ) (s₁ : Seq α) (s₂ : Seq β) : Seq γ := - ⟨fun n => Option.map₂ f (s₁.get? n) (s₂.get? n), fun {_} hn => - Option.map₂_eq_none_iff.2 <| (Option.map₂_eq_none_iff.1 hn).imp s₁.2 s₂.2⟩ - -/-- Pair two sequences into a sequence of pairs -/ -def zip : Seq α → Seq β → Seq (α × β) := - zipWith Prod.mk - -/-- Separate a sequence of pairs into two sequences -/ -def unzip (s : Seq (α × β)) : Seq α × Seq β := - (map Prod.fst s, map Prod.snd s) - -/-- The sequence of natural numbers some 0, some 1, ... -/ -def nats : Seq ℕ := - Stream'.nats - -/-- Enumerate a sequence by tagging each element with its index. -/ -def enum (s : Seq α) : Seq (ℕ × α) := - Seq.zip nats s - -/-- Folds a sequence using `f`, producing a sequence of intermediate values, i.e. -`[init, f init s.head, f (f init s.head) s.tail.head, ...]`. -/ -def fold (s : Seq α) (init : β) (f : β → α → β) : Seq β := - let f : β × Seq α → Option (β × (β × Seq α)) := fun (acc, x) => - match destruct x with - | none => .none - | some (x, s) => .some (f acc x, f acc x, s) - cons init <| corec f (init, s) - -/-- Applies `f` to the `n`th element of the sequence, if it exists, replacing that element -with the result. -/ -def update (s : Seq α) (n : ℕ) (f : α → α) : Seq α where - val := Function.update s.val n ((s.val n).map f) - property := by - have (i : ℕ) : Function.update s.val n ((s.get? n).map f) i = none ↔ s.get? i = none := by - by_cases hi : i = n <;> simp [Function.update, hi] - simp only [IsSeq, val_eq_get, this] - exact @s.prop - -/-- Sets the value of sequence `s` at index `n` to `a`. If the `n`th element does not exist -(`s` terminates earlier), the sequence is left unchanged. -/ -def set (s : Seq α) (n : ℕ) (a : α) : Seq α := - update s n fun _ ↦ a - -/-! -### Predicates on sequences --/ - -/-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ -def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x - -/-- -`Pairwise R s` means that all the elements with earlier indexes are -`R`-related to all the elements with later indexes. -``` -Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 -``` -For example if `R = (· ≠ ·)` then it asserts `s` has no duplicates, -and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. --/ -def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := - ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y - -/-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. -In particular, they both may be infinite. -/ -def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := - ∀ n, a.TerminatedAt n → b.TerminatedAt n - -section OfStream - -@[simp] -theorem ofStream_cons (a : α) (s) : ofStream (a::s) = cons a (ofStream s) := by - apply Subtype.eq; simp only [ofStream, cons]; rw [Stream'.map_cons] - -end OfStream - -section OfList - -theorem terminatedAt_ofList (l : List α) : - (ofList l).TerminatedAt l.length := by - simp [ofList, TerminatedAt] - -theorem terminates_ofList (l : List α) : (ofList l).Terminates := - ⟨_, terminatedAt_ofList l⟩ - -end OfList - -section Take - -@[simp] -theorem take_nil {n : ℕ} : (nil (α := α)).take n = List.nil := by - cases n <;> rfl - -@[simp] -theorem take_zero {s : Seq α} : s.take 0 = [] := by - cases s <;> rfl - -@[simp] -theorem take_succ_cons {n : ℕ} {x : α} {s : Seq α} : - (cons x s).take (n + 1) = x :: s.take n := by - rfl - -@[simp] -theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), - (s.take k)[n]? = if n < k then s.get? n else none - | n, 0, s => by simp [take] - | n, k+1, s => by - rw [take] - cases h : destruct s with - | none => - simp [destruct_eq_none h] - | some a => - match a with - | (x, r) => - rw [destruct_eq_cons h] - match n with - | 0 => simp - | n+1 => - simp [List.getElem?_cons_succ, Nat.add_lt_add_iff_right, getElem?_take] - -theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} - (h_get : s.get? m = .some x) : x ∈ s.take n := by - induction m generalizing n s with - | zero => - obtain ⟨l, hl⟩ := Nat.exists_add_one_eq.mpr h_mn - rw [← hl, take, head_eq_some h_get] - simp - | succ k ih => - obtain ⟨l, hl⟩ := Nat.exists_eq_add_of_lt h_mn - subst hl - have : ∃ y, s.get? 0 = .some y := by - apply ge_stable _ _ h_get - simp - obtain ⟨y, hy⟩ := this - rw [take, head_eq_some hy] - simp - right - apply ih (by omega) - rwa [get?_tail] - -theorem length_take_le {s : Seq α} {n : ℕ} : (s.take n).length ≤ n := by - induction n generalizing s with - | zero => simp - | succ m ih => - rw [take] - cases s.destruct with - | none => simp - | some v => - obtain ⟨x, r⟩ := v - simpa using ih - -theorem length_take_of_le_length {s : Seq α} {n : ℕ} - (hle : ∀ h : s.Terminates, n ≤ s.length h) : (s.take n).length = n := by - induction n generalizing s with - | zero => simp [take] - | succ n ih => - rw [take, destruct] - let ⟨a, ha⟩ := lt_length_iff'.1 (fun ht => lt_of_lt_of_le (Nat.succ_pos _) (hle ht)) - simp [Option.mem_def.1 ha] - rw [ih] - intro h - simp only [length, tail, Nat.le_find_iff, TerminatedAt, get?_mk, Stream'.tail] - intro m hmn hs - have := lt_length_iff'.1 (fun ht => (Nat.lt_of_succ_le (hle ht))) - rw [le_stable s (Nat.succ_le_of_lt hmn) hs] at this - simp at this - -end Take - -section ToList - -@[simp] -theorem length_toList (s : Seq α) (h : s.Terminates) : (toList s h).length = length s h := by - rw [toList, length_take_of_le_length] - intro _ - exact le_rfl - -@[simp] -theorem getElem?_toList (s : Seq α) (h : s.Terminates) (n : ℕ) : (toList s h)[n]? = s.get? n := by - ext k - simp only [toList, getElem?_take, Nat.lt_find_iff, length, - Option.ite_none_right_eq_some, and_iff_right_iff_imp, TerminatedAt] - intro h m hmn - let ⟨a, ha⟩ := ge_stable s hmn h - simp [ha] - -@[simp] -theorem ofList_toList (s : Seq α) (h : s.Terminates) : - ofList (toList s h) = s := by - ext n; simp [ofList] - -@[simp] -theorem toList_ofList (l : List α) : toList (ofList l) (terminates_ofList l) = l := - ofList_injective (by simp) - -@[simp] -theorem toList_nil : toList (nil : Seq α) ⟨0, terminatedAt_zero_iff.2 rfl⟩ = [] := by - ext; simp [nil, toList, const] - -theorem getLast?_toList (s : Seq α) (h : s.Terminates) : - (toList s h).getLast? = s.get? (s.length h - 1) := by - rw [List.getLast?_eq_getElem?, getElem?_toList, length_toList] - -end ToList - -section Append - -@[simp] -theorem cons_append (a : α) (s t) : append (cons a s) t = cons a (append s t) := - destruct_eq_cons <| by - dsimp [append]; rw [corec_eq] - dsimp [append]; rw [destruct_cons] - -@[simp] -theorem nil_append (s : Seq α) : append nil s = s := by - apply coinduction2; intro s - dsimp [append]; rw [corec_eq] - dsimp [append] - cases s - · trivial - · rw [destruct_cons] - dsimp - exact ⟨rfl, _, rfl, rfl⟩ - -@[simp] -theorem append_nil (s : Seq α) : append s nil = s := by - apply coinduction2 s; intro s - cases s - · trivial - · rw [cons_append, destruct_cons, destruct_cons] - dsimp - exact ⟨rfl, _, rfl, rfl⟩ - -@[simp] -theorem append_assoc (s t u : Seq α) : append (append s t) u = append s (append t u) := by - apply eq_of_bisim fun s1 s2 => ∃ s t u, s1 = append (append s t) u ∧ s2 = append s (append t u) - · intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, t, u, rfl, rfl⟩ => by - cases s <;> simp - case nil => - cases t <;> simp - case nil => - cases u <;> simp - case cons _ u => refine ⟨nil, nil, u, ?_, ?_⟩ <;> simp - case cons _ t => refine ⟨nil, t, u, ?_, ?_⟩ <;> simp - case cons _ s => exact ⟨s, t, u, rfl, rfl⟩ - · exact ⟨s, t, u, rfl, rfl⟩ - -theorem of_mem_append {s₁ s₂ : Seq α} {a : α} (h : a ∈ append s₁ s₂) : a ∈ s₁ ∨ a ∈ s₂ := by - have := h; revert this - generalize e : append s₁ s₂ = ss; intro h; revert s₁ - apply mem_rec_on h _ - intro b s' o s₁ - cases s₁ with - | nil => - intro m _ - apply Or.inr - simpa using m - | cons c t₁ => - intro m e - have this := congr_arg destruct e - rcases show a = c ∨ a ∈ append t₁ s₂ by simpa using m with e' | m - · rw [e'] - exact Or.inl (mem_cons _ _) - · obtain ⟨i1, i2⟩ := show c = b ∧ append t₁ s₂ = s' by simpa - rcases o with e' | IH - · simp [i1, e'] - · exact Or.imp_left (mem_cons_of_mem _) (IH m i2) - -theorem mem_append_left {s₁ s₂ : Seq α} {a : α} (h : a ∈ s₁) : a ∈ append s₁ s₂ := by - apply mem_rec_on h; intros; simp [*] - -@[simp] -theorem ofList_append (l l' : List α) : ofList (l ++ l') = append (ofList l) (ofList l') := by - induction l <;> simp [*] - -@[simp] -theorem ofStream_append (l : List α) (s : Stream' α) : - ofStream (l ++ₛ s) = append (ofList l) (ofStream s) := by - induction l <;> simp [*, Stream'.nil_append_stream, Stream'.cons_append_stream] - -end Append - -section Map - -@[simp] -theorem map_get? (f : α → β) : ∀ s n, get? (map f s) n = (get? s n).map f - | ⟨_, _⟩, _ => rfl - -@[simp] -theorem map_nil (f : α → β) : map f nil = nil := - rfl - -@[simp] -theorem map_cons (f : α → β) (a) : ∀ s, map f (cons a s) = cons (f a) (map f s) - | ⟨s, al⟩ => by apply Subtype.eq; dsimp [cons, map]; rw [Stream'.map_cons]; rfl - -@[simp] -theorem map_id : ∀ s : Seq α, map id s = s - | ⟨s, al⟩ => by - apply Subtype.eq; dsimp [map] - rw [Option.map_id, Stream'.map_id] - -@[simp] -theorem map_tail (f : α → β) : ∀ s, map f (tail s) = tail (map f s) - | ⟨s, al⟩ => by apply Subtype.eq; dsimp [tail, map] - -theorem map_comp (f : α → β) (g : β → γ) : ∀ s : Seq α, map (g ∘ f) s = map g (map f s) - | ⟨s, al⟩ => by - apply Subtype.eq; dsimp [map] - apply congr_arg fun f : _ → Option γ => Stream'.map f s - ext ⟨⟩ <;> rfl - -@[simp] -theorem terminatedAt_map_iff {f : α → β} {s : Seq α} {n : ℕ} : - (map f s).TerminatedAt n ↔ s.TerminatedAt n := by - simp [TerminatedAt] - -@[simp] -theorem terminates_map_iff {f : α → β} {s : Seq α} : - (map f s).Terminates ↔ s.Terminates := by - simp [Terminates] - -@[simp] -theorem length_map {s : Seq α} {f : α → β} (h : (s.map f).Terminates) : - (s.map f).length h = s.length (terminates_map_iff.1 h) := by - rw [length] - congr - ext - simp - -theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s - | ⟨_, _⟩ => Stream'.mem_map (Option.map f) - -theorem exists_of_mem_map {f} {b : β} : ∀ {s : Seq α}, b ∈ map f s → ∃ a, a ∈ s ∧ f a = b := - fun {s} h => by match s with - | ⟨g, al⟩ => - let ⟨o, om, oe⟩ := @Stream'.exists_of_mem_map _ _ (Option.map f) (some b) g h - rcases o with - | a - · injection oe - · injection oe with h'; exact ⟨a, om, h'⟩ - -@[simp] -theorem map_append (f : α → β) (s t) : map f (append s t) = append (map f s) (map f t) := by - apply - eq_of_bisim (fun s1 s2 => ∃ s t, s1 = map f (append s t) ∧ s2 = append (map f s) (map f t)) _ - ⟨s, t, rfl, rfl⟩ - intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, t, rfl, rfl⟩ => by - cases s <;> simp - case nil => - cases t <;> simp - case cons _ t => refine ⟨nil, t, ?_, ?_⟩ <;> simp - case cons _ s => exact ⟨s, t, rfl, rfl⟩ - -end Map - -section Join - - -@[simp] -theorem join_nil : join nil = (nil : Seq α) := - destruct_eq_none rfl - --- Not a simp lemmas as `join_cons` is more general -theorem join_cons_nil (a : α) (S) : join (cons (a, nil) S) = cons a (join S) := - destruct_eq_cons <| by simp [join] - --- Not a simp lemmas as `join_cons` is more general -theorem join_cons_cons (a b : α) (s S) : - join (cons (a, cons b s) S) = cons a (join (cons (b, s) S)) := - destruct_eq_cons <| by simp [join] - -@[simp] -theorem join_cons (a : α) (s S) : join (cons (a, s) S) = cons a (append s (join S)) := by - apply - eq_of_bisim - (fun s1 s2 => s1 = s2 ∨ ∃ a s S, s1 = join (cons (a, s) S) ∧ s2 = cons a (append s (join S))) - _ (Or.inr ⟨a, s, S, rfl, rfl⟩) - intro s1 s2 h - exact - match s1, s2, h with - | s, _, Or.inl <| Eq.refl s => by - cases s; · trivial - · rw [destruct_cons] - exact ⟨rfl, Or.inl rfl⟩ - | _, _, Or.inr ⟨a, s, S, rfl, rfl⟩ => by - cases s - · simp [join_cons_nil] - · simpa [join_cons_cons, join_cons_nil] using Or.inr ⟨_, _, S, rfl, rfl⟩ - -@[simp] -theorem join_append (S T : Seq (Seq1 α)) : join (append S T) = append (join S) (join T) := by - apply - eq_of_bisim fun s1 s2 => - ∃ s S T, s1 = append s (join (append S T)) ∧ s2 = append s (append (join S) (join T)) - · intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, S, T, rfl, rfl⟩ => by - cases s <;> simp - case nil => - cases S <;> simp - case nil => - cases T with - | nil => simp - | cons s T => - obtain ⟨a, s⟩ := s; simp only [join_cons, destruct_cons, true_and] - refine ⟨s, nil, T, ?_, ?_⟩ <;> simp - case cons s S => - obtain ⟨a, s⟩ := s - simpa using ⟨s, S, T, rfl, rfl⟩ - case cons _ s => exact ⟨s, S, T, rfl, rfl⟩ - · refine ⟨nil, S, T, ?_, ?_⟩ <;> simp - -end Join - -section Drop - -@[simp] -theorem drop_get? {n m : ℕ} {s : Seq α} : (s.drop n).get? m = s.get? (n + m) := by - induction n generalizing m with - | zero => simp [drop] - | succ k ih => - simp [Seq.get?_tail, drop] - convert ih using 2 - omega - -theorem dropn_add (s : Seq α) (m) : ∀ n, drop s (m + n) = drop (drop s m) n - | 0 => rfl - | n + 1 => congr_arg tail (dropn_add s _ n) - -theorem dropn_tail (s : Seq α) (n) : drop (tail s) n = drop s (n + 1) := by - rw [Nat.add_comm]; symm; apply dropn_add - -@[simp] -theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by - induction' n with n IH generalizing s; · rfl - rw [← get?_tail, ← dropn_tail]; apply IH - -@[simp] -theorem drop_zero {s : Seq α} : s.drop 0 = s := rfl - -@[simp] -theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} : - (cons x s).drop (n + 1) = s.drop n := by - simp [← dropn_tail] - -@[simp] -theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by - induction n with - | zero => simp [drop] - | succ m ih => simp [← dropn_tail, ih] - -theorem take_drop {s : Seq α} {n m : ℕ} : - (s.take n).drop m = (s.drop m).take (n - m) := by - induction m generalizing n s with - | zero => simp [drop] - | succ k ih => - cases s - · simp - cases n with - | zero => simp - | succ l => - simp only [take, destruct_cons, List.drop_succ_cons, Nat.reduceSubDiff] - rw [ih] - congr 1 - rw [drop_succ_cons] - -end Drop - -section ZipWith - -@[simp] -theorem get?_zipWith (f : α → β → γ) (s s' n) : - (zipWith f s s').get? n = Option.map₂ f (s.get? n) (s'.get? n) := - rfl - -@[simp] -theorem get?_zip (s : Seq α) (t : Seq β) (n : ℕ) : - get? (zip s t) n = Option.map₂ Prod.mk (get? s n) (get? t n) := - get?_zipWith _ _ _ _ - -@[simp] -theorem nats_get? (n : ℕ) : nats.get? n = some n := - rfl - -@[simp] -theorem get?_enum (s : Seq α) (n : ℕ) : get? (enum s) n = Option.map (Prod.mk n) (get? s n) := - get?_zip _ _ _ - -@[simp] -theorem zipWith_nil_left {f : α → β → γ} {s} : - zipWith f nil s = nil := - rfl - -@[simp] -theorem zipWith_nil_right {f : α → β → γ} {s} : - zipWith f s nil = nil := by - ext1 - simp - -@[simp] -theorem zipWith_cons_cons {f : α → β → γ} {x s x' s'} : - zipWith f (cons x s) (cons x' s') = cons (f x x') (zipWith f s s') := by - ext1 n - cases n <;> simp - -@[simp] -theorem zip_nil_left {s : Seq α} : - zip (@nil α) s = nil := - rfl - -@[simp] -theorem zip_nil_right {s : Seq α} : - zip s (@nil α) = nil := - zipWith_nil_right - -@[simp] -theorem zip_cons_cons {s s' : Seq α} {x x'} : - zip (cons x s) (cons x' s') = cons (x, x') (zip s s') := - zipWith_cons_cons - -@[simp] -theorem enum_nil : enum (nil : Seq α) = nil := - rfl - -@[simp] -theorem enum_cons (s : Seq α) (x : α) : - enum (cons x s) = cons (0, x) (map (Prod.map Nat.succ id) (enum s)) := by - ext ⟨n⟩ : 1 - · simp - · simp only [get?_enum, get?_cons_succ, map_get?, Option.map_map] - congr - -universe u' v' -variable {α' : Type u'} {β' : Type v'} - -theorem zipWith_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') (g : α' → β' → γ) : - zipWith g (s₁.map f₁) (s₂.map f₂) = zipWith (fun a b ↦ g (f₁ a) (f₂ b)) s₁ s₂ := by - ext1 n - simp only [get?_zipWith, map_get?] - cases s₁.get? n <;> cases s₂.get? n <;> simp - -theorem zipWith_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') (g : α' → β → γ) : - zipWith g (s₁.map f) s₂ = zipWith (fun a b ↦ g (f a) b) s₁ s₂ := by - convert zipWith_map _ _ _ (@id β) _ - simp - -theorem zipWith_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') (g : α → β' → γ) : - zipWith g s₁ (s₂.map f) = zipWith (fun a b ↦ g a (f b)) s₁ s₂ := by - convert zipWith_map _ _ (@id α) _ _ - simp - -theorem zip_map (s₁ : Seq α) (s₂ : Seq β) (f₁ : α → α') (f₂ : β → β') : - (s₁.map f₁).zip (s₂.map f₂) = (s₁.zip s₂).map (Prod.map f₁ f₂) := by - ext1 n - simp - cases s₁.get? n <;> cases s₂.get? n <;> simp - -theorem zip_map_left (s₁ : Seq α) (s₂ : Seq β) (f : α → α') : - (s₁.map f).zip s₂ = (s₁.zip s₂).map (Prod.map f id) := by - convert zip_map _ _ _ _ - simp - -theorem zip_map_right (s₁ : Seq α) (s₂ : Seq β) (f : β → β') : - s₁.zip (s₂.map f) = (s₁.zip s₂).map (Prod.map id f) := by - convert zip_map _ _ _ _ - simp - -end ZipWith - -section Fold - -@[simp] -theorem fold_nil (init : β) (f : β → α → β) : - nil.fold init f = cons init nil := by - unfold fold - simp [corec_nil] - -@[simp] -theorem fold_cons (init : β) (f : β → α → β) (x : α) (s : Seq α) : - (cons x s).fold init f = cons init (s.fold (f init x) f) := by - unfold fold - dsimp only - congr - rw [corec_cons] - simp - -@[simp] -theorem fold_head (init : β) (f : β → α → β) (s : Seq α) : - (s.fold init f).head = init := by - simp [fold] - -end Fold - -section Update - -variable (hd x : α) (tl : Seq α) (f : α → α) - -theorem get?_update (s : Seq α) (n : ℕ) (m : ℕ) : - (s.update n f).get? m = if m = n then (s.get? m).map f else s.get? m := by - simp [update, Function.update] - split_ifs with h_if - · simp [h_if] - · rfl - -@[simp] -theorem update_nil (n : ℕ) : update nil n f = nil := by - ext1 m - simp [get?_update] - -@[simp] -theorem set_nil (n : ℕ) (x : α) : set nil n x = nil := update_nil _ _ - -@[simp] -theorem update_cons_zero : (cons hd tl).update 0 f = cons (f hd) tl := by - ext1 n - cases n <;> simp [get?_update] - -@[simp] -theorem set_cons_zero (hd' : α) : (cons hd tl).set 0 hd' = cons hd' tl := - update_cons_zero _ _ _ - -@[simp] -theorem update_cons_succ (n : ℕ) : (cons hd tl).update (n + 1) f = cons hd (tl.update n f) := by - ext1 n - cases n <;> simp [get?_update] - -@[simp] -theorem set_cons_succ (n : ℕ) : (cons hd tl).set (n + 1) x = cons hd (tl.set n x) := - update_cons_succ _ _ _ _ - -theorem get?_set_of_not_terminatedAt {s : Seq α} {n : ℕ} (h_not_terminated : ¬ s.TerminatedAt n) : - (s.set n x).get? n = x := by - simpa [set, update, ← Option.ne_none_iff_exists'] using h_not_terminated - -theorem get?_set_of_terminatedAt {s : Seq α} {n : ℕ} (h_terminated : s.TerminatedAt n) : - (s.set n x).get? n = .none := by - simpa [set, get?_update] using h_terminated - -theorem get?_set_of_ne (s : Seq α) {m n : ℕ} (h : n ≠ m) : (s.set m x).get? n = s.get? n := by - simp [set, get?_update, h] - -theorem drop_set_of_lt (s : Seq α) {m n : ℕ} (h : m < n) : (s.set m x).drop n = s.drop n := by - ext1 i - simp [get?_set_of_ne _ _ (show n + i ≠ m by omega)] - -end Update - -section All - -@[simp] -theorem All.nil (p : α → Prop) : nil.All p := by - simp [All] - -theorem All.cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : tl.All p) : - ((cons hd tl).All p) := by - simp only [All, mem_cons_iff, forall_eq_or_imp] at * - exact ⟨h_hd, h_tl⟩ - -@[simp] -theorem All_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : - ((cons hd tl).All p) ↔ p hd ∧ tl.All p := by - simp [All] - -theorem All_get {p : α → Prop} {s : Seq α} (h : s.All p) {n : ℕ} {x : α} (hx : s.get? n = .some x) : - p x := by - unfold All at h - exact h _ (get?_mem hx) - -theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : - s.All p := by - simp only [All, mem_iff_exists_get?] - grind - -set_option linter.dupNamespace false in -private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : - motive (s.drop n) := by - induction n with - | zero => simpa - | succ m ih => - simp only [drop] - generalize s.drop m = t at * - cases t - · simpa - · exact h_step _ _ ih - -/-- Coinductive principle for `All`. -/ -theorem All.coind {s : Seq α} {p : α → Prop} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) - : s.All p := by - apply All_of_get - intro n - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) n - rw [← head_dropn] - generalize s.drop n = s' at this - cases s' with - | nil => simp - | cons hd tl => simp [(h_step hd tl this).left] - -theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : - s.All q := by - simp only [All] at * - grind - -theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : - (s.map f).All p ↔ s.All (p ∘ f) := by - simp [All] - refine ⟨fun _ _ hx ↦ ?_, fun _ _ hx ↦ ?_⟩ - · solve_by_elim [mem_map f hx] - · obtain ⟨_, _, hx'⟩ := exists_of_mem_map hx - rw [← hx'] - solve_by_elim - -theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} {x : α} (hx : x ∈ s.take n) : - p x := by - induction n generalizing s with - | zero => simp [take] at hx - | succ m ih => - cases s with - | nil => simp at hx - | cons hd tl => - simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all - rcases hx with (rfl | hx) - exacts [h_all.left, ih h_all.right hx] - -theorem set_All {p : α → Prop} {s : Seq α} (h_all : s.All p) {n : ℕ} {x : α} - (hx : p x) : (s.set n x).All p := by - apply All_of_get - intro m - rcases eq_or_ne n m with (rfl | h_nm) - · by_cases h_term : s.TerminatedAt n - · simp [get?_set_of_terminatedAt _ h_term] - · simpa [get?_set_of_not_terminatedAt _ h_term] - · rw [get?_set_of_ne] - exacts [fun x hx ↦ All_get h_all hx, h_nm.symm] - -end All - -section Pairwise - -@[simp] -theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by - simp [Pairwise] - -theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} - (h_lt : tl.All (R hd ·)) - (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by - simp only [Pairwise] at * - intro i j x y h_ij hx hy - cases j with - | zero => simp at h_ij - | succ k => - simp only [get?_cons_succ] at hy - cases i with - | zero => - simp only [get?_cons_zero, Option.some.injEq] at hx - exact hx ▸ All_get h_lt hy - | succ n => exact h_tl n k x y (by omega) hx hy - -theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} - (h : Pairwise R (.cons hd tl)) : tl.All (R hd ·) ∧ Pairwise R tl := by - simp only [Pairwise] at * - refine ⟨?_, fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy⟩ - apply All_of_get - intro n - specialize h 0 (n + 1) hd - simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h - cases h_tl : tl.get? n with - | none => simp - | some y => simp [h y h_tl] - -@[simp] -theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by - apply Pairwise.cons <;> simp - -theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl : Seq α} - (h : Pairwise R (cons hd (cons tl_hd tl_tl))) : - R hd tl_hd := by - simp only [Pairwise] at h - simpa using h 0 1 hd tl_hd Nat.one_pos - -theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α} - (h_hd : R hd tl_hd) - (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by - apply Pairwise.cons _ h_tl - rw [All_cons_iff] - exact ⟨h_hd, All_mp (fun x h ↦ trans_of _ h_hd h) h_tl.cons_elim.left⟩ - -/-- Coinductive principle for `Pairwise`. -/ -theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by - simp only [Pairwise] - intro i j x y h_ij hx hy - obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij - rw [← head_dropn] at hx - rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) i - generalize s.drop i = s' at * - cases s' with - | nil => simp at hx - | cons hd tl => - simp at hx hy - exact hx ▸ All_get (h_step hd tl this).left hy - -/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove -`R hd tl.head` instead of `tl.All (R hd ·)` in `h_step`. -/ -theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : - Pairwise R s := by - have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by - rw [← head_dropn] at hx - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) - exact (h_step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) - simp only [Pairwise] - intro i j x y h_ij hx hy - obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij - clear h_ij - induction k generalizing y with - | zero => exact h_succ hx hy - | succ k ih => - obtain ⟨z, hz⟩ := ge_stable (m := i + k + 1) _ (by omega) hy - exact _root_.trans (ih z hz) <| h_succ hz hy - -theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) : - s.tail.Pairwise R := by - cases s - · simp - · simp [h.cons_elim.right] - -theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) {n : ℕ} : - (s.drop n).Pairwise R := by - induction n with - | zero => simpa - | succ m ih => simp [drop, Pairwise_tail ih] - -end Pairwise - -section AtLeastAsLongAs - -theorem AtLeastAsLongAs.nil {a : Seq α} : - a.AtLeastAsLongAs (@nil β) := by - unfold AtLeastAsLongAs - simp [terminatedAt_nil] - -theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} - (h : a_tl.AtLeastAsLongAs b_tl) : - (Seq.cons a_hd a_tl).AtLeastAsLongAs (Seq.cons b_hd b_tl) := by - simp only [AtLeastAsLongAs] at * - intro n - cases n with - | zero => simp - | succ m => simpa using h m - -theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} - (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by - cases a with - | nil => - unfold AtLeastAsLongAs at h - simp only [terminatedAt_nil, forall_const] at h - specialize h 0 - simp [TerminatedAt] at h - | cons hd' tl' => use hd', tl' - -/-- Coinductive principle for `AtLeastAsLongAs`. -/ -theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} - (motive : Seq α → Seq β → Prop) (h_base : motive a b) - (h_step : ∀ a b, motive a b → - (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) - : a.AtLeastAsLongAs b := by - simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] - intro n - have (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by - induction n with - | zero => simpa - | succ m ih => - simp only [drop] at hb ⊢ - generalize b.drop m = tb at * - cases tb with - | nil => simp at hb - | cons tb_hd tb_tl => - simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih - obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) - simpa [ha] - contrapose - rw [head_eq_none_iff] - generalize b.drop n = tb at * - cases tb - · simp - · intro hb - obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) - simp [ha] - -@[simp] -theorem cons_AtLeastAsLongAs_cons_iff {a_hd : α} {a_tl : Seq α} {b_hd : β} - {b_tl : Seq β} : - (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl where - mp h := by - simp [AtLeastAsLongAs] at * - intro n - simpa using h (n + 1) - mpr := AtLeastAsLongAs.cons - -theorem map_AtLeastAsLongAs_self {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} - {b : Seq β} (h : a.AtLeastAsLongAs b) : - a.AtLeastAsLongAs (b.map f) := by - simp only [AtLeastAsLongAs, terminatedAt_map_iff] at * - intro n ha - simpa [TerminatedAt] using h n ha - -end AtLeastAsLongAs - -instance : Functor Seq where map := @map - -instance : LawfulFunctor Seq where - id_map := @map_id - comp_map := @map_comp - map_const := rfl - -end Seq - -namespace Seq1 - -variable {α : Type u} {β : Type v} {γ : Type w} - -open Stream'.Seq - -/-- Convert a `Seq1` to a sequence. -/ -def toSeq : Seq1 α → Seq α - | (a, s) => Seq.cons a s - -instance coeSeq : Coe (Seq1 α) (Seq α) := - ⟨toSeq⟩ - -/-- Map a function on a `Seq1` -/ -def map (f : α → β) : Seq1 α → Seq1 β - | (a, s) => (f a, Seq.map f s) - -theorem map_pair {f : α → β} {a s} : map f (a, s) = (f a, Seq.map f s) := rfl - -theorem map_id : ∀ s : Seq1 α, map id s = s - | ⟨a, s⟩ => by simp [map] - -/-- Flatten a nonempty sequence of nonempty sequences -/ -def join : Seq1 (Seq1 α) → Seq1 α - | ((a, s), S) => - match destruct s with - | none => (a, Seq.join S) - | some s' => (a, Seq.join (Seq.cons s' S)) - -@[simp] -theorem join_nil (a : α) (S) : join ((a, nil), S) = (a, Seq.join S) := - rfl - -@[simp] -theorem join_cons (a b : α) (s S) : - join ((a, Seq.cons b s), S) = (a, Seq.join (Seq.cons (b, s) S)) := by - dsimp [join]; rw [destruct_cons] - -/-- The `return` operator for the `Seq1` monad, - which produces a singleton sequence. -/ -def ret (a : α) : Seq1 α := - (a, nil) - -instance [Inhabited α] : Inhabited (Seq1 α) := - ⟨ret default⟩ - -/-- The `bind` operator for the `Seq1` monad, - which maps `f` on each element of `s` and appends the results together. - (Not all of `s` may be evaluated, because the first few elements of `s` - may already produce an infinite result.) -/ -def bind (s : Seq1 α) (f : α → Seq1 β) : Seq1 β := - join (map f s) - -@[simp] -theorem join_map_ret (s : Seq α) : Seq.join (Seq.map ret s) = s := by - apply coinduction2 s; intro s; cases s <;> simp [ret] - -@[simp] -theorem bind_ret (f : α → β) : ∀ s, bind s (ret ∘ f) = map f s - | ⟨a, s⟩ => by simp [bind, map, map_comp, ret] - -@[simp] -theorem ret_bind (a : α) (f : α → Seq1 β) : bind (ret a) f = f a := by - simp only [bind, map, ret.eq_1, map_nil] - obtain ⟨a, s⟩ := f a - cases s <;> simp - -@[simp] -theorem map_join' (f : α → β) (S) : Seq.map f (Seq.join S) = Seq.join (Seq.map (map f) S) := by - apply - Seq.eq_of_bisim fun s1 s2 => - ∃ s S, - s1 = Seq.append s (Seq.map f (Seq.join S)) ∧ s2 = append s (Seq.join (Seq.map (map f) S)) - · intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, S, rfl, rfl⟩ => by - cases s <;> simp - case nil => - cases S <;> simp - case cons x S => - obtain ⟨a, s⟩ := x - simpa [map] using ⟨_, _, rfl, rfl⟩ - case cons _ s => exact ⟨s, S, rfl, rfl⟩ - · refine ⟨nil, S, ?_, ?_⟩ <;> simp - -@[simp] -theorem map_join (f : α → β) : ∀ S, map f (join S) = join (map (map f) S) - | ((a, s), S) => by cases s <;> simp [map] - -@[simp] -theorem join_join (SS : Seq (Seq1 (Seq1 α))) : - Seq.join (Seq.join SS) = Seq.join (Seq.map join SS) := by - apply - Seq.eq_of_bisim fun s1 s2 => - ∃ s SS, - s1 = Seq.append s (Seq.join (Seq.join SS)) ∧ s2 = Seq.append s (Seq.join (Seq.map join SS)) - · intro s1 s2 h - exact - match s1, s2, h with - | _, _, ⟨s, SS, rfl, rfl⟩ => by - cases s <;> simp - case nil => - cases SS <;> simp - case cons S SS => - obtain ⟨s, S⟩ := S; obtain ⟨x, s⟩ := s - simp only [Seq.join_cons, join_append, destruct_cons] - cases s <;> simp - case nil => exact ⟨_, _, rfl, rfl⟩ - case cons x s => refine ⟨Seq.cons x (append s (Seq.join S)), SS, ?_, ?_⟩ <;> simp - case cons _ s => exact ⟨s, SS, rfl, rfl⟩ - · refine ⟨nil, SS, ?_, ?_⟩ <;> simp - -@[simp] -theorem bind_assoc (s : Seq1 α) (f : α → Seq1 β) (g : β → Seq1 γ) : - bind (bind s f) g = bind s fun x : α => bind (f x) g := by - obtain ⟨a, s⟩ := s - simp only [bind, map_pair, map_join] - rw [← map_comp] - simp only [show (fun x => join (map g (f x))) = join ∘ (map g ∘ f) from rfl] - rw [map_comp _ join] - generalize Seq.map (map g ∘ f) s = SS - rcases map g (f a) with ⟨⟨a, s⟩, S⟩ - induction' s using recOn with x s_1 <;> induction' S using recOn with x_1 s_2 <;> simp - · obtain ⟨x, t⟩ := x_1 - cases t <;> simp - · obtain ⟨y, t⟩ := x_1; simp - -instance monad : Monad Seq1 where - map := @map - pure := @ret - bind := @bind - -instance lawfulMonad : LawfulMonad Seq1 := LawfulMonad.mk' - (id_map := @map_id) - (bind_pure_comp := @bind_ret) - (pure_bind := @ret_bind) - (bind_assoc := @bind_assoc) - -end Seq1 - -end Stream' - -set_option linter.style.longFile 1900 +deprecated_module (since := "2025-08-26") From bf2dbca9991428c7ae886944a226add7e8afaf4e Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Wed, 27 Aug 2025 14:40:01 +0300 Subject: [PATCH 27/34] naming --- Mathlib/Data/Seq/Basic.lean | 60 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index 084bc80785dab3..b414cbc877bbd8 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -358,22 +358,22 @@ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s /-- Version of `eq_of_bisim` that looks more like an induction principle. -/ theorem eq_of_bisim' {s₁ s₂ : Seq α} (motive : Seq α → Seq α → Prop) - (h_base : motive s₁ s₂) - (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (base : motive s₁ s₂) + (step : ∀ s₁ s₂, motive s₁ s₂ → (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by - apply eq_of_bisim motive _ h_base + apply eq_of_bisim motive _ base intro s₁ s₂ h - rcases h_step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) + rcases step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) · simpa [h₁, h₂] · simp [h_nil₁, h_nil₂] /-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` -instead of `s₁ = nil ∧ s₂ = nil` in `h_step`. -/ +instead of `s₁ = nil ∧ s₂ = nil` in `step`. -/ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} (motive : Seq α → Seq α → Prop) - (h_base : motive s₁ s₂) - (h_step : ∀ s₁ s₂, motive s₁ s₂ → + (base : motive s₁ s₂) + (step : ∀ s₁ s₂, motive s₁ s₂ → (s₁ = s₂) ∨ (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))) : s₁ = s₂ := by let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ @@ -382,7 +382,7 @@ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} simp only [motive'] at ih ⊢ rcases ih with (rfl | ih) · cases s₁ <;> grind - rcases h_step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) + rcases step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) · cases s₁ <;> grind · grind @@ -1399,8 +1399,8 @@ theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some grind set_option linter.dupNamespace false in -private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : +private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (base : motive s) + (step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : motive (s.drop n) := by induction n with | zero => simpa @@ -1409,21 +1409,21 @@ private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (h_b generalize s.drop m = t at * cases t · simpa - · exact h_step _ _ ih + · exact step _ _ ih /-- Coinductive principle for `All`. -/ theorem All.coind {s : Seq α} {p : α → Prop} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) + (motive : Seq α → Prop) (base : motive s) + (step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) : s.All p := by apply All_of_get intro n - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) n + have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) n rw [← head_dropn] generalize s.drop n = s' at this cases s' with | nil => simp - | cons hd tl => simp [(h_step hd tl this).left] + | cons hd tl => simp [(step hd tl this).left] theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : s.All q := by @@ -1471,7 +1471,7 @@ theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by simp [Pairwise] theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} - (h_lt : tl.All (R hd ·)) + (h_hd : tl.All (R hd ·)) (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by simp only [Pairwise] at * intro i j x y h_ij hx hy @@ -1482,7 +1482,7 @@ theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} cases i with | zero => simp only [get?_cons_zero, Option.some.injEq] at hx - exact hx ▸ All_get h_lt hy + exact hx ▸ All_get h_hd hy | succ n => exact h_tl n k x y (by omega) hx hy theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} @@ -1516,31 +1516,31 @@ theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd t /-- Coinductive principle for `Pairwise`. -/ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by + (motive : Seq α → Prop) (base : motive s) + (step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by simp only [Pairwise] intro i j x y h_ij hx hy obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij rw [← head_dropn] at hx rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) i + have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) i generalize s.drop i = s' at * cases s' with | nil => simp at hx | cons hd tl => simp at hx hy - exact hx ▸ All_get (h_step hd tl this).left hy + exact hx ▸ All_get (step hd tl this).left hy /-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove -`R hd tl.head` instead of `tl.All (R hd ·)` in `h_step`. -/ +`R hd tl.head` instead of `tl.All (R hd ·)` in `step`. -/ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α} - (motive : Seq α → Prop) (h_base : motive s) - (h_step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : + (motive : Seq α → Prop) (base : motive s) + (step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : Pairwise R s := by have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by rw [← head_dropn] at hx - have := All.coind_drop_motive motive h_base (fun hd tl ih ↦ (h_step hd tl ih).right) - exact (h_step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) + have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) + exact (step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) simp only [Pairwise] intro i j x y h_ij hx hy obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij @@ -1593,8 +1593,8 @@ theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} /-- Coinductive principle for `AtLeastAsLongAs`. -/ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} - (motive : Seq α → Seq β → Prop) (h_base : motive a b) - (h_step : ∀ a b, motive a b → + (motive : Seq α → Seq β → Prop) (base : motive a b) + (step : ∀ a b, motive a b → (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) : a.AtLeastAsLongAs b := by simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] @@ -1609,7 +1609,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} | nil => simp at hb | cons tb_hd tb_tl => simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih - obtain ⟨a_hd, a_tl, ha, h_tail⟩ := h_step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) + obtain ⟨a_hd, a_tl, ha, h_tail⟩ := step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) simpa [ha] contrapose rw [head_eq_none_iff] @@ -1617,7 +1617,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} cases tb · simp · intro hb - obtain ⟨a_hd, a_tl, ha, _⟩ := h_step _ _ (this hb) _ _ (by rfl) + obtain ⟨a_hd, a_tl, ha, _⟩ := step _ _ (this hb) _ _ (by rfl) simp [ha] @[simp] From c032d95016c4319f0a4a1a68affe1fe21489a484 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Sat, 30 Aug 2025 01:04:13 +0300 Subject: [PATCH 28/34] merge --- Mathlib/Data/Seq/Defs.lean | 71 +++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index 0d26aae6a8a151..b09b537dc10736 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -26,9 +26,10 @@ sequences). It is encoded as an infinite stream of options such that if `f n = n One can convert between sequences and other types: `List`, `Stream'`, `MLList` using corresponding functions defined in this file. -There are also a number of operations on sequences mirroring those on lists: `Seq.map`, `Seq.zip`, -`Seq.zipWith`, `Seq.unzip`, `Seq.fold`, `Seq.update`, `Seq.drop`, `Seq.splitAt`, `Seq.append`, -`Seq.join`, `Seq.enum`, as well as a cases principle `Seq.recOn` which allows one to reason about +There are also a number of operations and predicates on sequences mirroring those on lists: +`Seq.map`, `Seq.zip`, `Seq.zipWith`, `Seq.unzip`, `Seq.fold`, `Seq.update`, `Seq.drop`, +`Seq.splitAt`, `Seq.append`, `Seq.join`, `Seq.enum`, `Seq.All`, `Seq.Pairwire`, +as well as a cases principle `Seq.recOn` which allows one to reason about sequences by cases (`nil` and `cons`). ## Main statements @@ -350,7 +351,7 @@ attribute [nolint simpNF] BisimO.eq_3 def IsBisimulation := ∀ ⦃s₁ s₂⦄, s₁ ~ s₂ → BisimO R (destruct s₁) (destruct s₂) --- If two streams are bisimilar, then they are equal +/-- If two streams are bisimilar, then they are equal. -/ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by apply Subtype.eq apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s' @@ -377,6 +378,37 @@ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s · simp · exact ⟨s₁, s₂, rfl, rfl, r⟩ +/-- Version of `eq_of_bisim` that looks more like an induction principle. -/ +theorem eq_of_bisim' {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (base : motive s₁ s₂) + (step : ∀ s₁ s₂, motive s₁ s₂ → + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ + (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by + apply eq_of_bisim motive _ base + intro s₁ s₂ h + rcases step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) + · simpa [h₁, h₂] + · simp [h_nil₁, h_nil₂] + +/-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` +instead of `s₁ = nil ∧ s₂ = nil` in `step`. -/ +theorem eq_of_bisim_strong {s₁ s₂ : Seq α} + (motive : Seq α → Seq α → Prop) + (base : motive s₁ s₂) + (step : ∀ s₁ s₂, motive s₁ s₂ → + (s₁ = s₂) ∨ + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ (motive s₁' s₂'))) : s₁ = s₂ := by + let motive' : Seq α → Seq α → Prop := fun s₁ s₂ => s₁ = s₂ ∨ motive s₁ s₂ + apply eq_of_bisim' motive' (by grind) + intro s₁ s₂ ih + simp only [motive'] at ih ⊢ + rcases ih with (rfl | ih) + · cases s₁ <;> grind + rcases step s₁ s₂ ih with (rfl | ⟨hd, s₁', s₂', _⟩) + · cases s₁ <;> grind + · grind + end Bisim theorem coinduction : @@ -510,6 +542,13 @@ instance : Membership α (Seq α) := -- Cannot be @[simp] because `n` can not be inferred by `simp`. theorem get?_mem {s : Seq α} {n : ℕ} {x : α} (h : s.get? n = .some x) : x ∈ s := ⟨n, h.symm⟩ +theorem mem_iff_exists_get? {s : Seq α} {x : α} : x ∈ s ↔ ∃ i, some x = s.get? i where + mp h := by + change (some x ∈ s.1) at h + rwa [Stream'.mem_iff_exists_get_eq] at h + mpr h := get?_mem h.choose_spec.symm + +@[simp] theorem notMem_nil (a : α) : a ∉ @nil α := fun ⟨_, (h : some a = none)⟩ => by injection h @[deprecated (since := "2025-05-23")] alias not_mem_nil := notMem_nil @@ -746,6 +785,30 @@ def update (s : Seq α) (n : ℕ) (f : α → α) : Seq α where def set (s : Seq α) (n : ℕ) (a : α) : Seq α := update s n fun _ ↦ a +/-! +### Predicates on sequences +-/ + +/-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ +def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x + +/-- +`Pairwise R s` means that all the elements with earlier indexes are +`R`-related to all the elements with later indexes. +``` +Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 +``` +For example if `R = (· ≠ ·)` then it asserts `s` has no duplicates, +and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. +-/ +def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := + ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y + +/-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. +In particular, they both may be infinite. -/ +def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := + ∀ n, a.TerminatedAt n → b.TerminatedAt n + end Seq end Stream' From 16c41501270c27f5cf166c08789862e1abbe3d1d Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Wed, 10 Sep 2025 01:37:03 +0300 Subject: [PATCH 29/34] suggestions --- Mathlib/Data/Seq/Basic.lean | 24 +++++++++++------------- Mathlib/Data/Seq/Defs.lean | 19 ++++++++++++------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index 3c35151b6d59b8..853c5a05d255a0 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -632,7 +632,6 @@ theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some simp only [All, mem_iff_exists_get?] grind -set_option linter.dupNamespace false in private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (base : motive s) (step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : motive (s.drop n) := by @@ -648,8 +647,8 @@ private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (bas /-- Coinductive principle for `All`. -/ theorem All.coind {s : Seq α} {p : α → Prop} (motive : Seq α → Prop) (base : motive s) - (step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) - : s.All p := by + (step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) : + s.All p := by apply All_of_get intro n have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) n @@ -765,8 +764,9 @@ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} simp at hx hy exact hx ▸ All_get (step hd tl this).left hy -/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. It allows to prove -`R hd tl.head` instead of `tl.All (R hd ·)` in `step`. -/ +/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. Compared to +`Pairwise.coind`, this allows you to prove `R hd tl.head` instead of `tl.All (R hd ·)` in `step`. +-/ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α} (motive : Seq α → Prop) (base : motive s) (step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) : @@ -803,8 +803,7 @@ section AtLeastAsLongAs theorem AtLeastAsLongAs.nil {a : Seq α} : a.AtLeastAsLongAs (@nil β) := by - unfold AtLeastAsLongAs - simp [terminatedAt_nil] + simp [AtLeastAsLongAs, terminatedAt_nil] theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} (h : a_tl.AtLeastAsLongAs b_tl) : @@ -819,8 +818,7 @@ theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by cases a with | nil => - unfold AtLeastAsLongAs at h - simp only [terminatedAt_nil, forall_const] at h + simp only [AtLeastAsLongAs, terminatedAt_nil, forall_const] at h specialize h 0 simp [TerminatedAt] at h | cons hd' tl' => use hd', tl' @@ -829,8 +827,8 @@ theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} (motive : Seq α → Seq β → Prop) (base : motive a b) (step : ∀ a b, motive a b → - (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) - : a.AtLeastAsLongAs b := by + (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) : + a.AtLeastAsLongAs b := by simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] intro n have (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by @@ -843,7 +841,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} | nil => simp at hb | cons tb_hd tb_tl => simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih - obtain ⟨a_hd, a_tl, ha, h_tail⟩ := step (a.drop m) (.cons tb_hd tb_tl) ih _ _ (by rfl) + obtain ⟨a_hd, a_tl, ha, h_tail⟩ := step (a.drop m) (.cons tb_hd tb_tl) ih _ _ rfl simpa [ha] contrapose rw [head_eq_none_iff] @@ -851,7 +849,7 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} cases tb · simp · intro hb - obtain ⟨a_hd, a_tl, ha, _⟩ := step _ _ (this hb) _ _ (by rfl) + obtain ⟨a_hd, a_tl, ha, _⟩ := step _ _ (this hb) _ _ rfl simp [ha] @[simp] diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index b09b537dc10736..92bbf32ba95209 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -351,7 +351,9 @@ attribute [nolint simpNF] BisimO.eq_3 def IsBisimulation := ∀ ⦃s₁ s₂⦄, s₁ ~ s₂ → BisimO R (destruct s₁) (destruct s₂) -/-- If two streams are bisimilar, then they are equal. -/ +/-- If two streams are bisimilar, then they are equal. There are also versions +`eq_of_bisim'` and `eq_of_bisim_strong` that does not mention `IsBisimulation` and look +more like an induction principles. -/ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s₁ = s₂ := by apply Subtype.eq apply Stream'.eq_of_bisim fun x y => ∃ s s' : Seq α, s.1 = x ∧ s'.1 = y ∧ R s s' @@ -378,20 +380,23 @@ theorem eq_of_bisim (bisim : IsBisimulation R) {s₁ s₂} (r : s₁ ~ s₂) : s · simp · exact ⟨s₁, s₂, rfl, rfl, r⟩ -/-- Version of `eq_of_bisim` that looks more like an induction principle. -/ +/-- Coinductive principle for equality on sequences. +This is a version of `eq_of_bisim` that looks more like an induction principle. -/ theorem eq_of_bisim' {s₁ s₂ : Seq α} (motive : Seq α → Seq α → Prop) (base : motive s₁ s₂) (step : ∀ s₁ s₂, motive s₁ s₂ → - (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') ∨ - (s₁ = nil ∧ s₂ = nil)) : s₁ = s₂ := by + (s₁ = nil ∧ s₂ = nil) ∨ + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') + ) : s₁ = s₂ := by apply eq_of_bisim motive _ base intro s₁ s₂ h - rcases step s₁ s₂ h with (⟨_, _, _, h₁, h₂, _⟩ | ⟨h_nil₁, h_nil₂⟩) - · simpa [h₁, h₂] + rcases step s₁ s₂ h with ⟨h_nil₁, h_nil₂⟩ | ⟨_, _, _, h₁, h₂, _⟩ · simp [h_nil₁, h_nil₂] + · simpa [h₁, h₂] -/-- Version of `eq_of_bisim'` that requires only `s₁ = s₂` +/-- Coinductive principle for equality on sequences. +This is a version of `eq_of_bisim'` that requires proving only `s₁ = s₂` instead of `s₁ = nil ∧ s₂ = nil` in `step`. -/ theorem eq_of_bisim_strong {s₁ s₂ : Seq α} (motive : Seq α → Seq α → Prop) From 4555527b833f0390c8cf33b3b5b6e5cf2cc1da43 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Wed, 10 Sep 2025 01:48:20 +0300 Subject: [PATCH 30/34] fix --- Mathlib/Data/Seq/Defs.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index 92bbf32ba95209..f8aa6eaccd6b1f 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -387,8 +387,8 @@ theorem eq_of_bisim' {s₁ s₂ : Seq α} (base : motive s₁ s₂) (step : ∀ s₁ s₂, motive s₁ s₂ → (s₁ = nil ∧ s₂ = nil) ∨ - (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂') - ) : s₁ = s₂ := by + (∃ x s₁' s₂', s₁ = cons x s₁' ∧ s₂ = cons x s₂' ∧ motive s₁' s₂')) : + s₁ = s₂ := by apply eq_of_bisim motive _ base intro s₁ s₂ h rcases step s₁ s₂ h with ⟨h_nil₁, h_nil₂⟩ | ⟨_, _, _, h₁, h₂, _⟩ From cebc7c2aa7b90c90120493f3a9de9d4291cc827a Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Fri, 12 Sep 2025 14:25:53 +0300 Subject: [PATCH 31/34] Merge branch 'master' into Seq_split --- Mathlib/Data/Seq/Basic.lean | 111 ++++++++++++++++-------------------- Mathlib/Data/Seq/Defs.lean | 5 +- 2 files changed, 51 insertions(+), 65 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index 60e1284c4664c8..b366ec8642317c 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -608,31 +608,27 @@ end Update section All -@[simp] -theorem All.nil (p : α → Prop) : nil.All p := by - simp [All] - -theorem All.cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : tl.All p) : - ((cons hd tl).All p) := by - simp only [All, mem_cons_iff, forall_eq_or_imp] at * +theorem all_cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : ∀ x ∈ tl, p x) : + (∀ x ∈ (cons hd tl), p x) := by + simp only [mem_cons_iff, forall_eq_or_imp] at * exact ⟨h_hd, h_tl⟩ @[simp] -theorem All_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : - ((cons hd tl).All p) ↔ p hd ∧ tl.All p := by - simp [All] +theorem all_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : + (∀ x ∈ cons hd tl, p x) ↔ p hd ∧ ∀ x ∈ tl, p x := by + simp -theorem All_get {p : α → Prop} {s : Seq α} (h : s.All p) {n : ℕ} {x : α} (hx : s.get? n = .some x) : +theorem all_get {p : α → Prop} {s : Seq α} (h : ∀ x ∈ s, p x) {n : ℕ} {x : α} + (hx : s.get? n = .some x) : p x := by - unfold All at h exact h _ (get?_mem hx) -theorem All_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : - s.All p := by - simp only [All, mem_iff_exists_get?] +theorem all_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) : + ∀ x ∈ s, p x := by + simp only [mem_iff_exists_get?] grind -private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (base : motive s) +private lemma all_coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (base : motive s) (step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) : motive (s.drop n) := by induction n with @@ -645,34 +641,29 @@ private lemma All.coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (bas · exact step _ _ ih /-- Coinductive principle for `All`. -/ -theorem All.coind {s : Seq α} {p : α → Prop} +theorem all_coind {s : Seq α} {p : α → Prop} (motive : Seq α → Prop) (base : motive s) (step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) : - s.All p := by - apply All_of_get + ∀ x ∈ s, p x := by + apply all_of_get intro n - have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) n + have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) n rw [← head_dropn] generalize s.drop n = s' at this cases s' with | nil => simp | cons hd tl => simp [(step hd tl this).left] -theorem All_mp {p q : α → Prop} (h : ∀ a, p a → q a) {s : Seq α} (hp : s.All p) : - s.All q := by - simp only [All] at * - grind - -theorem map_All_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : - (s.map f).All p ↔ s.All (p ∘ f) := by - simp [All] +theorem map_all_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} : + (∀ x ∈ (s.map f), p x) ↔ (∀ x ∈ s, (p ∘ f) x) := by refine ⟨fun _ _ hx ↦ ?_, fun _ _ hx ↦ ?_⟩ · solve_by_elim [mem_map f hx] · obtain ⟨_, _, hx'⟩ := exists_of_mem_map hx rw [← hx'] solve_by_elim -theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} {x : α} (hx : x ∈ s.take n) : +theorem take_all {s : Seq α} {p : α → Prop} (h_all : ∀ x ∈ s, p x) {n : ℕ} {x : α} + (hx : x ∈ s.take n) : p x := by induction n generalizing s with | zero => simp [take] at hx @@ -680,20 +671,21 @@ theorem take_All {s : Seq α} {p : α → Prop} (h_all : s.All p) {n : ℕ} {x : cases s with | nil => simp at hx | cons hd tl => - simp only [take_succ_cons, List.mem_cons, All_cons_iff] at hx h_all + simp only [take_succ_cons, List.mem_cons, all_cons_iff] at hx h_all rcases hx with (rfl | hx) exacts [h_all.left, ih h_all.right hx] -theorem set_All {p : α → Prop} {s : Seq α} (h_all : s.All p) {n : ℕ} {x : α} - (hx : p x) : (s.set n x).All p := by - apply All_of_get - intro m +theorem set_all {p : α → Prop} {s : Seq α} (h_all : ∀ x ∈ s, p x) {n : ℕ} {x : α} + (hx : p x) : ∀ y ∈ (s.set n x), p y := by + intro y hy + simp only [mem_iff_exists_get?] at hy + obtain ⟨m, hy⟩ := hy rcases eq_or_ne n m with (rfl | h_nm) · by_cases h_term : s.TerminatedAt n - · simp [get?_set_of_terminatedAt _ h_term] - · simpa [get?_set_of_not_terminatedAt _ h_term] - · rw [get?_set_of_ne] - exacts [fun x hx ↦ All_get h_all hx, h_nm.symm] + · simp [get?_set_of_terminatedAt _ h_term] at hy + · simp_all [get?_set_of_not_terminatedAt _ h_term] + · rw [get?_set_of_ne _ _ h_nm.symm] at hy + apply h_all _ (get?_mem hy.symm) end All @@ -704,31 +696,28 @@ theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by simp [Pairwise] theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α} - (h_hd : tl.All (R hd ·)) + (h_hd : ∀ x ∈ tl, R hd x) (h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by simp only [Pairwise] at * - intro i j x y h_ij hx hy + intro i j h_ij x hx y hy cases j with | zero => simp at h_ij | succ k => simp only [get?_cons_succ] at hy cases i with | zero => - simp only [get?_cons_zero, Option.some.injEq] at hx - exact hx ▸ All_get h_hd hy - | succ n => exact h_tl n k x y (by omega) hx hy + simp only [get?_cons_zero, Option.mem_def, Option.some.injEq] at hx + exact hx ▸ all_get h_hd hy + | succ n => exact h_tl n k (by omega) x hx y hy theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α} - (h : Pairwise R (.cons hd tl)) : tl.All (R hd ·) ∧ Pairwise R tl := by + (h : Pairwise R (.cons hd tl)) : (∀ x ∈ tl, R hd x) ∧ Pairwise R tl := by simp only [Pairwise] at * - refine ⟨?_, fun i j x y h_ij hx hy ↦ h (i + 1) (j + 1) x y (by omega) hx hy⟩ - apply All_of_get - intro n - specialize h 0 (n + 1) hd - simp only [Nat.zero_lt_succ, get?_cons_zero, get?_cons_succ, forall_const] at h - cases h_tl : tl.get? n with - | none => simp - | some y => simp [h y h_tl] + refine ⟨?_, fun i j h_ij ↦ h (i + 1) (j + 1) (by omega)⟩ + intro x hx + rw [mem_iff_exists_get?] at hx + obtain ⟨n, hx⟩ := hx + simpa [← hx] using h 0 (n + 1) (by omega) @[simp] theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by @@ -738,31 +727,31 @@ theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl (h : Pairwise R (cons hd (cons tl_hd tl_tl))) : R hd tl_hd := by simp only [Pairwise] at h - simpa using h 0 1 hd tl_hd Nat.one_pos + simpa using h 0 1 Nat.one_pos theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α} (h_hd : R hd tl_hd) (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by apply Pairwise.cons _ h_tl - rw [All_cons_iff] - exact ⟨h_hd, All_mp (fun x h ↦ trans_of _ h_hd h) h_tl.cons_elim.left⟩ + rw [all_cons_iff] + exact ⟨h_hd, fun x hx ↦ Trans.simple h_hd ((cons_elim h_tl).left x hx)⟩ /-- Coinductive principle for `Pairwise`. -/ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} (motive : Seq α → Prop) (base : motive s) - (step : ∀ hd tl, motive (.cons hd tl) → tl.All (R hd ·) ∧ motive tl) : Pairwise R s := by + (step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl, R hd x) ∧ motive tl) : Pairwise R s := by simp only [Pairwise] - intro i j x y h_ij hx hy + intro i j h_ij x hx y hy obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij rw [← head_dropn] at hx rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy - have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) i + have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) i generalize s.drop i = s' at * cases s' with | nil => simp at hx | cons hd tl => simp at hx hy - exact hx ▸ All_get (step hd tl this).left hy + exact hx ▸ all_get (step hd tl this).left hy /-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. Compared to `Pairwise.coind`, this allows you to prove `R hd tl.head` instead of `tl.All (R hd ·)` in `step`. @@ -773,10 +762,10 @@ theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α Pairwise R s := by have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by rw [← head_dropn] at hx - have := All.coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) + have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) exact (step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa) simp only [Pairwise] - intro i j x y h_ij hx hy + intro i j h_ij x hx y hy obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij clear h_ij induction k generalizing y with diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index f8aa6eaccd6b1f..6d0a7771e43fc9 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -794,9 +794,6 @@ def set (s : Seq α) (n : ℕ) (a : α) : Seq α := ### Predicates on sequences -/ -/-- `s.All p` means that the predicate `p` is true on each element of `s`. -/ -def All (s : Seq α) (p : α → Prop) : Prop := ∀ x ∈ s, p x - /-- `Pairwise R s` means that all the elements with earlier indexes are `R`-related to all the elements with later indexes. @@ -807,7 +804,7 @@ For example if `R = (· ≠ ·)` then it asserts `s` has no duplicates, and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. -/ def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := - ∀ i j x y, i < j → s.get? i = .some x → s.get? j = .some y → R x y + ∀ i j, i < j → ∀ x ∈ s.get? i, ∀ y ∈ s.get? j, R x y /-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. In particular, they both may be infinite. -/ From c0e859c1e047bbe2bab13121f346d761162d6b01 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Sat, 13 Sep 2025 01:43:39 +0300 Subject: [PATCH 32/34] remove AtLeastAsLongAs in favor of length' --- Mathlib/Data/Seq/Basic.lean | 210 +++++++++++++++++++++++++----------- Mathlib/Data/Seq/Defs.lean | 58 ++-------- 2 files changed, 157 insertions(+), 111 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index b366ec8642317c..c97b7bb7032a06 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -4,6 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Mario Carneiro, Vasilii Nesterov -/ import Mathlib.Data.Seq.Defs +import Mathlib.Data.ENat.Basic +import Mathlib.Tactic.ENatToNat +import Mathlib.Tactic.ApplyFun /-! # Basic properties of sequences (possibly infinite lists) @@ -20,6 +23,101 @@ namespace Seq variable {α : Type u} {β : Type v} {γ : Type w} +section length + +theorem length'_of_terminates {s : Seq α} (h : s.Terminates) : + s.length' = s.length h := by + simp [length', h] + +theorem length'_of_not_terminates {s : Seq α} (h : ¬ s.Terminates) : + s.length' = ⊤ := by + simp [length', h] + +@[simp] +theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl + +@[simp] +theorem length'_nil : length' (nil : Seq α) = 0 := by + simp -implicitDefEqProofs [length'] + +theorem length_cons {x : α} {s : Seq α} (h : s.Terminates) : + (cons x s).length (terminates_cons_iff.mpr h) = s.length h + 1 := by + apply Nat.find_comp_succ + simp + +@[simp] +theorem length'_cons (x : α) (s : Seq α) : + (cons x s).length' = s.length' + 1 := by + by_cases h : (cons x s).Terminates <;> have h' := h <;> rw [terminates_cons_iff] at h' + · simp [length'_of_terminates h, length'_of_terminates h', length_cons h'] + · simp [length'_of_not_terminates h, length'_of_not_terminates h'] + +@[simp] +theorem length_eq_zero {s : Seq α} {h : s.Terminates} : + s.length h = 0 ↔ s = nil := by + simp [length, TerminatedAt] + +@[simp] +theorem length'_eq_zero_iff_nil (s : Seq α) : + s.length' = 0 ↔ s = nil := by + cases s <;> simp + +theorem length'_ne_zero_iff_cons (s : Seq α) : + s.length' ≠ 0 ↔ ∃ x s', s = cons x s' := by + cases s <;> simp + +/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ +theorem length_le_iff' {s : Seq α} {n : ℕ} : + (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by + simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] + refine ⟨?_, ?_⟩ + · rintro ⟨_, k, hkn, hk⟩ + exact le_stable s hkn hk + · intro hn + exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + s.length h ≤ n ↔ s.TerminatedAt n := by + rw [← length_le_iff']; simp [h] + +theorem length'_le_iff {s : Seq α} {n : ℕ} : + s.length' ≤ n ↔ s.TerminatedAt n := by + by_cases h : s.Terminates + · simpa [length'_of_terminates h] using length_le_iff + · simpa [length'_of_not_terminates h] using forall_not_of_not_exists h n + +/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a +simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ +theorem lt_length_iff' {s : Seq α} {n : ℕ} : + (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by + simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, + ← Option.ne_none_iff_exists', ne_eq] + refine ⟨?_, ?_⟩ + · intro h hn + exact h n hn n le_rfl hn + · intro hn _ _ k hkn hk + exact hn <| le_stable s hkn hk + +/-- The statement of `length_le_iff` assumes that the sequence terminates. For a +statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : + n < s.length h ↔ ∃ a, a ∈ s.get? n := by + rw [← lt_length_iff']; simp [h] + +theorem lt_length'_iff {s : Seq α} {n : ℕ} : + n < s.length' ↔ ∃ a, a ∈ s.get? n := by + by_cases h : s.Terminates + · simpa [length'_of_terminates h] using lt_length_iff + · simp only [length'_of_not_terminates h, ENat.coe_lt_top, Option.mem_def, true_iff] + rw [not_terminates_iff] at h + rw [← Option.isSome_iff_exists] + exact h n + +end length + section OfStream @[simp] @@ -285,6 +383,13 @@ theorem length_map {s : Seq α} {f : α → β} (h : (s.map f).Terminates) : ext simp +@[simp] +theorem length'_map {s : Seq α} {f : α → β} : + (s.map f).length' = s.length' := by + by_cases h : (s.map f).Terminates <;> have h' := h <;> rw [terminates_map_iff] at h' + · rw [length'_of_terminates h, length'_of_terminates h', length_map h] + · rw [length'_of_not_terminates h, length'_of_not_terminates h'] + theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s | ⟨_, _⟩ => Stream'.mem_map (Option.map f) @@ -410,6 +515,21 @@ theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by | zero => simp [drop] | succ m ih => simp [← dropn_tail, ih] +@[simp] +theorem drop_length' {n : ℕ} {s : Seq α} : + (s.drop n).length' = s.length' - n := by + cases n with + | zero => simp + | succ n => + cases s with + | nil => simp + | cons x s => + simp only [drop_succ_cons, length'_cons, Nat.cast_add, Nat.cast_one] + convert drop_length' using 1 + generalize s.length' = m + enat_to_nat + omega + theorem take_drop {s : Seq α} {n m : ℕ} : (s.take n).drop m = (s.drop m).take (n - m) := by induction m generalizing n s with @@ -613,11 +733,6 @@ theorem all_cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : simp only [mem_cons_iff, forall_eq_or_imp] at * exact ⟨h_hd, h_tl⟩ -@[simp] -theorem all_cons_iff {p : α → Prop} {hd : α} {tl : Seq α} : - (∀ x ∈ cons hd tl, p x) ↔ p hd ∧ ∀ x ∈ tl, p x := by - simp - theorem all_get {p : α → Prop} {s : Seq α} (h : ∀ x ∈ s, p x) {n : ℕ} {x : α} (hx : s.get? n = .some x) : p x := by @@ -671,7 +786,7 @@ theorem take_all {s : Seq α} {p : α → Prop} (h_all : ∀ x ∈ s, p x) {n : cases s with | nil => simp at hx | cons hd tl => - simp only [take_succ_cons, List.mem_cons, all_cons_iff] at hx h_all + simp only [take_succ_cons, List.mem_cons, mem_cons_iff, forall_eq_or_imp] at hx h_all rcases hx with (rfl | hx) exacts [h_all.left, ih h_all.right hx] @@ -733,7 +848,7 @@ theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd t (h_hd : R hd tl_hd) (h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by apply Pairwise.cons _ h_tl - rw [all_cons_iff] + simp only [mem_cons_iff, forall_eq_or_imp] exact ⟨h_hd, fun x hx ↦ Trans.simple h_hd ((cons_elim h_tl).left x hx)⟩ /-- Coinductive principle for `Pairwise`. -/ @@ -788,39 +903,13 @@ theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) { end Pairwise -section AtLeastAsLongAs - -theorem AtLeastAsLongAs.nil {a : Seq α} : - a.AtLeastAsLongAs (@nil β) := by - simp [AtLeastAsLongAs, terminatedAt_nil] - -theorem AtLeastAsLongAs.cons {a_hd : α} {a_tl : Seq α} {b_hd : β} {b_tl : Seq β} - (h : a_tl.AtLeastAsLongAs b_tl) : - (Seq.cons a_hd a_tl).AtLeastAsLongAs (Seq.cons b_hd b_tl) := by - simp only [AtLeastAsLongAs] at * - intro n - cases n with - | zero => simp - | succ m => simpa using h m - -theorem AtLeastAsLongAs.cons_elim {a : Seq α} {hd : β} {tl : Seq β} - (h : a.AtLeastAsLongAs (.cons hd tl)) : ∃ hd' tl', a = .cons hd' tl' := by - cases a with - | nil => - simp only [AtLeastAsLongAs, terminatedAt_nil, forall_const] at h - specialize h 0 - simp [TerminatedAt] at h - | cons hd' tl' => use hd', tl' - -/-- Coinductive principle for `AtLeastAsLongAs`. -/ -theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} +/-- Coinductive principle for proving `b.length' ≤ a.length'` for two sequences `a` and `b`. -/ +theorem at_least_as_long_as_coind {a : Seq α} {b : Seq β} (motive : Seq α → Seq β → Prop) (base : motive a b) (step : ∀ a b, motive a b → (∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) : - a.AtLeastAsLongAs b := by - simp only [AtLeastAsLongAs, TerminatedAt, ← head_dropn] - intro n - have (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by + b.length' ≤ a.length' := by + have (n) (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by induction n with | zero => simpa | succ m ih => @@ -832,33 +921,26 @@ theorem AtLeastAsLongAs.coind {a : Seq α} {b : Seq β} simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih obtain ⟨a_hd, a_tl, ha, h_tail⟩ := step (a.drop m) (.cons tb_hd tb_tl) ih _ _ rfl simpa [ha] - contrapose - rw [head_eq_none_iff] - generalize b.drop n = tb at * - cases tb - · simp - · intro hb - obtain ⟨a_hd, a_tl, ha, _⟩ := step _ _ (this hb) _ _ rfl - simp [ha] - -@[simp] -theorem cons_AtLeastAsLongAs_cons_iff {a_hd : α} {a_tl : Seq α} {b_hd : β} - {b_tl : Seq β} : - (cons a_hd a_tl).AtLeastAsLongAs (cons b_hd b_tl) ↔ a_tl.AtLeastAsLongAs b_tl where - mp h := by - simp [AtLeastAsLongAs] at * - intro n - simpa using h (n + 1) - mpr := AtLeastAsLongAs.cons - -theorem map_AtLeastAsLongAs_self {α : Type v} {γ : Type w} {f : β → γ} {a : Seq α} - {b : Seq β} (h : a.AtLeastAsLongAs b) : - a.AtLeastAsLongAs (b.map f) := by - simp only [AtLeastAsLongAs, terminatedAt_map_iff] at * - intro n ha - simpa [TerminatedAt] using h n ha - -end AtLeastAsLongAs + by_cases ha : a.Terminates; swap + · simp [length'_of_not_terminates ha] + simp [length'_of_terminates ha, length'_le_iff] + by_contra! hb + have hb_cons : b.drop (a.length ha) ≠ .nil := by + intro hb' + simp only [← length'_eq_zero_iff_nil, drop_length', tsub_eq_zero_iff_le, length'_le_iff] at hb' + contradiction + specialize this (a.length ha) hb_cons + generalize b.drop (a.length ha) = b' at * + cases b' with + | nil => + contradiction + | cons b_hd b_tl => + obtain ⟨a_hd, a_tl, ha', _⟩ := step _ _ this _ _ rfl + apply_fun length' at ha' + simp only [drop_length', length'_of_terminates ha, tsub_self, length'_cons] at ha' + generalize a_tl.length' = u at ha' + enat_to_nat + omega instance : Functor Seq where map := @map diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index 6d0a7771e43fc9..6e3d6fcb1a9174 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -5,6 +5,7 @@ Authors: Mario Carneiro -/ import Mathlib.Data.Option.NAry import Mathlib.Data.Seq.Computation +import Mathlib.Data.ENat.Defs /-! # Possibly infinite lists @@ -28,7 +29,7 @@ functions defined in this file. There are also a number of operations and predicates on sequences mirroring those on lists: `Seq.map`, `Seq.zip`, `Seq.zipWith`, `Seq.unzip`, `Seq.fold`, `Seq.update`, `Seq.drop`, -`Seq.splitAt`, `Seq.append`, `Seq.join`, `Seq.enum`, `Seq.All`, `Seq.Pairwire`, +`Seq.splitAt`, `Seq.append`, `Seq.join`, `Seq.enum`, `Seq.Pairwire`, as well as a cases principle `Seq.recOn` which allows one to reason about sequences by cases (`nil` and `cons`). @@ -453,6 +454,11 @@ def Terminates (s : Seq α) : Prop := def length (s : Seq α) (h : s.Terminates) : ℕ := Nat.find h +open Classical in +/-- The `ENat`-valued length of a sequence. For non-terminating sequences, it is `⊤`. -/ +noncomputable def length' (s : Seq α) : ℕ∞ := + if h : s.Terminates then s.length h else ⊤ + /-- If a sequence terminated at position `n`, it also terminated at `m ≥ n`. -/ theorem terminated_stable : ∀ (s : Seq α) {m n : ℕ}, m ≤ n → s.TerminatedAt m → s.TerminatedAt n := le_stable @@ -482,13 +488,6 @@ theorem terminates_cons_iff {x : α} {s : Seq α} : · exact ⟨n, cons_terminatedAt_succ_iff.mp (terminated_stable _ (Nat.le_succ _) h)⟩ · exact ⟨n + 1, cons_terminatedAt_succ_iff.mpr h⟩ -@[simp] -theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl - -@[simp] theorem length_eq_zero {s : Seq α} {h : s.Terminates} : - s.length h = 0 ↔ s = nil := by - simp [length, TerminatedAt] - theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by refine ⟨?_, ?_⟩ · intro h @@ -498,41 +497,6 @@ theorem terminatedAt_zero_iff {s : Seq α} : s.TerminatedAt 0 ↔ s = nil := by · rintro rfl simp [TerminatedAt] -/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ -theorem length_le_iff' {s : Seq α} {n : ℕ} : - (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by - simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] - refine ⟨?_, ?_⟩ - · rintro ⟨_, k, hkn, hk⟩ - exact le_stable s hkn hk - · intro hn - exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - s.length h ≤ n ↔ s.TerminatedAt n := by - rw [← length_le_iff']; simp [h] - -/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ -theorem lt_length_iff' {s : Seq α} {n : ℕ} : - (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by - simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, - ← Option.ne_none_iff_exists', ne_eq] - refine ⟨?_, ?_⟩ - · intro h hn - exact h n hn n le_rfl hn - · intro hn _ _ k hkn hk - exact hn <| le_stable s hkn hk - -/-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ -theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : - n < s.length h ↔ ∃ a, a ∈ s.get? n := by - rw [← lt_length_iff']; simp [h] - /-! ### Membership -/ @@ -806,10 +770,10 @@ and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := ∀ i j, i < j → ∀ x ∈ s.get? i, ∀ y ∈ s.get? j, R x y -/-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. -In particular, they both may be infinite. -/ -def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := - ∀ n, a.TerminatedAt n → b.TerminatedAt n +-- /-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. +-- In particular, they both may be infinite. -/ +-- def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := +-- ∀ n, a.TerminatedAt n → b.TerminatedAt n end Seq From a9137102d6883cdd14862d6bffe71ed74022aa03 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Sat, 13 Sep 2025 01:54:00 +0300 Subject: [PATCH 33/34] fix --- Mathlib/Data/Seq/Basic.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index c97b7bb7032a06..fa0632f8ade0e5 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -167,8 +167,7 @@ theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α), rw [destruct_eq_cons h] match n with | 0 => simp - | n+1 => - simp [List.getElem?_cons_succ, Nat.add_lt_add_iff_right, getElem?_take] + | n+1 => simp [List.getElem?_cons_succ, getElem?_take] theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α} (h_get : s.get? m = some x) : x ∈ s.take n := by @@ -851,6 +850,7 @@ theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd t simp only [mem_cons_iff, forall_eq_or_imp] exact ⟨h_hd, fun x hx ↦ Trans.simple h_hd ((cons_elim h_tl).left x hx)⟩ + /-- Coinductive principle for `Pairwise`. -/ theorem Pairwise.coind {R : α → α → Prop} {s : Seq α} (motive : Seq α → Prop) (base : motive s) From 434074ecfb1134a0d77383866a2b1906da89e839 Mon Sep 17 00:00:00 2001 From: Vasilii Nesterov Date: Sun, 14 Sep 2025 22:27:50 +0300 Subject: [PATCH 34/34] clean --- Mathlib/Data/Seq/Basic.lean | 11 +++++------ Mathlib/Data/Seq/Defs.lean | 13 ++----------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Mathlib/Data/Seq/Basic.lean b/Mathlib/Data/Seq/Basic.lean index 017587f697d411..2ae40e2cc8710e 100644 --- a/Mathlib/Data/Seq/Basic.lean +++ b/Mathlib/Data/Seq/Basic.lean @@ -67,7 +67,7 @@ theorem length'_ne_zero_iff_cons (s : Seq α) : cases s <;> simp /-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `length_le_iff` -/ +simpler statement of the theorem where the sequence is known to terminate see `length_le_iff`. -/ theorem length_le_iff' {s : Seq α} {n : ℕ} : (∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop] @@ -78,7 +78,7 @@ theorem length_le_iff' {s : Seq α} {n : ℕ} : exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩ /-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +statement of the where the sequence is not known to terminate see `length_le_iff'`. -/ theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : s.length h ≤ n ↔ s.TerminatedAt n := by rw [← length_le_iff']; simp [h] @@ -90,7 +90,7 @@ theorem length'_le_iff {s : Seq α} {n : ℕ} : · simpa [length'_of_not_terminates h] using forall_not_of_not_exists h n /-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a -simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff` -/ +simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff`. -/ theorem lt_length_iff' {s : Seq α} {n : ℕ} : (∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def, @@ -102,7 +102,7 @@ theorem lt_length_iff' {s : Seq α} {n : ℕ} : exact hn <| le_stable s hkn hk /-- The statement of `length_le_iff` assumes that the sequence terminates. For a -statement of the where the sequence is not known to terminate see `length_le_iff'` -/ +statement of the where the sequence is not known to terminate see `length_le_iff'`. -/ theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} : n < s.length h ↔ ∃ a, a ∈ s.get? n := by rw [← lt_length_iff']; simp [h] @@ -778,8 +778,7 @@ theorem map_all_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} solve_by_elim theorem take_all {s : Seq α} {p : α → Prop} (h_all : ∀ x ∈ s, p x) {n : ℕ} {x : α} - (hx : x ∈ s.take n) : - p x := by + (hx : x ∈ s.take n) : p x := by induction n generalizing s with | zero => simp [take] at hx | succ m ih => diff --git a/Mathlib/Data/Seq/Defs.lean b/Mathlib/Data/Seq/Defs.lean index 855b5adc3149a5..6527dc63c81b2e 100644 --- a/Mathlib/Data/Seq/Defs.lean +++ b/Mathlib/Data/Seq/Defs.lean @@ -753,13 +753,9 @@ def update (s : Seq α) (n : ℕ) (f : α → α) : Seq α where def set (s : Seq α) (n : ℕ) (a : α) : Seq α := update s n fun _ ↦ a -/-! -### Predicates on sequences --/ - /-- -`Pairwise R s` means that all the elements with earlier indexes are -`R`-related to all the elements with later indexes. +`Pairwise R s` means that all the elements with earlier indices are +`R`-related to all the elements with later indices. ``` Pairwise R [1, 2, 3] ↔ R 1 2 ∧ R 1 3 ∧ R 2 3 ``` @@ -769,11 +765,6 @@ and if `R = (· < ·)` then it asserts that `s` is (strictly) sorted. def Pairwise (R : α → α → Prop) (s : Seq α) : Prop := ∀ i j, i < j → ∀ x ∈ s.get? i, ∀ y ∈ s.get? j, R x y --- /-- `s₁.AtLeastAsLongAs s₂` means that `s₁` has at least as many elements as sequence `s₂`. --- In particular, they both may be infinite. -/ --- def AtLeastAsLongAs (a : Seq α) (b : Seq β) : Prop := --- ∀ n, a.TerminatedAt n → b.TerminatedAt n - end Seq end Stream'