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

! This file was ported from Lean 3 source module group_theory.group_action.units
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.GroupTheory.GroupAction.Defs

/-! # Group actions on and by `Mˣ`

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

This file provides the action of a unit on a type `α`, `has_smul Mˣ α`, in the presence of
`has_smul M α`, with the obvious definition stated in `units.smul_def`. This definition preserves
`mul_action` and `distrib_mul_action` structures too.

Additionally, a `mul_action G M` for some group `G` satisfying some additional properties admits a
`mul_action G Mˣ` structure, again with the obvious definition stated in `units.coe_smul`.
These instances use a primed name.

The results are repeated for `add_units` and `has_vadd` where relevant.
-/

variable {G H M N : Type _} {α : Type _}

namespace Units

/-! ### Action of the units of `M` on a type `α` -/

@[to_additive]
instance [Monoid M] [SMul M α] : SMul Mˣ α where smul m a := (m : M) • a

/- warning: units.smul_def -> Units.smul_def is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {α : Type.{u2}} [_inst_1 : Monoid.{u1} M] [_inst_2 : SMul.{u1, u2} M α] (m : Units.{u1} M _inst_1) (a : α), Eq.{succ u2} α (SMul.smul.{u1, u2} (Units.{u1} M _inst_1) α (Units.hasSmul.{u1, u2} M α _inst_1 _inst_2) m a) (SMul.smul.{u1, u2} M α _inst_2 ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) m) a)
but is expected to have type
  forall {M : Type.{u2}} {α : Type.{u1}} [_inst_1 : Monoid.{u2} M] [_inst_2 : SMul.{u2, u1} M α] (m : Units.{u2} M _inst_1) (a : α), Eq.{succ u1} α (HSMul.hSMul.{u2, u1, u1} (Units.{u2} M _inst_1) α α (instHSMul.{u2, u1} (Units.{u2} M _inst_1) α (Units.instSMulUnits.{u2, u1} M α _inst_1 _inst_2)) m a) (HSMul.hSMul.{u2, u1, u1} M α α (instHSMul.{u2, u1} M α _inst_2) (Units.val.{u2} M _inst_1 m) a)
Case conversion may be inaccurate. Consider using '#align units.smul_def Units.smul_defₓ'. -/
@[to_additive]
theorem smul_def [Monoid M] [SMul M α] (m : Mˣ) (a : α) : m • a = (m : M) • a :=
  rfl
#align units.smul_def Units.smul_def
#align add_units.vadd_def AddUnits.vadd_def

/- warning: units.smul_is_unit -> Units.smul_isUnit is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {α : Type.{u2}} [_inst_1 : Monoid.{u1} M] [_inst_2 : SMul.{u1, u2} M α] {m : M} (hm : IsUnit.{u1} M _inst_1 m) (a : α), Eq.{succ u2} α (SMul.smul.{u1, u2} (Units.{u1} M _inst_1) α (Units.hasSmul.{u1, u2} M α _inst_1 _inst_2) (IsUnit.unit.{u1} M _inst_1 m hm) a) (SMul.smul.{u1, u2} M α _inst_2 m a)
but is expected to have type
  forall {M : Type.{u2}} {α : Type.{u1}} [_inst_1 : Monoid.{u2} M] [_inst_2 : SMul.{u2, u1} M α] {m : M} (hm : IsUnit.{u2} M _inst_1 m) (a : α), Eq.{succ u1} α (HSMul.hSMul.{u2, u1, u1} (Units.{u2} M _inst_1) α α (instHSMul.{u2, u1} (Units.{u2} M _inst_1) α (Units.instSMulUnits.{u2, u1} M α _inst_1 _inst_2)) (IsUnit.unit.{u2} M _inst_1 m hm) a) (HSMul.hSMul.{u2, u1, u1} M α α (instHSMul.{u2, u1} M α _inst_2) m a)
Case conversion may be inaccurate. Consider using '#align units.smul_is_unit Units.smul_isUnitₓ'. -/
@[simp]
theorem smul_isUnit [Monoid M] [SMul M α] {m : M} (hm : IsUnit m) (a : α) : hm.Unit • a = m • a :=
  rfl
#align units.smul_is_unit Units.smul_isUnit

/- warning: is_unit.inv_smul -> IsUnit.inv_smul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Monoid.{u1} α] {a : α} (h : IsUnit.{u1} α _inst_1 a), Eq.{succ u1} α (SMul.smul.{u1, u1} (Units.{u1} α _inst_1) α (Units.hasSmul.{u1, u1} α α _inst_1 (Mul.toSMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)))) (Inv.inv.{u1} (Units.{u1} α _inst_1) (Units.hasInv.{u1} α _inst_1) (IsUnit.unit.{u1} α _inst_1 a h)) a) (OfNat.ofNat.{u1} α 1 (OfNat.mk.{u1} α 1 (One.one.{u1} α (MulOneClass.toHasOne.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Monoid.{u1} α] {a : α} (h : IsUnit.{u1} α _inst_1 a), Eq.{succ u1} α (HSMul.hSMul.{u1, u1, u1} (Units.{u1} α _inst_1) α α (instHSMul.{u1, u1} (Units.{u1} α _inst_1) α (Units.instSMulUnits.{u1, u1} α α _inst_1 (MulAction.toSMul.{u1, u1} α α _inst_1 (Monoid.toMulAction.{u1} α _inst_1)))) (Inv.inv.{u1} (Units.{u1} α _inst_1) (Units.instInvUnits.{u1} α _inst_1) (IsUnit.unit.{u1} α _inst_1 a h)) a) (OfNat.ofNat.{u1} α 1 (One.toOfNat1.{u1} α (Monoid.toOne.{u1} α _inst_1)))
Case conversion may be inaccurate. Consider using '#align is_unit.inv_smul IsUnit.inv_smulₓ'. -/
theorem IsUnit.inv_smul [Monoid α] {a : α} (h : IsUnit a) : h.Unit⁻¹ • a = 1 :=
  h.val_inv_mul
#align is_unit.inv_smul IsUnit.inv_smul

@[to_additive]
instance [Monoid M] [SMul M α] [FaithfulSMul M α] : FaithfulSMul Mˣ α
    where eq_of_smul_eq_smul u₁ u₂ h := Units.ext <| eq_of_smul_eq_smul h

@[to_additive]
instance [Monoid M] [MulAction M α] : MulAction Mˣ α
    where
  one_smul := (one_smul M : _)
  mul_smul m n := mul_smul (m : M) n

instance [Monoid M] [Zero α] [SMulZeroClass M α] : SMulZeroClass Mˣ α
    where
  smul := (· • ·)
  smul_zero m := smul_zero m

instance [Monoid M] [AddZeroClass α] [DistribSMul M α] : DistribSMul Mˣ α
    where smul_add m := smul_add (m : M)

instance [Monoid M] [AddMonoid α] [DistribMulAction M α] : DistribMulAction Mˣ α :=
  { Units.distribSmul with }

instance [Monoid M] [Monoid α] [MulDistribMulAction M α] : MulDistribMulAction Mˣ α
    where
  smul_mul m := smul_mul' (m : M)
  smul_one m := smul_one m

#print Units.smulCommClass_left /-
instance smulCommClass_left [Monoid M] [SMul M α] [SMul N α] [SMulCommClass M N α] :
    SMulCommClass Mˣ N α where smul_comm m n := (smul_comm (m : M) n : _)
#align units.smul_comm_class_left Units.smulCommClass_left
-/

#print Units.smulCommClass_right /-
instance smulCommClass_right [Monoid N] [SMul M α] [SMul N α] [SMulCommClass M N α] :
    SMulCommClass M Nˣ α where smul_comm m n := (smul_comm m (n : N) : _)
#align units.smul_comm_class_right Units.smulCommClass_right
-/

instance [Monoid M] [SMul M N] [SMul M α] [SMul N α] [IsScalarTower M N α] : IsScalarTower Mˣ N α
    where smul_assoc m n := (smul_assoc (m : M) n : _)

/-! ### Action of a group `G` on units of `M` -/

/- warning: units.mul_action' -> Units.mulAction' is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)], MulAction.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))
but is expected to have type
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2)) (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)], MulAction.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))
Case conversion may be inaccurate. Consider using '#align units.mul_action' Units.mulAction'ₓ'. -/
/-- If an action `G` associates and commutes with multiplication on `M`, then it lifts to an
action on `Mˣ`. Notably, this provides `mul_action Mˣ Nˣ` under suitable
conditions.
-/
instance mulAction' [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M]
    [IsScalarTower G M M] : MulAction G Mˣ
    where
  smul g m :=
    ⟨g • (m : M), g⁻¹ • ↑m⁻¹, by rw [smul_mul_smul, Units.mul_inv, mul_right_inv, one_smul], by
      rw [smul_mul_smul, Units.inv_mul, mul_left_inv, one_smul]⟩
  one_smul m := Units.ext <| one_smul _ _
  mul_smul g₁ g₂ m := Units.ext <| mul_smul _ _ _
#align units.mul_action' Units.mulAction'

/- warning: units.coe_smul -> Units.val_smul is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)] (g : G) (m : Units.{u2} M _inst_2), Eq.{succ u2} M ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (Units.{u2} M _inst_2) M (HasLiftT.mk.{succ u2, succ u2} (Units.{u2} M _inst_2) M (CoeTCₓ.coe.{succ u2, succ u2} (Units.{u2} M _inst_2) M (coeBase.{succ u2, succ u2} (Units.{u2} M _inst_2) M (Units.hasCoe.{u2} M _inst_2)))) (SMul.smul.{u1, u2} G (Units.{u2} M _inst_2) (MulAction.toHasSmul.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u2} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) g m)) (SMul.smul.{u1, u2} G M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) g ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (Units.{u2} M _inst_2) M (HasLiftT.mk.{succ u2, succ u2} (Units.{u2} M _inst_2) M (CoeTCₓ.coe.{succ u2, succ u2} (Units.{u2} M _inst_2) M (coeBase.{succ u2, succ u2} (Units.{u2} M _inst_2) M (Units.hasCoe.{u2} M _inst_2)))) m))
but is expected to have type
  forall {G : Type.{u2}} {M : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Monoid.{u1} M] [_inst_3 : MulAction.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))] [_inst_4 : SMulCommClass.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2))] [_inst_5 : IsScalarTower.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2)) (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3)] (g : G) (m : Units.{u1} M _inst_2), Eq.{succ u1} M (Units.val.{u1} M _inst_2 (HSMul.hSMul.{u2, u1, u1} G (Units.{u1} M _inst_2) (Units.{u1} M _inst_2) (instHSMul.{u2, u1} G (Units.{u1} M _inst_2) (MulAction.toSMul.{u2, u1} G (Units.{u1} M _inst_2) (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) (Units.mulAction'.{u2, u1} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5))) g m)) (HSMul.hSMul.{u2, u1, u1} G M M (instHSMul.{u2, u1} G M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3)) g (Units.val.{u1} M _inst_2 m))
Case conversion may be inaccurate. Consider using '#align units.coe_smul Units.val_smulₓ'. -/
@[simp]
theorem val_smul [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    (g : G) (m : Mˣ) : ↑(g • m) = g • (m : M) :=
  rfl
#align units.coe_smul Units.val_smul

/- warning: units.smul_inv -> Units.smul_inv is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)] (g : G) (m : Units.{u2} M _inst_2), Eq.{succ u2} (Units.{u2} M _inst_2) (Inv.inv.{u2} (Units.{u2} M _inst_2) (Units.hasInv.{u2} M _inst_2) (SMul.smul.{u1, u2} G (Units.{u2} M _inst_2) (MulAction.toHasSmul.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u2} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) g m)) (SMul.smul.{u1, u2} G (Units.{u2} M _inst_2) (MulAction.toHasSmul.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u2} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5)) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) g) (Inv.inv.{u2} (Units.{u2} M _inst_2) (Units.hasInv.{u2} M _inst_2) m))
but is expected to have type
  forall {G : Type.{u2}} {M : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Monoid.{u1} M] [_inst_3 : MulAction.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))] [_inst_4 : SMulCommClass.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2))] [_inst_5 : IsScalarTower.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2)) (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3)] (g : G) (m : Units.{u1} M _inst_2), Eq.{succ u1} (Units.{u1} M _inst_2) (Inv.inv.{u1} (Units.{u1} M _inst_2) (Units.instInvUnits.{u1} M _inst_2) (HSMul.hSMul.{u2, u1, u1} G (Units.{u1} M _inst_2) (Units.{u1} M _inst_2) (instHSMul.{u2, u1} G (Units.{u1} M _inst_2) (MulAction.toSMul.{u2, u1} G (Units.{u1} M _inst_2) (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) (Units.mulAction'.{u2, u1} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5))) g m)) (HSMul.hSMul.{u2, u1, u1} G (Units.{u1} M _inst_2) (Units.{u1} M _inst_2) (instHSMul.{u2, u1} G (Units.{u1} M _inst_2) (MulAction.toSMul.{u2, u1} G (Units.{u1} M _inst_2) (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) (Units.mulAction'.{u2, u1} G M _inst_1 _inst_2 _inst_3 _inst_4 _inst_5))) (Inv.inv.{u2} G (InvOneClass.toInv.{u2} G (DivInvOneMonoid.toInvOneClass.{u2} G (DivisionMonoid.toDivInvOneMonoid.{u2} G (Group.toDivisionMonoid.{u2} G _inst_1)))) g) (Inv.inv.{u1} (Units.{u1} M _inst_2) (Units.instInvUnits.{u1} M _inst_2) m))
Case conversion may be inaccurate. Consider using '#align units.smul_inv Units.smul_invₓ'. -/
/-- Note that this lemma exists more generally as the global `smul_inv` -/
@[simp]
theorem smul_inv [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    (g : G) (m : Mˣ) : (g • m)⁻¹ = g⁻¹ • m⁻¹ :=
  ext rfl
#align units.smul_inv Units.smul_inv

/- warning: units.smul_comm_class' -> Units.smulCommClass' is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {H : Type.{u2}} {M : Type.{u3}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} H] [_inst_3 : Monoid.{u3} M] [_inst_4 : MulAction.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_5 : SMulCommClass.{u1, u3, u3} G M M (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_3)))] [_inst_6 : MulAction.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2))] [_inst_7 : SMulCommClass.{u2, u3, u3} H M M (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_3)))] [_inst_8 : IsScalarTower.{u1, u3, u3} G M M (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_3))) (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4)] [_inst_9 : IsScalarTower.{u2, u3, u3} H M M (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_3))) (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6)] [_inst_10 : SMulCommClass.{u1, u2, u3} G H M (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6)], SMulCommClass.{u1, u2, u3} G H (Units.{u3} M _inst_3) (MulAction.toHasSmul.{u1, u3} G (Units.{u3} M _inst_3) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u3} G M _inst_1 _inst_3 _inst_4 _inst_5 _inst_8)) (MulAction.toHasSmul.{u2, u3} H (Units.{u3} M _inst_3) (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) (Units.mulAction'.{u2, u3} H M _inst_2 _inst_3 _inst_6 _inst_7 _inst_9))
but is expected to have type
  forall {G : Type.{u1}} {H : Type.{u2}} {M : Type.{u3}} [_inst_1 : Group.{u1} G] [_inst_2 : Group.{u2} H] [_inst_3 : Monoid.{u3} M] [_inst_4 : MulAction.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_5 : SMulCommClass.{u1, u3, u3} G M M (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (MulAction.toSMul.{u3, u3} M M _inst_3 (Monoid.toMulAction.{u3} M _inst_3))] [_inst_6 : MulAction.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2))] [_inst_7 : SMulCommClass.{u2, u3, u3} H M M (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6) (MulAction.toSMul.{u3, u3} M M _inst_3 (Monoid.toMulAction.{u3} M _inst_3))] [_inst_8 : IsScalarTower.{u1, u3, u3} G M M (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (MulAction.toSMul.{u3, u3} M M _inst_3 (Monoid.toMulAction.{u3} M _inst_3)) (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4)] [_inst_9 : IsScalarTower.{u2, u3, u3} H M M (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6) (MulAction.toSMul.{u3, u3} M M _inst_3 (Monoid.toMulAction.{u3} M _inst_3)) (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6)] [_inst_10 : SMulCommClass.{u1, u2, u3} G H M (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_4) (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) _inst_6)], SMulCommClass.{u1, u2, u3} G H (Units.{u3} M _inst_3) (MulAction.toSMul.{u1, u3} G (Units.{u3} M _inst_3) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u3} G M _inst_1 _inst_3 _inst_4 _inst_5 _inst_8)) (MulAction.toSMul.{u2, u3} H (Units.{u3} M _inst_3) (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_2)) (Units.mulAction'.{u2, u3} H M _inst_2 _inst_3 _inst_6 _inst_7 _inst_9))
Case conversion may be inaccurate. Consider using '#align units.smul_comm_class' Units.smulCommClass'ₓ'. -/
/-- Transfer `smul_comm_class G H M` to `smul_comm_class G H Mˣ` -/
instance smulCommClass' [Group G] [Group H] [Monoid M] [MulAction G M] [SMulCommClass G M M]
    [MulAction H M] [SMulCommClass H M M] [IsScalarTower G M M] [IsScalarTower H M M]
    [SMulCommClass G H M] : SMulCommClass G H Mˣ
    where smul_comm g h m := Units.ext <| smul_comm g h (m : M)
#align units.smul_comm_class' Units.smulCommClass'

/- warning: units.is_scalar_tower' -> Units.isScalarTower' is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {H : Type.{u2}} {M : Type.{u3}} [_inst_1 : SMul.{u1, u2} G H] [_inst_2 : Group.{u1} G] [_inst_3 : Group.{u2} H] [_inst_4 : Monoid.{u3} M] [_inst_5 : MulAction.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2))] [_inst_6 : SMulCommClass.{u1, u3, u3} G M M (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_4)))] [_inst_7 : MulAction.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))] [_inst_8 : SMulCommClass.{u2, u3, u3} H M M (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_4)))] [_inst_9 : IsScalarTower.{u1, u3, u3} G M M (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_4))) (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5)] [_inst_10 : IsScalarTower.{u2, u3, u3} H M M (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (Mul.toSMul.{u3} M (MulOneClass.toHasMul.{u3} M (Monoid.toMulOneClass.{u3} M _inst_4))) (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7)] [_inst_11 : IsScalarTower.{u1, u2, u3} G H M _inst_1 (MulAction.toHasSmul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (MulAction.toHasSmul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5)], IsScalarTower.{u1, u2, u3} G H (Units.{u3} M _inst_4) _inst_1 (MulAction.toHasSmul.{u2, u3} H (Units.{u3} M _inst_4) (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) (Units.mulAction'.{u2, u3} H M _inst_3 _inst_4 _inst_7 _inst_8 _inst_10)) (MulAction.toHasSmul.{u1, u3} G (Units.{u3} M _inst_4) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) (Units.mulAction'.{u1, u3} G M _inst_2 _inst_4 _inst_5 _inst_6 _inst_9))
but is expected to have type
  forall {G : Type.{u1}} {H : Type.{u2}} {M : Type.{u3}} [_inst_1 : SMul.{u1, u2} G H] [_inst_2 : Group.{u1} G] [_inst_3 : Group.{u2} H] [_inst_4 : Monoid.{u3} M] [_inst_5 : MulAction.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2))] [_inst_6 : SMulCommClass.{u1, u3, u3} G M M (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5) (MulAction.toSMul.{u3, u3} M M _inst_4 (Monoid.toMulAction.{u3} M _inst_4))] [_inst_7 : MulAction.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))] [_inst_8 : SMulCommClass.{u2, u3, u3} H M M (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (MulAction.toSMul.{u3, u3} M M _inst_4 (Monoid.toMulAction.{u3} M _inst_4))] [_inst_9 : IsScalarTower.{u1, u3, u3} G M M (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5) (MulAction.toSMul.{u3, u3} M M _inst_4 (Monoid.toMulAction.{u3} M _inst_4)) (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5)] [_inst_10 : IsScalarTower.{u2, u3, u3} H M M (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (MulAction.toSMul.{u3, u3} M M _inst_4 (Monoid.toMulAction.{u3} M _inst_4)) (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7)] [_inst_11 : IsScalarTower.{u1, u2, u3} G H M _inst_1 (MulAction.toSMul.{u2, u3} H M (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) _inst_7) (MulAction.toSMul.{u1, u3} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) _inst_5)], IsScalarTower.{u1, u2, u3} G H (Units.{u3} M _inst_4) _inst_1 (MulAction.toSMul.{u2, u3} H (Units.{u3} M _inst_4) (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)) (Units.mulAction'.{u2, u3} H M _inst_3 _inst_4 _inst_7 _inst_8 _inst_10)) (MulAction.toSMul.{u1, u3} G (Units.{u3} M _inst_4) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_2)) (Units.mulAction'.{u1, u3} G M _inst_2 _inst_4 _inst_5 _inst_6 _inst_9))
Case conversion may be inaccurate. Consider using '#align units.is_scalar_tower' Units.isScalarTower'ₓ'. -/
/-- Transfer `is_scalar_tower G H M` to `is_scalar_tower G H Mˣ` -/
instance isScalarTower' [SMul G H] [Group G] [Group H] [Monoid M] [MulAction G M]
    [SMulCommClass G M M] [MulAction H M] [SMulCommClass H M M] [IsScalarTower G M M]
    [IsScalarTower H M M] [IsScalarTower G H M] : IsScalarTower G H Mˣ
    where smul_assoc g h m := Units.ext <| smul_assoc g h (m : M)
#align units.is_scalar_tower' Units.isScalarTower'

/- warning: units.is_scalar_tower'_left -> Units.isScalarTower'_left is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} {α : Type.{u3}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMul.{u2, u3} M α] [_inst_5 : SMul.{u1, u3} G α] [_inst_6 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_7 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)] [_inst_8 : IsScalarTower.{u1, u2, u3} G M α (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) _inst_4 _inst_5], IsScalarTower.{u1, u2, u3} G (Units.{u2} M _inst_2) α (MulAction.toHasSmul.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u2} G M _inst_1 _inst_2 _inst_3 _inst_6 _inst_7)) (Units.hasSmul.{u2, u3} M α _inst_2 _inst_4) _inst_5
but is expected to have type
  forall {G : Type.{u1}} {M : Type.{u2}} {α : Type.{u3}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMul.{u2, u3} M α] [_inst_5 : SMul.{u1, u3} G α] [_inst_6 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2))] [_inst_7 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2)) (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)] [_inst_8 : IsScalarTower.{u1, u2, u3} G M α (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) _inst_4 _inst_5], IsScalarTower.{u1, u2, u3} G (Units.{u2} M _inst_2) α (MulAction.toSMul.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (Units.mulAction'.{u1, u2} G M _inst_1 _inst_2 _inst_3 _inst_6 _inst_7)) (Units.instSMulUnits.{u2, u3} M α _inst_2 _inst_4) _inst_5
Case conversion may be inaccurate. Consider using '#align units.is_scalar_tower'_left Units.isScalarTower'_leftₓ'. -/
/-- Transfer `is_scalar_tower G M α` to `is_scalar_tower G Mˣ α` -/
instance isScalarTower'_left [Group G] [Monoid M] [MulAction G M] [SMul M α] [SMul G α]
    [SMulCommClass G M M] [IsScalarTower G M M] [IsScalarTower G M α] : IsScalarTower G Mˣ α
    where smul_assoc g m := (smul_assoc g (m : M) : _)
#align units.is_scalar_tower'_left Units.isScalarTower'_left

-- Just to prove this transfers a particularly useful instance.
example [Monoid M] [Monoid N] [MulAction M N] [SMulCommClass M N N] [IsScalarTower M N N] :
    MulAction Mˣ Nˣ :=
  Units.mulAction'

/- warning: units.mul_distrib_mul_action' -> Units.mulDistribMulAction' is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulDistribMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3)) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3)) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3))], MulDistribMulAction.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (DivInvMonoid.toMonoid.{u2} (Units.{u2} M _inst_2) (Group.toDivInvMonoid.{u2} (Units.{u2} M _inst_2) (Units.group.{u2} M _inst_2)))
but is expected to have type
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulDistribMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3)) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3)) (MulAction.toSMul.{u2, u2} M M _inst_2 (Monoid.toMulAction.{u2} M _inst_2)) (MulAction.toSMul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (MulDistribMulAction.toMulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_2 _inst_3))], MulDistribMulAction.{u1, u2} G (Units.{u2} M _inst_2) (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) (DivInvMonoid.toMonoid.{u2} (Units.{u2} M _inst_2) (Group.toDivInvMonoid.{u2} (Units.{u2} M _inst_2) (Units.instGroupUnits.{u2} M _inst_2)))
Case conversion may be inaccurate. Consider using '#align units.mul_distrib_mul_action' Units.mulDistribMulAction'ₓ'. -/
/-- A stronger form of `units.mul_action'`. -/
instance mulDistribMulAction' [Group G] [Monoid M] [MulDistribMulAction G M] [SMulCommClass G M M]
    [IsScalarTower G M M] : MulDistribMulAction G Mˣ :=
  { Units.mulAction' with
    smul := (· • ·)
    smul_one := fun m => Units.ext <| smul_one _
    smul_mul := fun g m₁ m₂ => Units.ext <| smul_mul' _ _ _ }
#align units.mul_distrib_mul_action' Units.mulDistribMulAction'

end Units

/- warning: is_unit.smul -> IsUnit.smul is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {M : Type.{u2}} [_inst_1 : Group.{u1} G] [_inst_2 : Monoid.{u2} M] [_inst_3 : MulAction.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))] [_inst_4 : SMulCommClass.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)))] [_inst_5 : IsScalarTower.{u1, u2, u2} G M M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) (Mul.toSMul.{u2} M (MulOneClass.toHasMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2))) (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3)] {m : M} (g : G), (IsUnit.{u2} M _inst_2 m) -> (IsUnit.{u2} M _inst_2 (SMul.smul.{u1, u2} G M (MulAction.toHasSmul.{u1, u2} G M (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) _inst_3) g m))
but is expected to have type
  forall {G : Type.{u2}} {M : Type.{u1}} [_inst_1 : Group.{u2} G] [_inst_2 : Monoid.{u1} M] [_inst_3 : MulAction.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))] [_inst_4 : SMulCommClass.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2))] [_inst_5 : IsScalarTower.{u2, u1, u1} G M M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3) (MulAction.toSMul.{u1, u1} M M _inst_2 (Monoid.toMulAction.{u1} M _inst_2)) (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3)] {m : M} (g : G), (IsUnit.{u1} M _inst_2 m) -> (IsUnit.{u1} M _inst_2 (HSMul.hSMul.{u2, u1, u1} G M M (instHSMul.{u2, u1} G M (MulAction.toSMul.{u2, u1} G M (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) _inst_3)) g m))
Case conversion may be inaccurate. Consider using '#align is_unit.smul IsUnit.smulₓ'. -/
theorem IsUnit.smul [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    {m : M} (g : G) (h : IsUnit m) : IsUnit (g • m) :=
  let ⟨u, hu⟩ := h
  hu ▸ ⟨g • u, Units.val_smul _ _⟩
#align is_unit.smul IsUnit.smul


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/

import algebra.char_p
import algebra.ring.pi
import analysis.special_functions.pow
import field_theory.perfect_closure
import ring_theory.localization
import ring_theory.subring
import ring_theory.valuation.integers

/-!
# Ring Perfection and Tilt

In this file we define the perfection of a ring of characteristic p, and the tilt of a field
given a valuation to `ℝ≥0`.

## TODO

Define the valuation on the tilt, and define a characteristic predicate for the tilt.

-/

universes u₁ u₂ u₃ u₄

open_locale nnreal

/-- The perfection of a monoid `M`, defined to be the projective limit of `M`
using the `p`-th power maps `M → M` indexed by the natural numbers, implemented as
`{ f : ℕ → M | ∀ n, f (n + 1) ^ p = f n }`. -/
def monoid.perfection (M : Type u₁) [comm_monoid M] (p : ℕ) : submonoid (ℕ → M) :=
{ carrier := { f | ∀ n, f (n + 1) ^ p = f n },
  one_mem' := λ n, one_pow _,
  mul_mem' := λ f g hf hg n, (mul_pow _ _ _).trans $ congr_arg2 _ (hf n) (hg n) }

/-- The perfection of a ring `R` with characteristic `p`,
defined to be the projective limit of `R` using the Frobenius maps `R → R`
indexed by the natural numbers, implemented as `{ f : ℕ → R | ∀ n, f (n + 1) ^ p = f n }`. -/
def ring.perfection (R : Type u₁) [comm_semiring R]
  (p : ℕ) [hp : fact p.prime] [char_p R p] :
  subsemiring (ℕ → R) :=
{ zero_mem' := λ n, zero_pow $ hp.1.pos,
  add_mem' := λ f g hf hg n, (frobenius_add R p _ _).trans $ congr_arg2 _ (hf n) (hg n),
  .. monoid.perfection R p }

namespace perfection

variables (R : Type u₁) [comm_semiring R] (p : ℕ) [hp : fact p.prime] [char_p R p]
include hp

/-- The `n`-th coefficient of an element of the perfection. -/
def coeff (n : ℕ) : ring.perfection R p →+* R :=
{ to_fun := λ f, f.1 n,
  map_one' := rfl,
  map_mul' := λ f g, rfl,
  map_zero' := rfl,
  map_add' := λ f g, rfl }

variables {R p}

@[ext] lemma ext {f g : ring.perfection R p} (h : ∀ n, coeff R p n f = coeff R p n g) : f = g :=
subtype.eq $ funext h

variables (R p)

/-- The `p`-th root of an element of the perfection. -/
def pth_root : ring.perfection R p →+* ring.perfection R p :=
{ to_fun := λ f, ⟨λ n, coeff R p (n + 1) f, λ n, f.2 _⟩,
  map_one' := rfl,
  map_mul' := λ f g, rfl,
  map_zero' := rfl,
  map_add' := λ f g, rfl }

variables {R p}

@[simp] lemma coeff_mk (f : ℕ → R) (hf) (n : ℕ) : coeff R p n ⟨f, hf⟩ = f n := rfl

lemma coeff_pth_root (f : ring.perfection R p) (n : ℕ) :
  coeff R p n (pth_root R p f) = coeff R p (n + 1) f :=
rfl

lemma coeff_pow_p (f : ring.perfection R p) (n : ℕ) :
  coeff R p (n + 1) (f ^ p) = coeff R p n f :=
by { rw ring_hom.map_pow, exact f.2 n }

lemma coeff_pow_p' (f : ring.perfection R p) (n : ℕ) :
  coeff R p (n + 1) f ^ p = coeff R p n f :=
f.2 n

lemma coeff_frobenius (f : ring.perfection R p) (n : ℕ) :
  coeff R p (n + 1) (frobenius _ p f) = coeff R p n f :=
by apply coeff_pow_p f n -- `coeff_pow_p f n` also works but is slow!

lemma coeff_iterate_frobenius (f : ring.perfection R p) (n m : ℕ) :
  coeff R p (n + m) (frobenius _ p ^[m] f) = coeff R p n f :=
nat.rec_on m rfl $ λ m ih, by erw [function.iterate_succ_apply', coeff_frobenius, ih]

lemma coeff_iterate_frobenius' (f : ring.perfection R p) (n m : ℕ) (hmn : m ≤ n) :
  coeff R p n (frobenius _ p ^[m] f) = coeff R p (n - m) f :=
eq.symm $ (coeff_iterate_frobenius _ _ m).symm.trans $ (nat.sub_add_cancel hmn).symm ▸ rfl

lemma pth_root_frobenius : (pth_root R p).comp (frobenius _ p) = ring_hom.id _ :=
ring_hom.ext $ λ x, ext $ λ n,
by rw [ring_hom.comp_apply, ring_hom.id_apply, coeff_pth_root, coeff_frobenius]

lemma frobenius_pth_root : (frobenius _ p).comp (pth_root R p) = ring_hom.id _ :=
ring_hom.ext $ λ x, ext $ λ n,
by rw [ring_hom.comp_apply, ring_hom.id_apply, ring_hom.map_frobenius, coeff_pth_root,
    ← ring_hom.map_frobenius, coeff_frobenius]

lemma coeff_add_ne_zero {f : ring.perfection R p} {n : ℕ} (hfn : coeff R p n f ≠ 0) (k : ℕ) :
  coeff R p (n + k) f ≠ 0 :=
nat.rec_on k hfn $ λ k ih h, ih $ by erw [← coeff_pow_p, ring_hom.map_pow, h, zero_pow hp.1.pos]

lemma coeff_ne_zero_of_le {f : ring.perfection R p} {m n : ℕ} (hfm : coeff R p m f ≠ 0)
  (hmn : m ≤ n) : coeff R p n f ≠ 0 :=
let ⟨k, hk⟩ := nat.exists_eq_add_of_le hmn in hk.symm ▸ coeff_add_ne_zero hfm k

variables (R p)

instance perfect_ring : perfect_ring (ring.perfection R p) p :=
{ pth_root' := pth_root R p,
  frobenius_pth_root' := congr_fun $ congr_arg ring_hom.to_fun $ @frobenius_pth_root R _ p _ _,
  pth_root_frobenius' := congr_fun $ congr_arg ring_hom.to_fun $ @pth_root_frobenius R _ p _ _ }

instance ring (R : Type u₁) [comm_ring R] [char_p R p] : ring (ring.perfection R p) :=
((ring.perfection R p).to_subring $ λ n, by simp_rw [← frobenius_def, pi.neg_apply,
    pi.one_apply, ring_hom.map_neg, ring_hom.map_one]).to_ring

instance comm_ring (R : Type u₁) [comm_ring R] [char_p R p] : comm_ring (ring.perfection R p) :=
((ring.perfection R p).to_subring $ λ n, by simp_rw [← frobenius_def, pi.neg_apply,
    pi.one_apply, ring_hom.map_neg, ring_hom.map_one]).to_comm_ring

/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* perfection S p`. -/
@[simps] def lift (R : Type u₁) [comm_semiring R] [char_p R p] [perfect_ring R p]
  (S : Type u₂) [comm_semiring S] [char_p S p] :
  (R →+* S) ≃ (R →+* ring.perfection S p) :=
{ to_fun := λ f,
  { to_fun := λ r, ⟨λ n, f $ _root_.pth_root R p ^[n] r,
      λ n, by rw [← f.map_pow, function.iterate_succ_apply', pth_root_pow_p]⟩,
    map_one' := ext $ λ n, (congr_arg f $ ring_hom.iterate_map_one _ _).trans f.map_one,
    map_mul' := λ x y, ext $ λ n, (congr_arg f $ ring_hom.iterate_map_mul _ _ _ _).trans $
      f.map_mul _ _,
    map_zero' := ext $ λ n, (congr_arg f $ ring_hom.iterate_map_zero _ _).trans f.map_zero,
    map_add' := λ x y, ext $ λ n, (congr_arg f $ ring_hom.iterate_map_add _ _ _ _).trans $
      f.map_add _ _ },
  inv_fun := ring_hom.comp $ coeff S p 0,
  left_inv := λ f, ring_hom.ext $ λ r, rfl,
  right_inv := λ f, ring_hom.ext $ λ r, ext $ λ n,
    show coeff S p 0 (f (_root_.pth_root R p ^[n] r)) = coeff S p n (f r),
    by rw [← coeff_iterate_frobenius _ 0 n, zero_add, ← ring_hom.map_iterate_frobenius,
      right_inverse_pth_root_frobenius.iterate] }

lemma hom_ext {R : Type u₁} [comm_semiring R] [char_p R p] [perfect_ring R p]
  {S : Type u₂} [comm_semiring S] [char_p S p] {f g : R →+* ring.perfection S p}
  (hfg : ∀ x, coeff S p 0 (f x) = coeff S p 0 (g x)) : f = g :=
(lift p R S).symm.injective $ ring_hom.ext hfg

variables {R} {S : Type u₂} [comm_semiring S] [char_p S p]

/-- A ring homomorphism `R →+* S` induces `perfection R p →+* perfection S p` -/
@[simps] def map (φ : R →+* S) : ring.perfection R p →+* ring.perfection S p :=
{ to_fun := λ f, ⟨λ n, φ (coeff R p n f), λ n, by rw [← φ.map_pow, coeff_pow_p']⟩,
  map_one' := subtype.eq $ funext $ λ n, φ.map_one,
  map_mul' := λ f g, subtype.eq $ funext $ λ n, φ.map_mul _ _,
  map_zero' := subtype.eq $ funext $ λ n, φ.map_zero,
  map_add' := λ f g, subtype.eq $ funext $ λ n, φ.map_add _ _ }

lemma coeff_map (φ : R →+* S) (f : ring.perfection R p) (n : ℕ) :
  coeff S p n (map p φ f) = φ (coeff R p n f) :=
rfl

end perfection

/-- A perfection map to a ring of characteristic `p` is a map that is isomorphic
to its perfection. -/
@[nolint has_inhabited_instance] structure perfection_map (p : ℕ) [fact p.prime]
  {R : Type u₁} [comm_semiring R] [char_p R p]
  {P : Type u₂} [comm_semiring P] [char_p P p] [perfect_ring P p] (π : P →+* R) : Prop :=
(injective : ∀ ⦃x y : P⦄, (∀ n, π (pth_root P p ^[n] x) = π (pth_root P p ^[n] y)) → x = y)
(surjective : ∀ f : ℕ → R, (∀ n, f (n + 1) ^ p = f n) →
  ∃ x : P, ∀ n, π (pth_root P p ^[n] x) = f n)

namespace perfection_map

variables {p : ℕ} [fact p.prime]
variables {R : Type u₁} [comm_semiring R] [char_p R p]
variables {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p]

/-- Create a `perfection_map` from an isomorphism to the perfection. -/
@[simps] lemma mk' {f : P →+* R} (g : P ≃+* ring.perfection R p)
  (hfg : perfection.lift p P R f = g) :
  perfection_map p f :=
{ injective := λ x y hxy, g.injective $ (ring_hom.ext_iff.1 hfg x).symm.trans $
    eq.symm $ (ring_hom.ext_iff.1 hfg y).symm.trans $ perfection.ext $ λ n, (hxy n).symm,
  surjective := λ y hy, let ⟨x, hx⟩ := g.surjective ⟨y, hy⟩ in
    ⟨x, λ n, show perfection.coeff R p n (perfection.lift p P R f x) =
        perfection.coeff R p n ⟨y, hy⟩,
      by rw [hfg, ← coe_fn_coe_base, hx]⟩ }

variables (p R P)

/-- The canonical perfection map from the perfection of a ring. -/
lemma of : perfection_map p (perfection.coeff R p 0) :=
mk' (ring_equiv.refl _) $ (equiv.apply_eq_iff_eq_symm_apply _).2 rfl

/-- For a perfect ring, it itself is the perfection. -/
lemma id [perfect_ring R p] : perfection_map p (ring_hom.id R) :=
{ injective := λ x y hxy, hxy 0,
  surjective := λ f hf, ⟨f 0, λ n, show pth_root R p ^[n] (f 0) = f n,
    from nat.rec_on n rfl $ λ n ih, injective_pow_p p $
      by rw [function.iterate_succ_apply', pth_root_pow_p _, ih, hf]⟩ }

variables {p R P}
/-- A perfection map induces an isomorphism to the prefection. -/
noncomputable def equiv {π : P →+* R} (m : perfection_map p π) : P ≃+* ring.perfection R p :=
ring_equiv.of_bijective (perfection.lift p P R π)
⟨λ x y hxy, m.injective $ λ n, (congr_arg (perfection.coeff R p n) hxy : _),
λ f, let ⟨x, hx⟩ := m.surjective f.1 f.2 in ⟨x, perfection.ext $ hx⟩⟩

lemma equiv_apply {π : P →+* R} (m : perfection_map p π) (x : P) :
  m.equiv x = perfection.lift p P R π x :=
rfl

lemma comp_equiv {π : P →+* R} (m : perfection_map p π) (x : P) :
  perfection.coeff R p 0 (m.equiv x) = π x :=
rfl

lemma comp_equiv' {π : P →+* R} (m : perfection_map p π) :
  (perfection.coeff R p 0).comp ↑m.equiv = π :=
ring_hom.ext $ λ x, rfl

lemma comp_symm_equiv {π : P →+* R} (m : perfection_map p π) (f : ring.perfection R p) :
  π (m.equiv.symm f) = perfection.coeff R p 0 f :=
(m.comp_equiv _).symm.trans $ congr_arg _ $ m.equiv.apply_symm_apply f

lemma comp_symm_equiv' {π : P →+* R} (m : perfection_map p π) :
  π.comp ↑m.equiv.symm = perfection.coeff R p 0 :=
ring_hom.ext m.comp_symm_equiv

variables (p R P)
/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* P`,
where `P` is any perfection of `S`. -/
@[simps] noncomputable def lift [perfect_ring R p] (S : Type u₂) [comm_semiring S] [char_p S p]
  (P : Type u₃) [comm_semiring P] [char_p P p] [perfect_ring P p]
  (π : P →+* S) (m : perfection_map p π) :
  (R →+* S) ≃ (R →+* P) :=
{ to_fun := λ f, ring_hom.comp ↑m.equiv.symm $ perfection.lift p R S f,
  inv_fun := λ f, π.comp f,
  left_inv := λ f, by { simp_rw [← ring_hom.comp_assoc, comp_symm_equiv'],
    exact (perfection.lift p R S).symm_apply_apply f },
  right_inv := λ f, ring_hom.ext $ λ x, m.equiv.injective $ (m.equiv.apply_symm_apply _).trans $
    show perfection.lift p R S (π.comp f) x = ring_hom.comp ↑m.equiv f x,
    from ring_hom.ext_iff.1 ((perfection.lift p R S).apply_eq_iff_eq_symm_apply.2 rfl) _ }

variables {R p}

lemma hom_ext [perfect_ring R p] {S : Type u₂} [comm_semiring S] [char_p S p]
  {P : Type u₃} [comm_semiring P] [char_p P p] [perfect_ring P p]
  (π : P →+* S) (m : perfection_map p π) {f g : R →+* P}
  (hfg : ∀ x, π (f x) = π (g x)) : f = g :=
(lift p R S P π m).symm.injective $ ring_hom.ext hfg

variables {R P} (p) {S : Type u₂} [comm_semiring S] [char_p S p]
variables {Q : Type u₄} [comm_semiring Q] [char_p Q p] [perfect_ring Q p]

/-- A ring homomorphism `R →+* S` induces `P →+* Q`, a map of the respective perfections. -/
@[nolint unused_arguments]
noncomputable def map {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ)
  (φ : R →+* S) : P →+* Q :=
lift p P S Q σ n $ φ.comp π

lemma comp_map {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ)
  (φ : R →+* S) : σ.comp (map p m n φ) = φ.comp π :=
(lift p P S Q σ n).symm_apply_apply _

lemma map_map {π : P →+* R} (m : perfection_map p π) {σ : Q →+* S} (n : perfection_map p σ)
  (φ : R →+* S) (x : P) : σ (map p m n φ x) = φ (π x) :=
ring_hom.ext_iff.1 (comp_map p m n φ) x

-- Why is this slow?
lemma map_eq_map (φ : R →+* S) :
  @map p _ R _ _ _ _ _ _ S _ _ _ _ _ _ _ (of p R) _ (of p S) φ = perfection.map p φ :=
hom_ext _ (of p S) $ λ f, by rw [map_map, perfection.coeff_map]

end perfection_map

section perfectoid

variables (K : Type u₁) [field K] (v : valuation K ℝ≥0)
variables (O : Type u₂) [comm_ring O] [algebra O K] (hv : v.integers O)
variables (p : ℕ)
include hv

/-- `O/(p)` for `O`, ring of integers of `K`. -/
@[nolint unused_arguments has_inhabited_instance] def mod_p :=
(ideal.span {p} : ideal O).quotient

variables [hp : fact p.prime] [hvp : fact (v p ≠ 1)]

namespace mod_p

instance : comm_ring (mod_p K v O hv p) :=
ideal.quotient.comm_ring _

include hp hvp
instance : char_p (mod_p K v O hv p) p :=
char_p.quotient O p $ mt hv.one_of_is_unit $ ((algebra_map O K).map_nat_cast p).symm ▸ hvp.1

instance : nontrivial (mod_p K v O hv p) :=
char_p.nontrivial_of_char_ne_one hp.1.ne_one

section classical
local attribute [instance] classical.dec

omit hp hvp
/-- For a field `K` with valuation `v : K → ℝ≥0` and ring of integers `O`,
a function `O/(p) → ℝ≥0` that sends `0` to `0` and `x + (p)` to `v(x)` as long as `x ∉ (p)`. -/
noncomputable def pre_val (x : mod_p K v O hv p) : ℝ≥0 :=
if x = 0 then 0 else v (algebra_map O K x.out')

variables {K v O hv p}
lemma pre_val_mk {x : O} (hx : (ideal.quotient.mk _ x : mod_p K v O hv p) ≠ 0) :
  pre_val K v O hv p (ideal.quotient.mk _ x) = v (algebra_map O K x) :=
begin
  obtain ⟨r, hr⟩ := ideal.mem_span_singleton'.1 (ideal.quotient.eq.1 $ quotient.sound' $
    @quotient.mk_out' O (ideal.span {p} : ideal O).quotient_rel x),
  refine (if_neg hx).trans (v.map_eq_of_sub_lt $ lt_of_not_ge' _),
  erw [← ring_hom.map_sub, ← hr, hv.le_iff_dvd],
  exact λ hprx, hx (ideal.quotient.eq_zero_iff_mem.2 $ ideal.mem_span_singleton.2 $
    dvd_of_mul_left_dvd hprx),
end

lemma pre_val_zero : pre_val K v O hv p 0 = 0 :=
if_pos rfl

lemma pre_val_mul {x y : mod_p K v O hv p} (hxy0 : x * y ≠ 0) :
  pre_val K v O hv p (x * y) = pre_val K v O hv p x * pre_val K v O hv p y :=
begin
  have hx0 : x ≠ 0 := mt (by { rintro rfl, rw zero_mul }) hxy0,
  have hy0 : y ≠ 0 := mt (by { rintro rfl, rw mul_zero }) hxy0,
  obtain ⟨r, rfl⟩ := ideal.quotient.mk_surjective x,
  obtain ⟨s, rfl⟩ := ideal.quotient.mk_surjective y,
  rw ← ring_hom.map_mul at hxy0 ⊢,
  rw [pre_val_mk hx0, pre_val_mk hy0, pre_val_mk hxy0, ring_hom.map_mul, v.map_mul]
end

lemma pre_val_add (x y : mod_p K v O hv p) :
  pre_val K v O hv p (x + y) ≤ max (pre_val K v O hv p x) (pre_val K v O hv p y) :=
begin
  by_cases hx0 : x = 0, { rw [hx0, zero_add], exact le_max_right _ _ },
  by_cases hy0 : y = 0, { rw [hy0, add_zero], exact le_max_left _ _ },
  by_cases hxy0 : x + y = 0, { rw [hxy0, pre_val_zero], exact zero_le _ },
  obtain ⟨r, rfl⟩ := ideal.quotient.mk_surjective x,
  obtain ⟨s, rfl⟩ := ideal.quotient.mk_surjective y,
  rw ← ring_hom.map_add at hxy0 ⊢,
  rw [pre_val_mk hx0, pre_val_mk hy0, pre_val_mk hxy0, ring_hom.map_add], exact v.map_add _ _
end

lemma v_p_lt_pre_val {x : mod_p K v O hv p} : v p < pre_val K v O hv p x ↔ x ≠ 0 :=
begin
  refine ⟨λ h hx, by { rw [hx, pre_val_zero] at h, exact not_lt_zero' h },
    λ h, lt_of_not_ge' $ λ hp, h _⟩,
  obtain ⟨r, rfl⟩ := ideal.quotient.mk_surjective x,
  rw [pre_val_mk h, ← (algebra_map O K).map_nat_cast p, hv.le_iff_dvd] at hp,
  rw [ideal.quotient.eq_zero_iff_mem, ideal.mem_span_singleton], exact hp
end

lemma pre_val_eq_zero {x : mod_p K v O hv p} : pre_val K v O hv p x = 0 ↔ x = 0 :=
⟨λ hvx, classical.by_contradiction $ λ hx0 : x ≠ 0,
  by { rw [← v_p_lt_pre_val, hvx] at hx0, exact not_lt_zero' hx0 },
λ hx, hx.symm ▸ pre_val_zero⟩

variables (hv hvp)

open nnreal

variables {hv} [hvp]
include hp
lemma mul_ne_zero_of_pow_p_ne_zero {x y : mod_p K v O hv p} (hx : x ^ p ≠ 0) (hy : y ^ p ≠ 0) :
  x * y ≠ 0 :=
begin
  obtain ⟨r, rfl⟩ := ideal.quotient.mk_surjective x,
  obtain ⟨s, rfl⟩ := ideal.quotient.mk_surjective y,
  have h1p : (0 : ℝ) < 1 / p := one_div_pos.2 (nat.cast_pos.2 hp.1.pos),
  rw ← ring_hom.map_mul, rw ← ring_hom.map_pow at hx hy,
  rw ← v_p_lt_val hv at hx hy ⊢,
  rw [ring_hom.map_pow, v.map_pow, ← rpow_lt_rpow_iff h1p, ← rpow_nat_cast, ← rpow_mul,
      mul_one_div_cancel (nat.cast_ne_zero.2 hp.1.ne_zero : (p : ℝ) ≠ 0), rpow_one] at hx hy,
  rw [ring_hom.map_mul, v.map_mul], refine lt_of_le_of_lt _ (mul_lt_mul'''' hx hy),
  by_cases hvp : v p = 0, { rw hvp, exact zero_le _ }, replace hvp := zero_lt_iff.2 hvp,
  conv_lhs { rw ← rpow_one (v p) }, rw ← rpow_add (ne_of_gt hvp),
  refine rpow_le_rpow_of_exponent_ge hvp ((algebra_map O K).map_nat_cast p ▸ hv.2 _) _,
  rw [← add_div, div_le_one (nat.cast_pos.2 hp.1.pos : 0 < (p : ℝ))], exact_mod_cast hp.1.two_le
end

end classical

end mod_p

include hp hvp
/-- Perfection of `O/(p)` where `O` is the ring of integers of `K`. -/
@[nolint has_inhabited_instance] def pre_tilt :=
ring.perfection (mod_p K v O hv p) p

namespace pre_tilt

instance : comm_ring (pre_tilt K v O hv p) :=
perfection.comm_ring p _

section classical
local attribute [instance] classical.dec

open perfection

/-- The valuation `Perfection(O/(p)) → ℝ≥0` as a function.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
noncomputable def val_aux (f : pre_tilt K v O hv p) : ℝ≥0 :=
if h : ∃ n, coeff _ _ n f ≠ 0
then mod_p.pre_val K v O hv p (coeff _ _ (nat.find h) f) ^ (p ^ nat.find h)
else 0

variables {K v O hv p}
lemma coeff_nat_find_add_ne_zero {f : pre_tilt K v O hv p} {h : ∃ n, coeff _ _ n f ≠ 0} (k : ℕ) :
  coeff _ _ (nat.find h + k) f ≠ 0 :=
coeff_add_ne_zero (nat.find_spec h) k

lemma val_aux_eq {f : pre_tilt K v O hv p} {n : ℕ} (hfn : coeff _ _ n f ≠ 0) :
  val_aux K v O hv p f = mod_p.pre_val K v O hv p (coeff _ _ n f) ^ (p ^ n) :=
begin
  have h : ∃ n, coeff _ _ n f ≠ 0 := ⟨n, hfn⟩,
  rw [val_aux, dif_pos h],
  obtain ⟨k, rfl⟩ := nat.exists_eq_add_of_le (nat.find_min' h hfn),
  induction k with k ih, { refl },
  obtain ⟨x, hx⟩ := ideal.quotient.mk_surjective (coeff _ _ (nat.find h + k + 1) f),
  have h1 : (ideal.quotient.mk _ x : mod_p K v O hv p) ≠ 0 := hx.symm ▸ hfn,
  have h2 : (ideal.quotient.mk _ (x ^ p) : mod_p K v O hv p) ≠ 0,
    by { erw [ring_hom.map_pow, hx, ← ring_hom.map_pow, coeff_pow_p],
      exact coeff_nat_find_add_ne_zero k },
  erw [ih (coeff_nat_find_add_ne_zero k), ← hx, ← coeff_pow_p, ring_hom.map_pow, ← hx,
      ← ring_hom.map_pow, mod_p.pre_val_mk h1, mod_p.pre_val_mk h2,
      ring_hom.map_pow, v.map_pow, ← pow_mul, pow_succ], refl
end

lemma val_aux_zero : val_aux K v O hv p 0 = 0 :=
dif_neg $ λ ⟨n, hn⟩, hn rfl

lemma val_aux_one : val_aux K v O hv p 1 = 1 :=
(val_aux_eq $ show coeff (mod_p K v O hv p) p 0 1 ≠ 0, from one_ne_zero).trans $
by { rw [pow_zero, pow_one, ring_hom.map_one, ← (ideal.quotient.mk _).map_one, mod_p.pre_val_mk,
    ring_hom.map_one, v.map_one], exact @one_ne_zero (mod_p K v O hv p) _ _ }

lemma val_aux_mul (f g : pre_tilt K v O hv p) :
  val_aux K v O hv p (f * g) = val_aux K v O hv p f * val_aux K v O hv p g :=
begin
  by_cases hf : f = 0, { rw [hf, zero_mul, val_aux_zero, zero_mul] },
  by_cases hg : g = 0, { rw [hg, mul_zero, val_aux_zero, mul_zero] },
  replace hf : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 (λ h, hf $ perfection.ext h),
  replace hg : ∃ n, coeff _ _ n g ≠ 0 := not_forall.1 (λ h, hg $ perfection.ext h),
  obtain ⟨m, hm⟩ := hf, obtain ⟨n, hn⟩ := hg,
  replace hm := coeff_ne_zero_of_le hm (le_max_left m n),
  replace hn := coeff_ne_zero_of_le hn (le_max_right m n),
  have hfg : coeff _ _ (max m n + 1) (f * g) ≠ 0,
  { rw ring_hom.map_mul, refine mod_p.mul_ne_zero_of_pow_p_ne_zero _ _;
    rw [← ring_hom.map_pow, coeff_pow_p]; assumption },
  rw [val_aux_eq (coeff_add_ne_zero hm 1), val_aux_eq (coeff_add_ne_zero hn 1), val_aux_eq hfg],
  rw ring_hom.map_mul at hfg ⊢, rw [mod_p.pre_val_mul hfg, mul_pow]
end

lemma val_aux_add (f g : pre_tilt K v O hv p) :
  val_aux K v O hv p (f + g) ≤ max (val_aux K v O hv p f) (val_aux K v O hv p g) :=
begin
  by_cases hf : f = 0, { rw [hf, zero_add, val_aux_zero, max_eq_right], exact zero_le _ },
  by_cases hg : g = 0, { rw [hg, add_zero, val_aux_zero, max_eq_left], exact zero_le _ },
  by_cases hfg : f + g = 0, { rw [hfg, val_aux_zero], exact zero_le _ },
  replace hf : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 (λ h, hf $ perfection.ext h),
  replace hg : ∃ n, coeff _ _ n g ≠ 0 := not_forall.1 (λ h, hg $ perfection.ext h),
  replace hfg : ∃ n, coeff _ _ n (f + g) ≠ 0 := not_forall.1 (λ h, hfg $ perfection.ext h),
  obtain ⟨m, hm⟩ := hf, obtain ⟨n, hn⟩ := hg, obtain ⟨k, hk⟩ := hfg,
  replace hm := coeff_ne_zero_of_le hm (le_trans (le_max_left m n) (le_max_left _ k)),
  replace hn := coeff_ne_zero_of_le hn (le_trans (le_max_right m n) (le_max_left _ k)),
  replace hk := coeff_ne_zero_of_le hk (le_max_right (max m n) k),
  rw [val_aux_eq hm, val_aux_eq hn, val_aux_eq hk, ring_hom.map_add],
  cases le_max_iff.1
    (mod_p.pre_val_add (coeff _ _ (max (max m n) k) f) (coeff _ _ (max (max m n) k) g)) with h h,
  { exact le_max_left_of_le (canonically_ordered_semiring.pow_le_pow_of_le_left h _) },
  { exact le_max_right_of_le (canonically_ordered_semiring.pow_le_pow_of_le_left h _) }
end

variables (K v O hv p)
/-- The valuation `Perfection(O/(p)) → ℝ≥0`.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
noncomputable def val : valuation (pre_tilt K v O hv p) ℝ≥0 :=
{ to_fun := val_aux K v O hv p,
  map_one' := val_aux_one,
  map_mul' := val_aux_mul,
  map_zero' := val_aux_zero,
  map_add' := val_aux_add }

variables {K v O hv p}
lemma map_eq_zero {f : pre_tilt K v O hv p} : val K v O hv p f = 0 ↔ f = 0 :=
begin
  by_cases hf0 : f = 0, { rw hf0, exact iff_of_true (valuation.map_zero _) rfl },
  obtain ⟨n, hn⟩ : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 (λ h, hf0 $ perfection.ext h),
  show val_aux K v O hv p f = 0 ↔ f = 0, refine iff_of_false (λ hvf, hn _) hf0,
  rw val_aux_eq hn at hvf, replace hvf := pow_eq_zero hvf, rwa mod_p.pre_val_eq_zero at hvf
end

end classical

instance : integral_domain (pre_tilt K v O hv p) :=
{ exists_pair_ne := (char_p.nontrivial_of_char_ne_one hp.1.ne_one).1,
  eq_zero_or_eq_zero_of_mul_eq_zero := λ f g hfg,
    by { simp_rw ← map_eq_zero at hfg ⊢, contrapose! hfg, rw valuation.map_mul,
      exact mul_ne_zero hfg.1 hfg.2 },
  .. (infer_instance : comm_ring (pre_tilt K v O hv p)) }

end pre_tilt

/-- The tilt of a field, as defined in Perfectoid Spaces by Peter Scholze, as in
[scholze2011perfectoid]. Given a field `K` with valuation `K → ℝ≥0` and ring of integers `O`,
this is implemented as the fraction field of the perfection of `O/(p)`. -/
@[nolint has_inhabited_instance] def tilt :=
fraction_ring (pre_tilt K v O hv p)

namespace tilt

noncomputable instance : field (tilt K v O hv p) :=
fraction_ring.field

end tilt

end perfectoid


/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Jeremy Avigad
-/
prelude
import init.logic
open decidable

universes u

namespace subtype

lemma exists_of_subtype {α : Type u} {p : α → Prop} : { x // p x } → ∃ x, p x
| ⟨a, h⟩ := ⟨a, h⟩

variables {α : Type u} {p : α → Prop}

lemma tag_irrelevant {a : α} (h1 h2 : p a) : mk a h1 = mk a h2 :=
rfl

protected lemma eq : ∀ {a1 a2 : {x // p x}}, val a1 = val a2 → a1 = a2
| ⟨x, h1⟩ ⟨.(x), h2⟩ rfl := rfl

lemma ne_of_val_ne {a1 a2 : {x // p x}} : val a1 ≠ val a2 → a1 ≠ a2 :=
mt $ congr_arg _

end subtype

open subtype

def subtype.inhabited {α : Type u} {p : α → Prop} {a : α} (h : p a) : inhabited {x // p x} :=
⟨⟨a, h⟩⟩


/-
Copyright (c) 2020 Jannis Limperg. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jannis Limperg

! This file was ported from Lean 3 source module data.list.indexes
! leanprover-community/mathlib commit 34ee86e6a59d911a8e4f89b68793ee7577ae79c7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.OfFn
import Mathbin.Data.List.Range

/-!
# Lemmas about list.*_with_index functions.

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

Some specification lemmas for `list.map_with_index`, `list.mmap_with_index`, `list.foldl_with_index`
and `list.foldr_with_index`.
-/

universe u v

open Function

namespace List

variable {α : Type u} {β : Type v}

section MapWithIndex

/- warning: list.map_with_index_nil -> List.mapIdx_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β), Eq.{succ u2} (List.{u2} β) (List.mapIdx.{u1, u2} α β f (List.nil.{u1} α)) (List.nil.{u2} β)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Nat -> α -> β), Eq.{succ u1} (List.{u1} β) (List.mapIdx.{u2, u1} α β f (List.nil.{u2} α)) (List.nil.{u1} β)
Case conversion may be inaccurate. Consider using '#align list.map_with_index_nil List.mapIdx_nilₓ'. -/
@[simp]
theorem mapIdx_nil {α β} (f : ℕ → α → β) : mapIdx f [] = [] :=
  rfl
#align list.map_with_index_nil List.mapIdx_nil

/- warning: list.map_with_index_core_eq clashes with [anonymous] -> [anonymous]
warning: list.map_with_index_core_eq -> [anonymous] is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : List.{u1} α) (f : Nat -> α -> β) (n : Nat), Eq.{succ u2} (List.{u2} β) ([anonymous].{u1, u2} α β f n l) (List.mapIdx.{u1, u2} α β (fun (i : Nat) (a : α) => f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) i n) a) l)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}}, (Nat -> α -> β) -> Nat -> (List.{u1} α) -> (List.{u2} β)
Case conversion may be inaccurate. Consider using '#align list.map_with_index_core_eq [anonymous]ₓ'. -/
theorem [anonymous] (l : List α) (f : ℕ → α → β) (n : ℕ) :
    l.map_with_index_core f n = l.mapIdx fun i a => f (i + n) a :=
  by
  induction' l with hd tl hl generalizing f n
  · simpa
  · rw [map_with_index]
    simp [map_with_index_core, hl, add_left_comm, add_assoc, add_comm]
#align list.map_with_index_core_eq [anonymous]

#print List.mapIdx_eq_enum_map /-
theorem mapIdx_eq_enum_map (l : List α) (f : ℕ → α → β) :
    l.mapIdx f = l.enum.map (Function.uncurry f) :=
  by
  induction' l with hd tl hl generalizing f
  · simp [List.enum_eq_zip_range]
  · rw [map_with_index, map_with_index_core, map_with_index_core_eq, hl]
    simp [enum_eq_zip_range, range_succ_eq_map, zip_with_map_left, map_uncurry_zip_eq_zip_with]
#align list.map_with_index_eq_enum_map List.mapIdx_eq_enum_map
-/

/- warning: list.map_with_index_cons -> List.mapIdx_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : List.{u1} α) (f : Nat -> α -> β) (a : α), Eq.{succ u2} (List.{u2} β) (List.mapIdx.{u1, u2} α β f (List.cons.{u1} α a l)) (List.cons.{u2} β (f (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) a) (List.mapIdx.{u1, u2} α β (fun (i : Nat) => f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) i (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) l))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (l : List.{u2} α) (f : Nat -> α -> β) (a : α), Eq.{succ u1} (List.{u1} β) (List.mapIdx.{u2, u1} α β f (List.cons.{u2} α a l)) (List.cons.{u1} β (f (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) (List.mapIdx.{u2, u1} α β (fun (i : Nat) => f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) i (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) l))
Case conversion may be inaccurate. Consider using '#align list.map_with_index_cons List.mapIdx_consₓ'. -/
@[simp]
theorem mapIdx_cons {α β} (l : List α) (f : ℕ → α → β) (a : α) :
    mapIdx f (a :: l) = f 0 a :: mapIdx (fun i => f (i + 1)) l := by
  simp [map_with_index_eq_enum_map, enum_eq_zip_range, map_uncurry_zip_eq_zip_with,
    range_succ_eq_map, zip_with_map_left]
#align list.map_with_index_cons List.mapIdx_cons

/- warning: list.map_with_index_append -> List.mapIdx_append is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} {α : Type.{u2}} (K : List.{u2} α) (L : List.{u2} α) (f : Nat -> α -> β), Eq.{succ u1} (List.{u1} β) (List.mapIdx.{u2, u1} α β f (Append.append.{u2} (List.{u2} α) (List.hasAppend.{u2} α) K L)) (Append.append.{u1} (List.{u1} β) (List.hasAppend.{u1} β) (List.mapIdx.{u2, u1} α β f K) (List.mapIdx.{u2, u1} α β (fun (i : Nat) (a : α) => f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) i (List.length.{u2} α K)) a) L))
but is expected to have type
  forall {β : Type.{u2}} {α : Type.{u1}} (K : List.{u1} α) (L : List.{u1} α) (f : Nat -> α -> β), Eq.{succ u2} (List.{u2} β) (List.mapIdx.{u1, u2} α β f (HAppend.hAppend.{u1, u1, u1} (List.{u1} α) (List.{u1} α) (List.{u1} α) (instHAppend.{u1} (List.{u1} α) (List.instAppendList.{u1} α)) K L)) (HAppend.hAppend.{u2, u2, u2} (List.{u2} β) (List.{u2} β) (List.{u2} β) (instHAppend.{u2} (List.{u2} β) (List.instAppendList.{u2} β)) (List.mapIdx.{u1, u2} α β f K) (List.mapIdx.{u1, u2} α β (fun (i : Nat) (a : α) => f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) i (List.length.{u1} α K)) a) L))
Case conversion may be inaccurate. Consider using '#align list.map_with_index_append List.mapIdx_appendₓ'. -/
theorem mapIdx_append {α} (K L : List α) (f : ℕ → α → β) :
    (K ++ L).mapIdx f = K.mapIdx f ++ L.mapIdx fun i a => f (i + K.length) a :=
  by
  induction' K with a J IH generalizing f
  · simp
  · simp [IH fun i => f (i + 1), add_assoc]
#align list.map_with_index_append List.mapIdx_append

/- warning: list.length_map_with_index -> List.length_mapIdx is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : List.{u1} α) (f : Nat -> α -> β), Eq.{1} Nat (List.length.{u2} β (List.mapIdx.{u1, u2} α β f l)) (List.length.{u1} α l)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (l : List.{u2} α) (f : Nat -> α -> β), Eq.{1} Nat (List.length.{u1} β (List.mapIdx.{u2, u1} α β f l)) (List.length.{u2} α l)
Case conversion may be inaccurate. Consider using '#align list.length_map_with_index List.length_mapIdxₓ'. -/
@[simp]
theorem length_mapIdx {α β} (l : List α) (f : ℕ → α → β) : (l.mapIdx f).length = l.length :=
  by
  induction' l with hd tl IH generalizing f
  · simp
  · simp [IH]
#align list.length_map_with_index List.length_mapIdx

/- warning: list.nth_le_map_with_index -> List.nthLe_mapIdx is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : List.{u1} α) (f : Nat -> α -> β) (i : Nat) (h : LT.lt.{0} Nat Nat.hasLt i (List.length.{u1} α l)) (h' : optParam.{0} (LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) i (List.length.{u2} β (List.mapIdx.{u1, u2} α β f l))) (LT.lt.trans_le.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) i (List.length.{u1} α l) (List.length.{u2} β (List.mapIdx.{u1, u2} α β f l)) h (Eq.ge.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (List.length.{u2} β (List.mapIdx.{u1, u2} α β f l)) (List.length.{u1} α l) (List.length_mapIdx.{u1, u2} α β l f)))), Eq.{succ u2} β (List.nthLe.{u2} β (List.mapIdx.{u1, u2} α β f l) i h') (f i (List.nthLe.{u1} α l i h))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (l : List.{u2} α) (f : Nat -> α -> β) (i : Nat) (h : LT.lt.{0} Nat instLTNat i (List.length.{u2} α l)) (h' : optParam.{0} (LT.lt.{0} Nat instLTNat i (List.length.{u1} β (List.mapIdx.{u2, u1} α β f l))) (LT.lt.trans_le.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) i (List.length.{u2} α l) (List.length.{u1} β (List.mapIdx.{u2, u1} α β f l)) h (Eq.ge.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) (List.length.{u1} β (List.mapIdx.{u2, u1} α β f l)) (List.length.{u2} α l) (List.length_mapIdx.{u1, u2} α β l f)))), Eq.{succ u1} β (List.nthLe.{u1} β (List.mapIdx.{u2, u1} α β f l) i h') (f i (List.nthLe.{u2} α l i h))
Case conversion may be inaccurate. Consider using '#align list.nth_le_map_with_index List.nthLe_mapIdxₓ'. -/
@[simp]
theorem nthLe_mapIdx {α β} (l : List α) (f : ℕ → α → β) (i : ℕ) (h : i < l.length)
    (h' : i < (l.mapIdx f).length := h.trans_le (l.length_mapIdx f).ge) :
    (l.mapIdx f).nthLe i h' = f i (l.nthLe i h) := by
  simp [map_with_index_eq_enum_map, enum_eq_zip_range]
#align list.nth_le_map_with_index List.nthLe_mapIdx

/- warning: list.map_with_index_eq_of_fn -> List.mapIdx_eq_ofFn is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : List.{u1} α) (f : Nat -> α -> β), Eq.{succ u2} (List.{u2} β) (List.mapIdx.{u1, u2} α β f l) (List.ofFn.{u2} β (List.length.{u1} α l) (fun (i : Fin (List.length.{u1} α l)) => f ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin (List.length.{u1} α l)) Nat (HasLiftT.mk.{1, 1} (Fin (List.length.{u1} α l)) Nat (CoeTCₓ.coe.{1, 1} (Fin (List.length.{u1} α l)) Nat (coeBase.{1, 1} (Fin (List.length.{u1} α l)) Nat (Fin.coeToNat (List.length.{u1} α l))))) i) (List.nthLe.{u1} α l ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin (List.length.{u1} α l)) Nat (HasLiftT.mk.{1, 1} (Fin (List.length.{u1} α l)) Nat (CoeTCₓ.coe.{1, 1} (Fin (List.length.{u1} α l)) Nat (coeBase.{1, 1} (Fin (List.length.{u1} α l)) Nat (Fin.coeToNat (List.length.{u1} α l))))) i) (Fin.is_lt (List.length.{u1} α l) i))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (l : List.{u2} α) (f : Nat -> α -> β), Eq.{succ u1} (List.{u1} β) (List.mapIdx.{u2, u1} α β f l) (List.ofFn.{u1} β (List.length.{u2} α l) (fun (i : Fin (List.length.{u2} α l)) => f (Fin.val (List.length.{u2} α l) i) (List.get.{u2} α l i)))
Case conversion may be inaccurate. Consider using '#align list.map_with_index_eq_of_fn List.mapIdx_eq_ofFnₓ'. -/
theorem mapIdx_eq_ofFn {α β} (l : List α) (f : ℕ → α → β) :
    l.mapIdx f = ofFn fun i : Fin l.length => f (i : ℕ) (l.nthLe i i.is_lt) :=
  by
  induction' l with hd tl IH generalizing f
  · simp
  · simpa [IH]
#align list.map_with_index_eq_of_fn List.mapIdx_eq_ofFn

end MapWithIndex

section FoldrWithIndex

/-- Specification of `foldr_with_index_aux`. -/
def foldrIdxSpec (f : ℕ → α → β → β) (start : ℕ) (b : β) (as : List α) : β :=
  foldr (uncurry f) b <| enumFrom start as
#align list.foldr_with_index_aux_spec List.foldrIdxSpecₓ

theorem foldrIdxSpec_cons (f : ℕ → α → β → β) (start b a as) :
    foldrIdxSpec f start b (a :: as) = f start a (foldrIdxSpec f (start + 1) b as) :=
  rfl
#align list.foldr_with_index_aux_spec_cons List.foldrIdxSpec_consₓ

theorem foldrIdx_eq_foldrIdxSpec (f : ℕ → α → β → β) (start b as) :
    foldrWithIndexAux f start b as = foldrIdxSpec f start b as :=
  by
  induction as generalizing start
  · rfl
  · simp only [foldr_with_index_aux, foldr_with_index_aux_spec_cons, *]
#align list.foldr_with_index_aux_eq_foldr_with_index_aux_spec List.foldrIdx_eq_foldrIdxSpecₓ

/- warning: list.foldr_with_index_eq_foldr_enum -> List.foldrIdx_eq_foldr_enum is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β -> β) (b : β) (as : List.{u1} α), Eq.{succ u2} β (List.foldrIdx.{u1, succ u2} α β f b as) (List.foldr.{u1, u2} (Prod.{0, u1} Nat α) β (Function.uncurry.{0, u1, u2} Nat α (β -> β) f) b (List.enum.{u1} α as))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β -> β) (b : β) (as : List.{u1} α), Eq.{succ u2} β (List.foldrIdx.{u1, succ u2} α β f b as (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (List.foldr.{u1, u2} (Prod.{0, u1} Nat α) β (Function.uncurry.{0, u1, u2} Nat α (β -> β) f) b (List.enum.{u1} α as))
Case conversion may be inaccurate. Consider using '#align list.foldr_with_index_eq_foldr_enum List.foldrIdx_eq_foldr_enumₓ'. -/
theorem foldrIdx_eq_foldr_enum (f : ℕ → α → β → β) (b : β) (as : List α) :
    foldrIdx f b as = foldr (uncurry f) b (enum as) := by
  simp only [foldr_with_index, foldr_with_index_aux_spec,
    foldr_with_index_aux_eq_foldr_with_index_aux_spec, enum]
#align list.foldr_with_index_eq_foldr_enum List.foldrIdx_eq_foldr_enum

end FoldrWithIndex

#print List.indexesValues_eq_filter_enum /-
theorem indexesValues_eq_filter_enum (p : α → Prop) [DecidablePred p] (as : List α) :
    indexesValues p as = filter (p ∘ Prod.snd) (enum as) := by
  simp [indexes_values, foldr_with_index_eq_foldr_enum, uncurry, filter_eq_foldr]
#align list.indexes_values_eq_filter_enum List.indexesValues_eq_filter_enum
-/

#print List.findIdxs_eq_map_indexesValues /-
theorem findIdxs_eq_map_indexesValues (p : α → Prop) [DecidablePred p] (as : List α) :
    findIdxs p as = map Prod.fst (indexesValues p as) := by
  simp only [indexes_values_eq_filter_enum, map_filter_eq_foldr, find_indexes,
    foldr_with_index_eq_foldr_enum, uncurry]
#align list.find_indexes_eq_map_indexes_values List.findIdxs_eq_map_indexesValues
-/

section FoldlWithIndex

/-- Specification of `foldl_with_index_aux`. -/
def foldlIdxSpec (f : ℕ → α → β → α) (start : ℕ) (a : α) (bs : List β) : α :=
  foldl (fun a (p : ℕ × β) => f p.fst a p.snd) a <| enumFrom start bs
#align list.foldl_with_index_aux_spec List.foldlIdxSpecₓ

theorem foldlIdxSpec_cons (f : ℕ → α → β → α) (start a b bs) :
    foldlIdxSpec f start a (b :: bs) = foldlIdxSpec f (start + 1) (f start a b) bs :=
  rfl
#align list.foldl_with_index_aux_spec_cons List.foldlIdxSpec_consₓ

theorem foldlIdx_eq_foldlIdxSpec (f : ℕ → α → β → α) (start a bs) :
    foldlWithIndexAux f start a bs = foldlIdxSpec f start a bs :=
  by
  induction bs generalizing start a
  · rfl
  · simp [foldl_with_index_aux, foldl_with_index_aux_spec_cons, *]
#align list.foldl_with_index_aux_eq_foldl_with_index_aux_spec List.foldlIdx_eq_foldlIdxSpecₓ

/- warning: list.foldl_with_index_eq_foldl_enum -> List.foldlIdx_eq_foldl_enum is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β -> α) (a : α) (bs : List.{u2} β), Eq.{succ u1} α (List.foldlIdx.{succ u1, u2} α β f a bs) (List.foldl.{u1, u2} α (Prod.{0, u2} Nat β) (fun (a : α) (p : Prod.{0, u2} Nat β) => f (Prod.fst.{0, u2} Nat β p) a (Prod.snd.{0, u2} Nat β p)) a (List.enum.{u2} β bs))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β -> α) (a : α) (bs : List.{u2} β), Eq.{succ u1} α (List.foldlIdx.{succ u1, u2} α β f a bs (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (List.foldl.{u1, u2} α (Prod.{0, u2} Nat β) (fun (a : α) (p : Prod.{0, u2} Nat β) => f (Prod.fst.{0, u2} Nat β p) a (Prod.snd.{0, u2} Nat β p)) a (List.enum.{u2} β bs))
Case conversion may be inaccurate. Consider using '#align list.foldl_with_index_eq_foldl_enum List.foldlIdx_eq_foldl_enumₓ'. -/
theorem foldlIdx_eq_foldl_enum (f : ℕ → α → β → α) (a : α) (bs : List β) :
    foldlIdx f a bs = foldl (fun a (p : ℕ × β) => f p.fst a p.snd) a (enum bs) := by
  simp only [foldl_with_index, foldl_with_index_aux_spec,
    foldl_with_index_aux_eq_foldl_with_index_aux_spec, enum]
#align list.foldl_with_index_eq_foldl_enum List.foldlIdx_eq_foldl_enum

end FoldlWithIndex

section MfoldWithIndex

variable {m : Type u → Type v} [Monad m]

/- warning: list.mfoldr_with_index_eq_mfoldr_enum -> List.foldrIdxM_eq_foldrM_enum is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Monad.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}} (f : Nat -> α -> β -> (m β)) (b : β) (as : List.{u3} α), Eq.{succ u2} (m β) (List.foldrIdxM.{u1, u2, u3} (fun {β : Type.{u1}} => m β) _inst_1 α β f b as) (List.foldrM.{u1, u2, u3} m _inst_1 β (Prod.{0, u3} Nat α) (Function.uncurry.{0, u3, max u1 u2} Nat α (β -> (m β)) f) b (List.enum.{u3} α as))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> β -> (m β)) (b : β) (as : List.{u1} α) [inst._@.Mathlib.Data.List.Indexes._hyg.2623 : LawfulMonad.{u2, u3} m _inst_1], Eq.{succ u3} (m β) (List.foldrIdxM.{u2, u3, u1} m _inst_1 α β f b as) (List.foldrM.{u2, u3, u1} m _inst_1 β (Prod.{0, u1} Nat α) (Function.uncurry.{0, u1, max u3 u2} Nat α (β -> (m β)) f) b (List.enum.{u1} α as))
Case conversion may be inaccurate. Consider using '#align list.mfoldr_with_index_eq_mfoldr_enum List.foldrIdxM_eq_foldrM_enumₓ'. -/
theorem foldrIdxM_eq_foldrM_enum {α β} (f : ℕ → α → β → m β) (b : β) (as : List α) :
    foldrIdxM f b as = foldrM (uncurry f) b (enum as) := by
  simp only [mfoldr_with_index, mfoldr_eq_foldr, foldr_with_index_eq_foldr_enum, uncurry]
#align list.mfoldr_with_index_eq_mfoldr_enum List.foldrIdxM_eq_foldrM_enum

/- warning: list.mfoldl_with_index_eq_mfoldl_enum -> List.foldlIdxM_eq_foldlM_enum is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Monad.{u1, u2} m] [_inst_2 : LawfulMonad.{u1, u2} m _inst_1] {α : Type.{u3}} {β : Type.{u1}} (f : Nat -> β -> α -> (m β)) (b : β) (as : List.{u3} α), Eq.{succ u2} (m β) (List.foldlIdxM.{u1, u2, u3} (fun {β : Type.{u1}} => m β) _inst_1 α β f b as) (List.foldlM.{u1, u2, u3} m _inst_1 β (Prod.{0, u3} Nat α) (fun (b : β) (p : Prod.{0, u3} Nat α) => f (Prod.fst.{0, u3} Nat α p) b (Prod.snd.{0, u3} Nat α p)) b (List.enum.{u3} α as))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] [_inst_2 : LawfulMonad.{u2, u3} m _inst_1] {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> β -> α -> (m β)) (b : β) (as : List.{u1} α), Eq.{succ u3} (m β) (List.foldlIdxM.{u2, u3, u1} m _inst_1 α β f b as) (List.foldlM.{u2, u3, u1} m _inst_1 β (Prod.{0, u1} Nat α) (fun (b : β) (p : Prod.{0, u1} Nat α) => f (Prod.fst.{0, u1} Nat α p) b (Prod.snd.{0, u1} Nat α p)) b (List.enum.{u1} α as))
Case conversion may be inaccurate. Consider using '#align list.mfoldl_with_index_eq_mfoldl_enum List.foldlIdxM_eq_foldlM_enumₓ'. -/
theorem foldlIdxM_eq_foldlM_enum [LawfulMonad m] {α β} (f : ℕ → β → α → m β) (b : β) (as : List α) :
    foldlIdxM f b as = foldlM (fun b (p : ℕ × α) => f p.fst b p.snd) b (enum as) := by
  rw [mfoldl_with_index, mfoldl_eq_foldl, foldl_with_index_eq_foldl_enum]
#align list.mfoldl_with_index_eq_mfoldl_enum List.foldlIdxM_eq_foldlM_enum

end MfoldWithIndex

section MmapWithIndex

variable {m : Type u → Type v} [Applicative m]

/- warning: list.mmap_with_index_aux_spec -> List.mapIdxMAuxSpec is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}}, (Nat -> α -> (m β)) -> Nat -> (List.{u3} α) -> (m (List.{u1} β))
but is expected to have type
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Monad.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}}, (Nat -> α -> (m β)) -> Nat -> (List.{u3} α) -> (m (List.{u1} β))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index_aux_spec List.mapIdxMAuxSpecₓ'. -/
/-- Specification of `mmap_with_index_aux`. -/
def mapIdxMAuxSpec {α β} (f : ℕ → α → m β) (start : ℕ) (as : List α) : m (List β) :=
  List.traverse (uncurry f) <| enumFrom start as
#align list.mmap_with_index_aux_spec List.mapIdxMAuxSpec

/- warning: list.mmap_with_index_aux_spec_cons -> List.mapIdxMAuxSpec_cons is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}} (f : Nat -> α -> (m β)) (start : Nat) (a : α) (as : List.{u3} α), Eq.{succ u2} (m (List.{u1} β)) (List.mapIdxMAuxSpec.{u1, u2, u3} m _inst_1 α β f start (List.cons.{u3} α a as)) (Seq.seq.{u1, u2} m (Applicative.toHasSeq.{u1, u2} m _inst_1) (List.{u1} β) (List.{u1} β) (Functor.map.{u1, u2} m (Applicative.toFunctor.{u1, u2} m _inst_1) β ((List.{u1} β) -> (List.{u1} β)) (List.cons.{u1} β) (f start a)) (List.mapIdxMAuxSpec.{u1, u2, u3} m _inst_1 α β f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) start (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) as))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] {α : Type.{u1}} {β : Type.{u2}} (f : Nat -> α -> (m β)) (start : Nat) (a : α) (as : List.{u1} α), Eq.{succ u3} (m (List.{u2} β)) (List.mapIdxMAuxSpec.{u2, u3, u1} m _inst_1 α β f start (List.cons.{u1} α a as)) (Seq.seq.{u2, u3} m (Applicative.toSeq.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) (List.{u2} β) (List.{u2} β) (Functor.map.{u2, u3} m (Applicative.toFunctor.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) β ((List.{u2} β) -> (List.{u2} β)) (List.cons.{u2} β) (f start a)) (fun (x._@.Mathlib.Data.List.Indexes._hyg.2846 : Unit) => List.mapIdxMAuxSpec.{u2, u3, u1} m _inst_1 α β f (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) start (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) as))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index_aux_spec_cons List.mapIdxMAuxSpec_consₓ'. -/
-- Note: `traverse` the class method would require a less universe-polymorphic
-- `m : Type u → Type u`.
theorem mapIdxMAuxSpec_cons {α β} (f : ℕ → α → m β) (start : ℕ) (a : α) (as : List α) :
    mapIdxMAuxSpec f start (a :: as) =
      List.cons <$> f start a <*> mapIdxMAuxSpec f (start + 1) as :=
  rfl
#align list.mmap_with_index_aux_spec_cons List.mapIdxMAuxSpec_cons

/- warning: list.mmap_with_index_aux_eq_mmap_with_index_aux_spec -> List.mapIdxMGo_eq_mapIdxMAuxSpec is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}} (f : Nat -> α -> (m β)) (start : Nat) (as : List.{u3} α), Eq.{succ u2} (m (List.{u1} β)) (List.mmapWithIndexAux.{u1, u2, u3} m _inst_1 α β f start as) (List.mapIdxMAuxSpec.{u1, u2, u3} m _inst_1 α β f start as)
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] [α : LawfulMonad.{u2, u3} m _inst_1] {β : Type.{u1}} {f : Type.{u2}} (start : Nat -> β -> (m f)) (as : Array.{u2} f) (as_1 : List.{u1} β), Eq.{succ u3} (m (List.{u2} f)) (List.mapIdxM.go.{u2, u3, u1} β f m _inst_1 start as_1 as) (Functor.map.{u2, u3} m (Applicative.toFunctor.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) (List.{u2} f) (List.{u2} f) (fun (x._@.Mathlib.Data.List.Indexes._hyg.2898 : List.{u2} f) => HAppend.hAppend.{u2, u2, u2} (List.{u2} f) (List.{u2} f) (List.{u2} f) (instHAppend.{u2} (List.{u2} f) (List.instAppendList.{u2} f)) (Array.toList.{u2} f as) x._@.Mathlib.Data.List.Indexes._hyg.2898) (List.mapIdxMAuxSpec.{u2, u3, u1} m _inst_1 β f start (Array.size.{u2} f as) as_1))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index_aux_eq_mmap_with_index_aux_spec List.mapIdxMGo_eq_mapIdxMAuxSpecₓ'. -/
theorem mapIdxMGo_eq_mapIdxMAuxSpec {α β} (f : ℕ → α → m β) (start : ℕ) (as : List α) :
    mmapWithIndexAux f start as = mapIdxMAuxSpec f start as :=
  by
  induction as generalizing start
  · rfl
  · simp [mmap_with_index_aux, mmap_with_index_aux_spec_cons, *]
#align list.mmap_with_index_aux_eq_mmap_with_index_aux_spec List.mapIdxMGo_eq_mapIdxMAuxSpec

/- warning: list.mmap_with_index_eq_mmap_enum -> List.mapIdxM_eq_mmap_enum is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] {α : Type.{u3}} {β : Type.{u1}} (f : Nat -> α -> (m β)) (as : List.{u3} α), Eq.{succ u2} (m (List.{u1} β)) (List.mapIdxM.{u1, u2, u3} m _inst_1 α β f as) (List.traverse.{u1, u2, u3} m _inst_1 (Prod.{0, u3} Nat α) β (Function.uncurry.{0, u3, u2} Nat α (m β) f) (List.enum.{u3} α as))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] [α : LawfulMonad.{u2, u3} m _inst_1] {β : Type.{u1}} {f : Type.{u2}} (as : Nat -> β -> (m f)) (as_1 : List.{u1} β), Eq.{succ u3} (m (List.{u2} f)) (List.mapIdxM.{u2, u3, u1} β f m _inst_1 as_1 as) (List.traverse.{u2, u3, u1} m (Monad.toApplicative.{u2, u3} m _inst_1) (Prod.{0, u1} Nat β) f (Function.uncurry.{0, u1, u3} Nat β (m f) as) (List.enum.{u1} β as_1))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index_eq_mmap_enum List.mapIdxM_eq_mmap_enumₓ'. -/
theorem mapIdxM_eq_mmap_enum {α β} (f : ℕ → α → m β) (as : List α) :
    mapIdxM f as = List.traverse (uncurry f) (enum as) := by
  simp only [mmap_with_index, mmap_with_index_aux_spec,
    mmap_with_index_aux_eq_mmap_with_index_aux_spec, enum]
#align list.mmap_with_index_eq_mmap_enum List.mapIdxM_eq_mmap_enum

end MmapWithIndex

section MmapWithIndex'

variable {m : Type u → Type v} [Applicative m] [LawfulApplicative m]

/- warning: list.mmap_with_index'_aux_eq_mmap_with_index_aux -> List.mapIdxMAux'_eq_mapIdxMGo is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] [_inst_2 : LawfulApplicative.{u1, u2} m _inst_1] {α : Type.{u3}} (f : Nat -> α -> (m PUnit.{succ u1})) (start : Nat) (as : List.{u3} α), Eq.{succ u2} (m PUnit.{succ u1}) (List.mapIdxMAux'.{u1, u2, u3} m _inst_1 α f start as) (SeqRight.seqRight.{u1, u2} m (Applicative.toHasSeqRight.{u1, u2} m _inst_1) (List.{u1} PUnit.{succ u1}) PUnit.{succ u1} (List.mmapWithIndexAux.{u1, u2, u3} m _inst_1 α PUnit.{succ u1} f start as) (Pure.pure.{u1, u2} m (Applicative.toHasPure.{u1, u2} m _inst_1) PUnit.{succ u1} PUnit.unit.{succ u1}))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] [_inst_2 : LawfulMonad.{u2, u3} m _inst_1] {α : Type.{u1}} (f : Nat -> α -> (m PUnit.{succ u2})) (start : List.{u1} α) (as : Array.{u2} PUnit.{succ u2}), Eq.{succ u3} (m PUnit.{succ u2}) (List.mapIdxMAux'.{u2, u3, u1} m _inst_1 α f (Array.size.{u2} PUnit.{succ u2} as) start) (SeqRight.seqRight.{u2, u3} m (Applicative.toSeqRight.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) (List.{u2} PUnit.{succ u2}) PUnit.{succ u2} (List.mapIdxM.go.{u2, u3, u1} α PUnit.{succ u2} m _inst_1 f start as) (fun (x._@.Mathlib.Data.List.Indexes._hyg.3204 : Unit) => Pure.pure.{u2, u3} m (Applicative.toPure.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) PUnit.{succ u2} PUnit.unit.{succ u2}))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index'_aux_eq_mmap_with_index_aux List.mapIdxMAux'_eq_mapIdxMGoₓ'. -/
theorem mapIdxMAux'_eq_mapIdxMGo {α} (f : ℕ → α → m PUnit) (start : ℕ) (as : List α) :
    mapIdxMAux' f start as = mmapWithIndexAux f start as *> pure PUnit.unit := by
  induction as generalizing start <;>
    simp [mmap_with_index'_aux, mmap_with_index_aux, *, seq_right_eq, const, -comp_const,
      functor_norm]
#align list.mmap_with_index'_aux_eq_mmap_with_index_aux List.mapIdxMAux'_eq_mapIdxMGo

/- warning: list.mmap_with_index'_eq_mmap_with_index -> List.mapIdxM'_eq_mapIdxM is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u1} -> Type.{u2}} [_inst_1 : Applicative.{u1, u2} m] [_inst_2 : LawfulApplicative.{u1, u2} m _inst_1] {α : Type.{u3}} (f : Nat -> α -> (m PUnit.{succ u1})) (as : List.{u3} α), Eq.{succ u2} (m PUnit.{succ u1}) (List.mapIdxM'.{u1, u2, u3} m _inst_1 α f as) (SeqRight.seqRight.{u1, u2} m (Applicative.toHasSeqRight.{u1, u2} m _inst_1) (List.{u1} PUnit.{succ u1}) PUnit.{succ u1} (List.mapIdxM.{u1, u2, u3} m _inst_1 α PUnit.{succ u1} f as) (Pure.pure.{u1, u2} m (Applicative.toHasPure.{u1, u2} m _inst_1) PUnit.{succ u1} PUnit.unit.{succ u1}))
but is expected to have type
  forall {m : Type.{u2} -> Type.{u3}} [_inst_1 : Monad.{u2, u3} m] [_inst_2 : LawfulMonad.{u2, u3} m _inst_1] {α : Type.{u1}} (f : Nat -> α -> (m PUnit.{succ u2})) (as : List.{u1} α), Eq.{succ u3} (m PUnit.{succ u2}) (List.mapIdxM'.{u2, u3, u1} m _inst_1 α f as) (SeqRight.seqRight.{u2, u3} m (Applicative.toSeqRight.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) (List.{u2} PUnit.{succ u2}) PUnit.{succ u2} (List.mapIdxM.{u2, u3, u1} α PUnit.{succ u2} m _inst_1 as f) (fun (x._@.Mathlib.Data.List.Indexes._hyg.3378 : Unit) => Pure.pure.{u2, u3} m (Applicative.toPure.{u2, u3} m (Monad.toApplicative.{u2, u3} m _inst_1)) PUnit.{succ u2} PUnit.unit.{succ u2}))
Case conversion may be inaccurate. Consider using '#align list.mmap_with_index'_eq_mmap_with_index List.mapIdxM'_eq_mapIdxMₓ'. -/
theorem mapIdxM'_eq_mapIdxM {α} (f : ℕ → α → m PUnit) (as : List α) :
    mapIdxM' f as = mapIdxM f as *> pure PUnit.unit := by
  apply mmap_with_index'_aux_eq_mmap_with_index_aux
#align list.mmap_with_index'_eq_mmap_with_index List.mapIdxM'_eq_mapIdxM

end MmapWithIndex'

end List