CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2019 Neil Strickland. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Neil Strickland
-/
import tactic.basic

import data.pnat.basic data.nat.prime data.multiset data.int.basic data.int.gcd
 algebra.group algebra.group_power algebra.ordered_ring

/-- The type of multisets of prime numbers.  Unique factorization
 gives an equivalence between this set and ℕ+, as we will formalize
 below. -/

def prime_multiset := multiset nat.primes

namespace prime_multiset

instance : inhabited prime_multiset :=
by unfold prime_multiset; apply_instance

instance : has_repr prime_multiset :=
by { dsimp [prime_multiset], apply_instance }

instance : canonically_ordered_monoid prime_multiset :=
by { dsimp [prime_multiset], apply_instance }

instance : lattice.distrib_lattice prime_multiset :=
by { dsimp [prime_multiset], apply_instance }

instance : lattice.semilattice_sup_bot prime_multiset :=
by { dsimp [prime_multiset], apply_instance }

instance : has_sub prime_multiset :=
by { dsimp [prime_multiset], apply_instance }

theorem add_sub_of_le {u v : prime_multiset} : u ≤ v → u + (v - u) = v :=
multiset.add_sub_of_le

/-- The multiset consisting of a single prime
-/
def of_prime (p : nat.primes) : prime_multiset := (p :: 0)

theorem card_of_prime (p : nat.primes) : multiset.card (of_prime p) = 1 := rfl

/-- 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 to_nat_multiset : prime_multiset → multiset ℕ :=
λ v, v.map (λ p, (p : ℕ))

instance coe_nat : has_coe prime_multiset (multiset ℕ) := ⟨to_nat_multiset⟩

instance coe_nat_hom : is_add_monoid_hom (coe : prime_multiset → multiset ℕ) :=
by { unfold_coes, dsimp [to_nat_multiset], apply_instance }

theorem coe_nat_inj : function.injective (coe : prime_multiset → multiset ℕ) :=
multiset.injective_map nat.primes.coe_nat_inj

theorem coe_nat_of_prime (p : nat.primes) :
((of_prime p) : multiset ℕ) = (p : ℕ) :: 0 := rfl

theorem coe_nat_prime (v : prime_multiset)
(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' }

def to_pnat_multiset : prime_multiset → multiset ℕ+ :=
λ v, v.map (λ p, (p : ℕ+))

instance coe_pnat : has_coe prime_multiset (multiset ℕ+) := ⟨to_pnat_multiset⟩

instance coe_pnat_hom : is_add_monoid_hom (coe : prime_multiset → multiset ℕ+) :=
by { unfold_coes, dsimp [to_pnat_multiset], apply_instance }

theorem coe_pnat_inj : function.injective (coe : prime_multiset → multiset ℕ+) :=
multiset.injective_map nat.primes.coe_pnat_inj

theorem coe_pnat_of_prime (p : nat.primes) :
((of_prime p) : multiset ℕ+) = (p : ℕ+) :: 0 := rfl

theorem coe_pnat_prime (v : prime_multiset)
  (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' }

instance coe_multiset_pnat_nat : has_coe (multiset ℕ+) (multiset ℕ) :=
⟨λ v, v.map (λ n, (n : ℕ))⟩

theorem coe_pnat_nat (v : prime_multiset) :
  ((v : (multiset ℕ+)) : (multiset ℕ)) = (v : multiset ℕ) :=
by { change (v.map (coe : nat.primes → ℕ+)).map subtype.val = v.map subtype.val,
     rw [multiset.map_map], congr }

def prod (v : prime_multiset) : ℕ+ := (v : multiset pnat).prod

theorem coe_prod (v : prime_multiset) : (v.prod : ℕ) = (v : multiset ℕ).prod :=
begin
  let h : (v.prod : ℕ) = ((v.map coe).map coe).prod :=
  (v.to_pnat_multiset.prod_hom coe).symm,
  rw [multiset.map_map] at h,
  have : (coe : ℕ+ → ℕ) ∘ (coe : nat.primes → ℕ+) = coe := funext (λ p, rfl),
  rw[this] at h, exact h,
end

theorem prod_of_prime (p : nat.primes) : (of_prime p).prod = (p : ℕ+) :=
by { change multiset.prod ((p : ℕ+) :: 0) = (p : ℕ+),
     rw [multiset.prod_cons, multiset.prod_zero, mul_one] }

def of_nat_multiset
  (v : multiset ℕ) (h : ∀ (p : ℕ), p ∈ v → p.prime) : prime_multiset :=
@multiset.pmap ℕ nat.primes nat.prime (λ p hp, ⟨p, hp⟩) v h

theorem to_of_nat_multiset (v : multiset ℕ) (h) :
  ((of_nat_multiset v h) : multiset ℕ) = v :=
begin
  unfold_coes,
  dsimp [of_nat_multiset, to_nat_multiset],
  have : (λ (p : ℕ) (h : p.prime), ((⟨p, h⟩ : nat.primes) : ℕ)) = (λ p h, id p) :=
    by {funext p h, refl},
  rw [multiset.map_pmap, this, multiset.pmap_eq_map, multiset.map_id]
end

theorem prod_of_nat_multiset (v : multiset ℕ) (h) :
  ((of_nat_multiset v h).prod : ℕ) = (v.prod : ℕ) :=
by rw[coe_prod, to_of_nat_multiset]

def of_pnat_multiset
  (v : multiset ℕ+) (h : ∀ (p : ℕ+), p ∈ v → p.prime) : prime_multiset :=
@multiset.pmap ℕ+ nat.primes pnat.prime (λ p hp, ⟨(p : ℕ), hp⟩) v h

theorem to_of_pnat_multiset (v : multiset ℕ+) (h) :
 ((of_pnat_multiset v h) : multiset ℕ+) = v :=
begin
  unfold_coes, dsimp[of_pnat_multiset, to_pnat_multiset],
  have : (λ (p : ℕ+) (h : p.prime), ((coe : nat.primes → ℕ+) ⟨p, h⟩)) = (λ p h, id p) :=
    by {funext p h, apply subtype.eq, refl},
  rw[multiset.map_pmap, this, multiset.pmap_eq_map, multiset.map_id]
end

theorem prod_of_pnat_multiset (v : multiset ℕ+) (h) :
 ((of_pnat_multiset v h).prod : ℕ+) = v.prod :=
by { dsimp [prod], rw [to_of_pnat_multiset] }

/-- Lists can be coerced to multisets; here we have some results
 about how this interacts with our constructions on multisets.
-/
def of_nat_list (l : list ℕ) (h : ∀ (p : ℕ), p ∈ l → p.prime) : prime_multiset :=
of_nat_multiset (l : multiset ℕ) h

theorem prod_of_nat_list (l : list ℕ) (h) : ((of_nat_list l h).prod : ℕ) = l.prod :=
by { have := prod_of_nat_multiset (l : multiset ℕ) h,
     rw [multiset.coe_prod] at this, exact this }

def of_pnat_list (l : list ℕ+) (h : ∀ (p : ℕ+), p ∈ l → p.prime) : prime_multiset :=
of_pnat_multiset (l : multiset ℕ+) h

theorem prod_of_pnat_list (l : list ℕ+) (h) : (of_pnat_list l h).prod = l.prod :=
by { have := prod_of_pnat_multiset (l : multiset ℕ+) h,
     rw [multiset.coe_prod] at this, exact this }

/-- The product map gives a homomorphism from the additive monoid
 of multisets to the multiplicative monoid ℕ+.
-/

theorem prod_zero : (0 : prime_multiset).prod = 1 :=
by { dsimp [prod], exact multiset.prod_zero }

theorem prod_add (u v : prime_multiset) : (u + v).prod = u.prod * v.prod :=
by { dsimp [prod],
     rw [is_add_monoid_hom.map_add (coe : prime_multiset → multiset ℕ+)],
     rw [multiset.prod_add] }

theorem prod_smul (d : ℕ) (u : prime_multiset) :
 (add_monoid.smul d u).prod = u.prod ^ d :=
by { induction d with d ih, refl,
     rw[succ_smul, prod_add, ih, nat.succ_eq_add_one, pow_succ, mul_comm] }

end prime_multiset

namespace pnat

/-- The prime factors of n, regarded as a multiset -/
def factor_multiset (n : ℕ+) : prime_multiset :=
prime_multiset.of_nat_list (nat.factors n) (@nat.mem_factors n)

/-- The product of the factors is the original number -/
theorem prod_factor_multiset (n : ℕ+) : (factor_multiset n).prod = n :=
eq $ by { dsimp [factor_multiset],
          rw [prime_multiset.prod_of_nat_list],
          exact nat.prod_factors n.pos }

theorem coe_nat_factor_multiset (n : ℕ+) :
  ((factor_multiset n) : (multiset ℕ)) = ((nat.factors n) : multiset ℕ) :=
prime_multiset.to_of_nat_multiset (nat.factors n) (@nat.mem_factors n)

end pnat

namespace prime_multiset

/-- If we start with a multiset of primes, take the product and
 then factor it, we get back the original multiset. -/
theorem factor_multiset_prod (v : prime_multiset) :
  v.prod.factor_multiset = v :=
begin
  apply prime_multiset.coe_nat_inj,
  rw [v.prod.coe_nat_factor_multiset, prime_multiset.coe_prod],
  rcases v with l,
  unfold_coes,
  dsimp [prime_multiset.to_nat_multiset],
  rw [multiset.coe_prod],
  let l' := l.map (coe : nat.primes → ℕ),
  have : ∀ (p : ℕ), p ∈ l' → p.prime :=
    λ 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,
end

end prime_multiset

namespace pnat

/-- Positive integers biject with multisets of primes. -/
def factor_multiset_equiv : ℕ+ ≃ prime_multiset :=
{ to_fun    := factor_multiset,
  inv_fun   := prime_multiset.prod,
  left_inv  := prod_factor_multiset,
  right_inv := prime_multiset.factor_multiset_prod }

/-- Factoring gives a homomorphism from the multiplicative
 monoid ℕ+ to the additive monoid of multisets. -/
theorem factor_multiset_one : factor_multiset 1 = 0 := rfl

theorem factor_multiset_mul (n m : ℕ+) :
  factor_multiset (n * m) = (factor_multiset n) + (factor_multiset m) :=
begin
  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[← prime_multiset.prod_add],
  repeat {rw[prime_multiset.factor_multiset_prod]},
end

theorem factor_multiset_pow (n : ℕ+) (m : ℕ) :
  factor_multiset (n ^ m) = add_monoid.smul m (factor_multiset n) :=
begin
  let u := factor_multiset n,
  have : n = u.prod := (prod_factor_multiset n).symm,
  rw[this, ← prime_multiset.prod_smul],
  repeat {rw[prime_multiset.factor_multiset_prod]},
end

/-- Factoring a prime gives the corresponding one-element multiset. -/
theorem factor_multiset_of_prime (p : nat.primes) :
  (p : ℕ+).factor_multiset = prime_multiset.of_prime p :=
begin
  apply factor_multiset_equiv.symm.injective,
  change (p : ℕ+).factor_multiset.prod = (prime_multiset.of_prime p).prod,
  rw[(p : ℕ+).prod_factor_multiset, prime_multiset.prod_of_prime],
end

/-- We now have four different results that all encode the
 idea that inequality of multisets corresponds to divisibility
 of positive integers. -/
theorem factor_multiset_le_iff {m n : ℕ+} :
  factor_multiset m ≤ factor_multiset n ↔ m ∣ n :=
begin
  split,
  { intro h,
    rw [← prod_factor_multiset m, ← prod_factor_multiset m],
    apply dvd_intro (n.factor_multiset - m.factor_multiset).prod,
    rw [← prime_multiset.prod_add, prime_multiset.factor_multiset_prod,
        prime_multiset.add_sub_of_le h, prod_factor_multiset] },
  { intro  h,
    rw [← mul_div_exact h, factor_multiset_mul],
    exact le_add_right (le_refl _) }
end

theorem factor_multiset_le_iff' {m : ℕ+} {v : prime_multiset}:
 factor_multiset m ≤ v ↔ m ∣ v.prod :=
by { let h := @factor_multiset_le_iff m v.prod,
     rw [v.factor_multiset_prod] at h, exact h }

end pnat

namespace prime_multiset

theorem prod_dvd_iff {u v : prime_multiset} : u.prod ∣ v.prod ↔ u ≤ v :=
by { let h := @pnat.factor_multiset_le_iff' u.prod v,
     rw [u.factor_multiset_prod] at h, exact h.symm }

theorem prod_dvd_iff' {u : prime_multiset} {n : ℕ+} : u.prod ∣ n ↔ u ≤ n.factor_multiset :=
by { let h := @prod_dvd_iff u n.factor_multiset,
     rw [n.prod_factor_multiset] at h, exact h }

end prime_multiset

namespace pnat

/-- The gcd and lcm operations on positive integers correspond
 to the inf and sup operations on multisets.
-/
theorem factor_multiset_gcd (m n : ℕ+) :
 factor_multiset (gcd m n) = (factor_multiset m) ⊓ (factor_multiset n) :=
begin
  apply le_antisymm,
  { apply lattice.le_inf_iff.mpr; split; apply factor_multiset_le_iff.mpr,
    exact gcd_dvd_left m n, exact gcd_dvd_right m n},
  { rw[← prime_multiset.prod_dvd_iff, prod_factor_multiset],
    apply dvd_gcd; rw[prime_multiset.prod_dvd_iff'],
    exact lattice.inf_le_left, exact lattice.inf_le_right}
end

theorem factor_multiset_lcm (m n : ℕ+) :
 factor_multiset (lcm m n) = (factor_multiset m) ⊔ (factor_multiset n) :=
begin
  apply le_antisymm,
  { rw[← prime_multiset.prod_dvd_iff, prod_factor_multiset],
    apply lcm_dvd; rw[← factor_multiset_le_iff'],
    exact lattice.le_sup_left, exact lattice.le_sup_right},
  { apply lattice.sup_le_iff.mpr; split; apply factor_multiset_le_iff.mpr,
    exact dvd_lcm_left m n, exact dvd_lcm_right m n },
end

/-- The number of occurrences of p in the factor multiset of m
 is the same as the p-adic valuation of m. -/
theorem count_factor_multiset (m : ℕ+) (p : nat.primes) (k : ℕ) :
 (p : ℕ+) ^ k ∣ m ↔ k ≤ m.factor_multiset.count p :=
begin
  intros,
  rw [multiset.le_count_iff_repeat_le],
  rw [← factor_multiset_le_iff, factor_multiset_pow, factor_multiset_of_prime],
  congr' 2,
  apply multiset.eq_repeat.mpr,
  split,
  { rw [multiset.card_smul, prime_multiset.card_of_prime, mul_one] },
  { have : ∀ (m : ℕ), add_monoid.smul m (p::0) = multiset.repeat p m :=
    λ m, by {induction m with m ih, { refl },
             rw [succ_smul, multiset.repeat_succ, ih],
             rw[multiset.cons_add, zero_add] },
    intros q h, rw [prime_multiset.of_prime, this k] at h,
    exact multiset.eq_of_mem_repeat h }
end

end pnat

namespace prime_multiset

theorem prod_inf (u v : prime_multiset) :
 (u ⊓ v).prod = pnat.gcd u.prod v.prod :=
begin
  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.factor_multiset_gcd n m, pnat.prod_factor_multiset]
end

theorem prod_sup (u v : prime_multiset) :
 (u ⊔ v).prod = pnat.lcm u.prod v.prod :=
begin
  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.factor_multiset_lcm n m, pnat.prod_factor_multiset]
end

end prime_multiset