/- Copyright (c) 2025 Lean FRO, LLC. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Markus Himmel -/ module prelude public import Init.Data.String.Basic import Init.Data.ByteArray.Lemmas import Init.Data.String.Lemmas.Basic /-! # `Splits` predicates on `String.ValidPos` and `String.Slice.Pos`. We introduce the predicate `p.Splits t₁ t₂` for a position `p` on a string or slice `s`, which means that `s = t₁ ++ t₂` with `p` lying between the two parts. This is a useful primitive when verifying string operations. -/ public section namespace String /-- We say that `p` splits `s` into `t₁` and `t₂` if `s = t₁ ++ t₂` and `p` is the position between `t₁` and `t₂`. -/ structure ValidPos.Splits {s : String} (p : s.ValidPos) (t₁ t₂ : String) : Prop where eq_append : s = t₁ ++ t₂ offset_eq_rawEndPos : p.offset = t₁.rawEndPos /-- We say that `p` splits `s` into `t₁` and `t₂` if `s = t₁ ++ t₂` and `p` is the position between `t₁` and `t₂`. -/ structure Slice.Pos.Splits {s : Slice} (p : s.Pos) (t₁ t₂ : String) : Prop where eq_append : s.copy = t₁ ++ t₂ offset_eq_rawEndPos : p.offset = t₁.rawEndPos @[simp] theorem ValidPos.splits_cast_iff {s₁ s₂ : String} {h : s₁ = s₂} {p : s₁.ValidPos} {t₁ t₂ : String} : (p.cast h).Splits t₁ t₂ ↔ p.Splits t₁ t₂ := by subst h; simp theorem ValidPos.Splits.cast {s₁ s₂ : String} {p : s₁.ValidPos} {t₁ t₂ : String} (h : s₁ = s₂) : p.Splits t₁ t₂ → (p.cast h).Splits t₁ t₂ := splits_cast_iff.mpr @[simp] theorem Slice.Pos.splits_cast_iff {s₁ s₂ : Slice} {h : s₁ = s₂} {p : s₁.Pos} {t₁ t₂ : String} : (p.cast h).Splits t₁ t₂ ↔ p.Splits t₁ t₂ := by subst h; simp theorem Slice.Pos.Splits.cast {s₁ s₂ : Slice} {p : s₁.Pos} {t₁ t₂ : String} (h : s₁ = s₂) : p.Splits t₁ t₂ → (p.cast h).Splits t₁ t₂ := splits_cast_iff.mpr theorem Slice.Pos.Splits.toCopy {s : Slice} {p : s.Pos} {t₁ t₂ : String} (h : p.Splits t₁ t₂) : p.toCopy.Splits t₁ t₂ where eq_append := h.eq_append offset_eq_rawEndPos := by simpa using h.offset_eq_rawEndPos theorem Slice.Pos.splits_of_splits_toCopy {s : Slice} {p : s.Pos} {t₁ t₂ : String} (h : p.toCopy.Splits t₁ t₂) : p.Splits t₁ t₂ where eq_append := h.eq_append offset_eq_rawEndPos := by simpa using h.offset_eq_rawEndPos @[simp] theorem Slice.Pos.splits_toCopy_iff {s : Slice} {p : s.Pos} {t₁ t₂ : String} : p.toCopy.Splits t₁ t₂ ↔ p.Splits t₁ t₂ := ⟨splits_of_splits_toCopy, (·.toCopy)⟩ @[simp] theorem ValidPos.splits_toSlice_iff {s : String} {p : s.ValidPos} {t₁ t₂ : String} : p.toSlice.Splits t₁ t₂ ↔ p.Splits t₁ t₂ := by rw [← Slice.Pos.splits_toCopy_iff, p.toCopy_toSlice_eq_cast, splits_cast_iff] theorem ValidPos.Splits.toSlice {s : String} {p : s.ValidPos} {t₁ t₂ : String} (h : p.Splits t₁ t₂) : p.toSlice.Splits t₁ t₂ := splits_toSlice_iff.mpr h theorem ValidPos.splits {s : String} (p : s.ValidPos) : p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where eq_append := by simp [← bytes_inj, Slice.bytes_copy, ← size_bytes] offset_eq_rawEndPos := by simp theorem Slice.Pos.splits {s : Slice} (p : s.Pos) : p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where eq_append := copy_eq_copy_sliceTo offset_eq_rawEndPos := by simp theorem ValidPos.Splits.bytes_left_eq {s : String} {p : s.ValidPos} {t₁ t₂} (h : p.Splits t₁ t₂) : t₁.bytes = s.bytes.extract 0 p.offset.byteIdx := by simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_left] theorem ValidPos.Splits.bytes_right_eq {s : String} {p : s.ValidPos} {t₁ t₂} (h : p.Splits t₁ t₂) : t₂.bytes = s.bytes.extract p.offset.byteIdx s.utf8ByteSize := by simp [h.eq_append, h.offset_eq_rawEndPos, ByteArray.extract_append_eq_right] theorem ValidPos.Splits.eq_left {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ := by rw [← String.bytes_inj, h₁.bytes_left_eq, h₂.bytes_left_eq] theorem ValidPos.Splits.eq_right {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₂ = t₄ := by rw [← String.bytes_inj, h₁.bytes_right_eq, h₂.bytes_right_eq] theorem ValidPos.Splits.eq {s : String} {p : s.ValidPos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ ∧ t₂ = t₄ := ⟨h₁.eq_left h₂, h₁.eq_right h₂⟩ theorem Slice.Pos.Splits.eq_left {s : Slice} {p : s.Pos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ := (splits_toCopy_iff.2 h₁).eq_left (splits_toCopy_iff.2 h₂) theorem Slice.Pos.Splits.eq_right {s : Slice} {p : s.Pos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₂ = t₄ := (splits_toCopy_iff.2 h₁).eq_right (splits_toCopy_iff.2 h₂) theorem Slice.Pos.Splits.eq {s : Slice} {p : s.Pos} {t₁ t₂ t₃ t₄} (h₁ : p.Splits t₁ t₂) (h₂ : p.Splits t₃ t₄) : t₁ = t₃ ∧ t₂ = t₄ := (splits_toCopy_iff.2 h₁).eq (splits_toCopy_iff.2 h₂) @[simp] theorem splits_endValidPos (s : String) : s.endValidPos.Splits s "" where eq_append := by simp offset_eq_rawEndPos := by simp @[simp] theorem splits_endValidPos_iff {s : String} : s.endValidPos.Splits t₁ t₂ ↔ t₁ = s ∧ t₂ = "" := ⟨fun h => ⟨h.eq_left s.splits_endValidPos, h.eq_right s.splits_endValidPos⟩, by rintro ⟨rfl, rfl⟩; exact t₁.splits_endValidPos⟩ theorem ValidPos.Splits.eq_endValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) : p = s.endValidPos ↔ t₂ = "" := ⟨fun h' => h.eq_right (h' ▸ s.splits_endValidPos), by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos, h.eq_append]⟩ theorem splits_startValidPos (s : String) : s.startValidPos.Splits "" s where eq_append := by simp offset_eq_rawEndPos := by simp @[simp] theorem splits_startValidPos_iff {s : String} : s.startValidPos.Splits t₁ t₂ ↔ t₁ = "" ∧ t₂ = s := ⟨fun h => ⟨h.eq_left s.splits_startValidPos, h.eq_right s.splits_startValidPos⟩, by rintro ⟨rfl, rfl⟩; exact t₂.splits_startValidPos⟩ theorem ValidPos.Splits.eq_startValidPos_iff {s : String} {p : s.ValidPos} (h : p.Splits t₁ t₂) : p = s.startValidPos ↔ t₁ = "" := ⟨fun h' => h.eq_left (h' ▸ s.splits_startValidPos), by rintro rfl; simp [ValidPos.ext_iff, h.offset_eq_rawEndPos]⟩ @[simp] theorem Slice.splits_endPos (s : Slice) : s.endPos.Splits s.copy "" where eq_append := by simp offset_eq_rawEndPos := by simp @[simp] theorem Slice.splits_endPos_iff {s : Slice} : s.endPos.Splits t₁ t₂ ↔ t₁ = s.copy ∧ t₂ = "" := by rw [← Pos.splits_toCopy_iff, ← endValidPos_copy, splits_endValidPos_iff] theorem Slice.Pos.Splits.eq_endPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) : p = s.endPos ↔ t₂ = "" := by rw [← toCopy_inj, ← endValidPos_copy, h.toCopy.eq_endValidPos_iff] @[simp] theorem Slice.splits_startPos (s : Slice) : s.startPos.Splits "" s.copy where eq_append := by simp offset_eq_rawEndPos := by simp @[simp] theorem Slice.splits_startPos_iff {s : Slice} : s.startPos.Splits t₁ t₂ ↔ t₁ = "" ∧ t₂ = s.copy := by rw [← Pos.splits_toCopy_iff, ← startValidPos_copy, splits_startValidPos_iff] theorem Slice.Pos.Splits.eq_startPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t₁ t₂) : p = s.startPos ↔ t₁ = "" := by rw [← toCopy_inj, ← startValidPos_copy, h.toCopy.eq_startValidPos_iff] theorem ValidPos.splits_next_right {s : String} (p : s.ValidPos) (hp : p ≠ s.endValidPos) : p.Splits (s.sliceTo p).copy (singleton (p.get hp) ++ (s.sliceFrom (p.next hp)).copy) where eq_append := by simpa [← append_assoc] using p.eq_copy_sliceTo_append_get hp offset_eq_rawEndPos := by simp theorem ValidPos.splits_next {s : String} (p : s.ValidPos) (hp : p ≠ s.endValidPos) : (p.next hp).Splits ((s.sliceTo p).copy ++ singleton (p.get hp)) (s.sliceFrom (p.next hp)).copy where eq_append := p.eq_copy_sliceTo_append_get hp offset_eq_rawEndPos := by simp theorem Slice.Pos.splits_next_right {s : Slice} (p : s.Pos) (hp : p ≠ s.endPos) : p.Splits (s.sliceTo p).copy (singleton (p.get hp) ++ (s.sliceFrom (p.next hp)).copy) where eq_append := by simpa [← append_assoc] using p.copy_eq_copy_sliceTo_append_get hp offset_eq_rawEndPos := by simp theorem Slice.Pos.splits_next {s : Slice} (p : s.Pos) (hp : p ≠ s.endPos) : (p.next hp).Splits ((s.sliceTo p).copy ++ singleton (p.get hp)) (s.sliceFrom (p.next hp)).copy where eq_append := p.copy_eq_copy_sliceTo_append_get hp offset_eq_rawEndPos := by simp theorem ValidPos.Splits.exists_eq_singleton_append {s : String} {p : s.ValidPos} (hp : p ≠ s.endValidPos) (h : p.Splits t₁ t₂) : ∃ t₂', t₂ = singleton (p.get hp) ++ t₂' := ⟨(s.sliceFrom (p.next hp)).copy, h.eq_right (p.splits_next_right hp)⟩ theorem ValidPos.Splits.exists_eq_append_singleton {s : String} {p : s.ValidPos} (hp : p ≠ s.endValidPos) (h : (p.next hp).Splits t₁ t₂) : ∃ t₁', t₁ = t₁' ++ singleton (p.get hp) := ⟨(s.sliceTo p).copy, h.eq_left (p.splits_next hp)⟩ theorem Slice.Pos.Splits.exists_eq_singleton_append {s : Slice} {p : s.Pos} (hp : p ≠ s.endPos) (h : p.Splits t₁ t₂) : ∃ t₂', t₂ = singleton (p.get hp) ++ t₂' := ⟨(s.sliceFrom (p.next hp)).copy, h.eq_right (p.splits_next_right hp)⟩ theorem Slice.Pos.Splits.exists_eq_append_singleton {s : Slice} {p : s.Pos} (hp : p ≠ s.endPos) (h : (p.next hp).Splits t₁ t₂) : ∃ t₁', t₁ = t₁' ++ singleton (p.get hp) := ⟨(s.sliceTo p).copy, h.eq_left (p.splits_next hp)⟩ theorem ValidPos.Splits.ne_endValidPos_of_singleton {s : String} {p : s.ValidPos} (h : p.Splits t₁ (singleton c ++ t₂)) : p ≠ s.endValidPos := by simp [h.eq_endValidPos_iff] theorem ValidPos.Splits.ne_startValidPos_of_singleton {s : String} {p : s.ValidPos} (h : p.Splits (t₁ ++ singleton c) t₂) : p ≠ s.startValidPos := by simp [h.eq_startValidPos_iff] theorem Slice.Pos.Splits.ne_endPos_of_singleton {s : Slice} {p : s.Pos} (h : p.Splits t₁ (singleton c ++ t₂)) : p ≠ s.endPos := by simp [h.eq_endPos_iff] theorem Slice.Pos.Splits.ne_startPos_of_singleton {s : Slice} {p : s.Pos} (h : p.Splits (t₁ ++ singleton c) t₂) : p ≠ s.startPos := by simp [h.eq_startPos_iff] /-- You might want to invoke `ValidPos.Splits.exists_eq_singleton_append` to be able to apply this. -/ theorem ValidPos.Splits.next {s : String} {p : s.ValidPos} (h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endValidPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by generalize h.ne_endValidPos_of_singleton = hp obtain ⟨rfl, rfl, rfl⟩ := by simpa using h.eq (splits_next_right p hp) exact splits_next p hp /-- You might want to invoke `Slice.Pos.Splits.exists_eq_singleton_append` to be able to apply this. -/ theorem Slice.Pos.Splits.next {s : Slice} {p : s.Pos} (h : p.Splits t₁ (singleton c ++ t₂)) : (p.next h.ne_endPos_of_singleton).Splits (t₁ ++ singleton c) t₂ := by generalize h.ne_endPos_of_singleton = hp obtain ⟨rfl, rfl, rfl⟩ := by simpa using h.eq (splits_next_right p hp) exact splits_next p hp end String