import data.real.irrational
import topology.basic
import algebra.order.floor

--OUTPUT 1
theorem irrational_orbit_dense {α : ℝ} (hα_irrat : irrational α) : closure ((λ m : ℤ, int.fract (α * ↑m)) '' (@set.univ ℤ)) = set.Icc 0 1 :=density_of_irrational_orbit (α : ℝ) (hα : ¬ is_rat α) : ∀ y ∈ Icc 0 1, ∃ x ∈ Icc 0 1, ∃ N : ℤ, |y - (N : ℝ) * x| < 1 :=
begin
  assume (y : ℝ) (h1 : y ∈ Icc 0 1),
  -- Let $\alpha$ be an irrational number. Then for distinct $i, j \in \mathbb{Z}$, we must have $\{i \alpha\} \neq\{j \alpha\}$. If this were not true, then
  -- $i \alpha-\lfloor i \alpha\rfloor=\{i \alpha\}=\{j \alpha\}=j \alpha-\lfloor j \alpha\rfloor$,
  -- which yields the false statement $\alpha=\frac{\lfloor i \alpha\rfloor-\lfloor j \alpha\rfloor}{i-j} \in \mathbb{Q}$. Hence,
  -- $S:=\{\{i \alpha\} \mid i \in \mathbb{Z}\}$
  -- is an infinite subset of $\left[0,1\right]$.
  have h2 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h3 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h4 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h5 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h6 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h7 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h8 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h9 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h10 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h11 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h12 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h13 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h14 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h15 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h16 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h17 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h18 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h19 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h20 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h21 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h22 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h23 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h24 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h25 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h26 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor (j : ℝ) * α, from sorry,
  have h27 : ∀ i j : ℤ, i ≠ j → (i : ℝ) * α - floor (i : ℝ) * α ≠ (j : ℝ) * α - floor
end --Needs more than 2000 tokens!

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

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

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

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

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

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

Thus $A \cap B \in \powerset S$ and closure is proved.
{{qed}}
-/
theorem power_set_intersection_closed {α : Type*} (S : set α) : ∀ A B ∈ 𝒫 S, (A ∩ B) ∈ 𝒫 S :=
begin
  -- $A$ and $B$ are sets. $A$ and $B$ belong to power set of $S$
  assume (A : set α) (hA : A ∈ 𝒫 S) (B : set α) (hB : B ∈ 𝒫 S),
  -- Then $A ⊆ S$ and $B ⊆ S$, by power set definition
  have h1 : (A ⊆ S) ∧ (B ⊆ S), from sorry,
  -- Then $(A ∩ B) ⊆ A$, by intersection of set is a subset
  have h2 : (A ∩ B) ⊆ A, from sorry,
  -- Then $(A ∩ B) ⊆ S$, by subset relation is transitive
  have h3 : (A ∩ B) ⊆ S, from sorry,
  -- Hence $(A ∩ B) ∈  𝒫 S$, by power set definition
  show (A ∩ B) ∈  𝒫 S, from sorry,
end

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

{{begin-eqn}}
{{eqn | l = \left({x + y}\right)^2
      | r = \left({x + y}\right) \cdot \left({x + y}\right)
}}
{{eqn | r = x \cdot \left({x + y}\right) + y \cdot \left({x + y}\right)
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x \cdot x + x \cdot y + y \cdot x + y \cdot y
      | c = Real Multiplication Distributes over Addition
}}
{{eqn | r = x^2 + 2xy + y^2
      | c =
}}
{{end-eqn}}
{{qed}}
-/
theorem square_of_sum (x y : ℝ) : (x + y)^2 = (x^2 + 2*x*y + y^2) :=
begin
  -- expand the power
  calc (x + y)^2 = (x+y)*(x+y) : by sorry
  -- distributive property of multiplication over addition gives:
  ... = x*(x+y) + y*(x+y) : by sorry
  -- applying the above property further gives:
  ... = x*x + x*y + y*x + y*y : by sorry
  -- rearranging the terms using commutativity and adding gives:
  ... = x^2 + 2*x*y + y^2 : by sorry,
end

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

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

Setting $b = a$, this becomes:

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

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

These $x$ and $y$ are both $e$, by definition of identity element.
{{qed}}
-/
theorem group_identity_unique {G : Type*} [group G] : ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a :=
begin
  -- Group has Latin Square Property
  have h1 : ∀ a b : G, ∃! x : G, a * x = b, from sorry,
  have h2 : ∀ a b : G, ∃! y : G, y * a = b, from sorry,

  -- Setting $b = a$, this becomes:
  have h3 : ∀ a : G, ∃! x : G, a * x = a, from sorry,
  have h4 : ∀ a : G, ∃! y : G, y * a = a, from sorry,

  -- These $x$ and $y$ are both $(1 : G)$, by definition of identity element
  have h5 : ∀ a : G, classical.some (h3 a) = (1 : G), from sorry,
  have h6 : ∀ a : G, classical.some (h4 a) = (1 : G), from sorry,

  show ∃! e : G, ∀ a : G, e * a = a ∧ a * e = a, from by {
    use (1 : G),
    have h7 : ∀ e : G, (∀ a : G, e * a = a ∧ a * e = a) → e = 1, from by {
      assume (e : G) (h7 : ∀ a : G, e * a = a ∧ a * e = a),
      have h8 : ∀ a : G, e = classical.some (h3 a), from sorry,
      have h9 : ∀ a : G, e = classical.some (h4 a), from sorry,
      show e = (1 : G), from sorry,
    },
    sorry,
  }
end

/--`theorem`
Squeeze Theorem for Real Numbers
Let $\sequence {x_n}$, $\sequence {y_n}$ and $\sequence {z_n}$ be sequences in $\R$.

Let $\sequence {y_n}$ and $\sequence {z_n}$ both be convergent to the following limit:
:$\ds \lim_{n \mathop \to \infty} y_n = l, \lim_{n \mathop \to \infty} z_n = l$

Suppose that:
:$\forall n \in \N: y_n \le x_n \le z_n$

Then:
:$x_n \to l$ as $n \to \infty$
that is:
:$\ds \lim_{n \mathop \to \infty} x_n = l$

`proof`
From Negative of Absolute Value:
:$\size {x - l} < \epsilon \iff l - \epsilon < x < l + \epsilon$

Let $\epsilon > 0$.

We need to prove that:
:$\exists N: \forall n > N: \size {x_n - l} < \epsilon$

As $\ds \lim_{n \mathop \to \infty} y_n = l$ we know that:
:$\exists N_1: \forall n > N_1: \size {y_n - l} < \epsilon$

As $\ds \lim_{n \mathop \to \infty} z_n = l$ we know that:
:$\exists N_2: \forall n > N_2: \size {z_n - l} < \epsilon$

Let $N = \max \set {N_1, N_2}$.

Then if $n > N$, it follows that $n > N_1$ and $n > N_2$.

So:
:$\forall n > N: l - \epsilon < y_n < l + \epsilon$
:$\forall n > N: l - \epsilon < z_n < l + \epsilon$

But:
:$\forall n \in \N: y_n \le x_n \le z_n$

So:
:$\forall n > N: l - \epsilon < y_n \le x_n \le z_n < l + \epsilon$

and so:
:$\forall n > N: l - \epsilon < x_n < l + \epsilon$

So:
:$\forall n > N: \size {x_n - l} < \epsilon$

Hence the result.
{{qed}}

-/
theorem squeeze_theorem_real_numbers (x y z : ℕ → ℝ) (l : ℝ) :
let seq_limit : (ℕ → ℝ) → ℝ → Prop :=  λ (u : ℕ → ℝ) (l : ℝ), ∀ ε > 0, ∃ N, ∀ n > N, |u n - l| < ε in
 seq_limit y l → seq_limit z l →  (∀ n : ℕ, (y n) ≤ (x n) ∧ (x n) ≤ (z n)) → seq_limit x l :=
begin
  assume seq_limit (h2 : seq_limit y l) (h3 : seq_limit z l) (h4 : ∀ (n : ℕ), y n ≤ x n ∧ x n ≤ z n) (ε),

  --From Negative of Absolute Value: $\size {x - l} < \epsilon \iff l - \epsilon < x < l + \epsilon$
  have h5 : ∀ x, |x - l| < ε ↔ (((l - ε) < x) ∧ (x < (l + ε))),
  from sorry,

  --Let $\epsilon > 0$.
  assume (h7 : ε > 0),

  --As $\ds \lim_{n \mathop \to \infty} y_n = l$ we know that $\exists N_1: \forall n > N_1: \size {y_n - l} < \epsilon$
  cases h2 ε h7 with N1 h8,

  --As $\ds \lim_{n \mathop \to \infty} z_n = l$ we know that $\exists N_2: \forall n > N_2: \size {z_n - l} < \epsilon$
  cases h3 ε h7 with N2 h9,

  --Let $N = \max \set {N_1, N_2}$.
  let N := max N1 N2,
  use N,

  --Then if $n > N$, it follows that $n > N_1$ and $n > N_2$.
  have h10 : ∀ n > N, n > N1 ∧ n > N2 := sorry,

  --$\forall n > N: l - \epsilon < y_n < l + \epsilon$
  --$\forall n > N: l - \epsilon < z_n < l + \epsilon$
  --$\forall n \in \N: y_n \le x_n \le z_n$
  --So $\forall n > N: l - \epsilon < y_n \le x_n \le z_n < l + \epsilon$
  have h11 : ∀ n > N, (((l - ε) < (y n)) ∧ ((y n) ≤ (x n))) ∧ (((x n) ≤ (z n)) ∧ ((z n) < l+ε)),
  from sorry,

  --$\forall n > N: l - \epsilon < x_n < l + \epsilon$
  have h15 : ∀ n > N, ((l - ε) < (x n)) ∧ ((x n) < (l+ε)),
  from sorry,

  --So $\forall n > N: \size {x_n - l} < \epsilon$
  --Hence the result
  show  ∀ (n : ℕ), n > N → |x n - l| < ε,
  from sorry,
end

/--`theorem`
Density of irrational orbit
The fractional parts of the integer multiples of an irrational number form a dense subset of the unit interval
`proof`
Let $\alpha$ be an irrational number. Then for distinct $i, j \in \mathbb{Z}$, we must have $\{i \alpha\} \neq\{j \alpha\}$. If this were not true, then
$$
i \alpha-\lfloor i \alpha\rfloor=\{i \alpha\}=\{j \alpha\}=j \alpha-\lfloor j \alpha\rfloor,
$$
which yields the false statement $\alpha=\frac{\lfloor i \alpha\rfloor-\lfloor j \alpha\rfloor}{i-j} \in \mathbb{Q}$. Hence,
$$
S:=\{\{i \alpha\} \mid i \in \mathbb{Z}\}
$$
is an infinite subset of $\left[0,1\right]$.

By the Bolzano-Weierstrass theorem, $S$ has a limit point in $[0, 1]$. One can thus find pairs of elements of $S$ that are arbitrarily close. Since (the absolute value of) the difference of any two elements of $S$ is also an element of $S$, it follows that $0$ is a limit point of $S$.

To show that $S$ is dense in $[0, 1]$, consider $y \in[0,1]$, and $\epsilon>0$. Then by selecting $x \in S$ such that $\{x\}<\epsilon$ (which exists as $0$ is a limit point), and $N$ such that $N \cdot\{x\} \leq y<(N+1) \cdot\{x\}$, we get: $|y-\{N x\}|<\epsilon$.

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


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

import for_mathlib.algebraic_topology.homotopical_algebra.cochain_complex.cm1
import for_mathlib.algebraic_topology.homotopical_algebra.cochain_complex.cm5a
import for_mathlib.algebra.homology.termwise_split
import for_mathlib.algebra.homology.lifting

noncomputable theory

open category_theory category_theory.category algebraic_topology

namespace category_theory

variables {C D : Type*} [category C] [category D]
  {A B X Y : C} (i : A ⟶ B) (p : X ⟶ Y)

lemma has_lifting_property.iff_of_fully_faithful (F : C ⥤ D)
  [full F] [faithful F] :
  has_lifting_property (F.map i) (F.map p) ↔
    has_lifting_property i p :=
begin
  split,
  { introI,
    exact ⟨λ f g sq, ⟨⟨{ l := F.preimage ((sq.map F).lift),
      fac_left' := F.map_injective (by simp),
      fac_right' := F.map_injective (by simp), }⟩⟩⟩, },
  { introI,
    refine ⟨λ f g sq, _⟩,
    have sq' : comm_sq (F.preimage f) i p (F.preimage g) :=
      ⟨F.map_injective (by simp only [functor.map_comp, functor.image_preimage, sq.w])⟩,
    exact ⟨⟨{ l := F.map sq'.lift,
      fac_left' :=
        by simpa only [F.map_comp, F.image_preimage] using F.congr_map sq'.fac_left,
      fac_right' :=
        by simpa only [F.map_comp, F.image_preimage] using F.congr_map sq'.fac_right, }⟩⟩, },
end

end category_theory

variables {C : Type*} [category C] [abelian C]

namespace cochain_complex

namespace minus

namespace projective_model_structure

open cochain_complex.hom_complex

section

variables {A B : minus C} (i : A ⟶ B) (hi : (arrow_classes C).cof i)
include hi

def splittings_of_cof (n : ℤ) : splitting (i.f n) ((limits.cokernel.π i).f n) :=
begin
-- show that it is a short_exact sequence
-- show that the cokernel is projective
-- then there is a splitting...
  sorry,
end

def cocycle_of_cof : cocycle (limits.cokernel i).obj A.obj 1 :=
twist.iso_of_termwise_split.z (splittings_of_cof i hi)

def iso_twist_of_cof : twist (cocycle_of_cof i hi) ≅ B.obj :=
twist.iso_of_termwise_split (splittings_of_cof i hi)

def arrow_iso_of_cof : arrow.mk (twist.inr (cocycle_of_cof i hi)) ≅ arrow.mk (ι.map i) := sorry

end

def CM4a : (arrow_classes C).CM4a :=
λ A B X Y i hi p hp, begin
  --has_lifting_property.iff_of_arrow_iso_left (arrow_iso_of_cof i hi.1) p,
  --cochain_complex.lifting.lift_of_coboundary
  sorry,
end

def CM4b : (arrow_classes C).CM4b := sorry

def CM4 : (arrow_classes C).CM4 :=
  ⟨CM4a, CM4b⟩

end projective_model_structure

end minus

end cochain_complex


import Iris.BI.BigOp
import Iris.BI.Classes
import Iris.BI.Extensions
import Iris.BI.Interface
import Iris.BI.DerivedConnectives
import Iris.Std.Classes
import Iris.Std.Rewrite
import Iris.Std.TC

namespace Iris.BI
open Iris.Std
open BI

-- Entails
instance entails_anti_symm [BI PROP] : AntiSymm (α := PROP) (· ⊣⊢ ·) (· ⊢ ·) where
  anti_symm := by
    intro _ _ H1 H2
    rw' [equiv_entails]
    exact And.intro H1 H2

-- Logic
theorem and_elim_l' [BI PROP] {P Q R : PROP} : (P ⊢ R) → P ∧ Q ⊢ R := by
  intro H
  rw' [and_elim_l, H]

theorem and_elim_r' [BI PROP] {P Q R : PROP} : (Q ⊢ R) → P ∧ Q ⊢ R := by
  intro H
  rw' [and_elim_r, H]

theorem or_intro_l' [BI PROP] {P Q R : PROP} : (P ⊢ Q) → P ⊢ Q ∨ R := by
  intro H
  rw' [H, ← or_intro_l]

theorem or_intro_r' [BI PROP] {P Q R : PROP} : (P ⊢ R) → P ⊢ Q ∨ R := by
  intro H
  rw' [H, ← or_intro_r]

theorem impl_intro_l [BI PROP] {P Q R : PROP} : (Q ∧ P ⊢ R) → P ⊢ Q → R := by
  intro H
  apply impl_intro_r
  rw' [← H]
  apply and_intro
  · exact and_elim_r
  · exact and_elim_l

theorem impl_elim [BI PROP] {P Q R : PROP} : (P ⊢ Q → R) → (P ⊢ Q) → P ⊢ R := by
  intro H1 H2
  rw' [← impl_elim_l' H1]
  apply and_intro
  · simp
  · exact H2

theorem impl_elim_r' [BI PROP] {P Q R : PROP} : (Q ⊢ P → R) → P ∧ Q ⊢ R := by
  intros H
  apply impl_elim (Q := P)
  · rw' [and_elim_r, H]
  · rw' [and_elim_l]

theorem impl_elim_l [BI PROP] {P Q : PROP} : (P → Q) ∧ P ⊢ Q := by
  apply impl_elim_l'
  simp

theorem impl_elim_r [BI PROP] {P Q : PROP} : P ∧ (P → Q) ⊢ Q := by
  apply impl_elim_r'
  simp

theorem False_elim [BI PROP] {P : PROP} : False ⊢ P := by
  apply pure_elim'
  simp

theorem True_intro [BI PROP] {P : PROP} : P ⊢ True := by
  apply pure_intro
  simp

@[rwMonoRule]
theorem and_mono [BI PROP] {P P' Q Q' : PROP} : (P ⊢ Q) → (P' ⊢ Q') → P ∧ P' ⊢ Q ∧ Q' := by
  intro H1 H2
  apply and_intro
  · rw' [← H1, and_elim_l]
  · rw' [← H2, and_elim_r]

@[rwMonoRule]
theorem or_mono [BI PROP] {P P' Q Q' : PROP} : (P ⊢ Q) → (P' ⊢ Q') → P ∨ P' ⊢ Q ∨ Q' := by
  intro H1 H2
  apply or_elim
  · apply or_intro_l'
    exact H1
  · apply or_intro_r'
    exact H2

@[rwMonoRule]
theorem impl_mono [BI PROP] {P P' Q Q' : PROP} : (Q ⊢ P) → (P' ⊢ Q') → (P → P') ⊢ Q → Q' := by
  intro HP HQ
  apply impl_intro_r
  rw' [HP, ← HQ]
  apply impl_elim_l'
  simp

@[rwMonoRule]
theorem forall_mono [BI PROP] {Φ Ψ : α → PROP} :
  (∀ a, Φ a ⊢ Ψ a) → (∀ a, Φ a) ⊢ ∀ a, Ψ a
:= by
  intro Hφ
  apply forall_intro
  intro a
  rw' [← Hφ a, ← forall_elim _]

@[rwMonoRule]
theorem exist_mono [BI PROP] {Φ Ψ : α → PROP} :
  (∀ a, Φ a ⊢ Ψ a) → (∃ a, Φ a) ⊢ ∃ a, Ψ a
:= by
  intro Hφ
  apply exist_elim
  intro a
  rw' [Hφ a, exist_intro _]

instance and_idemp [BI PROP] : Idemp (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∧ ·]) where
  idemp := by
    intro _
    apply anti_symm
    · exact and_elim_l
    · apply and_intro
      <;> simp

instance or_idemp [BI PROP] : Idemp (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∨ ·]) where
  idemp := by
    intro _
    apply anti_symm
    · apply or_elim
      <;> simp
    · exact or_intro_l

instance and_comm [BI PROP] : Comm (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∧ ·]) where
  comm := by
    intros
    apply anti_symm
    case left =>
      apply and_intro
      · exact and_elim_r
      · exact and_elim_l
    case right =>
      apply and_intro
      · exact and_elim_r
      · exact and_elim_l

instance True_and [BI PROP] : LeftId (α := PROP) (· ⊣⊢ ·) `[iprop| True] (`[iprop| · ∧ ·]) where
  left_id := by
    intros
    apply anti_symm
    case left =>
      exact and_elim_r
    case right =>
      apply and_intro ?_ reflexivity
      apply pure_intro
      simp

instance and_True [BI PROP] : RightId (α := PROP) (· ⊣⊢ ·) `[iprop| True] (`[iprop| · ∧ ·]) where
  right_id := by
    intros
    apply anti_symm
    case left =>
      exact and_elim_l
    case right =>
      apply and_intro reflexivity ?_
      apply pure_intro
      simp

instance and_assoc [BI PROP] : Assoc (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∧ ·]) where
  assoc := by
    intro _ _ _
    apply anti_symm
    <;> apply and_intro
    <;> try apply and_intro
    all_goals
      simp [and_elim_l, and_elim_r]
      try { apply and_elim_l' ; simp [and_elim_l, and_elim_r] }
      try { apply and_elim_r' ; simp [and_elim_l, and_elim_r] }

theorem and_or_l [BI PROP] {P Q R : PROP} : P ∧ (Q ∨ R) ⊣⊢ P ∧ Q ∨ P ∧ R := by
  apply anti_symm
  case left =>
    apply impl_elim_r'
    apply or_elim
    <;> apply impl_intro_l
    · exact or_intro_l
    · exact or_intro_r
  case right =>
    apply and_intro
    · apply or_elim
      <;> apply and_elim_l'
      <;> simp
    · apply or_elim
      <;> apply and_elim_r'
      · exact or_intro_l
      · exact or_intro_r

theorem and_exist_l [BI PROP] {P : PROP} {Ψ : α → PROP} : P ∧ (∃ a, Ψ a) ⊣⊢ ∃ a, P ∧ Ψ a := by
  apply anti_symm
  case left =>
    apply impl_elim_r'
    apply exist_elim
    intro a
    apply impl_intro_l
    rw' [← exist_intro a]
  case right =>
    apply exist_elim
    intro a
    apply and_intro
    · rw' [and_elim_l]
    · rw' [← exist_intro a, and_elim_r]

theorem or_alt [BI PROP] {P Q : PROP} : P ∨ Q ⊣⊢ ∃ (b : Bool), if b then P else Q := by
  apply anti_symm
  case left =>
    apply or_elim
    · rw' [← exist_intro true]
    · rw' [← exist_intro false]
  case right =>
    apply exist_elim
    intro b
    cases b
    · rw' [← or_intro_r]
    · rw' [← or_intro_l]

-- BI
@[rwMonoRule]
theorem wand_mono [BI PROP] {P P' Q Q' : PROP} : (Q ⊢ P) → (P' ⊢ Q') → (P -∗ P') ⊢ Q -∗ Q' := by
  intro HP HQ
  apply wand_intro_r
  rw' [HP, ← HQ]
  apply wand_elim_l'
  simp

instance sep_comm [BI PROP] : Comm (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∗ ·]) where
  comm := by
    intros
    apply anti_symm
    <;> exact sep_comm'

instance sep_assoc [BI PROP] : Assoc (α := PROP) (· ⊣⊢ ·) (`[iprop| · ∗ ·]) where
  assoc := by
    intros P Q R
    apply anti_symm
    case left =>
      rw' [
        (comm : P ∗ (Q ∗ R) ⊣⊢ _),
        (comm : P ∗ Q ⊣⊢ _),
        (comm : Q ∗ R ⊣⊢ _),
        (comm : (Q ∗ P) ∗ R ⊣⊢ _),
        sep_assoc']
    case right =>
      exact sep_assoc'

instance emp_sep [BI PROP] : LeftId (α := PROP) (· ⊣⊢ ·) `[iprop| emp] (`[iprop| · ∗ ·]) where
  left_id := by
    intros
    apply anti_symm
    · exact emp_sep_2
    · exact emp_sep_1

instance sep_emp [BI PROP] : RightId (α := PROP) (· ⊣⊢ ·) `[iprop| emp] (`[iprop| · ∗ ·]) where
  right_id := by
    intro x
    rw' [(comm : `[iprop| x ∗ emp] ⊣⊢ _), (left_id : emp ∗ x ⊣⊢ _)]

theorem True_sep_2 [BI PROP] {P : PROP} : P ⊢ True ∗ P := by
  rw' [emp_sep_1]
  apply sep_mono ?_ reflexivity
  apply pure_intro
  simp

theorem wand_intro_l [BI PROP] {P Q R : PROP} : (Q ∗ P ⊢ R) → P ⊢ Q -∗ R := by
  rw' [(comm : Q ∗ P ⊣⊢ _)]
  exact wand_intro_r

theorem wand_elim_l [BI PROP] {P Q : PROP} : (P -∗ Q) ∗ P ⊢ Q := by
  apply wand_elim_l'
  simp

theorem wand_elim_r [BI PROP] {P Q : PROP} : P ∗ (P -∗ Q) ⊢ Q := by
  rw' [sep_comm', wand_elim_l]

theorem wand_elim_r' [BI PROP] {P Q R : PROP} : (Q ⊢ P -∗ R) → P ∗ Q ⊢ R := by
  intro H
  rw' [H, wand_elim_r]

theorem sep_or_l [BI PROP] {P Q R : PROP} : P ∗ (Q ∨ R) ⊣⊢ (P ∗ Q) ∨ (P ∗ R) := by
  apply anti_symm
  case left =>
    apply wand_elim_r'
    apply or_elim
    <;> apply wand_intro_l
    · exact or_intro_l
    · exact or_intro_r
  case right =>
    apply or_elim
    · rw' [← or_intro_l]
    · rw' [← or_intro_r]

theorem sep_or_r [BI PROP] {P Q R : PROP} : (P ∨ Q) ∗ R ⊣⊢ (P ∗ R) ∨ (Q ∗ R) := by
  rw' [!(comm : _ ∗ R ⊣⊢ _), sep_or_l]

theorem sep_exist_l [BI PROP] {P : PROP} {Ψ : α → PROP} : P ∗ (∃ a, Ψ a) ⊣⊢ ∃ a, P ∗ Ψ a := by
  apply anti_symm
  case left =>
    apply wand_elim_r'
    apply exist_elim
    intro a
    apply wand_intro_l
    rw' [← exist_intro a]
  case right =>
    apply exist_elim
    intro a
    rw' [(exist_intro _ : Ψ a ⊢ _)]

theorem sep_exist_r [BI PROP] {Φ : α → PROP} {Q : PROP} : (∃ a, Φ a) ∗ Q ⊣⊢ ∃ a, Φ a ∗ Q := by
  apply anti_symm
  all_goals
    rw' [(comm : _ ∗ Q ⊣⊢ _), sep_exist_l]
    apply exist_mono
    intro _
    rw' [(comm : Q ∗ _ ⊣⊢ _)]

theorem wand_iff_refl [BI PROP] {P : PROP} : ⊢ P ∗-∗ P := by
  apply and_intro
  <;> apply wand_intro_l
  <;> rw' [(right_id : P ∗ emp ⊣⊢ _)]

theorem wand_entails [BI PROP] {P Q : PROP} : (⊢ P -∗ Q) → P ⊢ Q := by
  intro H
  rw' [← (left_id : emp ∗ P ⊣⊢ _), H]
  exact wand_elim_l

theorem entails_wand [BI PROP] {P Q : PROP} : (P ⊢ Q) → ⊢ P -∗ Q := by
  intro H
  rw' [← H]
  apply wand_intro_r
  rw' [(left_id : emp ∗ Q ⊣⊢ _)]

theorem equiv_wand_iff [BI PROP] {P Q : PROP} : (P ⊣⊢ Q) → ⊢ P ∗-∗ Q := by
  intro H
  rw' [H]
  exact wand_iff_refl

theorem wand_iff_equiv [BI PROP] {P Q : PROP} : (⊢ P ∗-∗ Q) → (P ⊣⊢ Q) := by
  intro HPQ
  apply anti_symm
  <;> apply wand_entails
  <;> rw' [HPQ]
  <;> simp [bi_wand_iff, and_elim_l, and_elim_r]

-- Pure
theorem pure_elim (φ : Prop) [BI PROP] {Q R : PROP} : (Q ⊢ ⌜φ⌝) → (φ → Q ⊢ R) → Q ⊢ R := by
  intro HQ HQR
  rw' [← (idemp : Q ∧ Q ⊣⊢ _), HQ]
  apply impl_elim_l'
  apply pure_elim'
  intro Hφ
  apply impl_intro_l
  rw' [and_elim_l]
  exact HQR Hφ

theorem pure_mono {φ1 φ2 : Prop} [BI PROP] : (φ1 → φ2) → ⌜φ1⌝ ⊢ (⌜φ2⌝ : PROP) := by
  intro H12
  apply pure_elim'
  intro H1
  apply pure_intro
  exact H12 H1

theorem pure_elim_l {φ : Prop} [BI PROP] {Q R : PROP} : (φ → Q ⊢ R) → ⌜φ⌝ ∧ Q ⊢ R := by
  intro H
  apply pure_elim φ
  · exact and_elim_l
  · intro Hφ
    rw' [H Hφ]
    exact and_elim_r

theorem pure_True {φ : Prop} [BI PROP] : φ → ⌜φ⌝ ⊣⊢ (True : PROP) := by
  intro Hφ
  apply anti_symm
  case left =>
    apply pure_intro
    exact True.intro
  case right =>
    apply pure_intro
    exact Hφ

theorem pure_and {φ1 φ2 : Prop} [BI PROP] : ⌜φ1 ∧ φ2⌝ ⊣⊢ ⌜φ1⌝ ∧ (⌜φ2⌝ : PROP) := by
  apply anti_symm
  case left =>
    apply and_intro
    <;> apply pure_mono
    <;> simp_all
  case right =>
    apply pure_elim φ1
    · exact and_elim_l
    intro _
    rw' [and_elim_r]
    apply pure_mono
    simp_all

theorem pure_or {φ1 φ2 : Prop} [BI PROP] : ⌜φ1 ∨ φ2⌝ ⊣⊢ ⌜φ1⌝ ∨ (⌜φ2⌝ : PROP) := by
  apply anti_symm
  case left =>
    apply pure_elim (φ1 ∨ φ2)
    · simp
    intro H
    cases H
    · apply or_intro_l'
      apply pure_mono
      simp_all
    · apply or_intro_r'
      apply pure_mono
      simp_all
  case right =>
    apply or_elim
    <;> apply pure_mono
    <;> simp_all

theorem pure_impl_1 {φ1 φ2 : Prop} [BI PROP] : ⌜φ1 → φ2⌝ ⊢ (⌜φ1⌝ → ⌜φ2⌝ : PROP) := by
  apply impl_intro_l
  rw' [← pure_and]
  apply pure_mono
  simp_all

theorem pure_forall_1 {φ : α → Prop} [BI PROP] : ⌜∀ x, φ x⌝ ⊢ ∀ x, (⌜φ x⌝ : PROP) := by
  apply forall_intro
  intro x
  apply pure_mono
  simp_all

theorem pure_exist [inst : BI PROP] {φ : α → Prop} : ⌜∃ x, φ x⌝ ⊣⊢ (∃ x, ⌜φ x⌝ : PROP) := by
  apply anti_symm
  case left =>
    apply pure_elim'
    intro ⟨x, H⟩
    rw' [← exist_intro x]
    apply pure_mono
    intro _
    exact H
  case right =>
    apply exist_elim
    intro a
    apply pure_mono
    intro H
    exact ⟨a, H⟩

-- Affine
theorem affinely_elim_emp [BI PROP] {P : PROP} : <affine> P ⊢ emp := by
  simp [bi_affinely, and_elim_l]

theorem affinely_elim [BI PROP] {P : PROP} : <affine> P ⊢ P := by
  simp [bi_affinely, and_elim_r]

@[rwMonoRule]
theorem affinely_mono [BI PROP] {P Q : PROP} : (P ⊢ Q) → <affine> P ⊢ <affine> Q := by
  intro H
  simp only [bi_affinely]
  rw' [H]

theorem affinely_idemp [BI PROP] {P : PROP} : <affine> <affine> P ⊣⊢ <affine> P := by
  simp only [bi_affinely]
  rw' [
    (assoc : emp ∧ emp ∧ _ ⊣⊢ _),
    (idemp : emp ∧ emp ⊣⊢ _)]

theorem affinely_emp [BI PROP] : <affine> emp ⊣⊢ (emp : PROP) := by
  simp only [bi_affinely]
  exact idemp

theorem affinely_or [BI PROP] {P Q : PROP} : <affine> (P ∨ Q) ⊣⊢ <affine> P ∨ <affine> Q := by
  exact and_or_l

theorem affinely_and [BI PROP] {P Q : PROP} : <affine> (P ∧ Q) ⊣⊢ <affine> P ∧ <affine> Q := by
  rw' [
    !bi_affinely,
    (comm : emp ∧ P ⊣⊢ _),
    (assoc : (P ∧ emp) ∧ _ ⊣⊢ _),
    ← (assoc : _ ⊣⊢ (P ∧ emp) ∧ _),
    ← (assoc : _ ⊣⊢ (P ∧ emp ∧ emp) ∧ _),
    (idemp : emp ∧ emp ⊣⊢ _),
    (assoc : P ∧ emp ∧ _ ⊣⊢ _),
    (assoc : emp ∧ P ∧ _ ⊣⊢ _),
    (comm : emp ∧ P ⊣⊢ _)]

theorem affinely_sep_2 [BI PROP] {P Q : PROP} : <affine> P ∗ <affine> Q ⊢ <affine> (P ∗ Q) := by
  simp only [bi_affinely]
  apply and_intro
  · rw' [!and_elim_l, (right_id : emp ∗ emp ⊣⊢ _)]
  · rw' [!and_elim_r]

theorem affinely_forall [BI PROP] {Φ : α → PROP} : <affine> (∀ a, Φ a) ⊢ ∀ a, <affine> (Φ a) := by
  apply forall_intro
  intro a
  rw' [forall_elim a]

theorem affinely_exist [BI PROP] {Φ : α → PROP} : <affine> (∃ a, Φ a) ⊣⊢ ∃ a, <affine> (Φ a) := by
  exact and_exist_l

theorem affinely_True_emp [BI PROP] : <affine> True ⊣⊢ <affine> (emp : PROP) := by
  apply anti_symm
  <;> simp only [bi_affinely]
  · apply and_intro
    <;> exact and_elim_l
  · rw' [(right_id : _ ∧ True ⊣⊢ _)]
    exact and_elim_l

theorem affinely_and_l [BI PROP] {P Q : PROP} : <affine> P ∧ Q ⊣⊢ <affine> (P ∧ Q) := by
  simp only [bi_affinely]
  rw' [(assoc : emp ∧ P ∧ _ ⊣⊢ _)]

theorem affinely_and_r [BI PROP] {P Q : PROP} : P ∧ <affine> Q ⊣⊢ <affine> (P ∧ Q) := by
  simp only [bi_affinely]
  rw' [
    (assoc : P ∧ emp ∧ _ ⊣⊢ _),
    (assoc : emp ∧ P ∧ _ ⊣⊢ _),
    (comm : P ∧ emp ⊣⊢ _)]

theorem affinely_and_lr [BI PROP] {P Q : PROP} : <affine> P ∧ Q ⊣⊢ P ∧ <affine> Q := by
  rw' [affinely_and_l, affinely_and_r]

-- Absorbing
theorem absorbingly_intro [BI PROP] {P : PROP} : P ⊢ <absorb> P := by
  exact True_sep_2

@[rwMonoRule]
theorem absorbingly_mono [BI PROP] {P Q : PROP} : (P ⊢ Q) → <absorb> P ⊢ <absorb> Q := by
  intro H
  simp only [bi_absorbingly]
  rw' [H]

theorem absorbingly_idemp [BI PROP] {P : PROP} : <absorb> <absorb> P ⊣⊢ <absorb> P := by
  apply anti_symm
  case left =>
    simp only [bi_absorbingly]
    rw' [(assoc : True ∗ True ∗ P ⊣⊢ _)]
    apply sep_mono ?_ reflexivity
    apply pure_intro
    simp
  case right =>
    rw' [← absorbingly_intro]

theorem absorbingly_pure {φ : Prop} [BI PROP] : <absorb> ⌜φ⌝ ⊣⊢ (⌜φ⌝ : PROP) := by
  apply anti_symm
  case left =>
    apply wand_elim_r'
    apply pure_elim'
    intro Hφ
    apply wand_intro_l
    apply pure_intro
    exact Hφ
  case right =>
    exact absorbingly_intro

theorem absorbingly_or [BI PROP] {P Q : PROP} : <absorb> (P ∨ Q) ⊣⊢ <absorb> P ∨ <absorb> Q := by
  simp [bi_absorbingly, sep_or_l]

theorem absorbingly_and_1 [BI PROP] {P Q : PROP} : <absorb> (P ∧ Q) ⊢ <absorb> P ∧ <absorb> Q := by
  apply and_intro
  · rw' [and_elim_l]
  · rw' [and_elim_r]

theorem absorbingly_forall [BI PROP] (Φ : α → PROP) : <absorb> (∀ a, Φ a) ⊢ ∀ a, <absorb> (Φ a) := by
  apply forall_intro
  intro a
  rw' [forall_elim a]

theorem absorbingly_exist [BI PROP] (Φ : α → PROP) : <absorb> (∃ a, Φ a) ⊣⊢ ∃ a, <absorb> (Φ a) := by
  simp [bi_absorbingly, sep_exist_l]

theorem absorbingly_sep [BI PROP] {P Q : PROP} : <absorb> (P ∗ Q) ⊣⊢ <absorb> P ∗ <absorb> Q := by
  rw' [← absorbingly_idemp]
  simp only [bi_absorbingly]
  rw' [
    (assoc : True ∗ P ∗ Q ⊣⊢ _),
    (assoc : True ∗ (True ∗ P) ∗ Q ⊣⊢ _),
    (comm : True ∗ True ∗ P ⊣⊢ _),
    ← (assoc : _ ⊣⊢ ((True ∗ P) ∗ True) ∗ Q)]

theorem absorbingly_True_emp [BI PROP] : <absorb> True ⊣⊢ <absorb> (emp : PROP) := by
  rw' [absorbingly_pure]
  simp only [bi_absorbingly]
  rw' [(right_id : True ∗ emp ⊣⊢ _)]

theorem absorbingly_wand [BI PROP] {P Q : PROP} : <absorb> (P -∗ Q) ⊢ <absorb> P -∗ <absorb> Q := by
  apply wand_intro_l
  rw' [← absorbingly_sep, wand_elim_r]

theorem absorbingly_sep_l [BI PROP] {P Q : PROP} : <absorb> P ∗ Q ⊣⊢ <absorb> (P ∗ Q) := by
  simp only [bi_absorbingly]
  rw' [(assoc : True ∗ P ∗ Q ⊣⊢ _)]

theorem absorbingly_sep_r [BI PROP] {P Q : PROP} : P ∗ <absorb> Q ⊣⊢ <absorb> (P ∗ Q) := by
  simp only [bi_absorbingly]
  rw' [!(assoc : _ ⊣⊢ _ ∗ Q), (comm : P ∗ True ⊣⊢ _)]

theorem absorbingly_sep_lr [BI PROP] {P Q : PROP} : <absorb> P ∗ Q ⊣⊢ P ∗ <absorb> Q := by
  rw' [absorbingly_sep_l, absorbingly_sep_r]

-- Affine / Absorbing Propositions
theorem affine_affinely [BI PROP] (P : PROP) [Affine P] : <affine> P ⊣⊢ P := by
  apply anti_symm
  <;> simp only [bi_affinely]
  · exact and_elim_r
  · apply and_intro
    · exact affine
    · simp

theorem absorbing_absorbingly [BI PROP] {P : PROP} [Absorbing P] : <absorb> P ⊣⊢ P := by
  apply anti_symm
  · exact absorbing
  · rw' [absorbingly_intro]

theorem sep_elim_l [BI PROP] {P Q : PROP} [instQP : TCOr (Affine Q) (Absorbing P)] : P ∗ Q ⊢ P := by
  cases instQP
  case l =>
    rw' [affine, (right_id : P ∗ emp ⊣⊢ _)]
  case r =>
    rw' [
      (pure_intro True.intro : Q ⊢ _),
      (comm : P ∗ True ⊣⊢ _),
      absorbing]

theorem sep_elim_r [BI PROP] {P Q : PROP} [TCOr (Affine P) (Absorbing Q)] : P ∗ Q ⊢ Q := by
  rw' [(comm : P ∗ Q ⊣⊢ _), sep_elim_l]

theorem sep_and [BI PROP] {P Q : PROP} [inst1 : TCOr (Affine P) (Absorbing Q)] [inst2 : TCOr (Absorbing P) (Affine Q)] :
  P ∗ Q ⊢ P ∧ Q
:= by
  cases inst1
  <;> cases inst2
  <;> apply and_intro
  <;> first | exact sep_elim_l | exact sep_elim_r

-- Persistent
theorem absorbingly_elim_persistently [BI PROP] {P : PROP} : <absorb> <pers> P ⊣⊢ <pers> P := by
  apply anti_symm
  case left =>
    simp only [bi_absorbingly]
    rw' [
      (comm : `[iprop| True ∗ <pers> P] ⊣⊢ _),
      persistently_absorbing]
  case right =>
    exact absorbingly_intro

theorem persistently_forall_1 [BI PROP] {Ψ : α → PROP} : <pers> (∀ a, Ψ a) ⊢ ∀ a, <pers> (Ψ a) := by
  apply forall_intro
  intro x
  rw' [forall_elim x]

theorem persistently_exist [BI PROP] {Ψ : α → PROP} : <pers> (∃ a, Ψ a) ⊣⊢ ∃ a, <pers> (Ψ a) := by
  apply anti_symm
  case left =>
    exact persistently_exist_1
  case right =>
    apply exist_elim
    intro a
    rw' [exist_intro a]

theorem persistently_and [BI PROP] {P Q : PROP} : <pers> (P ∧ Q) ⊣⊢ <pers> P ∧ <pers> Q := by
  apply anti_symm
  case left =>
    apply and_intro
    · rw' [and_elim_l]
    · rw' [and_elim_r]
  case right =>
    exact persistently_and_2

theorem persistently_if {p : Bool} [BI PROP] {P Q : PROP} :
  (<pers> if p then P else Q) ⊣⊢ if p then <pers> P else <pers> Q
:= by
  cases p
  <;> simp

theorem persistently_or [BI PROP] {P Q : PROP} : <pers> (P ∨ Q) ⊣⊢ <pers> P ∨ <pers> Q := by
  rw' [!or_alt, persistently_exist]
  apply anti_symm
  <;> apply exist_elim
  <;> intro a
  <;> rw' [← exist_intro a, persistently_if]

theorem persistently_emp_intro [BI PROP] {P : PROP} : P ⊢ <pers> emp := by
  rw' [← persistently_absorbing (Q := P)]
  conv =>
    lhs
    rw [← (left_id : emp ∗ P ⊣⊢ _)]
  rw' [persistently_emp_2]

theorem persistently_True_emp [BI PROP] : <pers> True ⊣⊢ <pers> (emp : PROP) := by
  apply anti_symm
  case left =>
    exact persistently_emp_intro
  case right =>
    apply persistently_mono
    apply pure_intro
    simp

theorem persistently_True [BI PROP] : True ⊢ <pers> (True : PROP) := by
  rw' [persistently_True_emp, persistently_emp_intro]

theorem persistently_and_emp [BI PROP] {P : PROP} : <pers> P ⊣⊢ <pers> (emp ∧ P) := by
  apply anti_symm
  case left =>
    rw' [persistently_and]
    apply and_intro ?_ reflexivity
    exact persistently_emp_intro
  case right =>
    rw' [and_elim_r]

theorem persistently_and_sep_elim_emp [BI PROP] {P Q : PROP} : <pers> P ∧ Q ⊢ (emp ∧ P) ∗ Q := by
  rw' [persistently_and_emp, persistently_and_sep_elim]

theorem persistently_and_sep_assoc [BI PROP] {P Q R : PROP} : <pers> P ∧ (Q ∗ R) ⊣⊢ (<pers> P ∧ Q) ∗ R := by
  apply anti_symm
  case left =>
    rw' [
      persistently_idemp_2,
      persistently_and_sep_elim_emp,
      (assoc : (emp ∧ <pers> P) ∗ Q ∗ R ⊣⊢ _)]
    apply sep_mono ?_ reflexivity
    apply and_intro
    · rw' [and_elim_r, persistently_absorbing]
    · rw' [and_elim_l, (left_id : emp ∗ Q ⊣⊢ _)]
  case right =>
    apply and_intro
    · rw' [and_elim_l, persistently_absorbing]
    · rw' [and_elim_r]

theorem persistently_and_emp_elim [BI PROP] {P : PROP} : emp ∧ <pers> P ⊢ P := by
  rw' [
    (comm : emp ∧ <pers> P ⊣⊢ _),
    persistently_and_sep_elim_emp,
    (right_id : (emp ∧ P) ∗ emp ⊣⊢ _),
    and_elim_r]

theorem persistently_into_absorbingly [BI PROP] {P : PROP} : <pers> P ⊢ <absorb> P := by
  rw' [
    ← (right_id : <pers> P ∧ True ⊣⊢ _),
    ← (left_id : emp ∗ True ⊣⊢ _),
    persistently_and_sep_assoc,
    (comm : `[iprop| <pers> P ∧ emp] ⊣⊢ _),
    persistently_and_emp_elim,
    (comm : `[iprop| P ∗ True] ⊣⊢ _)]

theorem persistently_elim [BI PROP] {P : PROP} [Absorbing P] : <pers> P ⊢ P := by
  rw' [persistently_into_absorbingly, absorbing]

theorem persistently_idemp [BI PROP] {P : PROP} : <pers> <pers> P ⊣⊢ <pers> P := by
  apply anti_symm
  · rw' [persistently_into_absorbingly, absorbingly_elim_persistently]
  · exact persistently_idemp_2

theorem persistently_pure {φ : Prop} [BI PROP] : <pers> ⌜φ⌝ ⊣⊢ (⌜φ⌝ : PROP) := by
  apply anti_symm
  case left =>
    rw' [persistently_into_absorbingly, absorbingly_pure]
  case right =>
    apply pure_elim'
    intro Hφ
    rw' [persistently_True]
    apply persistently_mono
    apply pure_intro
    exact Hφ

theorem persistently_and_sep_l_1 [BI PROP] {P Q : PROP} : <pers> P ∧ Q ⊢ <pers> P ∗ Q := by
  conv =>
    lhs
    rw [← (left_id : `[iprop| emp ∗ Q] ⊣⊢ _)]
  rw' [persistently_and_sep_assoc, and_elim_l]

theorem persistently_and_sep [BI PROP] {P Q : PROP} : <pers> (P ∧ Q) ⊢ <pers> (P ∗ Q) := by
  rw' [persistently_and, ← persistently_idemp, ← persistently_and]
  conv =>
    lhs
    rw [← (left_id : emp ∗ Q ⊣⊢ _)]
  rw' [
    persistently_and_sep_assoc,
    (comm : <pers> P ∧ emp ⊣⊢ _),
    persistently_and_emp_elim]

theorem persistently_affinely_elim [BI PROP] {P : PROP} : <pers> <affine> P ⊣⊢ <pers> P := by
  simp only [bi_affinely]
  rw' [
    persistently_and,
    ← persistently_True_emp,
    persistently_pure,
    (left_id : True ∧ <pers> P ⊣⊢ _)]

theorem and_sep_persistently [BI PROP] {P Q : PROP} : <pers> P ∧ <pers> Q ⊣⊢ <pers> P ∗ <pers> Q := by
  apply anti_symm
  case left =>
    exact persistently_and_sep_l_1
  case right =>
    apply and_intro
    · exact persistently_absorbing
    · rw' [(comm : `[iprop| <pers> P ∗ <pers> Q ⊣⊢ _]), persistently_absorbing]

theorem persistently_sep_2 [BI PROP] {P Q : PROP} : <pers> P ∗ <pers> Q ⊢ <pers> (P ∗ Q) := by
  rw' [← persistently_and_sep, persistently_and, ← and_sep_persistently]

-- Intuitionistic
theorem intuitionistically_elim [BI PROP] {P : PROP} : □ P ⊢ P := by
  exact persistently_and_emp_elim

theorem intuitionistically_emp [BI PROP] : □ emp ⊣⊢ (emp : PROP) := by
  simp only [bi_intuitionistically]
  rw' [
    ← persistently_True_emp,
    persistently_pure,
    affinely_True_emp,
    affinely_emp]

theorem intuitionistically_True_emp [BI PROP] : □ True ⊣⊢ (emp : PROP) := by
  rw' [← intuitionistically_emp]
  simp only [bi_intuitionistically]
  rw' [persistently_True_emp]

theorem intuitionistically_and [BI PROP] {P Q : PROP} : □ (P ∧ Q) ⊣⊢ □ P ∧ □ Q := by
  simp only [bi_intuitionistically]
  rw' [persistently_and, affinely_and]

theorem intuitionistically_forall [BI PROP] {Φ : α → PROP} : □ (∀ x, Φ x) ⊢ ∀ x, □ Φ x := by
  simp only [bi_intuitionistically]
  rw' [persistently_forall_1, affinely_forall]

theorem intuitionistically_or [BI PROP] {P Q : PROP} : □ (P ∨ Q) ⊣⊢ □ P ∨ □ Q := by
  simp only [bi_intuitionistically]
  rw' [persistently_or, affinely_or]

theorem intuitionistically_exist [BI PROP] {Φ : α → PROP} : □ (∃ x, Φ x) ⊣⊢ ∃ x, □ Φ x := by
  simp only [bi_intuitionistically]
  rw' [persistently_exist, affinely_exist]

theorem intuitionistically_sep_2 [BI PROP] {P Q : PROP} : □ P ∗ □ Q ⊢ □ (P ∗ Q) := by
  rw' [affinely_sep_2, persistently_sep_2]

@[rwMonoRule]
theorem intuitionistically_mono [BI PROP] {P Q : PROP} : (P ⊢ Q) → □ P ⊢ □ Q := by
  intro H
  simp only [bi_intuitionistically]
  rw' [H]

theorem intuitionistically_idemp [BI PROP] {P : PROP} : □ □ P ⊣⊢ □ P := by
  simp only [bi_intuitionistically]
  rw' [persistently_affinely_elim, persistently_idemp]

theorem intuitionistically_into_persistently_1 [BI PROP] {P : PROP} : □ P ⊢ <pers> P := by
  rw' [affinely_elim]

theorem intuitionistically_persistently_elim [BI PROP] {P : PROP} : □ <pers> P ⊣⊢ □ P := by
  simp only [bi_intuitionistically]
  rw' [persistently_idemp]

theorem intuitionistic_intuitionistically [BI PROP] {P : PROP} [Affine P] [Persistent P] : □ P ⊣⊢ P := by
  apply anti_symm
  · exact intuitionistically_elim
  conv =>
    lhs
    rw [← affine_affinely P]
  rw' [persistent]

theorem intuitionistically_affinely [BI PROP] {P : PROP} : □ P ⊢ <affine> P := by
  simp only [bi_intuitionistically, bi_affinely]
  apply and_intro
  · exact and_elim_l
  · exact persistently_and_emp_elim

theorem intuitionistically_affinely_elim [BI PROP] {P : PROP} : □ <affine> P ⊣⊢ □ P := by
  simp only [bi_intuitionistically]
  rw' [persistently_affinely_elim]

theorem persistently_and_intuitionistically_sep_l [BI PROP] {P Q : PROP} : <pers> P ∧ Q ⊣⊢ □ P ∗ Q := by
  apply anti_symm
  case left =>
    simp only [bi_intuitionistically, bi_affinely]
    rw' [
      (comm : emp ∧ <pers> P ⊣⊢ _),
      ← persistently_and_sep_assoc,
      (left_id : emp ∗ Q ⊣⊢ _)]
  case right =>
    apply and_intro
    · rw' [affinely_elim, persistently_absorbing]
    · rw' [affinely_elim_emp, (left_id : emp ∗ Q ⊣⊢ _)]

theorem persistently_and_intuitionistically_sep_r [BI PROP] {P Q : PROP} : P ∧ <pers> Q ⊣⊢ P ∗ □ Q := by
  rw' [(comm : P ∧ _ ⊣⊢ _), (comm : P ∗ _ ⊣⊢ _)]
  exact persistently_and_intuitionistically_sep_l

theorem and_sep_intuitionistically [BI PROP] {P Q : PROP} : □ P ∧ □ Q ⊣⊢ □ P ∗ □ Q := by
  rw' [← persistently_and_intuitionistically_sep_l]
  simp only [bi_intuitionistically]
  rw'[← affinely_and, affinely_and_r]

theorem intuitionistically_sep_dup [BI PROP] {P : PROP} : □ P ⊣⊢ □ P ∗ □ P := by
  rw' [← persistently_and_intuitionistically_sep_l]
  simp only [bi_intuitionistically]
  rw' [
    affinely_and_r,
    (idemp : _ ∧ _ ⊣⊢ _)]

-- Intuitionistic BIAffine
theorem intuitionistically_into_persistently [BIAffine PROP] {P : PROP} : □ P ⊣⊢ <pers> P := by
  exact affine_affinely _

-- Conditional Affine
@[rwMonoRule]
theorem affinely_if_mono {p : Bool} [BI PROP] {P Q : PROP} : (P ⊢ Q) → <affine>?p P ⊢ <affine>?p Q := by
  intro H
  cases p
  <;> simp [bi_affinely_if, H]
  revert H
  exact affinely_mono

theorem affinely_if_flag_mono {p q : Bool} [BI PROP] {P : PROP} : (q → p) → <affine>?p P ⊢ <affine>?q P := by
  cases p
  <;> cases q
  <;> simp [bi_affinely_if, affinely_elim]

theorem affinely_if_elim {p : Bool} [BI PROP] {P : PROP} : <affine>?p P ⊢ P := by
  cases p
  <;> simp [bi_affinely_if, affinely_elim]

theorem affinely_affinely_if {p : Bool} [BI PROP] {P : PROP} : <affine> P ⊢ <affine>?p P := by
  cases p
  <;> simp [bi_affinely_if, affinely_elim]

theorem affinely_if_and {p : Bool} [BI PROP] {P Q : PROP} : <affine>?p (P ∧ Q) ⊣⊢ <affine>?p P ∧ <affine>?p Q := by
  cases p
  <;> simp [bi_affinely_if, affinely_and]

theorem affinely_if_or {p : Bool} [BI PROP] {P Q : PROP} : <affine>?p (P ∨ Q) ⊣⊢ <affine>?p P ∨ <affine>?p Q := by
  cases p
  <;> simp [bi_affinely_if, affinely_or]

theorem affinely_if_exist {p : Bool} [BI PROP] {Ψ : α → PROP} : <affine>?p (∃ a, Ψ a) ⊣⊢ ∃ a, <affine>?p (Ψ a) := by
  cases p
  <;> simp [bi_affinely_if, affinely_exist]

theorem affinely_if_intro_false [BI PROP] (P : PROP) : P ⊣⊢ <affine>?false P := by
  simp [bi_affinely_if]

theorem affinely_if_intro_true [BI PROP] (P : PROP) : <affine> P ⊣⊢ <affine>?true P := by
  simp [bi_affinely_if]

-- Conditional Absorbing
@[rwMonoRule]
theorem absorbingly_if_mono {p : Bool} [BI PROP] {P Q : PROP} : (P ⊢ Q) → <absorb>?p P ⊢ <absorb>?p Q := by
  intro H
  cases p
  <;> simp [bi_absorbingly_if, H]
  revert H
  exact absorbingly_mono

-- Conditional Persistent
@[rwMonoRule]
theorem persistently_if_mono {p : Bool} [BI PROP] {P Q : PROP} : (P ⊢ Q) → <pers>?p P ⊢ <pers>?p Q := by
  intro H
  cases p
  <;> simp [bi_persistently_if, H]
  revert H
  exact persistently_mono

theorem persistently_if_intro_false [BI PROP] (P : PROP) : P ⊣⊢ <pers>?false P := by
  simp [bi_persistently_if]

theorem persistently_if_intro_true [BI PROP] (P : PROP) : <pers> P ⊣⊢ <pers>?true P := by
  simp [bi_persistently_if]

-- Conditional Intuitionistic
@[rwMonoRule]
theorem intuitionistically_if_mono {p : Bool} [BI PROP] {P Q : PROP} : (P ⊢ Q) → □?p P ⊢ □?p Q := by
  intro H
  cases p
  <;> simp [bi_intuitionistically_if, H]
  revert H
  exact intuitionistically_mono

theorem intuitionistically_if_elim {p : Bool} [BI PROP] {P : PROP} : □?p P ⊢ P := by
  cases p
  <;> simp [bi_intuitionistically_if, intuitionistically_elim]

theorem intuitionistically_intuitionistically_if (p : Bool) [BI PROP] {P : PROP} : □ P ⊢ □?p P := by
  cases p
  <;> simp [bi_intuitionistically_if]
  · exact intuitionistically_elim

theorem intuitionistically_if_and {p : Bool} [BI PROP] {P Q : PROP} : □?p (P ∧ Q) ⊣⊢ □?p P ∧ □?p Q := by
  cases p
  <;> simp [bi_intuitionistically_if, intuitionistically_and]

theorem intuitionistically_if_or (p : Bool) [BI PROP] {P Q : PROP} : □?p (P ∨ Q) ⊣⊢ □?p P ∨ □?p Q := by
  cases p
  <;> simp [bi_intuitionistically_if]
  rw' [intuitionistically_or]

theorem intuitionistically_if_exist {p : Bool} [BI PROP] {Ψ : α → PROP} : (□?p ∃ a, Ψ a) ⊣⊢ ∃ a, □?p Ψ a := by
  cases p
  <;> simp [bi_intuitionistically_if, intuitionistically_exist]

theorem intuitionistically_if_sep_2 {p : Bool} [BI PROP] {P Q : PROP} : □?p P ∗ □?p Q ⊢ □?p (P ∗ Q) := by
  cases p
  <;> simp [bi_intuitionistically_if]
  · exact intuitionistically_sep_2

theorem intuitionistically_if_idemp {p : Bool} [BI PROP] {P : PROP} : □?p □?p P ⊣⊢ □?p P := by
  cases p
  <;> simp [bi_intuitionistically_if]
  · exact intuitionistically_idemp

theorem intuitionistically_if_intro_true [BI PROP] (P : PROP) : □ P ⊣⊢ □?true P := by
  simp [bi_intuitionistically_if]

-- Persistent Propositions
theorem persistent_persistently_2 [BI PROP] {P : PROP} [Persistent P] : P ⊢ <pers> P := by
  rw' [persistent]

theorem persistent_and_affinely_sep_l_1 [BI PROP] {P Q : PROP} [Persistent P] : P ∧ Q ⊢ <affine> P ∗ Q := by
  rw' [
    persistent_persistently_2,
    persistently_and_intuitionistically_sep_l,
    intuitionistically_affinely]

theorem persistent_and_affinely_sep_r_1 [BI PROP] {P Q : PROP} [Persistent Q] : P ∧ Q ⊢ P ∗ <affine> Q := by
  rw' [
    (comm : P ∧ Q ⊣⊢ _),
    ← (comm : _ ⊣⊢ P ∗ <affine> Q),
    persistent_and_affinely_sep_l_1]

theorem persistent_and_affinely_sep_l [BI PROP] {P Q : PROP} [Persistent P] [Absorbing P] :
  P ∧ Q ⊣⊢ <affine> P ∗ Q
:= by
  apply anti_symm
  <;> rw' [persistent, ← persistently_elim, persistently_and_intuitionistically_sep_l]

theorem persistent_and_affinely_sep_r [BI PROP] {P Q : PROP} [Persistent Q] [Absorbing Q] :
  P ∧ Q ⊣⊢ P ∗ <affine> Q
:= by
  apply anti_symm
  case left =>
    rw' [
      persistent,
      ← persistently_elim,
      persistently_and_intuitionistically_sep_r]
  case right =>
    rw' [
      persistent,
      ← persistently_elim,
      persistently_and_intuitionistically_sep_r]

theorem persistent_and_sep_1 [BI PROP] {P Q : PROP} [inst : TCOr (Persistent P) (Persistent Q)] :
  P ∧ Q ⊢ P ∗ Q
:= by
  cases inst
  · rw' [persistent_and_affinely_sep_l_1, affinely_elim]
  · rw' [persistent_and_affinely_sep_r_1, affinely_elim]

theorem absorbingly_intuitionistically_into_persistently [BI PROP] {P : PROP} :
  <absorb> □ P ⊣⊢ <pers> P
:= by
  apply anti_symm
  case left =>
    rw' [
      intuitionistically_into_persistently_1,
      absorbingly_elim_persistently]
  case right =>
    rw' [
      ← (idemp : <pers> P ∧ _ ⊣⊢ _),
      persistently_and_intuitionistically_sep_r,
      (True_intro : <pers> P ⊢ _)]

theorem persistent_absorbingly_affinely_2 [BI PROP] {P : PROP} [Persistent P] : P ⊢ <absorb> <affine> P := by
  rw' [
    persistent,
    ← absorbingly_intuitionistically_into_persistently,
    intuitionistically_affinely]

-- Big Op
theorem big_op_sep_nil [BI PROP] : [∗] `[term| []] ⊣⊢ (emp : PROP) := by
  simp only [big_op]

theorem big_op_and_nil [BI PROP] : [∧] `[term| []] ⊣⊢ (True : PROP) := by
  simp only [big_op]

theorem big_op_sep_cons [BI PROP] {P : PROP} {Ps : List PROP} : [∗] `[term| P :: Ps] ⊣⊢ P ∗ [∗] `[term| Ps] := by
  cases Ps
  <;> simp only [big_op]
  rw' [(right_id : _ ∗ emp ⊣⊢ _)]

theorem big_op_and_cons [BI PROP] {P : PROP} {Ps : List PROP} : [∧] `[term| P :: Ps] ⊣⊢ P ∧ [∧] `[term| Ps] := by
  cases Ps
  <;> simp only [big_op]
  rw' [(right_id : _ ∧ True ⊣⊢ _)]

end Iris.BI


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

! This file was ported from Lean 3 source module data.int.absolute_value
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Module.Basic
import Mathbin.Algebra.Order.AbsoluteValue
import Mathbin.Data.Int.Cast.Lemmas
import Mathbin.Data.Int.Units
import Mathbin.GroupTheory.GroupAction.Units

/-!
# Absolute values and the integers

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

This file contains some results on absolute values applied to integers.

## Main results

 * `absolute_value.map_units_int`: an absolute value sends all units of `ℤ` to `1`
 * `int.nat_abs_hom`: `int.nat_abs` bundled as a `monoid_with_zero_hom`
-/

variable {R S : Type _} [Ring R] [LinearOrderedCommRing S]

/- warning: absolute_value.map_units_int -> AbsoluteValue.map_units_int is a dubious translation:
lean 3 declaration is
  forall {S : Type.{u1}} [_inst_2 : LinearOrderedCommRing.{u1} S] (abv : AbsoluteValue.{0, u1} Int S Int.semiring (StrictOrderedSemiring.toOrderedSemiring.{u1} S (StrictOrderedRing.toStrictOrderedSemiring.{u1} S (LinearOrderedRing.toStrictOrderedRing.{u1} S (LinearOrderedCommRing.toLinearOrderedRing.{u1} S _inst_2))))) (x : Units.{0} Int Int.monoid), Eq.{succ u1} S (coeFn.{succ u1, succ u1} (AbsoluteValue.{0, u1} Int S Int.semiring (StrictOrderedSemiring.toOrderedSemiring.{u1} S (StrictOrderedRing.toStrictOrderedSemiring.{u1} S (LinearOrderedRing.toStrictOrderedRing.{u1} S (LinearOrderedCommRing.toLinearOrderedRing.{u1} S _inst_2))))) (fun (f : AbsoluteValue.{0, u1} Int S Int.semiring (StrictOrderedSemiring.toOrderedSemiring.{u1} S (StrictOrderedRing.toStrictOrderedSemiring.{u1} S (LinearOrderedRing.toStrictOrderedRing.{u1} S (LinearOrderedCommRing.toLinearOrderedRing.{u1} S _inst_2))))) => Int -> S) (AbsoluteValue.hasCoeToFun.{0, u1} Int S Int.semiring (StrictOrderedSemiring.toOrderedSemiring.{u1} S (StrictOrderedRing.toStrictOrderedSemiring.{u1} S (LinearOrderedRing.toStrictOrderedRing.{u1} S (LinearOrderedCommRing.toLinearOrderedRing.{u1} S _inst_2))))) abv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Units.{0} Int Int.monoid) Int (HasLiftT.mk.{1, 1} (Units.{0} Int Int.monoid) Int (CoeTCₓ.coe.{1, 1} (Units.{0} Int Int.monoid) Int (coeBase.{1, 1} (Units.{0} Int Int.monoid) Int (Units.hasCoe.{0} Int Int.monoid)))) x)) (OfNat.ofNat.{u1} S 1 (OfNat.mk.{u1} S 1 (One.one.{u1} S (AddMonoidWithOne.toOne.{u1} S (AddGroupWithOne.toAddMonoidWithOne.{u1} S (AddCommGroupWithOne.toAddGroupWithOne.{u1} S (Ring.toAddCommGroupWithOne.{u1} S (StrictOrderedRing.toRing.{u1} S (LinearOrderedRing.toStrictOrderedRing.{u1} S (LinearOrderedCommRing.toLinearOrderedRing.{u1} S _inst_2))))))))))
but is expected to have type
  forall {S : Type.{u1}} [_inst_2 : LinearOrderedCommRing.{u1} S] (abv : AbsoluteValue.{0, u1} Int S Int.instSemiringInt (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) (x : Units.{0} Int Int.instMonoidInt), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (FunLike.coe.{succ u1, 1, succ u1} (AbsoluteValue.{0, u1} Int S Int.instSemiringInt (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) Int (fun (f : Int) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) f) (SubadditiveHomClass.toFunLike.{u1, 0, u1} (AbsoluteValue.{0, u1} Int S Int.instSemiringInt (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) Int S (Distrib.toAdd.{0} Int (NonUnitalNonAssocSemiring.toDistrib.{0} Int (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Int (Semiring.toNonAssocSemiring.{0} Int Int.instSemiringInt)))) (Distrib.toAdd.{u1} S (NonUnitalNonAssocSemiring.toDistrib.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (OrderedSemiring.toSemiring.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))))) (Preorder.toLE.{u1} S (PartialOrder.toPreorder.{u1} S (OrderedSemiring.toPartialOrder.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))) (AbsoluteValue.subadditiveHomClass.{0, u1} Int S Int.instSemiringInt (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2)))))) abv (Units.val.{0} Int Int.instMonoidInt x)) (OfNat.ofNat.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) 1 (One.toOfNat1.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (NonAssocRing.toOne.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (Ring.toNonAssocRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (StrictOrderedRing.toRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (LinearOrderedRing.toStrictOrderedRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) (LinearOrderedCommRing.toLinearOrderedRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : Int) => S) (Units.val.{0} Int Int.instMonoidInt x)) _inst_2)))))))
Case conversion may be inaccurate. Consider using '#align absolute_value.map_units_int AbsoluteValue.map_units_intₓ'. -/
@[simp]
theorem AbsoluteValue.map_units_int (abv : AbsoluteValue ℤ S) (x : ℤˣ) : abv x = 1 := by
  rcases Int.units_eq_one_or x with (rfl | rfl) <;> simp
#align absolute_value.map_units_int AbsoluteValue.map_units_int

/- warning: absolute_value.map_units_int_cast -> AbsoluteValue.map_units_int_cast is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} [_inst_1 : Ring.{u1} R] [_inst_2 : LinearOrderedCommRing.{u2} S] [_inst_3 : Nontrivial.{u1} R] (abv : AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) (x : Units.{0} Int Int.monoid), Eq.{succ u2} S (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) (fun (f : AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) => R -> S) (AbsoluteValue.hasCoeToFun.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) abv ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Int R (HasLiftT.mk.{1, succ u1} Int R (CoeTCₓ.coe.{1, succ u1} Int R (Int.castCoe.{u1} R (AddGroupWithOne.toHasIntCast.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Units.{0} Int Int.monoid) Int (HasLiftT.mk.{1, 1} (Units.{0} Int Int.monoid) Int (CoeTCₓ.coe.{1, 1} (Units.{0} Int Int.monoid) Int (coeBase.{1, 1} (Units.{0} Int Int.monoid) Int (Units.hasCoe.{0} Int Int.monoid)))) x))) (OfNat.ofNat.{u2} S 1 (OfNat.mk.{u2} S 1 (One.one.{u2} S (AddMonoidWithOne.toOne.{u2} S (AddGroupWithOne.toAddMonoidWithOne.{u2} S (AddCommGroupWithOne.toAddGroupWithOne.{u2} S (Ring.toAddCommGroupWithOne.{u2} S (StrictOrderedRing.toRing.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))))))))
but is expected to have type
  forall {R : Type.{u2}} {S : Type.{u1}} [_inst_1 : Ring.{u2} R] [_inst_2 : LinearOrderedCommRing.{u1} S] [_inst_3 : Nontrivial.{u2} R] (abv : AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) (x : Units.{0} Int Int.instMonoidInt), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R (fun (f : R) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) f) (SubadditiveHomClass.toFunLike.{max u2 u1, u2, u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R S (Distrib.toAdd.{u2} R (NonUnitalNonAssocSemiring.toDistrib.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R (Ring.toSemiring.{u2} R _inst_1))))) (Distrib.toAdd.{u1} S (NonUnitalNonAssocSemiring.toDistrib.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (OrderedSemiring.toSemiring.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))))) (Preorder.toLE.{u1} S (PartialOrder.toPreorder.{u1} S (OrderedSemiring.toPartialOrder.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))) (AbsoluteValue.subadditiveHomClass.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2)))))) abv (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (OfNat.ofNat.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) 1 (One.toOfNat1.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (NonAssocRing.toOne.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (Ring.toNonAssocRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (StrictOrderedRing.toRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (LinearOrderedRing.toStrictOrderedRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) (LinearOrderedCommRing.toLinearOrderedRing.{u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (Int.cast.{u2} R (Ring.toIntCast.{u2} R _inst_1) (Units.val.{0} Int Int.instMonoidInt x))) _inst_2)))))))
Case conversion may be inaccurate. Consider using '#align absolute_value.map_units_int_cast AbsoluteValue.map_units_int_castₓ'. -/
@[simp]
theorem AbsoluteValue.map_units_int_cast [Nontrivial R] (abv : AbsoluteValue R S) (x : ℤˣ) :
    abv ((x : ℤ) : R) = 1 := by rcases Int.units_eq_one_or x with (rfl | rfl) <;> simp
#align absolute_value.map_units_int_cast AbsoluteValue.map_units_int_cast

/- warning: absolute_value.map_units_int_smul -> AbsoluteValue.map_units_int_smul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} [_inst_1 : Ring.{u1} R] [_inst_2 : LinearOrderedCommRing.{u2} S] (abv : AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) (x : Units.{0} Int Int.monoid) (y : R), Eq.{succ u2} S (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) (fun (f : AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) => R -> S) (AbsoluteValue.hasCoeToFun.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) abv (SMul.smul.{0, u1} (Units.{0} Int Int.monoid) R (Units.hasSmul.{0, u1} Int R Int.monoid (SubNegMonoid.SMulInt.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddGroupWithOne.toAddGroup.{u1} R (AddCommGroupWithOne.toAddGroupWithOne.{u1} R (Ring.toAddCommGroupWithOne.{u1} R _inst_1)))))) x y)) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) (fun (f : AbsoluteValue.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) => R -> S) (AbsoluteValue.hasCoeToFun.{u1, u2} R S (Ring.toSemiring.{u1} R _inst_1) (StrictOrderedSemiring.toOrderedSemiring.{u2} S (StrictOrderedRing.toStrictOrderedSemiring.{u2} S (LinearOrderedRing.toStrictOrderedRing.{u2} S (LinearOrderedCommRing.toLinearOrderedRing.{u2} S _inst_2))))) abv y)
but is expected to have type
  forall {R : Type.{u2}} {S : Type.{u1}} [_inst_1 : Ring.{u2} R] [_inst_2 : LinearOrderedCommRing.{u1} S] (abv : AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) (x : Units.{0} Int Int.instMonoidInt) (y : R), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) (HSMul.hSMul.{0, u2, u2} (Units.{0} Int Int.instMonoidInt) R R (instHSMul.{0, u2} (Units.{0} Int Int.instMonoidInt) R (Units.instSMulUnits.{0, u2} Int R Int.instMonoidInt (SubNegMonoid.SMulInt.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddGroupWithOne.toAddGroup.{u2} R (Ring.toAddGroupWithOne.{u2} R _inst_1)))))) x y)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R (fun (f : R) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) f) (SubadditiveHomClass.toFunLike.{max u2 u1, u2, u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R S (Distrib.toAdd.{u2} R (NonUnitalNonAssocSemiring.toDistrib.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R (Ring.toSemiring.{u2} R _inst_1))))) (Distrib.toAdd.{u1} S (NonUnitalNonAssocSemiring.toDistrib.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (OrderedSemiring.toSemiring.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))))) (Preorder.toLE.{u1} S (PartialOrder.toPreorder.{u1} S (OrderedSemiring.toPartialOrder.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))) (AbsoluteValue.subadditiveHomClass.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2)))))) abv (HSMul.hSMul.{0, u2, u2} (Units.{0} Int Int.instMonoidInt) R R (instHSMul.{0, u2} (Units.{0} Int Int.instMonoidInt) R (Units.instSMulUnits.{0, u2} Int R Int.instMonoidInt (SubNegMonoid.SMulInt.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddGroupWithOne.toAddGroup.{u2} R (Ring.toAddGroupWithOne.{u2} R _inst_1)))))) x y)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R (fun (f : R) => (fun (x._@.Mathlib.Algebra.Order.Hom.Basic._hyg.99 : R) => S) f) (SubadditiveHomClass.toFunLike.{max u2 u1, u2, u1} (AbsoluteValue.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))) R S (Distrib.toAdd.{u2} R (NonUnitalNonAssocSemiring.toDistrib.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R (Semiring.toNonAssocSemiring.{u2} R (Ring.toSemiring.{u2} R _inst_1))))) (Distrib.toAdd.{u1} S (NonUnitalNonAssocSemiring.toDistrib.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (OrderedSemiring.toSemiring.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))))) (Preorder.toLE.{u1} S (PartialOrder.toPreorder.{u1} S (OrderedSemiring.toPartialOrder.{u1} S (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2))))))) (AbsoluteValue.subadditiveHomClass.{u2, u1} R S (Ring.toSemiring.{u2} R _inst_1) (OrderedCommSemiring.toOrderedSemiring.{u1} S (StrictOrderedCommSemiring.toOrderedCommSemiring.{u1} S (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u1} S (LinearOrderedCommRing.toLinearOrderedCommSemiring.{u1} S _inst_2)))))) abv y)
Case conversion may be inaccurate. Consider using '#align absolute_value.map_units_int_smul AbsoluteValue.map_units_int_smulₓ'. -/
@[simp]
theorem AbsoluteValue.map_units_int_smul (abv : AbsoluteValue R S) (x : ℤˣ) (y : R) :
    abv (x • y) = abv y := by rcases Int.units_eq_one_or x with (rfl | rfl) <;> simp
#align absolute_value.map_units_int_smul AbsoluteValue.map_units_int_smul

/- warning: int.nat_abs_hom -> Int.natAbsHom is a dubious translation:
lean 3 declaration is
  MonoidWithZeroHom.{0, 0} Int Nat (NonAssocSemiring.toMulZeroOneClass.{0} Int (NonAssocRing.toNonAssocSemiring.{0} Int (Ring.toNonAssocRing.{0} Int Int.ring))) (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))
but is expected to have type
  MonoidWithZeroHom.{0, 0} Int Nat (NonAssocSemiring.toMulZeroOneClass.{0} Int (NonAssocRing.toNonAssocSemiring.{0} Int (Ring.toNonAssocRing.{0} Int Int.instRingInt))) (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))
Case conversion may be inaccurate. Consider using '#align int.nat_abs_hom Int.natAbsHomₓ'. -/
/-- `int.nat_abs` as a bundled monoid with zero hom. -/
@[simps]
def Int.natAbsHom : ℤ →*₀ ℕ where
  toFun := Int.natAbs
  map_mul' := Int.natAbs_mul
  map_one' := Int.natAbs_one
  map_zero' := Int.natAbs_zero
#align int.nat_abs_hom Int.natAbsHom


import Mathlib.Algebra.Group.Defs

section

  def Array.cons {α} (x : α) (a : Array α) : Array α := mk (x :: a.data)

  infixr:67 " ::# " => Array.cons

  theorem Array.recursion {α}
  {motive : Array α → Sort v}
  (h0 : motive #[])
  (ih : (a : Array α) → (x : α) → motive a → motive (x ::# a))
  (a : Array α)
  : motive a :=
  let motive' : List α → Sort v := λ l => motive (mk l)
  show motive' a.data from
  List.rec h0 (λ x l h => ih (mk l) x h) a.data

  @[simp]
  theorem Array.forIn_cons {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m]
  (f : α → β → m (ForInStep β))
  (a : α) (as : Array α) (b : β)
  : (forIn (a ::# as) b f)
      = (do match (<- f a b) with
        | ForInStep.done b => pure b
        | ForInStep.yield b => forIn as b f) := by
    show (do match (<- f ((a ::# as).get ⟨as.size - as.size, (as.size.sub_self ▸ as.size.zero_lt_succ) ⟩) b) with
            | ForInStep.done b => pure b
            | ForInStep.yield b => forIn.loop (a ::# as) f as.size as.size.le_succ b)
      =  (do match (<- f a b) with
            | ForInStep.done b => pure b
            | ForInStep.yield b => forIn as b f : m β)
    suffices ((a ::# as).get ⟨as.size - as.size, (as.size.sub_self ▸ as.size.zero_lt_succ) ⟩) = a from by
      rw [this]
      apply congrArg; apply funext; intro r
      apply congrArg; apply funext; exact forIn_loop_cons_eq
    simp; rfl
  where
    forIn_loop_cons_eq : ∀ b : β, (forIn.loop (a ::# as) f as.size (Nat.le_succ as.size) b)
      = forIn.loop as f as.size (Nat.le_refl _) b :=
        forIn_loop_cons_eq_lemma as.size (Nat.le_refl _)
    forIn_loop_cons_eq_lemma (i : Nat) (hi : i ≤ as.size)
    : ∀ b : β, (forIn.loop (a ::# as) f i (Nat.le_trans hi as.size.le_succ) b)
      = forIn.loop as f i hi b := by induction i with
    | zero => intro b; rfl
    | succ i ih =>
      intro b
      have ih := ih (Nat.le_trans i.le_succ hi)
      show  (do match (<- f ((a ::# as).get ⟨(a ::# as).size - 1 - i, _⟩) b) with
            | ForInStep.done b => pure b
            | ForInStep.yield b => forIn.loop (a ::# as) f i _ b : m β) =
            (do match (<- f (as.get ⟨as.size - 1 - i, _⟩) b) with
            | ForInStep.done b => pure b
            | ForInStep.yield b => forIn.loop as f i _ b : m β)
      suffices (f ((a ::# as).get ⟨(a ::# as).size - 1 - i, _⟩) b)
          = (f (as.get ⟨as.size - 1 - i, _⟩) b) from by
        rw [this]
        apply congrArg; apply funext; intro r
        apply congrArg; apply funext; exact ih
      apply congrArg (f . _)
      have : as.size - 1 - i + 1 < as.size + 1 := by
        cases h : as.size with
        | zero => rw [h] at hi; contradiction
        | succ k => exact Nat.succ_lt_succ (Nat.succ_le_succ (Nat.sub_le k i))
      show (a ::# as).get _ = (a ::# as).get ⟨_, this⟩
      apply congrArg
      suffices (a ::# as).size - 1 - i = as.size - 1 - i + 1 from by simp [this]
      show as.size + 1 - 1 - i = as.size - 1 - i + 1
      rw [Nat.add_comm, Nat.add_sub_assoc, Nat.add_sub_assoc, Nat.add_comm]
      . exact Nat.le_pred_of_lt hi
      . exact Nat.le_trans i.zero_lt_succ hi

end

section
  class Cons (ρ : Type u) (α : outParam (Type v)) where
    cons : α → ρ → ρ

  class ForIn_Cons (m : Type u₁ → Type u₂) (ρ : Type u) (α : Type v)
    [Monad m] [Cons ρ α] [ForIn m ρ α]
    where
    forIn_cons {β} (f : α → β → m (ForInStep β)) (a : α) (as : ρ) (b : β)
      : forIn (Cons.cons a as) b f
      = f a b >>= λ | ForInStep.done b => pure b | ForInStep.yield b => forIn as b f

  instance {α} : Cons (List α) α where
    cons := List.cons

  instance {α} : Cons (Array α) α where
    cons := Array.cons

  instance {α} {m} [Monad m] : ForIn_Cons m (List α) α where
    forIn_cons := List.forIn_cons

  instance {α} {m} [Monad m] : ForIn_Cons m (Array α) α where
    forIn_cons := Array.forIn_cons

end

section
  variable {α : Type u} [AddCommSemigroup α]

  namespace List

  def sum (l : List α) (x₀ : α) : α :=
    match l with
    | [] => x₀
    | x :: l => l.sum x₀ + x

  def iter_sum (l : List α) (x₀ : α) : α :=
    Id.run do
      let mut s := x₀
      for x in l do
        s := s + x
      return s

  theorem iter_sum_cons_add (l : List α)
  : ∀ x₀ x : α, (x :: l).iter_sum x₀ = l.iter_sum x₀ + x := by
    induction l with
    | nil => exact λ _ _ => rfl
    | cons y l hl =>
      intro x₀ x
      show l.iter_sum (x₀ + x + y) = l.iter_sum (x₀ + y) + x
      rw [←hl]
      show l.iter_sum (x₀ + x + y) = l.iter_sum (x₀ + y + x)
      apply congrArg l.iter_sum
      rw [add_assoc, add_assoc, add_comm x y]

  theorem iter_sum_eq_sum (l : List α) (x₀ : α)
    : l.iter_sum x₀ = l.sum x₀ :=
    match l with
    | [] => rfl
    | x :: l => l.iter_sum_cons_add x₀ x ▸ l.iter_sum_eq_sum x₀ ▸ rfl

  end List

  namespace Array

  def iter_sum (a : Array α) (x₀ : α) : α :=
    Id.run do
      let mut s := x₀
      for x in a do
        s := s + x
      return s

  theorem iter_sum_cons_add
  : ∀ a : Array α, ∀ x₀ x, (x ::# a).iter_sum x₀ = a.iter_sum x₀ + x :=
    recursion (λ _ _ => rfl) <| by
    intro a y ha
    intro x₀ x
    simp [iter_sum]; show a.iter_sum (x₀ + x + y) = a.iter_sum (x₀ + y) + x
    rw [←ha]
    simp [iter_sum]; show a.iter_sum (x₀ + x + y) = a.iter_sum (x₀ + y + x)
    apply congrArg a.iter_sum
    rw [add_assoc, add_assoc, add_comm x y]

  end Array

  section
  variable {α : Type _} [Add α]
  variable {ρ} [∀ m, ForIn m ρ α] [Cons ρ α] [∀ m, [Monad m] → ForIn_Cons m ρ α]
  def iter_sum (a : ρ) (x₀ : α) : α :=
    Id.run do
      let mut s := x₀
      for x in a do
        s := s + x
      return s
  end

end


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Eric Weiser
-/
import tactic.doc_commands

/-!
# Documentation of the algebraic hierarchy

A library note giving advice on modifying the algebraic hierarchy.
(It is not intended as a "tour".)

TODO: Add sections about interactions with topological typeclasses, and order typeclasses.

-/

/--
# The algebraic hierarchy

In any theorem proving environment,
there are difficult decisions surrounding the design of the "algebraic hierarchy".

There is a danger of exponential explosion in the number of gadgets,
especially once interactions between algebraic and order/topological/etc structures are considered.

In mathlib, we try to avoid this by only introducing new algebraic typeclasses either
1. when there is "real mathematics" to be done with them, or
2. when there is a meaninful gain in simplicity by factoring out a common substructure.

(As examples, at this point we don't have `loop`, or `unital_magma`,
but we do have `lie_submodule` and `topological_field`!
We also have `group_with_zero`, as an exemplar of point 2.)

Generally in mathlib we use the extension mechanism (so `comm_ring` extends `ring`)
rather than mixins (e.g. with separate `ring` and `comm_mul` classes),
in part because of the potential blow-up in term sizes described at
https://www.ralfj.de/blog/2019/05/15/typeclasses-exponential-blowup.html
However there is tension here, as it results in considerable duplication in the API,
particularly in the interaction with order structures.

This library note is not intended as a design document
justifying and explaining the history of mathlib's algebraic hierarchy!
Instead it is intended as a developer's guide, for contributors wanting to extend
(either new leaves, or new intermediate classes) the algebraic hierarchy as it exists.

(Ideally we would have both a tour guide to the existing hierarchy,
and an account of the design choices.
See https://arxiv.org/abs/1910.09336 for an overview of mathlib as a whole,
with some attention to the algebraic hierarchy and
https://leanprover-community.github.io/mathlib-overview.html
for a summary of what is in mathlib today.)

## Instances

When adding a new typeclass `Z` to the algebraic hierarchy
one should attempt to add the following constructions and results,
when applicable:

* Instances transferred elementwise to products, like `prod.monoid`.
  See `algebra.group.prod` for more examples.
  ```
  instance prod.Z [Z M] [Z N] : Z (M × N) := ...
  ```
* Instances transferred elementwise to pi types, like `pi.monoid`.
  See `algebra.group.pi` for more examples.
  ```
  instance pi.Z [∀ i, Z $ f i] : Z (Π i : I, f i) := ...
  ```
* Instances transferred to `mul_opposite M`, like `mul_opposite.monoid`.
  See `algebra.opposites` for more examples.
  ```
  instance mul_opposite.Z [Z M] : Z (mul_opposite M) := ...
  ```
* Instances transferred to `ulift M`, like `ulift.monoid`.
  See `algebra.group.ulift` for more examples.
  ```
  instance ulift.Z [Z M] : Z (ulift M) := ...
  ```
* Definitions for transferring the proof fields of instances along
  injective or surjective functions that agree on the data fields,
  like `function.injective.monoid` and `function.surjective.monoid`.
  We make these definitions `@[reducible]`, see note [reducible non-instances].
  See `algebra.group.inj_surj` for more examples.
  ```
  @[reducible]
  def function.injective.Z [Z M₂] (f : M₁ → M₂) (hf : injective f)
    (one : f 1 = 1) (mul : ∀ x y, f (x * y) = f x * f y) : Z M₁ := ...

  @[reducible]
  def function.surjective.Z [Z M₁] (f : M₁ → M₂) (hf : surjective f)
    (one : f 1 = 1) (mul : ∀ x y, f (x * y) = f x * f y) : Z M₂ := ...
  ```
* Instances transferred elementwise to `finsupp`s, like `finsupp.semigroup`.
  See `data.finsupp.pointwise` for more examples.
  ```
  instance finsupp.Z [Z β] : Z (α →₀ β) := ...
  ```
* Instances transferred elementwise to `set`s, like `set.monoid`.
  See `algebra.pointwise` for more examples.
  ```
  instance set.Z [Z α] : Z (set α) := ...
  ```
* Definitions for transferring the entire structure across an equivalence, like `equiv.monoid`.
  See `data.equiv.transfer_instance` for more examples. See also the `transport` tactic.
  ```
  def equiv.Z (e : α ≃ β) [Z β] : Z α := ...
  /- When there is a new notion of `Z`-equiv: -/
  def equiv.Z_equiv (e : α ≃ β) [Z β] : by { letI := equiv.Z e, exact α ≃Z β } := ...
  ```

## Subobjects

When a new typeclass `Z` adds new data fields,
you should also create a new `sub_Z` `structure` with a `carrier` field.

This can be a lot of work; for now try to closely follow the existing examples
(e.g. `submonoid`, `subring`, `subalgebra`).
We would very much like to provide some automation here, but a prerequisite will be making
all the existing APIs more uniform.

If `Z` extends `Y`, then `sub_Z` should usually extend `sub_Y`.

When `Z` adds only new proof fields to an existing structure `Y`,
you should provide instances transferring
`Z α` to `Z (sub_Y α)`, like `submonoid.to_comm_monoid`.
Typically this is done using the `function.injective.Z` definition mentioned above.
```
instance sub_Y.to_Z [Z α] : Z (sub_Y α) :=
coe_injective.Z coe ...
```

## Morphisms and equivalences

## Category theory

For many algebraic structures, particularly ones used in representation theory, algebraic geometry,
etc., we also define "bundled" versions, which carry `category` instances.

These bundled versions are usually named in camel case,
so for example we have `AddCommGroup` as a bundled `add_comm_group`,
and `TopCommRing` (which bundles together `comm_ring`, `topological_space`, and `topological_ring`).

These bundled versions have many appealing features:
* a uniform notation for morphisms `X ⟶ Y`
* a uniform notation (and definition) for isomorphisms `X ≅ Y`
* a uniform API for subobjects, via the partial order `subobject X`
* interoperability with unbundled structures, via coercions to `Type`
  (so if `G : AddCommGroup`, you can treat `G` as a type,
  and it automatically has an `add_comm_group` instance)
  and lifting maps `AddCommGroup.of G`, when `G` is a type with an `add_comm_group` instance.

If, for example you do the work of proving that a typeclass `Z` has a good notion of tensor product,
you are strongly encouraged to provide the corresponding `monoidal_category` instance
on a bundled version.
This ensures that the API for tensor products is complete, and enables use of general machinery.
Similarly if you prove universal properties, or adjunctions, you are encouraged to state these
using categorical language!

One disadvantage of the bundled approach is that we can only speak of morphisms between
objects living in the same type-theoretic universe.
In practice this is rarely a problem.

# Making a pull request

With so many moving parts, how do you actually go about changing the algebraic hierarchy?

We're still evolving how to handle this, but the current suggestion is:

* If you're adding a new "leaf" class, the requirements are lower,
  and an initial PR can just add whatever is immediately needed.
* A new "intermediate" class, especially low down in the hierarchy,
  needs to be careful about leaving gaps.

In a perfect world, there would be a group of simultaneous PRs that basically cover everything!
(Or at least an expectation that PRs may not be merged immediately while waiting on other
PRs that fill out the API.)

However "perfect is the enemy of good", and it would also be completely reasonable
to add a TODO list in the main module doc-string for the new class,
briefly listing the parts of the API which still need to be provided.
Hopefully this document makes it easy to assemble this list.

Another alternative to a TODO list in the doc-strings is adding github issues.

-/
library_note "the algebraic hierarchy"

/--
Some definitions that define objects of a class cannot be instances, because they have an
explicit argument that does not occur in the conclusion. An example is `preorder.lift` that has a
function `f : α → β` as an explicit argument to lift a preorder on `β` to a preorder on `α`.

If these definitions are used to define instances of this class *and* this class is an argument to
some other type-class so that type-class inference will have to unfold these instances to check
for definitional equality, then these definitions should be marked `@[reducible]`.

For example, `preorder.lift` is used to define `units.preorder` and `partial_order.lift` is used
to define `units.partial_order`. In some cases it is important that type-class inference can
recognize that `units.preorder` and `units.partial_order` give rise to the same `has_le` instance.
For example, you might have another class that takes `[has_le α]` as an argument, and this argument
sometimes comes from `units.preorder` and sometimes from `units.partial_order`.
Therefore, `preorder.lift` and `partial_order.lift` are marked `@[reducible]`.
-/
library_note "reducible non-instances"


import tactic --hide

/-Lemma
$(\mathrm{true} \implies \mathrm{false}) \implies \mathrm{false}$.
-/
lemma true_false_false : (true → false) → false :=
begin
  intro f,
  apply f,
  triv,

end