l3lab/ntp-mathlib huggingface dataset:
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    

Proof state:
case left.a.a
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
a b : α
h_eq : Quot.lift f f_respects_relation ⟦a⟧ = Quot.lift f f_respects_relation ⟦b⟧
⊢ ⟦a⟧ = ⟦b⟧

Next tactic:
simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    

Proof state:
case left.a.a
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
a b : α
h_eq : {x | Setoid.r x a} = {x | Setoid.r x b}
⊢ ⟦a⟧ = ⟦b⟧

Next tactic:
apply Quotient.sound
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    

Proof state:
case left.a.a.a
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
a b : α
h_eq : {x | Setoid.r x a} = {x | Setoid.r x b}
⊢ a ≈ b

Next tactic:
show a ∈ { x | Setoid.r x b }
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    show a ∈ { x | Setoid.r x b }
    

Proof state:
case left.a.a.a
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
a b : α
h_eq : {x | Setoid.r x a} = {x | Setoid.r x b}
⊢ a ∈ {x | Setoid.r x b}

Next tactic:
rw [← h_eq]
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    show a ∈ { x | Setoid.r x b }
    rw [← h_eq]
    

Proof state:
case left.a.a.a
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
a b : α
h_eq : {x | Setoid.r x a} = {x | Setoid.r x b}
⊢ a ∈ {x | Setoid.r x a}

Next tactic:
exact Setoid.refl a
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    show a ∈ { x | Setoid.r x b }
    rw [← h_eq]
    exact Setoid.refl a
  · 

Proof state:
case right
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
⊢ Function.Surjective (Quot.lift f f_respects_relation)

Next tactic:
rw [Quot.surjective_lift]
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    show a ∈ { x | Setoid.r x b }
    rw [← h_eq]
    exact Setoid.refl a
  · rw [Quot.surjective_lift]
    

Proof state:
case right
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
⊢ Function.Surjective f

Next tactic:
intro ⟨c, a, hc⟩
/-- The equivalence between the quotient by an equivalence relation and its
type of equivalence classes. -/
noncomputable def quotientEquivClasses (r : Setoid α) : Quotient r ≃ Setoid.classes r := by
  let f (a : α) : Setoid.classes r := ⟨{ x | Setoid.r x a }, Setoid.mem_classes r a⟩
  have f_respects_relation (a b : α) (a_rel_b : Setoid.r a b) : f a = f b := by
    rw [Subtype.mk.injEq]
    exact Setoid.eq_of_mem_classes (Setoid.mem_classes r a) (Setoid.symm a_rel_b)
        (Setoid.mem_classes r b) (Setoid.refl b)
  apply Equiv.ofBijective (Quot.lift f f_respects_relation)
  constructor
  · intro (q_a : Quotient r) (q_b : Quotient r) h_eq
    induction' q_a using Quotient.ind with a
    induction' q_b using Quotient.ind with b
    simp only [Subtype.ext_iff, Quotient.lift_mk, Subtype.ext_iff] at h_eq
    apply Quotient.sound
    show a ∈ { x | Setoid.r x b }
    rw [← h_eq]
    exact Setoid.refl a
  · rw [Quot.surjective_lift]
    intro ⟨c, a, hc⟩
    

Proof state:
case right
α : Type u_1
r : Setoid α
f : α → ↑(classes r) := fun a => { val := {x | Setoid.r x a}, property := (_ : {x | Rel r x a} ∈ classes r) }
f_respects_relation : ∀ (a b : α), Setoid.r a b → f a = f b
c : Set α
a : α
hc : c = {x | Rel r x a}
⊢ ∃ a_1, f a_1 = { val := c, property := (_ : ∃ y, c = {x | Rel r x y}) }

Next tactic:
exact ⟨a, Subtype.ext hc.symm⟩
theorem IsPartition.sUnion_eq_univ {c : Set (Set α)} (hc : IsPartition c) : ⋃₀ c = Set.univ :=
  Set.eq_univ_of_forall fun x =>
    Set.mem_sUnion.2 <|
      let ⟨t, ht⟩ := hc.2 x
      ⟨t, by
        

Proof state:
α : Type u_1
c : Set (Set α)
hc : IsPartition c
x : α
t : Set α
ht : (fun b => ∃! x_1, x ∈ b) t ∧ ∀ (y : Set α), (fun b => ∃! x_1, x ∈ b) y → y = t
⊢ t ∈ c ∧ x ∈ t

Next tactic:
simp only [exists_unique_iff_exists] at ht
theorem IsPartition.sUnion_eq_univ {c : Set (Set α)} (hc : IsPartition c) : ⋃₀ c = Set.univ :=
  Set.eq_univ_of_forall fun x =>
    Set.mem_sUnion.2 <|
      let ⟨t, ht⟩ := hc.2 x
      ⟨t, by
        simp only [exists_unique_iff_exists] at ht
        

Proof state:
α : Type u_1
c : Set (Set α)
hc : IsPartition c
x : α
t : Set α
ht : (∃ (_ : t ∈ c), x ∈ t) ∧ ∀ (y : Set α), (∃ (_ : y ∈ c), x ∈ y) → y = t
⊢ t ∈ c ∧ x ∈ t

Next tactic:
tauto
/-- The equivalence classes of the equivalence relation defined by a partition of α equal
    the original partition. -/
theorem classes_mkClasses (c : Set (Set α)) (hc : IsPartition c) : (mkClasses c hc.2).classes = c :=
  Set.ext fun s => ⟨fun ⟨y, hs⟩ => (hc.2 y).elim₂ fun b hm hb _hy => by
    

Proof state:
α : Type u_1
c : Set (Set α)
hc : IsPartition c
s : Set α
x✝ : s ∈ classes (mkClasses c (_ : ∀ (a : α), ∃! b x, a ∈ b))
y : α
hs : s = {x | Rel (mkClasses c (_ : ∀ (a : α), ∃! b x, a ∈ b)) x y}
b : Set α
hm : b ∈ c
hb : y ∈ b
_hy : ∀ y_1 ∈ c, y ∈ y_1 → y_1 = b
⊢ s ∈ c

Next tactic:
rwa [show s = b from hs.symm ▸ Set.ext fun x =>
      ⟨fun hx => symm' (mkClasses c hc.2) hx b hm hb, fun hx b' hc' hx' =>
          eq_of_mem_eqv_class hc.2 hm hx hc' hx' ▸ hb⟩]
/-- Defining a partial order on partitions as the partial order on their induced
    equivalence relations. -/
instance Partition.partialOrder : PartialOrder (Subtype (@IsPartition α))
    where
  le := (· ≤ ·)
  lt x y := x ≤ y ∧ ¬y ≤ x
  le_refl _ := @le_refl (Setoid α) _ _
  le_trans _ _ _ := @le_trans (Setoid α) _ _ _ _
  lt_iff_le_not_le _ _ := Iff.rfl
  le_antisymm x y hx hy := by
    

Proof state:
α : Type u_1
x y : Subtype IsPartition
hx : x ≤ y
hy : y ≤ x
⊢ x = y

Next tactic:
let h := @le_antisymm (Setoid α) _ _ _ hx hy
/-- Defining a partial order on partitions as the partial order on their induced
    equivalence relations. -/
instance Partition.partialOrder : PartialOrder (Subtype (@IsPartition α))
    where
  le := (· ≤ ·)
  lt x y := x ≤ y ∧ ¬y ≤ x
  le_refl _ := @le_refl (Setoid α) _ _
  le_trans _ _ _ := @le_trans (Setoid α) _ _ _ _
  lt_iff_le_not_le _ _ := Iff.rfl
  le_antisymm x y hx hy := by
    let h := @le_antisymm (Setoid α) _ _ _ hx hy
    

Proof state:
α : Type u_1
x y : Subtype IsPartition
hx : x ≤ y
hy : y ≤ x
h : mkClasses ↑x (_ : ∀ (a : α), ∃! b x, a ∈ b) = mkClasses ↑y (_ : ∀ (a : α), ∃! b x, a ∈ b) := le_antisymm hx hy
⊢ x = y

Next tactic:
rw [Subtype.ext_iff_val, ← classes_mkClasses x.1 x.2, ← classes_mkClasses y.1 y.2, h]
/-- The order-preserving bijection between equivalence relations on a type `α`, and
  partitions of `α` into subsets. -/
protected def Partition.orderIso : Setoid α ≃o { C : Set (Set α) // IsPartition C }
    where
  toFun r := ⟨r.classes, empty_not_mem_classes, classes_eqv_classes⟩
  invFun C := mkClasses C.1 C.2.2
  left_inv := mkClasses_classes
  right_inv C := by 

Proof state:
α : Type u_1
C : { C // IsPartition C }
⊢ (fun r => { val := classes r, property := (_ : ∅ ∉ classes r ∧ ∀ (a : α), ∃! b x, a ∈ b) })
      ((fun C => mkClasses ↑C (_ : ∀ (a : α), ∃! b x, a ∈ b)) C) =
    C

Next tactic:
rw [Subtype.ext_iff_val, ← classes_mkClasses C.1 C.2]
/-- The order-preserving bijection between equivalence relations on a type `α`, and
  partitions of `α` into subsets. -/
protected def Partition.orderIso : Setoid α ≃o { C : Set (Set α) // IsPartition C }
    where
  toFun r := ⟨r.classes, empty_not_mem_classes, classes_eqv_classes⟩
  invFun C := mkClasses C.1 C.2.2
  left_inv := mkClasses_classes
  right_inv C := by rw [Subtype.ext_iff_val, ← classes_mkClasses C.1 C.2]
  map_rel_iff' {r s} := by
    

Proof state:
α : Type u_1
r s : Setoid α
⊢ { toFun := fun r => { val := classes r, property := (_ : ∅ ∉ classes r ∧ ∀ (a : α), ∃! b x, a ∈ b) },
          invFun := fun C => mkClasses ↑C (_ : ∀ (a : α), ∃! b x, a ∈ b),
          left_inv := (_ : ∀ (r : Setoid α), mkClasses (classes r) (_ : ∀ (a : α), ∃! b x, a ∈ b) = r),
          right_inv :=
            (_ :
              ∀ (C : { C // IsPartition C }),
                (fun r => { val := classes r, property := (_ : ∅ ∉ classes r ∧ ∀ (a : α), ∃! b x, a ∈ b) })
                    ((fun C => mkClasses ↑C (_ : ∀ (a : α), ∃! b x, a ∈ b)) C) =
                  C) }
        r ≤
      { toFun := fun r => { val := classes r, property := (_ : ∅ ∉ classes r ∧ ∀ (a : α), ∃! b x, a ∈ b) },
          invFun := fun C => mkClasses ↑C (_ : ∀ (a : α), ∃! b x, a ∈ b),
          left_inv := (_ : ∀ (r : Setoid α), mkClasses (classes r) (_ : ∀ (a : α), ∃! b x, a ∈ b) = r),
          right_inv :=
            (_ :
              ∀ (C : { C // IsPartition C }),
                (fun r => { val := classes r, property := (_ : ∅ ∉ classes r ∧ ∀ (a : α), ∃! b x, a ∈ b) })
                    ((fun C => mkClasses ↑C (_ : ∀ (a : α), ∃! b x, a ∈ b)) C) =
                  C) }
        s ↔
    r ≤ s

Next tactic:
conv_rhs => rw [← mkClasses_classes r, ← mkClasses_classes s]
/-- The order-preserving bijection between equivalence relations on a type `α`, and
  partitions of `α` into subsets. -/
protected def Partition.orderIso : Setoid α ≃o { C : Set (Set α) // IsPartition C }
    where
  toFun r := ⟨r.classes, empty_not_mem_classes, classes_eqv_classes⟩
  invFun C := mkClasses C.1 C.2.2
  left_inv := mkClasses_classes
  right_inv C := by rw [Subtype.ext_iff_val, ← classes_mkClasses C.1 C.2]
  map_rel_iff' {r s} := by
    conv_rhs => 

Proof state:
α : Type u_1
r s : Setoid α
| r ≤ s

Next tactic:
rw [← mkClasses_classes r, ← mkClasses_classes s]
theorem iUnion : ⋃ i, s i = univ := by
  

Proof state:
ι : Type u_1
α : Type u_2
s : ι → Set α
hs : IndexedPartition s
⊢ ⋃ i, s i = univ

Next tactic:
ext x
theorem iUnion : ⋃ i, s i = univ := by
  ext x
  

Proof state:
case h
ι : Type u_1
α : Type u_2
s : ι → Set α
hs : IndexedPartition s
x : α
⊢ x ∈ ⋃ i, s i ↔ x ∈ univ

Next tactic:
simp [hs.exists_mem x]
theorem proj_fiber (x : hs.Quotient) : hs.proj ⁻¹' {x} = s (hs.equivQuotient.symm x) :=
  Quotient.inductionOn' x fun x => by
    

Proof state:
ι : Type u_1
α : Type u_2
s : ι → Set α
hs : IndexedPartition s
x✝ : IndexedPartition.Quotient hs
x : α
⊢ proj hs ⁻¹' {Quotient.mk'' x} = s ((equivQuotient hs).symm (Quotient.mk'' x))

Next tactic:
ext y
theorem proj_fiber (x : hs.Quotient) : hs.proj ⁻¹' {x} = s (hs.equivQuotient.symm x) :=
  Quotient.inductionOn' x fun x => by
    ext y
    

Proof state:
case h
ι : Type u_1
α : Type u_2
s : ι → Set α
hs : IndexedPartition s
x✝ : IndexedPartition.Quotient hs
x y : α
⊢ y ∈ proj hs ⁻¹' {Quotient.mk'' x} ↔ y ∈ s ((equivQuotient hs).symm (Quotient.mk'' x))

Next tactic:
simp only [Set.mem_preimage, Set.mem_singleton_iff, hs.mem_iff_index_eq]
theorem proj_fiber (x : hs.Quotient) : hs.proj ⁻¹' {x} = s (hs.equivQuotient.symm x) :=
  Quotient.inductionOn' x fun x => by
    ext y
    simp only [Set.mem_preimage, Set.mem_singleton_iff, hs.mem_iff_index_eq]
    

Proof state:
case h
ι : Type u_1
α : Type u_2
s : ι → Set α
hs : IndexedPartition s
x✝ : IndexedPartition.Quotient hs
x y : α
⊢ proj hs y = Quotient.mk'' x ↔ index hs y = (equivQuotient hs).symm (Quotient.mk'' x)

Next tactic:
exact Quotient.eq''
lemma toUnitHom_eq_char' {a : ZMod n} (ha : IsUnit a) : χ a = χ.toUnitHom ha.unit := by 

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
a : ZMod n
ha : IsUnit a
⊢ χ a = ↑((toUnitHom χ) (IsUnit.unit ha))

Next tactic:
simp
lemma toUnitHom_eq_iff (ψ : DirichletCharacter R n) : toUnitHom χ = toUnitHom ψ ↔ χ = ψ := by 

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ ψ : DirichletCharacter R n
⊢ toUnitHom χ = toUnitHom ψ ↔ χ = ψ

Next tactic:
simp
lemma eval_modulus_sub (x : ZMod n) : χ (n - x) = χ (-x) := by 

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
x : ZMod n
⊢ χ (↑n - x) = χ (-x)

Next tactic:
simp
lemma periodic {m : ℕ} (hm : n ∣ m) : Function.Periodic χ m := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m : ℕ
hm : n ∣ m
⊢ Function.Periodic ⇑χ ↑m

Next tactic:
intro a
lemma periodic {m : ℕ} (hm : n ∣ m) : Function.Periodic χ m := by
  intro a
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m : ℕ
hm : n ∣ m
a : ZMod n
⊢ χ (a + ↑m) = χ a

Next tactic:
rw [← ZMod.nat_cast_zmod_eq_zero_iff_dvd] at hm
lemma periodic {m : ℕ} (hm : n ∣ m) : Function.Periodic χ m := by
  intro a
  rw [← ZMod.nat_cast_zmod_eq_zero_iff_dvd] at hm
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m : ℕ
hm : ↑m = 0
a : ZMod n
⊢ χ (a + ↑m) = χ a

Next tactic:
simp only [hm, add_zero]
/-- A function that modifies the level of a Dirichlet character to some multiple
  of its original level. -/
noncomputable def changeLevel {R : Type*} [CommMonoidWithZero R] {n m : ℕ} (hm : n ∣ m) :
    DirichletCharacter R n →* DirichletCharacter R m :=
  { toFun := fun ψ ↦ MulChar.ofUnitHom (ψ.toUnitHom.comp (ZMod.unitsMap hm)),
    map_one' := by 

Proof state:
R✝ : Type u_1
inst✝¹ : CommMonoidWithZero R✝
n✝ : ℕ
χ : DirichletCharacter R✝ n✝
R : Type u_2
inst✝ : CommMonoidWithZero R
n m : ℕ
hm : n ∣ m
⊢ (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1

Next tactic:
ext
/-- A function that modifies the level of a Dirichlet character to some multiple
  of its original level. -/
noncomputable def changeLevel {R : Type*} [CommMonoidWithZero R] {n m : ℕ} (hm : n ∣ m) :
    DirichletCharacter R n →* DirichletCharacter R m :=
  { toFun := fun ψ ↦ MulChar.ofUnitHom (ψ.toUnitHom.comp (ZMod.unitsMap hm)),
    map_one' := by ext; 

Proof state:
case h
R✝ : Type u_1
inst✝¹ : CommMonoidWithZero R✝
n✝ : ℕ
χ : DirichletCharacter R✝ n✝
R : Type u_2
inst✝ : CommMonoidWithZero R
n m : ℕ
hm : n ∣ m
a✝ : (ZMod m)ˣ
⊢ ((fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1) ↑a✝ = 1 ↑a✝

Next tactic:
simp
/-- A function that modifies the level of a Dirichlet character to some multiple
  of its original level. -/
noncomputable def changeLevel {R : Type*} [CommMonoidWithZero R] {n m : ℕ} (hm : n ∣ m) :
    DirichletCharacter R n →* DirichletCharacter R m :=
  { toFun := fun ψ ↦ MulChar.ofUnitHom (ψ.toUnitHom.comp (ZMod.unitsMap hm)),
    map_one' := by ext; simp,
    map_mul' := fun ψ₁ ψ₂ ↦ by 

Proof state:
R✝ : Type u_1
inst✝¹ : CommMonoidWithZero R✝
n✝ : ℕ
χ : DirichletCharacter R✝ n✝
R : Type u_2
inst✝ : CommMonoidWithZero R
n m : ℕ
hm : n ∣ m
ψ₁ ψ₂ : DirichletCharacter R n
⊢ OneHom.toFun
      { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
        map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
      (ψ₁ * ψ₂) =
    OneHom.toFun
        { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
          map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
        ψ₁ *
      OneHom.toFun
        { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
          map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
        ψ₂

Next tactic:
ext
/-- A function that modifies the level of a Dirichlet character to some multiple
  of its original level. -/
noncomputable def changeLevel {R : Type*} [CommMonoidWithZero R] {n m : ℕ} (hm : n ∣ m) :
    DirichletCharacter R n →* DirichletCharacter R m :=
  { toFun := fun ψ ↦ MulChar.ofUnitHom (ψ.toUnitHom.comp (ZMod.unitsMap hm)),
    map_one' := by ext; simp,
    map_mul' := fun ψ₁ ψ₂ ↦ by ext; 

Proof state:
case h
R✝ : Type u_1
inst✝¹ : CommMonoidWithZero R✝
n✝ : ℕ
χ : DirichletCharacter R✝ n✝
R : Type u_2
inst✝ : CommMonoidWithZero R
n m : ℕ
hm : n ∣ m
ψ₁ ψ₂ : DirichletCharacter R n
a✝ : (ZMod m)ˣ
⊢ (OneHom.toFun
        { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
          map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
        (ψ₁ * ψ₂))
      ↑a✝ =
    (OneHom.toFun
          { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
            map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
          ψ₁ *
        OneHom.toFun
          { toFun := fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm)),
            map_one' := (_ : (fun ψ => ofUnitHom (MonoidHom.comp (toUnitHom ψ) (ZMod.unitsMap hm))) 1 = 1) }
          ψ₂)
      ↑a✝

Next tactic:
simp
lemma changeLevel_def' {m : ℕ} (hm : n ∣ m) :
    (changeLevel hm χ).toUnitHom = χ.toUnitHom.comp (ZMod.unitsMap hm) := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m : ℕ
hm : n ∣ m
⊢ toUnitHom ((changeLevel hm) χ) = MonoidHom.comp (toUnitHom χ) (ZMod.unitsMap hm)

Next tactic:
simp [changeLevel]
@[simp]
lemma changeLevel_self : changeLevel (dvd_refl n) χ = χ := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
⊢ (changeLevel (_ : n ∣ n)) χ = χ

Next tactic:
simp [changeLevel, ZMod.unitsMap]
lemma changeLevel_self_toUnitHom : (changeLevel (dvd_refl n) χ).toUnitHom = χ.toUnitHom := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
⊢ toUnitHom ((changeLevel (_ : n ∣ n)) χ) = toUnitHom χ

Next tactic:
rw [changeLevel_self]
lemma changeLevel_trans {m d : ℕ} (hm : n ∣ m) (hd : m ∣ d) :
    changeLevel (dvd_trans hm hd) χ = changeLevel hd (changeLevel hm χ) := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m d : ℕ
hm : n ∣ m
hd : m ∣ d
⊢ (changeLevel (_ : n ∣ d)) χ = (changeLevel hd) ((changeLevel hm) χ)

Next tactic:
simp [changeLevel_def, MonoidHom.comp_assoc, ZMod.unitsMap_comp]
lemma changeLevel_eq_cast_of_dvd {m : ℕ} (hm : n ∣ m) (a : Units (ZMod m)) :
    (changeLevel hm χ) a = χ a := by
  

Proof state:
R : Type u_1
inst✝ : CommMonoidWithZero R
n : ℕ
χ : DirichletCharacter R n
m : ℕ
hm : n ∣ m
a : (ZMod m)ˣ
⊢ ((changeLevel hm) χ) ↑a = χ ↑↑a

Next tactic:
simpa [changeLevel_def, Function.comp_apply, MonoidHom.coe_comp] using
      toUnitHom_eq_char' _ <| ZMod.IsUnit_cast_of_dvd hm a