l3lab/ntp-mathlib huggingface dataset:
theorem laurentAux_div :
    laurentAux r (algebraMap _ _ p / algebraMap _ _ q) =
      algebraMap _ _ (taylor r p) / algebraMap _ _ (taylor r q) :=
  -- porting note: added `by exact taylor_mem_nonZeroDivisors r`
  map_apply_div _ (by 

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
⊢ R[X]⁰ ≤
    Submonoid.comap
      {
        toMonoidHom :=
          { toOneHom := { toFun := ⇑(taylor r), map_one' := (_ : (taylor r) 1 = Polynomial.C 1) },
            map_mul' := (_ : ∀ (p q : R[X]), (taylor r) (p * q) = (taylor r) p * (taylor r) q) },
        map_zero' := (_ : (taylor r) 0 = 0),
        map_add' := (_ : ∀ (x y : R[X]), (taylor r) (x + y) = (taylor r) x + (taylor r) y) }
      R[X]⁰

Next tactic:
exact taylor_mem_nonZeroDivisors r
@[simp]
theorem laurentAux_algebraMap : laurentAux r (algebraMap _ _ p) = algebraMap _ _ (taylor r p) := by
  

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
⊢ (laurentAux r) ((algebraMap R[X] (RatFunc R)) p) = (algebraMap R[X] (RatFunc R)) ((taylor r) p)

Next tactic:
rw [← mk_one, ← mk_one, mk_eq_div, laurentAux_div, mk_eq_div, taylor_one, map_one, map_one]
@[simp]
theorem laurent_X : laurent r X = X + C r := by
  

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
⊢ (laurent r) X = X + C r

Next tactic:
rw [← algebraMap_X, laurent_algebraMap, taylor_X, _root_.map_add, algebraMap_C]
@[simp]
theorem laurent_C (x : R) : laurent r (C x) = C x := by
  

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
x : R
⊢ (laurent r) (C x) = C x

Next tactic:
rw [← algebraMap_C, laurent_algebraMap, taylor_C]
@[simp]
theorem laurent_at_zero : laurent 0 f = f := by 

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
⊢ (laurent 0) f = f

Next tactic:
induction f using RatFunc.induction_on
@[simp]
theorem laurent_at_zero : laurent 0 f = f := by induction f using RatFunc.induction_on; 

Proof state:
case f
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
p✝ q✝ : R[X]
hq✝ : q✝ ≠ 0
⊢ (laurent 0) ((algebraMap R[X] (RatFunc R)) p✝ / (algebraMap R[X] (RatFunc R)) q✝) =
    (algebraMap R[X] (RatFunc R)) p✝ / (algebraMap R[X] (RatFunc R)) q✝

Next tactic:
simp
theorem laurent_laurent : laurent r (laurent s f) = laurent (r + s) f := by
  

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
⊢ (laurent r) ((laurent s) f) = (laurent (r + s)) f

Next tactic:
induction f using RatFunc.induction_on
theorem laurent_laurent : laurent r (laurent s f) = laurent (r + s) f := by
  induction f using RatFunc.induction_on
  

Proof state:
case f
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f : RatFunc R
p✝ q✝ : R[X]
hq✝ : q✝ ≠ 0
⊢ (laurent r) ((laurent s) ((algebraMap R[X] (RatFunc R)) p✝ / (algebraMap R[X] (RatFunc R)) q✝)) =
    (laurent (r + s)) ((algebraMap R[X] (RatFunc R)) p✝ / (algebraMap R[X] (RatFunc R)) q✝)

Next tactic:
simp_rw [laurent_div, taylor_taylor]
theorem laurent_injective : Function.Injective (laurent r) := fun _ _ h => by
  

Proof state:
R : Type u
inst✝ : CommRing R
hdomain : IsDomain R
r s : R
p q : R[X]
f x✝¹ x✝ : RatFunc R
h : (laurent r) x✝¹ = (laurent r) x✝
⊢ x✝¹ = x✝

Next tactic:
simpa [laurent_laurent] using congr_arg (laurent (-r)) h
instance decidableLT : @DecidableRel String (· < ·) := by
  

Proof state:
⊢ DecidableRel fun x x_1 => x < x_1

Next tactic:
simp only [LT']
instance decidableLT : @DecidableRel String (· < ·) := by
  simp only [LT']
  

Proof state:
⊢ DecidableRel fun s₁ s₂ => ltb (iter s₁) (iter s₂) = true

Next tactic:
infer_instance
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  

Proof state:
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
    ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }

Next tactic:
apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩)
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> 

Proof state:
case ind
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      Iterator.hasNext { s := s₁, i := i₁ } = true →
        get s₁ i₁ = get s₂ i₂ →
          (match Iterator.next { s := s₁, i := i₁ } with
            | { s := { data := cs₁ }, i := i₁ } =>
              match Iterator.next { s := s₂, i := i₂ } with
              | { s := { data := cs₂ }, i := i₂ } =>
                ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
                  ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }) →
            match { s := s₁, i := i₁ } with
            | { s := { data := cs₁ }, i := i₁ } =>
              match { s := s₂, i := i₂ } with
              | { s := { data := cs₂ }, i := i₂ } =>
                ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
                  ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }

Next tactic:
simp
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> 

Proof state:
case eq
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      Iterator.hasNext { s := s₁, i := i₁ } = true →
        ¬get s₁ i₁ = get s₂ i₂ →
          match { s := s₁, i := i₁ } with
          | { s := { data := cs₁ }, i := i₁ } =>
            match { s := s₂, i := i₂ } with
            | { s := { data := cs₂ }, i := i₂ } =>
              ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
                ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }

Next tactic:
simp
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> 

Proof state:
case base₁
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      ¬Iterator.hasNext { s := s₁, i := i₁ } = true →
        match { s := s₁, i := i₁ } with
        | { s := { data := cs₁ }, i := i₁ } =>
          match { s := s₂, i := i₂ } with
          | { s := { data := cs₂ }, i := i₂ } =>
            ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
              ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }

Next tactic:
simp
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> 

Proof state:
case base₂
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    ¬Iterator.hasNext { s := s₂, i := i₂ } = true →
      match { s := s₁, i := i₁ } with
      | { s := { data := cs₁ }, i := i₁ } =>
        match { s := s₂, i := i₂ } with
        | { s := { data := cs₂ }, i := i₂ } =>
          ltb { s := { data := c :: cs₁ }, i := i₁ + c } { s := { data := c :: cs₂ }, i := i₂ + c } =
            ltb { s := { data := cs₁ }, i := i₁ } { s := { data := cs₂ }, i := i₂ }

Next tactic:
simp
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  

Proof state:
case ind
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      Iterator.hasNext { s := s₁, i := i₁ } = true →
        get s₁ i₁ = get s₂ i₂ →
          ltb
                { s := { data := c :: (Iterator.next { s := s₁, i := i₁ }).1.data },
                  i := (Iterator.next { s := s₁, i := i₁ }).i + c }
                { s := { data := c :: (Iterator.next { s := s₂, i := i₂ }).1.data },
                  i := (Iterator.next { s := s₂, i := i₂ }).i + c } =
              ltb
                { s := { data := (Iterator.next { s := s₁, i := i₁ }).1.data },
                  i := (Iterator.next { s := s₁, i := i₁ }).i }
                { s := { data := (Iterator.next { s := s₂, i := i₂ }).1.data },
                  i := (Iterator.next { s := s₂, i := i₂ }).i } →
            ltb { s := { data := c :: s₁.data }, i := i₁ + c } { s := { data := c :: s₂.data }, i := i₂ + c } =
              ltb { s := { data := s₁.data }, i := i₁ } { s := { data := s₂.data }, i := i₂ }

Next tactic:
intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  

Proof state:
case eq
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      Iterator.hasNext { s := s₁, i := i₁ } = true →
        ¬get s₁ i₁ = get s₂ i₂ →
          ltb { s := { data := c :: s₁.data }, i := i₁ + c } { s := { data := c :: s₂.data }, i := i₂ + c } =
            ltb { s := { data := s₁.data }, i := i₁ } { s := { data := s₂.data }, i := i₂ }

Next tactic:
intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  

Proof state:
case base₁
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = true →
      Iterator.hasNext { s := s₁, i := i₁ } = false →
        ltb { s := { data := c :: s₁.data }, i := i₁ + c } { s := { data := c :: s₂.data }, i := i₂ + c } =
          ltb { s := { data := s₁.data }, i := i₁ } { s := { data := s₂.data }, i := i₂ }

Next tactic:
intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  

Proof state:
case base₂
c : Char
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ ∀ (s₁ s₂ : String) (i₁ i₂ : Pos),
    Iterator.hasNext { s := s₂, i := i₂ } = false →
      ltb { s := { data := c :: s₁.data }, i := i₁ + c } { s := { data := c :: s₂.data }, i := i₂ + c } =
        ltb { s := { data := s₁.data }, i := i₁ } { s := { data := s₂.data }, i := i₂ }

Next tactic:
intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  

Proof state:
case ind
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true →
    Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true →
      get { data := cs₁ } i₁ = get { data := cs₂ } i₂ →
        ltb
              { s := { data := c :: (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
                i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i + c }
              { s := { data := c :: (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
                i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i + c } =
            ltb
              { s := { data := (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
                i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i }
              { s := { data := (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
                i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i } →
          ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
              { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
            ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
intros
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  

Proof state:
case eq
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true →
    Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true →
      ¬get { data := cs₁ } i₁ = get { data := cs₂ } i₂ →
        ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
            { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
          ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
intros
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  

Proof state:
case base₁
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true →
    Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = false →
      ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
          { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
        ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
intros
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  

Proof state:
case base₂
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
⊢ Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = false →
    ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
        { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
      ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
intros
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  intros <;>
  (

Proof state:
case ind
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
a✝³ : Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true
a✝² : Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true
a✝¹ : get { data := cs₁ } i₁ = get { data := cs₂ } i₂
a✝ :
  ltb
      { s := { data := c :: (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i + c }
      { s := { data := c :: (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i + c } =
    ltb
      { s := { data := (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i }
      { s := { data := (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i }
⊢ ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
      { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
    ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
conv => lhs; rw [ltb]
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  intros <;>
  (conv => 

Proof state:
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
a✝³ : Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true
a✝² : Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true
a✝¹ : get { data := cs₁ } i₁ = get { data := cs₂ } i₂
a✝ :
  ltb
      { s := { data := c :: (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i + c }
      { s := { data := c :: (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i + c } =
    ltb
      { s := { data := (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i }
      { s := { data := (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i }
| ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
      { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
    ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
lhs; rw [ltb]
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  intros <;>
  (conv => 

Proof state:
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
a✝³ : Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true
a✝² : Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true
a✝¹ : get { data := cs₁ } i₁ = get { data := cs₂ } i₂
a✝ :
  ltb
      { s := { data := c :: (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i + c }
      { s := { data := c :: (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i + c } =
    ltb
      { s := { data := (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i }
      { s := { data := (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i }
| ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
      { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
    ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
lhs
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  intros <;>
  (conv => lhs; 

Proof state:
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
a✝³ : Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true
a✝² : Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true
a✝¹ : get { data := cs₁ } i₁ = get { data := cs₂ } i₂
a✝ :
  ltb
      { s := { data := c :: (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i + c }
      { s := { data := c :: (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i + c } =
    ltb
      { s := { data := (Iterator.next { s := { data := cs₁ }, i := i₁ }).1.data },
        i := (Iterator.next { s := { data := cs₁ }, i := i₁ }).i }
      { s := { data := (Iterator.next { s := { data := cs₂ }, i := i₂ }).1.data },
        i := (Iterator.next { s := { data := cs₂ }, i := i₂ }).i }
| ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
    { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c }

Next tactic:
rw [ltb]
theorem ltb_cons_addChar (c : Char) (cs₁ cs₂ : List Char) (i₁ i₂ : Pos) :
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ = ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ := by
  apply ltb.inductionOn ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ (motive := fun ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩ ↦
    ltb ⟨⟨c :: cs₁⟩, i₁ + c⟩ ⟨⟨c :: cs₂⟩, i₂ + c⟩ =
    ltb ⟨⟨cs₁⟩, i₁⟩ ⟨⟨cs₂⟩, i₂⟩) <;> simp <;>
  intro ⟨cs₁⟩ ⟨cs₂⟩ i₁ i₂ <;>
  intros <;>
  (

Proof state:
case eq
c : Char
cs₁✝ cs₂✝ : List Char
i₁✝ i₂✝ : Pos
cs₁ cs₂ : List Char
i₁ i₂ : Pos
a✝² : Iterator.hasNext { s := { data := cs₂ }, i := i₂ } = true
a✝¹ : Iterator.hasNext { s := { data := cs₁ }, i := i₁ } = true
a✝ : ¬get { data := cs₁ } i₁ = get { data := cs₂ } i₂
⊢ ltb { s := { data := c :: { data := cs₁ }.data }, i := i₁ + c }
      { s := { data := c :: { data := cs₂ }.data }, i := i₂ + c } =
    ltb { s := { data := { data := cs₁ }.data }, i := i₁ } { s := { data := { data := cs₂ }.data }, i := i₂ }

Next tactic:
conv => lhs; rw [ltb]