lean-ja / lean-ja/lean-by-example

付録アイデア: 配列の特定の要素を末尾に移動させる関数と、その in-place 版と、それらが一致することの証明

Open
#2,423 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Lean
Stars
188
Forks
15
Avg merge
9h 8m
Merged PRs (30d)
6

Description

証明は清書していないので無駄が多いものと思われるが。

import Lean

open Std.Do

set_option mvcgen.warning false

variable [BEq α] [Inhabited α]

def Array.moveLast (xs : Array α) (x : α) : Array α :=
  xs.filter (· != x) ++ xs.filter (· == x)

def Array.moveLastInPlace (xs : Array α) (x : α) : Array α := Id.run do
  let n := xs.size
  let mut ys : Vector α n := xs.toVector
  let mut write := 0

  for hi : i in [0:n] do
    if ys[i] != x then
      ys := ys.set! write ys[i]
      write := write + 1

  let write0 := write
  for hi : i in [write0:n] do
    ys := ys.set i x

  return ys.toArray

theorem Array.filter_extract_succ_right_of_pos {α : Type} {xs : Array α}
    {p : α -> Bool} {i : Nat} (hi : i < xs.size) (hp : p xs[i]) :
    (xs.extract 0 (i + 1)).filter p = ((xs.extract 0 i).filter p).push xs[i] := by
  rw [Array.extract_succ_right (w := by omega) hi]
  rw [Array.filter_push_of_pos (h := hp) (w := by simp [Array.size_extract]; omega)]

theorem Array.filter_extract_succ_right_of_neg {α : Type} {xs : Array α}
    {p : α -> Bool} {i : Nat} (hi : i < xs.size) (hp : ¬ p xs[i]) :
    (xs.extract 0 (i + 1)).filter p = (xs.extract 0 i).filter p := by
  rw [Array.extract_succ_right (w := by omega) hi]
  rw [Array.filter_push_of_neg (h := hp) (w := by simp [Array.size_extract]; omega)]

theorem Array.extract_set_next {α : Type} {xs : Array α}
    {i : Nat} {a : α} (hi : i < xs.size) :
    (xs.set i a hi).extract 0 (i + 1) = (xs.extract 0 i).push a := by
  rw [← Array.toList_inj]
  simp [List.extract_eq_take_drop, List.set_eq_take_append_cons_drop, hi]
  have hlen : (xs.toList.take i).length = i := by
    simp [Nat.min_eq_left (Nat.le_of_lt hi)]
  rw [show i + 1 = (xs.toList.take i).length + 1 by omega]
  rw [List.take_length_add_append]
  simp

theorem Array.extract_setIfInBounds_next {α : Type} {xs : Array α}
    {i : Nat} {a : α} (hi : i < xs.size) :
    (xs.setIfInBounds i a).extract 0 (i + 1) = (xs.extract 0 i).push a := by
  rw [Array.setIfInBounds_def]
  simp [hi, Array.extract_set_next]

theorem Vector.extract_set_next {α : Type} {n : Nat} {xs : Vector α n}
    {i : Nat} {a : α} (hi : i < n) :
    (xs.set i a).toArray.extract 0 (i + 1) = (xs.toArray.extract 0 i).push a := by
  simpa using Array.extract_set_next (xs := xs.toArray) (i := i) (a := a) (by simpa)

theorem Vector.extract_set!_next {α : Type} {n : Nat} {xs : Vector α n}
    {i : Nat} {a : α} (hi : i < n) :
    (xs.set! i a).toArray.extract 0 (i + 1) = (xs.toArray.extract 0 i).push a := by
  simpa using Array.extract_setIfInBounds_next (xs := xs.toArray) (i := i) (a := a) (by simpa)

theorem Array.toList_extract_zero {α : Type} (xs : Array α) (i : Nat) :
    (xs.extract 0 i).toList = xs.toList.take i := by
  simp [Array.toList_extract, List.extract_eq_take_drop]

theorem List.take_length_eq_self {α : Type} (xs : List α) :
    xs.take xs.length = xs := by
  induction xs with
  | nil => simp
  | cons x xs ih => simp [ih]

theorem Array.extract_all_toList {α : Type} (xs : Array α) :
    (xs.extract 0 xs.size).toList = xs.toList := by
  simp

theorem Vector.extract_set_next_toList {α : Type} {n : Nat} {xs : Vector α n}
    {i : Nat} {a : α} (hi : i < n) :
    ((xs.set i a).toArray.extract 0 (i + 1)).toList =
      (xs.toArray.extract 0 i).toList ++ [a] := by
  rw [Vector.extract_set_next hi]
  simp

theorem Vector.extract_set!_next_toList {α : Type} {n : Nat} {xs : Vector α n}
    {i : Nat} {a : α} (hi : i < n) :
    ((xs.set! i a).toArray.extract 0 (i + 1)).toList =
      (xs.toArray.extract 0 i).toList ++ [a] := by
  rw [Vector.extract_set!_next hi]
  simp

theorem Vector.getElem_set!_of_ne {α : Type} {n : Nat} {xs : Vector α n}
    {i j : Nat} {a : α} (hj : j < n) (hij : i ≠ j) :
    (xs.set! i a)[j] = xs[j] := by
  simp [Vector.set!, Array.set!_eq_setIfInBounds, hj, hij]

theorem List.replicate_succ_append {α : Type} (n : Nat) (x : α) :
    List.replicate (n + 1) x = List.replicate n x ++ [x] := by
  simpa using
    (List.replicate_append_replicate (n := n) (m := 1) (a := x)).symm

theorem List.filter_take_succ_of_pos {α : Type} {xs : List α}
    {p : α -> Bool} {i : Nat} (hi : i < xs.length) (hp : p xs[i]) :
    (xs.take (i + 1)).filter p = (xs.take i).filter p ++ [xs[i]] := by
  induction xs generalizing i with
  | nil => cases hi
  | cons a xs ih =>
      cases i with
      | zero =>
          simp at hp ⊢
          exact hp
      | succ i =>
          have hi' : i < xs.length := Nat.lt_of_succ_lt_succ hi
          have hp' : p xs[i] := hp
          have hih := ih hi' hp'
          by_cases ha : p a
          · simp [List.take, ha, hih]
          · have ha' : p a = false := by
              cases h : p a <;> simp_all
            simp [List.take, ha', hih]

theorem List.filter_take_succ_of_neg {α : Type} {xs : List α}
    {p : α -> Bool} {i : Nat} (hi : i < xs.length) (hp : ¬ p xs[i]) :
    (xs.take (i + 1)).filter p = (xs.take i).filter p := by
  induction xs generalizing i with
  | nil => cases hi
  | cons a xs ih =>
      cases i with
      | zero =>
          have hp' : p a = false := by
            cases h : p a <;> simp_all
          simp [hp']
      | succ i =>
          have hi' : i < xs.length := Nat.lt_of_succ_lt_succ hi
          have hp' : ¬ p xs[i] := hp
          have hih := ih hi' hp'
          by_cases ha : p a
          · simp [List.take, ha, hih]
          · have ha' : p a = false := by
              cases h : p a <;> simp_all
            simp [List.take, ha', hih]

theorem Array.size_sub_filter_ne_eq_count {α : Type} [BEq α] [LawfulBEq α]
    (xs : Array α) (x : α) :
    xs.size - (xs.filter (fun y => y != x)).size = xs.count x := by
  have hsize := Array.size_eq_countP_add_countP (p := fun y => y != x) (xs := xs)
  rw [Array.countP_eq_size_filter] at hsize
  have hcount :
      Array.countP (fun y => decide ¬(y != x) = true) xs = xs.count x := by
    rw [Array.count_eq_countP]
    apply Array.countP_congr
    intro y hy
    by_cases h : y == x
    · have hyx : y = x := eq_of_beq h
      subst hyx
      simp
    · have hyx : y ≠ x := by
        intro hyx
        subst hyx
        simp at h
      simp [h, hyx]
  omega

theorem Vector.extract_set!_filter_extract_succ_of_pos {α : Type} {n : Nat}
    {ys : Vector α n} {xs : Array α} {p : α -> Bool} {i write : Nat}
    (hwrite : write < n) (hi : i < xs.size) (hin : i < n)
    (hprefix : ys.toArray.extract 0 write = (xs.extract 0 i).filter p)
    (hread : ys[i] = xs[i]) (hp : p ys[i]) :
    (ys.set! write ys[i]).toArray.extract 0 (write + 1) =
      (xs.extract 0 (i + 1)).filter p := by
  rw [Vector.extract_set!_next hwrite]
  rw [hprefix]
  have hpx : p xs[i] := by simpa [hread] using hp
  rw [Array.filter_extract_succ_right_of_pos hi hpx]
  simp [hread]

attribute [grind =] Array.filter_extract_succ_right_of_pos
attribute [grind =] Array.filter_extract_succ_right_of_neg
attribute [grind =] Array.extract_setIfInBounds_next
attribute [grind =] Vector.extract_set_next
attribute [grind =] Vector.extract_set!_next
attribute [grind =] Array.toList_extract_zero
attribute [grind =] Vector.extract_set_next_toList
attribute [grind =] Vector.extract_set!_next_toList
attribute [grind =] Vector.getElem_set!_of_ne

grind_pattern Vector.extract_set!_filter_extract_succ_of_pos =>
  (ys.set! write ys[i]).toArray.extract 0 (write + 1),
  (xs.extract 0 (i + 1)).filter p

theorem scratch_moveLastInPlace_eq_moveLast
    {α : Type} [BEq α] [LawfulBEq α] [Inhabited α] (xs : Array α) (x : α) :
    Array.moveLastInPlace xs x = Array.moveLast xs x := by
  generalize h : Array.moveLastInPlace xs x = r
  apply Id.of_wp_run_eq h
  mvcgen invariants
  · ⇓⟨cursor, write, ys⟩ => by
      expose_names
      exact ⌜
        (ys.toArray.extract 0 write).toList =
          (xs.toList.take cursor.prefix.length).filter (· != x) ∧
        write = ((xs.toList.take cursor.prefix.length).filter (· != x)).length ∧
        write ≤ cursor.prefix.length ∧
        (∀ (j : Nat) (hj : j < n), cursor.prefix.length ≤ j -> ys[j] = xs[j])⌝
  · ⇓⟨cursor, ys⟩ => by
      expose_names
      exact ⌜
        (ys.toArray.extract 0 (write0 + cursor.prefix.length)).toList =
          (xs.filter (· != x)).toList ++
          List.replicate cursor.prefix.length x ∧
        write0 = (xs.filter (· != x)).toList.length ∧
        write0 + cursor.prefix.length ≤ n⌝
  case vc1.step.isTrue =>
    rename_i pref cur suff hrange b write ys hcond hinv
    rcases hinv with ⟨hprefix, hwrite, hwrite_le, htail⟩
    have hcur : cur = pref.length := by grind
    subst cur
    have hcur_lt : pref.length < xs.size := by grind
    have hread : ys[pref.length] = xs[pref.length] := by
      simpa [ys] using htail pref.length (by simpa using hcur_lt) (by omega)
    have hread' : b.snd[pref.length] = xs[pref.length] := by
      simpa [ys] using hread
    have hwrite_lt : b.fst < xs.size := by omega
    have hp : (fun y => y != x) xs[pref.length] := by
      simpa [hread] using hcond
    have hf :
        (xs.toList.take (pref.length + 1)).filter (fun y => y != x) =
          (xs.toList.take pref.length).filter (fun y => y != x) ++ [xs[pref.length]] :=
      List.filter_take_succ_of_pos (xs := xs.toList) (p := fun y => y != x)
        (i := pref.length) (by simpa using hcur_lt) hp
    refine ⟨?_, ?_, ?_, ?_⟩
    · dsimp only [write, ys]
      rw [Vector.extract_set!_next_toList (by simpa using hwrite_lt), hprefix]
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      rw [hf]
      simp [hread']
    · dsimp only [write]
      rw [hwrite]
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      rw [hf]
      simp
    · change b.fst + 1 ≤ (pref ++ [pref.length]).length
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      omega
    · intro j hj hjge
      have hge : pref.length + 1 ≤ j := by
        simpa [show (pref ++ [pref.length]).length = pref.length + 1 by simp] using hjge
      have hne : b.fst ≠ j := by omega
      dsimp only [write, ys]
      rw [Vector.getElem_set!_of_ne hj hne]
      exact htail j hj (by omega)
  case vc2.step.isFalse =>
    rename_i pref cur suff hrange b write ys hcond hinv
    rcases hinv with ⟨hprefix, hwrite, hwrite_le, htail⟩
    have hcur : cur = pref.length := by grind
    subst cur
    have hcur_lt : pref.length < xs.size := by grind
    have hread : ys[pref.length] = xs[pref.length] := by
      simpa [ys] using htail pref.length (by simpa using hcur_lt) (by omega)
    have hp : ¬ (fun y => y != x) xs[pref.length] := by
      intro hp
      exact hcond (by simpa [hread] using hp)
    have hf :
        (xs.toList.take (pref.length + 1)).filter (fun y => y != x) =
          (xs.toList.take pref.length).filter (fun y => y != x) :=
      List.filter_take_succ_of_neg (xs := xs.toList) (p := fun y => y != x)
        (i := pref.length) (by simpa using hcur_lt) hp
    refine ⟨?_, ?_, ?_, ?_⟩
    · dsimp only [write, ys]
      rw [hprefix]
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      rw [hf]
    · dsimp only [write]
      rw [hwrite]
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      rw [hf]
    · change b.fst ≤ (pref ++ [pref.length]).length
      rw [show (pref ++ [pref.length]).length = pref.length + 1 by simp]
      omega
    · intro j hj hjge
      exact htail j hj (by
        have hge : pref.length + 1 ≤ j := by
          simpa [show (pref ++ [pref.length]).length = pref.length + 1 by simp] using hjge
        omega)
  case vc3.pre =>
    refine ⟨by simp, by simp, by simp, ?_⟩
    intro j hj _
    rfl
  case vc4.step =>
    rename_i r_1 write0 hfirst pref cur suff hrange b hinv
    rcases hinv with ⟨hprefix, hwrite0, hle⟩
    have hcur : cur = write0 + pref.length := by grind
    subst cur
    have hcur_lt : write0 + pref.length < xs.size := by grind
    refine ⟨?_, hwrite0, ?_⟩
    · rw [show write0 + (pref ++ [write0 + pref.length]).length =
          write0 + pref.length + 1 by simp [Nat.add_assoc]]
      rw [Vector.extract_set_next_toList (xs := b) (i := write0 + pref.length)
        (a := x) (by simpa using hcur_lt)]
      rw [hprefix]
      rw [show (pref ++ [write0 + pref.length]).length = pref.length + 1 by simp]
      rw [List.replicate_succ_append]
      simp [List.append_assoc]
    · rw [show (pref ++ [write0 + pref.length]).length = pref.length + 1 by simp]
      omega
  case vc5.post.success.pre =>
    rename_i n ys r_1 write0 hfirst
    rcases hfirst with ⟨hprefix, hwrite, hle, htail⟩
    have htake : xs.toList.take [:n].toList.length = xs.toList := by
      rw [show [:n].toList.length = xs.toList.length by simp [n]]
      exact List.take_length_eq_self xs.toList
    refine ⟨?_, ?_, ?_⟩
    · dsimp only [write0]
      simp only [List.length_nil, Nat.add_zero, List.replicate, List.append_nil]
      rw [hprefix]
      rw [htake]
      simp
    · dsimp only [write0]
      rw [hwrite]
      rw [htake]
      simp
    · dsimp only [write0]
      simpa [n] using hle
  case vc6.post.success.post.success =>
    rename_i n ys r_1 write0 hfirst r hsecond
    rcases hsecond with ⟨hprefix, hwrite0, hle⟩
    rw [← Array.toList_inj]
    have hwrite0_size : write0 = (xs.filter (fun y => y != x)).size := by
      rw [hwrite0]
      simpa using (Array.length_toList (xs := xs.filter (fun y => y != x)))
    have hend : write0 + [write0:n].toList.length = n := by
      simp
      omega
    have hrlist :
        r.toArray.toList =
          (xs.filter (fun y => y != x)).toList ++
          List.replicate [write0:n].toList.length x := by
      have hfull :
          (r.toArray.extract 0 (write0 + [write0:n].toList.length)).toList =
            r.toArray.toList := by
        rw [hend]
        simpa using Array.extract_all_toList r.toArray
      rw [← hfull]
      exact hprefix
    rw [hrlist]
    unfold Array.moveLast
    rw [Array.filter_beq (xs := xs) x]
    simp [Array.toList_append, Array.toList_replicate]
    congr 1
    rw [show n = xs.size by simp [n]]
    rw [hwrite0_size]
    exact Array.size_sub_filter_ne_eq_count xs x

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied Lean definitions Array.moveLast and Array.moveLastInPlace, then inspect the supporting lemmas leading to scratch_moveLastInPlace_eq_moveLast. Check how appendix examples are organized in the repository before deciding where this material belongs. Done means the example is integrated and the equality proof is accepted by Lean.

Written by the indexing model from the issue text.

Assessment

Domain
documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.