/-
Copyright (c) 2020 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 algebra.module.opposites
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Module.Equiv
import Mathbin.GroupTheory.GroupAction.Opposite

/-!
# Module operations on `Mᵐᵒᵖ`

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

This file contains definitions that build on top of the group action definitions in
`group_theory.group_action.opposite`.
-/

namespace MulOpposite

universe u v

variable (R : Type u) {M : Type v} [Semiring R] [AddCommMonoid M] [Module R M]

/-- `mul_opposite.distrib_mul_action` extends to a `module` -/
instance : Module R (MulOpposite M) :=
  {
    MulOpposite.distribMulAction M
      R with
    add_smul := fun r₁ r₂ x => unop_injective <| add_smul r₁ r₂ (unop x)
    zero_smul := fun x => unop_injective <| zero_smul _ (unop x) }

#print MulOpposite.opLinearEquiv /-
/-- The function `op` is a linear equivalence. -/
def opLinearEquiv : M ≃ₗ[R] Mᵐᵒᵖ :=
  { opAddEquiv with map_smul' := MulOpposite.op_smul }
#align mul_opposite.op_linear_equiv MulOpposite.opLinearEquiv
-/

/- warning: mul_opposite.coe_op_linear_equiv -> MulOpposite.coe_opLinearEquiv is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} ((fun (_x : LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) => M -> (MulOpposite.{u2} M)) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (coeFn.{succ u2, succ u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (fun (_x : LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) => M -> (MulOpposite.{u2} M)) (LinearEquiv.hasCoeToFun.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1)) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (MulOpposite.op.{u2} M)
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (forall (a : M), (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : M) => MulOpposite.{u2} M) a) (FunLike.coe.{succ u2, succ u2, succ u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : M) => MulOpposite.{u2} M) _x) (SMulHomClass.toFunLike.{u2, u1, u2, u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) R M (MulOpposite.{u2} M) (SMulZeroClass.toSMul.{u1, u2} R M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (DistribSMul.toSMulZeroClass.{u1, u2} R M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (DistribMulAction.toDistribSMul.{u1, u2} R M (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} M _inst_2) (Module.toDistribMulAction.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (SMulZeroClass.toSMul.{u1, u2} R (MulOpposite.{u2} M) (AddMonoid.toZero.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))) (DistribSMul.toSMulZeroClass.{u1, u2} R (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))) (DistribMulAction.toDistribSMul.{u1, u2} R (MulOpposite.{u2} M) (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)) (Module.toDistribMulAction.{u1, u2} R (MulOpposite.{u2} M) _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3))))) (DistribMulActionHomClass.toSMulHomClass.{u2, u1, u2, u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) R M (MulOpposite.{u2} M) (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} M _inst_2) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)) (Module.toDistribMulAction.{u1, u2} R M _inst_1 _inst_2 _inst_3) (Module.toDistribMulAction.{u1, u2} R (MulOpposite.{u2} M) _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (SemilinearMapClass.distribMulActionHomClass.{u1, u2, u2, u2} R M (MulOpposite.{u2} M) (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (SemilinearEquivClass.instSemilinearMapClass.{u1, u1, u2, u2, u2} R R M (MulOpposite.{u2} M) (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (LinearEquiv.instSemilinearEquivClassLinearEquiv.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1)))))) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (MulOpposite.op.{u2} M)
Case conversion may be inaccurate. Consider using '#align mul_opposite.coe_op_linear_equiv MulOpposite.coe_opLinearEquivₓ'. -/
@[simp]
theorem coe_opLinearEquiv : (opLinearEquiv R : M → Mᵐᵒᵖ) = op :=
  rfl
#align mul_opposite.coe_op_linear_equiv MulOpposite.coe_opLinearEquiv

/- warning: mul_opposite.coe_op_linear_equiv_symm -> MulOpposite.coe_opLinearEquiv_symm is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} ((fun (_x : LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) => (MulOpposite.{u2} M) -> M) (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (coeFn.{succ u2, succ u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) (fun (_x : LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) => (MulOpposite.{u2} M) -> M) (LinearEquiv.hasCoeToFun.{u1, u1, u2, u2} R R (MulOpposite.{u2} M) M _inst_1 _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1)) (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (MulOpposite.unop.{u2} M)
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (forall (a : MulOpposite.{u2} M), (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : MulOpposite.{u2} M) => M) a) (FunLike.coe.{succ u2, succ u2, succ u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) (MulOpposite.{u2} M) (fun (_x : MulOpposite.{u2} M) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : MulOpposite.{u2} M) => M) _x) (SMulHomClass.toFunLike.{u2, u1, u2, u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) R (MulOpposite.{u2} M) M (SMulZeroClass.toSMul.{u1, u2} R (MulOpposite.{u2} M) (AddMonoid.toZero.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))) (DistribSMul.toSMulZeroClass.{u1, u2} R (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))) (DistribMulAction.toDistribSMul.{u1, u2} R (MulOpposite.{u2} M) (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)) (Module.toDistribMulAction.{u1, u2} R (MulOpposite.{u2} M) _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3))))) (SMulZeroClass.toSMul.{u1, u2} R M (AddMonoid.toZero.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (DistribSMul.toSMulZeroClass.{u1, u2} R M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)) (DistribMulAction.toDistribSMul.{u1, u2} R M (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} M _inst_2) (Module.toDistribMulAction.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (DistribMulActionHomClass.toSMulHomClass.{u2, u1, u2, u2} (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) R (MulOpposite.{u2} M) M (MonoidWithZero.toMonoid.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1)) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)) (AddCommMonoid.toAddMonoid.{u2} M _inst_2) (Module.toDistribMulAction.{u1, u2} R (MulOpposite.{u2} M) _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (Module.toDistribMulAction.{u1, u2} R M _inst_1 _inst_2 _inst_3) (SemilinearMapClass.distribMulActionHomClass.{u1, u2, u2, u2} R (MulOpposite.{u2} M) M (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (SemilinearEquivClass.instSemilinearMapClass.{u1, u1, u2, u2, u2} R R (MulOpposite.{u2} M) M (LinearEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) _inst_1 _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (LinearEquiv.instSemilinearEquivClassLinearEquiv.{u1, u1, u2, u2} R R (MulOpposite.{u2} M) M _inst_1 _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1)))))) (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (MulOpposite.unop.{u2} M)
Case conversion may be inaccurate. Consider using '#align mul_opposite.coe_op_linear_equiv_symm MulOpposite.coe_opLinearEquiv_symmₓ'. -/
@[simp]
theorem coe_opLinearEquiv_symm : ((opLinearEquiv R).symm : Mᵐᵒᵖ → M) = unop :=
  rfl
#align mul_opposite.coe_op_linear_equiv_symm MulOpposite.coe_opLinearEquiv_symm

/- warning: mul_opposite.coe_op_linear_equiv_to_linear_map -> MulOpposite.coe_opLinearEquiv_toLinearMap is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} ((fun (_x : LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) => M -> (MulOpposite.{u2} M)) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (coeFn.{succ u2, succ u2} (LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (fun (_x : LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) => M -> (MulOpposite.{u2} M)) (LinearMap.hasCoeToFun.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (MulOpposite.op.{u2} M)
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (forall (a : M), (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => MulOpposite.{u2} M) a) (FunLike.coe.{succ u2, succ u2, succ u2} (LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3)) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : M) => MulOpposite.{u2} M) _x) (LinearMap.instFunLikeLinearMap.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (MulOpposite.op.{u2} M)
Case conversion may be inaccurate. Consider using '#align mul_opposite.coe_op_linear_equiv_to_linear_map MulOpposite.coe_opLinearEquiv_toLinearMapₓ'. -/
@[simp]
theorem coe_opLinearEquiv_toLinearMap : ((opLinearEquiv R).toLinearMap : M → Mᵐᵒᵖ) = op :=
  rfl
#align mul_opposite.coe_op_linear_equiv_to_linear_map MulOpposite.coe_opLinearEquiv_toLinearMap

/- warning: mul_opposite.coe_op_linear_equiv_symm_to_linear_map -> MulOpposite.coe_opLinearEquiv_symm_toLinearMap is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} ((fun (_x : LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) => (MulOpposite.{u2} M) -> M) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (coeFn.{succ u2, succ u2} (LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) (fun (_x : LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) => (MulOpposite.{u2} M) -> M) (LinearMap.hasCoeToFun.{u1, u1, u2, u2} R R (MulOpposite.{u2} M) M _inst_1 _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (MulOpposite.unop.{u2} M)
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (forall (a : MulOpposite.{u2} M), (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : MulOpposite.{u2} M) => M) a) (FunLike.coe.{succ u2, succ u2, succ u2} (LinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3) (MulOpposite.{u2} M) (fun (_x : MulOpposite.{u2} M) => (fun (x._@.Mathlib.Algebra.Module.LinearMap._hyg.6190 : MulOpposite.{u2} M) => M) _x) (LinearMap.instFunLikeLinearMap.{u1, u1, u2, u2} R R (MulOpposite.{u2} M) M _inst_1 _inst_1 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (LinearEquiv.toLinearMap.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)))) (MulOpposite.unop.{u2} M)
Case conversion may be inaccurate. Consider using '#align mul_opposite.coe_op_linear_equiv_symm_to_linear_map MulOpposite.coe_opLinearEquiv_symm_toLinearMapₓ'. -/
@[simp]
theorem coe_opLinearEquiv_symm_toLinearMap :
    ((opLinearEquiv R).symm.toLinearMap : Mᵐᵒᵖ → M) = unop :=
  rfl
#align mul_opposite.coe_op_linear_equiv_symm_to_linear_map MulOpposite.coe_opLinearEquiv_symm_toLinearMap

/- warning: mul_opposite.op_linear_equiv_to_add_equiv -> MulOpposite.opLinearEquiv_toAddEquiv is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (AddEquiv.{u2, u2} M (MulOpposite.{u2} M) (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (AddZeroClass.toHasAdd.{u2} (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))))) (LinearEquiv.toAddEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (MulOpposite.opAddEquiv.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (AddEquiv.{u2, u2} M (MulOpposite.{u2} M) (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (AddZeroClass.toAdd.{u2} (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2))))) (LinearEquiv.toAddEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) M (MulOpposite.{u2} M) _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3)) (MulOpposite.opAddEquiv.{u2} M (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))))
Case conversion may be inaccurate. Consider using '#align mul_opposite.op_linear_equiv_to_add_equiv MulOpposite.opLinearEquiv_toAddEquivₓ'. -/
@[simp]
theorem opLinearEquiv_toAddEquiv : (opLinearEquiv R : M ≃ₗ[R] Mᵐᵒᵖ).toAddEquiv = opAddEquiv :=
  rfl
#align mul_opposite.op_linear_equiv_to_add_equiv MulOpposite.opLinearEquiv_toAddEquiv

/- warning: mul_opposite.op_linear_equiv_symm_to_add_equiv -> MulOpposite.opLinearEquiv_symm_toAddEquiv is a dubious translation:
lean 3 declaration is
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (AddEquiv.{u2, u2} (MulOpposite.{u2} M) M (AddZeroClass.toHasAdd.{u2} (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)))) (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (LinearEquiv.toAddEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (AddEquiv.symm.{u2, u2} M (MulOpposite.{u2} M) (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulOpposite.hasAdd.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (MulOpposite.opAddEquiv.{u2} M (AddZeroClass.toHasAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))
but is expected to have type
  forall (R : Type.{u1}) {M : Type.{u2}} [_inst_1 : Semiring.{u1} R] [_inst_2 : AddCommMonoid.{u2} M] [_inst_3 : Module.{u1, u2} R M _inst_1 _inst_2], Eq.{succ u2} (AddEquiv.{u2, u2} (MulOpposite.{u2} M) M (AddZeroClass.toAdd.{u2} (MulOpposite.{u2} M) (AddMonoid.toAddZeroClass.{u2} (MulOpposite.{u2} M) (AddCommMonoid.toAddMonoid.{u2} (MulOpposite.{u2} M) (MulOpposite.addCommMonoid.{u2} M _inst_2)))) (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (LinearEquiv.toAddEquiv.{u1, u1, u2, u2} R R _inst_1 _inst_1 (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.{u2} M) M (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_2 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) _inst_3 (LinearEquiv.symm.{u1, u1, u2, u2} R R M (MulOpposite.{u2} M) _inst_1 _inst_1 _inst_2 (MulOpposite.addCommMonoid.{u2} M _inst_2) _inst_3 (MulOpposite.module.{u1, u2} R M _inst_1 _inst_2 _inst_3) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHom.id.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (RingHomInvPair.ids.{u1} R _inst_1) (RingHomInvPair.ids.{u1} R _inst_1) (MulOpposite.opLinearEquiv.{u1, u2} R M _inst_1 _inst_2 _inst_3))) (AddEquiv.symm.{u2, u2} M (MulOpposite.{u2} M) (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2))) (MulOpposite.add.{u2} M (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))) (MulOpposite.opAddEquiv.{u2} M (AddZeroClass.toAdd.{u2} M (AddMonoid.toAddZeroClass.{u2} M (AddCommMonoid.toAddMonoid.{u2} M _inst_2)))))
Case conversion may be inaccurate. Consider using '#align mul_opposite.op_linear_equiv_symm_to_add_equiv MulOpposite.opLinearEquiv_symm_toAddEquivₓ'. -/
@[simp]
theorem opLinearEquiv_symm_toAddEquiv :
    (opLinearEquiv R : M ≃ₗ[R] Mᵐᵒᵖ).symm.toAddEquiv = opAddEquiv.symm :=
  rfl
#align mul_opposite.op_linear_equiv_symm_to_add_equiv MulOpposite.opLinearEquiv_symm_toAddEquiv

end MulOpposite


import primitive_element
import field_theory.fixed
import linear_algebra.direct_sum.finsupp

variables (F : Type*) [field F] (E : Type*) [field E] [algebra F E]

instance aut : group (E ≃ₐ[F] E) := {
    mul := λ ϕ ψ, ψ.trans ϕ,
    mul_assoc := λ ϕ ψ χ, rfl,
    one := 1,
    one_mul := λ ϕ, by {ext,refl},
    mul_one := λ ϕ, by {ext,refl},
    inv := alg_equiv.symm,
    mul_left_inv := λ ϕ, by {ext,exact alg_equiv.symm_apply_apply ϕ a},
}

instance aut_action : mul_semiring_action (E ≃ₐ[F] E) E := {
    smul := alg_equiv.to_fun,
    smul_zero := alg_equiv.map_zero,
    smul_one := alg_equiv.map_one,
    one_smul := λ _, rfl,
    smul_add := alg_equiv.map_add,
    smul_mul := alg_equiv.map_mul,
    mul_smul := λ _ _ _, rfl,
}

instance subgroup_action (G : Type*) [group G] [mul_semiring_action G E] (H : subgroup G) :
mul_semiring_action H E := {
    smul := λ h e, (↑h : G) • e,
    smul_zero := λ _, smul_zero _,
    smul_one := λ _, smul_one _,
    one_smul := one_smul G,
    smul_add := λ _, smul_add _,
    smul_mul := λ _, mul_semiring_action.smul_mul _,
    mul_smul := λ x y _, mul_action.mul_smul ↑x ↑y _,
}

lemma base_field_is_fixed : set.range (algebra_map F E) ⊆ mul_action.fixed_points (E ≃ₐ[F] E) E :=
begin
    intros x hx ϕ,
    cases hx with f hf,
    rw ←hf,
    exact alg_equiv.commutes ϕ f,
end

lemma dim_finsupp (B : set E) : vector_space.dim E (B →₀ E) = cardinal.mk B :=
begin
    --this might be in mathlib but I can't find it
    sorry,
end

theorem artin_inequality (G : Type*) [group G] [fintype G] [mul_semiring_action G E] :
vector_space.dim (mul_action.fixed_points G E) E ≤ fintype.card G :=
begin
    set F := mul_action.fixed_points G E,
    cases exists_is_basis F E with B hB,
    rw ← is_basis.mk_range_eq_dim hB,
    apply le_trans cardinal.mk_range_le,
    replace hB := hB.left,
    set map : (B →₀ E) →ₗ[E] (G → E) := finsupp.lsum (λ b, (linear_map.pi (λ g, algebra_map E (E →ₗ[E] E) (g • ↑b)))),
    have hB : vector_space.dim E (B →₀ E) = cardinal.mk B := dim_finsupp E B,
    have hG : vector_space.dim E (G → E) = fintype.card G := dim_fun',
    rw ←hB,
    --suffices : vector_space.dim E (B →₀ E) ≤ vector_space.dim E (G → E),
    -- rw ← hG, --(doesn't work for some reason)

    --choose c in ker(map) with fewest nonzero entries
    --if c is nonzero the play a produce a nonzero element with fewer nonzero entries
    --thus, kernel is zero
    --now look at dimensions over E
    --have dimG : vector_space.dim E (G → E) = fintype.card G := dim_fun',
    --more work needed for dimension of B →₀ E (first show finite_dimensional and deduce that B is finite?)
    sorry,
end

universe u

theorem artin_inequality' (F E G : Type u) [field F] [field E] [algebra F E] [group G] [fintype G]
    [mul_semiring_action G E] : vector_space.dim (mul_action.fixed_points G E) E ≤ fintype.card G :=
begin
    set F := mul_action.fixed_points G E,
    cases exists_is_basis F E with B hB,
    rw ← is_basis.mk_range_eq_dim hB,
    apply le_trans cardinal.mk_range_le,
    replace hB := hB.left,
    set map : (B →₀ E) →ₗ[E] (G → E) := finsupp.lsum (λ b, (linear_map.pi (λ g, algebra_map E (E →ₗ[E] E) (g • ↑b)))),
    have hB : vector_space.dim E (B →₀ E) = cardinal.mk B := dim_finsupp E B,
    have hG : vector_space.dim E (G → E) = fintype.card G := dim_fun',
    rw ←hB,
    --suffices : vector_space.dim E (B →₀ E) ≤ vector_space.dim E (G → E),
    -- rw ← hG, --(doesn't work for some reason)

    --choose c in ker(map) with fewest nonzero entries
    --if c is nonzero the play a produce a nonzero element with fewer nonzero entries
    --thus, kernel is zero
    --now look at dimensions over E
    --have dimG : vector_space.dim E (G → E) = fintype.card G := dim_fun',
    --more work needed for dimension of B →₀ E (first show finite_dimensional and deduce that B is finite?)
    sorry,
end

--things to do:
--  give notation for aut
--  define fixed field and show that it is indeed a field
--  maybe use group actions more generally rather than just subgroups of (E ≃ₐ[F] E)
--  prove Artin's theorem that [E : E^G] ≤ |G|
--  show that if extension is finite separable then cardinality of automorphism group ≤ [E : F]
--  show that if G is finite then G = Aut(E / E^G)


/-
Copyright (c) 2014-15 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Floris van Doorn

Partially ported from Coq HoTT
Theorems about pi-types (dependent function spaces)
-/

import .sigma hott.arity hott.cubical.square

universe u
hott_theory

namespace hott
open hott.eq hott.equiv hott.is_equiv hott.funext hott.sigma hott.is_trunc unit

namespace pi
  variables {A : Type _} {A' : Type _} {B : A → Type _} {B' : A' → Type _} {C : Πa, B a → Type _}
            {D : Πa b, C a b → Type _}
            {a a' a'' : A} {b b₁ b₂ : B a} {b' : B a'} {b'' : B a''} {f g : Πa, B a}

  /- Paths -/

  /-
    Paths [p : f ≈ g] in a function type [Πx:X, P x] are equivalent to functions taking values
    in path types, [H : Πx:X, f x = g x], or concisely, [H : f ~ g].

    This is developed in init.funext.
  -/

  /- homotopy.symm is an equivalence -/
  @[hott] def is_equiv_homotopy_symm : is_equiv (homotopy.symm : f ~ g → g ~ f) :=
  begin
    apply adjointify homotopy.symm homotopy.symm,
    { intro p, apply eq_of_homotopy, intro a, apply eq.inv_inv },
    { intro p, apply eq_of_homotopy, intro a, apply eq.inv_inv }
  end

  /-
    The identification of the path space of a dependent function space,
    up to equivalence, is of course just funext.
  -/

  @[hott] def pi_eq_equiv (f g : Πx, B x) : (f = g) ≃ (f ~ g) := eq_equiv_homotopy f g

  @[hott,instance] def is_equiv_eq_of_homotopy (f g : Πx, B x)
    : is_equiv (eq_of_homotopy : f ~ g → f = g) :=
  is_equiv_inv _

  @[hott] def homotopy_equiv_eq (f g : Πx, B x) : (f ~ g) ≃ (f = g) :=
  equiv.mk eq_of_homotopy (by apply_instance)

  /- Transport -/

  @[hott] def pi_transport (p : a = a') (f : Π(b : B a), C a b)
    : (transport (λa, Π(b : B a), C a b) p f) ~ (λb, tr_inv_tr p b ▸ (p ▸D f (p⁻¹ ▸ b))) :=
  by induction p; reflexivity

  /- A special case of [transport_pi] where the type [B] does not depend on [A],
      and so it is just a fixed type [B]. -/
  @[hott] def pi_transport_constant {C : A → A' → Type _} (p : a = a') (f : Π(b : A'), C a b) (b : A')
    : (transport _ p f) b = @transport A (λa, C a b) _ _ p (f b) := --fix
  by induction p; reflexivity

  /- Pathovers -/

  @[hott] def pi_pathover' {f : Πb, C a b} {g : Πb', C a' b'} {p : a = a'}
    (r : Π(b : B a) (b' : B a') (q : b =[p] b'), f b =[apd011 C p q; λX, X] g b') : f =[p; λa, Πb, C a b] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, intro b,
    let z := eq_of_pathover_idp (r b b idpo), exact z --fix
  end

  @[hott] def pi_pathover_left' {f : Πb, C a b} {g : Πb', C a' b'} {p : a = a'}
    (r : Π(b : B a), f b =[apd011 C p (pathover_tr _ _); λX, X] g (p ▸ b)) : f =[p; λa, Πb, C a b] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    let z := eq_of_pathover_idp (r b), exact z --fix
  end

  @[hott] def pi_pathover_right' {f : Πb, C a b} {g : Πb', C a' b'} {p : a = a'}
    (r : Π(b' : B a'), f (p⁻¹ ▸ b') =[apd011 C p (tr_pathover _ _); λX, X] g b') : f =[p; λa, Πb, C a b] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    dsimp [apd011, tr_pathover] at r,
    let z := eq_of_pathover_idp (r b), exact z --fix
  end

  @[hott] def pi_pathover_constant {C : A → A' → Type _} {f : Π(b : A'), C a b}
    {g : Π(b : A'), C a' b} {p : a = a'}
    (r : Π(b : A'), f b =[p; λa, C a b] g b) : f =[p; λa, Πb, C a b] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    let z := eq_of_pathover_idp (r b), exact z --fix
  end

  -- a version where C is uncurried, but where the conclusion of r is still a proper pathover
  -- instead of a heterogenous equality
  @[hott] def pi_pathover {C : (Σa, B a) → Type _} {f : Πb, C ⟨a, b⟩} {g : Πb', C ⟨a', b'⟩}
    {p : a = a'} (r : Π(b : B a) (b' : B a') (q : b =[p] b'), f b =[dpair_eq_dpair p q] g b')
    : f =[p; λa, Πb, C ⟨a, b⟩] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    apply (@eq_of_pathover_idp _ C), exact (r b b (pathover.idpatho b)),
  end

  @[hott] def pi_pathover_left {C : (Σa, B a) → Type _} {f : Πb, C ⟨a, b⟩} {g : Πb', C ⟨a', b'⟩}
    {p : a = a'} (r : Π(b : B a), f b =[dpair_eq_dpair p (pathover_tr _ _)] g (p ▸ b))
    : f =[p; λa, Πb, C ⟨a, b⟩] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    apply eq_of_pathover_idp, exact pathover_ap _ _ (r b)
  end

  @[hott] def pi_pathover_right {C : (Σa, B a) → Type _} {f : Πb, C ⟨a, b⟩} {g : Πb', C ⟨a', b'⟩}
    {p : a = a'} (r : Π(b' : B a'), f (p⁻¹ ▸ b') =[dpair_eq_dpair p (tr_pathover _ _)] g b')
    : f =[p; λa, Πb, C ⟨a, b⟩] g :=
  begin
    induction p, apply pathover_idp_of_eq,
    apply eq_of_homotopy, hintro b,
    apply eq_of_pathover_idp, exact pathover_ap _ _ (r b)
  end

  /- Maps on paths -/

  /- The action of maps given by lambda. -/
  @[hott] def ap_lambdaD {C : A' → Type _} (p : a = a') (f : Πa b, C b) :
    ap (λa b, f a b) p = eq_of_homotopy (λb, ap (λa, f a b) p) :=
  begin
    apply (eq.rec_on p),
    apply inverse,
    apply eq_of_homotopy_idp
  end

  /- Dependent paths -/

  /- with more implicit arguments the conclusion of the following theorem is
     (Π(b : B a), transportD B C p b (f b) = g (transport B p b)) ≃
     (transport (λa, Π(b : B a), C a b) p f = g) -/
  @[hott] def heq_piD (p : a = a') (f : Π(b : B a), C a b)
    (g : Π(b' : B a'), C a' b') : (Π(b : B a), p ▸D (f b) = g (p ▸ b)) ≃ (p ▸ f = g) :=
  eq.rec_on p (λg, homotopy_equiv_eq _ _) g

  @[hott] def heq_pi {C : A → Type _} (p : a = a') (f : Π(b : B a), C a)
    (g : Π(b' : B a'), C a') : (Π(b : B a), p ▸ (f b) = g (p ▸ b)) ≃ (p ▸ f = g) :=
  eq.rec_on p (λg, homotopy_equiv_eq _ _) g

  section
  /- more implicit arguments:
  (Π(b : B a), transport C (sigma_eq p idp) (f b) = g (p ▸ b)) ≃
  (Π(b : B a), transportD B (λ(a : A) (b : B a), C ⟨a, b⟩) p b (f b) = g (transport B p b)) -/
  @[hott] def heq_pi_sigma {C : (Σa, B a) → Type _} (p : a = a')
    (f : Π(b : B a), C ⟨a, b⟩) (g : Π(b' : B a'), C ⟨a', b'⟩) :
    (Π(b : B a), (dpair_eq_dpair p (pathover_tr p b)) ▸ (f b) = g (p ▸ b)) ≃
    (Π(b : B a), transportD (λa b, C ⟨a, b⟩) p _ (f b) = g (p ▸ b)) :=
  by induction p; refl
  end

  /- Functorial action -/
  variables (f0 : A' → A) (f1 : Π(a':A'), B (f0 a') → B' a')

  /- The functoriality of [forall] is slightly subtle: it is contravariant in the domain type and covariant in the codomain, but the codomain is dependent on the domain. -/

  @[hott] def pi_functor : (Π(a:A), B a) → (Π(a':A'), B' a') :=
  λg a', f1 a' (g (f0 a'))

  @[hott] def pi_functor_left (B : A → Type _) : (Π(a:A), B a) → (Π(a':A'), B (f0 a')) :=
  pi_functor f0 (λa, id)

  @[hott] def pi_functor_right {B' : A → Type _} (f1 : Π(a:A), B a → B' a)
    : (Π(a:A), B a) → (Π(a:A), B' a) :=
  pi_functor id f1

  @[hott] def pi_iff_pi {B' : A → Type _} (f : Πa, B a ↔ B' a) : (Π(a:A), B a) ↔ (Π(a:A), B' a) :=
  ⟨pi_functor id (λa, (f a).1), pi_functor id (λa, (f a).2)⟩

  @[hott,hsimp] def pi_functor_eq (g : Πa, B a) (a' : A') : pi_functor f0 f1 g a' = f1 a' (g (f0 a')) :=
  by refl
  @[hott] def ap_pi_functor {g g' : Π(a:A), B a} (h : g ~ g')
    : ap (pi_functor f0 f1) (eq_of_homotopy h)
      = eq_of_homotopy (λ(a' : A'), (ap (f1 a') (h (f0 a')))) :=
  begin
  apply is_equiv_rect
    (@apd10 A B g g') (λh, ap (pi_functor f0 f1) (eq_of_homotopy h) =
     eq_of_homotopy (λa':A', (ap (f1 a') (h (f0 a'))))), intro p, clear h,
  induction p,
  refine (ap (ap (pi_functor f0 f1)) (eq_of_homotopy_idp g)) ⬝ _,
  symmetry, apply eq_of_homotopy_idp
  end

  /- Equivalences -/

  /-@[hott] def pi_equiv_pi (f0 : A' ≃ A) (f1 : Πa', (B (to_fun f0 a') ≃ B' a'))
    : (Πa, B a) ≃ (Πa', B' a') :=
  begin
    fapply equiv.MK,
    exact pi_functor f0 f1,
          (pi_functor f0⁻¹ᶠ
          (λ(a : A) (b' : B' (f0⁻¹ᶠ a)), transport B (right_inv f0 a) ((f1 (f0⁻¹ᶠ a))⁻¹ᶠ b')))
  end-/
  @[hott, instance] def is_equiv_pi_functor [H0 : is_equiv f0]
    [H1 : Πa', is_equiv (f1 a')] : is_equiv (pi_functor f0 f1) :=
  adjointify (pi_functor f0 f1) (pi_functor f0⁻¹ᶠ
          (λ(a : A) (b' : B' (f0⁻¹ᶠ a)), transport B (right_inv f0 a) ((f1 (f0⁻¹ᶠ a))⁻¹ᶠ b')))
    begin
      intro h, apply eq_of_homotopy, intro a', dsimp,
      rwr [adj f0 a', hott.eq.inverse (tr_compose B f0 (left_inv f0 a') _), fn_tr_eq_tr_fn _ f1 _, right_inv (f1 _)], --fix
      apply apdt
    end
    begin
      intro h, apply eq_of_homotopy, intro a, dsimp,
      rwr [left_inv (f1 _)],
      apply apdt
    end

  @[hott] def pi_equiv_pi_of_is_equiv [H : is_equiv f0]
    [H1 : Πa', is_equiv (f1 a')] : (Πa, B a) ≃ (Πa', B' a') :=
  equiv.mk (pi_functor f0 f1) (by apply_instance)

  @[hott] def pi_equiv_pi (f0 : A' ≃ A) (f1 : Πa', (B (f0 a') ≃ B' a'))
    : (Πa, B a) ≃ (Πa', B' a') :=
  pi_equiv_pi_of_is_equiv f0 (λa', f1 a')

  @[hott] def pi_equiv_pi_right {P Q : A → Type _} (g : Πa, P a ≃ Q a)
    : (Πa, P a) ≃ (Πa, Q a) :=
  pi_equiv_pi equiv.rfl g

  /- Equivalence if one of the types is contractible -/
  variable (B)
  @[hott] def pi_equiv_of_is_contr_left [H : is_contr A]
    : (Πa, B a) ≃ B (center A) :=
  begin
    fapply equiv.MK,
    { intro f, exact f (center A)},
    { intros b a, exact center_eq a ▸ b},
    { intro b, dsimp, rwr [prop_eq_of_is_contr (center_eq (center A)) idp]},
    { intro f, apply eq_of_homotopy, intro a, dsimp, induction (center_eq a),
      refl }
  end

  @[hott] def pi_equiv_of_is_contr_right [H : Πa, is_contr (B a)]
    : (Πa, B a) ≃ unit :=
  begin
    fapply equiv.MK,
    { intro f, exact star },
    { intros u a, exact center _ },
    { intro u, induction u, reflexivity },
    { intro f, apply eq_of_homotopy, intro a, apply is_prop.elim }
  end
  variable {B}

  /- Interaction with other type constructors -/

  -- most of these are in the file of the other type constructor

  @[hott] def pi_empty_left (B : empty → Type _) : (Πx, B x) ≃ unit :=
  begin
    fapply equiv.MK,
    { intro f, exact star },
    { intros x y, exact empty.elim y },
    { intro x, induction x, reflexivity },
    { intro f, apply eq_of_homotopy, intro y, exact empty.elim y }
  end

  @[hott] def pi_unit_left (B : unit → Type _) : (Πx, B x) ≃ B star :=
  pi_equiv_of_is_contr_left _

  @[hott] def pi_bool_left (B : bool → Type _) : (Πx, B x) ≃ B ff × B tt :=
  begin
    fapply equiv.MK,
    { intro f, exact (f ff, f tt) },
    { intros x b, induction x, induction b, all_goals {assumption}},
    { intro x, induction x, reflexivity },
    { intro f, apply eq_of_homotopy, intro b, induction b, all_goals {reflexivity}},
  end

  /- Truncatedness: any dependent product of n-types is an n-type -/

  @[hott, instance, priority 1520] theorem is_trunc_pi (B : A → Type _) (n : trunc_index)
      [H : ∀a, is_trunc n (B a)] : is_trunc n (Πa, B a) :=
  begin
    unfreezeI, induction n with n IH generalizing B H; resetI,
      { fapply is_contr.mk,
          intro a, apply center,
          intro f, apply eq_of_homotopy,
            intro x, apply (center_eq (f x)) },
      { apply is_trunc_succ_intro, intros f g,
          exact is_trunc_equiv_closed_rev n (eq_equiv_homotopy f g) (by apply_instance) }
  end

  @[hott] theorem is_trunc_pi_eq (n : trunc_index) (f g : Πa, B a)
      [H : ∀a, is_trunc n (f a = g a)] : is_trunc n (f = g) :=
  is_trunc_equiv_closed_rev n (eq_equiv_homotopy f g) (by apply_instance)

  @[hott, instance] theorem is_trunc_not (n : trunc_index) (A : Type _) : is_trunc (n.+1) ¬A :=
  by dsimp [not]; apply_instance

  @[hott] theorem is_prop_pi_eq (a : A) : is_prop (Π(a' : A), a = a') :=
  is_prop_of_imp_is_contr
  ( assume (f : Πa', a = a'),
    have is_contr A, from is_contr.mk a f,
    by unfreezeI; apply_instance)

  @[hott] theorem is_prop_neg (A : Type _) : is_prop (¬A) := by apply_instance
  @[hott] theorem is_prop_ne {A : Type _} (a b : A) : is_prop (a ≠ b) := by apply_instance

  @[hott] def is_contr_pi_of_neg {A : Type _} (B : A → Type _) (H : ¬ A) : is_contr (Πa, B a) :=
  begin
    apply is_contr.mk (λa, empty.elim (H a)), intro f, apply eq_of_homotopy, intro x, exact empty.elim (H x)
  end

  /- Symmetry of Π -/
  @[hott] def pi_flip {P : A → A' → Type _} (f : Πa b, P a b) (b : A') (a : A) : P a b := f a b
  @[hott, instance] def is_equiv_flip {P : A → A' → Type _}
    : is_equiv (@pi_flip A A' P) :=
  begin
    fapply is_equiv.mk,
    exact (@pi_flip _ _ (pi_flip P)),
    all_goals {intro f; refl}
  end

  @[hott] def pi_comm_equiv {P : A → A' → Type _} : (Πa b, P a b) ≃ (Πb a, P a b) :=
  equiv.mk (@pi_flip _ _ P) (by apply_instance)

  /- Dependent functions are equivalent to nondependent functions into the total space together
     with a homotopy -/
  @[hott] def pi_equiv_arrow_sigma_right {A : Type _} {B : A → Type _} (f : Πa, B a) :
    Σ(f : A → Σa, B a), sigma.fst ∘ f ~ id :=
  ⟨λa, ⟨a, f a⟩, λa, idp⟩

  @[hott] def pi_equiv_arrow_sigma_left
    (v : Σ(f : A → Σa, B a), sigma.fst ∘ f ~ id) (a : A) : B a :=
  transport B (v.2 a) (v.1 a).2

  open funext
  @[hott] def pi_equiv_arrow_sigma : (Πa, B a) ≃ Σ(f : A → Σa, B a), sigma.fst ∘ f ~ id :=
  begin
    fapply equiv.MK,
    { exact pi_equiv_arrow_sigma_right },
    { exact pi_equiv_arrow_sigma_left },
    { intro v, induction v with f p, fapply sigma_eq,
      { apply eq_of_homotopy, intro a, fapply sigma_eq,
        { exact (p a)⁻¹ },
        { apply inverseo, apply pathover_tr }},
      { apply pi_pathover_constant, intro a, apply eq_pathover_constant_right,
        refine ap_compose (λf : A → A, f a) _ _ ⬝ph _,
        refine ap02 _ (compose_eq_of_homotopy (@sigma.fst A B) _) ⬝ph _,
        refine ap_eq_apd10 _ _ ⬝ph _,
        refine apd10 (right_inv apd10 _) a ⬝ph _,
        refine sigma_eq_fst _ _ ⬝ph _, apply square_of_eq, exact (con.left_inv _)⁻¹ }},
    { intro a, reflexivity }
  end

end pi

namespace pi

  /- pointed pi types -/
  open pointed

  @[hott, instance] def pointed_pi {A : Type _} (P : A → Type _) [H : Πx, pointed (P x)]
      : pointed (Πx, P x) :=
  pointed.mk (λx, pt)

end pi
end hott


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.types
import Mathlib.category_theory.currying
import Mathlib.PostPort

universes v u

namespace Mathlib

/-!
# The morphism comparing a colimit of limits with the corresponding limit of colimits.

For `F : J × K ⥤ C` there is always a morphism $\colim_k \lim_j F(j,k) → \lim_j \colim_k F(j, k)$.
While it is not usually an isomorphism, with additional hypotheses on `J` and `K` it may be,
in which case we say that "colimits commute with limits".

The prototypical example, proved in `category_theory.limits.filtered_colimit_commutes_finite_limit`,
is that when `C = Type`, filtered colimits commute with finite limits.

## References
* Borceux, Handbook of categorical algebra 1, Section 2.13
* [Stacks: Filtered colimits](https://stacks.math.columbia.edu/tag/002W)
-/

namespace category_theory.limits

theorem map_id_left_eq_curry_map {J : Type v} {K : Type v} [small_category J] [small_category K] {C : Type u} [category C] (F : J × K ⥤ C) {j : J} {k : K} {k' : K} {f : k ⟶ k'} : functor.map F (𝟙, f) = functor.map (functor.obj (functor.obj curry F) j) f :=
  rfl

theorem map_id_right_eq_curry_swap_map {J : Type v} {K : Type v} [small_category J] [small_category K] {C : Type u} [category C] (F : J × K ⥤ C) {j : J} {j' : J} {f : j ⟶ j'} {k : K} : functor.map F (f, 𝟙) = functor.map (functor.obj (functor.obj curry (prod.swap K J ⋙ F)) k) f :=
  rfl

/--
The universal morphism
$\colim_k \lim_j F(j,k) → \lim_j \colim_k F(j, k)$.
-/
def colimit_limit_to_limit_colimit {J : Type v} {K : Type v} [small_category J] [small_category K] {C : Type u} [category C] (F : J × K ⥤ C) [has_limits_of_shape J C] [has_colimits_of_shape K C] : colimit (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim) ⟶ limit (functor.obj curry F ⋙ colim) :=
  limit.lift (functor.obj curry F ⋙ colim)
    (cone.mk (cocone.X (colimit.cocone (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim)))
      (nat_trans.mk
        fun (j : J) =>
          colimit.desc (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim)
            (cocone.mk (cocone.X (colimit.cocone (functor.obj (functor.obj curry F) j)))
              (nat_trans.mk
                fun (k : K) =>
                  limit.π (functor.obj (functor.obj curry (prod.swap K J ⋙ F)) k) j ≫
                    colimit.ι (functor.obj (functor.obj curry F) j) k))))

/--
Since `colimit_limit_to_limit_colimit` is a morphism from a colimit to a limit,
this lemma characterises it.
-/
@[simp] theorem ι_colimit_limit_to_limit_colimit_π {J : Type v} {K : Type v} [small_category J] [small_category K] {C : Type u} [category C] (F : J × K ⥤ C) [has_limits_of_shape J C] [has_colimits_of_shape K C] (j : J) (k : K) : colimit.ι (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim) k ≫
    colimit_limit_to_limit_colimit F ≫ limit.π (functor.obj curry F ⋙ colim) j =
  limit.π (functor.obj (functor.obj curry (prod.swap K J ⋙ F)) k) j ≫ colimit.ι (functor.obj (functor.obj curry F) j) k := sorry

@[simp] theorem ι_colimit_limit_to_limit_colimit_π_apply {J : Type v} {K : Type v} [small_category J] [small_category K] (F : J × K ⥤ Type v) (j : J) (k : K) (f : functor.obj (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim) k) : limit.π (functor.obj curry F ⋙ colim) j
    (colimit_limit_to_limit_colimit F (colimit.ι (functor.obj curry (prod.swap K J ⋙ F) ⋙ lim) k f)) =
  colimit.ι (functor.obj (functor.obj curry F) j) k
    (limit.π (functor.obj (functor.obj curry (prod.swap K J ⋙ F)) k) j f) := sorry


/-
Copyright (c) 2018 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Johan Commelin, Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.natural_isomorphism
import Mathlib.PostPort

universes v₁ v₂ v₃ u₁ u₂ u₃ l

namespace Mathlib

/-!
# Comma categories

A comma category is a construction in category theory, which builds a category out of two functors
with a common codomain. Specifically, for functors `L : A ⥤ T` and `R : B ⥤ T`, an object in
`comma L R` is a morphism `hom : L.obj left ⟶ R.obj right` for some objects `left : A` and
`right : B`, and a morphism in `comma L R` between `hom : L.obj left ⟶ R.obj right` and
`hom' : L.obj left' ⟶ R.obj right'` is a commutative square

```
L.obj left   ⟶   L.obj left'
      |               |
  hom |               | hom'
      ↓               ↓
R.obj right  ⟶   R.obj right',
```

where the top and bottom morphism come from morphisms `left ⟶ left'` and `right ⟶ right'`,
respectively.

## Main definitions

* `comma L R`: the comma category of the functors `L` and `R`.
* `over X`: the over category of the object `X` (developed in `over.lean`).
* `under X`: the under category of the object `X` (also developed in `over.lean`).
* `arrow T`: the arrow category of the category `T` (developed in `arrow.lean`).

## References

* <https://ncatlab.org/nlab/show/comma+category>

## Tags

comma, slice, coslice, over, under, arrow
-/

namespace category_theory

/-- The objects of the comma category are triples of an object `left : A`, an object
   `right : B` and a morphism `hom : L.obj left ⟶ R.obj right`.  -/
structure comma {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T)
where
  left : autoParam A
  (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.obviously")
    (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "obviously") [])
  right : autoParam B
  (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.obviously")
    (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "obviously") [])
  hom : functor.obj L left ⟶ functor.obj R right

-- Satisfying the inhabited linter

protected instance comma.inhabited {T : Type u₃} [category T] [Inhabited T] : Inhabited (comma 𝟭 𝟭) :=
  { default := comma.mk 𝟙 }

/-- A morphism between two objects in the comma category is a commutative square connecting the
    morphisms coming from the two objects using morphisms in the image of the functors `L` and `R`.
-/
structure comma_morphism {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} (X : comma L R) (Y : comma L R)
where
  left : autoParam (comma.left X ⟶ comma.left Y)
  (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.obviously")
    (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "obviously") [])
  right : autoParam (comma.right X ⟶ comma.right Y)
  (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.obviously")
    (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "obviously") [])
  w' : autoParam (functor.map L left ≫ comma.hom Y = comma.hom X ≫ functor.map R right)
  (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.obviously")
    (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "obviously") [])

-- Satisfying the inhabited linter

protected instance comma_morphism.inhabited {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} [Inhabited (comma L R)] : Inhabited (comma_morphism Inhabited.default Inhabited.default) :=
  { default := comma_morphism.mk }

@[simp] theorem comma_morphism.w {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} {Y : comma L R} (c : comma_morphism X Y) : functor.map L (comma_morphism.left c) ≫ comma.hom Y = comma.hom X ≫ functor.map R (comma_morphism.right c) := sorry

@[simp] theorem comma_morphism.w_assoc {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} {Y : comma L R} (c : comma_morphism X Y) {X' : T} (f' : functor.obj R (comma.right Y) ⟶ X') : functor.map L (comma_morphism.left c) ≫ comma.hom Y ≫ f' = comma.hom X ≫ functor.map R (comma_morphism.right c) ≫ f' := sorry

protected instance comma_category {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} : category (comma L R) :=
  category.mk

namespace comma

@[simp] theorem id_left {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} : comma_morphism.left 𝟙 = 𝟙 :=
  rfl

@[simp] theorem id_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} : comma_morphism.right 𝟙 = 𝟙 :=
  rfl

@[simp] theorem comp_left {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} {Y : comma L R} {Z : comma L R} {f : X ⟶ Y} {g : Y ⟶ Z} : comma_morphism.left (f ≫ g) = comma_morphism.left f ≫ comma_morphism.left g :=
  rfl

@[simp] theorem comp_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L : A ⥤ T} {R : B ⥤ T} {X : comma L R} {Y : comma L R} {Z : comma L R} {f : X ⟶ Y} {g : Y ⟶ Z} : comma_morphism.right (f ≫ g) = comma_morphism.right f ≫ comma_morphism.right g :=
  rfl

/-- The functor sending an object `X` in the comma category to `X.left`. -/
def fst {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T) : comma L R ⥤ A :=
  functor.mk (fun (X : comma L R) => left X) fun (_x _x_1 : comma L R) (f : _x ⟶ _x_1) => comma_morphism.left f

/-- The functor sending an object `X` in the comma category to `X.right`. -/
@[simp] theorem snd_map {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T) (_x : comma L R) : ∀ (_x_1 : comma L R) (f : _x ⟶ _x_1), functor.map (snd L R) f = comma_morphism.right f :=
  fun (_x_1 : comma L R) (f : _x ⟶ _x_1) => Eq.refl (functor.map (snd L R) f)

/-- We can interpret the commutative square constituting a morphism in the comma category as a
    natural transformation between the functors `fst ⋙ L` and `snd ⋙ R` from the comma category
    to `T`, where the components are given by the morphism that constitutes an object of the comma
    category. -/
@[simp] theorem nat_trans_app {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T) (X : comma L R) : nat_trans.app (nat_trans L R) X = hom X :=
  Eq.refl (nat_trans.app (nat_trans L R) X)

/--
Construct an isomorphism in the comma category given isomorphisms of the objects whose forward
directions give a commutative square.
-/
@[simp] theorem iso_mk_inv_left {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] {L₁ : A ⥤ T} {R₁ : B ⥤ T} {X : comma L₁ R₁} {Y : comma L₁ R₁} (l : left X ≅ left Y) (r : right X ≅ right Y) (h : functor.map L₁ (iso.hom l) ≫ hom Y = hom X ≫ functor.map R₁ (iso.hom r)) : comma_morphism.left (iso.inv (iso_mk l r h)) = iso.inv l :=
  Eq.refl (comma_morphism.left (iso.inv (iso_mk l r h)))

/-- A natural transformation `L₁ ⟶ L₂` induces a functor `comma L₂ R ⥤ comma L₁ R`. -/
@[simp] theorem map_left_obj_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (R : B ⥤ T) {L₁ : A ⥤ T} {L₂ : A ⥤ T} (l : L₁ ⟶ L₂) (X : comma L₂ R) : right (functor.obj (map_left R l) X) = right X :=
  Eq.refl (right (functor.obj (map_left R l) X))

/-- The functor `comma L R ⥤ comma L R` induced by the identity natural transformation on `L` is
    naturally isomorphic to the identity functor. -/
@[simp] theorem map_left_id_inv_app_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T) (X : comma L R) : comma_morphism.right (nat_trans.app (iso.inv (map_left_id L R)) X) = 𝟙 :=
  Eq.refl (comma_morphism.right (nat_trans.app (iso.inv (map_left_id L R)) X))

/-- The functor `comma L₁ R ⥤ comma L₃ R` induced by the composition of two natural transformations
    `l : L₁ ⟶ L₂` and `l' : L₂ ⟶ L₃` is naturally isomorphic to the composition of the two functors
    induced by these natural transformations. -/
@[simp] theorem map_left_comp_hom_app_left {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (R : B ⥤ T) {L₁ : A ⥤ T} {L₂ : A ⥤ T} {L₃ : A ⥤ T} (l : L₁ ⟶ L₂) (l' : L₂ ⟶ L₃) (X : comma L₃ R) : comma_morphism.left (nat_trans.app (iso.hom (map_left_comp R l l')) X) = 𝟙 :=
  Eq.refl (comma_morphism.left (nat_trans.app (iso.hom (map_left_comp R l l')) X))

/-- A natural transformation `R₁ ⟶ R₂` induces a functor `comma L R₁ ⥤ comma L R₂`. -/
def map_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) {R₁ : B ⥤ T} {R₂ : B ⥤ T} (r : R₁ ⟶ R₂) : comma L R₁ ⥤ comma L R₂ :=
  functor.mk (fun (X : comma L R₁) => mk (hom X ≫ nat_trans.app r (right X)))
    fun (X Y : comma L R₁) (f : X ⟶ Y) => comma_morphism.mk

/-- The functor `comma L R ⥤ comma L R` induced by the identity natural transformation on `R` is
    naturally isomorphic to the identity functor. -/
@[simp] theorem map_right_id_inv_app_left {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) (R : B ⥤ T) (X : comma L R) : comma_morphism.left (nat_trans.app (iso.inv (map_right_id L R)) X) = 𝟙 :=
  Eq.refl (comma_morphism.left (nat_trans.app (iso.inv (map_right_id L R)) X))

/-- The functor `comma L R₁ ⥤ comma L R₃` induced by the composition of the natural transformations
    `r : R₁ ⟶ R₂` and `r' : R₂ ⟶ R₃` is naturally isomorphic to the composition of the functors
    induced by these natural transformations. -/
@[simp] theorem map_right_comp_inv_app_right {A : Type u₁} [category A] {B : Type u₂} [category B] {T : Type u₃} [category T] (L : A ⥤ T) {R₁ : B ⥤ T} {R₂ : B ⥤ T} {R₃ : B ⥤ T} (r : R₁ ⟶ R₂) (r' : R₂ ⟶ R₃) (X : comma L R₁) : comma_morphism.right (nat_trans.app (iso.inv (map_right_comp L r r')) X) = 𝟙 :=
  Eq.refl (comma_morphism.right (nat_trans.app (iso.inv (map_right_comp L r r')) X))


/-
Copyright (c) 2018 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton
-/

import analysis.topology.continuity tactic.tidy

open set

universes u v w

section locally_compact

-- There are various definitions of "locally compact space" in the
-- literature, which agree for Hausdorff spaces but not in general.
-- This one is the precise condition on X needed for the evaluation
-- map C(X, Y) × X → Y to be continuous for all Y when C(X, Y) is
-- given the compact-open topology.
class locally_compact_space (α : Type u) [topological_space α] :=
(local_compact_nhds : ∀ (x : α) (n ∈ (nhds x).sets), ∃ s ∈ (nhds x).sets, s ⊆ n ∧ compact s)

variables {α : Type u} [topological_space α]

lemma locally_compact_of_compact_nhds [t2_space α]
  (h : ∀ x : α, ∃ s, s ∈ (nhds x).sets ∧ compact s) :
  locally_compact_space α :=
⟨assume x n hn,
  let ⟨u, un, uo, xu⟩ := mem_nhds_sets_iff.mp hn in
  let ⟨k, kx, kc⟩ := h x in
  -- K is compact but not necessarily contained in N.
  -- K \ U is again compact and doesn't contain x, so
  -- we may find open sets V, W separating x from K \ U.
  -- Then K \ W is a compact neighborhood of x contained in U.
  let ⟨v, w, vo, wo, xv, kuw, vw⟩ :=
    compact_compact_separated compact_singleton (compact_diff kc uo)
      (by rw [singleton_inter_eq_empty]; exact λ h, h.2 xu) in
  have wn : -w ∈ (nhds x).sets, from
   mem_nhds_sets_iff.mpr
     ⟨v, subset_compl_iff_disjoint.mpr vw, vo, singleton_subset_iff.mp xv⟩,
  ⟨k - w,
   filter.inter_mem_sets kx wn,
   subset.trans (diff_subset_comm.mp kuw) un,
   compact_diff kc wo⟩⟩

lemma locally_compact_of_compact [t2_space α] (h : compact (univ : set α)) :
  locally_compact_space α :=
locally_compact_of_compact_nhds (assume x, ⟨univ, mem_nhds_sets is_open_univ trivial, h⟩)

end locally_compact

section compact_open
variables {α : Type u} {β : Type v} {γ : Type w}
variables [topological_space α] [topological_space β] [topological_space γ]

local notation `C(` α `, ` β `)` := subtype (continuous : set (α → β))

def compact_open_gen {s : set α} (hs : compact s) {u : set β} (hu : is_open u) : set C(α,β) :=
{f | f.val '' s ⊆ u}

-- The compact-open topology on the space of continuous maps α → β.
def compact_open : topological_space C(α, β) :=
topological_space.generate_from
  {m | ∃ (s : set α) (hs : compact s) (u : set β) (hu : is_open u), m = compact_open_gen hs hu}

local attribute [instance] compact_open

private lemma is_open_gen {s : set α} (hs : compact s) {u : set β} (hu : is_open u) :
  is_open (compact_open_gen hs hu) :=
topological_space.generate_open.basic _ (by dsimp [mem_set_of_eq]; tauto)

section functorial

variables {g : β → γ} (hg : continuous g)

def continuous_map.induced : C(α, β) → C(α, γ) :=
λ f, ⟨g ∘ f, f.property.comp hg⟩

private lemma preimage_gen {s : set α} (hs : compact s) {u : set γ} (hu : is_open u) :
  continuous_map.induced hg ⁻¹' (compact_open_gen hs hu) = compact_open_gen hs (hg _ hu) :=
begin
  ext ⟨f, _⟩,
  change g ∘ f '' s ⊆ u ↔ f '' s ⊆ g ⁻¹' u,
  rw [image_comp, image_subset_iff]
end

-- C(α, -) is a functor.
lemma continuous_induced : continuous (continuous_map.induced hg : C(α, β) → C(α, γ)) :=
continuous_generated_from $ assume m ⟨s, hs, u, hu, hm⟩,
  by rw [hm, preimage_gen]; apply is_open_gen

end functorial

section ev

variables (α β)
def ev : C(α, β) × α → β := λ p, p.1.val p.2

variables {α β}
-- The evaluation map C(α, β) × α → β is continuous if α is locally compact.
lemma continuous_ev [locally_compact_space α] : continuous (ev α β) :=
continuous_iff_tendsto.mpr $ assume ⟨f, x⟩ n hn,
  let ⟨v, vn, vo, fxv⟩ := mem_nhds_sets_iff.mp hn in
  have v ∈ (nhds (f.val x)).sets, from mem_nhds_sets vo fxv,
  let ⟨s, hs, sv, sc⟩ :=
    locally_compact_space.local_compact_nhds x (f.val ⁻¹' v)
      (f.property.tendsto x this) in
  let ⟨u, us, uo, xu⟩ := mem_nhds_sets_iff.mp hs in
  show (ev α β) ⁻¹' n ∈ (nhds (f, x)).sets, from
  let w := set.prod (compact_open_gen sc vo) u in
  have w ⊆ ev α β ⁻¹' n, from assume ⟨f', x'⟩ ⟨hf', hx'⟩, calc
    f'.val x' ∈ f'.val '' s  : mem_image_of_mem f'.val (us hx')
    ...       ⊆ v            : hf'
    ...       ⊆ n            : vn,
  have is_open w, from is_open_prod (is_open_gen _ _) uo,
  have (f, x) ∈ w, from ⟨image_subset_iff.mpr sv, xu⟩,
  mem_nhds_sets_iff.mpr ⟨w, by assumption, by assumption, by assumption⟩

end ev

section coev

variables (α β)
def coev : β → C(α, β × α) :=
λ b, ⟨λ a, (b, a), continuous.prod_mk continuous_const continuous_id⟩

variables {α β}
lemma image_coev {y : β} (s : set α) : (coev α β y).val '' s = set.prod {y} s := by tidy

-- The coevaluation map β → C(α, β × α) is continuous (always).
lemma continuous_coev : continuous (coev α β) :=
continuous_generated_from $ begin
  rintros _ ⟨s, sc, u, uo, rfl⟩,
  rw is_open_iff_forall_mem_open,
  intros y hy,
  change (coev α β y).val '' s ⊆ u at hy,
  rw image_coev s at hy,
  rcases generalized_tube_lemma compact_singleton sc uo hy
    with ⟨v, w, vo, wo, yv, sw, vwu⟩,
  refine ⟨v, _, vo, singleton_subset_iff.mp yv⟩,
  intros y' hy',
  change (coev α β y').val '' s ⊆ u,
  rw image_coev s,
  exact subset.trans (prod_mono (singleton_subset_iff.mpr hy') sw) vwu
end

end coev

end compact_open


import combinatorics.simple_graph.coloring

--OUTPUT 1
theorem bipartite_is_two_colorable (G : Type*) [graph G] : bipartite G ↔ two_colorable G :=
begin
  have h1 : ∀ A B : set G, edges A B → bipartite G → two_colorable G, from
  begin
    assume (A : set G) (B : set G),
    assume (h1 : edges A B),
    assume (h2 : bipartite G),
    have h3 : ∀ v : G, (v ∈ A) ∨ v ∈ B, from by auto [h2, defn.bipartite],
    have h4 : ∀ v u : G, v ≠ u → (v ∈ A ∧ u ∈ A) ∨ (v ∈ B ∧ u ∈ B) → false, from
    begin
      assume (v : G) (u : G),
      assume h4 : v ≠ u,
      assume h5 : v ∈ A ∧ u ∈ A ∨ v ∈ B ∧ u ∈ B,
      have h6 : (v ∈ A ∧ u ∈ A) → false, from by auto [h1, h5, h4],
      have h7 : (v ∈ B ∧ u ∈ B) → false, from by auto [h1, h5, h4],
      show false, from by auto [h5, h6, h7],
    end,
    have h5 : ∀ v u : G, (v ≠ u ∧ v ∈ A ∧ u ∈ A) → false, from by auto [h4],
    have h6 : ∀ v u : G, (v ≠ u ∧ v ∈ B ∧ u ∈ B) → false, from by auto [h4],
    have h7 : ∀ v : G, (v ∈ A) → v ≠ u → u ∈ B, from
    begin
      assume (v : G) (h7 : v ∈ A),
      assume h8 : v ≠ u,
      show u ∈ B, from or.elim (h3 v)
      begin
        assume h9 : v ∈ A,
        show u ∈ B, from by auto [h5, h7, h9, h8],
      end
      begin
        assume h10 : v ∈ B,
        have h11 : v ≠ u ∧ v ∈ B ∧ u ∈ B, from by auto [h8, h10],
        have h12 : false, from by auto [h6, h11],
        show u ∈ B, from by auto [h12, false.elim],
      end,
    end,
    have h8 : ∀ v : G, (v ∈ B) → v ≠ u → u ∈ A, from
    begin
      assume (v : G) (h8 : v ∈ B),
      assume h9 : v ≠ u,
      show u ∈ A, from or.elim (h3 v)
      begin
        assume h10 : v ∈ A,
        have h11 : v ≠ u ∧ v ∈ A ∧ u ∈ A, from by auto [h9, h10],
        have h12 : false, from by auto [h5, h11],
        show u ∈ A, from by auto [h12, false.elim],
      end
      begin
        assume h13 : v ∈ B,
        show u ∈ A, from by auto [h7, h8, h13, h9],
      end,
    end,
    have h9 : ∀ v : G, (v ∈ A) → v ≠ u → u ∈ B, from by auto [h7],
    have h10 : ∀ v : G, (v ∈ B) → v ≠ u → u ∈ A, from by auto [h8],
    show two_colorable G, from by auto [h3, h9, h10, defn.two_colorable],
  end,

  have h2 : ∀ C : G → color, coloring C → bipartite G → two_colorable G, from
  begin
    assume (C : G → color),
    assume (h2 : is_coloring C),
    assume (h3 : bipartite G),
    have h4 : ∃ A B : set G, C = coloring_of A B, from by auto [h3, defn.bipartite, defn.two_colorable, h2],
    show two_colorable G, from
    begin
      assume A : set G,
      assume B : set G,
      assume : edges A B,
      assume h4 : ∀ v : G, C v = red ∧ v ∈ A ∨ C v = blue ∧ v ∈ B,
      have h5 : ∃! x : G, x ∈ A, from by auto [h4],
      have h6 : ∃! x : G, x ∈ B, from by auto [h4],
      have h7 : ∃! x : G, C x = red, from by auto [h4, h5],
      have h8 : ∃! x : G, C x = blue, from by auto [h4, h6],
      have h9 : ∃ A B : set G, C = coloring_of A B, from by auto [use A, use B, h4],
      have h10 : coloring_of A B = C, from (and.left h9),
      show two_colorable G, from by auto [h7, h8, h10, h2, is_coloring_of],
    end,
  end,

  show bipartite G ↔ two_colorable G, from by auto [h1, h2, defn.two_colorable, defn.bipartite]
end

--OUTPUT 2
theorem bipartite_iff_two_colorable {G : graph} : (is_bipartite G) ↔ (is_two_colorable G) :=
begin
  apply iff.intro,
  intro H,
  show ∃ (c : coloring G), is_valid_coloring c ∧ num_colors c = 2,
  by auto [bipartite.two_coloring, H],

  intro H,
  cases H with c h,
  cases h with h1 h2,
  show is_bipartite G,
  by auto [two_coloring, h1, h2],
end

--OUTPUT 3
theorem bipartite_2colorable (g : Type*) [decidable_eq g] [fintype g] : ∀ ⦃e : g → g → Prop⦄ [decidable_rel e] (h1 : ∀x y, e x y → e y x) (h2 : ∀x, e x x ↔ false), (∀x, decidable (∃y, e x y)) → (∀a b, e a b → a ≠ b) → (∀x, ¬e x x) → (∀a b c, e a b → e b c → e a c ↔ false) → (∃x, ∀y, ¬e x y) → (∃A B, A ≠ B ∧ ∀a b, e a b → (a ∈ A → b ∈ B) ∧ (a ∈ B → b ∈ A)) ↔ ∀x y : g, x ≠ y → ¬e x y → (e x y ↔ false) :=
begin
  assume e h1 h2 h3 h4 h5 h6 h7 h8 h9,
  show ∀x y, x ≠ y → ¬e x y → e x y ↔ false, from
    assume x y h10 h11, by auto [h7],
end

--OUTPUT 4
theorem bipartite_is_two_colorable (G : Type*) [graph G] :
  (∀ ν : G, (ν ∈ v G) ∨ (ν ∈ v G)) ↔ G.is_bipartite :=
begin
    have h1 : G.is_bipartite ↔
  (∃ A B : set G, A ∩ B = ∅ ∧ A ∪ B = v G ∧ (∀ (a b : G), (e G) a b → a ∈ A ∨ b ∈ A)), from by auto,

  have h2 : (∀ ν : G, ν ∈ v G ∨ ν ∈ v G) ↔
  (∃ A : set G, A ⊆ v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)), from by auto,

  have h21 : (∃ A : set G, A ⊆ v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)) ↔
  (∃ A : set G, A ⊆ v G ∧ A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)), from by auto,

  have h22 : (∃ A : set G, A ⊆ v G ∧ A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)) ↔
  (∃ A : set G, A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)), from by auto,

  have h23 : (∃ A : set G, A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)) ↔
  (∃ A : set G, A ⊆ v G ∧ A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)), from by auto,

  have h24 : (∃ A : set G, A ⊆ v G ∧ A = v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)) ↔
  (∃ A : set G, A ⊆ v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)), from by auto,

  have h25 : (∃ A : set G, A ⊆ v G ∧ (∀ ν : G, ν ∈ v G → ν ∈ A)) ↔
  (∀ ν : G, ν ∈ v G ∨ ν ∈ v G), from by auto,

  have h3 : (∃ A B : set G, A ∩ B = ∅ ∧ A ∪ B = v G ∧ (∀ (a b : G), (e G) a b → a ∈ A ∨ b ∈ A)) ↔
  (∃ A B : set G, A ⊆ v G ∧ B ⊆ v G ∧ A ∪ B = v G ∧ (∀ (a b : G), (e G) a b → a ∈ A ∨ b ∈ A)), from by auto,

  have h4 : (∃ A B : set G, A ⊆ v G ∧ B ⊆ v G ∧ A ∪ B = v G ∧ (∀ (a b : G), (e G) a b → a ∈ A ∨ b ∈ A)) ↔
  (∃ A B : set G, A ∩ B = ∅ ∧ A ∪ B = v G ∧ (∀ (a b : G), (e G) a b → a ∈ A ∨ b ∈ A)), from by auto,

  show G.is_bipartite ↔ (∀ ν : G, ν ∈ v G ∨ ν ∈ v G), from by auto [h1, h2, h21, h22, h23, h24, h25, h3, h4],
end

--OUTPUT 5
theorem two_colorable_bipartite (G : Type*) [graph G] : (2-colorable G) ↔ (bipartite G) :=
begin
  show (2-colorable G) ↔ (bipartite G), by split;
  show (2-colorable G) → (bipartite G), from by {intro n, exact n},
  show (bipartite G) → (2-colorable G), from by {intro n, exact n}
end

--OUTPUT 6
theorem graph_twocolorable_iff_bipartite {V : Type*} [decidable_eq V] (G : simple_graph V) : twocolorable G ↔ bipartite G :=
begin
  have h1 : G.edges = (∅ : rel V), from by auto [fintype.card_empty, card_eq_zero],

  have h2 : twocolorable G ↔ twocolor G G.verts = (∅ : rel V), from by auto [twocolor_def, rres],
  have h3 : twocolor G G.verts = (∅ : rel V) ↔ bipartite G, from by auto using [subset_of_empty_rres, empty_rres_of_subset, twocolor_subset_rres, twocolor_subset_verts],

  show twocolorable G ↔ bipartite G, from by auto [h1, h2, h3],
end

--OUTPUT 7
theorem two_color {G : Type*} [graph G] : two_colorable G ↔ bipartite G :=
begin
  split,
  {
    intro h,
    cases h with f hf,
    have h1:∀ (u v:vertex G), (u∉adjacent v) ∨ (v∉adjacent u), from begin
      have hf_inv : ∀ (u v:vertex G), (u ∈ adjacent v) ↔ f u ≠ f v, from begin
        intro,
        intro,
        apply iff.symm,
        apply iff.intro,
        {
          intro h1,
          simp at hf,
          by_cases h2: (vertex v = vertex u) ∨ (vertex v ≠ vertex u), simp [h2],
          {
            rw h2 at h1,
            rw h1 at hf,
            simp at hf,
            have h3: vertex v = vertex u, from by auto [ne.def, eq.symm],
            contradiction,
          },
          {
            rw h2 at h1,
            simp at hf,
            have h3: (vertex u ≠ vertex v), from by auto [ne.def],
            have h4: (vertex v ≠ vertex u), from by auto [ne.symm],
            have h5: (vertex v ∉ adjacent u), from by auto [hf, ne.def],
            have h6: (vertex u ∉ adjacent v), from by auto [ne.symm],
            show (vertex v ≠ vertex u) ↔ (vertex u ≠ vertex v), from by auto [ne.def],
          },
        },
        {
          intro h1,
          simp at hf,
          by_cases h2: (vertex v = vertex u) ∨ (vertex v ≠ vertex u), simp [h2],
          {
            rw h2 at h1,
            rw h1 at hf,
            simp at hf,
            have h3: vertex v = vertex u, from by auto [ne.def, eq.symm],
            contradiction,
          },
          {
            rw h2 at h1,
            simp at hf,
            have h3: (vertex u ≠ vertex v), from by auto [ne.def],
            have h4: (vertex v ≠ vertex u), from by auto [ne.symm],
            have h5: (vertex v ∉ adjacent u), from by auto [hf, ne.def],
            have h6: (vertex u ∉ adjacent v), from by auto [ne.symm],
            show (vertex v ≠ vertex u) ↔ (vertex u ≠ vertex v), from by auto [ne.def],
          },
        },
      end,
      rw hf_inv,
      intro,
      intro,
      rw hf_inv u v,
      contrapose! hf,
      simp,
    end,
    have h2:∀ (u:vertex G), (∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (¬ ∀ v : vertex G, v∉adjacent u), from begin
      intro,
      intro,
      have h: ((∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (¬ ∀ v : vertex G, v∉adjacent u)) ↔ ¬ ((∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (∀ v : vertex G, v∉adjacent u)), from by auto [not_not_iff, imp.symm],
      sorry,
    end,
    have h3:∀ (u:vertex G), (∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (¬ ∀ v : vertex G, v∉adjacent u), from begin
      intro,
      intro,
      have h3, {
        have h3:∀ (u v:vertex G), ∃! x : vertex G, x in adjacent u, from begin
          rw ←(adjacent_symm (u:vertex G)),
          apply adjacent_unique,
        end,
        cases h3 u v with x hx,
        use x,
        rw hx,
        apply exists_unique.exists,
        use x,
        split,
        {
          assume h1,
          simp at hx,
          assume h2,
          simp at hx,
          from h2,
        },
        {
          assume h1,
          assume h2,
          simp at hx,
          rw hx at h_1,
          rw hx at h2,
          assume h3,
          by_contradiction,
          have h4, {
            assume h4,
            rw h3 at h2,
            simp at h2,
            have h5, {
              simp [h2] at h1,
              assumption,
            },
            have h6, {
              simp [h2] at h_1,
              assumption,
            },
            from h6,
          },
          from h4,
        },
      },
      have h4:¬∀ (v:vertex G), v∉adjacent (u:vertex G), from begin
        show ¬∀ (v:vertex G), v∉adjacent (u:vertex G) ↔ (∃ (v:vertex G), v∈adjacent u ∧ f v ≠ f u), from by auto [exists_prop, not_not_iff],
        rw h4,
        assumption,
      end,
      have h5:∀ (v:vertex G), v∈adjacent (u:vertex G) ↔ ¬(v∉adjacent u), from begin
        assume v,
        have h5:¬∀ (v:vertex G), v∉adjacent (u:vertex G) ↔ (∃ (v:vertex G), v∈adjacent u ∧ f v ≠ f u), from by auto [exists_prop],
        rw ←h5,
        assumption,
      end,
      show (∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (¬ ∀ v : vertex G, v∉adjacent u), from begin
        assume h1,
        rw h5 u,
        simp at h1,
        assume h2,
        simp at h1,
        from h1,
      end,
    end,
    assume h1,
    cases h1 with v hv,
    cases hv with hv1 hv2,
    have h2: ¬∀ v : vertex G, v∉adjacent (u:vertex G), from (begin
      have hx:∃! (x:vertex G), x in adjacent u, from begin
        apply (adjacent_unique u),
      end,
      show ¬∀ v : vertex G, v∉adjacent (u:vertex G) ↔ (∃ (v:vertex G), v∈adjacent u ∧ f v ≠ f u), from by auto [exists_prop, not_not_iff],
      rw ←(adjacent_unique u) at hv,
      rw ←hv,
      simp at h2,
      assume h4,
      simp at h2,
      from h4,
    end),
    have h3:∀ v : vertex G, v∉adjacent (u:vertex G) ↔ ¬(v∈adjacent u), from begin
      intro,
      simp [h2],
    end,
    show (∃ v : vertex G, v∈adjacent u ∧ f v ≠ f u) → (¬ ∀ v : vertex G, v∉adjacent u), from begin
      assume h2,
      simp at h2,
      assume h3,
      simp at h2,
      from h3,
    end,
  },

  {
    assume h,
end --Needs more than 2000 tokens!

--OUTPUT 8
theorem two_colorable_iff_bipartite (G : Type*) [Graph G] : (∀ v : G, ∃! c : bool, c = true ∨ c = false) ↔ ∀ e : Edge G, (∃ v1 v2 : G, v1 ≠ v2) :=
begin
  unfold Graph,
  have h1 : ∀ v : G, ∃! c : bool, c = true ∨ c = false ↔ (∃! v1 : G, v1 ≠ v) ∧ (∃! v2 : G, v2 ≠ v), from by auto [exists_unique.unique, exists_unique.exists],
  have h2 : (∃! v1 : G, v1 ≠ v) ↔ (∃! v2 : G, v2 ≠ v), from by auto [exists_unique.unique, exists_unique.exists],
  have h3 : (∃! v1 : G, v1 ≠ v) ∧ (∃! v2 : G, v2 ≠ v) ↔ (∃! v2 : G, v2 ≠ v) ∧ (∃! v1 : G, v1 ≠ v), from by auto [and.comm],
  have h4 : ∀ v : G, ∃! c : bool, c = true ∨ c = false ↔ (∃! v2 : G, v2 ≠ v) ∧ (∃! v1 : G, v1 ≠ v), from by auto [h1, h2, h3],
  have h5 : ∀ v : G, (∃! c : bool, c = true ∨ c = false) ↔ (∃! v2 : G, v2 ≠ v) ∧ (∃! v1 : G, v1 ≠ v), from by auto [h1, h2, h3, h4],
  have h6 : ∀ v1 v2 : G, (v1 ≠ v2) ↔ (∃ w : G, w ≠ v1 ∧ w ≠ v2), from by auto [exists_unique.exists, exists_unique.unique, ne.def],
  have h7 : (∃ w : G, w ≠ v1 ∧ w ≠ v2) ↔ (∃ w : G, w ≠ v1) ∧ (∃ w : G, w ≠ v2), from by auto [exists_and_distrib_right, and_comm],
  have h8 : (∃! v1 : G, v1 ≠ v) ↔ (∃ w : G, w ≠ v), from by auto [exists_unique.exists, exists_unique.unique, ne.def],
  have h9 : ∀ v : G, (∃! v1 : G, v1 ≠ v) ↔ (∃ w : G, w ≠ v), from by auto [h8],
  have h10 : (∃! v1 : G, v1 ≠ v) ∧ (∃! v2 : G, v2 ≠ v) ↔ (∃ w1 : G, w1 ≠ v) ∧ (∃ w2 : G, w2 ≠ v), from by auto [h8, h9, and_comm],
  have h11 : (∃ w1 : G, w1 ≠ v) ∧ (∃ w2 : G, w2 ≠ v) ↔ ((∃ w1 : G, w1 ≠ v) ∧ (∃ w2 : G, w2 ≠ v)) ∧ ((∃ w1 : G, w1 ≠ v) ∧ (∃ w2 : G, w2 ≠ v)), from by auto [and.comm],
  show ∀ v : G, ∃! c : bool, c = true ∨ c = false ↔ ∀ e : Edge G, (∃ v1 v2 : G, v1 ≠ v2), from by auto [h5, h6, h7, h8, h9, h10, h11]
end

--OUTPUT 9
theorem bipartite_two_colorable (G : Type*) [graph G] : two_colorable G ↔ bipartite G :=
begin
  split,
  intro h1,
  have h2 : ∃ R B : set G, two_coloring G R B ∧ graph.subset_disjoint G R B, from h1.elim (
    assume R B,
    assume h2 : two_coloring G R B,
    have h3 : graph.subset_disjoint G R B, from h2.elim (
      assume A B,
      assume h3 : A = R ∧ B = B,
      have h4 : A ⊆ (vertices G), from h3.left.elim (
        assume A R,
        assume h3 : A = R,
        have h4 : A ⊆ vertices G, from h3.symm ▸ set.subset.refl _,
        have h5 : graph.subset_disjoint G R B, from graph.subset_disjoint.intro (h4.left) (by auto [set.subset_of_mem_vertices, set.subset.trans]),
        h5
      ),
      have h5 : graph.subset_disjoint G R B, from graph.subset_disjoint.intro (h4.left) (by auto [set.subset_of_mem_vertices, set.subset.trans]),
      h5
    ),
    show ∃ R B : set G, two_coloring G R B ∧ graph.subset_disjoint G R B, from ⟨R, B, h2, h3⟩
  ),
  have h3 : ∃ R B : set G, two_coloring G R B, from h2.elim (
    assume R B,
    assume h3 : two_coloring G R B ∧ graph.subset_disjoint G R B,
    have h4 : two_coloring G R B, from h3.left,
    show ∃ R B : set G, two_coloring G R B, from ⟨R, B⟩
  ),
  have h4 : ∃ R B : set G, graph.subset_disjoint G R B, from h2.elim (
    assume R B,
    assume h4 : two_coloring G R B ∧ graph.subset_disjoint G R B,
    have h5 : graph.subset_disjoint G R B, from h4.right,
    show ∃ R B : set G, graph.subset_disjoint G R B, from ⟨R, B⟩
  ),
  have h5 : bipartite G, from h4.elim (
    assume R B,
    assume h5 : graph.subset_disjoint G R B,
    show bipartite G, from bipartite.intro ⟨R, B⟩ h5
  ),
  show two_colorable G ↔ bipartite G, from ⟨λh6,h5,λh7,h3⟩,
end

--OUTPUT 10
theorem two_colorable_iff_bipartite (G : Type*) [graph G] :
  G ⊆ ℤ^2 ↔ ∀ (f : G → bool), (∀ (x y : G), x ≠ y → ϑ(x, y) → f(x) ≠ f(y)) :=
begin
  have h0 : G ⊆ ℤ^2 → ∀ (f : G → bool), (∀ (x y : G), x ≠ y → ϑ(x, y) → f(x) ≠ f(y)),
  {
    assume h : G ⊆ ℤ^2,
    assume (f : G → bool),
    assume x : G,
    assume y : G,
    assume (h1 : x ≠ y) (h2 : ϑ(x, y)),
    have h3 : ∃! x1 : G × G, x1.1 = x ∧ x1.2 = y, from by auto [h2, graph.ϑ_iff_edge],
    have h4 : (x, y) ∈ G × G, from by auto [exists_unique.unique, h3, classical.some_spec, exists_unique.exists],
    have h5 : (x, y) ∈ ℤ^2, from by auto [h, set.mem_of_mem_prod, h4],
    have h6 : (x, y) ∈ ℤ × ℤ, from by auto [h5, set.mem_image_of_mem_prod, set.mem_sigma],
    have h7 : (x, y) ∈ ℕ × ℕ, from by auto [h6, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image, set.mem_image],
    have h8 : (x, y) ∈ (ℕ × ℕ) ∖ {(0, 0)}, from by auto [h7, set.mem_diff, set.mem_singleton_iff.mpr],
    have h9 : 0 ≠ 0, from by auto [exists_unique.unique, h3, classical.some_spec, exists_unique.exists],
    show f x ≠ f y, from by auto [finset.mem_compl_iff, finset.mem_prod, finset.mem_singleton_iff.mp, finset.mem_compl_iff, finset.mem_prod, finset.mem_singleton_iff.mp],
  },
  have h1 : ∀ (f : G → bool), (∀ (x y : G), x ≠ y → ϑ(x, y) → f(x) ≠ f(y)) → G ⊆ ℤ^2,
  {
    assume (f : G → bool),
    assume (h0 : ∀ (x y : G), x ≠ y → ϑ(x, y) → f(x) ≠ f(y)),
    have h1 : ∀ (a : G), a ∈ ℤ^2,
    {
      assume a : G,
      show a ∈ ℤ^2, from by auto [graph.ϑ_iff_edge, f.two_coloring, h0] using [exists_unique.unique, exists_unique.exists],
    },
    show G ⊆ ℤ^2, from by auto [h1],
  },
  show G ⊆ ℤ^2 ↔ ∀ (f : G → bool), (∀ (x y : G), x ≠ y → ϑ(x, y) → f(x) ≠ f(y)), from by auto [h0, h1],
end

/- FEW SHOT PROMPTS TO CODEX(START)
/--`theorem`
Power Set is Closed under Intersection
Let $S$ be a set.

Let $\powerset S$ be the power set of $S$.

Then:
:$\forall A, B \in \powerset S: A \cap B \in \powerset S$
`proof`
Let $A, B \in \powerset S$.

Then by the definition of power set, $A \subseteq S$ and $B \subseteq S$.

From Intersection is Subset we have that $A \cap B \subseteq A$.

It follows from Subset Relation is Transitive that $A \cap B \subseteq S$.

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from by auto [set.subset_of_mem_powerset, set.subset_of_mem_powerset],
  have h2 : (A ∩ B) ⊆ A, from by auto [set.inter_subset_left],
  have h3 : (A ∩ B) ⊆ S, from by auto [set.subset.trans],
  show (A ∩ B) ∈  𝒫 S, from by auto [set.mem_powerset],
end

/--`theorem`
Square of Sum
 :$\forall x, y \in \R: \paren {x + y}^2 = x^2 + 2 x y + y^2$
`proof`
Follows from the distribution of multiplication over addition:

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  calc (x + y)^2 = (x+y)*(x+y) : by auto [sq]
  ... = x*(x+y) + y*(x+y) : by auto [add_mul]
  ... = x*x + x*y + y*x + y*y : by auto [mul_comm, add_mul] using [ring]
  ... = x^2 + 2*x*y + y^2 : by auto [sq, mul_comm] using [ring]
end

/--`theorem`
Identity of Group is Unique
Let $\struct {G, \circ}$ be a group. Then there is a unique identity element $e \in G$.
`proof`
From Group has Latin Square Property, there exists a unique $x \in G$ such that:
:$a x = b$

and there exists a unique $y \in G$ such that:
:$y a = b$

Setting $b = a$, this becomes:

There exists a unique $x \in G$ such that:
:$a x = a$

and there exists a unique $y \in G$ such that:
:$y a = a$

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from by auto using [use (a⁻¹ * b)],
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from by auto using [use b * a⁻¹],

  have h3 : ∀ a : G, ∃! x : G, a * x = a, from by auto [h1],
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from by auto [h2],

  have h5 : ∀ a : G, classical.some (h3 a).exists = (1 : G), from by auto [exists_unique.unique, h3, classical.some_spec, exists_unique.exists, mul_one],
  have h6 : ∀ a : G, classical.some (h4 a).exists = (1 : G), from by auto [exists_unique.unique, h4, classical.some_spec, exists_unique.exists, one_mul],

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by auto [h3, h4, exists_unique.unique, classical.some_spec, exists_unique.exists] using [use (1 : G)],
end

/--`theorem`
Bipartite Graph is two colorable
Let $G$ be a graph. Then $G$ is 2-colorable if and only if $G$ is bipartite.
`proof`
Let $G$ be a 2-colorable graph, which means we can color every vertex either red or blue, and no edge will have both endpoints colored the same color. Let $A$ denote the subset of vertices colored red, and let $B$ denote the subset of vertices colored blue. Since all vertices of $A$ are red, there are no edges within $A$, and similarly for $B$. This implies that every edge has one endpoint in $A$ and the other in $B$, which means $G$ is bipartite.

Conversely, suppose $G$ is bipartite, that is, we can partition the vertices into two subsets $V_{1}, V_{2}$ every edge has one endpoint in $V_{1}$ and the other in $V_{2}$. Then coloring every vertex of $V_{1}$ red and every vertex of $V_{2}$ blue yields a valid coloring, so $G$ is 2-colorable.

QED

-/
theorem
FEW SHOT PROMPTS TO CODEX(END)-/


/-
Copyright (c) 2020 Eric Wieser. All rights reserved.
Released under MIT license as described in the file LICENSE.
Authors: Eric Wieser
-/
import geometric_algebra.from_mathlib.basic
import linear_algebra.clifford_algebra.conjugation
import algebra.module.opposites

variables {R : Type*} [comm_ring R]
variables {M : Type*} [add_comm_group M] [module R M]
variables {Q : quadratic_form R M}
/-!
# Conjugations

This file contains additional lemmas about `clifford_algebra.reverse` and `clifford_algebra.involute`.

As more and more goes into Mathlib, this file will become smaller and spaller.
The links above will take you to the collection of mathlib theorems.
-/

namespace clifford_algebra

section reverse
  open mul_opposite

  /-- helper lemma for expanding the sign of reverse -/
  lemma reverse_prod_sign_aux (n : ℕ) :
    (-1 : R)^((n + 1)*(n + 1 + 1)/2) = (-1 : R)^(n*(n + 1)/2) * (-1 : R)^(n + 1) :=
  begin
    -- work with just the exponents
    rw ←pow_add,
    conv_rhs { rw neg_one_pow_eq_pow_mod_two },
    conv_lhs { rw neg_one_pow_eq_pow_mod_two },
    congr' 1,
    -- work through the ugly nat proof
    rw [add_mul, one_mul, mul_add _ (n + 1), mul_one, add_assoc, add_assoc n 1 1, ←add_assoc n n, ←mul_two, ←mul_two],
    have : 0 < 2 := by linarith,
    rw [nat.add_div_eq_of_add_mod_lt _, nat.add_div_eq_of_add_mod_lt _, nat.mul_div_cancel _ this, nat.mul_div_cancel _ this],
    -- extra goals created by `add_div_eq_of_add_mod_lt`
    { rw [nat.mul_mod_left, nat.mul_mod_left, zero_add],
      exact this },
    { rw [nat.add_mod (n * 2), nat.mul_mod_left, nat.mul_mod_left, add_zero, nat.zero_mod, add_zero],
      exact nat.mod_lt _ this },
  end

  /-- TODO: this needs an assumption that the vectors are othogonal -/
  lemma reverse_prod_map_sign (l : list M):
    reverse (l.map $ ι Q).prod = ((-1 : R)^(l.length*(l.length + 1)/2)) • (l.map $ ι Q).prod :=
  begin
    induction l with x xs hxs,
    { simp },
    simp [hxs, reverse_prod_sign_aux, mul_smul],
    congr,
    sorry, -- this needs to be an assumption
  end

end reverse

end clifford_algebra


import for_mathlib.Profinite.extend

import Lbar.pseudo_normed_group
import pseudo_normed_group.category

noncomputable theory

open_locale nnreal

variables (r' : ℝ≥0) [fact (0 < r')]
variables {S₁ S₂ : Type*} [fintype S₁] [fintype S₂]

namespace Lbar

open pseudo_normed_group (filtration level)
open category_theory category_theory.limits

lemma map_zero (f : S₁ → S₂) : map f (0 : Lbar r' S₁) = 0 :=
by { ext, simp only [coe_zero, map_to_fun, pi.zero_apply, finset.sum_const_zero], }

lemma map_add (f : S₁ → S₂) (x y : Lbar r' S₁) : map f (x + y) = map f x + map f y :=
by { ext s n, simp only [map_to_fun, pi.add_apply, coe_add, finset.sum_add_distrib], }

lemma map_strict (f : S₁ → S₂) ⦃c : ℝ≥0⦄ ⦃x : (Lbar r' S₁)⦄
  (hx : x ∈ filtration (Lbar r' S₁) c) : map f x ∈ filtration (Lbar r' S₂) c :=
Lbar.nnnorm_map_le_of_nnnorm_le f _ hx

lemma map_continuous (f : S₁ → S₂) (c : ℝ≥0) :
  continuous (level (map f) (map_strict r' f) c) :=
begin
  let φ := (level (map f) (map_strict r' f) c),
  rw Lbar_le.continuous_iff,
  intros M,
  have : Lbar_le.truncate M ∘ φ = Lbar_bdd.map f ∘ Lbar_le.truncate M, { ext, refl },
  rw this,
  exact continuous_of_discrete_topology.comp Lbar_le.continuous_truncate,
end

lemma map_Tinv (f : S₁ → S₂) (x : Lbar r' S₁) :
  map f (Tinv x) = Tinv (map f x) :=
by ext s ⟨_|n⟩; simp only [map_to_fun, Tinv_zero, Tinv_succ, finset.sum_const_zero]

end Lbar

open Lbar

/-- `Fintype_Lbar r' S` is functorial in the finite type `S`. -/
@[simps] def Fintype_Lbar : Fintype ⥤ ProFiltPseuNormGrpWithTinv₁ r' :=
{ obj := λ S,
  { M := Lbar r' S,
    exhaustive' := λ x, ⟨∥x∥₊, by rw Lbar.mem_filtration_iff⟩ },
  map := λ S₁ S₂ f,
  { to_fun := map f,
    map_zero' := map_zero r' f,
    map_add' := map_add r' f,
    strict' := map_strict r' f,
    continuous' := map_continuous r' f,
    map_Tinv' := map_Tinv r' f },
  map_id' := by { intros, ext1 x, exact map_id x },
  map_comp' := by { intros X Y Z f g, ext1 x, exact map_comp f x g } }

namespace Lbar

open category_theory pseudo_normed_group category_theory.limits

example (X : Profinite) : has_limit (X.fintype_diagram ⋙ Fintype_Lbar r') :=
by apply_instance

/-- `Lbar r' S` extends to a functor in `S`, for profinite `S`. -/
def functor : Profinite ⥤ ProFiltPseuNormGrpWithTinv₁ r' :=
Profinite.extend (Fintype_Lbar r')

end Lbar