/-
Copyright (c) 2021 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot

! This file was ported from Lean 3 source module topology.algebra.with_zero_topology
! leanprover-community/mathlib commit 932872382355f00112641d305ba0619305dc8642
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Order.WithZero
import Mathbin.Topology.Algebra.Order.Field

/-!
# The topology on linearly ordered commutative groups with zero

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

Let `Γ₀` be a linearly ordered commutative group to which we have adjoined a zero element.
Then `Γ₀` may naturally be endowed with a topology that turns `Γ₀` into a topological monoid.
Neighborhoods of zero are sets containing `{γ | γ < γ₀}` for some invertible element `γ₀`
and every invertible element is open.
In particular the topology is the following:
"a subset `U ⊆ Γ₀` is open if `0 ∉ U` or if there is an invertible
`γ₀ ∈ Γ₀` such that `{γ | γ < γ₀} ⊆ U`", see `linear_ordered_comm_group_with_zero.is_open_iff`.

We prove this topology is ordered and T₃ (in addition to be compatible with the monoid
structure).

All this is useful to extend a valuation to a completion. This is an abstract version of how the
absolute value (resp. `p`-adic absolute value) on `ℚ` is extended to `ℝ` (resp. `ℚₚ`).

## Implementation notes

This topology is not defined as a global instance since it may not be the desired topology on a
linearly ordered commutative group with zero. You can locally activate this topology using
`open_locale with_zero_topology`.
-/

open Topology Filter

open TopologicalSpace Filter Set Function

namespace WithZeroTopology

variable {α Γ₀ : Type _} [LinearOrderedCommGroupWithZero Γ₀] {γ γ₁ γ₂ : Γ₀} {l : Filter α}
  {f : α → Γ₀}

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ «expr ≠ » 0) -/
#print WithZeroTopology.topologicalSpace /-
/-- The topology on a linearly ordered commutative group with a zero element adjoined.
A subset U is open if 0 ∉ U or if there is an invertible element γ₀ such that {γ | γ < γ₀} ⊆ U. -/
protected def topologicalSpace : TopologicalSpace Γ₀ :=
  TopologicalSpace.mkOfNhds <| update pure 0 <| ⨅ (γ) (_ : γ ≠ 0), 𝓟 (Iio γ)
#align with_zero_topology.topological_space WithZeroTopology.topologicalSpace
-/

attribute [scoped instance] WithZeroTopology.topologicalSpace

/- warning: with_zero_topology.nhds_eq_update -> WithZeroTopology.nhds_eq_update is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Eq.{succ u1} (Γ₀ -> (Filter.{u1} Γ₀)) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1)) (Function.update.{succ u1, succ u1} Γ₀ (fun (a : Γ₀) => Filter.{u1} Γ₀) (fun (a : Γ₀) (b : Γ₀) => Eq.decidable.{u1} Γ₀ (LinearOrderedCommMonoid.toLinearOrder.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))) a b) (Pure.pure.{u1, u1} (fun {Γ₀ : Type.{u1}} => Filter.{u1} Γ₀) Filter.hasPure.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))))))) (infᵢ.{u1, succ u1} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.completeLattice.{u1} Γ₀))) Γ₀ (fun (γ : Γ₀) => infᵢ.{u1, 0} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.completeLattice.{u1} Γ₀))) (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) => Filter.principal.{u1} Γ₀ (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ)))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Eq.{succ u1} (Γ₀ -> (Filter.{u1} Γ₀)) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1)) (Function.update.{succ u1, succ u1} Γ₀ (fun (a : Γ₀) => Filter.{u1} Γ₀) (fun (a : Γ₀) (b : Γ₀) => instDecidableEq.{u1} Γ₀ (LinearOrderedCommMonoid.toLinearOrder.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))) a b) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))) (infᵢ.{u1, succ u1} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.instCompleteLatticeFilter.{u1} Γ₀))) Γ₀ (fun (γ : Γ₀) => infᵢ.{u1, 0} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.instCompleteLatticeFilter.{u1} Γ₀))) (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) => Filter.principal.{u1} Γ₀ (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ)))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.nhds_eq_update WithZeroTopology.nhds_eq_updateₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ «expr ≠ » 0) -/
theorem nhds_eq_update : (𝓝 : Γ₀ → Filter Γ₀) = update pure 0 (⨅ (γ) (_ : γ ≠ 0), 𝓟 (Iio γ)) :=
  funext <| nhds_mkOfNhds_single <| le_infᵢ₂ fun γ h₀ => le_principal_iff.2 <| zero_lt_iff.2 h₀
#align with_zero_topology.nhds_eq_update WithZeroTopology.nhds_eq_update

/-!
### Neighbourhoods of zero
-/

/- warning: with_zero_topology.nhds_zero -> WithZeroTopology.nhds_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Eq.{succ u1} (Filter.{u1} Γ₀) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (infᵢ.{u1, succ u1} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.completeLattice.{u1} Γ₀))) Γ₀ (fun (γ : Γ₀) => infᵢ.{u1, 0} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.completeLattice.{u1} Γ₀))) (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) => Filter.principal.{u1} Γ₀ (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Eq.{succ u1} (Filter.{u1} Γ₀) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (infᵢ.{u1, succ u1} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.instCompleteLatticeFilter.{u1} Γ₀))) Γ₀ (fun (γ : Γ₀) => infᵢ.{u1, 0} (Filter.{u1} Γ₀) (ConditionallyCompleteLattice.toInfSet.{u1} (Filter.{u1} Γ₀) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} Γ₀) (Filter.instCompleteLatticeFilter.{u1} Γ₀))) (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) => Filter.principal.{u1} Γ₀ (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.nhds_zero WithZeroTopology.nhds_zeroₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ «expr ≠ » 0) -/
theorem nhds_zero : 𝓝 (0 : Γ₀) = ⨅ (γ) (_ : γ ≠ 0), 𝓟 (Iio γ) := by rw [nhds_eq_update, update_same]
#align with_zero_topology.nhds_zero WithZeroTopology.nhds_zero

/- warning: with_zero_topology.has_basis_nhds_zero -> WithZeroTopology.hasBasis_nhds_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Filter.HasBasis.{u1, succ u1} Γ₀ Γ₀ (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (fun (γ : Γ₀) => Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀], Filter.HasBasis.{u1, succ u1} Γ₀ Γ₀ (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (fun (γ : Γ₀) => Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.has_basis_nhds_zero WithZeroTopology.hasBasis_nhds_zeroₓ'. -/
/-- In a linearly ordered group with zero element adjoined, `U` is a neighbourhood of `0` if and
only if there exists a nonzero element `γ₀` such that `Iio γ₀ ⊆ U`. -/
theorem hasBasis_nhds_zero : (𝓝 (0 : Γ₀)).HasBasis (fun γ : Γ₀ => γ ≠ 0) Iio :=
  by
  rw [nhds_zero]
  refine' has_basis_binfi_principal _ ⟨1, one_ne_zero⟩
  exact directedOn_iff_directed.2 (directed_of_inf fun a b hab => Iio_subset_Iio hab)
#align with_zero_topology.has_basis_nhds_zero WithZeroTopology.hasBasis_nhds_zero

/- warning: with_zero_topology.Iio_mem_nhds_zero -> WithZeroTopology.Iio_mem_nhds_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) -> (Membership.Mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (Filter.hasMem.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) -> (Membership.mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (instMembershipSetFilter.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.Iio_mem_nhds_zero WithZeroTopology.Iio_mem_nhds_zeroₓ'. -/
theorem Iio_mem_nhds_zero (hγ : γ ≠ 0) : Iio γ ∈ 𝓝 (0 : Γ₀) :=
  hasBasis_nhds_zero.mem_of_mem hγ
#align with_zero_topology.Iio_mem_nhds_zero WithZeroTopology.Iio_mem_nhds_zero

/- warning: with_zero_topology.nhds_zero_of_units -> WithZeroTopology.nhds_zero_of_units is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] (γ : Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))), Membership.Mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (Filter.hasMem.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))) Γ₀ (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))) Γ₀ (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))) Γ₀ (coeBase.{succ u1, succ u1} (Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))) Γ₀ (Units.hasCoe.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))) γ)) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))))))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] (γ : Units.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))), Membership.mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (instMembershipSetFilter.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (Units.val.{u1} Γ₀ (MonoidWithZero.toMonoid.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))) γ)) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.nhds_zero_of_units WithZeroTopology.nhds_zero_of_unitsₓ'. -/
/-- If `γ` is an invertible element of a linearly ordered group with zero element adjoined, then
`Iio (γ : Γ₀)` is a neighbourhood of `0`. -/
theorem nhds_zero_of_units (γ : Γ₀ˣ) : Iio ↑γ ∈ 𝓝 (0 : Γ₀) :=
  Iio_mem_nhds_zero γ.NeZero
#align with_zero_topology.nhds_zero_of_units WithZeroTopology.nhds_zero_of_units

/- warning: with_zero_topology.tendsto_zero -> WithZeroTopology.tendsto_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {Γ₀ : Type.{u2}} [_inst_1 : LinearOrderedCommGroupWithZero.{u2} Γ₀] {l : Filter.{u1} α} {f : α -> Γ₀}, Iff (Filter.Tendsto.{u1, u2} α Γ₀ f l (nhds.{u2} Γ₀ (WithZeroTopology.topologicalSpace.{u2} Γ₀ _inst_1) (OfNat.ofNat.{u2} Γ₀ 0 (OfNat.mk.{u2} Γ₀ 0 (Zero.zero.{u2} Γ₀ (MulZeroClass.toHasZero.{u2} Γ₀ (MulZeroOneClass.toMulZeroClass.{u2} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u2} Γ₀ (GroupWithZero.toMonoidWithZero.{u2} Γ₀ (CommGroupWithZero.toGroupWithZero.{u2} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u2} Γ₀ _inst_1))))))))))) (forall (γ₀ : Γ₀), (Ne.{succ u2} Γ₀ γ₀ (OfNat.ofNat.{u2} Γ₀ 0 (OfNat.mk.{u2} Γ₀ 0 (Zero.zero.{u2} Γ₀ (MulZeroClass.toHasZero.{u2} Γ₀ (MulZeroOneClass.toMulZeroClass.{u2} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u2} Γ₀ (GroupWithZero.toMonoidWithZero.{u2} Γ₀ (CommGroupWithZero.toGroupWithZero.{u2} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u2} Γ₀ _inst_1)))))))))) -> (Filter.Eventually.{u1} α (fun (x : α) => LT.lt.{u2} Γ₀ (Preorder.toLT.{u2} Γ₀ (PartialOrder.toPreorder.{u2} Γ₀ (OrderedCommMonoid.toPartialOrder.{u2} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u2} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u2} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u2} Γ₀ _inst_1)))))) (f x) γ₀) l))
but is expected to have type
  forall {α : Type.{u2}} {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {l : Filter.{u2} α} {f : α -> Γ₀}, Iff (Filter.Tendsto.{u2, u1} α Γ₀ f l (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))))) (forall (γ₀ : Γ₀), (Ne.{succ u1} Γ₀ γ₀ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) -> (Filter.Eventually.{u2} α (fun (x : α) => LT.lt.{u1} Γ₀ (Preorder.toLT.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))))) (f x) γ₀) l))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.tendsto_zero WithZeroTopology.tendsto_zeroₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ₀ «expr ≠ » 0) -/
theorem tendsto_zero : Tendsto f l (𝓝 (0 : Γ₀)) ↔ ∀ (γ₀) (_ : γ₀ ≠ 0), ∀ᶠ x in l, f x < γ₀ := by
  simp [nhds_zero]
#align with_zero_topology.tendsto_zero WithZeroTopology.tendsto_zero

/-!
### Neighbourhoods of non-zero elements
-/

/- warning: with_zero_topology.nhds_of_ne_zero -> WithZeroTopology.nhds_of_ne_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) -> (Eq.{succ u1} (Filter.{u1} Γ₀) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) γ) (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} Γ₀ γ))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) -> (Eq.{succ u1} (Filter.{u1} Γ₀) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) γ) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} Γ₀ γ))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.nhds_of_ne_zero WithZeroTopology.nhds_of_ne_zeroₓ'. -/
/-- The neighbourhood filter of a nonzero element consists of all sets containing that
element. -/
@[simp]
theorem nhds_of_ne_zero {γ : Γ₀} (h₀ : γ ≠ 0) : 𝓝 γ = pure γ := by
  rw [nhds_eq_update, update_noteq h₀]
#align with_zero_topology.nhds_of_ne_zero WithZeroTopology.nhds_of_ne_zero

#print WithZeroTopology.nhds_coe_units /-
/-- The neighbourhood filter of an invertible element consists of all sets containing that
element. -/
theorem nhds_coe_units (γ : Γ₀ˣ) : 𝓝 (γ : Γ₀) = pure (γ : Γ₀) :=
  nhds_of_ne_zero γ.NeZero
#align with_zero_topology.nhds_coe_units WithZeroTopology.nhds_coe_units
-/

#print WithZeroTopology.singleton_mem_nhds_of_units /-
/-- If `γ` is an invertible element of a linearly ordered group with zero element adjoined, then
`{γ}` is a neighbourhood of `γ`. -/
theorem singleton_mem_nhds_of_units (γ : Γ₀ˣ) : ({γ} : Set Γ₀) ∈ 𝓝 (γ : Γ₀) := by simp
#align with_zero_topology.singleton_mem_nhds_of_units WithZeroTopology.singleton_mem_nhds_of_units
-/

/- warning: with_zero_topology.singleton_mem_nhds_of_ne_zero -> WithZeroTopology.singleton_mem_nhds_of_ne_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) -> (Membership.Mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (Filter.hasMem.{u1} Γ₀) (Singleton.singleton.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.hasSingleton.{u1} Γ₀) γ) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) γ))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {γ : Γ₀}, (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) -> (Membership.mem.{u1, u1} (Set.{u1} Γ₀) (Filter.{u1} Γ₀) (instMembershipSetFilter.{u1} Γ₀) (Singleton.singleton.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.instSingletonSet.{u1} Γ₀) γ) (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) γ))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.singleton_mem_nhds_of_ne_zero WithZeroTopology.singleton_mem_nhds_of_ne_zeroₓ'. -/
/-- If `γ` is a nonzero element of a linearly ordered group with zero element adjoined, then `{γ}`
is a neighbourhood of `γ`. -/
theorem singleton_mem_nhds_of_ne_zero (h : γ ≠ 0) : ({γ} : Set Γ₀) ∈ 𝓝 (γ : Γ₀) := by simp [h]
#align with_zero_topology.singleton_mem_nhds_of_ne_zero WithZeroTopology.singleton_mem_nhds_of_ne_zero

/- warning: with_zero_topology.has_basis_nhds_of_ne_zero -> WithZeroTopology.hasBasis_nhds_of_ne_zero is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {x : Γ₀}, (Ne.{succ u1} Γ₀ x (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) -> (Filter.HasBasis.{u1, 1} Γ₀ Unit (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) x) (fun (i : Unit) => True) (fun (i : Unit) => Singleton.singleton.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.hasSingleton.{u1} Γ₀) x))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {x : Γ₀}, (Ne.{succ u1} Γ₀ x (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) -> (Filter.HasBasis.{u1, 1} Γ₀ Unit (nhds.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) x) (fun (i : Unit) => True) (fun (i : Unit) => Singleton.singleton.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.instSingletonSet.{u1} Γ₀) x))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.has_basis_nhds_of_ne_zero WithZeroTopology.hasBasis_nhds_of_ne_zeroₓ'. -/
theorem hasBasis_nhds_of_ne_zero {x : Γ₀} (h : x ≠ 0) :
    HasBasis (𝓝 x) (fun i : Unit => True) fun i => {x} :=
  by
  rw [nhds_of_ne_zero h]
  exact has_basis_pure _
#align with_zero_topology.has_basis_nhds_of_ne_zero WithZeroTopology.hasBasis_nhds_of_ne_zero

#print WithZeroTopology.hasBasis_nhds_units /-
theorem hasBasis_nhds_units (γ : Γ₀ˣ) : HasBasis (𝓝 (γ : Γ₀)) (fun i : Unit => True) fun i => {γ} :=
  hasBasis_nhds_of_ne_zero γ.NeZero
#align with_zero_topology.has_basis_nhds_units WithZeroTopology.hasBasis_nhds_units
-/

/- warning: with_zero_topology.tendsto_of_ne_zero -> WithZeroTopology.tendsto_of_ne_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {Γ₀ : Type.{u2}} [_inst_1 : LinearOrderedCommGroupWithZero.{u2} Γ₀] {l : Filter.{u1} α} {f : α -> Γ₀} {γ : Γ₀}, (Ne.{succ u2} Γ₀ γ (OfNat.ofNat.{u2} Γ₀ 0 (OfNat.mk.{u2} Γ₀ 0 (Zero.zero.{u2} Γ₀ (MulZeroClass.toHasZero.{u2} Γ₀ (MulZeroOneClass.toMulZeroClass.{u2} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u2} Γ₀ (GroupWithZero.toMonoidWithZero.{u2} Γ₀ (CommGroupWithZero.toGroupWithZero.{u2} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u2} Γ₀ _inst_1)))))))))) -> (Iff (Filter.Tendsto.{u1, u2} α Γ₀ f l (nhds.{u2} Γ₀ (WithZeroTopology.topologicalSpace.{u2} Γ₀ _inst_1) γ)) (Filter.Eventually.{u1} α (fun (x : α) => Eq.{succ u2} Γ₀ (f x) γ) l))
but is expected to have type
  forall {α : Type.{u1}} {Γ₀ : Type.{u2}} [_inst_1 : LinearOrderedCommGroupWithZero.{u2} Γ₀] {l : Filter.{u1} α} {f : α -> Γ₀} {γ : Γ₀}, (Ne.{succ u2} Γ₀ γ (OfNat.ofNat.{u2} Γ₀ 0 (Zero.toOfNat0.{u2} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u2} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u2} Γ₀ _inst_1))))) -> (Iff (Filter.Tendsto.{u1, u2} α Γ₀ f l (nhds.{u2} Γ₀ (WithZeroTopology.topologicalSpace.{u2} Γ₀ _inst_1) γ)) (Filter.Eventually.{u1} α (fun (x : α) => Eq.{succ u2} Γ₀ (f x) γ) l))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.tendsto_of_ne_zero WithZeroTopology.tendsto_of_ne_zeroₓ'. -/
theorem tendsto_of_ne_zero {γ : Γ₀} (h : γ ≠ 0) : Tendsto f l (𝓝 γ) ↔ ∀ᶠ x in l, f x = γ := by
  rw [nhds_of_ne_zero h, tendsto_pure]
#align with_zero_topology.tendsto_of_ne_zero WithZeroTopology.tendsto_of_ne_zero

#print WithZeroTopology.tendsto_units /-
theorem tendsto_units {γ₀ : Γ₀ˣ} : Tendsto f l (𝓝 (γ₀ : Γ₀)) ↔ ∀ᶠ x in l, f x = γ₀ :=
  tendsto_of_ne_zero γ₀.NeZero
#align with_zero_topology.tendsto_units WithZeroTopology.tendsto_units
-/

#print WithZeroTopology.Iio_mem_nhds /-
theorem Iio_mem_nhds (h : γ₁ < γ₂) : Iio γ₂ ∈ 𝓝 γ₁ := by
  rcases eq_or_ne γ₁ 0 with (rfl | h₀) <;> simp [*, h.ne', Iio_mem_nhds_zero]
#align with_zero_topology.Iio_mem_nhds WithZeroTopology.Iio_mem_nhds
-/

/-!
### Open/closed sets
-/

/- warning: with_zero_topology.is_open_iff -> WithZeroTopology.isOpen_iff is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {s : Set.{u1} Γ₀}, Iff (IsOpen.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) s) (Or (Not (Membership.Mem.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.hasMem.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))))))) s)) (Exists.{succ u1} Γ₀ (fun (γ : Γ₀) => Exists.{0} (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) => HasSubset.Subset.{u1} (Set.{u1} Γ₀) (Set.hasSubset.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ) s))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {s : Set.{u1} Γ₀}, Iff (IsOpen.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) s) (Or (Not (Membership.mem.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.instMembershipSet.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))) s)) (Exists.{succ u1} Γ₀ (fun (γ : Γ₀) => And (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (HasSubset.Subset.{u1} (Set.{u1} Γ₀) (Set.instHasSubsetSet.{u1} Γ₀) (Set.Iio.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ) s))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.is_open_iff WithZeroTopology.isOpen_iffₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ «expr ≠ » 0) -/
theorem isOpen_iff {s : Set Γ₀} : IsOpen s ↔ (0 : Γ₀) ∉ s ∨ ∃ (γ : _)(_ : γ ≠ 0), Iio γ ⊆ s :=
  by
  rw [isOpen_iff_mem_nhds, ← and_forall_ne (0 : Γ₀)]
  simp (config := { contextual := true }) [nhds_of_ne_zero, imp_iff_not_or,
    has_basis_nhds_zero.mem_iff]
#align with_zero_topology.is_open_iff WithZeroTopology.isOpen_iff

/- warning: with_zero_topology.is_closed_iff -> WithZeroTopology.isClosed_iff is a dubious translation:
lean 3 declaration is
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {s : Set.{u1} Γ₀}, Iff (IsClosed.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) s) (Or (Membership.Mem.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.hasMem.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1))))))))) s) (Exists.{succ u1} Γ₀ (fun (γ : Γ₀) => Exists.{0} (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) (fun (H : Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (OfNat.mk.{u1} Γ₀ 0 (Zero.zero.{u1} Γ₀ (MulZeroClass.toHasZero.{u1} Γ₀ (MulZeroOneClass.toMulZeroClass.{u1} Γ₀ (MonoidWithZero.toMulZeroOneClass.{u1} Γ₀ (GroupWithZero.toMonoidWithZero.{u1} Γ₀ (CommGroupWithZero.toGroupWithZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toCommGroupWithZero.{u1} Γ₀ _inst_1)))))))))) => HasSubset.Subset.{u1} (Set.{u1} Γ₀) (Set.hasSubset.{u1} Γ₀) s (Set.Ici.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ)))))
but is expected to have type
  forall {Γ₀ : Type.{u1}} [_inst_1 : LinearOrderedCommGroupWithZero.{u1} Γ₀] {s : Set.{u1} Γ₀}, Iff (IsClosed.{u1} Γ₀ (WithZeroTopology.topologicalSpace.{u1} Γ₀ _inst_1) s) (Or (Membership.mem.{u1, u1} Γ₀ (Set.{u1} Γ₀) (Set.instMembershipSet.{u1} Γ₀) (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1)))) s) (Exists.{succ u1} Γ₀ (fun (γ : Γ₀) => And (Ne.{succ u1} Γ₀ γ (OfNat.ofNat.{u1} Γ₀ 0 (Zero.toOfNat0.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toZero.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) (HasSubset.Subset.{u1} (Set.{u1} Γ₀) (Set.instHasSubsetSet.{u1} Γ₀) s (Set.Ici.{u1} Γ₀ (PartialOrder.toPreorder.{u1} Γ₀ (OrderedCommMonoid.toPartialOrder.{u1} Γ₀ (LinearOrderedCommMonoid.toOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommMonoidWithZero.toLinearOrderedCommMonoid.{u1} Γ₀ (LinearOrderedCommGroupWithZero.toLinearOrderedCommMonoidWithZero.{u1} Γ₀ _inst_1))))) γ)))))
Case conversion may be inaccurate. Consider using '#align with_zero_topology.is_closed_iff WithZeroTopology.isClosed_iffₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (γ «expr ≠ » 0) -/
theorem isClosed_iff {s : Set Γ₀} : IsClosed s ↔ (0 : Γ₀) ∈ s ∨ ∃ (γ : _)(_ : γ ≠ 0), s ⊆ Ici γ :=
  by
  simp only [← isOpen_compl_iff, is_open_iff, mem_compl_iff, Classical.not_not, ← compl_Ici,
    compl_subset_compl]
#align with_zero_topology.is_closed_iff WithZeroTopology.isClosed_iff

#print WithZeroTopology.isOpen_Iio /-
theorem isOpen_Iio {a : Γ₀} : IsOpen (Iio a) :=
  isOpen_iff.mpr <| imp_iff_not_or.mp fun ha => ⟨a, ne_of_gt ha, Subset.rfl⟩
#align with_zero_topology.is_open_Iio WithZeroTopology.isOpen_Iio
-/

/-!
### Instances
-/

#print WithZeroTopology.orderClosedTopology /-
/-- The topology on a linearly ordered group with zero element adjoined is compatible with the order
structure: the set `{p : Γ₀ × Γ₀ | p.1 ≤ p.2}` is closed. -/
protected theorem orderClosedTopology : OrderClosedTopology Γ₀ :=
  {
    isClosed_le' :=
      by
      simp only [← isOpen_compl_iff, compl_set_of, not_le, isOpen_iff_mem_nhds]
      rintro ⟨a, b⟩ (hab : b < a)
      rw [nhds_prod_eq, nhds_of_ne_zero (zero_le'.trans_lt hab).ne', pure_prod]
      exact Iio_mem_nhds hab }
#align with_zero_topology.order_closed_topology WithZeroTopology.orderClosedTopology
-/

attribute [scoped instance] WithZeroTopology.orderClosedTopology

#print WithZeroTopology.t3Space /-
/-- The topology on a linearly ordered group with zero element adjoined is T₃. -/
theorem t3Space : T3Space Γ₀ :=
  {
    to_regularSpace :=
      RegularSpace.ofLift'_closure fun γ =>
        by
        rcases ne_or_eq γ 0 with (h₀ | rfl)
        ·
          rw [nhds_of_ne_zero h₀, lift'_pure (monotone_closure Γ₀), closure_singleton,
            principal_singleton]
        ·
          exact
            has_basis_nhds_zero.lift'_closure_eq_self fun x hx =>
              is_closed_iff.2 <| Or.inl <| zero_lt_iff.2 hx }
#align with_zero_topology.t3_space WithZeroTopology.t3Space
-/

attribute [scoped instance] WithZeroTopology.t3Space

/-- The topology on a linearly ordered group with zero element adjoined makes it a topological
monoid. -/
protected theorem continuousMul : ContinuousMul Γ₀ :=
  ⟨by
    rw [continuous_iff_continuousAt]
    rintro ⟨x, y⟩
    wlog hle : x ≤ y generalizing x y
    · have := tendsto.comp (this y x (le_of_not_le hle)) (continuous_swap.tendsto (x, y))
      simpa only [mul_comm, Function.comp, Prod.swap]
    rcases eq_or_ne x 0 with (rfl | hx) <;> [rcases eq_or_ne y 0 with (rfl | hy), skip]
    · rw [ContinuousAt, MulZeroClass.zero_mul]
      refine'
        ((has_basis_nhds_zero.prod_nhds has_basis_nhds_zero).tendsto_iffₓ has_basis_nhds_zero).2
          fun γ hγ => ⟨(γ, 1), ⟨hγ, one_ne_zero⟩, _⟩
      rintro ⟨x, y⟩ ⟨hx : x < γ, hy : y < 1⟩
      exact (mul_lt_mul₀ hx hy).trans_eq (mul_one γ)
    · rw [ContinuousAt, MulZeroClass.zero_mul, nhds_prod_eq, nhds_of_ne_zero hy, prod_pure,
        tendsto_map'_iff]
      refine' (has_basis_nhds_zero.tendsto_iff has_basis_nhds_zero).2 fun γ hγ => _
      refine' ⟨γ / y, div_ne_zero hγ hy, fun x hx => _⟩
      calc
        x * y < γ / y * y := mul_lt_right₀ _ hx hy
        _ = γ := div_mul_cancel _ hy

    · have hy : y ≠ 0 := ((zero_lt_iff.mpr hx).trans_le hle).ne'
      rw [ContinuousAt, nhds_prod_eq, nhds_of_ne_zero hx, nhds_of_ne_zero hy, prod_pure_pure]
      exact pure_le_nhds (x * y)⟩
#align with_zero_topology.has_continuous_mul WithZeroTopology.continuousMul

attribute [scoped instance] WithZeroTopology.continuousMul

protected theorem hasContinuousInv₀ : HasContinuousInv₀ Γ₀ :=
  ⟨fun γ h => by
    rw [ContinuousAt, nhds_of_ne_zero h]
    exact pure_le_nhds γ⁻¹⟩
#align with_zero_topology.has_continuous_inv₀ WithZeroTopology.hasContinuousInv₀

attribute [scoped instance] WithZeroTopology.hasContinuousInv₀

end WithZeroTopology


/-
Copyright (c) 2014 Floris van Doorn (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro
-/
import algebra.group_power.order

/-! # `nat.pow`

Results on the power operation on natural numbers.
-/

namespace nat

/-! ### `pow` -/

-- This is redundant with `pow_le_pow_of_le_left'`,
-- We leave a version in the `nat` namespace as well.
-- (The global `pow_le_pow_of_le_left` needs an extra hypothesis `0 ≤ x`.)
protected theorem pow_le_pow_of_le_left {x y : ℕ} (H : x ≤ y) : ∀ i : ℕ, x^i ≤ y^i :=
pow_le_pow_of_le_left' H

theorem pow_le_pow_of_le_right {x : ℕ} (H : 0 < x) {i j : ℕ} (h : i ≤ j) : x ^ i ≤ x ^ j :=
pow_le_pow' H h

theorem pow_lt_pow_of_lt_left {x y : ℕ} (H : x < y) {i} (h : 0 < i) : x^i < y^i :=
pow_lt_pow_of_lt_left H (zero_le _) h

theorem pow_lt_pow_of_lt_right {x : ℕ} (H : 1 < x) {i j : ℕ} (h : i < j) : x^i < x^j :=
pow_lt_pow H h

lemma pow_lt_pow_succ {p : ℕ} (h : 1 < p) (n : ℕ) : p^n < p^(n+1) :=
pow_lt_pow_of_lt_right h n.lt_succ_self

lemma lt_pow_self {p : ℕ} (h : 1 < p) : ∀ n : ℕ, n < p ^ n
| 0 := by simp [zero_lt_one]
| (n+1) := calc
  n + 1 < p^n + 1 : nat.add_lt_add_right (lt_pow_self _) _
    ... ≤ p ^ (n+1) : pow_lt_pow_succ h _

lemma lt_two_pow (n : ℕ) : n < 2^n :=
lt_pow_self dec_trivial n

lemma one_le_pow (n m : ℕ) (h : 0 < m) : 1 ≤ m^n :=
by { rw ←one_pow n, exact nat.pow_le_pow_of_le_left h n }
lemma one_le_pow' (n m : ℕ) : 1 ≤ (m+1)^n := one_le_pow n (m+1) (succ_pos m)

lemma one_le_two_pow (n : ℕ) : 1 ≤ 2^n := one_le_pow n 2 dec_trivial

lemma one_lt_pow (n m : ℕ) (h₀ : 0 < n) (h₁ : 1 < m) : 1 < m^n :=
by { rw ←one_pow n, exact pow_lt_pow_of_lt_left h₁ h₀ }
lemma one_lt_pow' (n m : ℕ) : 1 < (m+2)^(n+1) :=
one_lt_pow (n+1) (m+2) (succ_pos n) (nat.lt_of_sub_eq_succ rfl)

@[simp] lemma one_lt_pow_iff {k n : ℕ} (h : 0 ≠ k) : 1 < n ^ k ↔ 1 < n :=
begin
  cases n,
  { cases k; simp [zero_pow_eq] },
  cases n,
  { rw one_pow },
  refine ⟨λ _, one_lt_succ_succ n, λ _, _⟩,
  induction k with k hk,
  { exact absurd rfl h },
  cases k,
  { simp },
  exact one_lt_mul (one_lt_succ_succ _).le (hk (succ_ne_zero k).symm),
end

lemma one_lt_two_pow (n : ℕ) (h₀ : 0 < n) : 1 < 2^n := one_lt_pow n 2 h₀ dec_trivial
lemma one_lt_two_pow' (n : ℕ) : 1 < 2^(n+1) := one_lt_pow (n+1) 2 (succ_pos n) dec_trivial

lemma pow_right_strict_mono {x : ℕ} (k : 2 ≤ x) : strict_mono (λ (n : ℕ), x^n) :=
λ _ _, pow_lt_pow_of_lt_right k

lemma pow_le_iff_le_right {x m n : ℕ} (k : 2 ≤ x) : x^m ≤ x^n ↔ m ≤ n :=
strict_mono.le_iff_le (pow_right_strict_mono k)

lemma pow_lt_iff_lt_right {x m n : ℕ} (k : 2 ≤ x) : x^m < x^n ↔ m < n :=
strict_mono.lt_iff_lt (pow_right_strict_mono k)

lemma pow_right_injective {x : ℕ} (k : 2 ≤ x) : function.injective (λ (n : ℕ), x^n) :=
strict_mono.injective (pow_right_strict_mono k)

lemma pow_left_strict_mono {m : ℕ} (k : 1 ≤ m) : strict_mono (λ (x : ℕ), x^m) :=
λ _ _ h, pow_lt_pow_of_lt_left h k

lemma mul_lt_mul_pow_succ {n a q : ℕ} (a0 : 0 < a) (q1 : 1 < q) :
  n * q < a * q ^ (n + 1) :=
begin
  rw [pow_succ', ← mul_assoc, mul_lt_mul_right (zero_lt_one.trans q1)],
  exact lt_mul_of_one_le_of_lt (nat.succ_le_iff.mpr a0) (nat.lt_pow_self q1 n),
end

end nat

lemma strict_mono.nat_pow {n : ℕ} (hn : 1 ≤ n) {f : ℕ → ℕ} (hf : strict_mono f) :
  strict_mono (λ m, (f m) ^ n) :=
(nat.pow_left_strict_mono hn).comp hf

namespace nat

lemma pow_le_iff_le_left {m x y : ℕ} (k : 1 ≤ m) : x^m ≤ y^m ↔ x ≤ y :=
strict_mono.le_iff_le (pow_left_strict_mono k)

lemma pow_lt_iff_lt_left {m x y : ℕ} (k : 1 ≤ m) : x^m < y^m ↔ x < y :=
strict_mono.lt_iff_lt (pow_left_strict_mono k)

lemma pow_left_injective {m : ℕ} (k : 1 ≤ m) : function.injective (λ (x : ℕ), x^m) :=
strict_mono.injective (pow_left_strict_mono k)

theorem sq_sub_sq (a b : ℕ) : a ^ 2 - b ^ 2 = (a + b) * (a - b) :=
by { rw [sq, sq], exact nat.mul_self_sub_mul_self_eq a b }

alias nat.sq_sub_sq ← nat.pow_two_sub_pow_two

/-! ### `pow` and `mod` / `dvd` -/

theorem mod_pow_succ {b : ℕ} (b_pos : 0 < b) (w m : ℕ) :
  m % (b^succ w) = b * (m/b % b^w) + m % b :=
begin
  apply nat.strong_induction_on m,
  clear m,
  intros p IH,
  cases lt_or_ge p (b^succ w) with h₁ h₁,
  -- base case: p < b^succ w
  { have h₂ : p / b < b^w,
    { rw [div_lt_iff_lt_mul p _ b_pos],
      simpa [pow_succ'] using h₁ },
    rw [mod_eq_of_lt h₁, mod_eq_of_lt h₂],
    simp [div_add_mod] },
  -- step: p ≥ b^succ w
  { -- Generate condition for induction hypothesis
    have h₂ : p - b^succ w < p,
    { exact tsub_lt_self ((pow_pos b_pos _).trans_le h₁) (pow_pos b_pos _) },
    -- Apply induction
    rw [mod_eq_sub_mod h₁, IH _ h₂],
    -- Normalize goal and h1
    simp only [pow_succ],
    simp only [ge, pow_succ] at h₁,
    -- Pull subtraction outside mod and div
    rw [sub_mul_mod _ _ _ h₁, sub_mul_div _ _ _ h₁],
    -- Cancel subtraction inside mod b^w
    have p_b_ge :  b^w ≤ p / b,
    { rw [le_div_iff_mul_le _ _ b_pos, mul_comm],
      exact h₁ },
    rw [eq.symm (mod_eq_sub_mod p_b_ge)] }
end

lemma pow_dvd_pow_iff_pow_le_pow {k l : ℕ} : Π {x : ℕ} (w : 0 < x), x^k ∣ x^l ↔ x^k ≤ x^l
| (x+1) w :=
begin
  split,
  { intro a, exact le_of_dvd (pow_pos (succ_pos x) l) a, },
  { intro a, cases x with x,
    { simp only [one_pow], },
    { have le := (pow_le_iff_le_right (nat.le_add_left _ _)).mp a,
      use (x+2)^(l-k),
      rw [←pow_add, add_comm k, tsub_add_cancel_of_le le], } }
end

/-- If `1 < x`, then `x^k` divides `x^l` if and only if `k` is at most `l`. -/
lemma pow_dvd_pow_iff_le_right {x k l : ℕ} (w : 1 < x) : x^k ∣ x^l ↔ k ≤ l :=
by rw [pow_dvd_pow_iff_pow_le_pow (lt_of_succ_lt w), pow_le_iff_le_right w]

lemma pow_dvd_pow_iff_le_right' {b k l : ℕ} : (b+2)^k ∣ (b+2)^l ↔ k ≤ l :=
pow_dvd_pow_iff_le_right (nat.lt_of_sub_eq_succ rfl)

lemma not_pos_pow_dvd : ∀ {p k : ℕ} (hp : 1 < p) (hk : 1 < k), ¬ p^k ∣ p
| (succ p) (succ k) hp hk h :=
  have succ p * (succ p)^k ∣ succ p * 1, by simpa [pow_succ] using h,
  have (succ p) ^ k ∣ 1, from dvd_of_mul_dvd_mul_left (succ_pos _) this,
  have he : (succ p) ^ k = 1, from eq_one_of_dvd_one this,
  have k < (succ p) ^ k, from lt_pow_self hp k,
  have k < 1, by rwa [he] at this,
  have k = 0, from nat.eq_zero_of_le_zero $ le_of_lt_succ this,
  have 1 < 1, by rwa [this] at hk,
  absurd this dec_trivial

lemma pow_dvd_of_le_of_pow_dvd {p m n k : ℕ} (hmn : m ≤ n) (hdiv : p ^ n ∣ k) : p ^ m ∣ k :=
(pow_dvd_pow _ hmn).trans hdiv

lemma dvd_of_pow_dvd {p k m : ℕ} (hk : 1 ≤ k) (hpk : p^k ∣ m) : p ∣ m :=
by rw ←pow_one p; exact pow_dvd_of_le_of_pow_dvd hk hpk

lemma pow_div {x m n : ℕ} (h : n ≤ m) (hx : 0 < x) : x ^ m / x ^ n = x ^ (m - n) :=
by rw [nat.div_eq_iff_eq_mul_left (pow_pos hx n) (pow_dvd_pow _ h), pow_sub_mul_pow _ h]

/-! ### `shiftl` and `shiftr` -/

lemma shiftl_eq_mul_pow (m) : ∀ n, shiftl m n = m * 2 ^ n
| 0     := (nat.mul_one _).symm
| (k+1) := show bit0 (shiftl m k) = m * (2 * 2 ^ k),
  by rw [bit0_val, shiftl_eq_mul_pow, mul_left_comm, mul_comm 2]

lemma shiftl'_tt_eq_mul_pow (m) : ∀ n, shiftl' tt m n + 1 = (m + 1) * 2 ^ n
| 0     := by simp [shiftl, shiftl', pow_zero, nat.one_mul]
| (k+1) :=
begin
  change bit1 (shiftl' tt m k) + 1 = (m + 1) * (2 * 2 ^ k),
  rw bit1_val,
  change 2 * (shiftl' tt m k + 1) = _,
  rw [shiftl'_tt_eq_mul_pow, mul_left_comm, mul_comm 2],
end

lemma one_shiftl (n) : shiftl 1 n = 2 ^ n :=
(shiftl_eq_mul_pow _ _).trans (nat.one_mul _)

@[simp] lemma zero_shiftl (n) : shiftl 0 n = 0 :=
(shiftl_eq_mul_pow _ _).trans (nat.zero_mul _)

lemma shiftr_eq_div_pow (m) : ∀ n, shiftr m n = m / 2 ^ n
| 0     := (nat.div_one _).symm
| (k+1) := (congr_arg div2 (shiftr_eq_div_pow k)).trans $
           by rw [div2_val, nat.div_div_eq_div_mul, mul_comm]; refl

@[simp] lemma zero_shiftr (n) : shiftr 0 n = 0 :=
(shiftr_eq_div_pow _ _).trans (nat.zero_div _)

theorem shiftl'_ne_zero_left (b) {m} (h : m ≠ 0) (n) : shiftl' b m n ≠ 0 :=
by induction n; simp [shiftl', bit_ne_zero, *]

theorem shiftl'_tt_ne_zero (m) : ∀ {n} (h : n ≠ 0), shiftl' tt m n ≠ 0
| 0        h := absurd rfl h
| (succ n) _ := nat.bit1_ne_zero _

/-! ### `size` -/

@[simp] theorem size_zero : size 0 = 0 := by simp [size]

@[simp] theorem size_bit {b n} (h : bit b n ≠ 0) : size (bit b n) = succ (size n) :=
begin
  rw size,
  conv { to_lhs, rw [binary_rec], simp [h] },
  rw div2_bit,
end

@[simp] theorem size_bit0 {n} (h : n ≠ 0) : size (bit0 n) = succ (size n) :=
@size_bit ff n (nat.bit0_ne_zero h)

@[simp] theorem size_bit1 (n) : size (bit1 n) = succ (size n) :=
@size_bit tt n (nat.bit1_ne_zero n)

@[simp] theorem size_one : size 1 = 1 :=
show size (bit1 0) = 1, by rw [size_bit1, size_zero]

@[simp] theorem size_shiftl' {b m n} (h : shiftl' b m n ≠ 0) :
  size (shiftl' b m n) = size m + n :=
begin
  induction n with n IH; simp [shiftl'] at h ⊢,
  rw [size_bit h, nat.add_succ],
  by_cases s0 : shiftl' b m n = 0; [skip, rw [IH s0]],
  rw s0 at h ⊢,
  cases b, {exact absurd rfl h},
  have : shiftl' tt m n + 1 = 1 := congr_arg (+1) s0,
  rw [shiftl'_tt_eq_mul_pow] at this,
  have m0 := succ.inj (eq_one_of_dvd_one ⟨_, this.symm⟩),
  subst m0,
  simp at this,
  have : n = 0 := nat.eq_zero_of_le_zero (le_of_not_gt $ λ hn,
    ne_of_gt (pow_lt_pow_of_lt_right dec_trivial hn) this),
  subst n, refl
end

@[simp] theorem size_shiftl {m} (h : m ≠ 0) (n) :
  size (shiftl m n) = size m + n :=
size_shiftl' (shiftl'_ne_zero_left _ h _)

theorem lt_size_self (n : ℕ) : n < 2^size n :=
begin
  rw [← one_shiftl],
  have : ∀ {n}, n = 0 → n < shiftl 1 (size n), { simp },
  apply binary_rec _ _ n, {apply this rfl},
  intros b n IH,
  by_cases bit b n = 0, {apply this h},
  rw [size_bit h, shiftl_succ],
  exact bit_lt_bit0 _ IH
end

theorem size_le {m n : ℕ} : size m ≤ n ↔ m < 2^n :=
⟨λ h, lt_of_lt_of_le (lt_size_self _) (pow_le_pow_of_le_right dec_trivial h),
begin
  rw [← one_shiftl], revert n,
  apply binary_rec _ _ m,
  { intros n h, simp },
  { intros b m IH n h,
    by_cases e : bit b m = 0, { simp [e] },
    rw [size_bit e],
    cases n with n,
    { exact e.elim (nat.eq_zero_of_le_zero (le_of_lt_succ h)) },
    { apply succ_le_succ (IH _),
      apply lt_imp_lt_of_le_imp_le (λ h', bit0_le_bit _ h') h } }
end⟩

theorem lt_size {m n : ℕ} : m < size n ↔ 2^m ≤ n :=
by rw [← not_lt, decidable.iff_not_comm, not_lt, size_le]

theorem size_pos {n : ℕ} : 0 < size n ↔ 0 < n :=
by rw lt_size; refl

theorem size_eq_zero {n : ℕ} : size n = 0 ↔ n = 0 :=
by have := @size_pos n; simp [pos_iff_ne_zero] at this;
   exact decidable.not_iff_not.1 this

theorem size_pow {n : ℕ} : size (2^n) = n+1 :=
le_antisymm
  (size_le.2 $ pow_lt_pow_of_lt_right dec_trivial (lt_succ_self _))
  (lt_size.2 $ le_refl _)

theorem size_le_size {m n : ℕ} (h : m ≤ n) : size m ≤ size n :=
size_le.2 $ lt_of_le_of_lt h (lt_size_self _)

end nat


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.field_theory.finite.basic
import Mathlib.data.zmod.basic
import Mathlib.data.nat.parity
import Mathlib.PostPort

namespace Mathlib

/-!
# Quadratic reciprocity.

This file contains results about quadratic residues modulo a prime number.

The main results are the law of quadratic reciprocity, `quadratic_reciprocity`, as well as the
interpretations in terms of existence of square roots depending on the congruence mod 4,
`exists_pow_two_eq_prime_iff_of_mod_four_eq_one`, and
`exists_pow_two_eq_prime_iff_of_mod_four_eq_three`.

Also proven are conditions for `-1` and `2` to be a square modulo a prime,
`exists_pow_two_eq_neg_one_iff_mod_four_ne_three` and
`exists_pow_two_eq_two_iff`

## Implementation notes

The proof of quadratic reciprocity implemented uses Gauss' lemma and Eisenstein's lemma
-/

namespace zmod

/-- Euler's Criterion: A unit `x` of `zmod p` is a square if and only if `x ^ (p / 2) = 1`. -/
theorem euler_criterion_units (p : ℕ) [fact (nat.prime p)] (x : units (zmod p)) : (∃ (y : units (zmod p)), y ^ bit0 1 = x) ↔ x ^ (p / bit0 1) = 1 := sorry

/-- Euler's Criterion: a nonzero `a : zmod p` is a square if and only if `x ^ (p / 2) = 1`. -/
theorem euler_criterion (p : ℕ) [fact (nat.prime p)] {a : zmod p} (ha : a ≠ 0) : (∃ (y : zmod p), y ^ bit0 1 = a) ↔ a ^ (p / bit0 1) = 1 := sorry

theorem exists_pow_two_eq_neg_one_iff_mod_four_ne_three (p : ℕ) [fact (nat.prime p)] : (∃ (y : zmod p), y ^ bit0 1 = -1) ↔ p % bit0 (bit0 1) ≠ bit1 1 := sorry

theorem pow_div_two_eq_neg_one_or_one (p : ℕ) [fact (nat.prime p)] {a : zmod p} (ha : a ≠ 0) : a ^ (p / bit0 1) = 1 ∨ a ^ (p / bit0 1) = -1 := sorry

/-- Wilson's Lemma: the product of `1`, ..., `p-1` is `-1` modulo `p`. -/
@[simp] theorem wilsons_lemma (p : ℕ) [fact (nat.prime p)] : ↑(nat.factorial (p - 1)) = -1 := sorry

@[simp] theorem prod_Ico_one_prime (p : ℕ) [fact (nat.prime p)] : (finset.prod (finset.Ico 1 p) fun (x : ℕ) => ↑x) = -1 := sorry

end zmod

/-- The image of the map sending a non zero natural number `x ≤ p / 2` to the absolute value
  of the element of interger in the interval `(-p/2, p/2]` congruent to `a * x` mod p is the set
  of non zero natural numbers `x` such that `x ≤ p / 2` -/
theorem Ico_map_val_min_abs_nat_abs_eq_Ico_map_id (p : ℕ) [hp : fact (nat.prime p)] (a : zmod p) (hap : a ≠ 0) : multiset.map (fun (x : ℕ) => int.nat_abs (zmod.val_min_abs (a * ↑x)))
    (finset.val (finset.Ico 1 (Nat.succ (p / bit0 1)))) =
  multiset.map (fun (a : ℕ) => a) (finset.val (finset.Ico 1 (Nat.succ (p / bit0 1)))) := sorry

theorem div_eq_filter_card {a : ℕ} {b : ℕ} {c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) : a / b = finset.card (finset.filter (fun (x : ℕ) => x * b ≤ a) (finset.Ico 1 (Nat.succ c))) := sorry

/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`  -/
/-- Each of the sums in this lemma is the cardinality of the set integer points in each of the
  two triangles formed by the diagonal of the rectangle `(0, p/2) × (0, q/2)`. Adding them
  gives the number of points in the rectangle. -/
namespace zmod

/-- The Legendre symbol of `a` and `p` is an integer defined as

* `0` if `a` is `0` modulo `p`;
* `1` if `a ^ (p / 2)` is `1` modulo `p`
   (by `euler_criterion` this is equivalent to “`a` is a square modulo `p`”);
* `-1` otherwise.

-/
def legendre_sym (a : ℕ) (p : ℕ) : ℤ :=
  ite (↑a = 0) 0 (ite (↑a ^ (p / bit0 1) = 1) 1 (-1))

theorem legendre_sym_eq_pow (a : ℕ) (p : ℕ) [hp : fact (nat.prime p)] : ↑(legendre_sym a p) = ↑a ^ (p / bit0 1) := sorry

theorem legendre_sym_eq_one_or_neg_one (a : ℕ) (p : ℕ) (ha : ↑a ≠ 0) : legendre_sym a p = -1 ∨ legendre_sym a p = 1 := sorry

theorem legendre_sym_eq_zero_iff (a : ℕ) (p : ℕ) : legendre_sym a p = 0 ↔ ↑a = 0 := sorry

/-- Gauss' lemma. The legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2` -/
theorem gauss_lemma (p : ℕ) [fact (nat.prime p)] {a : ℕ} [hp1 : fact (p % bit0 1 = 1)] (ha0 : ↑a ≠ 0) : legendre_sym a p =
  (-1) ^ finset.card (finset.filter (fun (x : ℕ) => p / bit0 1 < val (↑a * ↑x)) (finset.Ico 1 (Nat.succ (p / bit0 1)))) := sorry

theorem legendre_sym_eq_one_iff (p : ℕ) [fact (nat.prime p)] {a : ℕ} (ha0 : ↑a ≠ 0) : legendre_sym a p = 1 ↔ ∃ (b : zmod p), b ^ bit0 1 = ↑a := sorry

theorem eisenstein_lemma (p : ℕ) [fact (nat.prime p)] [hp1 : fact (p % bit0 1 = 1)] {a : ℕ} (ha1 : a % bit0 1 = 1) (ha0 : ↑a ≠ 0) : legendre_sym a p = (-1) ^ finset.sum (finset.Ico 1 (Nat.succ (p / bit0 1))) fun (x : ℕ) => x * a / p := sorry

theorem quadratic_reciprocity (p : ℕ) (q : ℕ) [fact (nat.prime p)] [fact (nat.prime q)] [hp1 : fact (p % bit0 1 = 1)] [hq1 : fact (q % bit0 1 = 1)] (hpq : p ≠ q) : legendre_sym p q * legendre_sym q p = (-1) ^ (p / bit0 1 * (q / bit0 1)) := sorry

-- move this

protected instance fact_prime_two : fact (nat.prime (bit0 1)) :=
  nat.prime_two

theorem legendre_sym_two (p : ℕ) [fact (nat.prime p)] [hp1 : fact (p % bit0 1 = 1)] : legendre_sym (bit0 1) p = (-1) ^ (p / bit0 (bit0 1) + p / bit0 1) := sorry

theorem exists_pow_two_eq_two_iff (p : ℕ) [fact (nat.prime p)] [hp1 : fact (p % bit0 1 = 1)] : (∃ (a : zmod p), a ^ bit0 1 = bit0 1) ↔ p % bit0 (bit0 (bit0 1)) = 1 ∨ p % bit0 (bit0 (bit0 1)) = bit1 (bit1 1) := sorry

theorem exists_pow_two_eq_prime_iff_of_mod_four_eq_one (p : ℕ) (q : ℕ) [fact (nat.prime p)] [fact (nat.prime q)] (hp1 : p % bit0 (bit0 1) = 1) [hq1 : fact (q % bit0 1 = 1)] : (∃ (a : zmod p), a ^ bit0 1 = ↑q) ↔ ∃ (b : zmod q), b ^ bit0 1 = ↑p := sorry

theorem exists_pow_two_eq_prime_iff_of_mod_four_eq_three (p : ℕ) (q : ℕ) [fact (nat.prime p)] [fact (nat.prime q)] (hp3 : p % bit0 (bit0 1) = bit1 1) (hq3 : q % bit0 (bit0 1) = bit1 1) (hpq : p ≠ q) : (∃ (a : zmod p), a ^ bit0 1 = ↑q) ↔ ¬∃ (b : zmod q), b ^ bit0 1 = ↑p := sorry


import tactic
/-!

Order of the subgroup divides order of the group, from first principles,
with proofs written in tactic mode.

Just so kmb can see how long the proof is. Can we teach it?

-/

-- We're overwriting inbuilt group theory here so we always work in
-- a namespace

namespace mygroup

-- definition of a group, with canonical "hard" choice of axioms
class group (G : Type) extends has_mul G, has_one G, has_inv G :=
(mul_assoc : ∀ (a b c : G), a * b * c = a * (b * c))
(one_mul : ∀ (a : G), 1 * a = a)
(mul_left_inv : ∀ (a : G), a⁻¹ * a = 1)

-- "easy" choice is to add mul_one and mul_right_inv as axioms too. We don't need them.

namespace group

variables {G : Type} [group G]

/-! # Basic API building for groups  -/

-- We need mul_one and mul_right_inv ASAP.

-- calc proof of key intermediate lemma (NB I never know whether to make a,b,c implicit)
lemma mul_left_cancel (a b c : G) (Habac : a * b = a * c) : b = c :=
 calc b = 1 * b         : by rw one_mul
    ... = (a⁻¹ * a) * b : by rw mul_left_inv
    ... = a⁻¹ * (a * b) : by rw mul_assoc
    ... = a⁻¹ * (a * c) : by rw Habac
    ... = (a⁻¹ * a) * c : by rw mul_assoc
    ... = 1 * c         : by rw mul_left_inv
    ... = c             : by rw one_mul

-- rw proof of key intermediate lemma
lemma mul_left_cancel' (a b c : G) (Habac : a * b = a * c) : b = c :=
begin
  rw [←one_mul b, ←mul_left_inv a, mul_assoc, Habac,
      ←mul_assoc, mul_left_inv, one_mul],
end

-- second key intermediate lemma
lemma mul_eq_of_eq_inv_mul' {a x y : G} (h : x = a⁻¹ * y) : a * x = y :=
begin
  apply mul_left_cancel a⁻¹,
  exact calc
  a⁻¹ * (a * x) = a⁻¹ * a * x : by rw mul_assoc
  ...           = 1 * x       : by rw mul_left_inv
  ...           = x           : by rw one_mul
  ...           = a⁻¹ * y     : by rw h
end

-- rw proof
lemma mul_eq_of_eq_inv_mul {a x y : G} (h : x = a⁻¹ * y) : a * x = y :=
begin
  apply mul_left_cancel a⁻¹,
  rw ←mul_assoc,
  rw mul_left_inv,
  rwa one_mul, -- rewrite then assumption
end

-- Alternatively, get the simplifier to do some of the work for us
attribute [simp] one_mul mul_left_inv

-- third proof using automation
lemma mul_eq_of_eq_inv_mul'' {a x y : G} : x = a⁻¹ * y → a * x = y :=
λ h, mul_left_cancel a⁻¹ _ _ $ by rw ←mul_assoc; simp [h]

-- We can now prove `mul_one` and `mul_right_inv`

-- nice short proof of mul_one
-- Do we want to teach @simp BTW?
@[simp] theorem mul_one (a : G) : a * 1 = a :=
begin
  apply mul_eq_of_eq_inv_mul,
  rw mul_left_inv,
  -- note no refl, which is different to NNG
end

-- final theorem which is sometimes presented as an axiom to UGs
@[simp] theorem mul_right_inv (a : G) : a * a⁻¹ = 1 :=
begin
  apply mul_eq_of_eq_inv_mul,
  rw mul_one,
end

-- My *instinct* now is that we are well on the way to a good rewrite system
-- except that I don't know how to handle associativity. Maybe mathematicians
-- are not so interested in confluent rewriting systems though. Skip `simp`
-- altogether here?

-- Now two things we need to prove that a ~ b := ab⁻¹ ∈ H is an equivalence relation

-- do we want to make automation work? *Can* we make automation work to solve this?
@[simp] theorem mul_inv_rev (a b : G) : (a * b)⁻¹ = b⁻¹ * a⁻¹ :=
begin
  apply mul_left_cancel (a * b),
  rw mul_right_inv,
  rw [mul_assoc, ←mul_assoc b],
  rw mul_right_inv,
  rw [one_mul, mul_right_inv]
end

@[simp] theorem inv_inv (a : G) : a⁻¹⁻¹ = a :=
begin
  apply mul_left_cancel a⁻¹,
  simp,
end

end group

/-! # Bundled subgroups -/

/-- A subgroup of a group G is a subset containing 1
and closed under multiplication and inverse. -/
structure subgroup (G : Type) [group G] :=
(carrier : set G)
(one_mem' : (1 : G) ∈ carrier)
(mul_mem' {x y} : x ∈ carrier → y ∈ carrier → x * y ∈ carrier)
(inv_mem' {x} : x ∈ carrier → x⁻¹ ∈ carrier)

-- non-dashed names later when we have ∈ notation for subgroups
-- I think this is worth doing because it looks sexier

namespace subgroup

variables {G : Type} [group G] (H : subgroup G)

-- ∈ notation
instance : has_mem G (subgroup G) := ⟨λ m H, m ∈ H.carrier⟩

/-- A subgroup contains the group's 1. -/
theorem one_mem : (1 : G) ∈ H := H.one_mem'

/-- A subgroup is closed under multiplication. -/
theorem mul_mem {x y : G} : x ∈ H → y ∈ H → x * y ∈ H := subgroup.mul_mem' H

/-- A subgroup is closed under inverse -/
theorem inv_mem {x : G} : x ∈ H → x⁻¹ ∈ H := subgroup.inv_mem' H

/- There's a lattice story here.

instance : has_le (subgroup G) := ⟨λ S T, S.carrier ⊆ T.carrier⟩

...
-/

-- do we need ext stuff? Don't think so

/-

/-- Two subgroups are equal if the underlying subsets are equal. -/
theorem ext' {H K : subgroup G} (h : H.carrier = K.carrier) : H = K :=
begin
  cases H,
  cases K,
  congr',
end

-- true by definition
lemma mem_coe {g : G} : g ∈ H.carrier ↔ g ∈ H := iff.rfl

/-- Two subgroups are equal if they have the same elements. -/
@[ext] theorem ext {H K : subgroup G}
  (h : ∀ x, x ∈ H ↔ x ∈ K) : H = K :=
begin
  apply ext',
  apply set.ext,
  assumption -- could rewrite mem_coe first
end

/-- Two subgroups are equal if and only if the underlying subsets are equal. -/
theorem ext'_iff {H K : subgroup G} : H.carrier = K.carrier ↔ H = K :=
begin
  split,
  { exact ext'},
  { intro h,
    rw h,
  }
end

-/

-- this gives us a coercion to sort, which we'll need when doing e.g. |H| (unless we use finset.card,
-- but then we'll need a coercion to finset)
instance : has_coe (subgroup G) (set G) := ⟨subgroup.carrier⟩

end subgroup

-- We now write *two* proofs of Lagrange's theorem, one using equiv rels and the other using cosets.

/-! # Lagrange's theorem via cosets -/
namespace group

section lagrange

variables {G : Type} [group G]

-- right coset
def coset (H : subgroup G) (a : G) : set G := {b | ∃ h ∈ H, b = h * a}

variable {H : subgroup G}

-- every element is in a coset
lemma mem_coset (a : G) : a ∈ coset H a :=
begin
  use 1,
  split,
  { exact H.one_mem},
  { rw one_mul}
end

-- intermediate lemma about overlapping cosets
lemma coset_sub_aux (a b : G) : (coset H a ∩ coset H b) ≠ ∅ → coset H a ⊆ coset H b :=
begin
  intro hab,
  rw set.ne_empty_iff_nonempty at hab,
  rcases hab with ⟨c, ⟨h1, H1, hca⟩, ⟨h2, H2, hcb⟩⟩,
  rintros g ⟨h3, H3, hgh⟩,
  use h3 * h1⁻¹ * h2,
  split,
  { refine H.mul_mem _ H2,
    refine H.mul_mem H3 _,
    exact H.inv_mem H1},
  { rw [hgh, mul_assoc, ←hcb, mul_assoc],
    congr',
    rw [hca, ←mul_assoc, mul_left_inv, one_mul],
  }
end

-- overlapping cosets are equal
lemma coset_eq (a b : G) (h : coset H a ∩ coset H b ≠ ∅) : coset H a = coset H b :=
begin
  apply set.subset.antisymm,
  { exact coset_sub_aux a b h},
  { convert coset_sub_aux b a _,
    rwa set.inter_comm,
  }
end

lemma coset_mul_inv (a b : G) (hb : b ∈ coset H a) : b * a⁻¹ ∈ H :=
begin
  rcases hb with ⟨h, hh, rfl⟩,
  rw mul_assoc, simp [hh]
end

-- x ↦ xa⁻¹b is a map from Ha to Hb
def coset_map (a b : G) : coset H a → coset H b :=
λ ha, ⟨ha.val * a⁻¹ * b, begin
  use ha.val * a⁻¹,
  split,
  { apply coset_mul_inv,
    exact ha.property}, -- need to say something about how set got coerced to subtype
  { refl}
end⟩

theorem coset_map_id (a : G) (g : coset H a) : coset_map a a g = g :=
begin
  unfold coset_map,
  rw subtype.ext, dsimp, -- can we make this nicer?
  rw mul_assoc, simp,
end

theorem coset_map_comp (a b c : G) (g : coset H a) : coset_map b c (coset_map a b g) = coset_map a c g :=
begin
  unfold coset_map,
  rw subtype.ext, dsimp, -- can we make this nicer?
  congr' 1,
  rw mul_assoc,
  simp,
end

-- all cosets have the same size
def coset_equiv (a b : G) : coset H a ≃ coset H b :=
{ to_fun := coset_map a b,
  inv_fun := coset_map b a,
  left_inv := begin
    intro g,
    rw coset_map_comp,
    apply coset_map_id,
  end,
  right_inv := λ g, by rw [coset_map_comp, coset_map_id]
}

end lagrange

/-

What is going to happen here is that we will end up with X : set (set G),
a collection of subsets of G (the cosets Ha) which are
(a) pairwise disjoint
(b) all the same size (namely |H|)
(c) cover G

and then we have to pull out some magic to prove that |H| divides |G|
in the case where everything is finite.
-/
end group

/-! # Lagrange's theorem via equiv relns -/
namespace group

section lagrange

parameters {G : Type} [group G] {H : subgroup G}

def rel : ∀ (a b : G), Prop := λ a b, a * b⁻¹ ∈ H

local notation a ` ~ ` b := rel a b -- needs to be local so I can use H

theorem rel_def (a b : G) : a ~ b ↔ a * b⁻¹ ∈ H := iff.rfl

theorem rel.refl : ∀ a : G, a ~ a :=
begin
  intro a,
  show a * a⁻¹ ∈ H,
  rw mul_right_inv,
  exact H.one_mem
end

theorem rel.symm : ∀ a b : G, a ~ b → b ~ a :=
begin
  intros a b hab,
  unfold rel at hab ⊢,
  convert H.inv_mem hab, -- introduce convert? This is a natural proof
  -- simp solves this now
  rw mul_inv_rev,
  rw inv_inv,
end

theorem rel.trans : ∀ a b c : G, a ~ b → b ~ c → a ~ c :=
begin
  intros a b c hab hbc,
  unfold rel at hab hbc ⊢,
  convert H.mul_mem hab hbc using 1,
  rw [mul_assoc, ←mul_assoc b⁻¹, mul_left_inv, one_mul],
  -- note no refl needed
end

theorem rel.eqv : equivalence (rel) := ⟨rel.refl, rel.symm, rel.trans⟩

/- Unused in this approach:

def eqclass (a : G) : set G := {b : G | a ~ b}

theorem mem_eqclass (a : G) : a ∈ eqclass a :=
begin
  exact rel.refl a,
end

def eqclasses : set (set G) := set.range eqclass

theorem eq_class_nonempty (C : set G) (hC : C ∈ eqclasses) : set.nonempty C :=
begin
  cases hC with a ha,
  rw ←ha,
  use a,
  apply mem_eqclass
end
-/

-- now we can use the axiom of choice to choose a set of representatives

-- equivalence classes are right cosets Ha

def rel.setoid : setoid G := ⟨rel, rel.eqv⟩

def GmodH := quotient (rel.setoid)

-- quotient.out chooses an element of an equivalence class

/-- let S be a set of coset reps for G/H -/
def S : set G := set.range (quotient.out' : GmodH → G)

/-- to_S sends a function to the canonical coset rep of its equivalence class -/
noncomputable def to_S : G → S := λ g, ⟨quotient.out' (quotient.mk' g : GmodH), _, rfl⟩

lemma rel_out (g : G) :
  to_S g ~ g :=
begin
  letI : setoid G := rel.setoid, -- incomprehensible to the beginner
  exact quotient.mk_out g,
end

lemma rel_out' (g : G) : g ~ to_S g :=
begin
  apply rel.symm,
  apply rel_out,
end

-- this is horrible
lemma to_S_idem (g : G) : to_S (to_S g).val = to_S g :=
begin
  unfold to_S,
  dsimp,
  rw subtype.ext,
  dsimp,
  rw quotient.out_eq',
end

lemma to_S_s (s : S) : to_S s = s :=
begin
  rcases s with ⟨_, gbar, rfl⟩,
  unfold to_S,
  simp,
end

lemma to_S_h_mul (g : G) (h : H) : to_S (h * g) = to_S g :=
begin
  unfold to_S,
  rw subtype.ext,
  dsimp,
  congr' 1,
  apply quotient.sound',
  -- where is my notation
  show ↑h * g * g⁻¹ ∈ H,
  convert h.property,
  rw mul_assoc,
  simp, refl,
end

noncomputable def quotient_times_cosetreps : S × H ≃ G :=
{ to_fun := λ hs, (hs.2 * hs.1),
  inv_fun := λ g, ⟨to_S g, ⟨g * (to_S g)⁻¹, rel_out' g⟩⟩,
  left_inv := begin
    intro sh,
    cases sh with s h,
    ext,
    { show to_S (↑h * ↑s) = s, -- goal is a nightmare before this
      rw to_S_h_mul,
      apply to_S_s,
    },
    { dsimp,
      rw subtype.ext,
      dsimp,
      rw to_S_h_mul,
      rw mul_assoc,
      convert mul_one ↑h,
      convert mul_right_inv ↑s,
      apply to_S_s,
    }
  end,
  right_inv := begin
    intro g,
    show g * (↑(to_S g))⁻¹ * ↑(to_S g) = g,
    rw mul_assoc,
    simp,
  end }

-- Now Lagrange follows by card_prod

end lagrange

end group

namespace group

/-! # powers  -/

open nat
-- definition of powers
@[simp] def pow {G : Type} [group G] : G → ℕ → G
| g 0 := 1
| g (n + 1) := (pow g n) * g

instance has_pow {G : Type} [group G] : has_pow G ℕ := ⟨pow⟩

variables (G : Type) [group G]

-- interface so term mode proof
def pow_zero (g : G) : g ^ 0 = 1 := rfl

-- interface so term mode proof
def pow_succ (g : G) (n : ℕ) : g ^ (succ n) = g^n * g := rfl

def pow_add (g : G) (a b : ℕ) : g ^ (a + b) = g^a * g^b :=
begin
  induction b with d hd,
  { symmetry, apply mul_one}, -- slightly cocky proof using a lot of defeq. do we want to talk about this?
  { rw pow_succ, -- I wasn't expecting that! Maybe `show g^(succ(a + d))=g^a*g^(succ d)` is a good first move?
    rw pow_succ,
    rw hd,
    rw mul_assoc,
  }
end

def pow_mul (g : G) (a b : ℕ) : g ^ (a * b) = (g ^ a) ^ b :=
begin
  induction b with d hd,
  { rw mul_zero,
    refl, -- cocky
  },
  { rw mul_succ,
    rw pow_add,
    rw hd,
    rw pow_succ,
  }
end

/- Do we need this?
-- To understand this one needs to understand the definition of ℤ :-(
def zpow {G : Type} [group G] : G → ℤ → G
| g (int.of_nat n) := pow g n
| g -[1+ n] := (g ^ (n + 1))⁻¹

instance has_zpow {G : Type} [group G] : has_pow G ℤ := ⟨zpow⟩

-- def zpow_add (g : G) (a b : ℤ) : g ^ (a + b) = g^a * g^b :=
-/

end group

end mygroup