inductive Vector (α : Type u): Nat → Type u where
| nil : Vector α 0
| cons (head : α) (tail : Vector α n) : Vector α (n+1)

class Order (α : Type u) extends LE α, LT α, Max α where
  ltDecidable : DecidableRel (@LT.lt α _)
  max_def x y : max x y = if x < y then x else y

namespace Vector
  def mem (a : α) : Vector α n → Prop
  | nil => False
  | cons b l => a = b ∨ mem a l

  def foldr (f : α → β → β) (init : β) : Vector α n → β
  | nil     => init
  | cons a l => f a (foldr f init l)

  theorem foldr_max [Order β] {v: Vector α n} (f : α → β) (init : β)
    (h: v.mem y):
    f y ≤ v.foldr (λ x acc => max (f x) acc) init := by
    induction v <;> simp only[foldr,Order.max_def]
    · admit
    · split <;> admit
end Vector


/- Defines theorems about init and supporting function -/
import galois.list.simplify_eq

universe variable u

namespace list

variable {α : Type u}

theorem length_init : ∀ (l : list α), length (init l) = length l - 1
| nil := by simp [init]
| (x::l)   :=
begin
  have p := length_init l,
  cases l with b r,
  -- Case where l = nil
  { simp [init] },
  -- Case where l = b :: r
  {  simp [init, p, nat.add_sub_cancel, nat.add_sub_cancel_left], }
end

-- A list with length 0 must be nil nil
theorem length_0_implies_nil {l : list α} (h : length l = 0) : l = nil :=
begin
  cases l,
  simp,
  contradiction
end

-- Maps a non-empty list to the initial part and last part.
theorem init_append_last_self (l : list α) (pr : l ≠ nil) : init l ++ [last l pr] = l :=
begin
  revert pr,
  induction l with x l ind,
  { contradiction },
  { intros pr,
    cases l with y l,
    { simp [init] },
    { simp [init, last, ind (last._main._proof_2 y l)] }
  },
end

end list


/- Along with the half-finished definition of substitution here is an attempt to define terms using lists instead of vectors.
    The problem is, since the type "list" does ot say anything about the size of the list, it is possible to use this definition to define terms that apply a function of any arity to a list of any length.
    To prevent this from happening, we might be able to use an auxiliary well_formed_term definition, and prove that it is preserved after operations like substitution.
    However, the same well-founded recursion problem reared its head at a certain point -- here it was after writing a really long theorem about the preservation of well-formed-ness.
    If you wait for it to compile, you can read the error message below on

        mutual theorem sub_term_well_formed, sub_term_list_well_formed

    It was too much to think about creating a well-founded relation over these theorems; it takes about two minutes just for Lean to read through the tactics as it is..
    I'm pretty sure that this approach is not any easier. -/

structure Language :=
    language :: (relations : Type) (functions : Type ) (arityF :  functions →  nat) (arityR : relations → nat)
variable L : Language
variable n : nat

universe u
inductive term
    | var : nat → term
    | apply: L.functions → list term → term

mutual def well_formed_term, well_formed_term_list
with well_formed_term : term L -> Prop
    | (term.var L _) := true
    | (term.apply f l) := ((L.arityF f) = list.length l) ∧ (well_formed_term_list l)
with well_formed_term_list : list (term L) → Prop
    | [] := true
    | (list.cons t ts) := (well_formed_term t) ∧ well_formed_term_list ts
def nat_list_union : list nat → list nat → list nat
    | [] l := l
    | (list.cons n ns) l := if  n ∈ l then list.cons n (nat_list_union ns l) else nat_list_union ns l

def nat_list_remove : nat → list nat → list nat
    | _ [] := []
    | n (list.cons m ms) := if (n=m) then nat_list_remove n ms else list.cons n (nat_list_remove n ms)
def nat_list_max : list nat → nat
    | [] := 0
    | (list.cons n ns) := if n ≥ nat_list_max ns then n else nat_list_max ns
lemma nat_list_max_is_max : ∀ (l : list nat) (n : nat), n ∈ l → (nat_list_max l) ≥ n :=
 begin
    intros l n n_in_l,
    induction l,
        apply absurd,
        apply list.not_mem_nil,
        exact n_in_l,
        exact not_false,
    simp [nat_list_max],
    have ex_mid : (l_hd ≥ nat_list_max l_tl) ∨ ¬ (l_hd ≥ nat_list_max l_tl), by apply decidable.em,
    cases ex_mid,
        have n_or : (n = l_hd ∨ n ∈ l_tl), by {apply list.eq_or_mem_of_mem_cons, exact n_in_l},
        rewrite if_pos,
        cases n_or,
            apply le_of_eq,
            exact n_or,
            apply le_trans,
            apply l_ih,
            exact n_or,
        exact ex_mid,
        exact ex_mid,
        rewrite if_neg,
        have n_or : (n = l_hd ∨ n ∈ l_tl), by {apply list.eq_or_mem_of_mem_cons, exact n_in_l},
        cases n_or,
            apply le_trans,
            have hd_lt : l_hd < nat_list_max l_tl, by {apply iff.mpr, apply lt_iff_not_ge,exact ex_mid},
            apply le_of_lt,
            simp [n_or],
            exact hd_lt,
        apply le_of_eq,
        apply eq.refl,
        apply l_ih,
        exact n_or,
    exact ex_mid
 end
def new_nat_list : list nat → nat := λ l, nat_list_max l + 1
lemma new_is_new : ∀ (l : list nat), ¬ (new_nat_list l ∈ l) :=
begin
    simp [new_nat_list],
    intros l new_in,
    apply absurd,
    apply nat.lt_succ_self, exact nat_list_max l,
    apply not_lt_of_ge,
    apply nat_list_max_is_max,
    simp [nat.succ_eq_add_one],
    exact new_in
end

mutual def free_vars, free_vars_list
with free_vars : (term L) → list nat
    | (term.var L n) := [n]
    | (term.apply f l) := free_vars_list l
with free_vars_list : list (term L) → list nat
    | [] := []
    | (list.cons t ts) := nat_list_union (free_vars t) (free_vars_list ts)
inductive formula : Type
    | equal : term L → term L → formula
    | atomic : ∀ r : L.relations, list (term L) → formula
    | imp : formula → formula → formula
    | not :  formula → formula
    | forallF : nat → formula → formula
open formula
def choice: list nat → nat := λ l, 1 + nat_list_max l

def sub_list :Type := list (nat × term L)
def in_sub_list : nat → sub_list L → Prop
    | n [] := false
    | n ((m, _) :: s_tl) := (n=m) ∨ (in_sub_list n s_tl)
def find_term_in_sub_list : nat → sub_list L →  term L
    | n [] := term.var L n
    | n ((m, t) :: sl_tl) := if n =m then t else find_term_in_sub_list n sl_tl

def well_formed_sub_list : sub_list L → Prop
    | [] := true
    | ((m, t) :: l_tl) := (¬ in_sub_list L m l_tl) ∧ well_formed_term L t ∧ well_formed_sub_list l_tl
theorem eq_or_mem_of_mem_cons {n m: nat} {l : sub_list L} {t : term L}: (in_sub_list L n ((m, t) :: l)) → n = m ∨ (in_sub_list L n l) :=
assume h, h

mutual def substitute_term, substitute_term_list
with substitute_term : sub_list L → term L → term L
   | sl (term.var L n) := find_term_in_sub_list L n sl
   | sl (term.apply f ts) := term.apply f (substitute_term_list sl ts)
with substitute_term_list : sub_list L → list (term L ) → list (term L)
    | sl [] := []
    | sl (t :: ts) := (substitute_term sl t) :: (substitute_term_list sl ts)
lemma sub_term_list_preserves_length {sl : sub_list L} {ts : list (term L)} : list.length ts = list.length (substitute_term_list L sl ts) :=
begin
induction ts,
simp [list.length, substitute_term_list],
simp [list.length_cons, substitute_term_list, ts_ih]
end

lemma rw_well_formed_term_definition : Π {f : L.functions} {ts : list (term L)}, well_formed_term L (term.apply f ts) → L.arityF f = list.length ts ∧ well_formed_term_list L ts := by {simp [well_formed_term], intros f ts, apply id}
lemma rw_well_formed_term_list_definition : Π {t ts}, well_formed_term_list L (t :: ts) → well_formed_term L t ∧ (well_formed_term_list L ts) := by {simp [well_formed_term_list], intros t ts wftl,exact wftl}
lemma rw_well_formed_sub_list_definition : Π {hd_fst hd_snd tl}, well_formed_sub_list L ((hd_fst, hd_snd) :: tl) → well_formed_term L hd_snd ∧ well_formed_sub_list L tl  := by {simp [well_formed_sub_list], intros fst snd tl wfsl, apply and.elim_right, exact wfsl}
lemma find_term_well_formed : Π {n : nat} {sl : sub_list L}, well_formed_sub_list L sl → well_formed_term L (find_term_in_sub_list L n sl) := begin
    intros n sl wfsl,
    induction sl,
    simp [find_term_in_sub_list, well_formed_term],
    cases sl_hd,
    simp [find_term_in_sub_list],
        have ex_mid : n = sl_hd_fst ∨ ¬ n = sl_hd_fst, by {apply decidable.em},
        cases ex_mid,
        simp [if_pos ex_mid],
        apply and.elim_left,
        apply rw_well_formed_sub_list_definition,
        exact wfsl,
        simp [if_neg ex_mid],
        apply sl_ih,
        apply and.elim_right,
        apply rw_well_formed_sub_list_definition,
        exact wfsl
end
mutual theorem sub_term_well_formed, sub_term_list_well_formed
with sub_term_well_formed : Π {t: term L} {sl : sub_list L},  well_formed_term L t →  well_formed_sub_list L sl → well_formed_term L (substitute_term L sl t)
    | t sl wft wfsl :=
        begin

            induction t,
                simp [substitute_term],
                induction sl,
                simp [find_term_in_sub_list, well_formed_term],
                cases sl_hd,
                    simp [find_term_in_sub_list],
                    have ex_mid : t = sl_hd_fst ∨ ¬ t = sl_hd_fst, by apply decidable.em,
                    cases ex_mid,
                        simp [if_pos ex_mid],
                        apply and.elim_left,
                        apply and.elim_right,
                        exact wfsl,
                        simp [if_neg ex_mid],
                        have wf_sl : well_formed_sub_list L sl_tl, by {apply and.elim_right, apply and.elim_right, apply wfsl},
                        apply sl_ih,
                        exact wf_sl,
            simp [substitute_term,well_formed_term],
            apply and.intro,
            apply eq.trans,
            apply and.elim_left,
            apply rw_well_formed_term_definition,
            exact wft,
        apply sub_term_list_preserves_length,
        apply sub_term_list_well_formed,
        apply and.elim_right,
        apply rw_well_formed_term_definition,
        exact wft,
        exact wfsl
end
with sub_term_list_well_formed : Π {ts : list (term L)} {sl : sub_list L}, well_formed_term_list L ts → well_formed_sub_list L sl → (well_formed_term_list L (substitute_term_list L sl ts))
    | ts sl wftl wfsl := begin
    repeat {simp [substitute_term_list]},
    induction ts,
        simp [substitute_term_list, substitute_term_list, substitute_term_list],
        simp [substitute_term_list, well_formed_term_list],
        apply and.intro,
            apply sub_term_well_formed,
            apply and.elim_left,
            apply rw_well_formed_term_list_definition,
            exact wftl,
            exact wfsl,
        apply ts_ih,
        apply and.elim_right,
        apply rw_well_formed_term_list_definition,
        exact wftl,
end

definition substitute_formula : list nat → list (term L) → formula L := sorry


-- 5.8 Exercises
-- #1

-- 4.6 Exercises
-- #1

variables (α : Type*) (p q : α → Prop)

example : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
begin
  apply iff.intro,
    intro h,
    split,
      intro a,
      have hpq : p a ∧ q a := h a,
      cases hpq with hp hq,
      assumption,
    intro a,
    have hpq : p a ∧ q a := h a,
    cases hpq with hp hq,
    assumption,
  intro h,
  cases h with hp hq,
  intro a,
  split,
    exact hp a,
  exact hq a,
end

example : (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
begin
  intro h,
  intro hp,
  intro a,
  have hpq : p a → q a := h a,
  have hpa : p a := hp a,
  apply hpq,
  assumption,
end

example : (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
begin
  intros h a,
  cases h with hp hq,
    left,
    exact hp a,
  right,
  exact hq a,
end


/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.calculus.extend_deriv
import Mathlib.analysis.calculus.iterated_deriv
import Mathlib.analysis.special_functions.exp_log
import Mathlib.topology.algebra.polynomial
import Mathlib.PostPort

namespace Mathlib

/-!
# Smoothness of specific functions

The real function `exp_neg_inv_glue` given by `x ↦ exp (-1/x)` for `x > 0` and `0`
for `x ≤ 0` is a basic building block to construct smooth partitions of unity. We prove that it
is `C^∞` in `exp_neg_inv_glue.smooth`.
-/

/-- `exp_neg_inv_glue` is the real function given by `x ↦ exp (-1/x)` for `x > 0` and `0`
for `x ≤ 0`. is a basic building block to construct smooth partitions of unity. Its main property
is that it vanishes for `x ≤ 0`, it is positive for `x > 0`, and the junction between the two
behaviors is flat enough to retain smoothness. The fact that this function is `C^∞` is proved in
`exp_neg_inv_glue.smooth`. -/
def exp_neg_inv_glue (x : ℝ) : ℝ := ite (x ≤ 0) 0 (real.exp (-(x⁻¹)))

namespace exp_neg_inv_glue

/-- Our goal is to prove that `exp_neg_inv_glue` is `C^∞`. For this, we compute its successive
derivatives for `x > 0`. The `n`-th derivative is of the form `P_aux n (x) exp(-1/x) / x^(2 n)`,
where `P_aux n` is computed inductively. -/
def P_aux : ℕ → polynomial ℝ := sorry

/-- Formula for the `n`-th derivative of `exp_neg_inv_glue`, as an auxiliary function `f_aux`. -/
def f_aux (n : ℕ) (x : ℝ) : ℝ :=
  ite (x ≤ 0) 0 (polynomial.eval x (P_aux n) * real.exp (-(x⁻¹)) / x ^ (bit0 1 * n))

/-- The `0`-th auxiliary function `f_aux 0` coincides with `exp_neg_inv_glue`, by definition. -/
theorem f_aux_zero_eq : f_aux 0 = exp_neg_inv_glue := sorry

/-- For positive values, the derivative of the `n`-th auxiliary function `f_aux n`
(given in this statement in unfolded form) is the `n+1`-th auxiliary function, since
the polynomial `P_aux (n+1)` was chosen precisely to ensure this. -/
theorem f_aux_deriv (n : ℕ) (x : ℝ) (hx : x ≠ 0) :
    has_deriv_at (fun (x : ℝ) => polynomial.eval x (P_aux n) * real.exp (-(x⁻¹)) / x ^ (bit0 1 * n))
        (polynomial.eval x (P_aux (n + 1)) * real.exp (-(x⁻¹)) / x ^ (bit0 1 * (n + 1))) x :=
  sorry

/-- For positive values, the derivative of the `n`-th auxiliary function `f_aux n`
is the `n+1`-th auxiliary function. -/
theorem f_aux_deriv_pos (n : ℕ) (x : ℝ) (hx : 0 < x) :
    has_deriv_at (f_aux n)
        (polynomial.eval x (P_aux (n + 1)) * real.exp (-(x⁻¹)) / x ^ (bit0 1 * (n + 1))) x :=
  sorry

/-- To get differentiability at `0` of the auxiliary functions, we need to know that their limit
is `0`, to be able to apply general differentiability extension theorems. This limit is checked in
this lemma. -/
theorem f_aux_limit (n : ℕ) :
    filter.tendsto
        (fun (x : ℝ) => polynomial.eval x (P_aux n) * real.exp (-(x⁻¹)) / x ^ (bit0 1 * n))
        (nhds_within 0 (set.Ioi 0)) (nhds 0) :=
  sorry

/-- Deduce from the limiting behavior at `0` of its derivative and general differentiability
extension theorems that the auxiliary function `f_aux n` is differentiable at `0`,
with derivative `0`. -/
theorem f_aux_deriv_zero (n : ℕ) : has_deriv_at (f_aux n) 0 0 := sorry

/-- At every point, the auxiliary function `f_aux n` has a derivative which is
equal to `f_aux (n+1)`. -/
theorem f_aux_has_deriv_at (n : ℕ) (x : ℝ) : has_deriv_at (f_aux n) (f_aux (n + 1) x) x := sorry

/-- The successive derivatives of the auxiliary function `f_aux 0` are the
functions `f_aux n`, by induction. -/
theorem f_aux_iterated_deriv (n : ℕ) : iterated_deriv n (f_aux 0) = f_aux n := sorry

/-- The function `exp_neg_inv_glue` is smooth. -/
theorem smooth : times_cont_diff ℝ ⊤ exp_neg_inv_glue := sorry

/-- The function `exp_neg_inv_glue` vanishes on `(-∞, 0]`. -/
theorem zero_of_nonpos {x : ℝ} (hx : x ≤ 0) : exp_neg_inv_glue x = 0 := sorry

/-- The function `exp_neg_inv_glue` is positive on `(0, +∞)`. -/
theorem pos_of_pos {x : ℝ} (hx : 0 < x) : 0 < exp_neg_inv_glue x := sorry

/-- The function exp_neg_inv_glue` is nonnegative. -/
theorem nonneg (x : ℝ) : 0 ≤ exp_neg_inv_glue x :=
  or.dcases_on (le_or_gt x 0) (fun (h : x ≤ 0) => ge_of_eq (zero_of_nonpos h))
    fun (h : x > 0) => le_of_lt (pos_of_pos h)

end Mathlib


/-
Copyright (c) 2019 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Gluing metric spaces
Authors: Sébastien Gouëzel
-/
import topology.metric_space.isometry

/-!
# Metric space gluing

Gluing two metric spaces along a common subset. Formally, we are given

```
     Φ
  γ ---> α
  |
  |Ψ
  v
  β
```
where `hΦ : isometry Φ` and `hΨ : isometry Ψ`.
We want to complete the square by a space `glue_space hΦ hΨ` and two isometries
`to_glue_l hΦ hΨ` and `to_glue_r hΦ hΨ` that make the square commute.
We start by defining a predistance on the disjoint union `α ⊕ β`, for which
points `Φ p` and `Ψ p` are at distance 0. The (quotient) metric space associated
to this predistance is the desired space.

This is an instance of a more general construction, where `Φ` and `Ψ` do not have to be isometries,
but the distances in the image almost coincide, up to `2ε` say. Then one can almost glue the two
spaces so that the images of a point under `Φ` and `Ψ` are ε-close. If `ε > 0`, this yields a metric
space structure on `α ⊕ β`, without the need to take a quotient. In particular, when `α` and `β` are
inhabited, this gives a natural metric space structure on `α ⊕ β`, where the basepoints are at
distance 1, say, and the distances between other points are obtained by going through the two
basepoints.

We also define the inductive limit of metric spaces. Given
```
     f 0        f 1        f 2        f 3
X 0 -----> X 1 -----> X 2 -----> X 3 -----> ...
```
where the `X n` are metric spaces and `f n` isometric embeddings, we define the inductive
limit of the `X n`, also known as the increasing union of the `X n` in this context, if we
identify `X n` and `X (n+1)` through `f n`. This is a metric space in which all `X n` embed
isometrically and in a way compatible with `f n`.

-/

noncomputable theory

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

open function set
open_locale uniformity

namespace metric
section approx_gluing

variables [metric_space α] [metric_space β]
          {Φ : γ → α} {Ψ : γ → β} {ε : ℝ}
open sum (inl inr)

/-- Define a predistance on α ⊕ β, for which Φ p and Ψ p are at distance ε -/
def glue_dist (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) : α ⊕ β → α ⊕ β → ℝ
| (inl x) (inl y) := dist x y
| (inr x) (inr y) := dist x y
| (inl x) (inr y) := (⨅ p, dist x (Φ p) + dist y (Ψ p)) + ε
| (inr x) (inl y) := (⨅ p, dist y (Φ p) + dist x (Ψ p)) + ε

private lemma glue_dist_self (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) : ∀x, glue_dist Φ Ψ ε x x = 0
| (inl x) := dist_self _
| (inr x) := dist_self _

lemma glue_dist_glued_points [nonempty γ] (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) (p : γ) :
  glue_dist Φ Ψ ε (inl (Φ p)) (inr (Ψ p)) = ε :=
begin
  have : (⨅ q, dist (Φ p) (Φ q) + dist (Ψ p) (Ψ q)) = 0,
  { have A : ∀q, 0 ≤ dist (Φ p) (Φ q) + dist (Ψ p) (Ψ q) :=
      λq, by rw ← add_zero (0 : ℝ); exact add_le_add dist_nonneg dist_nonneg,
    refine le_antisymm _ (le_cinfi A),
    have : 0 = dist (Φ p) (Φ p) + dist (Ψ p) (Ψ p), by simp,
    rw this,
    exact cinfi_le ⟨0, forall_range_iff.2 A⟩ p },
  rw [glue_dist, this, zero_add]
end

private lemma glue_dist_comm (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) :
  ∀x y, glue_dist Φ Ψ ε x y = glue_dist Φ Ψ ε y x
| (inl x) (inl y) := dist_comm _ _
| (inr x) (inr y) := dist_comm _ _
| (inl x) (inr y) := rfl
| (inr x) (inl y) := rfl

variable [nonempty γ]

private lemma glue_dist_triangle (Φ : γ → α) (Ψ : γ → β) (ε : ℝ)
  (H : ∀p q, abs (dist (Φ p) (Φ q) - dist (Ψ p) (Ψ q)) ≤ 2 * ε) :
  ∀x y z, glue_dist Φ Ψ ε x z ≤ glue_dist Φ Ψ ε x y + glue_dist Φ Ψ ε y z
| (inl x) (inl y) (inl z) := dist_triangle _ _ _
| (inr x) (inr y) (inr z) := dist_triangle _ _ _
| (inr x) (inl y) (inl z) := begin
    have B : ∀a b, bdd_below (range (λ (p : γ), dist a (Φ p) + dist b (Ψ p))) :=
      λa b, ⟨0, forall_range_iff.2 (λp, add_nonneg dist_nonneg dist_nonneg)⟩,
    unfold glue_dist,
    have : (⨅ p, dist z (Φ p) + dist x (Ψ p)) ≤ (⨅ p, dist y (Φ p) + dist x (Ψ p)) + dist y z,
    { have : (⨅ p, dist y (Φ p) + dist x (Ψ p)) + dist y z =
            infi ((λt, t + dist y z) ∘ (λp, dist y (Φ p) + dist x (Ψ p))),
      { refine map_cinfi_of_continuous_at_of_monotone (continuous_at_id.add continuous_at_const) _
          (B _ _),
        intros x y hx, simpa },
      rw [this, comp],
      refine cinfi_le_cinfi (B _ _) (λp, _),
      calc
        dist z (Φ p) + dist x (Ψ p) ≤ (dist y z + dist y (Φ p)) + dist x (Ψ p) :
          add_le_add (dist_triangle_left _ _ _) (le_refl _)
        ... = dist y (Φ p) + dist x (Ψ p) + dist y z : by ring },
    linarith
  end
| (inr x) (inr y) (inl z) := begin
    have B : ∀a b, bdd_below (range (λ (p : γ), dist a (Φ p) + dist b (Ψ p))) :=
      λa b, ⟨0, forall_range_iff.2 (λp, add_nonneg dist_nonneg dist_nonneg)⟩,
    unfold glue_dist,
    have : (⨅ p, dist z (Φ p) + dist x (Ψ p)) ≤ dist x y + ⨅ p, dist z (Φ p) + dist y (Ψ p),
    { have : dist x y + (⨅ p, dist z (Φ p) + dist y (Ψ p)) =
            infi ((λt, dist x y + t) ∘ (λp, dist z (Φ p) + dist y (Ψ p))),
      { refine map_cinfi_of_continuous_at_of_monotone (continuous_at_const.add continuous_at_id) _
          (B _ _),
        intros x y hx, simpa },
      rw [this, comp],
      refine cinfi_le_cinfi (B _ _) (λp, _),
      calc
        dist z (Φ p) + dist x (Ψ p) ≤ dist z (Φ p) + (dist x y + dist y (Ψ p)) :
          add_le_add (le_refl _) (dist_triangle _ _ _)
        ... = dist x y + (dist z (Φ p) + dist y (Ψ p)) : by ring },
    linarith
  end
| (inl x) (inl y) (inr z) := begin
    have B : ∀a b, bdd_below (range (λ (p : γ), dist a (Φ p) + dist b (Ψ p))) :=
      λa b, ⟨0, forall_range_iff.2 (λp, add_nonneg dist_nonneg dist_nonneg)⟩,
    unfold glue_dist,
    have : (⨅ p, dist x (Φ p) + dist z (Ψ p)) ≤ dist x y + ⨅ p, dist y (Φ p) + dist z (Ψ p),
    { have : dist x y + (⨅ p, dist y (Φ p) + dist z (Ψ p)) =
            infi ((λt, dist x y + t) ∘ (λp, dist y (Φ p) + dist z (Ψ p))),
      { refine map_cinfi_of_continuous_at_of_monotone (continuous_at_const.add continuous_at_id) _
          (B _ _),
        intros x y hx, simpa },
      rw [this, comp],
      refine cinfi_le_cinfi (B _ _) (λp, _),
      calc
        dist x (Φ p) + dist z (Ψ p) ≤ (dist x y + dist y (Φ p)) + dist z (Ψ p) :
          add_le_add (dist_triangle _ _ _) (le_refl _)
        ... = dist x y + (dist y (Φ p) + dist z (Ψ p)) : by ring },
    linarith
  end
| (inl x) (inr y) (inr z) := begin
    have B : ∀a b, bdd_below (range (λ (p : γ), dist a (Φ p) + dist b (Ψ p))) :=
      λa b, ⟨0, forall_range_iff.2 (λp, add_nonneg dist_nonneg dist_nonneg)⟩,
    unfold glue_dist,
    have : (⨅ p, dist x (Φ p) + dist z (Ψ p)) ≤ (⨅ p, dist x (Φ p) + dist y (Ψ p)) + dist y z,
    { have : (⨅ p, dist x (Φ p) + dist y (Ψ p)) + dist y z =
            infi ((λt, t + dist y z) ∘ (λp, dist x (Φ p) + dist y (Ψ p))),
      { refine map_cinfi_of_continuous_at_of_monotone (continuous_at_id.add continuous_at_const) _
          (B _ _),
        intros x y hx, simpa },
      rw [this, comp],
      refine cinfi_le_cinfi (B _ _) (λp, _),
      calc
        dist x (Φ p) + dist z (Ψ p) ≤ dist x (Φ p) + (dist y z + dist y (Ψ p)) :
          add_le_add (le_refl _) (dist_triangle_left _ _ _)
        ... = dist x (Φ p) + dist y (Ψ p) + dist y z : by ring },
    linarith
  end
| (inl x) (inr y) (inl z) := le_of_forall_pos_le_add $ λδ δpos, begin
    obtain ⟨p, hp⟩ : ∃ p, dist x (Φ p) + dist y (Ψ p) < (⨅ p, dist x (Φ p) + dist y (Ψ p)) + δ / 2,
      from exists_lt_of_cinfi_lt (by linarith),
    obtain ⟨q, hq⟩ : ∃ q, dist z (Φ q) + dist y (Ψ q) < (⨅ p, dist z (Φ p) + dist y (Ψ p)) + δ / 2,
      from exists_lt_of_cinfi_lt (by linarith),
    have : dist (Φ p) (Φ q) ≤ dist (Ψ p) (Ψ q) + 2 * ε,
      { have := le_trans (le_abs_self _) (H p q), by linarith },
    calc dist x z ≤ dist x (Φ p) + dist (Φ p) (Φ q) + dist (Φ q) z : dist_triangle4 _ _ _ _
      ... ≤ dist x (Φ p) + dist (Ψ p) (Ψ q) + dist z (Φ q) + 2 * ε : by rw [dist_comm z]; linarith
      ... ≤ dist x (Φ p) + (dist y (Ψ p) + dist y (Ψ q)) + dist z (Φ q) + 2 * ε :
        add_le_add (add_le_add (add_le_add (le_refl _) (dist_triangle_left _ _ _)) le_rfl) le_rfl
      ... ≤ ((⨅ p, dist x (Φ p) + dist y (Ψ p)) + ε) +
            ((⨅ p, dist z (Φ p) + dist y (Ψ p)) + ε) + δ : by linarith
  end
| (inr x) (inl y) (inr z) := le_of_forall_pos_le_add $ λδ δpos, begin
    obtain ⟨p, hp⟩ : ∃ p, dist y (Φ p) + dist x (Ψ p) < (⨅ p, dist y (Φ p) + dist x (Ψ p)) + δ / 2,
      from exists_lt_of_cinfi_lt (by linarith),
    obtain ⟨q, hq⟩ : ∃ q, dist y (Φ q) + dist z (Ψ q) < (⨅ p, dist y (Φ p) + dist z (Ψ p)) + δ / 2,
      from exists_lt_of_cinfi_lt (by linarith),
    have : dist (Ψ p) (Ψ q) ≤ dist (Φ p) (Φ q) + 2 * ε,
      { have := le_trans (neg_le_abs_self _) (H p q), by linarith },
    calc dist x z ≤ dist x (Ψ p) + dist (Ψ p) (Ψ q) + dist (Ψ q) z : dist_triangle4 _ _ _ _
      ... ≤ dist x (Ψ p) + dist (Φ p) (Φ q) + dist z (Ψ q) + 2 * ε : by rw [dist_comm z]; linarith
      ... ≤ dist x (Ψ p) + (dist y (Φ p) + dist y (Φ q)) + dist z (Ψ q) + 2 * ε :
        add_le_add (add_le_add (add_le_add le_rfl (dist_triangle_left _ _ _)) le_rfl) le_rfl
      ... ≤ ((⨅ p, dist y (Φ p) + dist x (Ψ p)) + ε) +
            ((⨅ p, dist y (Φ p) + dist z (Ψ p)) + ε) + δ : by linarith
  end

private lemma glue_eq_of_dist_eq_zero (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) (ε0 : 0 < ε) :
  ∀p q : α ⊕ β, glue_dist Φ Ψ ε p q = 0 → p = q
| (inl x) (inl y) h := by rw eq_of_dist_eq_zero h
| (inl x) (inr y) h := begin
    have : 0 ≤ (⨅ p, dist x (Φ p) + dist y (Ψ p)) :=
      le_cinfi (λp, by simpa using add_le_add (@dist_nonneg _ _ x _) (@dist_nonneg _ _ y _)),
    have : 0 + ε ≤ glue_dist Φ Ψ ε (inl x) (inr y) := add_le_add this (le_refl ε),
    exfalso,
    linarith
  end
| (inr x) (inl y) h := begin
    have : 0 ≤ ⨅ p, dist y (Φ p) + dist x (Ψ p) :=
      le_cinfi (λp, by simpa [add_comm]
                         using add_le_add (@dist_nonneg _ _ x _) (@dist_nonneg _ _ y _)),
    have : 0 + ε ≤ glue_dist Φ Ψ ε (inr x) (inl y) := add_le_add this (le_refl ε),
    exfalso,
    linarith
  end
| (inr x) (inr y) h := by rw eq_of_dist_eq_zero h

/-- Given two maps Φ and Ψ intro metric spaces α and β such that the distances between Φ p and Φ q,
and between Ψ p and Ψ q, coincide up to 2 ε where ε > 0, one can almost glue the two spaces α
and β along the images of Φ and Ψ, so that Φ p and Ψ p are at distance ε. -/
def glue_metric_approx (Φ : γ → α) (Ψ : γ → β) (ε : ℝ) (ε0 : 0 < ε)
  (H : ∀p q, abs (dist (Φ p) (Φ q) - dist (Ψ p) (Ψ q)) ≤ 2 * ε) : metric_space (α ⊕ β) :=
{ dist               := glue_dist Φ Ψ ε,
  dist_self          := glue_dist_self Φ Ψ ε,
  dist_comm          := glue_dist_comm Φ Ψ ε,
  dist_triangle      := glue_dist_triangle Φ Ψ ε H,
  eq_of_dist_eq_zero := glue_eq_of_dist_eq_zero Φ Ψ ε ε0 }

end approx_gluing

section sum
/- A particular case of the previous construction is when one uses basepoints in α and β and one
glues only along the basepoints, putting them at distance 1. We give a direct definition of
the distance, without infi, as it is easier to use in applications, and show that it is equal to
the gluing distance defined above to take advantage of the lemmas we have already proved. -/

variables [metric_space α] [metric_space β] [inhabited α] [inhabited β]
open sum (inl inr)

/- Distance on a disjoint union. There are many (noncanonical) ways to put a distance compatible
with each factor.
If the two spaces are bounded, one can say for instance that each point in the first is at distance
`diam α + diam β + 1` of each point in the second.
Instead, we choose a construction that works for unbounded spaces, but requires basepoints.
We embed isometrically each factor, set the basepoints at distance 1,
arbitrarily, and say that the distance from `a` to `b` is the sum of the distances of `a` and `b` to
their respective basepoints, plus the distance 1 between the basepoints.
Since there is an arbitrary choice in this construction, it is not an instance by default. -/

def sum.dist : α ⊕ β → α ⊕ β → ℝ
| (inl a) (inl a') := dist a a'
| (inr b) (inr b') := dist b b'
| (inl a) (inr b)  := dist a (default α) + 1 + dist (default β) b
| (inr b) (inl a)  := dist b (default β) + 1 + dist (default α) a

lemma sum.dist_eq_glue_dist {p q : α ⊕ β} :
  sum.dist p q = glue_dist (λ_ : unit, default α) (λ_ : unit, default β) 1 p q :=
by cases p; cases q; refl <|> simp [sum.dist, glue_dist, dist_comm, add_comm, add_left_comm]

private lemma sum.dist_comm (x y : α ⊕ β) : sum.dist x y = sum.dist y x :=
by cases x; cases y; simp only [sum.dist, dist_comm, add_comm, add_left_comm]

lemma sum.one_dist_le {x : α} {y : β} : 1 ≤ sum.dist (inl x) (inr y) :=
le_trans (le_add_of_nonneg_right dist_nonneg) $
add_le_add_right (le_add_of_nonneg_left dist_nonneg) _

lemma sum.one_dist_le' {x : α} {y : β} : 1 ≤ sum.dist (inr y) (inl x) :=
by rw sum.dist_comm; exact sum.one_dist_le

private lemma sum.mem_uniformity (s : set ((α ⊕ β) × (α ⊕ β))) :
  s ∈ 𝓤 (α ⊕ β) ↔ ∃ ε > 0, ∀ a b, sum.dist a b < ε → (a, b) ∈ s :=
begin
  split,
  { rintro ⟨hsα, hsβ⟩,
    rcases mem_uniformity_dist.1 hsα with ⟨εα, εα0, hα⟩,
    rcases mem_uniformity_dist.1 hsβ with ⟨εβ, εβ0, hβ⟩,
    refine ⟨min (min εα εβ) 1, lt_min (lt_min εα0 εβ0) zero_lt_one, _⟩,
    rintro (a|a) (b|b) h,
    { exact hα (lt_of_lt_of_le h (le_trans (min_le_left _ _) (min_le_left _ _))) },
    { cases not_le_of_lt (lt_of_lt_of_le h (min_le_right _ _)) sum.one_dist_le },
    { cases not_le_of_lt (lt_of_lt_of_le h (min_le_right _ _)) sum.one_dist_le' },
    { exact hβ (lt_of_lt_of_le h (le_trans (min_le_left _ _) (min_le_right _ _))) } },
  { rintro ⟨ε, ε0, H⟩,
    split; rw [filter.mem_sets, filter.mem_map, mem_uniformity_dist];
      exact ⟨ε, ε0, λ x y h, H _ _ (by exact h)⟩ }
end

/-- The distance on the disjoint union indeed defines a metric space. All the distance properties
follow from our choice of the distance. The harder work is to show that the uniform structure
defined by the distance coincides with the disjoint union uniform structure. -/
def metric_space_sum : metric_space (α ⊕ β) :=
{ dist               := sum.dist,
  dist_self          := λx, by cases x; simp only [sum.dist, dist_self],
  dist_comm          := sum.dist_comm,
  dist_triangle      := λp q r,
    by simp only [dist, sum.dist_eq_glue_dist]; exact glue_dist_triangle _ _ _ (by norm_num) _ _ _,
  eq_of_dist_eq_zero := λp q,
    by simp only [dist, sum.dist_eq_glue_dist]; exact glue_eq_of_dist_eq_zero _ _ _ zero_lt_one _ _,
  to_uniform_space   := sum.uniform_space,
  uniformity_dist    := uniformity_dist_of_mem_uniformity _ _ sum.mem_uniformity }

local attribute [instance] metric_space_sum

lemma sum.dist_eq {x y : α ⊕ β} : dist x y = sum.dist x y := rfl

/-- The left injection of a space in a disjoint union in an isometry -/
lemma isometry_on_inl : isometry (sum.inl : α → (α ⊕ β)) :=
isometry_emetric_iff_metric.2 $ λx y, rfl

/-- The right injection of a space in a disjoint union in an isometry -/
lemma isometry_on_inr : isometry (sum.inr : β → (α ⊕ β)) :=
isometry_emetric_iff_metric.2 $ λx y, rfl

end sum

section gluing
/- Exact gluing of two metric spaces along isometric subsets. -/

variables [nonempty γ] [metric_space γ] [metric_space α] [metric_space β]
          {Φ : γ → α} {Ψ : γ → β} {ε : ℝ}
open sum (inl inr)
local attribute [instance] pseudo_metric.dist_setoid

def glue_premetric (hΦ : isometry Φ) (hΨ : isometry Ψ) : pseudo_metric_space (α ⊕ β) :=
{ dist          := glue_dist Φ Ψ 0,
  dist_self     := glue_dist_self Φ Ψ 0,
  dist_comm     := glue_dist_comm Φ Ψ 0,
  dist_triangle := glue_dist_triangle Φ Ψ 0 $ λp q, by rw [hΦ.dist_eq, hΨ.dist_eq]; simp }

def glue_space (hΦ : isometry Φ) (hΨ : isometry Ψ) : Type* :=
@pseudo_metric_quot _ (glue_premetric hΦ hΨ)

instance metric_space_glue_space (hΦ : isometry Φ) (hΨ : isometry Ψ) :
  metric_space (glue_space hΦ hΨ) :=
@metric_space_quot _ (glue_premetric hΦ hΨ)

def to_glue_l (hΦ : isometry Φ) (hΨ : isometry Ψ) (x : α) : glue_space hΦ hΨ :=
by letI : pseudo_metric_space (α ⊕ β) := glue_premetric hΦ hΨ; exact ⟦inl x⟧

def to_glue_r (hΦ : isometry Φ) (hΨ : isometry Ψ) (y : β) : glue_space hΦ hΨ :=
by letI : pseudo_metric_space (α ⊕ β) := glue_premetric hΦ hΨ; exact ⟦inr y⟧

instance inhabited_left (hΦ : isometry Φ) (hΨ : isometry Ψ) [inhabited α] :
  inhabited (glue_space hΦ hΨ) :=
⟨to_glue_l _ _ (default _)⟩

instance inhabited_right (hΦ : isometry Φ) (hΨ : isometry Ψ) [inhabited β] :
  inhabited (glue_space hΦ hΨ) :=
⟨to_glue_r _ _ (default _)⟩

lemma to_glue_commute (hΦ : isometry Φ) (hΨ : isometry Ψ) :
  (to_glue_l hΦ hΨ) ∘ Φ = (to_glue_r hΦ hΨ) ∘ Ψ :=
begin
  letI : pseudo_metric_space (α ⊕ β) := glue_premetric hΦ hΨ,
  funext,
  simp only [comp, to_glue_l, to_glue_r, quotient.eq],
  exact glue_dist_glued_points Φ Ψ 0 x
end

lemma to_glue_l_isometry (hΦ : isometry Φ) (hΨ : isometry Ψ) : isometry (to_glue_l hΦ hΨ) :=
isometry_emetric_iff_metric.2 $ λ_ _, rfl

lemma to_glue_r_isometry (hΦ : isometry Φ) (hΨ : isometry Ψ) : isometry (to_glue_r hΦ hΨ) :=
isometry_emetric_iff_metric.2 $ λ_ _, rfl

end gluing --section

section inductive_limit
/- In this section, we define the inductive limit of
     f 0        f 1        f 2        f 3
X 0 -----> X 1 -----> X 2 -----> X 3 -----> ...
where the X n are metric spaces and f n isometric embeddings. We do it by defining a premetric
space structure on Σn, X n, where the predistance dist x y is obtained by pushing x and y in a
common X k using composition by the f n, and taking the distance there. This does not depend on
the choice of k as the f n are isometries. The metric space associated to this premetric space
is the desired inductive limit.-/
open nat

variables {X : ℕ → Type u} [∀n, metric_space (X n)] {f : Πn, X n → X (n+1)}

/-- Predistance on the disjoint union Σn, X n. -/
def inductive_limit_dist (f : Πn, X n → X (n+1)) (x y : Σn, X n) : ℝ :=
dist (le_rec_on (le_max_left  x.1 y.1) f x.2 : X (max x.1 y.1))
     (le_rec_on (le_max_right x.1 y.1) f y.2 : X (max x.1 y.1))

/-- The predistance on the disjoint union Σn, X n can be computed in any X k for large enough k.-/
lemma inductive_limit_dist_eq_dist (I : ∀n, isometry (f n))
  (x y : Σn, X n) (m : ℕ) : ∀hx : x.1 ≤ m, ∀hy : y.1 ≤ m,
  inductive_limit_dist f x y = dist (le_rec_on hx f x.2 : X m) (le_rec_on hy f y.2 : X m) :=
begin
  induction m with m hm,
  { assume hx hy,
    have A : max x.1 y.1 = 0, { rw [nonpos_iff_eq_zero.1 hx, nonpos_iff_eq_zero.1 hy], simp },
    unfold inductive_limit_dist,
    congr; simp only [A] },
  { assume hx hy,
    by_cases h : max x.1 y.1 = m.succ,
    { unfold inductive_limit_dist,
      congr; simp only [h] },
    { have : max x.1 y.1 ≤ succ m := by simp [hx, hy],
      have : max x.1 y.1 ≤ m := by simpa [h] using of_le_succ this,
      have xm : x.1 ≤ m := le_trans (le_max_left _ _) this,
      have ym : y.1 ≤ m := le_trans (le_max_right _ _) this,
      rw [le_rec_on_succ xm, le_rec_on_succ ym, (I m).dist_eq],
      exact hm xm ym }}
end

/-- Premetric space structure on Σn, X n.-/
def inductive_premetric (I : ∀n, isometry (f n)) :
  pseudo_metric_space (Σn, X n) :=
{ dist          := inductive_limit_dist f,
  dist_self     := λx, by simp [dist, inductive_limit_dist],
  dist_comm     := λx y, begin
    let m := max x.1 y.1,
    have hx : x.1 ≤ m := le_max_left _ _,
    have hy : y.1 ≤ m := le_max_right _ _,
    unfold dist,
    rw [inductive_limit_dist_eq_dist I x y m hx hy, inductive_limit_dist_eq_dist I y x m hy hx,
        dist_comm]
  end,
  dist_triangle := λx y z, begin
    let m := max (max x.1 y.1) z.1,
    have hx : x.1 ≤ m := le_trans (le_max_left _ _) (le_max_left _ _),
    have hy : y.1 ≤ m := le_trans (le_max_right _ _) (le_max_left _ _),
    have hz : z.1 ≤ m := le_max_right _ _,
    calc inductive_limit_dist f x z
      = dist (le_rec_on hx f x.2 : X m) (le_rec_on hz f z.2 : X m) :
        inductive_limit_dist_eq_dist I x z m hx hz
      ... ≤ dist (le_rec_on hx f x.2 : X m) (le_rec_on hy f y.2 : X m)
          + dist (le_rec_on hy f y.2 : X m) (le_rec_on hz f z.2 : X m) :
        dist_triangle _ _ _
      ... = inductive_limit_dist f x y + inductive_limit_dist f y z :
         by rw [inductive_limit_dist_eq_dist I x y m hx hy,
                inductive_limit_dist_eq_dist I y z m hy hz]
  end }

local attribute [instance] inductive_premetric pseudo_metric.dist_setoid

/-- The type giving the inductive limit in a metric space context. -/
def inductive_limit (I : ∀n, isometry (f n)) : Type* :=
@pseudo_metric_quot _ (inductive_premetric I)

/-- Metric space structure on the inductive limit. -/
instance metric_space_inductive_limit (I : ∀n, isometry (f n)) :
  metric_space (inductive_limit I) :=
@metric_space_quot _ (inductive_premetric I)

/-- Mapping each `X n` to the inductive limit. -/
def to_inductive_limit (I : ∀n, isometry (f n)) (n : ℕ) (x : X n) : metric.inductive_limit I :=
by letI : pseudo_metric_space (Σn, X n) := inductive_premetric I; exact ⟦sigma.mk n x⟧

instance (I : ∀ n, isometry (f n)) [inhabited (X 0)] : inhabited (inductive_limit I) :=
⟨to_inductive_limit _ 0 (default _)⟩

/-- The map `to_inductive_limit n` mapping `X n` to the inductive limit is an isometry. -/
lemma to_inductive_limit_isometry (I : ∀n, isometry (f n)) (n : ℕ) :
  isometry (to_inductive_limit I n) := isometry_emetric_iff_metric.2 $ λx y,
begin
  change inductive_limit_dist f ⟨n, x⟩ ⟨n, y⟩ = dist x y,
  rw [inductive_limit_dist_eq_dist I ⟨n, x⟩ ⟨n, y⟩ n (le_refl n) (le_refl n),
      le_rec_on_self, le_rec_on_self]
end

/-- The maps `to_inductive_limit n` are compatible with the maps `f n`. -/
lemma to_inductive_limit_commute (I : ∀n, isometry (f n)) (n : ℕ) :
  (to_inductive_limit I n.succ) ∘ (f n) = to_inductive_limit I n :=
begin
  funext,
  simp only [comp, to_inductive_limit, quotient.eq],
  show inductive_limit_dist f ⟨n.succ, f n x⟩ ⟨n, x⟩ = 0,
  { rw [inductive_limit_dist_eq_dist I ⟨n.succ, f n x⟩ ⟨n, x⟩ n.succ,
        le_rec_on_self, le_rec_on_succ, le_rec_on_self, dist_self],
    exact le_refl _,
    exact le_refl _,
    exact le_succ _ }
end

end inductive_limit --section
end metric --namespace


/-
Copyright (c) 2022 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov

! This file was ported from Lean 3 source module algebra.free_monoid.count
! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.FreeMonoid.Basic
import Mathbin.Data.List.Count

/-!
# `list.count` as a bundled homomorphism

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

In this file we define `free_monoid.countp`, `free_monoid.count`, `free_add_monoid.countp`, and
`free_add_monoid.count`. These are `list.countp` and `list.count` bundled as multiplicative and
additive homomorphisms from `free_monoid` and `free_add_monoid`.

We do not use `to_additive` because it can't map `multiplicative ℕ` to `ℕ`.
-/

variable {α : Type _} (p : α → Prop) [DecidablePred p]

namespace FreeAddMonoid

/- warning: free_add_monoid.countp -> FreeAddMonoid.countp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_2 : DecidablePred.{succ u1} α p], AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_2 : DecidablePred.{succ u1} α p], AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)
Case conversion may be inaccurate. Consider using '#align free_add_monoid.countp FreeAddMonoid.countpₓ'. -/
/-- `list.countp` as a bundled additive monoid homomorphism. -/
def countp (p : α → Prop) [DecidablePred p] : FreeAddMonoid α →+ ℕ :=
  ⟨List.countp p, List.countp_nil p, List.countp_append _⟩
#align free_add_monoid.countp FreeAddMonoid.countp

/- warning: free_add_monoid.countp_of -> FreeAddMonoid.countp_of is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (FreeAddMonoid.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeAddMonoid.of.{u1} α x)) (ite.{1} Nat (p x) (_inst_1 x) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))))
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) (FreeAddMonoid.of.{u1} α x)) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) (fun (_x : FreeAddMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddZeroClass.toAdd.{u1} (FreeAddMonoid.{u1} α) (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (FreeAddMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeAddMonoid.of.{u1} α x)) (ite.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) (FreeAddMonoid.of.{u1} α x)) (Eq.{1} Prop (p x) (Eq.{1} Bool Bool.true Bool.true)) (instDecidableEqProp (p x) (Eq.{1} Bool Bool.true Bool.true) (instDecidableIff (p x) (Eq.{1} Bool Bool.true Bool.true) (_inst_1 x) (instDecidableEqBool Bool.true Bool.true))) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) (FreeAddMonoid.of.{u1} α x)) 1 (instOfNatNat 1)) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) (FreeAddMonoid.of.{u1} α x)) 0 (instOfNatNat 0)))
Case conversion may be inaccurate. Consider using '#align free_add_monoid.countp_of FreeAddMonoid.countp_ofₓ'. -/
theorem countp_of (x : α) : countp p (of x) = if p x then 1 else 0 :=
  rfl
#align free_add_monoid.countp_of FreeAddMonoid.countp_of

/- warning: free_add_monoid.countp_apply -> FreeAddMonoid.countp_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (l : FreeAddMonoid.{u1} α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (FreeAddMonoid.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) l) (List.countp.{u1} α p (fun (a : α) => _inst_1 a) l)
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (l : FreeAddMonoid.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) l) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) (fun (_x : FreeAddMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddZeroClass.toAdd.{u1} (FreeAddMonoid.{u1} α) (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (FreeAddMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) l) (List.countp.{u1} α (fun (a : α) => Decidable.decide (p a) ((fun (a : α) => _inst_1 a) a)) l)
Case conversion may be inaccurate. Consider using '#align free_add_monoid.countp_apply FreeAddMonoid.countp_applyₓ'. -/
theorem countp_apply (l : FreeAddMonoid α) : countp p l = List.countp p l :=
  rfl
#align free_add_monoid.countp_apply FreeAddMonoid.countp_apply

/- warning: free_add_monoid.count -> FreeAddMonoid.count is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α], α -> (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α], α -> (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))
Case conversion may be inaccurate. Consider using '#align free_add_monoid.count FreeAddMonoid.countₓ'. -/
/-- `list.count` as a bundled additive monoid homomorphism. -/
def count [DecidableEq α] (x : α) : FreeAddMonoid α →+ ℕ :=
  countp (Eq x)
#align free_add_monoid.count FreeAddMonoid.count

/- warning: free_add_monoid.count_of -> FreeAddMonoid.count_of is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (FreeAddMonoid.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) (FreeAddMonoid.of.{u1} α y)) (Pi.single.{u1, 0} α (fun (y : α) => Nat) (fun (a : α) (b : α) => _inst_2 a b) (fun (i : α) => Nat.hasZero) x (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) y)
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) (FreeAddMonoid.of.{u1} α y)) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) (fun (_x : FreeAddMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddZeroClass.toAdd.{u1} (FreeAddMonoid.{u1} α) (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (FreeAddMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) (FreeAddMonoid.of.{u1} α y)) (Pi.single.{u1, 0} α (fun (y : α) => Nat) (fun (a : α) (b : α) => _inst_2 a b) (fun (i : α) => LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) x (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) y)
Case conversion may be inaccurate. Consider using '#align free_add_monoid.count_of FreeAddMonoid.count_ofₓ'. -/
theorem count_of [DecidableEq α] (x y : α) : count x (of y) = Pi.single x 1 y := by
  simp only [count, countp_of, Pi.single_apply, eq_comm]
#align free_add_monoid.count_of FreeAddMonoid.count_of

/- warning: free_add_monoid.count_apply -> FreeAddMonoid.count_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (l : FreeAddMonoid.{u1} α), Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (FreeAddMonoid.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.cancelAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) l) (List.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x l)
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (l : FreeAddMonoid.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) l) (FunLike.coe.{succ u1, succ u1, 1} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) (fun (_x : FreeAddMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : FreeAddMonoid.{u1} α) => Nat) _x) (AddHomClass.toFunLike.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddZeroClass.toAdd.{u1} (FreeAddMonoid.{u1} α) (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α))))) (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddMonoidHomClass.toAddHomClass.{u1, u1, 0} (AddMonoidHom.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoidHom.addMonoidHomClass.{u1, 0} (FreeAddMonoid.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (FreeAddMonoid.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (FreeAddMonoid.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (FreeAddMonoid.{u1} α) (FreeAddMonoid.instAddCancelMonoidFreeAddMonoid.{u1} α)))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) (FreeAddMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) l) (List.count.{u1} α (instBEq.{u1} α (fun (a : α) (b : α) => _inst_2 a b)) x l)
Case conversion may be inaccurate. Consider using '#align free_add_monoid.count_apply FreeAddMonoid.count_applyₓ'. -/
theorem count_apply [DecidableEq α] (x : α) (l : FreeAddMonoid α) : count x l = List.count x l :=
  rfl
#align free_add_monoid.count_apply FreeAddMonoid.count_apply

end FreeAddMonoid

namespace FreeMonoid

/- warning: free_monoid.countp -> FreeMonoid.countp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_2 : DecidablePred.{succ u1} α p], MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_2 : DecidablePred.{succ u1} α p], MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))
Case conversion may be inaccurate. Consider using '#align free_monoid.countp FreeMonoid.countpₓ'. -/
/-- `list.countp` as a bundled multiplicative monoid homomorphism. -/
def countp (p : α → Prop) [DecidablePred p] : FreeMonoid α →* Multiplicative ℕ :=
  (FreeAddMonoid.countp p).toMultiplicative
#align free_monoid.countp FreeMonoid.countp

/- warning: free_monoid.countp_of' -> FreeMonoid.countp_of' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} (Multiplicative.{0} Nat) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (FreeMonoid.{u1} α) -> (Multiplicative.{0} Nat)) (MonoidHom.hasCoeToFun.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeMonoid.of.{u1} α x)) (ite.{1} (Multiplicative.{0} Nat) (p x) (_inst_1 x) (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) (FreeMonoid.of.{u1} α x)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (fun (_x : FreeMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (MulOneClass.toMul.{u1} (FreeMonoid.{u1} α) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α))))) (MulOneClass.toMul.{0} (Multiplicative.{0} Nat) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (MonoidHom.monoidHomClass.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeMonoid.of.{u1} α x)) (ite.{1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (p x) (_inst_1 x) (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))))
Case conversion may be inaccurate. Consider using '#align free_monoid.countp_of' FreeMonoid.countp_of'ₓ'. -/
theorem countp_of' (x : α) :
    countp p (of x) = if p x then Multiplicative.ofAdd 1 else Multiplicative.ofAdd 0 :=
  rfl
#align free_monoid.countp_of' FreeMonoid.countp_of'

/- warning: free_monoid.countp_of -> FreeMonoid.countp_of is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} (Multiplicative.{0} Nat) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (FreeMonoid.{u1} α) -> (Multiplicative.{0} Nat)) (MonoidHom.hasCoeToFun.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeMonoid.of.{u1} α x)) (ite.{1} (Multiplicative.{0} Nat) (p x) (_inst_1 x) (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{0} (Multiplicative.{0} Nat) 1 (OfNat.mk.{0} (Multiplicative.{0} Nat) 1 (One.one.{0} (Multiplicative.{0} Nat) (Multiplicative.hasOne.{0} Nat Nat.hasZero)))))
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (x : α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) (FreeMonoid.of.{u1} α x)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (fun (_x : FreeMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (MulOneClass.toMul.{u1} (FreeMonoid.{u1} α) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α))))) (MulOneClass.toMul.{0} (Multiplicative.{0} Nat) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (MonoidHom.monoidHomClass.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) (FreeMonoid.of.{u1} α x)) (ite.{1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (p x) (_inst_1 x) (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 1 (One.toOfNat1.{0} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (instOneMultiplicative.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)))))
Case conversion may be inaccurate. Consider using '#align free_monoid.countp_of FreeMonoid.countp_ofₓ'. -/
theorem countp_of (x : α) : countp p (of x) = if p x then Multiplicative.ofAdd 1 else 1 := by
  rw [countp_of', ofAdd_zero]
#align free_monoid.countp_of FreeMonoid.countp_of

/- warning: free_monoid.countp_apply -> FreeMonoid.countp_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (l : FreeAddMonoid.{u1} α), Eq.{1} (Multiplicative.{0} Nat) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (FreeMonoid.{u1} α) -> (Multiplicative.{0} Nat)) (MonoidHom.hasCoeToFun.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) l) (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (List.countp.{u1} α p (fun (a : α) => _inst_1 a) l))
but is expected to have type
  forall {α : Type.{u1}} (p : α -> Prop) [_inst_1 : DecidablePred.{succ u1} α p] (l : FreeAddMonoid.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) l) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (fun (_x : FreeMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (MulOneClass.toMul.{u1} (FreeMonoid.{u1} α) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α))))) (MulOneClass.toMul.{0} (Multiplicative.{0} Nat) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (MonoidHom.monoidHomClass.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) (FreeMonoid.countp.{u1} α p (fun (a : α) => _inst_1 a)) l) (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (List.countp.{u1} α (fun (a : α) => Decidable.decide (p a) ((fun (a : α) => _inst_1 a) a)) l))
Case conversion may be inaccurate. Consider using '#align free_monoid.countp_apply FreeMonoid.countp_applyₓ'. -/
-- `rfl` is not transitive
theorem countp_apply (l : FreeAddMonoid α) : countp p l = Multiplicative.ofAdd (List.countp p l) :=
  rfl
#align free_monoid.countp_apply FreeMonoid.countp_apply

/- warning: free_monoid.count -> FreeMonoid.count is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α], α -> (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α], α -> (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))
Case conversion may be inaccurate. Consider using '#align free_monoid.count FreeMonoid.countₓ'. -/
/-- `list.count` as a bundled additive monoid homomorphism. -/
def count [DecidableEq α] (x : α) : FreeMonoid α →* Multiplicative ℕ :=
  countp (Eq x)
#align free_monoid.count FreeMonoid.count

/- warning: free_monoid.count_apply -> FreeMonoid.count_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (l : FreeAddMonoid.{u1} α), Eq.{1} (Multiplicative.{0} Nat) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (FreeMonoid.{u1} α) -> (Multiplicative.{0} Nat)) (MonoidHom.hasCoeToFun.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) l) (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (List.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (l : FreeAddMonoid.{u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) l) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (fun (_x : FreeMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (MulOneClass.toMul.{u1} (FreeMonoid.{u1} α) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α))))) (MulOneClass.toMul.{0} (Multiplicative.{0} Nat) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (MonoidHom.monoidHomClass.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) (FreeMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) l) (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (List.count.{u1} α (instBEq.{u1} α (fun (a : α) (b : α) => _inst_2 a b)) x l))
Case conversion may be inaccurate. Consider using '#align free_monoid.count_apply FreeMonoid.count_applyₓ'. -/
theorem count_apply [DecidableEq α] (x : α) (l : FreeAddMonoid α) :
    count x l = Multiplicative.ofAdd (List.count x l) :=
  rfl
#align free_monoid.count_apply FreeMonoid.count_apply

/- warning: free_monoid.count_of -> FreeMonoid.count_of is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{1} (Multiplicative.{0} Nat) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (FreeMonoid.{u1} α) -> (Multiplicative.{0} Nat)) (MonoidHom.hasCoeToFun.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.cancelMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) (FreeMonoid.of.{u1} α y)) (Pi.mulSingle.{u1, 0} α (fun (_x : α) => Multiplicative.{0} Nat) (fun (a : α) (b : α) => _inst_2 a b) (fun (i : α) => Multiplicative.hasOne.{0} Nat Nat.hasZero) x (coeFn.{1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) (fun (_x : Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) => Nat -> (Multiplicative.{0} Nat)) (Equiv.hasCoeToFun.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) y)
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) (FreeMonoid.of.{u1} α y)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (fun (_x : FreeMonoid.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : FreeMonoid.{u1} α) => Multiplicative.{0} Nat) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (MulOneClass.toMul.{u1} (FreeMonoid.{u1} α) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α))))) (MulOneClass.toMul.{0} (Multiplicative.{0} Nat) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (MonoidHom.monoidHomClass.{u1, 0} (FreeMonoid.{u1} α) (Multiplicative.{0} Nat) (Monoid.toMulOneClass.{u1} (FreeMonoid.{u1} α) (RightCancelMonoid.toMonoid.{u1} (FreeMonoid.{u1} α) (CancelMonoid.toRightCancelMonoid.{u1} (FreeMonoid.{u1} α) (FreeMonoid.instCancelMonoidFreeMonoid.{u1} α)))) (Multiplicative.mulOneClass.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) (FreeMonoid.count.{u1} α (fun (a : α) (b : α) => _inst_2 a b) x) (FreeMonoid.of.{u1} α y)) (Pi.mulSingle.{u1, 0} α (fun (_x : α) => Multiplicative.{0} Nat) (fun (a : α) (b : α) => _inst_2 a b) (fun (i : α) => instOneMultiplicative.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) x (FunLike.coe.{1, 1, 1} (Equiv.{1, 1} Nat (Multiplicative.{0} Nat)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Nat) => Multiplicative.{0} Nat) _x) (Equiv.instFunLikeEquiv.{1, 1} Nat (Multiplicative.{0} Nat)) (Multiplicative.ofAdd.{0} Nat) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) y)
Case conversion may be inaccurate. Consider using '#align free_monoid.count_of FreeMonoid.count_ofₓ'. -/
theorem count_of [DecidableEq α] (x y : α) :
    count x (of y) = @Pi.mulSingle α (fun _ => Multiplicative ℕ) _ _ x (Multiplicative.ofAdd 1) y :=
  by simp only [count, countp_of, Pi.mulSingle_apply, eq_comm]
#align free_monoid.count_of FreeMonoid.count_of

end FreeMonoid


example (a : Nat) : (a - 8000) + 1 = Nat.succ (a - 8000) := by
  rfl

theorem mwe (a : Nat) : (a - 100000000) + 1 <= 1 := by
  apply Nat.succ_le_succ
  sorry

@[irreducible]
def someConstant : Nat := 100000000

theorem mwe' (a : Nat) : (a - someConstant) + 1 <= someConstant + 1 := by
  apply Nat.succ_le_succ
  sorry

theorem mwe'' (a : Nat) : Nat.add (a - 100000000) 1 <= Nat.zero.succ := by
  apply Nat.succ_le_succ
  sorry


import data.real.basic
import data.set.intervals.unordered_interval

theorem USAMO_Problem_3_1993 (f : ℝ -> ℝ) [f ≥ 0] [f(1) = 1]:
(∀ x y : ℝ, (x ∈ (set.interval (0 : ℝ) (1: ℝ)) ∧ y ∈ (set.interval (0 : ℝ) (1: ℝ))) →
 x+y ∈ (set.interval (0 : ℝ) (1: ℝ)) → f(x) + f(y) ≤ f(x+y)) →
(∀ x : ℝ, x ∈ (set.interval (0 : ℝ) (1: ℝ)) → f(x) >= 2*x)
∧ (¬ (∃ c : ℝ, c < 2 ∧ (∀ x : ℝ, x ∈ (set.interval (0 : ℝ) (1: ℝ)) →  f(x) >= c*x)))
:= sorry


/-
Copyright (c) 2021 David Wärn,. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: David Wärn, Scott Morrison
-/
import combinatorics.quiver.basic

/-!
# Paths in quivers

Given a quiver `V`, we define the type of paths from `a : V` to `b : V` as an inductive
family. We define composition of paths and the action of prefunctors on paths.
-/

universes v v₁ v₂ u u₁ u₂

namespace quiver

/-- `G.path a b` is the type of paths from `a` to `b` through the arrows of `G`. -/
inductive path {V : Type u} [quiver.{v} V] (a : V) : V → Sort (max (u+1) v)
| nil  : path a
| cons : Π {b c : V}, path b → (b ⟶ c) → path c

/-- An arrow viewed as a path of length one. -/
def hom.to_path {V} [quiver V] {a b : V} (e : a ⟶ b) : path a b :=
path.nil.cons e

namespace path

variables {V : Type u} [quiver V]

/-- The length of a path is the number of arrows it uses. -/
def length {a : V} : Π {b : V}, path a b → ℕ
| _ path.nil        := 0
| _ (path.cons p _) := p.length + 1

instance {a : V} : inhabited (path a a) := ⟨path.nil⟩

@[simp] lemma length_nil {a : V} :
  (path.nil : path a a).length = 0 := rfl

@[simp] lemma length_cons (a b c : V) (p : path a b)
  (e : b ⟶ c) : (p.cons e).length = p.length + 1 := rfl

/-- Composition of paths. -/
def comp {a b : V} : Π {c}, path a b → path b c → path a c
| _ p (path.nil) := p
| _ p (path.cons q e) := (p.comp q).cons e

@[simp] lemma comp_cons {a b c d : V} (p : path a b) (q : path b c) (e : c ⟶ d) :
  p.comp (q.cons e) = (p.comp q).cons e := rfl
@[simp] lemma comp_nil {a b : V} (p : path a b) : p.comp path.nil = p := rfl
@[simp] lemma nil_comp {a : V} : ∀ {b} (p : path a b), path.nil.comp p = p
| a path.nil := rfl
| b (path.cons p e) := by rw [comp_cons, nil_comp]
@[simp] lemma comp_assoc {a b c : V} : ∀ {d}
  (p : path a b) (q : path b c) (r : path c d),
    (p.comp q).comp r = p.comp (q.comp r)
| c p q path.nil := rfl
| d p q (path.cons r e) := by rw [comp_cons, comp_cons, comp_cons, comp_assoc]

end path

end quiver

namespace prefunctor

open quiver

variables {V : Type u₁} [quiver.{v₁} V] {W : Type u₂} [quiver.{v₂} W] (F : prefunctor V W)

/-- The image of a path under a prefunctor. -/
def map_path {a : V} :
  Π {b : V}, path a b → path (F.obj a) (F.obj b)
| _ path.nil := path.nil
| _ (path.cons p e) := path.cons (map_path p) (F.map e)

@[simp] lemma map_path_nil (a : V) : F.map_path (path.nil : path a a) = path.nil := rfl
@[simp] lemma map_path_cons {a b c : V} (p : path a b) (e : b ⟶ c) :
  F.map_path (path.cons p e) = path.cons (F.map_path p) (F.map e) := rfl

@[simp] lemma map_path_comp {a b : V} (p : path a b) :
  ∀ {c : V} (q : path b c), F.map_path (p.comp q) = (F.map_path p).comp (F.map_path q)
| _ path.nil := rfl
| _ (path.cons p e) := begin dsimp, rw [map_path_comp], end

end prefunctor


/-
Copyright (c) 2022 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic -- imports all the Lean tactics

/-!

# Logic in Lean, example sheet 2 : `true` and `false`

We learn about the `true` and `false` propositions.

## Tactics you will need

To solve the levels on this sheet you will need to know all previous
tactics, plus the following two new ones. Check out their explanations
in the course book. Or just try them out and hover over them to see
if you can understand what's going on.

* `triv`
* `exfalso`

-/

-- Throughout this sheet, `P`, `Q` and `R` will denote propositions.

variables (P Q R : Prop)

example : true :=
begin
  sorry
end

example : true → true :=
begin
  sorry
end

example : false → true :=
begin
  sorry
end

example : false → false :=
begin
  sorry
end

example : (true → false) → false :=
begin
  sorry
end

example : false → P :=
begin
  sorry
end

example : true → false → true → false → true → false :=
begin
  sorry
end

example : P → ((P → false) → false) :=
begin
  sorry
end

example : (P → false) → P → Q :=
begin
  sorry
end

example : (true → false) → P :=
begin
  sorry
end


open function

#print surjective
#check @exists.intro

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}
open function

lemma surjective_comp {g : β → γ} {f : α → β}
  (hg : surjective g) (hf : surjective f) :
surjective (g ∘ f)
| y :=  ⟨ exists.elim (hg y), hf (exists.elim (hg y)) ⟩

namespace hidden
inductive my_nat : Type
| zero : my_nat
| succ : my_nat → my_nat

namespace my_nat
notation `ze` := my_nat.zero
notation a `+o` := my_nat.succ a
notation `one` := ze +o

def add : my_nat → my_nat → my_nat
| n ze := n
| n (m +o) := (add n m) +o

notation a `pl` b := add a b
local infix ` + ` := add
#check ze pl (ze +o)

def mult : my_nat → my_nat → my_nat
| n ze := ze
| n (m +o) := (mult n m) + n

notation a `mul` b := mult a b
local infix ` * ` := mult

def exp : my_nat → my_nat → my_nat
| n ze := one
| n (m +o) := (exp n m) * n

theorem zero_add : ∀ (n : my_nat), ze + n = n
| ze := rfl
| (n +o) := congr_arg succ (zero_add n)

end my_nat
end hidden

namespace hidden

inductive list (α : Type*)
| nil {} : list
| cons : α → list → list

namespace list

notation `[` l:(foldr `,` (h t, cons h t) nil) `]` := l

notation h :: t  := cons h t

def append (s t : list α) : list α :=
list.rec_on s t (λ x l u, x::u)

notation s ++ t := append s t

theorem nil_append (t : list α) : nil ++ t = t := rfl

theorem cons_append (x : α) (s t : list α) : x::s ++ t = x::(s ++ t) := rfl

-- BEGIN
theorem append_nil (t : list α) : t ++ nil = t := sorry

theorem append_assoc (r s t : list α) :
  r ++ s ++ t = r ++ (s ++ t) := sorry
-- END

def rev : list α → list α
| nil := nil
| (h :: t) := (rev t) ++ [h]

#reduce [1,2,5,4,6,3]
#reduce rev [1,2,5,4,6,3]

theorem rev_rev : ∀(l : list α ), rev (rev l) = l
| nil := rfl
| (h :: t) := sorry

end list

end hidden