/-
Copyright (c) 2019 Neil Strickland. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Neil Strickland

! This file was ported from Lean 3 source module data.pnat.factors
! leanprover-community/mathlib commit 50832daea47b195a48b5b33b1c8b2162c48c3afc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.BigOperators.Multiset.Basic
import Mathbin.Data.Pnat.Prime
import Mathbin.Data.Nat.Factors
import Mathbin.Data.Multiset.Sort

/-!
# Prime factors of nonzero naturals

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

This file defines the factorization of a nonzero natural number `n` as a multiset of primes,
the multiplicity of `p` in this factors multiset being the p-adic valuation of `n`.

## Main declarations

* `prime_multiset`: Type of multisets of prime numbers.
* `factor_multiset n`: Multiset of prime factors of `n`.
-/

#print PrimeMultiset /-
/-- The type of multisets of prime numbers.  Unique factorization
 gives an equivalence between this set and ℕ+, as we will formalize
 below. -/
def PrimeMultiset :=
  Multiset Nat.Primes deriving Inhabited, CanonicallyOrderedAddMonoid, DistribLattice,
  SemilatticeSup, OrderBot, Sub, OrderedSub
#align prime_multiset PrimeMultiset
-/

namespace PrimeMultiset

-- `@[derive]` doesn't work for `meta` instances
unsafe instance : Repr PrimeMultiset := by delta PrimeMultiset <;> infer_instance

#print PrimeMultiset.ofPrime /-
/-- The multiset consisting of a single prime -/
def ofPrime (p : Nat.Primes) : PrimeMultiset :=
  ({p} : Multiset Nat.Primes)
#align prime_multiset.of_prime PrimeMultiset.ofPrime
-/

/- warning: prime_multiset.card_of_prime -> PrimeMultiset.card_ofPrime is a dubious translation:
lean 3 declaration is
  forall (p : Nat.Primes), Eq.{1} Nat (coeFn.{1, 1} (AddMonoidHom.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.orderedCancelAddCommMonoid.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.orderedCancelAddCommMonoid.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{0} Nat.Primes) -> Nat) (AddMonoidHom.hasCoeToFun.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.orderedCancelAddCommMonoid.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{0} Nat.Primes) (PrimeMultiset.ofPrime p)) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))
but is expected to have type
  forall (p : Nat.Primes), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{0} Nat.Primes) => Nat) (PrimeMultiset.ofPrime p)) (FunLike.coe.{1, 1, 1} (AddMonoidHom.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{0} Nat.Primes) (fun (_x : Multiset.{0} Nat.Primes) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{0} Nat.Primes) => Nat) _x) (AddHomClass.toFunLike.{0, 0, 0} (AddMonoidHom.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{0} Nat.Primes) Nat (AddZeroClass.toAdd.{0} (Multiset.{0} Nat.Primes) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes))))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{0, 0, 0} (AddMonoidHom.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{0, 0} (Multiset.{0} Nat.Primes) Nat (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat.Primes) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat.Primes) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat.Primes) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat.Primes) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat.Primes)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (Multiset.card.{0} Nat.Primes) (PrimeMultiset.ofPrime p)) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{0} Nat.Primes) => Nat) (PrimeMultiset.ofPrime p)) 1 (instOfNatNat 1))
Case conversion may be inaccurate. Consider using '#align prime_multiset.card_of_prime PrimeMultiset.card_ofPrimeₓ'. -/
theorem card_ofPrime (p : Nat.Primes) : Multiset.card (ofPrime p) = 1 :=
  rfl
#align prime_multiset.card_of_prime PrimeMultiset.card_ofPrime

#print PrimeMultiset.toNatMultiset /-
/-- We can forget the primality property and regard a multiset
 of primes as just a multiset of positive integers, or a multiset
 of natural numbers.  In the opposite direction, if we have a
 multiset of positive integers or natural numbers, together with
 a proof that all the elements are prime, then we can regard it
 as a multiset of primes.  The next block of results records
 obvious properties of these coercions.
-/
def toNatMultiset : PrimeMultiset → Multiset ℕ := fun v => v.map fun p => (p : ℕ)
#align prime_multiset.to_nat_multiset PrimeMultiset.toNatMultiset
-/

#print PrimeMultiset.coeNat /-
instance coeNat : Coe PrimeMultiset (Multiset ℕ) :=
  ⟨toNatMultiset⟩
#align prime_multiset.coe_nat PrimeMultiset.coeNat
-/

/- warning: prime_multiset.coe_nat_monoid_hom -> PrimeMultiset.coeNatMonoidHom is a dubious translation:
lean 3 declaration is
  AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.orderedCancelAddCommMonoid.{0} Nat))))))
but is expected to have type
  AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))
Case conversion may be inaccurate. Consider using '#align prime_multiset.coe_nat_monoid_hom PrimeMultiset.coeNatMonoidHomₓ'. -/
/-- `prime_multiset.coe`, the coercion from a multiset of primes to a multiset of
naturals, promoted to an `add_monoid_hom`. -/
def coeNatMonoidHom : PrimeMultiset →+ Multiset ℕ :=
  { Multiset.mapAddMonoidHom coe with toFun := coe }
#align prime_multiset.coe_nat_monoid_hom PrimeMultiset.coeNatMonoidHom

/- warning: prime_multiset.coe_coe_nat_monoid_hom -> PrimeMultiset.coe_coeNatMonoidHom is a dubious translation:
lean 3 declaration is
  Eq.{1} ((fun (_x : AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.orderedCancelAddCommMonoid.{0} Nat))))))) => PrimeMultiset -> (Multiset.{0} Nat)) PrimeMultiset.coeNatMonoidHom) (coeFn.{1, 1} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.orderedCancelAddCommMonoid.{0} Nat))))))) (fun (_x : AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.orderedCancelAddCommMonoid.{0} Nat))))))) => PrimeMultiset -> (Multiset.{0} Nat)) (AddMonoidHom.hasCoeToFun.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.orderedCancelAddCommMonoid.{0} Nat))))))) PrimeMultiset.coeNatMonoidHom) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PrimeMultiset (Multiset.{0} Nat) (HasLiftT.mk.{1, 1} PrimeMultiset (Multiset.{0} Nat) (CoeTCₓ.coe.{1, 1} PrimeMultiset (Multiset.{0} Nat) (coeBase.{1, 1} PrimeMultiset (Multiset.{0} Nat) PrimeMultiset.coeNat))))
but is expected to have type
  Eq.{1} (forall (a : PrimeMultiset), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : PrimeMultiset) => Multiset.{0} Nat) a) (FunLike.coe.{1, 1, 1} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))) PrimeMultiset (fun (_x : PrimeMultiset) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : PrimeMultiset) => Multiset.{0} Nat) _x) (AddHomClass.toFunLike.{0, 0, 0} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))) PrimeMultiset (Multiset.{0} Nat) (AddZeroClass.toAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))) (AddZeroClass.toAdd.{0} (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))) (AddMonoidHomClass.toAddHomClass.{0, 0, 0} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))) PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat)))))) (AddMonoidHom.addMonoidHomClass.{0, 0} PrimeMultiset (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))))) PrimeMultiset.coeNatMonoidHom) (Coe.coe.{1, 1} PrimeMultiset (Multiset.{0} Nat) PrimeMultiset.coeNat)
Case conversion may be inaccurate. Consider using '#align prime_multiset.coe_coe_nat_monoid_hom PrimeMultiset.coe_coeNatMonoidHomₓ'. -/
@[simp]
theorem coe_coeNatMonoidHom : (coeNatMonoidHom : PrimeMultiset → Multiset ℕ) = coe :=
  rfl
#align prime_multiset.coe_coe_nat_monoid_hom PrimeMultiset.coe_coeNatMonoidHom

#print PrimeMultiset.coeNat_injective /-
theorem coeNat_injective : Function.Injective (coe : PrimeMultiset → Multiset ℕ) :=
  Multiset.map_injective Nat.Primes.coe_nat_injective
#align prime_multiset.coe_nat_injective PrimeMultiset.coeNat_injective
-/

#print PrimeMultiset.coeNat_ofPrime /-
theorem coeNat_ofPrime (p : Nat.Primes) : (ofPrime p : Multiset ℕ) = {p} :=
  rfl
#align prime_multiset.coe_nat_of_prime PrimeMultiset.coeNat_ofPrime
-/

#print PrimeMultiset.coeNat_prime /-
theorem coeNat_prime (v : PrimeMultiset) (p : ℕ) (h : p ∈ (v : Multiset ℕ)) : p.Prime :=
  by
  rcases multiset.mem_map.mp h with ⟨⟨p', hp'⟩, ⟨h_mem, h_eq⟩⟩
  exact h_eq ▸ hp'
#align prime_multiset.coe_nat_prime PrimeMultiset.coeNat_prime
-/

#print PrimeMultiset.toPNatMultiset /-
/-- Converts a `prime_multiset` to a `multiset ℕ+`. -/
def toPNatMultiset : PrimeMultiset → Multiset ℕ+ := fun v => v.map fun p => (p : ℕ+)
#align prime_multiset.to_pnat_multiset PrimeMultiset.toPNatMultiset
-/

#print PrimeMultiset.coePNat /-
instance coePNat : Coe PrimeMultiset (Multiset ℕ+) :=
  ⟨toPNatMultiset⟩
#align prime_multiset.coe_pnat PrimeMultiset.coePNat
-/

/- warning: prime_multiset.coe_pnat_monoid_hom -> PrimeMultiset.coePNatMonoidHom is a dubious translation:
lean 3 declaration is
  AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.orderedCancelAddCommMonoid.{0} PNat))))))
but is expected to have type
  AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))
Case conversion may be inaccurate. Consider using '#align prime_multiset.coe_pnat_monoid_hom PrimeMultiset.coePNatMonoidHomₓ'. -/
/-- `coe_pnat`, the coercion from a multiset of primes to a multiset of positive
naturals, regarded as an `add_monoid_hom`. -/
def coePNatMonoidHom : PrimeMultiset →+ Multiset ℕ+ :=
  { Multiset.mapAddMonoidHom coe with toFun := coe }
#align prime_multiset.coe_pnat_monoid_hom PrimeMultiset.coePNatMonoidHom

/- warning: prime_multiset.coe_coe_pnat_monoid_hom -> PrimeMultiset.coe_coePNatMonoidHom is a dubious translation:
lean 3 declaration is
  Eq.{1} ((fun (_x : AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.orderedCancelAddCommMonoid.{0} PNat))))))) => PrimeMultiset -> (Multiset.{0} PNat)) PrimeMultiset.coePNatMonoidHom) (coeFn.{1, 1} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.orderedCancelAddCommMonoid.{0} PNat))))))) (fun (_x : AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.orderedCancelAddCommMonoid.{0} PNat))))))) => PrimeMultiset -> (Multiset.{0} PNat)) (AddMonoidHom.hasCoeToFun.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.orderedCancelAddCommMonoid.{0} PNat))))))) PrimeMultiset.coePNatMonoidHom) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) PrimeMultiset (Multiset.{0} PNat) (HasLiftT.mk.{1, 1} PrimeMultiset (Multiset.{0} PNat) (CoeTCₓ.coe.{1, 1} PrimeMultiset (Multiset.{0} PNat) (coeBase.{1, 1} PrimeMultiset (Multiset.{0} PNat) PrimeMultiset.coePNat))))
but is expected to have type
  Eq.{1} (forall (a : PrimeMultiset), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : PrimeMultiset) => Multiset.{0} PNat) a) (FunLike.coe.{1, 1, 1} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))) PrimeMultiset (fun (_x : PrimeMultiset) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : PrimeMultiset) => Multiset.{0} PNat) _x) (AddHomClass.toFunLike.{0, 0, 0} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))) PrimeMultiset (Multiset.{0} PNat) (AddZeroClass.toAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))) (AddZeroClass.toAdd.{0} (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))) (AddMonoidHomClass.toAddHomClass.{0, 0, 0} (AddMonoidHom.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))) PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat)))))) (AddMonoidHom.addMonoidHomClass.{0, 0} PrimeMultiset (Multiset.{0} PNat) (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} PNat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} PNat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} PNat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} PNat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} PNat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} PNat))))))))) PrimeMultiset.coePNatMonoidHom) (Coe.coe.{1, 1} PrimeMultiset (Multiset.{0} PNat) PrimeMultiset.coePNat)
Case conversion may be inaccurate. Consider using '#align prime_multiset.coe_coe_pnat_monoid_hom PrimeMultiset.coe_coePNatMonoidHomₓ'. -/
@[simp]
theorem coe_coePNatMonoidHom : (coePNatMonoidHom : PrimeMultiset → Multiset ℕ+) = coe :=
  rfl
#align prime_multiset.coe_coe_pnat_monoid_hom PrimeMultiset.coe_coePNatMonoidHom

#print PrimeMultiset.coePNat_injective /-
theorem coePNat_injective : Function.Injective (coe : PrimeMultiset → Multiset ℕ+) :=
  Multiset.map_injective Nat.Primes.coe_pnat_injective
#align prime_multiset.coe_pnat_injective PrimeMultiset.coePNat_injective
-/

#print PrimeMultiset.coePNat_ofPrime /-
theorem coePNat_ofPrime (p : Nat.Primes) : (ofPrime p : Multiset ℕ+) = {(p : ℕ+)} :=
  rfl
#align prime_multiset.coe_pnat_of_prime PrimeMultiset.coePNat_ofPrime
-/

#print PrimeMultiset.coePNat_prime /-
theorem coePNat_prime (v : PrimeMultiset) (p : ℕ+) (h : p ∈ (v : Multiset ℕ+)) : p.Prime :=
  by
  rcases multiset.mem_map.mp h with ⟨⟨p', hp'⟩, ⟨h_mem, h_eq⟩⟩
  exact h_eq ▸ hp'
#align prime_multiset.coe_pnat_prime PrimeMultiset.coePNat_prime
-/

#print PrimeMultiset.coeMultisetPNatNat /-
instance coeMultisetPNatNat : Coe (Multiset ℕ+) (Multiset ℕ) :=
  ⟨fun v => v.map fun n => (n : ℕ)⟩
#align prime_multiset.coe_multiset_pnat_nat PrimeMultiset.coeMultisetPNatNat
-/

#print PrimeMultiset.coePNat_nat /-
theorem coePNat_nat (v : PrimeMultiset) : ((v : Multiset ℕ+) : Multiset ℕ) = (v : Multiset ℕ) :=
  by
  change (v.map (coe : Nat.Primes → ℕ+)).map Subtype.val = v.map Subtype.val
  rw [Multiset.map_map]
  congr
#align prime_multiset.coe_pnat_nat PrimeMultiset.coePNat_nat
-/

#print PrimeMultiset.prod /-
/-- The product of a `prime_multiset`, as a `ℕ+`. -/
def prod (v : PrimeMultiset) : ℕ+ :=
  (v : Multiset PNat).Prod
#align prime_multiset.prod PrimeMultiset.prod
-/

#print PrimeMultiset.coe_prod /-
theorem coe_prod (v : PrimeMultiset) : (v.Prod : ℕ) = (v : Multiset ℕ).Prod :=
  by
  let h : (v.prod : ℕ) = ((v.map coe).map coe).Prod :=
    pnat.coe_monoid_hom.map_multiset_prod v.to_pnat_multiset
  rw [Multiset.map_map] at h
  have : (coe : ℕ+ → ℕ) ∘ (coe : Nat.Primes → ℕ+) = coe := funext fun p => rfl
  rw [this] at h; exact h
#align prime_multiset.coe_prod PrimeMultiset.coe_prod
-/

#print PrimeMultiset.prod_ofPrime /-
theorem prod_ofPrime (p : Nat.Primes) : (ofPrime p).Prod = (p : ℕ+) :=
  Multiset.prod_singleton _
#align prime_multiset.prod_of_prime PrimeMultiset.prod_ofPrime
-/

#print PrimeMultiset.ofNatMultiset /-
/-- If a `multiset ℕ` consists only of primes, it can be recast as a `prime_multiset`. -/
def ofNatMultiset (v : Multiset ℕ) (h : ∀ p : ℕ, p ∈ v → p.Prime) : PrimeMultiset :=
  @Multiset.pmap ℕ Nat.Primes Nat.Prime (fun p hp => ⟨p, hp⟩) v h
#align prime_multiset.of_nat_multiset PrimeMultiset.ofNatMultiset
-/

#print PrimeMultiset.to_ofNatMultiset /-
theorem to_ofNatMultiset (v : Multiset ℕ) (h) : (ofNatMultiset v h : Multiset ℕ) = v :=
  by
  unfold_coes
  dsimp [of_nat_multiset, to_nat_multiset]
  have : (fun (p : ℕ) (h : p.Prime) => ((⟨p, h⟩ : Nat.Primes) : ℕ)) = fun p h => id p :=
    by
    funext p h
    rfl
  rw [Multiset.map_pmap, this, Multiset.pmap_eq_map, Multiset.map_id]
#align prime_multiset.to_of_nat_multiset PrimeMultiset.to_ofNatMultiset
-/

#print PrimeMultiset.prod_ofNatMultiset /-
theorem prod_ofNatMultiset (v : Multiset ℕ) (h) : ((ofNatMultiset v h).Prod : ℕ) = (v.Prod : ℕ) :=
  by rw [coe_prod, to_of_nat_multiset]
#align prime_multiset.prod_of_nat_multiset PrimeMultiset.prod_ofNatMultiset
-/

#print PrimeMultiset.ofPNatMultiset /-
/-- If a `multiset ℕ+` consists only of primes, it can be recast as a `prime_multiset`. -/
def ofPNatMultiset (v : Multiset ℕ+) (h : ∀ p : ℕ+, p ∈ v → p.Prime) : PrimeMultiset :=
  @Multiset.pmap ℕ+ Nat.Primes PNat.Prime (fun p hp => ⟨(p : ℕ), hp⟩) v h
#align prime_multiset.of_pnat_multiset PrimeMultiset.ofPNatMultiset
-/

#print PrimeMultiset.to_ofPNatMultiset /-
theorem to_ofPNatMultiset (v : Multiset ℕ+) (h) : (ofPNatMultiset v h : Multiset ℕ+) = v :=
  by
  unfold_coes; dsimp [of_pnat_multiset, to_pnat_multiset]
  have : (fun (p : ℕ+) (h : p.Prime) => (coe : Nat.Primes → ℕ+) ⟨p, h⟩) = fun p h => id p :=
    by
    funext p h
    apply Subtype.eq
    rfl
  rw [Multiset.map_pmap, this, Multiset.pmap_eq_map, Multiset.map_id]
#align prime_multiset.to_of_pnat_multiset PrimeMultiset.to_ofPNatMultiset
-/

#print PrimeMultiset.prod_ofPNatMultiset /-
theorem prod_ofPNatMultiset (v : Multiset ℕ+) (h) : ((ofPNatMultiset v h).Prod : ℕ+) = v.Prod :=
  by
  dsimp [Prod]
  rw [to_of_pnat_multiset]
#align prime_multiset.prod_of_pnat_multiset PrimeMultiset.prod_ofPNatMultiset
-/

#print PrimeMultiset.ofNatList /-
/-- Lists can be coerced to multisets; here we have some results
about how this interacts with our constructions on multisets. -/
def ofNatList (l : List ℕ) (h : ∀ p : ℕ, p ∈ l → p.Prime) : PrimeMultiset :=
  ofNatMultiset (l : Multiset ℕ) h
#align prime_multiset.of_nat_list PrimeMultiset.ofNatList
-/

#print PrimeMultiset.prod_ofNatList /-
theorem prod_ofNatList (l : List ℕ) (h) : ((ofNatList l h).Prod : ℕ) = l.Prod :=
  by
  have := prod_of_nat_multiset (l : Multiset ℕ) h
  rw [Multiset.coe_prod] at this
  exact this
#align prime_multiset.prod_of_nat_list PrimeMultiset.prod_ofNatList
-/

#print PrimeMultiset.ofPNatList /-
/-- If a `list ℕ+` consists only of primes, it can be recast as a `prime_multiset` with
the coercion from lists to multisets. -/
def ofPNatList (l : List ℕ+) (h : ∀ p : ℕ+, p ∈ l → p.Prime) : PrimeMultiset :=
  ofPNatMultiset (l : Multiset ℕ+) h
#align prime_multiset.of_pnat_list PrimeMultiset.ofPNatList
-/

/- warning: prime_multiset.prod_of_pnat_list -> PrimeMultiset.prod_ofPNatList is a dubious translation:
lean 3 declaration is
  forall (l : List.{0} PNat) (h : forall (p : PNat), (Membership.Mem.{0, 0} PNat (List.{0} PNat) (List.hasMem.{0} PNat) p l) -> (PNat.Prime p)), Eq.{1} PNat (PrimeMultiset.prod (PrimeMultiset.ofPNatList l h)) (List.prod.{0} PNat PNat.hasMul PNat.hasOne l)
but is expected to have type
  forall (l : List.{0} PNat) (h : forall (p : PNat), (Membership.mem.{0, 0} PNat (List.{0} PNat) (List.instMembershipList.{0} PNat) p l) -> (PNat.Prime p)), Eq.{1} PNat (PrimeMultiset.prod (PrimeMultiset.ofPNatList l h)) (List.prod.{0} PNat instPNatMul instOnePNat l)
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_of_pnat_list PrimeMultiset.prod_ofPNatListₓ'. -/
theorem prod_ofPNatList (l : List ℕ+) (h) : (ofPNatList l h).Prod = l.Prod :=
  by
  have := prod_of_pnat_multiset (l : Multiset ℕ+) h
  rw [Multiset.coe_prod] at this
  exact this
#align prime_multiset.prod_of_pnat_list PrimeMultiset.prod_ofPNatList

/- warning: prime_multiset.prod_zero -> PrimeMultiset.prod_zero is a dubious translation:
lean 3 declaration is
  Eq.{1} PNat (PrimeMultiset.prod (OfNat.ofNat.{0} PrimeMultiset 0 (OfNat.mk.{0} PrimeMultiset 0 (Zero.zero.{0} PrimeMultiset (AddZeroClass.toHasZero.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid))))))))) (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))
but is expected to have type
  Eq.{1} PNat (PrimeMultiset.prod (OfNat.ofNat.{0} PrimeMultiset 0 (Zero.toOfNat0.{0} PrimeMultiset (AddMonoid.toZero.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))))) (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_zero PrimeMultiset.prod_zeroₓ'. -/
/-- The product map gives a homomorphism from the additive monoid
of multisets to the multiplicative monoid ℕ+. -/
theorem prod_zero : (0 : PrimeMultiset).Prod = 1 :=
  by
  dsimp [Prod]
  exact Multiset.prod_zero
#align prime_multiset.prod_zero PrimeMultiset.prod_zero

/- warning: prime_multiset.prod_add -> PrimeMultiset.prod_add is a dubious translation:
lean 3 declaration is
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (HAdd.hAdd.{0, 0, 0} PrimeMultiset PrimeMultiset PrimeMultiset (instHAdd.{0} PrimeMultiset (AddZeroClass.toHasAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))))) u v)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) (PrimeMultiset.prod u) (PrimeMultiset.prod v))
but is expected to have type
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (HAdd.hAdd.{0, 0, 0} PrimeMultiset PrimeMultiset PrimeMultiset (instHAdd.{0} PrimeMultiset (AddZeroClass.toAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))))) u v)) (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) (PrimeMultiset.prod u) (PrimeMultiset.prod v))
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_add PrimeMultiset.prod_addₓ'. -/
theorem prod_add (u v : PrimeMultiset) : (u + v).Prod = u.Prod * v.Prod :=
  by
  change (coe_pnat_monoid_hom (u + v)).Prod = _
  rw [coe_pnat_monoid_hom.map_add]
  exact Multiset.prod_add _ _
#align prime_multiset.prod_add PrimeMultiset.prod_add

/- warning: prime_multiset.prod_smul -> PrimeMultiset.prod_smul is a dubious translation:
lean 3 declaration is
  forall (d : Nat) (u : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (SMul.smul.{0, 0} Nat PrimeMultiset (AddMonoid.SMul.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) d u)) (HPow.hPow.{0, 0, 0} PNat Nat PNat (instHPow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) (PrimeMultiset.prod u) d)
but is expected to have type
  forall (d : Nat) (u : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (HSMul.hSMul.{0, 0, 0} Nat PrimeMultiset PrimeMultiset (instHSMul.{0, 0} Nat PrimeMultiset (AddMonoid.SMul.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))) d u)) (Pow.pow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (PrimeMultiset.prod u) d)
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_smul PrimeMultiset.prod_smulₓ'. -/
theorem prod_smul (d : ℕ) (u : PrimeMultiset) : (d • u).Prod = u.Prod ^ d :=
  by
  induction' d with d ih
  rfl
  rw [succ_nsmul, prod_add, ih, Nat.succ_eq_add_one, pow_succ, mul_comm]
#align prime_multiset.prod_smul PrimeMultiset.prod_smul

end PrimeMultiset

namespace PNat

#print PNat.factorMultiset /-
/-- The prime factors of n, regarded as a multiset -/
def factorMultiset (n : ℕ+) : PrimeMultiset :=
  PrimeMultiset.ofNatList (Nat.factors n) (@Nat.prime_of_mem_factors n)
#align pnat.factor_multiset PNat.factorMultiset
-/

#print PNat.prod_factorMultiset /-
/-- The product of the factors is the original number -/
theorem prod_factorMultiset (n : ℕ+) : (factorMultiset n).Prod = n :=
  eq <| by
    dsimp [factor_multiset]
    rw [PrimeMultiset.prod_ofNatList]
    exact Nat.prod_factors n.ne_zero
#align pnat.prod_factor_multiset PNat.prod_factorMultiset
-/

#print PNat.coeNat_factorMultiset /-
theorem coeNat_factorMultiset (n : ℕ+) :
    (factorMultiset n : Multiset ℕ) = (Nat.factors n : Multiset ℕ) :=
  PrimeMultiset.to_ofNatMultiset (Nat.factors n) (@Nat.prime_of_mem_factors n)
#align pnat.coe_nat_factor_multiset PNat.coeNat_factorMultiset
-/

end PNat

namespace PrimeMultiset

#print PrimeMultiset.factorMultiset_prod /-
/-- If we start with a multiset of primes, take the product and
 then factor it, we get back the original multiset. -/
theorem factorMultiset_prod (v : PrimeMultiset) : v.Prod.factorMultiset = v :=
  by
  apply PrimeMultiset.coeNat_injective
  rw [v.prod.coe_nat_factor_multiset, PrimeMultiset.coe_prod]
  rcases v with ⟨l⟩
  unfold_coes
  dsimp [PrimeMultiset.toNatMultiset]
  rw [Multiset.coe_prod]
  let l' := l.map (coe : Nat.Primes → ℕ)
  have : ∀ p : ℕ, p ∈ l' → p.Prime := fun p hp =>
    by
    rcases list.mem_map.mp hp with ⟨⟨p', hp'⟩, ⟨h_mem, h_eq⟩⟩
    exact h_eq ▸ hp'
  exact multiset.coe_eq_coe.mpr (@Nat.factors_unique _ l' rfl this).symm
#align prime_multiset.factor_multiset_prod PrimeMultiset.factorMultiset_prod
-/

end PrimeMultiset

namespace PNat

#print PNat.factorMultisetEquiv /-
/-- Positive integers biject with multisets of primes. -/
def factorMultisetEquiv : ℕ+ ≃ PrimeMultiset
    where
  toFun := factorMultiset
  invFun := PrimeMultiset.prod
  left_inv := prod_factorMultiset
  right_inv := PrimeMultiset.factorMultiset_prod
#align pnat.factor_multiset_equiv PNat.factorMultisetEquiv
-/

/- warning: pnat.factor_multiset_one -> PNat.factorMultiset_one is a dubious translation:
lean 3 declaration is
  Eq.{1} PrimeMultiset (PNat.factorMultiset (OfNat.ofNat.{0} PNat 1 (OfNat.mk.{0} PNat 1 (One.one.{0} PNat PNat.hasOne)))) (OfNat.ofNat.{0} PrimeMultiset 0 (OfNat.mk.{0} PrimeMultiset 0 (Zero.zero.{0} PrimeMultiset (AddZeroClass.toHasZero.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid))))))))
but is expected to have type
  Eq.{1} PrimeMultiset (PNat.factorMultiset (OfNat.ofNat.{0} PNat 1 (instOfNatPNatHAddNatInstHAddInstAddNatOfNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))) (OfNat.ofNat.{0} PrimeMultiset 0 (Zero.toOfNat0.{0} PrimeMultiset (AddMonoid.toZero.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_one PNat.factorMultiset_oneₓ'. -/
/-- Factoring gives a homomorphism from the multiplicative
 monoid ℕ+ to the additive monoid of multisets. -/
theorem factorMultiset_one : factorMultiset 1 = 0 := by
  simp [factor_multiset, PrimeMultiset.ofNatList, PrimeMultiset.ofNatMultiset]
#align pnat.factor_multiset_one PNat.factorMultiset_one

/- warning: pnat.factor_multiset_mul -> PNat.factorMultiset_mul is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat PNat.hasMul) n m)) (HAdd.hAdd.{0, 0, 0} PrimeMultiset PrimeMultiset PrimeMultiset (instHAdd.{0} PrimeMultiset (AddZeroClass.toHasAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))))) (PNat.factorMultiset n) (PNat.factorMultiset m))
but is expected to have type
  forall (n : PNat) (m : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (HMul.hMul.{0, 0, 0} PNat PNat PNat (instHMul.{0} PNat instPNatMul) n m)) (HAdd.hAdd.{0, 0, 0} PrimeMultiset PrimeMultiset PrimeMultiset (instHAdd.{0} PrimeMultiset (AddZeroClass.toAdd.{0} PrimeMultiset (AddMonoid.toAddZeroClass.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))))) (PNat.factorMultiset n) (PNat.factorMultiset m))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_mul PNat.factorMultiset_mulₓ'. -/
theorem factorMultiset_mul (n m : ℕ+) :
    factorMultiset (n * m) = factorMultiset n + factorMultiset m :=
  by
  let u := factor_multiset n
  let v := factor_multiset m
  have : n = u.prod := (prod_factor_multiset n).symm; rw [this]
  have : m = v.prod := (prod_factor_multiset m).symm; rw [this]
  rw [← PrimeMultiset.prod_add]
  repeat' rw [PrimeMultiset.factorMultiset_prod]
#align pnat.factor_multiset_mul PNat.factorMultiset_mul

/- warning: pnat.factor_multiset_pow -> PNat.factorMultiset_pow is a dubious translation:
lean 3 declaration is
  forall (n : PNat) (m : Nat), Eq.{1} PrimeMultiset (PNat.factorMultiset (HPow.hPow.{0, 0, 0} PNat Nat PNat (instHPow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) n m)) (SMul.smul.{0, 0} Nat PrimeMultiset (AddMonoid.SMul.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) m (PNat.factorMultiset n))
but is expected to have type
  forall (n : PNat) (m : Nat), Eq.{1} PrimeMultiset (PNat.factorMultiset (Pow.pow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) n m)) (HSMul.hSMul.{0, 0, 0} Nat PrimeMultiset PrimeMultiset (instHSMul.{0, 0} Nat PrimeMultiset (AddMonoid.SMul.{0} PrimeMultiset (AddCommMonoid.toAddMonoid.{0} PrimeMultiset (OrderedAddCommMonoid.toAddCommMonoid.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid))))) m (PNat.factorMultiset n))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_pow PNat.factorMultiset_powₓ'. -/
theorem factorMultiset_pow (n : ℕ+) (m : ℕ) : factorMultiset (n ^ m) = m • factorMultiset n :=
  by
  let u := factor_multiset n
  have : n = u.prod := (prod_factor_multiset n).symm
  rw [this, ← PrimeMultiset.prod_smul]
  repeat' rw [PrimeMultiset.factorMultiset_prod]
#align pnat.factor_multiset_pow PNat.factorMultiset_pow

#print PNat.factorMultiset_ofPrime /-
/-- Factoring a prime gives the corresponding one-element multiset. -/
theorem factorMultiset_ofPrime (p : Nat.Primes) :
    (p : ℕ+).factorMultiset = PrimeMultiset.ofPrime p :=
  by
  apply factor_multiset_equiv.symm.injective
  change (p : ℕ+).factorMultiset.Prod = (PrimeMultiset.ofPrime p).Prod
  rw [(p : ℕ+).prod_factorMultiset, PrimeMultiset.prod_ofPrime]
#align pnat.factor_multiset_of_prime PNat.factorMultiset_ofPrime
-/

/- warning: pnat.factor_multiset_le_iff -> PNat.factorMultiset_le_iff is a dubious translation:
lean 3 declaration is
  forall {m : PNat} {n : PNat}, Iff (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (PNat.factorMultiset m) (PNat.factorMultiset n)) (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) m n)
but is expected to have type
  forall {m : PNat} {n : PNat}, Iff (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (PNat.factorMultiset m) (PNat.factorMultiset n)) (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) m n)
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_le_iff PNat.factorMultiset_le_iffₓ'. -/
/-- We now have four different results that all encode the
 idea that inequality of multisets corresponds to divisibility
 of positive integers. -/
theorem factorMultiset_le_iff {m n : ℕ+} : factorMultiset m ≤ factorMultiset n ↔ m ∣ n :=
  by
  constructor
  · intro h
    rw [← prod_factor_multiset m, ← prod_factor_multiset m]
    apply Dvd.intro (n.factor_multiset - m.factor_multiset).Prod
    rw [← PrimeMultiset.prod_add, PrimeMultiset.factorMultiset_prod, add_tsub_cancel_of_le h,
      prod_factor_multiset]
  · intro h
    rw [← mul_div_exact h, factor_multiset_mul]
    exact le_self_add
#align pnat.factor_multiset_le_iff PNat.factorMultiset_le_iff

/- warning: pnat.factor_multiset_le_iff' -> PNat.factorMultiset_le_iff' is a dubious translation:
lean 3 declaration is
  forall {m : PNat} {v : PrimeMultiset}, Iff (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) (PNat.factorMultiset m) v) (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) m (PrimeMultiset.prod v))
but is expected to have type
  forall {m : PNat} {v : PrimeMultiset}, Iff (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) (PNat.factorMultiset m) v) (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) m (PrimeMultiset.prod v))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_le_iff' PNat.factorMultiset_le_iff'ₓ'. -/
theorem factorMultiset_le_iff' {m : ℕ+} {v : PrimeMultiset} : factorMultiset m ≤ v ↔ m ∣ v.Prod :=
  by
  let h := @factor_multiset_le_iff m v.prod
  rw [v.factor_multiset_prod] at h
  exact h
#align pnat.factor_multiset_le_iff' PNat.factorMultiset_le_iff'

end PNat

namespace PrimeMultiset

/- warning: prime_multiset.prod_dvd_iff -> PrimeMultiset.prod_dvd_iff is a dubious translation:
lean 3 declaration is
  forall {u : PrimeMultiset} {v : PrimeMultiset}, Iff (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) (PrimeMultiset.prod u) (PrimeMultiset.prod v)) (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) u v)
but is expected to have type
  forall {u : PrimeMultiset} {v : PrimeMultiset}, Iff (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) (PrimeMultiset.prod u) (PrimeMultiset.prod v)) (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) u v)
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_dvd_iff PrimeMultiset.prod_dvd_iffₓ'. -/
theorem prod_dvd_iff {u v : PrimeMultiset} : u.Prod ∣ v.Prod ↔ u ≤ v :=
  by
  let h := @PNat.factorMultiset_le_iff' u.prod v
  rw [u.factor_multiset_prod] at h
  exact h.symm
#align prime_multiset.prod_dvd_iff PrimeMultiset.prod_dvd_iff

/- warning: prime_multiset.prod_dvd_iff' -> PrimeMultiset.prod_dvd_iff' is a dubious translation:
lean 3 declaration is
  forall {u : PrimeMultiset} {n : PNat}, Iff (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) (PrimeMultiset.prod u) n) (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset PrimeMultiset.canonicallyOrderedAddMonoid)))) u (PNat.factorMultiset n))
but is expected to have type
  forall {u : PrimeMultiset} {n : PNat}, Iff (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) (PrimeMultiset.prod u) n) (LE.le.{0} PrimeMultiset (Preorder.toLE.{0} PrimeMultiset (PartialOrder.toPreorder.{0} PrimeMultiset (OrderedAddCommMonoid.toPartialOrder.{0} PrimeMultiset (CanonicallyOrderedAddMonoid.toOrderedAddCommMonoid.{0} PrimeMultiset instPrimeMultisetCanonicallyOrderedAddMonoid)))) u (PNat.factorMultiset n))
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_dvd_iff' PrimeMultiset.prod_dvd_iff'ₓ'. -/
theorem prod_dvd_iff' {u : PrimeMultiset} {n : ℕ+} : u.Prod ∣ n ↔ u ≤ n.factorMultiset :=
  by
  let h := @prod_dvd_iff u n.factor_multiset
  rw [n.prod_factor_multiset] at h
  exact h
#align prime_multiset.prod_dvd_iff' PrimeMultiset.prod_dvd_iff'

end PrimeMultiset

namespace PNat

/- warning: pnat.factor_multiset_gcd -> PNat.factorMultiset_gcd is a dubious translation:
lean 3 declaration is
  forall (m : PNat) (n : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (PNat.gcd m n)) (Inf.inf.{0} PrimeMultiset (SemilatticeInf.toHasInf.{0} PrimeMultiset (Lattice.toSemilatticeInf.{0} PrimeMultiset (DistribLattice.toLattice.{0} PrimeMultiset PrimeMultiset.distribLattice))) (PNat.factorMultiset m) (PNat.factorMultiset n))
but is expected to have type
  forall (m : PNat) (n : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (PNat.gcd m n)) (Inf.inf.{0} PrimeMultiset (Lattice.toInf.{0} PrimeMultiset (DistribLattice.toLattice.{0} PrimeMultiset instPrimeMultisetDistribLattice)) (PNat.factorMultiset m) (PNat.factorMultiset n))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_gcd PNat.factorMultiset_gcdₓ'. -/
/-- The gcd and lcm operations on positive integers correspond
 to the inf and sup operations on multisets. -/
theorem factorMultiset_gcd (m n : ℕ+) :
    factorMultiset (gcd m n) = factorMultiset m ⊓ factorMultiset n :=
  by
  apply le_antisymm
  · apply le_inf_iff.mpr <;> constructor <;> apply factor_multiset_le_iff.mpr
    exact gcd_dvd_left m n
    exact gcd_dvd_right m n
  · rw [← PrimeMultiset.prod_dvd_iff, prod_factor_multiset]
    apply dvd_gcd <;> rw [PrimeMultiset.prod_dvd_iff']
    exact inf_le_left
    exact inf_le_right
#align pnat.factor_multiset_gcd PNat.factorMultiset_gcd

/- warning: pnat.factor_multiset_lcm -> PNat.factorMultiset_lcm is a dubious translation:
lean 3 declaration is
  forall (m : PNat) (n : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (PNat.lcm m n)) (Sup.sup.{0} PrimeMultiset (SemilatticeSup.toHasSup.{0} PrimeMultiset PrimeMultiset.semilatticeSup) (PNat.factorMultiset m) (PNat.factorMultiset n))
but is expected to have type
  forall (m : PNat) (n : PNat), Eq.{1} PrimeMultiset (PNat.factorMultiset (PNat.lcm m n)) (Sup.sup.{0} PrimeMultiset (SemilatticeSup.toSup.{0} PrimeMultiset instPrimeMultisetSemilatticeSup) (PNat.factorMultiset m) (PNat.factorMultiset n))
Case conversion may be inaccurate. Consider using '#align pnat.factor_multiset_lcm PNat.factorMultiset_lcmₓ'. -/
theorem factorMultiset_lcm (m n : ℕ+) :
    factorMultiset (lcm m n) = factorMultiset m ⊔ factorMultiset n :=
  by
  apply le_antisymm
  · rw [← PrimeMultiset.prod_dvd_iff, prod_factor_multiset]
    apply lcm_dvd <;> rw [← factor_multiset_le_iff']
    exact le_sup_left
    exact le_sup_right
  · apply sup_le_iff.mpr <;> constructor <;> apply factor_multiset_le_iff.mpr
    exact dvd_lcm_left m n
    exact dvd_lcm_right m n
#align pnat.factor_multiset_lcm PNat.factorMultiset_lcm

/- warning: pnat.count_factor_multiset -> PNat.count_factorMultiset is a dubious translation:
lean 3 declaration is
  forall (m : PNat) (p : Nat.Primes) (k : Nat), Iff (Dvd.Dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) (HPow.hPow.{0, 0, 0} PNat Nat PNat (instHPow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat PNat.linearOrderedCancelCommMonoid))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat.Primes PNat (HasLiftT.mk.{1, 1} Nat.Primes PNat (CoeTCₓ.coe.{1, 1} Nat.Primes PNat (coeBase.{1, 1} Nat.Primes PNat Nat.Primes.coePNat))) p) k) m) (LE.le.{0} Nat Nat.hasLe k (Multiset.count.{0} Nat.Primes (fun (a : Nat.Primes) (b : Nat.Primes) => Subtype.decidableEq.{0} Nat (fun (x : Nat) => Nat.Prime x) (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) a b) p (PNat.factorMultiset m)))
but is expected to have type
  forall (m : PNat) (p : Nat.Primes) (k : Nat), Iff (Dvd.dvd.{0} PNat (semigroupDvd.{0} PNat (Monoid.toSemigroup.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid))))))) (Pow.pow.{0, 0} PNat Nat (Monoid.Pow.{0} PNat (RightCancelMonoid.toMonoid.{0} PNat (CancelMonoid.toRightCancelMonoid.{0} PNat (CancelCommMonoid.toCancelMonoid.{0} PNat (OrderedCancelCommMonoid.toCancelCommMonoid.{0} PNat (LinearOrderedCancelCommMonoid.toOrderedCancelCommMonoid.{0} PNat instPNatLinearOrderedCancelCommMonoid)))))) (Nat.Primes.toPNat p) k) m) (LE.le.{0} Nat instLENat k (Multiset.count.{0} Nat.Primes (fun (a : Nat.Primes) (b : Nat.Primes) => Nat.instPrimesDecidableEq a b) p (PNat.factorMultiset m)))
Case conversion may be inaccurate. Consider using '#align pnat.count_factor_multiset PNat.count_factorMultisetₓ'. -/
/-- The number of occurrences of p in the factor multiset of m
 is the same as the p-adic valuation of m. -/
theorem count_factorMultiset (m : ℕ+) (p : Nat.Primes) (k : ℕ) :
    (p : ℕ+) ^ k ∣ m ↔ k ≤ m.factorMultiset.count p :=
  by
  intros
  rw [Multiset.le_count_iff_replicate_le]
  rw [← factor_multiset_le_iff, factor_multiset_pow, factor_multiset_of_prime]
  congr 2
  apply multiset.eq_replicate.mpr
  constructor
  · rw [Multiset.card_nsmul, PrimeMultiset.card_ofPrime, mul_one]
  · intro q h
    rw [PrimeMultiset.ofPrime, Multiset.nsmul_singleton _ k] at h
    exact Multiset.eq_of_mem_replicate h
#align pnat.count_factor_multiset PNat.count_factorMultiset

end PNat

namespace PrimeMultiset

/- warning: prime_multiset.prod_inf -> PrimeMultiset.prod_inf is a dubious translation:
lean 3 declaration is
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (Inf.inf.{0} PrimeMultiset (SemilatticeInf.toHasInf.{0} PrimeMultiset (Lattice.toSemilatticeInf.{0} PrimeMultiset (DistribLattice.toLattice.{0} PrimeMultiset PrimeMultiset.distribLattice))) u v)) (PNat.gcd (PrimeMultiset.prod u) (PrimeMultiset.prod v))
but is expected to have type
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (Inf.inf.{0} PrimeMultiset (Lattice.toInf.{0} PrimeMultiset (DistribLattice.toLattice.{0} PrimeMultiset instPrimeMultisetDistribLattice)) u v)) (PNat.gcd (PrimeMultiset.prod u) (PrimeMultiset.prod v))
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_inf PrimeMultiset.prod_infₓ'. -/
theorem prod_inf (u v : PrimeMultiset) : (u ⊓ v).Prod = PNat.gcd u.Prod v.Prod :=
  by
  let n := u.prod
  let m := v.prod
  change (u ⊓ v).Prod = PNat.gcd n m
  have : u = n.factor_multiset := u.factor_multiset_prod.symm; rw [this]
  have : v = m.factor_multiset := v.factor_multiset_prod.symm; rw [this]
  rw [← PNat.factorMultiset_gcd n m, PNat.prod_factorMultiset]
#align prime_multiset.prod_inf PrimeMultiset.prod_inf

/- warning: prime_multiset.prod_sup -> PrimeMultiset.prod_sup is a dubious translation:
lean 3 declaration is
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (Sup.sup.{0} PrimeMultiset (SemilatticeSup.toHasSup.{0} PrimeMultiset PrimeMultiset.semilatticeSup) u v)) (PNat.lcm (PrimeMultiset.prod u) (PrimeMultiset.prod v))
but is expected to have type
  forall (u : PrimeMultiset) (v : PrimeMultiset), Eq.{1} PNat (PrimeMultiset.prod (Sup.sup.{0} PrimeMultiset (SemilatticeSup.toSup.{0} PrimeMultiset instPrimeMultisetSemilatticeSup) u v)) (PNat.lcm (PrimeMultiset.prod u) (PrimeMultiset.prod v))
Case conversion may be inaccurate. Consider using '#align prime_multiset.prod_sup PrimeMultiset.prod_supₓ'. -/
theorem prod_sup (u v : PrimeMultiset) : (u ⊔ v).Prod = PNat.lcm u.Prod v.Prod :=
  by
  let n := u.prod
  let m := v.prod
  change (u ⊔ v).Prod = PNat.lcm n m
  have : u = n.factor_multiset := u.factor_multiset_prod.symm; rw [this]
  have : v = m.factor_multiset := v.factor_multiset_prod.symm; rw [this]
  rw [← PNat.factorMultiset_lcm n m, PNat.prod_factorMultiset]
#align prime_multiset.prod_sup PrimeMultiset.prod_sup

end PrimeMultiset


import group_theory.basic
import data.finset group_theory.group_action

open finset fintype

universes u v

local attribute [instance, priority 0] classical.prop_decidable

variables {α : Type u} {β : Type v} {t k v : ℕ}

namespace mathieu_group
/--
A Steiner system $S(t,k,v)$, where $t < k < v$ are positive integers is a finite set $X$ of cardinality $v$, a collection of $k$ element subsets of $X$ (called blocks), such that each $t$ element subset of $X$ is contained in a unique block.
-/
structure steiner_system (t k v : ℕ) :=
(X : Type u)
(blocks : finset (finset X))
(h₁ : fintype X)
(h₂ : card X = v)
(h₃ : ∀ b ∈ blocks, card b = k)
(h₄ : ∀ x : finset X, card x = t → ∃! b, b ∈ blocks ∧ x ⊂ b)

/-- The coercion from steiner systems to types. -/
instance : has_coe_to_sort (steiner_system t k v) := ⟨Type u, λ s, s.X⟩
/-- The underlying set of a steiner system is finite. -/
instance steiner_system_fintype (s : steiner_system t k v) : fintype s := s.h₁

def nonempty_steiner_system {s : steiner_system t k v} (h : v > 0) : nonempty s :=
by { rw ← fintype.card_pos_iff, rw ←s.h₂ at h, exact h }

/-- The set of all isomorphisms of between two steiner systems consists of all equivalences of the underlying sets which are block-preserving. -/
def steiner_system_isomorphism
{t₁ k₁ v₁ : ℕ}
{t₂ k₂ v₂ : ℕ}
(s₁ : steiner_system t₁ k₁ v₁)
(s₂ : steiner_system t₂ k₂ v₂) := {f : s₁ ≃ s₂ | ∀ b ∈ s₁.blocks, finset.image f b ∈ s₂.blocks }

/-- The automorphism set $\mathrm{Aut}(s)$ of a steiner system $s$ is the set of isomorphisms from $s$ to $s$. -/
def Aut {t k v : ℕ}(s : steiner_system t k v) := steiner_system_isomorphism s s

/-- The automorphism set of a steiner system can be equipped with a group structure with group identity the identity function, multiplication is function composition, inverses are function inverses. -/
instance {s : steiner_system t k v} : group (Aut s) :=
{
   one := ⟨equiv.refl s, omitted⟩  ,
   mul := fun f g, ⟨f.1.trans g.1, omitted⟩,
   inv := fun f, ⟨ equiv.symm f.1, omitted⟩,
   one_mul := omitted,
   mul_one := omitted,
   mul_left_inv := omitted,
   mul_assoc := omitted,
}

/-- The Steiner system $S(5,8,24)$ exists and is unique up to isomorphism.-/
lemma is_unique_s_5_8_24 : ∃ x: steiner_system 5 8 24, ∀ y : steiner_system 5 8 24,
  nonempty $ steiner_system_isomorphism x y := omitted

/-- Using the axiom of choice, we can pick a representative of the isomorphism class of $S(5,8,24)$.-/
noncomputable def s_5_8_24 : steiner_system 5 8 24 :=
classical.some is_unique_s_5_8_24

/-- The Steiner system $S(5,6,12)$ exists and is unique up to isomorphism.-/
lemma is_unique_s_5_6_12 : ∃ x : steiner_system 5 6 12, ∀ y : steiner_system 5 6 12,
  nonempty $ steiner_system_isomorphism x y := omitted

/-- Using the axiom of choice, we can pick a representative of the isomorphism class of $S(5,6,12)$.-/
noncomputable def s_5_6_12 : steiner_system 5 6 12 := classical.some is_unique_s_5_6_12

/-- The automorphism group of a steiner system acts on the underlying set through the evaluation action.-/
def evaluation {t k v : ℕ} (s : steiner_system t k v) : Aut(s) → s → s :=
λ f x, f.1 x

/-- The evaluation action of the automorphism group satisfies the properties of a monoid action. -/
instance evaluation_action {t k v : ℕ} (s : steiner_system t k v) : mul_action (Aut s) s :=
{ smul := evaluation s,
  one_smul := omitted,
  mul_smul := omitted }

/- *TODO(Kody) : move these to mathlib?* -/

/-- The two point stabilizer of a group is the set of all group elements which fix two distinct points of the group via the group action. -/
def two_pt_stabilizer [monoid α] (f : mul_action α β) {x : β × β} (h : x.1 ≠ x.2) :
  set α :=
{ y : α | y • x.1 = x.1 ∧ y • x.2 = x.2}

/-- The two point stabilizer of a group is a subgroup of the group. -/
instance is_subgroup_two_pt_stabilizer (α) [group α] (f : mul_action α β)
  {x : β × β} (h : x.1 ≠ x.2) : is_subgroup (two_pt_stabilizer f h) := omitted

end mathieu_group


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

! This file was ported from Lean 3 source module data.set.pointwise.big_operators
! leanprover-community/mathlib commit 008205aa645b3f194c1da47025c5f110c8406eab
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.BigOperators.Basic
import Mathlib.Data.Set.Pointwise.Basic

/-!
# Results about pointwise operations on sets and big operators.
-/

namespace Set

section BigOperators

open BigOperators

open Pointwise Function

variable {α : Type _} {ι : Type _} [CommMonoid α]

/-- The n-ary version of `Set.mem_mul`. -/
@[to_additive " The n-ary version of `Set.mem_add`. "]
theorem mem_finset_prod (t : Finset ι) (f : ι → Set α) (a : α) :
    (a ∈ ∏ i in t, f i) ↔ ∃ (g : ι → α)(_ : ∀ {i}, i ∈ t → g i ∈ f i), (∏ i in t, g i) = a := by
  classical
    induction' t using Finset.induction_on with i is hi ih generalizing a
    · simp_rw [Finset.prod_empty, Set.mem_one]
      exact ⟨fun h ↦ ⟨fun _ ↦ a, fun hi ↦ False.elim (Finset.not_mem_empty _ hi), h.symm⟩,
        fun ⟨_, _, hf⟩ ↦ hf.symm⟩
    rw [Finset.prod_insert hi, Set.mem_mul]
    simp_rw [Finset.prod_insert hi]
    simp_rw [ih]
    constructor
    · rintro ⟨x, y, hx, ⟨g, hg, rfl⟩, rfl⟩
      refine ⟨Function.update g i x, ?_, ?_⟩
      · intro j hj
        obtain rfl | hj := Finset.mem_insert.mp hj
        · rwa [Function.update_same]
        · rw [update_noteq (ne_of_mem_of_not_mem hj hi)]
          exact hg hj
      · rw [Finset.prod_update_of_not_mem hi, Function.update_same]
    · rintro ⟨g, hg, rfl⟩
      exact ⟨g i, is.prod g, hg (is.mem_insert_self _),
        ⟨⟨g, fun hi ↦ hg (Finset.mem_insert_of_mem hi), rfl⟩, rfl⟩⟩
#align set.mem_finset_prod Set.mem_finset_prod
#align set.mem_finset_sum Set.mem_finset_sum

/-- A version of `Set.mem_finset_prod` with a simpler RHS for products over a Fintype. -/
@[to_additive " A version of `Set.mem_finset_sum` with a simpler RHS for sums over a Fintype. "]
theorem mem_fintype_prod [Fintype ι] (f : ι → Set α) (a : α) :
    (a ∈ ∏ i, f i) ↔ ∃ (g : ι → α)(_ : ∀ i, g i ∈ f i), (∏ i, g i) = a := by
  rw [mem_finset_prod]
  simp
#align set.mem_fintype_prod Set.mem_fintype_prod
#align set.mem_fintype_sum Set.mem_fintype_sum

/-- An n-ary version of `Set.mul_mem_mul`. -/
@[to_additive " An n-ary version of `Set.add_mem_add`. "]
theorem list_prod_mem_list_prod (t : List ι) (f : ι → Set α) (g : ι → α) (hg : ∀ i ∈ t, g i ∈ f i) :
    (t.map g).prod ∈ (t.map f).prod := by
  induction' t with h tl ih
  · simp_rw [List.map_nil, List.prod_nil, Set.mem_one]
  · simp_rw [List.map_cons, List.prod_cons]
    exact mul_mem_mul (hg h <| List.mem_cons_self _ _)
      (ih fun i hi ↦ hg i <| List.mem_cons_of_mem _ hi)
#align set.list_prod_mem_list_prod Set.list_prod_mem_list_prod
#align set.list_sum_mem_list_sum Set.list_sum_mem_list_sum

/-- An n-ary version of `Set.mul_subset_mul`. -/
@[to_additive " An n-ary version of `Set.add_subset_add`. "]
theorem list_prod_subset_list_prod (t : List ι) (f₁ f₂ : ι → Set α) (hf : ∀ i ∈ t, f₁ i ⊆ f₂ i) :
    (t.map f₁).prod ⊆ (t.map f₂).prod := by
  induction' t with h tl ih
  · rfl
  · simp_rw [List.map_cons, List.prod_cons]
    exact mul_subset_mul (hf h <| List.mem_cons_self _ _)
      (ih fun i hi ↦ hf i <| List.mem_cons_of_mem _ hi)
#align set.list_prod_subset_list_prod Set.list_prod_subset_list_prod
#align set.list_sum_subset_list_sum Set.list_sum_subset_list_sum

@[to_additive]
theorem list_prod_singleton {M : Type _} [CommMonoid M] (s : List M) :
    (s.map fun i ↦ ({i} : Set M)).prod = {s.prod} :=
  (map_list_prod (singletonMonoidHom : M →* Set M) _).symm
#align set.list_prod_singleton Set.list_prod_singleton
#align set.list_sum_singleton Set.list_sum_singleton

/-- An n-ary version of `Set.mul_mem_mul`. -/
@[to_additive " An n-ary version of `Set.add_mem_add`. "]
theorem multiset_prod_mem_multiset_prod (t : Multiset ι) (f : ι → Set α) (g : ι → α)
    (hg : ∀ i ∈ t, g i ∈ f i) : (t.map g).prod ∈ (t.map f).prod := by
  induction t using Quotient.inductionOn
  simp_rw [Multiset.quot_mk_to_coe, Multiset.coe_map, Multiset.coe_prod]
  exact list_prod_mem_list_prod _ _ _ hg
#align set.multiset_prod_mem_multiset_prod Set.multiset_prod_mem_multiset_prod
#align set.multiset_sum_mem_multiset_sum Set.multiset_sum_mem_multiset_sum

/-- An n-ary version of `Set.mul_subset_mul`. -/
@[to_additive " An n-ary version of `Set.add_subset_add`. "]

@[to_additive]
theorem multiset_prod_singleton {M : Type _} [CommMonoid M] (s : Multiset M) :
    (s.map fun i ↦ ({i} : Set M)).prod = {s.prod} :=
  (map_multiset_prod (singletonMonoidHom : M →* Set M) _).symm
#align set.multiset_prod_singleton Set.multiset_prod_singleton
#align set.multiset_sum_singleton Set.multiset_sum_singleton

/-- An n-ary version of `Set.mul_mem_mul`. -/
@[to_additive " An n-ary version of `Set.add_mem_add`. "]
theorem finset_prod_mem_finset_prod (t : Finset ι) (f : ι → Set α) (g : ι → α)
    (hg : ∀ i ∈ t, g i ∈ f i) : (∏ i in t, g i) ∈ ∏ i in t, f i :=
  multiset_prod_mem_multiset_prod _ _ _ hg
#align set.finset_prod_mem_finset_prod Set.finset_prod_mem_finset_prod
#align set.finset_sum_mem_finset_sum Set.finset_sum_mem_finset_sum

/-- An n-ary version of `Set.mul_subset_mul`. -/
@[to_additive " An n-ary version of `Set.add_subset_add`. "]
theorem finset_prod_subset_finset_prod (t : Finset ι) (f₁ f₂ : ι → Set α)
    (hf : ∀ i ∈ t, f₁ i ⊆ f₂ i) : (∏ i in t, f₁ i) ⊆ ∏ i in t, f₂ i :=
  multiset_prod_subset_multiset_prod _ _ _ hf
#align set.finset_prod_subset_finset_prod Set.finset_prod_subset_finset_prod
#align set.finset_sum_subset_finset_sum Set.finset_sum_subset_finset_sum

@[to_additive]
theorem finset_prod_singleton {M ι : Type _} [CommMonoid M] (s : Finset ι) (I : ι → M) :
    (∏ i : ι in s, ({I i} : Set M)) = {∏ i : ι in s, I i} :=
  (map_prod (singletonMonoidHom : M →* Set M) _ _).symm
#align set.finset_prod_singleton Set.finset_prod_singleton
#align set.finset_sum_singleton Set.finset_sum_singleton

/-! TODO: define `decidable_mem_finset_prod` and `decidable_mem_finset_sum`. -/

end BigOperators

end Set


/-
Copyright (c) 2021 Bryan Gin-ge Chen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Adam Topaz, Bryan Gin-ge Chen
-/

import order.boolean_algebra

/-!
# Symmetric difference

The symmetric difference or disjunctive union of sets `A` and `B` is the set of elements that are
in either `A` or `B` but not both. Translated into propositions, the symmetric difference is `xor`.

The symmetric difference operator (`symm_diff`) is defined in this file for any type with `⊔` and
`\` via the formula `(A \ B) ⊔ (B \ A)`, however the theorems proved about it only hold for
`generalized_boolean_algebra`s and `boolean_algebra`s.

The symmetric difference is the addition operator in the Boolean ring structure on Boolean algebras.

## Main declarations

* `symm_diff`: the symmetric difference operator, defined as `(A \ B) ⊔ (B \ A)`

In generalized Boolean algebras, the symmetric difference operator is:

* `symm_diff_comm`: commutative, and
* `symm_diff_assoc`: associative.

## Notations

* `a ∆ b`: `symm_diff a b`

## References

The proof of associativity follows the note "Associativity of the Symmetric Difference of Sets: A
Proof from the Book" by John McCuan:

* <https://people.math.gatech.edu/~mccuan/courses/4317/symmetricdifference.pdf>

## Tags
boolean ring, generalized boolean algebra, boolean algebra, symmetric differences
-/

/-- The symmetric difference operator on a type with `⊔` and `\` is `(A \ B) ⊔ (B \ A)`. -/
def symm_diff {α : Type*} [has_sup α] [has_sdiff α] (A B : α) : α := (A \ B) ⊔ (B \ A)

/- This notation might conflict with the Laplacian once we have it. Feel free to put it in locale
`order` or `symm_diff` if that happens. -/
infix ` ∆ `:100 := symm_diff

lemma symm_diff_def {α : Type*} [has_sup α] [has_sdiff α] (A B : α) :
  A ∆ B = (A \ B) ⊔ (B \ A) :=
rfl

lemma symm_diff_eq_xor (p q : Prop) : p ∆ q = xor p q := rfl

section generalized_boolean_algebra
variables {α : Type*} [generalized_boolean_algebra α] (a b c : α)

lemma symm_diff_comm : a ∆ b = b ∆ a := by simp only [(∆), sup_comm]

instance symm_diff_is_comm : is_commutative α (∆) := ⟨symm_diff_comm⟩

@[simp] lemma symm_diff_self : a ∆ a = ⊥ := by rw [(∆), sup_idem, sdiff_self]
@[simp] lemma symm_diff_bot : a ∆ ⊥ = a := by rw [(∆), sdiff_bot, bot_sdiff, sup_bot_eq]
@[simp] lemma bot_symm_diff : ⊥ ∆ a = a := by rw [symm_diff_comm, symm_diff_bot]

lemma symm_diff_eq_sup_sdiff_inf : a ∆ b = (a ⊔ b) \ (a ⊓ b) :=
by simp [sup_sdiff, sdiff_inf, sup_comm, (∆)]

@[simp] lemma sup_sdiff_symm_diff : (a ⊔ b) \ (a ∆ b) = a ⊓ b :=
sdiff_eq_symm inf_le_sup (by rw symm_diff_eq_sup_sdiff_inf)

lemma disjoint_symm_diff_inf : disjoint (a ∆ b) (a ⊓ b) :=
begin
  rw [symm_diff_eq_sup_sdiff_inf],
  exact disjoint_sdiff_self_left,
end

lemma symm_diff_le_sup : a ∆ b ≤ a ⊔ b := by { rw symm_diff_eq_sup_sdiff_inf, exact sdiff_le }

lemma inf_symm_diff_distrib_left : a ⊓ (b ∆ c) = (a ⊓ b) ∆ (a ⊓ c) :=
by rw [symm_diff_eq_sup_sdiff_inf, inf_sdiff_distrib_left, inf_sup_left, inf_inf_distrib_left,
  symm_diff_eq_sup_sdiff_inf]

lemma inf_symm_diff_distrib_right : (a ∆ b) ⊓ c = (a ⊓ c) ∆ (b ⊓ c) :=
by simp_rw [@inf_comm _ _ _ c, inf_symm_diff_distrib_left]

lemma sdiff_symm_diff : c \ (a ∆ b) = (c ⊓ a ⊓ b) ⊔ ((c \ a) ⊓ (c \ b)) :=
by simp only [(∆), sdiff_sdiff_sup_sdiff']

lemma sdiff_symm_diff' : c \ (a ∆ b) = (c ⊓ a ⊓ b) ⊔ (c \ (a ⊔ b)) :=
by rw [sdiff_symm_diff, sdiff_sup, sup_comm]

lemma symm_diff_sdiff : (a ∆ b) \ c = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) :=
by rw [symm_diff_def, sup_sdiff, sdiff_sdiff_left, sdiff_sdiff_left]

@[simp] lemma symm_diff_sdiff_left : (a ∆ b) \ a = b \ a :=
by rw [symm_diff_def, sup_sdiff, sdiff_idem, sdiff_sdiff_self, bot_sup_eq]

@[simp] lemma symm_diff_sdiff_right : (a ∆ b) \ b = a \ b :=
by rw [symm_diff_comm, symm_diff_sdiff_left]

@[simp] lemma sdiff_symm_diff_self : a \ (a ∆ b) = a ⊓ b := by simp [sdiff_symm_diff]

lemma symm_diff_eq_iff_sdiff_eq {a b c : α} (ha : a ≤ c) :
  a ∆ b = c ↔ c \ a = b :=
begin
  split; intro h,
  { have hba : disjoint (a ⊓ b) c := begin
      rw [←h, disjoint.comm],
      exact disjoint_symm_diff_inf _ _,
    end,
    have hca : _ := congr_arg (\ a) h,
    rw [symm_diff_sdiff_left] at hca,
    rw [←hca, sdiff_eq_self_iff_disjoint],
    exact hba.of_disjoint_inf_of_le ha },
  { have hd : disjoint a b := by { rw ←h, exact disjoint_sdiff_self_right },
    rw [symm_diff_def, hd.sdiff_eq_left, hd.sdiff_eq_right, ←h, sup_sdiff_cancel_right ha] }
end

lemma disjoint.symm_diff_eq_sup {a b : α} (h : disjoint a b) : a ∆ b = a ⊔ b :=
by rw [(∆), h.sdiff_eq_left, h.sdiff_eq_right]

lemma symm_diff_eq_sup : a ∆ b = a ⊔ b ↔ disjoint a b :=
begin
  split; intro h,
  { rw [symm_diff_eq_sup_sdiff_inf, sdiff_eq_self_iff_disjoint] at h,
    exact h.of_disjoint_inf_of_le le_sup_left, },
  { exact h.symm_diff_eq_sup, },
end

lemma symm_diff_symm_diff_left :
  a ∆ b ∆ c = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) ⊔ (c \ (a ⊔ b)) ⊔ (a ⊓ b ⊓ c) :=
calc a ∆ b ∆ c = ((a ∆ b) \ c) ⊔ (c \ (a ∆ b))   : symm_diff_def _ _
           ... = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) ⊔
                   ((c \ (a ⊔ b)) ⊔ (c ⊓ a ⊓ b)) :
                                by rw [sdiff_symm_diff', @sup_comm _ _ (c ⊓ a ⊓ b), symm_diff_sdiff]
           ... = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) ⊔
                   (c \ (a ⊔ b)) ⊔ (a ⊓ b ⊓ c)   : by ac_refl

lemma symm_diff_symm_diff_right :
  a ∆ (b ∆ c) = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) ⊔ (c \ (a ⊔ b)) ⊔ (a ⊓ b ⊓ c) :=
calc a ∆ (b ∆ c) = (a \ (b ∆ c)) ⊔ ((b ∆ c) \ a) : symm_diff_def _ _
             ... = (a \ (b ⊔ c)) ⊔ (a ⊓ b ⊓ c) ⊔
                     (b \ (c ⊔ a) ⊔ c \ (b ⊔ a))   :
                                by rw [sdiff_symm_diff', @sup_comm _ _ (a ⊓ b ⊓ c), symm_diff_sdiff]
             ... = (a \ (b ⊔ c)) ⊔ (b \ (a ⊔ c)) ⊔
                     (c \ (a ⊔ b)) ⊔ (a ⊓ b ⊓ c)   : by ac_refl

lemma symm_diff_assoc : a ∆ b ∆ c = a ∆ (b ∆ c) :=
by rw [symm_diff_symm_diff_left, symm_diff_symm_diff_right]

instance symm_diff_is_assoc : is_associative α (∆) := ⟨symm_diff_assoc⟩

@[simp] lemma symm_diff_symm_diff_self : a ∆ (a ∆ b) = b := by simp [←symm_diff_assoc]

@[simp] lemma symm_diff_symm_diff_self' : a ∆ b ∆ a = b :=
by rw [symm_diff_comm, ←symm_diff_assoc, symm_diff_self, bot_symm_diff]

@[simp] lemma symm_diff_right_inj : a ∆ b = a ∆ c ↔ b = c :=
begin
  split; intro h,
  { have H1 := congr_arg ((∆) a) h,
    rwa [symm_diff_symm_diff_self, symm_diff_symm_diff_self] at H1, },
  { rw h, },
end

@[simp] lemma symm_diff_left_inj : a ∆ b = c ∆ b ↔ a = c :=
by rw [symm_diff_comm a b, symm_diff_comm c b, symm_diff_right_inj]

@[simp] lemma symm_diff_eq_left : a ∆ b = a ↔ b = ⊥ :=
calc a ∆ b = a ↔ a ∆ b = a ∆ ⊥ : by rw symm_diff_bot
           ... ↔     b = ⊥     : by rw symm_diff_right_inj

@[simp] lemma symm_diff_eq_right : a ∆ b = b ↔ a = ⊥ := by rw [symm_diff_comm, symm_diff_eq_left]

@[simp] lemma symm_diff_eq_bot : a ∆ b = ⊥ ↔ a = b :=
calc a ∆ b = ⊥ ↔ a ∆ b = a ∆ a : by rw symm_diff_self
           ... ↔     a = b     : by rw [symm_diff_right_inj, eq_comm]

@[simp] lemma symm_diff_symm_diff_inf : a ∆ b ∆ (a ⊓ b) = a ⊔ b :=
by rw [symm_diff_eq_iff_sdiff_eq (symm_diff_le_sup _ _), sup_sdiff_symm_diff]

@[simp] lemma inf_symm_diff_symm_diff : (a ⊓ b) ∆ (a ∆ b) = a ⊔ b :=
by rw [symm_diff_comm, symm_diff_symm_diff_inf]

variables {a b c}

protected lemma disjoint.symm_diff_left (ha : disjoint a c) (hb : disjoint b c) :
  disjoint (a ∆ b) c :=
by { rw symm_diff_eq_sup_sdiff_inf, exact (ha.sup_left hb).disjoint_sdiff_left }

protected lemma disjoint.symm_diff_right (ha : disjoint a b) (hb : disjoint a c) :
  disjoint a (b ∆ c) :=
(ha.symm.symm_diff_left hb.symm).symm

end generalized_boolean_algebra

section boolean_algebra
variables {α : Type*} [boolean_algebra α] (a b c : α)

lemma symm_diff_eq : a ∆ b = (a ⊓ bᶜ) ⊔ (b ⊓ aᶜ) := by simp only [(∆), sdiff_eq]

@[simp] lemma symm_diff_top : a ∆ ⊤ = aᶜ := by simp [symm_diff_eq]
@[simp] lemma top_symm_diff : ⊤ ∆ a = aᶜ := by rw [symm_diff_comm, symm_diff_top]

lemma compl_symm_diff : (a ∆ b)ᶜ = (a ⊓ b) ⊔ (aᶜ ⊓ bᶜ) :=
by simp only [←top_sdiff, sdiff_symm_diff, top_inf_eq]

lemma symm_diff_eq_top_iff : a ∆ b = ⊤ ↔ is_compl a b :=
by rw [symm_diff_eq_iff_sdiff_eq le_top, top_sdiff, compl_eq_iff_is_compl]

lemma is_compl.symm_diff_eq_top (h : is_compl a b) : a ∆ b = ⊤ := (symm_diff_eq_top_iff a b).2 h

@[simp] lemma compl_symm_diff_self : aᶜ ∆ a = ⊤ :=
by simp only [symm_diff_eq, compl_compl, inf_idem, compl_sup_eq_top]

@[simp] lemma symm_diff_compl_self : a ∆ aᶜ = ⊤ := by rw [symm_diff_comm, compl_symm_diff_self]

lemma symm_diff_symm_diff_right' :
  a ∆ (b ∆ c) = (a ⊓ b ⊓ c) ⊔ (a ⊓ bᶜ ⊓ cᶜ) ⊔ (aᶜ ⊓ b ⊓ cᶜ) ⊔ (aᶜ ⊓ bᶜ ⊓ c) :=
calc a ∆ (b ∆ c) = (a ⊓ ((b ⊓ c) ⊔ (bᶜ ⊓ cᶜ))) ⊔
                     (((b ⊓ cᶜ) ⊔ (c ⊓ bᶜ)) ⊓ aᶜ)  : by rw [symm_diff_eq, compl_symm_diff,
                                                            symm_diff_eq]
             ... = (a ⊓ b ⊓ c) ⊔ (a ⊓ bᶜ ⊓ cᶜ) ⊔
                     (b ⊓ cᶜ ⊓ aᶜ) ⊔ (c ⊓ bᶜ ⊓ aᶜ) : by rw [inf_sup_left, inf_sup_right,
                                                            ←sup_assoc, ←inf_assoc, ←inf_assoc]
             ... = (a ⊓ b ⊓ c) ⊔ (a ⊓ bᶜ ⊓ cᶜ) ⊔
                     (aᶜ ⊓ b ⊓ cᶜ) ⊔ (aᶜ ⊓ bᶜ ⊓ c) : begin
                                                       congr' 1,
                                                       { congr' 1,
                                                         rw [inf_comm, inf_assoc], },
                                                       { apply inf_left_right_swap }
                                                     end

end boolean_algebra


import data.set.function

open set function

universes u v
variable  α : Type u
variable  β : Type v
variable  f : α → β
variables s t : set α
variables u v : set β

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si f es inyectiva, entonces
--    f ⁻¹' (f '' s) ⊆ s
-- ----------------------------------------------------------------------

example
  (h : injective f)
  : f ⁻¹' (f '' s) ⊆ s :=
begin
  intros x hx,
  have h1 : f x ∈ f '' s := hx,
  rcases h1 with ⟨y, ys, fyfx⟩,
  have h2 : y = x := h fyfx,
  rw ← h2,
  exact ys,
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s : set α,
h : injective f
⊢ f ⁻¹' (f '' s) ⊆ s
  >> intros x hx,
x : α,
hx : x ∈ f ⁻¹' (f '' s)
⊢ x ∈ s
  >> have h1 : f x ∈ f '' s := hx,
h1 : f x ∈ f '' s
⊢ x ∈ s
  >> rcases h1 with ⟨y, ys, fyfx⟩,
y : α,
ys : y ∈ s,
fyfx : f y = f x
⊢ x ∈ s
  >> have h2 : y = x := h fyfx,
h2 : y = x
⊢ x ∈ s
  >> rw ← h2,
⊢ y ∈ s
  >> exact ys,
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    f '' (f⁻¹' u) ⊆ u
-- ----------------------------------------------------------------------

example : f '' (f⁻¹' u) ⊆ u :=
begin
  rintros y ⟨x, hxu, fxy⟩,
  rw ← fxy,
  exact hxu,
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
u : set β
⊢ f '' (f ⁻¹' u) ⊆ u
  >> rintros y ⟨x, hxu, fxy⟩,
y : β,
x : α,
hxu : x ∈ f ⁻¹' u,
fxy : f x = y
⊢ y ∈ u
  >> rw ← fxy,
⊢ f x ∈ u
  >> exact hxu,
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si f es suprayectiva, entonces
--    u ⊆ f '' (f⁻¹' u)
-- ----------------------------------------------------------------------

example
  (h : surjective f)
  : u ⊆ f '' (f⁻¹' u) :=
begin
  intros y hy,
  rcases h y with ⟨x,hx⟩,
  use x,
  split,
  { simp,
    rw hx,
    exact hy},
  { exact hx },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
u : set β,
h : surjective f
⊢ u ⊆ f '' (f ⁻¹' u)
  >> intros y hy,
y : β,
hy : y ∈ u
⊢ y ∈ f '' (f ⁻¹' u)
  >> rcases h y with ⟨x,hx⟩,
x : α,
hx : f x = y
⊢ y ∈ f '' (f ⁻¹' u)
  >> use x,
⊢ x ∈ f ⁻¹' u ∧ f x = y
  >> split,
| ⊢ x ∈ f ⁻¹' u
|   >> { simp,
| ⊢ f x ∈ u
|   >>   rw hx,
| ⊢ y ∈ u
|   >>   exact hy},
⊢ f x = y
  >> { exact hx },
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si
--    s ⊆ t
-- entonces
--    f '' s ⊆ f '' t
-- ----------------------------------------------------------------------

example
  (h : s ⊆ t)
  : f '' s ⊆ f '' t :=
begin
  rintros y ⟨x,xs,fxy⟩,
  use x,
  split,
  { apply h,
    exact xs },
  { exact fxy },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s t : set α,
h : s ⊆ t
⊢ f '' s ⊆ f '' t
  >> rintros y ⟨x,xs,fxy⟩,
y : β,
x : α,
xs : x ∈ s,
fxy : f x = y
⊢ y ∈ f '' t
  >> use x,
⊢ x ∈ t ∧ f x = y
  >> split,
| ⊢ x ∈ t
|   >> { apply h,
| ⊢ x ∈ s
|   >>   exact xs },
⊢ f x = y
  >> { exact fxy },
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si
--    u ⊆ v
-- entonces
--    f ⁻¹' u ⊆ f ⁻¹' v
-- ----------------------------------------------------------------------

example
  (h : u ⊆ v)
  : f ⁻¹' u ⊆ f ⁻¹' v :=
begin
  intros y hy,
  simp at *,
  apply h,
  exact hy,
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
u v : set β,
h : u ⊆ v
⊢ f ⁻¹' u ⊆ f ⁻¹' v
  >> intros y hy,
y : α,
hy : y ∈ f ⁻¹' u
⊢ y ∈ f ⁻¹' v
  >> simp at *,
hy : f y ∈ u
⊢ f y ∈ v
  >> apply h,
⊢ f y ∈ u
  >> exact hy,
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    f ⁻¹' (u ∪ v) = (f ⁻¹' u) ∪ (f ⁻¹' v)
-- ----------------------------------------------------------------------

example : f ⁻¹' (u ∪ v) = (f ⁻¹' u) ∪ (f ⁻¹' v) :=
begin
  ext x,
  split,
  { intro hx,
    rcases hx with hxu | hxv,
    { left,
      apply hxu },
    { right,
      apply hxv }},
  { intro hx',
    simp,
    rcases hx' with hxu' | hxv',
    { left,
      apply hxu' },
    { right,
      apply hxv' }},
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
u v : set β
⊢ f ⁻¹' (u ∪ v) = f ⁻¹' u ∪ f ⁻¹' v
  >> ext x,
x : α
⊢ x ∈ f ⁻¹' (u ∪ v) ↔ x ∈ f ⁻¹' u ∪ f ⁻¹' v
  >> split,
| ⊢ x ∈ f ⁻¹' (u ∪ v) → x ∈ f ⁻¹' u ∪ f ⁻¹' v
|   >> { intro hx,
| hx : x ∈ f ⁻¹' (u ∪ v)
| ⊢ x ∈ f ⁻¹' u ∪ f ⁻¹' v
|   >>   rcases hx with hxu | hxv,
| | hxu : f x ∈ u
| | ⊢ x ∈ f ⁻¹' u ∪ f ⁻¹' v
| |   >>   { left,
| | ⊢ x ∈ f ⁻¹' u
| |   >>     apply hxu },
| ⊢ x ∈ f ⁻¹' u ∪ f ⁻¹' v
|   >>   { right,
| ⊢ x ∈ f ⁻¹' v
|   >>     apply hxv }},
⊢ x ∈ f ⁻¹' u ∪ f ⁻¹' v → x ∈ f ⁻¹' (u ∪ v)
  >> { intro hx',
hx' : x ∈ f ⁻¹' u ∪ f ⁻¹' v
⊢ x ∈ f ⁻¹' (u ∪ v)
  >>   simp,
⊢ f x ∈ u ∨ f x ∈ v
  >>   rcases hx' with hxu' | hxv',
| hxu' : x ∈ f ⁻¹' u
| ⊢ f x ∈ u ∨ f x ∈ v
|   >>   { left,
| ⊢ f x ∈ u
|   >>     apply hxu' },
hxv' : x ∈ f ⁻¹' v
⊢ f x ∈ u ∨ f x ∈ v
  >>   { right,
⊢ f x ∈ v
  >>     apply hxv' }},
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    f '' (s ∩ t) ⊆ (f '' s) ∩ (f '' t)
-- ----------------------------------------------------------------------

example : f '' (s ∩ t) ⊆ (f '' s) ∩ (f '' t) :=
begin
  rintros y ⟨x,⟨xs,xt⟩,fxy⟩,
  split,
  { use x,
    exact ⟨xs, fxy⟩},
  { use x,
    exact ⟨xt, fxy⟩},
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s t : set α
⊢ f '' (s ∩ t) ⊆ f '' s ∩ f '' t
  >> rintros y ⟨x,⟨xs,xt⟩,fxy⟩,
y : β,
x : α,
fxy : f x = y,
xs : x ∈ s,
xt : x ∈ t
⊢ y ∈ f '' s ∩ f '' t
  >> split,
| ⊢ y ∈ f '' s
|   >> { use x,
| ⊢ x ∈ s ∧ f x = y
|   >>   exact ⟨xs, fxy⟩},
⊢ y ∈ f '' t
  >> { use x,
⊢ x ∈ t ∧ f x = y
  >>   exact ⟨xt, fxy⟩},
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que si f es inyectiva, entonces
--    (f '' s) ∩ (f '' t) ⊆ f '' (s ∩ t)
-- ----------------------------------------------------------------------

example
  (h : injective f)
  : (f '' s) ∩ (f '' t) ⊆ f '' (s ∩ t) :=
begin
  rintros y ⟨ys,yt⟩,
  rcases ys with ⟨x,xs,fxy⟩,
  use x,
  split,
  { split,
    { exact xs },
    { rcases yt with ⟨z,zs,fzy⟩,
      rw ←fzy at fxy,
      rw h fxy,
      exact zs }},
  { exact fxy },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s t : set α,
h : injective f
⊢ f '' s ∩ f '' t ⊆ f '' (s ∩ t)
  >> rintros y ⟨ys,yt⟩,
y : β,
ys : y ∈ f '' s,
yt : y ∈ f '' t
⊢ y ∈ f '' (s ∩ t)
  >> rcases ys with ⟨x,xs,fxy⟩,
x : α,
xs : x ∈ s,
fxy : f x = y
⊢ y ∈ f '' (s ∩ t)
  >> use x,
⊢ x ∈ s ∩ t ∧ f x = y
  >> split,
| ⊢ x ∈ s ∩ t
|   >> { split,
| | ⊢ x ∈ s
| |   >>   { exact xs },
| ⊢ x ∈ t
|   >>   { rcases yt with ⟨z,zs,fzy⟩,
| z : α,
| zs : z ∈ t,
| fzy : f z = y
| ⊢ x ∈ t
|   >>     rw ←fzy at fxy,
| fxy : f x = f z
| ⊢ x ∈ t
|   >>     rw h fxy,
| fxy : f x = f z
| ⊢ z ∈ t
|   >>     exact zs }},
⊢ f x = y
  >> { exact fxy },
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    (f '' s) \ (f '' t) ⊆ f '' (s \ t)
-- ----------------------------------------------------------------------

example : (f '' s) \ (f '' t) ⊆ f '' (s \ t) :=
begin
  rintros y ⟨h1,h2⟩,
  rcases h1 with ⟨x,xs,fxy⟩,
  use x,
  split,
  { split,
    { exact xs },
    { intro h3,
      apply h2,
      use x,
      exact ⟨h3, fxy⟩}},
  { exact fxy },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s t : set α
⊢ f '' s \ f '' t ⊆ f '' (s \ t)
  >> rintros y ⟨h1,h2⟩,
y : β,
h1 : y ∈ f '' s,
h2 : y ∉ f '' t
⊢ y ∈ f '' (s \ t)
  >> rcases h1 with ⟨x,xs,fxy⟩,
x : α,
xs : x ∈ s,
fxy : f x = y
⊢ y ∈ f '' (s \ t)
  >> use x,
⊢ x ∈ s \ t ∧ f x = y
  >> split,
| ⊢ x ∈ s \ t
|   >> { split,
| ⊢ x ∈ s
| |   >>   { exact xs },
| ⊢ (λ (a : α), a ∉ t) x
|   >>   { intro h3,
| h3 : x ∈ t
| ⊢ false
|   >>     apply h2,
| ⊢ y ∈ f '' t
|   >>     use x,
| ⊢ x ∈ t ∧ f x = y
|   >>     exact ⟨h3, fxy⟩}},
⊢ f x = y
  >> { exact fxy },
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    (f ⁻¹' u) \ (f ⁻¹' v) ⊆ f ⁻¹' (u \ v)
-- ----------------------------------------------------------------------

example : (f ⁻¹' u) \ (f ⁻¹' v) ⊆ f ⁻¹' (u \ v) :=
begin
  rintros x ⟨hxu,hxv⟩,
  simp,
  split,
  { exact hxu },
  { intro h,
    apply hxv,
    exact h },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
u v : set β
⊢ f ⁻¹' u \ f ⁻¹' v ⊆ f ⁻¹' (u \ v)
  >> rintros x ⟨hxu,hxv⟩,
x : α,
hxu : x ∈ f ⁻¹' u,
hxv : x ∉ f ⁻¹' v
⊢ x ∈ f ⁻¹' (u \ v)
  >> simp,
⊢ f x ∈ u ∧ f x ∉ v
  >> split,
| ⊢ f x ∈ u
|   >> { exact hxu },
⊢ f x ∉ v
  >> { intro h,
h : f x ∈ v
⊢ false
  >>   apply hxv,
⊢ x ∈ f ⁻¹' v
  >>   exact h },
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    (f '' s) ∩ v = f '' (s ∩ (f ⁻¹' v))
-- ----------------------------------------------------------------------

example : (f '' s) ∩ v = f '' (s ∩ (f ⁻¹' v)) :=
begin
  ext y,
  split,
  { rintros ⟨ys,yv⟩,
    rcases ys with ⟨x,xs,fxy⟩,
    use x,
    split,
    { split,
      { exact xs },
      { simp,
        rw fxy,
        exact yv }},
    { exact fxy }},
  { intro h,
    rcases h with ⟨x,⟨hxs,hxv⟩,fxy⟩,
    split,
    { rw ←fxy,
      exact mem_image_of_mem f hxs },
    { rw ←fxy,
      exact hxv }},
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s : set α,
v : set β
⊢ f '' s ∩ v = f '' (s ∩ f ⁻¹' v)
  >> ext y,
y : β
⊢ y ∈ f '' s ∩ v ↔ y ∈ f '' (s ∩ f ⁻¹' v)
  >> split,
| ⊢ y ∈ f '' s ∩ v → y ∈ f '' (s ∩ f ⁻¹' v)
|   >> { rintros ⟨ys,yv⟩,
| ys : y ∈ f '' s,
| yv : y ∈ v
| ⊢ y ∈ f '' (s ∩ f ⁻¹' v)
|   >>   rcases ys with ⟨x,xs,fxy⟩,
| x : α,
| xs : x ∈ s,
| fxy : f x = y
| ⊢ y ∈ f '' (s ∩ f ⁻¹' v)
|   >>   use x,
⊢ x ∈ s ∩ f ⁻¹' v ∧ f x = y
|   >>   split,
| ⊢ x ∈ s ∩ f ⁻¹' v
|   >>   { split,
| |  ⊢ x ∈ s
| |    >>     { exact xs },
| ⊢ x ∈ f ⁻¹' v
|   >>     { simp,
| ⊢ f x ∈ v
|   >>       rw fxy,
| ⊢ y ∈ v
|   >>       exact yv }},
| ⊢ f x = y
|   >>   { exact fxy }},
⊢ y ∈ f '' (s ∩ f ⁻¹' v) → y ∈ f '' s ∩ v
  >> { intro h,
h : y ∈ f '' (s ∩ f ⁻¹' v)
⊢ y ∈ f '' s ∩ v
  >>   rcases h with ⟨x,⟨hxs,hxv⟩,fxy⟩,
x : α,
fxy : f x = y,
hxs : x ∈ s,
hxv : x ∈ f ⁻¹' v
⊢ y ∈ f '' s ∩ v
  >>   split,
| ⊢ y ∈ f '' s
|   >>   { rw ←fxy,
| ⊢ f x ∈ f '' s
|   >>     exact mem_image_of_mem f hxs },
⊢ y ∈ v
  >>   { rw ←fxy,
⊢ f x ∈ v
  >>     exact hxv }},
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    f '' (s ∩ f ⁻¹' u) ⊆ (f '' s) ∪ u
-- ----------------------------------------------------------------------

example : f '' (s ∩ f ⁻¹' u) ⊆ (f '' s) ∪ u :=
begin
  intros y h,
  rcases h with ⟨x,⟨xs,xu⟩,fxy⟩,
  right,
  rw ←fxy,
  exact xu,
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s : set α,
u : set β
⊢ f '' (s ∩ f ⁻¹' u) ⊆ f '' s ∪ u
  >> intros y h,
y : β,
h : y ∈ f '' (s ∩ f ⁻¹' u)
⊢ y ∈ f '' s ∪ u
  >> rcases h with ⟨x,⟨xs,xu⟩,fxy⟩,
x : α,
fxy : f x = y,
xs : x ∈ s,
xu : x ∈ f ⁻¹' u
⊢ y ∈ f '' s ∪ u
  >> right,
⊢ y ∈ u
  >> rw ←fxy,
⊢ f x ∈ u
  >> exact xu,
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    s ∩ f ⁻¹' u ⊆ f ⁻¹' ((f '' s) ∩ u) :=
-- ----------------------------------------------------------------------

example : s ∩ f ⁻¹' u ⊆ f ⁻¹' ((f '' s) ∩ u) :=
begin
  rintros x ⟨xs,xu⟩,
  simp at xu,
  split,
  { exact mem_image_of_mem f xs },
  { exact xu },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s : set α,
u : set β
⊢ s ∩ f ⁻¹' u ⊆ f ⁻¹' (f '' s ∩ u)
  >> rintros x ⟨xs,xu⟩,
x : α,
xs : x ∈ s,
xu : x ∈ f ⁻¹' u
⊢ x ∈ f ⁻¹' (f '' s ∩ u)
  >> simp at xu,
xu : f x ∈ u
⊢ x ∈ f ⁻¹' (f '' s ∩ u)
  >> split,
| ⊢ f x ∈ f '' s
|   >> { exact mem_image_of_mem f xs },
⊢ f x ∈ u
  >> { exact xu },
no goals
-/

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    s ∪ f ⁻¹' u ⊆ f ⁻¹' ((f '' s) ∪ u)
-- ----------------------------------------------------------------------

example : s ∪ f ⁻¹' u ⊆ f ⁻¹' ((f '' s) ∪ u) :=
begin
  rintros x (xs | xu),
  { left,
    exact mem_image_of_mem f xs },
  { right,
    exact xu },
end

-- Prueba
-- ======

/-
α : Type u,
β : Type v,
f : α → β,
s : set α,
u : set β
⊢ s ∪ f ⁻¹' u ⊆ f ⁻¹' (f '' s ∪ u)
  >> rintros x (xs | xu),
| x : α,
| xs : x ∈ s
| ⊢ x ∈ f ⁻¹' (f '' s ∪ u)
|   >> { left,
| ⊢ f x ∈ f '' s
|   >>   exact mem_image_of_mem f xs },
xu : x ∈ f ⁻¹' u
⊢ x ∈ f ⁻¹' (f '' s ∪ u)
  >> { right,
⊢ f x ∈ u
  >>   exact xu },
no goals
-/