/-
Copyright (c) 2022 Kyle Miller. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kyle Miller, Vincent Beffara

! This file was ported from Lean 3 source module combinatorics.simple_graph.metric
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Combinatorics.SimpleGraph.Connectivity
import Mathbin.Data.Nat.Lattice

/-!
# Graph metric

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This module defines the `simple_graph.dist` function, which takes
pairs of vertices to the length of the shortest walk between them.

## Main definitions

- `simple_graph.dist` is the graph metric.

## Todo

- Provide an additional computable version of `simple_graph.dist`
  for when `G` is connected.

- Evaluate `nat` vs `enat` for the codomain of `dist`, or potentially
  having an additional `edist` when the objects under consideration are
  disconnected graphs.

- When directed graphs exist, a directed notion of distance,
  likely `enat`-valued.

## Tags

graph metric, distance

-/

namespace SimpleGraph

variable {V : Type _} (G : SimpleGraph V)

/-! ## Metric -/

#print SimpleGraph.dist /-
/-- The distance between two vertices is the length of the shortest walk between them.
If no such walk exists, this uses the junk value of `0`. -/
noncomputable def dist (u v : V) : ℕ :=
  infₛ (Set.range (Walk.length : G.Walk u v → ℕ))
#align simple_graph.dist SimpleGraph.dist
-/

variable {G}

#print SimpleGraph.Reachable.exists_walk_of_dist /-
protected theorem Reachable.exists_walk_of_dist {u v : V} (hr : G.Reachable u v) :
    ∃ p : G.Walk u v, p.length = G.dist u v :=
  Nat.infₛ_mem (Set.range_nonempty_iff_nonempty.mpr hr)
#align simple_graph.reachable.exists_walk_of_dist SimpleGraph.Reachable.exists_walk_of_dist
-/

#print SimpleGraph.Connected.exists_walk_of_dist /-
protected theorem Connected.exists_walk_of_dist (hconn : G.Connected) (u v : V) :
    ∃ p : G.Walk u v, p.length = G.dist u v :=
  (hconn u v).exists_walk_of_dist
#align simple_graph.connected.exists_walk_of_dist SimpleGraph.Connected.exists_walk_of_dist
-/

#print SimpleGraph.dist_le /-
theorem dist_le {u v : V} (p : G.Walk u v) : G.dist u v ≤ p.length :=
  Nat.infₛ_le ⟨p, rfl⟩
#align simple_graph.dist_le SimpleGraph.dist_le
-/

#print SimpleGraph.dist_eq_zero_iff_eq_or_not_reachable /-
@[simp]
theorem dist_eq_zero_iff_eq_or_not_reachable {u v : V} :
    G.dist u v = 0 ↔ u = v ∨ ¬G.Reachable u v := by simp [dist, Nat.infₛ_eq_zero, reachable]
#align simple_graph.dist_eq_zero_iff_eq_or_not_reachable SimpleGraph.dist_eq_zero_iff_eq_or_not_reachable
-/

#print SimpleGraph.dist_self /-
theorem dist_self {v : V} : dist G v v = 0 := by simp
#align simple_graph.dist_self SimpleGraph.dist_self
-/

#print SimpleGraph.Reachable.dist_eq_zero_iff /-
protected theorem Reachable.dist_eq_zero_iff {u v : V} (hr : G.Reachable u v) :
    G.dist u v = 0 ↔ u = v := by simp [hr]
#align simple_graph.reachable.dist_eq_zero_iff SimpleGraph.Reachable.dist_eq_zero_iff
-/

#print SimpleGraph.Reachable.pos_dist_of_ne /-
protected theorem Reachable.pos_dist_of_ne {u v : V} (h : G.Reachable u v) (hne : u ≠ v) :
    0 < G.dist u v :=
  Nat.pos_of_ne_zero (by simp [h, hne])
#align simple_graph.reachable.pos_dist_of_ne SimpleGraph.Reachable.pos_dist_of_ne
-/

#print SimpleGraph.Connected.dist_eq_zero_iff /-
protected theorem Connected.dist_eq_zero_iff (hconn : G.Connected) {u v : V} :
    G.dist u v = 0 ↔ u = v := by simp [hconn u v]
#align simple_graph.connected.dist_eq_zero_iff SimpleGraph.Connected.dist_eq_zero_iff
-/

#print SimpleGraph.Connected.pos_dist_of_ne /-
protected theorem Connected.pos_dist_of_ne {u v : V} (hconn : G.Connected) (hne : u ≠ v) :
    0 < G.dist u v :=
  Nat.pos_of_ne_zero (by simp [hconn.dist_eq_zero_iff, hne])
#align simple_graph.connected.pos_dist_of_ne SimpleGraph.Connected.pos_dist_of_ne
-/

#print SimpleGraph.dist_eq_zero_of_not_reachable /-
theorem dist_eq_zero_of_not_reachable {u v : V} (h : ¬G.Reachable u v) : G.dist u v = 0 := by
  simp [h]
#align simple_graph.dist_eq_zero_of_not_reachable SimpleGraph.dist_eq_zero_of_not_reachable
-/

#print SimpleGraph.nonempty_of_pos_dist /-
theorem nonempty_of_pos_dist {u v : V} (h : 0 < G.dist u v) :
    (Set.univ : Set (G.Walk u v)).Nonempty := by
  simpa [Set.range_nonempty_iff_nonempty, Set.nonempty_iff_univ_nonempty] using
    Nat.nonempty_of_pos_infₛ h
#align simple_graph.nonempty_of_pos_dist SimpleGraph.nonempty_of_pos_dist
-/

#print SimpleGraph.Connected.dist_triangle /-
protected theorem Connected.dist_triangle (hconn : G.Connected) {u v w : V} :
    G.dist u w ≤ G.dist u v + G.dist v w :=
  by
  obtain ⟨p, hp⟩ := hconn.exists_walk_of_dist u v
  obtain ⟨q, hq⟩ := hconn.exists_walk_of_dist v w
  rw [← hp, ← hq, ← walk.length_append]
  apply dist_le
#align simple_graph.connected.dist_triangle SimpleGraph.Connected.dist_triangle
-/

private theorem dist_comm_aux {u v : V} (h : G.Reachable u v) : G.dist u v ≤ G.dist v u :=
  by
  obtain ⟨p, hp⟩ := h.symm.exists_walk_of_dist
  rw [← hp, ← walk.length_reverse]
  apply dist_le
#align simple_graph.dist_comm_aux simple_graph.dist_comm_aux

#print SimpleGraph.dist_comm /-
theorem dist_comm {u v : V} : G.dist u v = G.dist v u :=
  by
  by_cases h : G.reachable u v
  · apply le_antisymm (dist_comm_aux h) (dist_comm_aux h.symm)
  · have h' : ¬G.reachable v u := fun h' => absurd h'.symm h
    simp [h, h', dist_eq_zero_of_not_reachable]
#align simple_graph.dist_comm SimpleGraph.dist_comm
-/

end SimpleGraph


import for_mathlib.nonarchimedean.basic
import for_mathlib.topological_rings

open set filter function lattice add_group_with_zero_nhd

--local attribute [instance] pointwise_mul pointwise_add
local attribute [instance] set.pointwise_mul_semiring
local attribute [instance] set.smul_set_action

local notation `𝓝` x: 70 := nhds x

class subgroups_basis (A : Type*) [ring A] extends filter_basis A :=
[sub_groups {} : ∀ {G}, G ∈ sets → is_add_subgroup G]
(h_mul : ∀ {G}, G ∈ sets → ∃ H, H ∈ sets ∧ H * H ⊆ G)
(h_left_mul : ∀ x : A, ∀ {G}, G ∈ sets → ∃ H, H ∈ sets ∧ H ⊆ (λ y : A, x*y) ⁻¹' G)
(h_right_mul : ∀ x : A, ∀ {G}, G ∈ sets → ∃ H, H ∈ sets ∧ H ⊆ (λ y : A, y*x) ⁻¹' G)

namespace subgroups_basis
variables (A : Type*) [ring A] [subgroups_basis A]

instance to_ring_filter_basis : ring_filter_basis A :=
{ zero := begin
    intros G G_in,
    haveI := subgroups_basis.sub_groups G_in,
    exact is_add_submonoid.zero_mem G,
  end,
  add := begin
    intros G G_in,
    use [G, G_in],
    haveI := subgroups_basis.sub_groups G_in,
    rintro _ ⟨x, x_in, y, y_in, rfl⟩,
    exact is_add_submonoid.add_mem x_in y_in,
  end,
  neg := begin
    intros G G_in,
    use [G, G_in],
    intros x x_in,
    haveI : is_add_subgroup G := subgroups_basis.sub_groups G_in,
    apply is_add_subgroup.neg_mem x_in
  end,
  conj := begin
    intros x₀ U U_in,
    use [U, U_in],
    intros x x_in,
    simp [x_in]
  end,
  mul := λ G G_in, by simpa using h_mul G_in,
  mul_left :=  λ x₀ G G_in, by simpa using h_left_mul x₀ G_in,
  mul_right := λ x₀ G G_in, by simpa using h_right_mul x₀ G_in,
  ..‹subgroups_basis A› }

open subgroups_basis

def basis : set (set A) := (subgroups_basis.to_filter_basis A).sets

def topology : topological_space A :=
  (subgroups_basis.to_ring_filter_basis A).to_add_group_filter_basis.topology

lemma is_op [t : topological_space A] (h : t = topology A) {G : set A} (hG : G ∈ basis A) :
  is_open G :=
begin
  haveI := subgroups_basis.sub_groups hG,
  rw is_open_iff_mem_nhds,
  intros a ha,
  exact (add_group_filter_basis.mem_nhds
          (subgroups_basis.to_ring_filter_basis A).to_add_group_filter_basis h).2
        ⟨G, hG, λ g hg, is_add_submonoid.add_mem ha hg⟩
end

local attribute [instance] subgroups_basis.topology

def nhds_basis : nhds_basis A :=
(subgroups_basis.to_ring_filter_basis A).to_add_group_filter_basis.nhds_basis rfl

local attribute [instance] subgroups_basis.nhds_basis

variables {A}

lemma mem_nhds {s : set A} {x : A} : s ∈ 𝓝 x ↔ ∃ G ∈ basis A, {y | y - x ∈ G} ⊆ s :=
begin
  rw add_group_filter_basis.mem_nhds _ rfl,
  apply exists_congr,
  intro t,
  apply exists_congr,
  intro h,
  rw ← image_subset_iff,
  have l :left_inverse (λ y, y - x) (λ y, x + y),
  { intro y, simp only [], abel, },
  have r :right_inverse (λ y, y - x) (λ y, x + y),
  { intro y, simp only [], abel, },
  rw image_eq_preimage_of_inverse l r,
  exact iff.rfl
end

lemma mem_nhds_zero {s : set A} : s ∈ 𝓝 (0 : A) ↔ ∃ G ∈ basis A, G ⊆ s :=
by simp [mem_nhds]

lemma tendsto_into {α : Type*} (F : filter α) (f : α → A) (a : A) :
  tendsto f F 𝓝 a ↔ ∀ G ∈ basis A, {x | f x - a ∈ G} ∈ F :=
begin
  rw nhds_basis.tendsto_into,
  split ; intros h,
  { intros G G_in,
    specialize h {y : A | y - a ∈ G},
    rw add_group_filter_basis.mem_nhds_basis at h,
    apply h,
    simpa using G_in },
  { intros U U_in,
    rw add_group_filter_basis.mem_nhds_basis at U_in,
    specialize h _ U_in,
    change {x : α | a + (f x - a) ∈ U} ∈ F at h,
    simpa only [show ∀ x, a + (f x - a) = f x, by intros ; abel] using h },
end

lemma continuous_into {α : Type*} [topological_space α] (f : α → A) :
  continuous f ↔ ∀ x, ∀ G ∈ basis A, {x' | f x' - f x ∈ G} ∈ 𝓝 x :=
begin
  rw continuous_iff_continuous_at,
  apply forall_congr,
  exact λ _, tendsto_into _ _ _,
end

def is_topological_add_group : topological_add_group A :=
  (subgroups_basis.to_ring_filter_basis A).to_add_group_filter_basis.is_topological_group rfl

local attribute [instance] is_topological_add_group

lemma nonarchimedean : topological_add_group.nonarchimedean A :=
begin
  intros U hU,
  rcases mem_nhds_zero.mp hU with ⟨G, G_in, hG⟩,
  exact ⟨⟨G, ⟨is_op A rfl G_in, subgroups_basis.sub_groups G_in⟩⟩, hG⟩
end

section comm_ring

variables {R : Type*} [comm_ring R] {ι : Type*} [inhabited ι] (G : ι → set R) [∀ i, is_add_subgroup $ G i]
  (h_directed : ∀ i j, ∃ k, G k ⊆ G i ∩ G j)
  (h_left_mul : ∀ (x : R) i, ∃ j, x • (G j) ⊆ G i)
  (h_mul : ∀ i, ∃ j, G j * G j ⊆ G i)
include h_directed h_left_mul h_mul

def of_indexed_of_comm : subgroups_basis R :=
{ sets := range G,
  ne_empty := range_nonempty _,
  directed := begin
    rintros _ _ ⟨i, rfl⟩ ⟨j, rfl⟩,
    rw exists_mem_range,
    tauto
  end,
  sub_groups := begin
    rintro _ ⟨i, rfl⟩,
    apply_instance
  end,
  h_mul := begin
    rintros _ ⟨i, rfl⟩,
    rw exists_mem_range',
    tauto
  end,
  h_left_mul := begin
    rintros x _ ⟨i, rfl⟩,
    rw exists_mem_range',
    rcases h_left_mul x i with ⟨j, h⟩,
    use j,
    rw ← image_subset_iff,
    change (λ y, x • y) '' G j ⊆ G i,
    rwa ← smul_set_eq_image,
  end,
  h_right_mul := begin
    rintros x _ ⟨i, rfl⟩,
    rw exists_mem_range',
    rcases h_left_mul x i with ⟨j, h⟩,
    use j,
    simp only [mul_comm],
    rw ← image_subset_iff,
    change  (λ y, x • y) '' G j ⊆ G i,
    rwa ← smul_set_eq_image
  end }

end comm_ring
section comm_algebra
open algebra submodule

variables {R : Type*} {B : Type*} [comm_ring R] [comm_ring B] [algebra R B]
  {ι : Type*} [inhabited ι] (M : ι → submodule R B)
  (h_directed : ∀ i j, ∃ k, M k ≤ M i ⊓ M j)
  (h_left_mul : ∀ (a : B) i, ∃ j, a • M j ≤ M i)
  (h_mul      : ∀ i, ∃ j, M j * M j ≤ M i)
include h_directed h_left_mul h_mul

def of_indexed_submodules_of_comm : subgroups_basis B :=
begin
  letI : ∀ i, is_add_subgroup (M i).carrier := λ i, submodule.submodule_is_add_subgroup _,
  apply of_indexed_of_comm _ h_directed,
  { intros x i,
      cases h_left_mul x i with j hj,
      use j,
      erw smul_singleton at hj,
      rw set.smul_set_eq_image,
      exact hj },
    { intro i,
      cases h_mul i with j hj,
      use j,
      rintros _ ⟨x, hx, y, hy, rfl⟩,
    exact hj (mul_mem_mul hx hy) }
end

end comm_algebra

end subgroups_basis


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.pempty
! leanprover-community/mathlib commit 23aa88e32dcc9d2a24cca7bc23268567ed4cd7d6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.DiscreteCategory

/-!
# The empty category

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

Defines a category structure on `pempty`, and the unique functor `pempty ⥤ C` for any category `C`.
-/

universe w v u

-- morphism levels before object levels. See note [category_theory universes].
namespace CategoryTheory

namespace Functor

variable (C : Type u) [Category.{v} C]

/- warning: category_theory.functor.empty_equivalence -> CategoryTheory.Functor.emptyEquivalence is a dubious translation:
lean 3 declaration is
  CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} PEmpty.{succ u1}) (CategoryTheory.discreteCategory.{u1} PEmpty.{succ u1}) (CategoryTheory.Discrete.{u2} PEmpty.{succ u2}) (CategoryTheory.discreteCategory.{u2} PEmpty.{succ u2})
but is expected to have type
  CategoryTheory.Equivalence.{u1, u2, u1, u2} (CategoryTheory.Discrete.{u1} PEmpty.{succ u1}) (CategoryTheory.Discrete.{u2} PEmpty.{succ u2}) (CategoryTheory.discreteCategory.{u1} PEmpty.{succ u1}) (CategoryTheory.discreteCategory.{u2} PEmpty.{succ u2})
Case conversion may be inaccurate. Consider using '#align category_theory.functor.empty_equivalence CategoryTheory.Functor.emptyEquivalenceₓ'. -/
/-- Equivalence between two empty categories. -/
def emptyEquivalence : Discrete.{w} PEmpty ≌ Discrete.{v} PEmpty :=
  Equivalence.mk
    { obj := PEmpty.elim ∘ Discrete.as
      map := fun x => x.as.elim }
    { obj := PEmpty.elim ∘ Discrete.as
      map := fun x => x.as.elim } (by tidy) (by tidy)
#align category_theory.functor.empty_equivalence CategoryTheory.Functor.emptyEquivalence

#print CategoryTheory.Functor.empty /-
/-- The canonical functor out of the empty category. -/
def empty : Discrete.{w} PEmpty ⥤ C :=
  Discrete.functor PEmpty.elim
#align category_theory.functor.empty CategoryTheory.Functor.empty
-/

variable {C}

#print CategoryTheory.Functor.emptyExt /-
/-- Any two functors out of the empty category are isomorphic. -/
def emptyExt (F G : Discrete.{w} PEmpty ⥤ C) : F ≅ G :=
  Discrete.natIso fun x => x.as.elim
#align category_theory.functor.empty_ext CategoryTheory.Functor.emptyExt
-/

#print CategoryTheory.Functor.uniqueFromEmpty /-
/-- Any functor out of the empty category is isomorphic to the canonical functor from the empty
category.
-/
def uniqueFromEmpty (F : Discrete.{w} PEmpty ⥤ C) : F ≅ empty C :=
  emptyExt _ _
#align category_theory.functor.unique_from_empty CategoryTheory.Functor.uniqueFromEmpty
-/

#print CategoryTheory.Functor.empty_ext' /-
/-- Any two functors out of the empty category are *equal*. You probably want to use
`empty_ext` instead of this.
-/
theorem empty_ext' (F G : Discrete.{w} PEmpty ⥤ C) : F = G :=
  Functor.ext (fun x => x.as.elim) fun x _ _ => x.as.elim
#align category_theory.functor.empty_ext' CategoryTheory.Functor.empty_ext'
-/

end Functor

end CategoryTheory


import algebra.group data.equiv.basic
import group_theory.subgroup
import group_theory.quotient_group
import for_mathlib.equiv

variables {G : Type*} [group G]
open quotient_group

-- this one lemma is not PR'ed yet.
def mul_equiv.quot_eq_of_eq {G1 : set G} [normal_subgroup G1] {G2 : set G} [normal_subgroup G2]
(h : G1 = G2) : mul_equiv (quotient G1) (quotient G2) :=
{ to_fun := λ q, quotient.lift_on' q (quotient_group.mk : G → quotient G2) $
    λ a b hab, quotient.sound'
  begin
    change a⁻¹ * b ∈ G1 at hab, rwa h at hab
  end,
  inv_fun := λ q, quotient.lift_on' q (quotient_group.mk : G → quotient G1) $
    λ a b hab, quotient.sound'
  begin
    change a⁻¹ * b ∈ G2 at hab, rwa ←h at hab,
  end,
  left_inv := λ x, by induction x; refl,
  right_inv := λ x, by induction x; refl,
  map_mul' := λ a b, begin
    let f : G → quotient G2 := quotient_group.mk,
    have h2 := quotient_group.is_group_hom_quotient_lift G1 f,
    have h3 := h2 (λ x hx, by rwa [←is_group_hom.mem_ker f, quotient_group.ker_mk G2, ←h]),
    have h4 := h3.map_mul,
    exact h4 a b,
  end
  }

variables {M : Type*} [monoid M]

lemma units.ext_inv (a b : units M) (h : a.inv = b.inv) : a = b :=
inv_inj $ units.ext h

-- is this true for non-commutative monoids?
-- KL: No, s := nat.pred, t := nat.succ, u := id
/-- produces a unit s from a proof that s divides a unit -/
def units.unit_of_mul_left_eq_unit {M : Type*} [comm_monoid M]
  {s t : M} {u : units M}
(h : s * t = u) : units M :=
{ val := s,
  inv := t * (u⁻¹ : units M),
  val_inv := by {show s * (t * (u⁻¹ : units M)) = 1, rw [←mul_assoc, h], simp},
  inv_val := by {show t * (u⁻¹ : units M) * s = 1, rw [mul_comm, ←mul_assoc, h], simp} }


/-
Copyright (c) 2023 Michail Karatarakis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Michail Karatarakis
-/

import ring_theory.ideal.local_ring
import ring_theory.valuation.valuation_subring
import number_theory.number_field.basic
import ring_theory.dedekind_domain.adic_valuation
import ring_theory.valuation.ramification_group

namespace local_ring

variables {A : Type*} {B : Type*} [comm_ring B] [comm_ring A]
  [local_ring A] [local_ring B] [algebra A B] [is_local_ring_hom (algebra_map A B)]

instance (e : B ≃+* B) : is_local_ring_hom (e.to_ring_hom) := {
  map_nonunit := begin
  rintros b  ⟨u, hu⟩,
  have huinv : u * u⁻¹ = 1,
    { simp only [mul_right_inv], },
  unfold is_unit,
  change ∃ v, _,
  let v := units.map((e.symm).to_ring_hom.to_monoid_hom) u,
  use v,
  change e.symm(u) = b,
  change (u : B) = e b at hu,
  rw hu,
  simp only [ring_equiv.symm_apply_apply],
end }

/- -/
noncomputable instance general_algebra_map :
  algebra (local_ring.residue_field A) (local_ring.residue_field B) :=
ring_hom.to_algebra (local_ring.residue_field.map (algebra_map A B))

/-- The group homomorphism from the Galois group to the Galois group of residue fields. -/
def algebra_equiv_to_residue_equiv :
(B ≃ₐ[A] B) →* ((local_ring.residue_field B) ≃ₐ[local_ring.residue_field A] (local_ring.residue_field B)) :=
{ to_fun := λ x,
  { to_fun := local_ring.residue_field.map (x.to_ring_equiv.to_ring_hom),
    inv_fun := local_ring.residue_field.map (x.symm.to_ring_equiv.to_ring_hom),
    left_inv := λ y, begin
      simp only [alg_equiv.to_ring_equiv_eq_coe, alg_equiv.symm_to_ring_equiv, residue_field.map_map,
      ring_equiv.symm_to_ring_hom_comp_to_ring_hom, residue_field.map_id, ring_hom.id_apply],
      end,
    right_inv := λ y, begin
      simp only [alg_equiv.to_ring_equiv_eq_coe, alg_equiv.symm_to_ring_equiv, residue_field.map_map,
      ring_equiv.to_ring_hom_comp_symm_to_ring_hom, residue_field.map_id, ring_hom.id_apply],
      end,
    map_mul' := λ x y, begin
      simp only [map_mul],
      end,
    map_add' := λ x y, begin
      simp only [map_add],
      end,
    commutes' := begin
      have hx := x.commutes,
      suffices : (residue_field.map x.to_ring_equiv.to_ring_hom).comp (algebra_map (residue_field A) (residue_field B)) = algebra_map (residue_field A) (residue_field B),
      rwa fun_like.ext_iff at this,
      have hres : function.surjective (residue A), {
      exact (ideal.quotient.mk (maximal_ideal A)).is_surjective,
      },
      rw ← ring_hom.cancel_right hres,
      rw ring_hom.comp_assoc,
      change _ = (residue_field.map _).comp _,
      rw residue_field.map_comp_residue,
      change (residue_field.map x.to_ring_equiv.to_ring_hom).comp ((residue_field.map _).comp _) = _,
      rw residue_field.map_comp_residue,
      rw ← ring_hom.comp_assoc,
      rw residue_field.map_comp_residue,
      ext r,
      simp only [alg_equiv.to_ring_equiv_eq_coe, ring_hom.coe_comp, function.comp_app],
      congr',
      exact hx r,
      end, },
  map_one' := begin
    ext,
    simp only [alg_equiv.to_ring_equiv_eq_coe, alg_equiv.coe_mk, alg_equiv.one_apply],
    have hid := local_ring.residue_field.map_id_apply a,
    apply hid,
    end,
  map_mul' := λ x y,
    begin
    ext,
    simp only [alg_equiv.to_ring_equiv_eq_coe, residue_field.map_map, alg_equiv.coe_mk, alg_equiv.mul_apply],
    refl,
    end }

end local_ring

namespace valuation_subring

variables {K L : Type*} [field K] [field L]

/-- The map from the pullback of a valuation subring A to A along a ring homomorphism K →+* L -/
def ring_hom.valuation_subring_comap (A : valuation_subring L) (f : K →+* L):
   (comap A f) →+* A :=
{ to_fun := λ x, ⟨f x.1, x.2⟩,
  map_one' := subtype.eq f.map_one,
  map_mul' := λ x y, subtype.eq (f.map_mul x y),
  map_add' := λ x y, subtype.eq (f.map_add x y),
  map_zero' := subtype.eq f.map_zero, }

/-- The map from the pullback of a valuation subring A to A along a ring homomorphism K →+* L, is local -/
instance valuation_subring_comap_local (A : valuation_subring L) (f : K →+* L) :
  (is_local_ring_hom (ring_hom.valuation_subring_comap A f))  :=
{ map_nonunit :=
  begin
    rintros ⟨a, ha⟩ ⟨y, hy : (y : ↥A) = ⟨f a, _⟩⟩,
    have hainv : a * a⁻¹ = 1,
    { apply mul_inv_cancel,
      rintro rfl,
      have hy0 : (y : A) = 0, simp [hy, f.map_zero], refl,
      have this : (0 : A) ≠ 1 := zero_ne_one,
      rw [← zero_mem_nonunits, ← hy0] at this,
      exact this (units.is_unit y),
    },
    refine is_unit_of_mul_eq_one ⟨a, ha⟩ ⟨a⁻¹, (_ : f a⁻¹ ∈ A)⟩
      (_ : (⟨a * a⁻¹, _⟩ : A.comap f)= ⟨1, _⟩), swap, simp [hainv],
    rcases y with ⟨⟨y1, h₁⟩, ⟨y2, h₂⟩, h1, h2⟩,
    convert h₂,
    have hy' : y1 = f a, simpa using hy,
    have h1' : y1 * y2 = 1 := subtype.mk.inj h1,
    apply_fun f at hainv,
    rw [map_mul, map_one] at hainv,
    rw hy' at h1',
    exact inv_unique hainv h1',
  end
}
/-  -/
noncomputable instance algebra_comap (A : valuation_subring L) (f : K →+* L) :
   algebra (local_ring.residue_field (comap A f)) (local_ring.residue_field A) :=
ring_hom.to_algebra (local_ring.residue_field.map (ring_hom.valuation_subring_comap A f))

section

variables (K) [algebra K L]

open_locale

/-- The group homomorphism from the decomposition group to the group
 of automorphisms of the residue field of a valuation subring A-/
def decomposition_subgroup_to_residue_aut (A : valuation_subring L) :
 (A.decomposition_subgroup K) →* ring_aut (local_ring.residue_field A) :=
 (local_ring.residue_field.map_aut).comp
 (mul_semiring_action.to_ring_aut (A.decomposition_subgroup K) A)

instance foo (A : valuation_subring L) : algebra (comap A (algebra_map K L)) A :=
ring_hom.to_algebra (ring_hom.valuation_subring_comap A (algebra_map K L))

/-- The group homomorphism from the decomposition group to the Galois group of
A fixing the pullback of A. -/
def decomposition_subgroup.restrict (A : valuation_subring L) :
  (A.decomposition_subgroup K) →* (A ≃ₐ[comap A (algebra_map K L)] A) := {
  to_fun := λ x, {
    commutes' := begin
      rintros ⟨r, hr⟩,
      cases x with d hd,
      have := d.commutes,
      specialize this r,
      apply subtype.ext,
      exact this,
    end,
    ..(mul_semiring_action.to_ring_aut (A.decomposition_subgroup K) A x) },
  map_one' := begin
    ext,
    simp only [map_one, alg_equiv.coe_mk, alg_equiv.one_apply],
    refl,
    end,
  map_mul' := begin
    rintros x y,
    ext,
    simp only [alg_equiv.mul_apply, alg_equiv.coe_mk, map_mul],
    refl,
    end,
    }

end

open_locale number_field
open is_dedekind_domain

variables [number_field K] [number_field L] [algebra K L] [is_galois K L] (K)

open valuation_subring

/-- Obtaining the valuation subring of L from the nonzero prime
 ideals of its ring of integers-/
noncomputable def _root_.is_dedekind_domain.height_one_spectrum.valuation_subring
 (q : height_one_spectrum (𝓞 L)) : valuation_subring L :=
  q.valuation.valuation_subring

/-- The natural reduction homomorphism from the decomposition group
  to the Galois group of the residue field of A
  fixing the residue field of the pullback of A -/
noncomputable def decomposition_subgroup.to_residue_field_equiv (A : valuation_subring L) :
  (decomposition_subgroup K A) →*
  ((local_ring.residue_field A) ≃ₐ[local_ring.residue_field (comap A (algebra_map K L))]
  (local_ring.residue_field A)) :=
  (local_ring.algebra_equiv_to_residue_equiv).comp (decomposition_subgroup.restrict K A)

/- The natural reduction homomorphism is surjective. -/
theorem decomposition_subgroup.surjective (q : height_one_spectrum (𝓞 L)) :
  function.surjective (decomposition_subgroup.to_residue_field_equiv K
  q.valuation_subring) := sorry

lemma equal_kernels (q : height_one_spectrum (𝓞 L)) :
  (decomposition_subgroup.to_residue_field_equiv K
q.valuation_subring).ker = inertia_subgroup K q.valuation_subring := sorry

/-- If inertia is trivial, the natural reduction homomorphism is bijective. -/
theorem decomposition_subgroup.bijective (q : height_one_spectrum (𝓞 L))
  (h : inertia_subgroup K q.valuation_subring = ⊥) :
function.bijective (decomposition_subgroup.to_residue_field_equiv K
 q.valuation_subring) :=
begin
split,
{ have : (decomposition_subgroup.to_residue_field_equiv K
 q.valuation_subring).ker = ⊥,
{ rw equal_kernels K q,
  exact h,},
exact (decomposition_subgroup.to_residue_field_equiv K
 q.valuation_subring).ker_eq_bot_iff.mp this,},
{exact decomposition_subgroup.surjective K q}
end

/-- The Frobenius element as an element of the decomposition group -/
def frobq (ha : algebra.is_algebraic K L)
  (q : height_one_spectrum (𝓞 L))
  (h : inertia_subgroup K q.valuation_subring = ⊥) :
  (decomposition_subgroup K q.valuation_subring) := sorry

end valuation_subring