l3lab/ntp-mathlib huggingface dataset:
set_option linter.deprecated false in
theorem nthLe_succ_scanl {i : ℕ} {h : i + 1 < (scanl f b l).length} :
    (scanl f b l).nthLe (i + 1) h =
      f ((scanl f b l).nthLe i (Nat.lt_of_succ_lt h))
        (l.nthLe i (Nat.lt_of_succ_lt_succ (lt_of_lt_of_le h (le_of_eq (length_scanl b l))))) := by
  induction i generalizing b l with
  | zero =>
    cases l
    · simp only [length, zero_eq, lt_self_iff_false] at h
    · simp [scanl_cons, singleton_append, nthLe_zero_scanl, nthLe_cons]
  | succ i hi =>
    cases l
    · simp only [length, add_lt_iff_neg_right, scanl_nil] at h
      exact absurd h (not_lt_of_lt Nat.succ_pos')
    · simp_rw [scanl_cons]
      rw [nthLe_append_right]
      · simp only [length, zero_add 1, succ_add_sub_one, hi]; rfl
      · 

Proof state:
case succ.cons.h₁
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : β → α → β
a : α
i : ℕ
hi :
  ∀ {b : β} {l : List α} {h : i + 1 < length (scanl f b l)},
    nthLe (scanl f b l) (i + 1) h =
      f (nthLe (scanl f b l) i (_ : i < length (scanl f b l))) (nthLe l i (_ : i < length l))
b : β
head✝ : α
tail✝ : List α
h : succ i + 1 < length (scanl f b (head✝ :: tail✝))
⊢ length [b] ≤ succ i + 1

Next tactic:
simp only [length, Nat.zero_le, le_add_iff_nonneg_left]
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
a : α
l : List α
⊢ scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l

Next tactic:
simp only [scanr, foldr, cons.injEq, and_true]
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  simp only [scanr, foldr, cons.injEq, and_true]
  

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
a : α
l : List α
⊢ f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) l).1 = f a (foldr f b l)

Next tactic:
induction l generalizing a with
  | nil => rfl
  | cons hd tl ih => simp only [foldr, ih]
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  simp only [scanr, foldr, cons.injEq, and_true]
  induction l generalizing a with
  

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
a : α
⊢ f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) []).1 = f a (foldr f b [])

Next tactic:
| nil => rfl
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  simp only [scanr, foldr, cons.injEq, and_true]
  induction l generalizing a with
  | nil => 

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
a : α
⊢ f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) []).1 = f a (foldr f b [])

Next tactic:
rfl
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  simp only [scanr, foldr, cons.injEq, and_true]
  induction l generalizing a with
  | nil => rfl
  

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
hd : α
tl : List α
ih : ∀ (a : α), f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) tl).1 = f a (foldr f b tl)
a : α
⊢ f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) (hd :: tl)).1 = f a (foldr f b (hd :: tl))

Next tactic:
| cons hd tl ih => simp only [foldr, ih]
@[simp]
theorem scanr_cons (f : α → β → β) (b : β) (a : α) (l : List α) :
    scanr f b (a :: l) = foldr f b (a :: l) :: scanr f b l := by
  simp only [scanr, foldr, cons.injEq, and_true]
  induction l generalizing a with
  | nil => rfl
  | cons hd tl ih => 

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
b : β
hd : α
tl : List α
ih : ∀ (a : α), f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) tl).1 = f a (foldr f b tl)
a : α
⊢ f a (foldr (fun a x => (f a x.1, x.1 :: x.2)) (b, []) (hd :: tl)).1 = f a (foldr f b (hd :: tl))

Next tactic:
simp only [foldr, ih]
theorem foldl1_eq_foldr1 : ∀ a b l, foldl f a (l ++ [b]) = foldr f b (a :: l)
  | a, b, nil => rfl
  | a, b, c :: l => by
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b c : α
l : List α
⊢ foldl f a (c :: l ++ [b]) = foldr f b (a :: c :: l)

Next tactic:
simp only [cons_append, foldl_cons, foldr_cons, foldl1_eq_foldr1 _ _ l]
theorem foldl1_eq_foldr1 : ∀ a b l, foldl f a (l ++ [b]) = foldr f b (a :: l)
  | a, b, nil => rfl
  | a, b, c :: l => by
    simp only [cons_append, foldl_cons, foldr_cons, foldl1_eq_foldr1 _ _ l]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b c : α
l : List α
⊢ f (f a c) (foldr f b l) = f a (f c (foldr f b l))

Next tactic:
rw [hassoc]
theorem foldl_eq_of_comm_of_assoc : ∀ a b l, foldl f a (b :: l) = f b (foldl f a l)
  | a, b, nil => hcomm a b
  | a, b, c :: l => by
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b c : α
l : List α
⊢ foldl f a (b :: c :: l) = f b (foldl f a (c :: l))

Next tactic:
simp only [foldl_cons]
theorem foldl_eq_of_comm_of_assoc : ∀ a b l, foldl f a (b :: l) = f b (foldl f a l)
  | a, b, nil => hcomm a b
  | a, b, c :: l => by
    simp only [foldl_cons]
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b c : α
l : List α
⊢ foldl f (f (f a b) c) l = f b (foldl f (f a c) l)

Next tactic:
rw [← foldl_eq_of_comm_of_assoc .., right_comm _ hcomm hassoc]
theorem foldl_eq_of_comm_of_assoc : ∀ a b l, foldl f a (b :: l) = f b (foldl f a l)
  | a, b, nil => hcomm a b
  | a, b, c :: l => by
    simp only [foldl_cons]
    rw [← foldl_eq_of_comm_of_assoc .., right_comm _ hcomm hassoc]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b c : α
l : List α
⊢ foldl f (f (f a c) b) l = foldl f (f a c) (b :: l)

Next tactic:
rfl
theorem foldl_eq_foldr : ∀ a l, foldl f a l = foldr f a l
  | a, nil => rfl
  | a, b :: l => by
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b : α
l : List α
⊢ foldl f a (b :: l) = foldr f a (b :: l)

Next tactic:
simp only [foldr_cons, foldl_eq_of_comm_of_assoc hcomm hassoc]
theorem foldl_eq_foldr : ∀ a l, foldl f a l = foldr f a l
  | a, nil => rfl
  | a, b :: l => by
    simp only [foldr_cons, foldl_eq_of_comm_of_assoc hcomm hassoc]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → α → α
hcomm : Commutative f
hassoc : Associative f
a b : α
l : List α
⊢ f b (foldl f a l) = f b (foldr f a l)

Next tactic:
rw [foldl_eq_foldr a l]
theorem foldl_eq_of_comm' : ∀ a b l, foldl f a (b :: l) = f (foldl f a l) b
  | a, b, [] => rfl
  | a, b, c :: l => by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → α
hf : ∀ (a : α) (b c : β), f (f a b) c = f (f a c) b
a : α
b c : β
l : List β
⊢ foldl f a (b :: c :: l) = f (foldl f a (c :: l)) b

Next tactic:
rw [foldl, foldl, foldl, ← foldl_eq_of_comm' .., foldl, hf]
theorem foldl_eq_foldr' : ∀ a l, foldl f a l = foldr (flip f) a l
  | a, [] => rfl
  | a, b :: l => by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → α
hf : ∀ (a : α) (b c : β), f (f a b) c = f (f a c) b
a : α
b : β
l : List β
⊢ foldl f a (b :: l) = foldr (flip f) a (b :: l)

Next tactic:
rw [foldl_eq_of_comm' hf, foldr, foldl_eq_foldr' ..]
theorem foldl_eq_foldr' : ∀ a l, foldl f a l = foldr (flip f) a l
  | a, [] => rfl
  | a, b :: l => by rw [foldl_eq_of_comm' hf, foldr, foldl_eq_foldr' ..]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → α
hf : ∀ (a : α) (b c : β), f (f a b) c = f (f a c) b
a : α
b : β
l : List β
⊢ f (foldr (flip f) a l) b = flip f b (foldr (flip f) a l)

Next tactic:
rfl
theorem foldr_eq_of_comm' : ∀ a b l, foldr f a (b :: l) = foldr f (f b a) l
  | a, b, [] => rfl
  | a, b, c :: l => by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
hf : ∀ (a b : α) (c : β), f a (f b c) = f b (f a c)
a : β
b c : α
l : List α
⊢ foldr f a (b :: c :: l) = foldr f (f b a) (c :: l)

Next tactic:
rw [foldr, foldr, foldr, hf, ← foldr_eq_of_comm' ..]
theorem foldr_eq_of_comm' : ∀ a b l, foldr f a (b :: l) = foldr f (f b a) l
  | a, b, [] => rfl
  | a, b, c :: l => by rw [foldr, foldr, foldr, hf, ← foldr_eq_of_comm' ..]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
f : α → β → β
hf : ∀ (a b : α) (c : β), f a (f b c) = f b (f a c)
a : β
b c : α
l : List α
⊢ f c (f b (foldr f a l)) = f c (foldr f a (b :: l))

Next tactic:
rfl
theorem foldl_assoc : ∀ {l : List α} {a₁ a₂}, (l <*> a₁ ⋆ a₂) = a₁ ⋆ l <*> a₂
  | [], a₁, a₂ => rfl
  | a :: l, a₁, a₂ =>
    calc
      ((a :: l) <*> a₁ ⋆ a₂) = l <*> a₁ ⋆ a₂ ⋆ a := by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
op : α → α → α
ha : IsAssociative α op
hc : IsCommutative α op
a : α
l : List α
a₁ a₂ : α
⊢ ((a :: l) <*> op a₁ a₂) = l <*> op a₁ (op a₂ a)

Next tactic:
simp only [foldl_cons, ha.assoc]
theorem foldl_assoc : ∀ {l : List α} {a₁ a₂}, (l <*> a₁ ⋆ a₂) = a₁ ⋆ l <*> a₂
  | [], a₁, a₂ => rfl
  | a :: l, a₁, a₂ =>
    calc
      ((a :: l) <*> a₁ ⋆ a₂) = l <*> a₁ ⋆ a₂ ⋆ a := by simp only [foldl_cons, ha.assoc]
      _ = a₁ ⋆ (a :: l) <*> a₂ := by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
op : α → α → α
ha : IsAssociative α op
hc : IsCommutative α op
a : α
l : List α
a₁ a₂ : α
⊢ (l <*> op a₁ (op a₂ a)) = op a₁ ((a :: l) <*> a₂)

Next tactic:
rw [foldl_assoc, foldl_cons]
theorem foldl_op_eq_op_foldr_assoc :
    ∀ {l : List α} {a₁ a₂}, ((l <*> a₁) ⋆ a₂) = a₁ ⋆ l.foldr (· ⋆ ·) a₂
  | [], a₁, a₂ => rfl
  | a :: l, a₁, a₂ => by
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
op : α → α → α
ha : IsAssociative α op
hc : IsCommutative α op
a : α
l : List α
a₁ a₂ : α
⊢ op ((a :: l) <*> a₁) a₂ = op a₁ (foldr (fun x x_1 => op x x_1) a₂ (a :: l))

Next tactic:
simp only [foldl_cons, foldr_cons, foldl_assoc, ha.assoc]
theorem foldl_op_eq_op_foldr_assoc :
    ∀ {l : List α} {a₁ a₂}, ((l <*> a₁) ⋆ a₂) = a₁ ⋆ l.foldr (· ⋆ ·) a₂
  | [], a₁, a₂ => rfl
  | a :: l, a₁, a₂ => by
    simp only [foldl_cons, foldr_cons, foldl_assoc, ha.assoc]; 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
op : α → α → α
ha : IsAssociative α op
hc : IsCommutative α op
a : α
l : List α
a₁ a₂ : α
⊢ op a₁ (op (l <*> a) a₂) = op a₁ (op a (foldr (fun x x_1 => op x x_1) a₂ l))

Next tactic:
rw [foldl_op_eq_op_foldr_assoc]
theorem foldl_assoc_comm_cons {l : List α} {a₁ a₂} : ((a₁ :: l) <*> a₂) = a₁ ⋆ l <*> a₂ := by
  

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
op : α → α → α
ha : IsAssociative α op
hc : IsCommutative α op
l : List α
a₁ a₂ : α
⊢ ((a₁ :: l) <*> a₂) = op a₁ (l <*> a₂)

Next tactic:
rw [foldl_cons, hc.comm, foldl_assoc]
theorem foldrM_eq_foldr (f : α → β → m β) (b l) :
    foldrM f b l = foldr (fun a mb => mb >>= f a) (pure b) l := by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : α → β → m β
b : β
l : List α
⊢ foldrM f b l = foldr (fun a mb => mb >>= f a) (pure b) l

Next tactic:
induction l
theorem foldrM_eq_foldr (f : α → β → m β) (b l) :
    foldrM f b l = foldr (fun a mb => mb >>= f a) (pure b) l := by induction l <;> 

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : α → β → m β
b : β
⊢ foldrM f b [] = foldr (fun a mb => mb >>= f a) (pure b) []

Next tactic:
simp [*]
theorem foldrM_eq_foldr (f : α → β → m β) (b l) :
    foldrM f b l = foldr (fun a mb => mb >>= f a) (pure b) l := by induction l <;> 

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : α → β → m β
b : β
head✝ : α
tail✝ : List α
tail_ih✝ : foldrM f b tail✝ = foldr (fun a mb => mb >>= f a) (pure b) tail✝
⊢ foldrM f b (head✝ :: tail✝) = foldr (fun a mb => mb >>= f a) (pure b) (head✝ :: tail✝)

Next tactic:
simp [*]
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
l : List α
⊢ List.foldlM f b l =
    foldl
      (fun mb a => do
        let b ← mb
        f b a)
      (pure b) l

Next tactic:
suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by 

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
l : List α
h :
  ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b l) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb l
⊢ List.foldlM f b l =
    foldl
      (fun mb a => do
        let b ← mb
        f b a)
      (pure b) l

Next tactic:
simp [← h (pure b)]
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
l : List α
⊢ ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b l) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb l

Next tactic:
induction l with
  | nil => intro; simp
  | cons _ _ l_ih => intro; simp only [List.foldlM, foldl, ← l_ih, functor_norm]
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
⊢ ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b []) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb []

Next tactic:
| nil => intro; simp
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  | nil => 

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
⊢ ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b []) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb []

Next tactic:
intro
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  | nil => intro; 

Proof state:
case nil
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
mb✝ : m β
⊢ (do
      let b ← mb✝
      List.foldlM f b []) =
    foldl
      (fun mb a => do
        let b ← mb
        f b a)
      mb✝ []

Next tactic:
simp
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  | nil => intro; simp
  

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
head✝ : α
tail✝ : List α
l_ih :
  ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b tail✝) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb tail✝
⊢ ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b (head✝ :: tail✝)) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb (head✝ :: tail✝)

Next tactic:
| cons _ _ l_ih => intro; simp only [List.foldlM, foldl, ← l_ih, functor_norm]
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  | nil => intro; simp
  | cons _ _ l_ih => 

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
head✝ : α
tail✝ : List α
l_ih :
  ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b tail✝) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb tail✝
⊢ ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b (head✝ :: tail✝)) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb (head✝ :: tail✝)

Next tactic:
intro
theorem foldlM_eq_foldl (f : β → α → m β) (b l) :
    List.foldlM f b l = foldl (fun mb a => mb >>= fun b => f b a) (pure b) l := by
  suffices h :
    ∀ mb : m β, (mb >>= fun b => List.foldlM f b l) = foldl (fun mb a => mb >>= fun b => f b a) mb l
    by simp [← h (pure b)]
  induction l with
  | nil => intro; simp
  | cons _ _ l_ih => intro; 

Proof state:
case cons
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
m : Type v → Type w
inst✝¹ : Monad m
inst✝ : LawfulMonad m
f : β → α → m β
b : β
head✝ : α
tail✝ : List α
l_ih :
  ∀ (mb : m β),
    (do
        let b ← mb
        List.foldlM f b tail✝) =
      foldl
        (fun mb a => do
          let b ← mb
          f b a)
        mb tail✝
mb✝ : m β
⊢ (do
      let b ← mb✝
      List.foldlM f b (head✝ :: tail✝)) =
    foldl
      (fun mb a => do
        let b ← mb
        f b a)
      mb✝ (head✝ :: tail✝)

Next tactic:
simp only [List.foldlM, foldl, ← l_ih, functor_norm]
@[simp]
theorem splitAt_eq_take_drop (n : ℕ) (l : List α) : splitAt n l = (take n l, drop n l) := by
  by_cases h : n < l.length <;> rw [splitAt, go_eq_take_drop]
  · rw [if_pos h]; rfl
  · rw [if_neg h, take_all_of_le <| le_of_not_lt h, drop_eq_nil_of_le <| le_of_not_lt h]
where
  go_eq_take_drop (n : ℕ) (l xs : List α) (acc : Array α) : splitAt.go l xs n acc =
      if n < xs.length then (acc.toList ++ take n xs, drop n xs) else (l, []) := by
    

Proof state:
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
p : α → Bool
xs✝ ys : List α
ls : List (List α)
f : List α → List α
n✝ : ℕ
l✝ : List α
n : ℕ
l xs : List α
acc : Array α
⊢ splitAt.go l xs n acc = if n < length xs then (Array.toList acc ++ take n xs, drop n xs) else (l, [])

Next tactic:
split_ifs with h
@[simp]
theorem splitAt_eq_take_drop (n : ℕ) (l : List α) : splitAt n l = (take n l, drop n l) := by
  by_cases h : n < l.length <;> rw [splitAt, go_eq_take_drop]
  · rw [if_pos h]; rfl
  · rw [if_neg h, take_all_of_le <| le_of_not_lt h, drop_eq_nil_of_le <| le_of_not_lt h]
where
  go_eq_take_drop (n : ℕ) (l xs : List α) (acc : Array α) : splitAt.go l xs n acc =
      if n < xs.length then (acc.toList ++ take n xs, drop n xs) else (l, []) := by
    split_ifs with h
    · 

Proof state:
case pos
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
p : α → Bool
xs✝ ys : List α
ls : List (List α)
f : List α → List α
n✝ : ℕ
l✝ : List α
n : ℕ
l xs : List α
acc : Array α
h : n < length xs
⊢ splitAt.go l xs n acc = (Array.toList acc ++ take n xs, drop n xs)

Next tactic:
induction n generalizing xs acc with
      | zero =>
        rw [splitAt.go, take, drop, append_nil]
        · intros h₁; rw [h₁] at h; contradiction
        · intros; contradiction
      | succ _ ih =>
        cases xs with
        | nil => contradiction
        | cons hd tl =>
          rw [length, succ_eq_add_one] at h
          rw [splitAt.go, take, drop, append_cons, Array.toList_eq, ← Array.push_data,
            ← Array.toList_eq]
          exact ih _ _ <| lt_of_add_lt_add_right h
@[simp]
theorem splitAt_eq_take_drop (n : ℕ) (l : List α) : splitAt n l = (take n l, drop n l) := by
  by_cases h : n < l.length <;> rw [splitAt, go_eq_take_drop]
  · rw [if_pos h]; rfl
  · rw [if_neg h, take_all_of_le <| le_of_not_lt h, drop_eq_nil_of_le <| le_of_not_lt h]
where
  go_eq_take_drop (n : ℕ) (l xs : List α) (acc : Array α) : splitAt.go l xs n acc =
      if n < xs.length then (acc.toList ++ take n xs, drop n xs) else (l, []) := by
    split_ifs with h
    · induction n generalizing xs acc with
      

Proof state:
case pos.zero
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
p : α → Bool
xs✝ ys : List α
ls : List (List α)
f : List α → List α
n : ℕ
l✝ l xs : List α
acc : Array α
h : zero < length xs
⊢ splitAt.go l xs zero acc = (Array.toList acc ++ take zero xs, drop zero xs)

Next tactic:
| zero =>
        rw [splitAt.go, take, drop, append_nil]
        · intros h₁; rw [h₁] at h; contradiction
        · intros; contradiction
@[simp]
theorem splitAt_eq_take_drop (n : ℕ) (l : List α) : splitAt n l = (take n l, drop n l) := by
  by_cases h : n < l.length <;> rw [splitAt, go_eq_take_drop]
  · rw [if_pos h]; rfl
  · rw [if_neg h, take_all_of_le <| le_of_not_lt h, drop_eq_nil_of_le <| le_of_not_lt h]
where
  go_eq_take_drop (n : ℕ) (l xs : List α) (acc : Array α) : splitAt.go l xs n acc =
      if n < xs.length then (acc.toList ++ take n xs, drop n xs) else (l, []) := by
    split_ifs with h
    · induction n generalizing xs acc with
      | zero =>
        

Proof state:
case pos.zero
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
p : α → Bool
xs✝ ys : List α
ls : List (List α)
f : List α → List α
n : ℕ
l✝ l xs : List α
acc : Array α
h : zero < length xs
⊢ splitAt.go l xs zero acc = (Array.toList acc ++ take zero xs, drop zero xs)

Next tactic:
rw [splitAt.go, take, drop, append_nil]
@[simp]
theorem splitAt_eq_take_drop (n : ℕ) (l : List α) : splitAt n l = (take n l, drop n l) := by
  by_cases h : n < l.length <;> rw [splitAt, go_eq_take_drop]
  · rw [if_pos h]; rfl
  · rw [if_neg h, take_all_of_le <| le_of_not_lt h, drop_eq_nil_of_le <| le_of_not_lt h]
where
  go_eq_take_drop (n : ℕ) (l xs : List α) (acc : Array α) : splitAt.go l xs n acc =
      if n < xs.length then (acc.toList ++ take n xs, drop n xs) else (l, []) := by
    split_ifs with h
    · induction n generalizing xs acc with
      | zero =>
        rw [splitAt.go, take, drop, append_nil]
        · 

Proof state:
case pos.zero.x_4
ι : Type u_1
α : Type u
β : Type v
γ : Type w
l₁ l₂ : List α
p : α → Bool
xs✝ ys : List α
ls : List (List α)
f : List α → List α
n : ℕ
l✝ l xs : List α
acc : Array α
h : zero < length xs
⊢ xs = [] → False

Next tactic:
intros h₁