l3lab/ntp-mathlib huggingface dataset:
example : Semiring (⨁ n : ℕ, (⨂[R]^n) M) := by 

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
⊢ Semiring (⨁ (n : ℕ), (⨂[R]^n) M)

Next tactic:
infer_instance
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r s : R
⊢ (cast R M
        (_ :
          (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst =
            (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst))
      (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).snd =
    (GradedMonoid.mk (0 + 0)
        (GradedMonoid.GMul.mul ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)
          ((LinearMap.toAddMonoidHom ↑algebraMap₀) s))).snd

Next tactic:
rw [← LinearEquiv.eq_symm_apply]
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r s : R
⊢ (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).snd =
    (LinearEquiv.symm
        (cast R M
          (_ :
            (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst =
              (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst)))
      (GradedMonoid.mk (0 + 0)
          (GradedMonoid.GMul.mul ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)
            ((LinearMap.toAddMonoidHom ↑algebraMap₀) s))).snd

Next tactic:
have := algebraMap₀_mul_algebraMap₀ (M := M) r s
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r s : R
this : (cast R M (_ : 0 + 0 = 0)) (GradedMonoid.GMul.mul (algebraMap₀ r) (algebraMap₀ s)) = algebraMap₀ (r * s)
⊢ (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).snd =
    (LinearEquiv.symm
        (cast R M
          (_ :
            (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst =
              (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) (r * s))).fst)))
      (GradedMonoid.mk (0 + 0)
          (GradedMonoid.GMul.mul ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)
            ((LinearMap.toAddMonoidHom ↑algebraMap₀) s))).snd

Next tactic:
exact this.symm
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
⊢ (cast R M
        (_ :
          (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst + x.fst =
            x.fst + (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst))
      (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd =
    (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).snd

Next tactic:
have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
this :
  (cast R M (_ : 0 + x.fst = x.fst)) (GradedMonoid.GMul.mul (algebraMap₀ r) x.snd) =
    (cast R M (_ : x.fst + 0 = x.fst)) (GradedMonoid.GMul.mul x.snd (algebraMap₀ r))
⊢ (cast R M
        (_ :
          (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst + x.fst =
            x.fst + (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst))
      (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd =
    (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).snd

Next tactic:
rw [← LinearEquiv.eq_symm_apply, cast_symm]
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
    rw [← LinearEquiv.eq_symm_apply, cast_symm]
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
this :
  (cast R M (_ : 0 + x.fst = x.fst)) (GradedMonoid.GMul.mul (algebraMap₀ r) x.snd) =
    (cast R M (_ : x.fst + 0 = x.fst)) (GradedMonoid.GMul.mul x.snd (algebraMap₀ r))
⊢ (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd =
    (cast R M
        (_ :
          (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst =
            (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).fst))
      (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).snd

Next tactic:
rw [← LinearEquiv.eq_symm_apply, cast_symm, cast_cast] at this
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
    rw [← LinearEquiv.eq_symm_apply, cast_symm]
    rw [← LinearEquiv.eq_symm_apply, cast_symm, cast_cast] at this
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
this :
  GradedMonoid.GMul.mul (algebraMap₀ r) x.snd =
    (cast R M (_ : x.fst + 0 = 0 + x.fst)) (GradedMonoid.GMul.mul x.snd (algebraMap₀ r))
⊢ (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd =
    (cast R M
        (_ :
          (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).fst =
            (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).fst))
      (x * GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r)).snd

Next tactic:
exact this
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
    rw [← LinearEquiv.eq_symm_apply, cast_symm]
    rw [← LinearEquiv.eq_symm_apply, cast_symm, cast_cast] at this
    exact this)
  smul_def r x := gradedMonoid_eq_of_cast (zero_add x.fst).symm (by
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
⊢ (cast R M (_ : x.fst = 0 + x.fst)) (r • x).snd =
    (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd

Next tactic:
rw [← LinearEquiv.eq_symm_apply, cast_symm]
/-- The tensor powers form a graded algebra.

Note that this instance implies `Algebra R (⨁ n : ℕ, ⨂[R]^n M)` via `DirectSum.Algebra`. -/
instance galgebra : DirectSum.GAlgebra R fun i => (⨂[R]^i) M where
  toFun := (algebraMap₀ : R ≃ₗ[R] (⨂[R]^0) M).toLinearMap.toAddMonoidHom
  map_one := algebraMap₀_one
  map_mul r s := gradedMonoid_eq_of_cast rfl (by
    rw [← LinearEquiv.eq_symm_apply]
    have := algebraMap₀_mul_algebraMap₀ (M := M) r s
    exact this.symm)
  commutes r x := gradedMonoid_eq_of_cast (add_comm _ _) (by
    have := (algebraMap₀_mul r x.snd).trans (mul_algebraMap₀ r x.snd).symm
    rw [← LinearEquiv.eq_symm_apply, cast_symm]
    rw [← LinearEquiv.eq_symm_apply, cast_symm, cast_cast] at this
    exact this)
  smul_def r x := gradedMonoid_eq_of_cast (zero_add x.fst).symm (by
    rw [← LinearEquiv.eq_symm_apply, cast_symm]
    

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
r : R
x : GradedMonoid fun i => (⨂[R]^i) M
⊢ (r • x).snd =
    (cast R M (_ : (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).fst = (r • x).fst))
      (GradedMonoid.mk 0 ((LinearMap.toAddMonoidHom ↑algebraMap₀) r) * x).snd

Next tactic:
exact (algebraMap₀_mul r x.snd).symm
example : Algebra R (⨁ n : ℕ, (⨂[R]^n) M) := by 

Proof state:
R : Type u_1
M : Type u_2
inst✝² : CommSemiring R
inst✝¹ : AddCommMonoid M
inst✝ : Module R M
⊢ Algebra R (⨁ (n : ℕ), (⨂[R]^n) M)

Next tactic:
infer_instance
theorem encode_injective [Encodable α] : Function.Injective (@encode α _)
  | x, y, e => Option.some.inj <| by 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
x y : α
e : encode x = encode y
⊢ some x = some y

Next tactic:
rw [← encodek, e, encodek]
theorem surjective_decode_iget (α : Type*) [Encodable α] [Inhabited α] :
    Surjective fun n => ((Encodable.decode n).iget : α) := fun x =>
  ⟨Encodable.encode x, by 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝¹ : Encodable α
inst✝ : Inhabited α
x : α
⊢ (fun n => iget (decode n)) (encode x) = x

Next tactic:
simp_rw [Encodable.encodek]
/-- If `α` is encodable and there is an injection `f : β → α`, then `β` is encodable as well. -/
def ofLeftInjection [Encodable α] (f : β → α) (finv : α → Option β)
    (linv : ∀ b, finv (f b) = some b) : Encodable β :=
  ⟨fun b => encode (f b), fun n => (decode n).bind finv, fun b => by
    

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
f : β → α
finv : α → Option β
linv : ∀ (b : β), finv (f b) = some b
b : β
⊢ (fun n => Option.bind (decode n) finv) ((fun b => encode (f b)) b) = some b

Next tactic:
simp [Encodable.encodek, linv]
theorem decode_ofEquiv {α β} [Encodable α] (e : β ≃ α) (n : ℕ) :
    @decode _ (ofEquiv _ e) n = (decode n).map e.symm :=
  show Option.bind _ _ = Option.map _ _
  by 

Proof state:
α✝ : Type u_1
β✝ : Type u_2
α : Type u_3
β : Type u_4
inst✝ : Encodable α
e : β ≃ α
n : ℕ
⊢ Option.bind (decode n) (some ∘ ⇑e.symm) = Option.map (⇑e.symm) (decode n)

Next tactic:
rw [Option.map_eq_bind]
instance _root_.PUnit.encodable : Encodable PUnit :=
  ⟨fun _ => 0, fun n => Nat.casesOn n (some PUnit.unit) fun _ => none, fun _ => by 

Proof state:
α : Type u_1
β : Type u_2
x✝ : PUnit.{?u.2542 + 1}
⊢ (fun n => Nat.casesOn n (some PUnit.unit) fun x => none) ((fun x => 0) x✝) = some x✝

Next tactic:
simp
/-- If `α` is encodable, then so is `Option α`. -/
instance _root_.Option.encodable {α : Type*} [h : Encodable α] : Encodable (Option α) :=
  ⟨fun o => Option.casesOn o Nat.zero fun a => succ (encode a), fun n =>
    Nat.casesOn n (some none) fun m => (decode m).map some, fun o => by
    

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
h : Encodable α
o : Option α
⊢ (fun n => Nat.casesOn n (some none) fun m => Option.map some (decode m))
      ((fun o => Option.casesOn o zero fun a => succ (encode a)) o) =
    some o

Next tactic:
cases o
/-- If `α` is encodable, then so is `Option α`. -/
instance _root_.Option.encodable {α : Type*} [h : Encodable α] : Encodable (Option α) :=
  ⟨fun o => Option.casesOn o Nat.zero fun a => succ (encode a), fun n =>
    Nat.casesOn n (some none) fun m => (decode m).map some, fun o => by
    cases o <;> 

Proof state:
case none
α✝ : Type u_1
β : Type u_2
α : Type u_3
h : Encodable α
⊢ (fun n => Nat.casesOn n (some none) fun m => Option.map some (decode m))
      ((fun o => Option.casesOn o zero fun a => succ (encode a)) none) =
    some none

Next tactic:
dsimp
/-- If `α` is encodable, then so is `Option α`. -/
instance _root_.Option.encodable {α : Type*} [h : Encodable α] : Encodable (Option α) :=
  ⟨fun o => Option.casesOn o Nat.zero fun a => succ (encode a), fun n =>
    Nat.casesOn n (some none) fun m => (decode m).map some, fun o => by
    cases o <;> 

Proof state:
case some
α✝ : Type u_1
β : Type u_2
α : Type u_3
h : Encodable α
val✝ : α
⊢ (fun n => Nat.casesOn n (some none) fun m => Option.map some (decode m))
      ((fun o => Option.casesOn o zero fun a => succ (encode a)) (some val✝)) =
    some (some val✝)

Next tactic:
dsimp
/-- If `α` is encodable, then so is `Option α`. -/
instance _root_.Option.encodable {α : Type*} [h : Encodable α] : Encodable (Option α) :=
  ⟨fun o => Option.casesOn o Nat.zero fun a => succ (encode a), fun n =>
    Nat.casesOn n (some none) fun m => (decode m).map some, fun o => by
    cases o <;> dsimp; 

Proof state:
case some
α✝ : Type u_1
β : Type u_2
α : Type u_3
h : Encodable α
val✝ : α
⊢ Option.map some (decode (encode val✝)) = some (some val✝)

Next tactic:
simp [encodek, Nat.succ_ne_zero]
theorem mem_decode₂' [Encodable α] {n : ℕ} {a : α} :
    a ∈ decode₂ α n ↔ a ∈ decode n ∧ encode a = n := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
n : ℕ
a : α
⊢ a ∈ decode₂ α n ↔ a ∈ decode n ∧ encode a = n

Next tactic:
simp [decode₂]
theorem mem_decode₂' [Encodable α] {n : ℕ} {a : α} :
    a ∈ decode₂ α n ↔ a ∈ decode n ∧ encode a = n := by
  simp [decode₂]; 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
n : ℕ
a : α
⊢ (∃ a_1, decode n = some a_1 ∧ a_1 = a ∧ encode a_1 = n) ↔ decode n = some a ∧ encode a = n

Next tactic:
exact ⟨fun ⟨_, h₁, rfl, h₂⟩ => ⟨h₁, h₂⟩, fun ⟨h₁, h₂⟩ => ⟨_, h₁, rfl, h₂⟩⟩
@[simp]
theorem decode₂_encode [Encodable α] (a : α) : decode₂ α (encode a) = some a := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
a : α
⊢ decode₂ α (encode a) = some a

Next tactic:
ext
@[simp]
theorem decode₂_encode [Encodable α] (a : α) : decode₂ α (encode a) = some a := by
  ext
  

Proof state:
case a
α : Type u_1
β : Type u_2
inst✝ : Encodable α
a a✝ : α
⊢ a✝ ∈ decode₂ α (encode a) ↔ a✝ ∈ some a

Next tactic:
simp [mem_decode₂, eq_comm, decode₂_eq_some]
theorem decode₂_ne_none_iff [Encodable α] {n : ℕ} :
    decode₂ α n ≠ none ↔ n ∈ Set.range (encode : α → ℕ) := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : Encodable α
n : ℕ
⊢ decode₂ α n ≠ none ↔ n ∈ Set.range encode

Next tactic:
simp_rw [Set.range, Set.mem_setOf_eq, Ne.def, Option.eq_none_iff_forall_not_mem,
    Encodable.mem_decode₂, not_forall, not_not]
/-- The encoding function has decidable range. -/
def decidableRangeEncode (α : Type*) [Encodable α] : DecidablePred (· ∈ Set.range (@encode α _)) :=
  fun x =>
  decidable_of_iff (Option.isSome (decode₂ α x))
    ⟨fun h => ⟨Option.get _ h, by 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x : ℕ
h : isSome (decode₂ α x) = true
⊢ encode (Option.get (decode₂ α x) h) = x

Next tactic:
rw [← decode₂_is_partial_inv (Option.get _ h), Option.some_get]
/-- The encoding function has decidable range. -/
def decidableRangeEncode (α : Type*) [Encodable α] : DecidablePred (· ∈ Set.range (@encode α _)) :=
  fun x =>
  decidable_of_iff (Option.isSome (decode₂ α x))
    ⟨fun h => ⟨Option.get _ h, by rw [← decode₂_is_partial_inv (Option.get _ h), Option.some_get]⟩,
      fun ⟨n, hn⟩ => by 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x : ℕ
x✝ : (fun x => x ∈ Set.range encode) x
n : α
hn : encode n = x
⊢ isSome (decode₂ α x) = true

Next tactic:
rw [← hn, encodek₂]
/-- The encoding function has decidable range. -/
def decidableRangeEncode (α : Type*) [Encodable α] : DecidablePred (· ∈ Set.range (@encode α _)) :=
  fun x =>
  decidable_of_iff (Option.isSome (decode₂ α x))
    ⟨fun h => ⟨Option.get _ h, by rw [← decode₂_is_partial_inv (Option.get _ h), Option.some_get]⟩,
      fun ⟨n, hn⟩ => by rw [← hn, encodek₂]; 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x : ℕ
x✝ : (fun x => x ∈ Set.range encode) x
n : α
hn : encode n = x
⊢ isSome (some n) = true

Next tactic:
exact rfl
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
n : ↑(Set.range encode)
⊢ isSome (decode₂ α ↑n) = true

Next tactic:
cases' n.2 with x hx
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; 

Proof state:
case intro
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
n : ↑(Set.range encode)
x : α
hx : encode x = ↑n
⊢ isSome (decode₂ α ↑n) = true

Next tactic:
rw [← hx, encodek₂]
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; 

Proof state:
case intro
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
n : ↑(Set.range encode)
x : α
hx : encode x = ↑n
⊢ isSome (some x) = true

Next tactic:
exact rfl
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; exact rfl)
  left_inv a := by 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
a : α
⊢ (fun n => Option.get (decode₂ α ↑n) (_ : isSome (decode₂ α ↑n) = true))
      ((fun a => { val := encode a, property := (_ : encode a ∈ Set.range encode) }) a) =
    a

Next tactic:
dsimp
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; exact rfl)
  left_inv a := by dsimp; 

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
a : α
⊢ Option.get (decode₂ α (encode a)) (_ : isSome (decode₂ α (encode a)) = true) = a

Next tactic:
rw [← Option.some_inj, Option.some_get, encodek₂]
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; exact rfl)
  left_inv a := by dsimp; rw [← Option.some_inj, Option.some_get, encodek₂]
  right_inv := fun ⟨n, x, hx⟩ => by
    

Proof state:
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x✝ : ↑(Set.range encode)
n : ℕ
x : α
hx : encode x = n
⊢ (fun a => { val := encode a, property := (_ : encode a ∈ Set.range encode) })
      ((fun n => Option.get (decode₂ α ↑n) (_ : isSome (decode₂ α ↑n) = true))
        { val := n, property := (_ : ∃ y, encode y = n) }) =
    { val := n, property := (_ : ∃ y, encode y = n) }

Next tactic:
apply Subtype.eq
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; exact rfl)
  left_inv a := by dsimp; rw [← Option.some_inj, Option.some_get, encodek₂]
  right_inv := fun ⟨n, x, hx⟩ => by
    apply Subtype.eq
    

Proof state:
case a
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x✝ : ↑(Set.range encode)
n : ℕ
x : α
hx : encode x = n
⊢ ↑((fun a => { val := encode a, property := (_ : encode a ∈ Set.range encode) })
        ((fun n => Option.get (decode₂ α ↑n) (_ : isSome (decode₂ α ↑n) = true))
          { val := n, property := (_ : ∃ y, encode y = n) })) =
    ↑{ val := n, property := (_ : ∃ y, encode y = n) }

Next tactic:
dsimp
/-- An encodable type is equivalent to the range of its encoding function. -/
def equivRangeEncode (α : Type*) [Encodable α] : α ≃ Set.range (@encode α _)
    where
  toFun := fun a : α => ⟨encode a, Set.mem_range_self _⟩
  invFun n :=
    Option.get _
      (show isSome (decode₂ α n.1) by cases' n.2 with x hx; rw [← hx, encodek₂]; exact rfl)
  left_inv a := by dsimp; rw [← Option.some_inj, Option.some_get, encodek₂]
  right_inv := fun ⟨n, x, hx⟩ => by
    apply Subtype.eq
    dsimp
    

Proof state:
case a
α✝ : Type u_1
β : Type u_2
α : Type u_3
inst✝ : Encodable α
x✝ : ↑(Set.range encode)
n : ℕ
x : α
hx : encode x = n
⊢ encode (Option.get (decode₂ α n) (_ : isSome (decode₂ α n) = true)) = n

Next tactic:
conv =>
      rhs
      rw [← hx]