module

public import Mathlib.Data.Prod.Lex
public import Mathlib.RingTheory.Coprime.Basic
public import Mathlib.Algebra.Star.SelfAdjoint
public import Mathlib.Topology.Algebra.Group.Basic
import all Mathlib.Data.Prod.Lex
meta import AutoGeneralization

public section

namespace AutoGeneralizationTest.Examples

/-! User-facing examples using only the public command and linter syntax. -/

class Weak (α : Type) where
  op : α → α

class Middle (α : Type) extends Weak α where
  tr : True

class Strong (α : Type) extends Middle α where
  size : Nat

theorem typeClassSource {α : Type} [Strong α] (x : α) :
    Weak.op x = Weak.op x :=
  rfl

-- Inspect every candidate. Clicking `[apply]` inserts its theorem below the command.
/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.typeClassSource (1 found):
  [apply] candidate 1/1: [Strong α] → [Weak α]
    result: theorem typeClassSource_generalized {α : Type} [Weak α] (x : α) : Weak.op x = Weak.op x
-/
#guard_msgs in
#autogeneralize typeClassSource

-- Source inserted by the candidate's code action.
theorem typeClassSource_generalized {α : Type} [Weak α] (x : α) : Weak.op x = Weak.op x :=
  rfl

theorem dependentTypeClassSource {α : Type} [Strong α] (x : α)
    (h : Weak.op x = x) : h = h :=
  rfl

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.dependentTypeClassSource (1 found):
  [apply] candidate 1/1: [Strong α] → [Weak α]
    result: theorem dependentTypeClassSource_generalized {α : Type} [Weak α] (x : α) (h : Weak.op x = x) : h = h
-/
#guard_msgs in
#autogeneralize dependentTypeClassSource

theorem dependentTypeClassSource_generalized {α : Type} [Weak α] (x : α)
    (h : Weak.op x = x) : h = h :=
  rfl

/-- A dependency on a field of the stronger class cannot be removed yet. -/
theorem strongFieldDependency {α : Type} [s : Strong α]
    (_ : Fin (s.size + 1)) (x : α) : Weak.op x = Weak.op x :=
  rfl

/--
info: No type class generalization found for AutoGeneralizationTest.Examples.strongFieldDependency.
-/
#guard_msgs in
#autogeneralize strongFieldDependency

/--
error: -- Found 2 errors in 17 declarations (plus 23 automatically generated ones) in the current file with 1 linters

/- The `generalizeTypeClass` linter reports:
THEOREM TYPECLASS ASSUMPTIONS CAN BE GENERALIZED:
This linter can be disabled with `@[nolint generalizeTypeClass]`. -/
#check @typeClassSource /- [inst✝ : Strong α] can be generalized to [Weak α] -/
#check @dependentTypeClassSource /- [inst✝ : Strong α] can be generalized to [Weak α] -/
-/
#guard_msgs in
#lint only generalizeTypeClass

-- A later binder may depend on a field supplied by the weaker class.
class HasBound (α : Type) where
  bound : Nat

class RichBound (α : Type) extends HasBound α where
  tag : Nat

theorem dependentIndexSource {α : Type} [RichBound α]
    (i : Fin (HasBound.bound (α := α) + 1)) : i = i :=
  rfl

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.dependentIndexSource (1 found):
  [apply] candidate 1/1: [RichBound α] → [HasBound α]
    result: theorem dependentIndexSource_generalized {α : Type} [HasBound α] (i : Fin (HasBound.bound α + 1)) : i = i
-/
#guard_msgs in
#autogeneralize dependentIndexSource

theorem dependentIndexSource_generalized {α : Type} [HasBound α]
    (i : Fin (HasBound.bound α + 1)) : i = i :=
  rfl

-- If the weaker class depends on a later argument, its binder moves after it.
class Seeded (α : Type) where
  seed : Nat

class AtPoint (α : Type) (x : α) where
  measure : Nat

instance atPointOfSeeded {α : Type} [s : Seeded α] (x : α) : AtPoint α x where
  measure := s.seed

theorem reorderedSource {α : Type} [Seeded α] (x : α) :
    AtPoint.measure (α := α) (x := x) = AtPoint.measure (α := α) (x := x) :=
  rfl

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.reorderedSource (1 found):
  [apply] candidate 1/1: [Seeded α] → [AtPoint α x]
    result: theorem reorderedSource_generalized {α : Type} (x : α) [AtPoint α x] : AtPoint.measure x = AtPoint.measure x
-/
#guard_msgs in
#autogeneralize reorderedSource

theorem reorderedSource_generalized {α : Type} (x : α) [AtPoint α x] :
    AtPoint.measure (α := α) (x := x) = AtPoint.measure (α := α) (x := x) :=
  rfl

/-! ### Generalizing a Mathlib theorem with two capabilities -/

open Prod

/-
The command can inspect this imported theorem directly. Its current signature
is already minimal.
-/
/-- info: No type class generalization found for Prod.Lex.toLex_le_toLex. -/
#guard_msgs in
#autogeneralize Prod.Lex.toLex_le_toLex

/--
`Prod.Lex.toLex_le_toLex` needs only `LT` on the first component and `LE` on the
second. Using the same type twice splits one `Preorder` into two evidence binders.
-/
theorem lexicographicSelf {α : Type} [Preorder α] {x y : α × α} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2 :=
  Prod.Lex.toLex_le_toLex

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.lexicographicSelf (1 found):
  [apply] candidate 1/1: [Preorder α] → [LT α] [LE α]
    result: theorem lexicographicSelf_generalized {α : Type} [LT α] [LE α] {x y : α × α} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2
-/
#guard_msgs in
#autogeneralize lexicographicSelf

theorem lexicographicSelf_generalized {α : Type} [LT α] [LE α] {x y : α × α} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2 :=
  Prod.Lex.toLex_le_toLex

/-! ### Generalizing through a registered instance -/

/-
This wrapper deliberately strengthens Mathlib's `Semifield.isCoprime_iff` to
`Field`. Recovering `Semifield` makes the result usable for types such as `ℚ≥0`.
-/
theorem coprimeIffOverField {R : Type*} [Field R] {m n : R} :
    IsCoprime m n ↔ m ≠ 0 ∨ n ≠ 0 :=
  Semifield.isCoprime_iff

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.coprimeIffOverField (1 found):
  [apply] candidate 1/1: [Field R] → [Semifield R]
    result: theorem coprimeIffOverField_generalized.{u_1} {R : Type u_1} [Semifield R] {m n : R} :
    IsCoprime m n ↔ m ≠ 0 ∨ n ≠ 0
-/
#guard_msgs in
#autogeneralize coprimeIffOverField

theorem coprimeIffOverField_generalized {R : Type*} [Semifield R] {m n : R} :
    IsCoprime m n ↔ m ≠ 0 ∨ n ≠ 0 :=
  Semifield.isCoprime_iff

example {m n : ℚ≥0} : IsCoprime m n ↔ m ≠ 0 ∨ n ≠ 0 :=
  coprimeIffOverField_generalized

/-
`StarRing R` stores lower algebraic evidence selected in the original `Field`
context. Re-synthesizing that evidence lets this proof keep `StarRing` while
weakening `Field` to `Semifield`.
-/
theorem selfAdjointDivOverField {R : Type*} [Field R] [StarRing R] {x y : R}
    (hx : IsSelfAdjoint x) (hy : IsSelfAdjoint y) : IsSelfAdjoint (x / y) :=
  IsSelfAdjoint.div hx hy

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.selfAdjointDivOverField (1 found):
  [apply] candidate 1/1: [Field R] → [Semifield R]
    result: theorem selfAdjointDivOverField_generalized.{u_1} {R : Type u_1} [Semifield R] [StarRing R] {x y : R}
    (hx : IsSelfAdjoint x) (hy : IsSelfAdjoint y) : IsSelfAdjoint (x / y)
-/
#guard_msgs in
#autogeneralize selfAdjointDivOverField

theorem selfAdjointDivOverField_generalized {R : Type*} [Semifield R] [StarRing R]
    {x y : R} (hx : IsSelfAdjoint x) (hy : IsSelfAdjoint y) : IsSelfAdjoint (x / y) :=
  IsSelfAdjoint.div hx hy

/-! ### Generalizing every source -/

/-- With different component types, each `Preorder` supplies one required capability. -/
theorem lexicographicPair {α β : Type} [Preorder α] [Preorder β] {x y : α × β} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2 :=
  Prod.Lex.toLex_le_toLex

/--
info: Complete type class generalization for AutoGeneralizationTest.Examples.lexicographicPair (2 steps):
  [apply] step 1: [Preorder β] → [LE β]
  step 2: [Preorder α] → [LT α]
    result: theorem lexicographicPair_generalized {α β : Type} [LT α] [LE β] {x y : α × β} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2
-/
#guard_msgs in
#autogeneralize! lexicographicPair

theorem lexicographicPair_generalized {α β : Type} [LT α] [LE β] {x y : α × β} :
    toLex x ≤ toLex y ↔ x.1 < y.1 ∨ x.1 = y.1 ∧ x.2 ≤ y.2 :=
  Prod.Lex.toLex_le_toLex

/-! ### Replaying a type-class generalization from Mathlib -/

open Set Topology

/--
Before Mathlib PR #23193, `inv_mem_connectedComponent_one` used these stronger
`Group` and `IsTopologicalGroup` assumptions. The current theorem supplies the proof.
-/
theorem topologicalInvComponent {G : Type} [TopologicalSpace G] [Group G]
    [IsTopologicalGroup G] {g : G} (hg : g ∈ connectedComponent (1 : G)) :
    g⁻¹ ∈ connectedComponent (1 : G) :=
  inv_mem_connectedComponent_one hg

/--
info: Type class generalization candidates for AutoGeneralizationTest.Examples.topologicalInvComponent (1 found):
  [apply] candidate 1/1: [IsTopologicalGroup G] → [ContinuousInv G]
    result: theorem topologicalInvComponent_generalized {G : Type} [TopologicalSpace G] [Group G] [ContinuousInv G]
    {g : G} (hg : g ∈ connectedComponent 1) : g⁻¹ ∈ connectedComponent 1
-/
#guard_msgs in
#autogeneralize topologicalInvComponent

/--
info: Complete type class generalization for AutoGeneralizationTest.Examples.topologicalInvComponent (2 steps):
  [apply] step 1: [IsTopologicalGroup G] → [ContinuousInv G]
  step 2: [Group G] → [DivisionMonoid G]
    result: theorem topologicalInvComponent_generalized {G : Type} [TopologicalSpace G] [DivisionMonoid G]
    [ContinuousInv G] {g : G} (hg : g ∈ connectedComponent 1) : g⁻¹ ∈ connectedComponent 1
-/
#guard_msgs in
#autogeneralize! topologicalInvComponent

theorem topologicalInvComponent_generalized {G : Type} [TopologicalSpace G]
    [DivisionMonoid G] [ContinuousInv G] {g : G} (hg : g ∈ connectedComponent (1 : G)) :
    g⁻¹ ∈ connectedComponent (1 : G) :=
  inv_mem_connectedComponent_one hg

end AutoGeneralizationTest.Examples
end
