import data.nat.prime

/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We will use basic definitions about primes and divisibility taken from the mathlib
 library.  The home page of the library is
 https://github.com/leanprover/mathlib
 and you can browse and search the source code there.  If you set things up in a standard
 way, you should have a copy of the library in more or less the same place as your own
 Lean files, with directory structure like this:

.git: a director that we can ignore.

.gitignore,
      leanpkg.path,
      leanpkg.toml: files that we can ignore

_target

deps

mathlib

Subdirectories algebra, analysis, category, computability, data, ....
         These contain uncompiled, human-readable files with extension .lean,
	 and also compiled files with extension .olean.

src

Your own Lean files, such as the one shown here.

 The line `import data.nat.prime` allows us to use definitions
 and results from the file data/nat/prime.lean.  For
 example, that file proves that $2$ is prime, and gives the name
 `prime_two` to that fact.  (However, see the comment on the next line.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
open nat
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  The file data/nat/prime.lean proves
  a number of facts.  For example, it proves that $2$
  is prime, and gives the name `prime_two` to that fact.
  However, the same file also says `namespace nat` near the top.
  Because of this, the full name of the relevant theorem is really
  `nat.prime_two`.  The line `open nat`
  in the current file allows us to drop the `nat` prefix.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/

lemma larger_prime : ∀ n : ℕ, ∃ p, (prime p) ∧ (p > n) :=
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  This line states the result that we want to prove, and attaches the name
  `larger_prime` to the result.

There are various symbols that do not appear on your keyboard.  These
    can usually be entered in Visual Studio Code using LaTeX syntax.  For example,
    you can type `\forall` followed by a space, and the
    `\forall` will automatically turn into into
    `∀`.  For `ℕ`, `ℤ`, `ℚ` and `ℝ` one can enter `\nat`, `\int`, `\rat` and `\real`.
    Alternatively, one can enter `\N`, `\Z`, `\Q` and `\R`.

To indicate that $n$ is in $ℕ$ we write `n : ℕ`
    rather than `n ∈ ℕ`, following the conventions of type
    theory rather than set theory.

The expression `prime p` refers to the definition of primality
    taken from the file data/nat/prime.lean.
    That definition essentially gives a function from natural numbers to
    truth values.  To apply that function to the argument $p$, we write `prime p`
    (following the conventions of Lisp-like programming languages) rather than
    `prime(p)` as in traditional functional notation.  Note that if we
    had not had the line `open nat`, we would have had to say
    `nat.prime p` instead of `prime p`.  If you did not already know
    where to find the definition of primality, you could enter "prime"
    in the search boxes at
    github.com/leanprover/mathlib
    and
    github.com/leanprover/lean.

Note the commas associated with the quantifiers, and the
    `:=` at the end of the line.  If you have this file
    open in Visual Studio Code, and you take out the first comma, then you should
    see three error messages in the Lean messages window.  (If you do not see the
    relevant window, you should press CTRL-SHIFT-ENTER, or click the
     icon near the top of the screen.)
    None of those error messages is optimal, but
    unknown identifier 'n' gives some indication of
    the location of the problem.  On the other hand, if you leave out the
    := then the error messages are completely opaque.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
begin
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  This marks the beginning of the proof.  For some simple kinds of proofs
  it is not necessary to have a `begin ... end` wrapper.  For some
  complicated proofs there will be nested `begin ... end` blocks to
  prove various intermediate claims that arise in the course of the proof.

  Inside the `begin ... end` block, we enter
  "tactic mode", as discussed in Chapter 5 of the book
  Theorem
  Proving in Lean.  (References to "Chapter n" or "Section p.q" below
  will always refer to this book.)  This can be explained as follows.  A
  Lean proof is a kind of complex data structure.  Outside of tactic mode,
  we can construct a proof by programming methods, in which we explicitly
  apply various functions to simpler structures that we have already defined.
  Inside tactic mode, we can construct proofs by a kind of dialogue with
  the proof assistant, which is much more similar to a traditional
  writer's imagined dialogue with the reader.

  If you have this file open in Visual Studio Code, and you click at the
  end of the word `begin`, you should see a message like this in the
  Lean messages window:

   ⊢ ∀ (n : ℕ), ∃ (p : ℕ), prime p ∧ p > n

  This states our current proof goal.  (If you do not see the relevant
  window, you should press CTRL-SHIFT-ENTER, or click the
   icon near the top of the
  screen.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 intro n,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  We are aiming to prove a statement that is universally quantified over the
  natural numbers.  In traditional writing, we would start by saying "let $n$
  be an arbitrary natural number", or some similar phrase.  The line
  `intro n,` plays a similar role in Lean.

  If you click on the end of this line in VS Code, you will see that the tactic
  state has changed to

    n : ℕ
    ⊢ ∃ (p : ℕ), prime p ∧ p > n

  This means, essentially, that we are working in a context where we have a given
  natural number $n$, and we aim to prove that there is a prime $p$ with $p>n$.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 let m := fact n + 1,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  We now define $m$ to be $n!+1$.  Again note the terminating comma.  The tactic
  state changes to

   n : ℕ,
   m : ℕ := fact n + 1
   ⊢ ∃ (p : ℕ), prime p ∧ p > n

  incorporating the definition of $m$ in the context.

  In VS Code you can hover over the word `fact` with your mouse to see
  that the full name is `nat.fact` and that the type is `ℕ → ℕ`, and
  there is also a comment explaining that `fact` is the factorial
  function.  If you hold the CTRL key while hovering, you will see the
  actual recursive definition of the function.  If you hold the CTRL
  key and click, then you will jump to the file where that definition
  appears.  Alternatively, you can right-click and then type F12 or
  Alt-F12.  For this particular definition, the relevant file is data/nat/basic.lean.  Note that we did not
  explicitly import `data.nat.basic`, but we imported
  `data.nat.prime`, which imports `data.nat.sqrt`, which imports
  `data.nat.basic`.  Some standard properties of factorials are proved
  in the same place.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 let p := min_fac m,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 This line defines $p$ using the function `min_fac` defined in data/nat/basic.lean.  This is supposed to be
 defined by the requirement that `min_fac k` is the smallest $d>1$
 such that $d$ divides $k$.  This makes sense for $k=0$ (with
 `min_fac 0 = 2`) and for $k>1$, but this characterisation would leave
 `min_fac 1` undefined.  The approach taken by the Lean library is to
 arbitrarily define `min_fac 1 = 1`.  This of course means that some
 theorems about the properties of `min_fac` will need auxiliary
 hypotheses.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 use p,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Our goal is to prove that there exists a number with certain
properties.  The tactic `use p` converts this to the goal of proving
that the specific number `p` has those properties.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have p_prime : prime p,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Our goal is to prove `prime p ∧ p > n`.  The most obvious thing to do
next would be to split this into two goals: the proof that `p` is
prime, and the proof that `p > n`.  However, it is convenient to use
the fact that `p` is prime as part of the proof that `p > n`, and the
obvious structure would not allow for that.  We need to attach a name
to the claim that `p` is prime, then prove it, then return to the
main goal.

The effect of the current line is to add `prime p` as a new current
goal, with `prime p ∧ p > n` retained as a secondary goal to be
addressed later.  The current line also ensures that the name `p_prime`
will be attached to our proof that `p` is prime, once we have provided
it.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 {
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We open a set of curly brackets in which to deal with the first goal
(of proving that `p` is prime).  This is not strictly necessary: we
could just give a sequence of tactics, and Lean would apply them to
the first goal until that goal was solved, and then apply any
subsequent tactics to the second goal.  However, it is usually clearer
to use brackets.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
  apply min_fac_prime,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We now apply the theorem named `min_fac_prime` from
 data/nat/prime.lean.

 Recall that `min_fac n` was arbitrarily defined to be $1$ when $n=1$,
 so the theorem that `min_fac n` is prime is only valid with the side
 condition that $n\neq 1$.  (In more detail, the theorem
 `min_fac_prime` is stated as

  ∀ {n : ℕ}, n ≠ 1 → prime (min_fac n),

 so it is essentially a function that takes as input a natural number
 $n$ together with a proof that $n\neq 1$, and produces a proof that
 `min_fac n` is prime.  The `apply` tactic works out that the relevant
 value of `n` is $m = n!+1$, so it replaces the goal of proving that
 $p$ is prime by the goal of proving that $n!+1≠1$.  Unfortunately we
 will need to supply a few fiddly details to achieve this goal.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
apply ne_of_gt,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First, we apply the theorem `ne_of_gt`, which says that
`a > b → a ≠ b`.  The `apply` tactic works out which values of `a` and
`b` are relevant (this process is called "unification").  Also, the
theorem is valid for any type with a preorder, and the `apply` tactic
knows that by default it should use the standard order on `ℕ`; this
is handled using the mechanism of typeclass instances as described in
Chapter 10.  Thus, the goal changes to
proving that $n!+1>1$.

The name `ne_of_gt` illustrates the standard naming conventions for
the mathlib library: the string `ne` indicates the conclusion `a ≠ b`,
the string `gt` indicates the hypothesis `a > b`, and the string
`_of_` indicates an implication.  This is designed to work well with
auto-completion in VS Code: you know you want to prove a statement of
the form `a ≠ b`, so you can start typing `apply ne_of_...` and Lean
will suggest things that might be helpful.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
apply succ_lt_succ,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We now apply the theorem `succ_lt_succ`, which says that
`i < j → (i + 1) < (j + 1)`; this converts the goal to $0<n!$.
Here `lt` stands for "less than", and `succ` refers to the successor
operation $a\mapsto a+1$.  As in Peano arithmetic, addition is
actually defined in terms of the successor operation rather than the
other way around.  The hypothesis of this theorem is easy to guess,
so the name is just based on the conclusion.

You might think that we would instead need a theorem saying
`i > j → (i + 1) > (j + 1)` instead, and this would presumably be
called `succ_gt_succ`.  However, Lean defines `a > b` to mean `b < a`,
and it applies this transformation quite eagerly.  Theorems are
generally written with hypotheses and conclusions in the form `b < a`,
and Lean applies any required translations automatically.  There is
not in fact a theorem named `succ_gt_succ`, but we do not need one.

Note also that the `apply` tactic has unfolded the definition
$m:=n!+1$ in order to see that `succ_lt_succ` is applicable.  We
could have helped with this by writing `dsimp [m],` before
`apply succ_lt_succ`; this would have changed the goal to say
explicitly that `(fact n) + 1 > 1`.  This kind of help is sometimes
necessary, but not in this particular case.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
apply fact_pos,
},
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The theorem `fact_pos` says that $k! > 0$ for all $k$, so we can apply
it to solve the current goal.  We have added a comma after `fact_pos`
just to persuade Lean to give an explicit message saying "no goals".
This message is potentially misleading; we still need to prove that
$p > n$, but that goal is hidden while we are inside the curly
brackets that enclose the proof that $p$ is prime.  If we move the
cursor outside those curly brackets, then the goal $p > n$ becomes
visible again.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 split,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Our goal is to prove `prime p ∧ p > n`.  The tactic `split` converts
this into two goals, namely `prime p` and `p > n`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 {assumption},
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The current goal is to prove that `p` is prime.  The context already
contains a term (named `p_prime`) that `p` is prime, so we can just
use the `assumption` tactic to complete the proof.  We have placed this
in curly brackets to separate it from the proof of the other goal
(that `p > n`) but this is not strictly necessary.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
{
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We now open a second pair of curly brackets, within which we will
prove that $p > n$.  Again, these brackets are not strictly necessary,
but they help to make the structure of the proof visible.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 apply lt_of_not_ge,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Our goal is to prove that `p > n`, which Lean converts automatically
to `n < p`.  The theorem `lt_of_not_ge` converts this goal to
`¬ n ≥ p`.  As with the theorem `ne_of_gt` mentioned previously,
 this conversion rule is not proved
 directly for `ℕ`.  Instead, the standard library defines the standard
 order on `ℕ` and proves that it is a linear order, using a definition
 for which the above rule is not an axiom; instead, it is proved as a
 simple lemma valid for any type equipped with a linear order.  When
 we invoke `lt_of_not_ge` here, Lean needs to remember that we have
 given `ℕ` a linear order that it should use by default unless we say
 otherwise.  This is handled by the mechanism of typeclass instances,
 as described in Chapter 10.  (In fact, this
 same mechanism is also used to interpret expressions such as
 `n + m`: the symbol `+` has different meanings in different places,
 and typeclasses are used to supply the relevant definition in any
 given context.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
  intro p_le_n,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 It is part of the basic logical framework of Lean that a proof of
 `¬ n ≥ p` is the same as a procedure that takes a hypothetical proof
 of `n ≥ p` (or equivalently `p ≤ n`) and produces a contradiction, or
 in other words a proof of the proposition `false`.

 The current line means: "let `p_le_n` be a hypothetical proof that
 $p\leq n$".  The meaning of the assumption is forced by the nature
 of the goal.  By contrast, the name of the assumption is arbitrary;
 we have chosen to call it `p_le_n` to indicate its meaning, but we
 could equally well have written `intro foo,` instead.  In fact, we
 could just have written `intro`, and then Lean would choose an
 arbitrary name.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have p_gt_0 : p > 0, {apply min_fac_pos},
-- (b) have p_gt_0 : p > 0, exact min_fac_pos m,
-- (c) have p_gt_0 : p > 0 := min_fac_pos m,
-- (d) have p_gt_0 : p > 0 := min_fac_pos _,
-- (e) have p_gt_0 :=  min_fac_pos m,
-- (f) have p_gt_0 : p > 0 := by {apply min_fac_pos},
-- (g) let p_gt_0 :=  min_fac_pos m,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We now state that $p>0$, attach the name `p_gt_0` to this claim, and
 prove it by applying the theorem named `min_fac_pos` from
 data/nat/prime.lean.  In more detail,
 the phrase `have p_gt_0 : p > 0,` adds `p > 0` as a new goal, and
 the phrase `{apply min_fac_pos},` gives a proof; the new goal
 therefore disappears, and the context gains a term named `p_gt_0`
 of type `p > 0`.

 The following five lines all start with a double dash; this converts
 them to comments which are ignored by Lean.  In each case we could
 remove the double dash and the initial label to get a line of Lean
 code which would have the same effect as the current line.

 Version (b) uses the `exact` tactic instead of `apply`.  After
 `exact` we must supply a term that represents a proof of `p > 0`.
 We can again use `min_fac_pos` for this, except that we now need to
 supply the argument `m`.  Versions (c) to (g) all involve a ":=",
 and on the right hand side of that symbol we must work in term mode
 by default, rather than using tactics.  Thus, version (c) is
 essentially the same as (b).   In this situation we have to supply
 something as an argument to `min_fac_pos`, but we are allowed to
 write the symbol `_`, which instructs Lean to work out what is
 required; this is version (d).  Lean can only do this because it
 knows that `p_gt_0` is supposed to have type `p > 0`.  We can leave
 out that type annotation as in version (e), but in that case we need
 to give the argument explicitly.  In version (f) we use the
 construct `by { ... }` to switch back into tactic mode.  Version
 (g) illustrates the fact that `have` is essentially a synonym for
 `let`; it is standard to use `have` for propositions and `let` for
 other types, but this is not enforced.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have d0 : p ∣ fact n := dvd_fact p_gt_0 p_le_n,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We now prove that $p$ divides $n!$.  The proof appeals to the fact
 (named `dvd_fact`) that $a$ divides $b!$ whenever $a>0$ and
 $a\leq b$.  We supply the explicit arguments `p_gt_0` and `p_le_n` to
 verify the hypotheses; the implicit arguments $p$ and $n$ are deduced
 from the context.

 One needs to be careful about the symbol for divisibility.  If you
 just type `|` then you will get a tall vertical bar, which is used
 by Lean for syntax related to pattern matching, and which does not
 relate to divisibility.  If you type `\|` you will get a shorter
 vertical bar, which is the one that you need.  Alternatively, one
 could write `has_dvd.dvd a b` instead of `a ∣ b`.  (The full
 explanation for this slightly awkward syntax involves typeclasses;
 we will not give details here.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have d1 : p ∣ fact n + 1 := min_fac_dvd m,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We now claim that $p$ divides $m=n! + 1$.  As $p$ was defined to be
 `min_fac m`, this amounts to the claim that the `min_fac` function
 was correctly defined.  This was proved in `prime.lean` under the
 name `min_fac_dvd`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have d : p ∣ 1 := (nat.dvd_add_iff_right d0).mpr d1,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 We now want to prove that `p` divides `1`.  For this, we appeal
 to the fact that if $a$ divides $b$, then $a$ divides $c$ iff $a$
 divides $b+c$.  This fact has the name `dvd_add_iff_right`.
 Unfortunately, there are two versions of this fact that are currently
 visible.  One of them comes from the file
 init/algebra/ring.lean, and applies to
 an arbitrary commutative ring.  The other comes from
 init/data/nat/lemmas.lean, and applies
 only to `ℕ` (which is a semiring rather than a ring).  Note that both
 of these are in the core Lean library, not in mathlib.  We need to
 use the version for `ℕ`, so we need to resolve the ambiguity by
 providing the explicit prefix in `nat.dvd_add_iff_right`.

 By applying `nat.dvd_add_iff_right` to `d0`, we obtain a proof that
 $p$ divides $n!+1$ iff $p$ divides $1$.  The syntax `(...).mpr`
 extracts the right-to-left half of this equivalence, which we can
 then apply to the fact `d1` to obtain a proof that $p$ divides $1$.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 exact prime.not_dvd_one p_prime d
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Our goal is to produce a contradiction, or equivalently to prove the
 proposition `false`.  We do this using the keyword `exact` (which is
 the name of a kind of tautological tactic) followed by an expression
 that evaluates to `false`.  The theorem `prime.not_dvd_one` says that
 no prime divides $1$.  Equivalently, it is a function that accepts a
 proof that a number is prime, together with a proof that that number
 divides $1$, and produces `false`.  We can thus apply
 `prime.not_dvd_one` to the ingredients `p_prime` and `d` to obtain
 the required contradiction.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 },
end
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The proof is now complete.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/

#check larger_prime
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Lean accepts correct proofs without explicit acknowledgement.  To
get more positive feedback, we can enter
`#check larger_prime`: this will restate
the fact that we have proved in the message window.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/

lemma larger_prime' : ∀ n : ℕ, ∃ p, (prime p) ∧ (p > n) :=
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We now start again and give essentially the same proof in a different
style.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
begin
 intro n,
 let m := fact n + 1,
 let p := min_fac m,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These lines are the same as before.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have : m ≠ 1 := ne_of_gt (nat.succ_lt_succ (fact_pos n)),
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here we have a slight variation.  Previously, in every `have` statement
there was a name before the colon, which could be used to refer to the
relevant fact after it had been proved.  Here we omit the name.  On
the next line, we will need to refer back to the fact that `m ≠ 1`,
but we will just use the keyword `this`, which refers back to the most
recent anonymous `have`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 have p_prime : prime p := min_fac_prime this,
 have p_gt_0 : p > 0 := min_fac_pos m,
 have not_p_le_n : ¬ p ≤ n, {
  intro p_le_n,
  have d0 : p ∣ fact n := dvd_fact p_gt_0 p_le_n,
  have d1 : p ∣ fact n + 1 := min_fac_dvd m,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These lines are much the same as before, but with proof terms instead
of tactics.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
  have : p ∣ 1 := (nat.dvd_add_iff_right d0).mpr d1,
  exact prime.not_dvd_one p_prime ‹p ∣ 1›
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is another anonymous `have`.  We could again use the keyword
`this` to refer to the result, but instead we illustrate another
possible syntax.  Because the context contains a unique term of type
`p ∣ 1`, we can use the notation `‹p ∣ 1›` to refer to it.  This
notation involves "French quotes", which can be entered as \f< and
\f>.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 },
 have p_gt_n : p > n := lt_of_not_ge not_p_le_n,
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As before, we use `lt_of_not_ge` to get a proof of `p > n`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/
 exact (exists.intro p (and.intro p_prime p_gt_n)),
end
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The function `and.intro` can be used to combine `p_prime` and `p_gt_n`
into a proof of `prime p ∧ p > n`, then the function `exists.intro`
can be used to generate a proof of `∃ p, prime p ∧ p > n`.  We can
feed this into the `exact` tactic to finish the proof.

Usually we would not write `exists.intro p (and.intro p_prime p_gt_n)`,
but instead would use the terser expression `⟨p,⟨p_prime,p_gt_n⟩⟩`.
The brackets here are angle brackets, entered as \< and \> or
\langle and \rangle.  With the default fonts, they can be hard to
distinguish visually from ordinary round brackets, and they are also
different from the French quotes that we used earlier.  Anyway, angle
brackets can be used in many situations to combine terms, provided
that Lean already knows the expected type of the combination.  Here
we are using the `exact` tactic so the argument must match the type
of the goal `∃ p, prime p ∧ p > n`, and Lean can work everything out
from this.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/

lemma larger_prime'' : ∀ n : ℕ, ∃ p, (prime p) ∧ (p > n) :=
λ n,
 let m := fact n + 1 in
 let p := min_fac m in
 let p_prime :=
  min_fac_prime (ne_of_gt (nat.succ_lt_succ (fact_pos n))) in
  ⟨p,⟨p_prime,
     (lt_of_not_ge
      (λ p_le_n,
        prime.not_dvd_one
         p_prime
          ((nat.dvd_add_iff_right
            (dvd_fact p_prime.pos p_le_n)).mpr
              (min_fac_dvd m))))⟩⟩
/-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Finally, we give a third proof written as a single proof term with no
tactics of any kind.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-/


/-

Idea: Take (homo_locus_pres G) × G and introduce multiplication:
(x, a) * (y, b) = (x * y * homo_locus_of(a, b), a * b)

Prove it is a group. Then prove it is isomorphic to pq_group G

-/

import pq_group_homo_locus_pres

universes u

section pq_group_homo_locus_pres_prod

@[ext]
structure homo_locus_pres_prod (G : Type u) [group G] := (x : homo_locus_pres G) (a : G)

variables {G : Type u} [group G]

instance homo_locus_pres_prod_is_group : group (homo_locus_pres_prod G) := {
  mul := λ ⟨x, a⟩ ⟨y, b⟩, ⟨x * y * homo_locus_of(a, b), a * b⟩,
  inv := λ ⟨x, a⟩, ⟨x⁻¹, a⁻¹⟩,
  one := ⟨1, 1⟩,
  mul_assoc := begin
    rintros ⟨x, a⟩ ⟨y, b⟩ ⟨z, c⟩,
    ext1,
    {
      show (x * y * homo_locus_of (a, b)) * z * homo_locus_of (a * b, c) = x * (y * z * homo_locus_of (b, c)) * homo_locus_of (a, b * c),
      simp only [mul_assoc],
      apply congr_arg,
      apply congr_arg,
      rw ←mul_assoc,
      rw homo_locus_pres_comm,
      rw ←mul_assoc,
      rw homo_locus_assoc a b c,
      rw homo_locus_pres_comm,
      apply congr_arg,
      rw homo_locus_pres_comm,
    },
    {
      show a * b * c = a * (b * c),
      rw mul_assoc,
    },
  end,
  one_mul := begin
    rintros ⟨x, a⟩,
    ext1,
    {
      show 1 * x * homo_locus_of (1, a) = x,
      rw homo_locus_of_one,
      simp only [mul_one, one_mul],
      exact homo_locus_closed_left_one a,
    },
    {
      show 1 * a = a,
      rw one_mul,
    },
  end,
  mul_one := begin
    rintros ⟨x, a⟩,
    ext1,
    {
      show x * 1 * homo_locus_of (a, 1) = x,
      rw homo_locus_of_one,
      simp only [mul_one, one_mul],
      exact homo_locus_closed_right_one a,
    },
    {
      show a * 1 = a,
      rw mul_one,
    },
  end,
  mul_left_inv := begin
    rintros ⟨x, a⟩,
    ext1,
    {
      show x⁻¹ * x * homo_locus_of (a⁻¹, a) = 1,
      rw mul_left_inv,
      rw one_mul,
      apply homo_locus_of_one,
      exact homo_locus_closed_left_inv a,
    },
    {
      show a⁻¹ * a = 1,
      rw mul_left_inv,
    },
  end }

lemma homo_locus_pres_prod_mul_def (x y : homo_locus_pres G) (a b : G) : (⟨x, a⟩ * ⟨y, b⟩ : homo_locus_pres_prod G) = ⟨x * y * homo_locus_of (a, b), a * b⟩ := rfl

lemma homo_locus_pres_prod_inv_def (x : homo_locus_pres G) (a : G) : (⟨x, a⟩⁻¹ : homo_locus_pres_prod G) = ⟨x⁻¹, a⁻¹⟩ := rfl

lemma homo_locus_pres_prod_one_def : (1 : homo_locus_pres_prod G) = ⟨1, 1⟩ := rfl

lemma homo_locus_pres_prod_mul_def_unsplit (x y : homo_locus_pres_prod G) : (x * y : homo_locus_pres_prod G) = ⟨x.x * y.x * homo_locus_of (x.a, y.a), x.a * y.a⟩ := begin
  cases x,
  cases y,
  refl,
end

lemma homo_locus_pres_prod_inv_def_unsplit (x : homo_locus_pres_prod G) : (x⁻¹ : homo_locus_pres_prod G) = ⟨x.x⁻¹, x.a⁻¹⟩ := begin
  cases x,
  refl,
end

end pq_group_homo_locus_pres_prod

section pq_group_homo_locus_pres_prod_iso

variables {G : Type u} [group G]

def pq_group_homo_locus_pres_prod_iso_forward : pq_group G →* homo_locus_pres_prod G :=
begin
  fapply pq_morph_to_L_morph_adj,
  {
    intro g,
    exact ⟨1, g⟩,
  },
  {
    split,
    {
      intros a b,
      conv {
        to_rhs,
        rw rhd_def_group,
      },
      simp only [homo_locus_pres_prod_mul_def, homo_locus_pres_prod_inv_def],
      split,
      {
        simp only [one_inv, mul_one, one_mul],
        rw ←homo_locus_of_inv,
        simp only [mul_right_inv],
      },
      {
        refl,
      },
    },
    {
      intros a,
      have for_nat : ∀ n : ℕ, (⟨1, a ^ n⟩ : homo_locus_pres_prod G) = ⟨1, a⟩ ^ n,
      {
        intros n,
        induction n,
        {
          refl,
        },
        {
          rw pow_succ,
          rw pow_succ,
          rw ←n_ih,
          clear n_ih,
          rw homo_locus_pres_prod_mul_def,
          simp only,
          split,
          {
            simp only [one_mul],
            symmetry,
            apply homo_locus_of_one,
            convert homo_locus_closed_same_power a 1 n_n,
            rw gpow_one,
          },
          {
            refl,
          },
        },
      },
      intros n,
      induction n,
      {
        simp only [int.of_nat_eq_coe, gpow_coe_nat],
        rw for_nat,
      },
      {
        simp only [gpow_neg_succ_of_nat],
        rw ←for_nat,
        simp only [homo_locus_pres_prod_inv_def],
        split,
        {
          rw one_inv,
        },
        {
          refl,
        },
      },
    },
  },
end

lemma pq_group_homo_locus_pres_prod_iso_forward_of (x : G) : pq_group_homo_locus_pres_prod_iso_forward (of x) = ⟨1, x⟩ := rfl

def pq_group_homo_locus_pres_prod_iso_backward : homo_locus_pres_prod G →* pq_group G := ⟨λ x, (↑(homo_locus_pres_iso_ker_counit_forward x.x) : pq_group G) * (of x.a), begin
  simp only [homo_locus_pres_prod_one_def, of_one, mul_one, subgroup.coe_one, monoid_hom.map_one],
end, begin
  rintros ⟨x, a⟩ ⟨y, b⟩,
  simp only [homo_locus_pres_prod_mul_def],
  simp only [monoid_hom.map_mul, subgroup.coe_mul],
  rw homo_locus_pres_iso_ker_counit_forward_homo_locus_of,
  simp only [subgroup.coe_mk],
  simp only [mul_assoc],
  simp only [mul_right_inj, mul_one, mul_left_inv],
  rw ←mul_assoc,
  rw counit_ker_center _ (of a),
  rw ←mul_assoc,
end⟩

lemma pq_group_homo_locus_pres_prod_iso_right_inv_aux (a b c : G) :  pq_group_homo_locus_pres_prod_iso_forward (pq_group_homo_locus_pres_prod_iso_backward {x := homo_locus_of (a, b), a := c}) = {x := homo_locus_of (a, b), a := c} :=
begin
  unfold pq_group_homo_locus_pres_prod_iso_backward,
  simp only [monoid_hom.map_mul, monoid_hom.coe_mk],
  rw homo_locus_pres_iso_ker_counit_forward_homo_locus_of,
  simp only [monoid_hom.map_mul, gpow_zero, monoid_hom.map_mul_inv, subgroup.coe_mk],
  simp only [pq_group_homo_locus_pres_prod_iso_forward_of],
  simp only [homo_locus_pres_prod_mul_def, homo_locus_pres_prod_inv_def],
  split,
  {
    have : homo_locus_of (a * b, (a * b)⁻¹) = 1,
    {
      rw homo_locus_of_one,
      exact homo_locus_closed_right_inv (a * b),
    },
    rw this,
    clear this,
    have : homo_locus_of (a * b * (a * b)⁻¹, c) = 1,
    {
      group,
      rw homo_locus_of_one,
      exact homo_locus_closed_left_one c,
    },
    rw this,
    clear this,
    group,
  },
  {
    group,
  },
end

def pq_group_homo_locus_pres_prod_iso : pq_group G ≃* homo_locus_pres_prod G := {
  to_fun := pq_group_homo_locus_pres_prod_iso_forward,
  inv_fun := pq_group_homo_locus_pres_prod_iso_backward,
  left_inv := begin
    apply morphism_equality_comp_elem_id,
    intros g,
    unfold pq_group_homo_locus_pres_prod_iso_forward,
    rw pq_morph_to_L_morph_adj_comm_of,
    unfold pq_group_homo_locus_pres_prod_iso_backward,
    simp only [monoid_hom.coe_mk],
    simp only [one_mul, subgroup.coe_one, monoid_hom.map_one],
  end,
  right_inv := begin
    intros x,
    cases x with x a,
    induction x,
    {
      rw homo_locus_pres_quot_mk_helper,
      induction x generalizing a,
      {
        rw ←homo_locus_pres_one_def,
        convert pq_group_homo_locus_pres_prod_iso_right_inv_aux 1 1 a;
        {
          rw homo_locus_of_one,
          exact homo_locus_closed_refl 1,
        },
      },
      {
        rw ←homo_locus_of_def (x_x, x_y),
        rw pq_group_homo_locus_pres_prod_iso_right_inv_aux,
      },
      {
        rw ←homo_locus_pres_mul_def,
        have : ({x := ⟦x_x⟧ * ⟦x_y⟧, a := a} : homo_locus_pres_prod G) = {x := ⟦x_x⟧, a := a} * {x := ⟦x_y⟧, a := 1},
        {
          rw homo_locus_pres_prod_mul_def,
          ext1,
          {
            simp only,
            suffices : homo_locus_of (a, 1) = 1,
            {
              rw this,
              rw mul_one,
            },
            rw homo_locus_of_one,
            exact homo_locus_closed_right_one a,
          },
          {
            simp only,
            rw mul_one,
          },
        },
        rw this,
        simp only [monoid_hom.map_mul],
        congr,
        apply x_ih_x,
        apply x_ih_y,
      },
      {
        rw ←homo_locus_pres_inv_def,
        have : ({x := ⟦x_x⟧⁻¹, a := a} : homo_locus_pres_prod G) = {x := ⟦x_x⟧, a := a⁻¹}⁻¹,
        {
          rw homo_locus_pres_prod_inv_def,
          rw inv_inv,
        },
        rw this,
        simp only [inv_inj, monoid_hom.map_inv],
        apply x_ih,
      },
    },
    {refl,},
  end,
  map_mul' := begin
    intros a b,
    simp only [monoid_hom.map_mul],
  end }

lemma pq_group_homo_locus_pres_prod_iso_left_inv (x : pq_group G) : pq_group_homo_locus_pres_prod_iso_backward(pq_group_homo_locus_pres_prod_iso_forward x) = x :=
begin
  apply pq_group_homo_locus_pres_prod_iso.left_inv,
end

lemma pq_group_homo_locus_pres_prod_iso_right_inv (x : homo_locus_pres_prod G) : pq_group_homo_locus_pres_prod_iso_forward(pq_group_homo_locus_pres_prod_iso_backward x) = x :=
begin
  apply pq_group_homo_locus_pres_prod_iso.right_inv,
end

end pq_group_homo_locus_pres_prod_iso


/-
Copyright (c) 2020 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import algebra.lie.matrix
import linear_algebra.bilinear_form

/-!
# Lie algebras of skew-adjoint endomorphisms of a bilinear form

When a module carries a bilinear form, the Lie algebra of endomorphisms of the module contains a
distinguished Lie subalgebra: the skew-adjoint endomorphisms. Such subalgebras are important
because they provide a simple, explicit construction of the so-called classical Lie algebras.

This file defines the Lie subalgebra of skew-adjoint endomorphims cut out by a bilinear form on
a module and proves some basic related results. It also provides the corresponding definitions and
results for the Lie algebra of square matrices.

## Main definitions

  * `skew_adjoint_lie_subalgebra`
  * `skew_adjoint_lie_subalgebra_equiv`
  * `skew_adjoint_matrices_lie_subalgebra`
  * `skew_adjoint_matrices_lie_subalgebra_equiv`

## Tags

lie algebra, skew-adjoint, bilinear form
-/

universes u v w w₁

section skew_adjoint_endomorphisms
open bilin_form

variables {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
variables (B : bilin_form R M)

lemma bilin_form.is_skew_adjoint_bracket (f g : module.End R M)
  (hf : f ∈ B.skew_adjoint_submodule) (hg : g ∈ B.skew_adjoint_submodule) :
  ⁅f, g⁆ ∈ B.skew_adjoint_submodule :=
begin
  rw mem_skew_adjoint_submodule at *,
  have hfg : is_adjoint_pair B B (f * g) (g * f), { rw ←neg_mul_neg g f, exact hf.mul hg, },
  have hgf : is_adjoint_pair B B (g * f) (f * g), { rw ←neg_mul_neg f g, exact hg.mul hf, },
  change bilin_form.is_adjoint_pair B B (f * g - g * f) (-(f * g - g * f)), rw neg_sub,
  exact hfg.sub hgf,
end

/-- Given an `R`-module `M`, equipped with a bilinear form, the skew-adjoint endomorphisms form a
Lie subalgebra of the Lie algebra of endomorphisms. -/
def skew_adjoint_lie_subalgebra : lie_subalgebra R (module.End R M) :=
{ lie_mem' := B.is_skew_adjoint_bracket, ..B.skew_adjoint_submodule }

variables {N : Type w} [add_comm_group N] [module R N] (e : N ≃ₗ[R] M)

/-- An equivalence of modules with bilinear forms gives equivalence of Lie algebras of skew-adjoint
endomorphisms. -/
def skew_adjoint_lie_subalgebra_equiv :
  skew_adjoint_lie_subalgebra (B.comp (↑e : N →ₗ[R] M) ↑e) ≃ₗ⁅R⁆ skew_adjoint_lie_subalgebra B :=
begin
  apply lie_equiv.of_subalgebras _ _ e.lie_conj,
  ext f,
  simp only [lie_subalgebra.mem_coe, submodule.mem_map_equiv, lie_subalgebra.mem_map_submodule,
    coe_coe],
  exact (bilin_form.is_pair_self_adjoint_equiv (-B) B e f).symm,
end

@[simp] lemma skew_adjoint_lie_subalgebra_equiv_apply
  (f : skew_adjoint_lie_subalgebra (B.comp ↑e ↑e)) :
  ↑(skew_adjoint_lie_subalgebra_equiv B e f) = e.lie_conj f :=
by simp [skew_adjoint_lie_subalgebra_equiv]

@[simp] lemma skew_adjoint_lie_subalgebra_equiv_symm_apply (f : skew_adjoint_lie_subalgebra B) :
  ↑((skew_adjoint_lie_subalgebra_equiv B e).symm f) = e.symm.lie_conj f :=
by simp [skew_adjoint_lie_subalgebra_equiv]

end skew_adjoint_endomorphisms

section skew_adjoint_matrices
open_locale matrix

variables {R : Type u} {n : Type w} [comm_ring R] [decidable_eq n] [fintype n]
variables (J : matrix n n R)

lemma matrix.lie_transpose (A B : matrix n n R) : ⁅A, B⁆ᵀ = ⁅Bᵀ, Aᵀ⁆ :=
show (A * B - B * A)ᵀ = (Bᵀ * Aᵀ - Aᵀ * Bᵀ), by simp

lemma matrix.is_skew_adjoint_bracket (A B : matrix n n R)
  (hA : A ∈ skew_adjoint_matrices_submodule J) (hB : B ∈ skew_adjoint_matrices_submodule J) :
  ⁅A, B⁆ ∈ skew_adjoint_matrices_submodule J :=
begin
  simp only [mem_skew_adjoint_matrices_submodule] at *,
  change ⁅A, B⁆ᵀ ⬝ J = J ⬝ -⁅A, B⁆, change Aᵀ ⬝ J = J ⬝ -A at hA, change Bᵀ ⬝ J = J ⬝ -B at hB,
  simp only [←matrix.mul_eq_mul] at *,
  rw [matrix.lie_transpose, lie_ring.of_associative_ring_bracket,
    lie_ring.of_associative_ring_bracket, sub_mul, mul_assoc, mul_assoc, hA, hB, ←mul_assoc,
    ←mul_assoc, hA, hB],
  noncomm_ring,
end

/-- The Lie subalgebra of skew-adjoint square matrices corresponding to a square matrix `J`. -/
def skew_adjoint_matrices_lie_subalgebra : lie_subalgebra R (matrix n n R) :=
{ lie_mem' := J.is_skew_adjoint_bracket, ..(skew_adjoint_matrices_submodule J) }

@[simp] lemma mem_skew_adjoint_matrices_lie_subalgebra (A : matrix n n R) :
  A ∈ skew_adjoint_matrices_lie_subalgebra J ↔ A ∈ skew_adjoint_matrices_submodule J :=
iff.rfl

/-- An invertible matrix `P` gives a Lie algebra equivalence between those endomorphisms that are
skew-adjoint with respect to a square matrix `J` and those with respect to `PᵀJP`. -/
noncomputable def skew_adjoint_matrices_lie_subalgebra_equiv (P : matrix n n R) (h : is_unit P) :
  skew_adjoint_matrices_lie_subalgebra J ≃ₗ⁅R⁆ skew_adjoint_matrices_lie_subalgebra (Pᵀ ⬝ J ⬝ P) :=
lie_equiv.of_subalgebras _ _ (P.lie_conj h).symm
begin
  ext A,
  suffices : P.lie_conj h A ∈ skew_adjoint_matrices_submodule J ↔
    A ∈ skew_adjoint_matrices_submodule (Pᵀ ⬝ J ⬝ P),
  { simp only [lie_subalgebra.mem_coe, submodule.mem_map_equiv, lie_subalgebra.mem_map_submodule,
      coe_coe], exact this, },
  simp [matrix.is_skew_adjoint, J.is_adjoint_pair_equiv _ _ P h],
end

lemma skew_adjoint_matrices_lie_subalgebra_equiv_apply
  (P : matrix n n R) (h : is_unit P) (A : skew_adjoint_matrices_lie_subalgebra J) :
  ↑(skew_adjoint_matrices_lie_subalgebra_equiv J P h A) = P⁻¹ ⬝ ↑A ⬝ P :=
by simp [skew_adjoint_matrices_lie_subalgebra_equiv]

/-- An equivalence of matrix algebras commuting with the transpose endomorphisms restricts to an
equivalence of Lie algebras of skew-adjoint matrices. -/
def skew_adjoint_matrices_lie_subalgebra_equiv_transpose {m : Type w} [decidable_eq m] [fintype m]
  (e : matrix n n R ≃ₐ[R] matrix m m R) (h : ∀ A, (e A)ᵀ = e (Aᵀ)) :
  skew_adjoint_matrices_lie_subalgebra J ≃ₗ⁅R⁆ skew_adjoint_matrices_lie_subalgebra (e J) :=
lie_equiv.of_subalgebras _ _ e.to_lie_equiv
begin
  ext A,
  suffices : J.is_skew_adjoint (e.symm A) ↔ (e J).is_skew_adjoint A, by simpa [this],
  simp [matrix.is_skew_adjoint, matrix.is_adjoint_pair, ← matrix.mul_eq_mul,
    ← h, ← function.injective.eq_iff e.injective],
end

@[simp] lemma skew_adjoint_matrices_lie_subalgebra_equiv_transpose_apply
  {m : Type w} [decidable_eq m] [fintype m]
  (e : matrix n n R ≃ₐ[R] matrix m m R) (h : ∀ A, (e A)ᵀ = e (Aᵀ))
  (A : skew_adjoint_matrices_lie_subalgebra J) :
  (skew_adjoint_matrices_lie_subalgebra_equiv_transpose J e h A : matrix m m R) = e A :=
rfl

lemma mem_skew_adjoint_matrices_lie_subalgebra_unit_smul (u : units R) (J A : matrix n n R) :
  A ∈ skew_adjoint_matrices_lie_subalgebra ((u : R) • J) ↔
  A ∈ skew_adjoint_matrices_lie_subalgebra J :=
begin
  change A ∈ skew_adjoint_matrices_submodule ((u : R) • J) ↔  A ∈ skew_adjoint_matrices_submodule J,
  simp only [mem_skew_adjoint_matrices_submodule, matrix.is_skew_adjoint, matrix.is_adjoint_pair],
  split; intros h,
  { simpa using congr_arg (λ B, (↑u⁻¹ : R) • B) h, },
  { simp [h], },
end

end skew_adjoint_matrices


import incidence_world.level04 --hide
open IncidencePlane --hide

/-
# Incidence World

## Level 5: proving useful lemmas (III).

Remember that a couple of levels ago we proved that a line could help us deciding that two points were different. Now we will see that
a point can help us deciding that two lines are different.

To solve this level, you just need three lines of code. Try to finish it by your own. Here you have a clue for each of the lines:

**Line 1:** Remember that a goal of the form `⊢ P ≠ Q` can be read as `⊢ (P = Q) → false` as well.

**Line 2:** If you have the hypotheses `h : A = B` and `h2 : A ∈ r`, then `rw h at h2,` will change `h2` into `h2 : B ∈ r`.

**Line 3:** If the current goal is `⊢ false` and you have the hypotheses `h : ¬ P` and `h2 : P`, which contradict each other, then `tauto,`
will make progress.
-/

/- Hint : Click here for a hint, in case you get stuck.
Remember that `¬ P` is the same as `P → false`, so `intro` may get you going.
Still bewildered? Click on "View source" (located on the top right corner of the game screen) to see the solution.
-/

variables {Ω : Type} [IncidencePlane Ω] --hide
variables {P : Ω} {r s : Line Ω} --hide

/- Lemma :
If two lines `r` and `s` do not share a point, then they are not equal.
-/
lemma ne_of_not_share_point  (hPr : P ∈ r)
(hPs : P ∉ s): r ≠ s:=
begin

  intro H,
  rw H at hPr,
  tauto,

end


/-
Copyright (c) 2021 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Riccardo Brasca
-/
import analysis.normed.group.hom

/-!
# Quotients of seminormed groups

For any `semi_normed_group M` and any `S : add_subgroup M`, we provide a `semi_normed_group`
the group quotient `M ⧸ S`.
If `S` is closed, we provide `normed_group (M ⧸ S)` (regardless of whether `M` itself is
separated). The two main properties of these structures are the underlying topology is the quotient
topology and the projection is a normed group homomorphism which is norm non-increasing
(better, it has operator norm exactly one unless `S` is dense in `M`). The corresponding
universal property is that every normed group hom defined on `M` which vanishes on `S` descends
to a normed group hom defined on `M ⧸ S`.

This file also introduces a predicate `is_quotient` characterizing normed group homs that
are isomorphic to the canonical projection onto a normed group quotient.

## Main definitions

We use `M` and `N` to denote seminormed groups and `S : add_subgroup M`.
All the following definitions are in the `add_subgroup` namespace. Hence we can access
`add_subgroup.normed_mk S` as `S.normed_mk`.

* `semi_normed_group_quotient` : The seminormed group structure on the quotient by
    an additive subgroup. This is an instance so there is no need to explictly use it.

* `normed_group_quotient` : The normed group structure on the quotient by
    a closed additive subgroup. This is an instance so there is no need to explictly use it.

* `normed_mk S` : the normed group hom from `M` to `M ⧸ S`.

* `lift S f hf`: implements the universal property of `M ⧸ S`. Here
    `(f : normed_group_hom M N)`, `(hf : ∀ s ∈ S, f s = 0)` and
    `lift S f hf : normed_group_hom (M ⧸ S) N`.

* `is_quotient`: given `f : normed_group_hom M N`, `is_quotient f` means `N` is isomorphic
    to a quotient of `M` by a subgroup, with projection `f`. Technically it asserts `f` is
    surjective and the norm of `f x` is the infimum of the norms of `x + m` for `m` in `f.ker`.

## Main results

* `norm_normed_mk` : the operator norm of the projection is `1` if the subspace is not dense.

* `is_quotient.norm_lift`: Provided `f : normed_hom M N` satisfies `is_quotient f`, for every
     `n : N` and positive `ε`, there exists `m` such that `f m = n ∧ ∥m∥ < ∥n∥ + ε`.

## Implementation details

For any `semi_normed_group M` and any `S : add_subgroup M` we define a norm on `M ⧸ S` by
`∥x∥ = Inf (norm '' {m | mk' S m = x})`. This formula is really an implementation detail, it
shouldn't be needed outside of this file setting up the theory.

Since `M ⧸ S` is automatically a topological space (as any quotient of a topological space),
one needs to be careful while defining the `semi_normed_group` instance to avoid having two
different topologies on this quotient. This is not purely a technological issue.
Mathematically there is something to prove. The main point is proved in the auxiliary lemma
`quotient_nhd_basis` that has no use beyond this verification and states that zero in the quotient
admits as basis of neighborhoods in the quotient topology the sets `{x | ∥x∥ < ε}` for positive `ε`.

Once this mathematical point it settled, we have two topologies that are propositionaly equal. This
is not good enough for the type class system. As usual we ensure *definitional* equality
using forgetful inheritance, see Note [forgetful inheritance]. A (semi)-normed group structure
includes a uniform space structure which includes a topological space structure, together
with propositional fields asserting compatibility conditions.
The usual way to define a `semi_normed_group` is to let Lean build a uniform space structure
using the provided norm, and then trivially build a proof that the norm and uniform structure are
compatible. Here the uniform structure is provided using `topological_add_group.to_uniform_space`
which uses the topological structure and the group structure to build the uniform structure. This
uniform structure induces the correct topological structure by construction, but the fact that it
is compatible with the norm is not obvious; this is where the mathematical content explained in
the previous paragraph kicks in.

-/

noncomputable theory

open quotient_add_group metric set
open_locale topological_space nnreal

variables {M N : Type*} [semi_normed_group M] [semi_normed_group N]

/-- The definition of the norm on the quotient by an additive subgroup. -/
noncomputable
instance norm_on_quotient (S : add_subgroup M) : has_norm (M ⧸ S) :=
{ norm := λ x, Inf (norm '' {m | mk' S m = x}) }

lemma image_norm_nonempty {S : add_subgroup M} :
  ∀ x : M ⧸ S, (norm '' {m | mk' S m = x}).nonempty :=
begin
  rintro ⟨m⟩,
  rw set.nonempty_image_iff,
  use m,
  change mk' S m = _,
  refl
end

lemma bdd_below_image_norm (s : set M) : bdd_below (norm '' s) :=
begin
  use 0,
  rintro _ ⟨x, hx, rfl⟩,
  apply norm_nonneg
end

/-- The norm on the quotient satisfies `∥-x∥ = ∥x∥`. -/
lemma quotient_norm_neg {S : add_subgroup M} (x : M ⧸ S) : ∥-x∥ = ∥x∥ :=
begin
  suffices : norm '' {m | mk' S m = x} = norm '' {m | mk' S m = -x},
    by simp only [this, norm],
  ext r,
  split,
  { rintros ⟨m, hm : mk' S m = x, rfl⟩,
    subst hm,
    rw ← norm_neg,
    exact ⟨-m, by simp only [(mk' S).map_neg, set.mem_set_of_eq], rfl⟩ },
  { rintros ⟨m, hm : mk' S m = -x, rfl⟩,
    use -m,
    simp at hm,
    simp [hm], }
end

lemma quotient_norm_sub_rev {S : add_subgroup M} (x y : M ⧸ S) : ∥x - y∥ = ∥y - x∥ :=
by rw [show x - y = -(y - x), by abel, quotient_norm_neg]

/-- The norm of the projection is smaller or equal to the norm of the original element. -/
lemma quotient_norm_mk_le (S : add_subgroup M) (m : M) :
  ∥mk' S m∥ ≤ ∥m∥ :=
begin
  apply cInf_le,
  use 0,
  { rintros _ ⟨n, h, rfl⟩,
    apply norm_nonneg },
  { apply set.mem_image_of_mem,
    rw set.mem_set_of_eq }
end

/-- The norm of the projection is smaller or equal to the norm of the original element. -/
lemma quotient_norm_mk_le' (S : add_subgroup M) (m : M) :
  ∥(m : M ⧸ S)∥ ≤ ∥m∥ := quotient_norm_mk_le S m

/-- The norm of the image under the natural morphism to the quotient. -/
lemma quotient_norm_mk_eq (S : add_subgroup M) (m : M) :
  ∥mk' S m∥ = Inf ((λ x, ∥m + x∥) '' S) :=
begin
  change Inf _ = _,
  congr' 1,
  ext r,
  simp_rw [coe_mk', eq_iff_sub_mem],
  split,
  { rintros ⟨y, h, rfl⟩,
    use [y - m, h],
    simp },
  { rintros ⟨y, h, rfl⟩,
    use m + y,
    simpa using h },
end

/-- The quotient norm is nonnegative. -/
lemma quotient_norm_nonneg (S : add_subgroup M) : ∀ x : M ⧸ S, 0 ≤ ∥x∥ :=
begin
  rintros ⟨m⟩,
  change 0 ≤ ∥mk' S m∥,
  apply le_cInf (image_norm_nonempty _),
  rintros _ ⟨n, h, rfl⟩,
  apply norm_nonneg
end

/-- The quotient norm is nonnegative. -/
lemma norm_mk_nonneg (S : add_subgroup M) (m : M) : 0 ≤ ∥mk' S m∥ :=
quotient_norm_nonneg S _

/-- The norm of the image of `m : M` in the quotient by `S` is zero if and only if `m` belongs
to the closure of `S`. -/
lemma quotient_norm_eq_zero_iff (S : add_subgroup M) (m : M) :
  ∥mk' S m∥ = 0 ↔ m ∈ closure (S : set M) :=
begin
  have : 0 ≤ ∥mk' S m∥ := norm_mk_nonneg S m,
  rw [← this.le_iff_eq, quotient_norm_mk_eq, real.Inf_le_iff],
  simp_rw [zero_add],
  { calc (∀ ε > (0 : ℝ), ∃ r ∈ (λ x, ∥m + x∥) '' (S : set M), r < ε) ↔
        (∀ ε > 0, (∃ x ∈ S, ∥m + x∥ < ε)) : by simp [set.bex_image_iff]
     ... ↔ ∀ ε > 0, (∃ x ∈ S, ∥m + -x∥ < ε) : _
     ... ↔ ∀ ε > 0, (∃ x ∈ S, x ∈ metric.ball m ε) : by simp [dist_eq_norm, ← sub_eq_add_neg,
                                                              norm_sub_rev]
     ... ↔ m ∈ closure ↑S : by simp [metric.mem_closure_iff, dist_comm],
    apply forall_congr, intro ε, apply forall_congr, intro  ε_pos,
    rw [← S.exists_neg_mem_iff_exists_mem],
    simp },
  { use 0,
    rintro _ ⟨x, x_in, rfl⟩,
    apply norm_nonneg },
  rw set.nonempty_image_iff,
  use [0, S.zero_mem]
end

/-- For any `x : M ⧸ S` and any `0 < ε`, there is `m : M` such that `mk' S m = x`
and `∥m∥ < ∥x∥ + ε`. -/
lemma norm_mk_lt {S : add_subgroup M} (x : M ⧸ S) {ε : ℝ} (hε : 0 < ε) :
  ∃ (m : M), mk' S m = x ∧ ∥m∥ < ∥x∥ + ε :=
begin
  obtain ⟨_, ⟨m : M, H : mk' S m = x, rfl⟩, hnorm : ∥m∥ < ∥x∥ + ε⟩ :=
    real.lt_Inf_add_pos (image_norm_nonempty x) hε,
  subst H,
  exact ⟨m, rfl, hnorm⟩,
end

/-- For any `m : M` and any `0 < ε`, there is `s ∈ S` such that `∥m + s∥ < ∥mk' S m∥ + ε`. -/
lemma norm_mk_lt' (S : add_subgroup M) (m : M) {ε : ℝ} (hε : 0 < ε) :
  ∃ s ∈ S, ∥m + s∥ < ∥mk' S m∥ + ε :=
begin
  obtain ⟨n : M, hn : mk' S n = mk' S m, hn' : ∥n∥ < ∥mk' S m∥ + ε⟩ :=
    norm_mk_lt (quotient_add_group.mk' S m) hε,
  erw [eq_comm, quotient_add_group.eq] at hn,
  use [- m + n, hn],
  rwa [add_neg_cancel_left]
end

/-- The quotient norm satisfies the triangle inequality. -/
lemma quotient_norm_add_le (S : add_subgroup M) (x y : M ⧸ S) : ∥x + y∥ ≤ ∥x∥ + ∥y∥ :=
begin
  refine le_of_forall_pos_le_add (λ ε hε, _),
  replace hε := half_pos hε,
  obtain ⟨m, rfl, hm : ∥m∥ < ∥mk' S m∥ + ε / 2⟩ := norm_mk_lt x hε,
  obtain ⟨n, rfl, hn : ∥n∥ < ∥mk' S n∥ + ε / 2⟩ := norm_mk_lt y hε,
  calc ∥mk' S m + mk' S n∥ = ∥mk' S (m +  n)∥ : by rw (mk' S).map_add
  ... ≤ ∥m + n∥ : quotient_norm_mk_le S (m + n)
  ... ≤ ∥m∥ + ∥n∥ : norm_add_le _ _
  ... ≤ ∥mk' S m∥ + ∥mk' S n∥ + ε : by linarith
end

/-- The quotient norm of `0` is `0`. -/
lemma norm_mk_zero (S : add_subgroup M) : ∥(0 : M ⧸ S)∥ = 0 :=
begin
  erw quotient_norm_eq_zero_iff,
  exact subset_closure S.zero_mem
end

/-- If `(m : M)` has norm equal to `0` in `M ⧸ S` for a closed subgroup `S` of `M`, then
`m ∈ S`. -/
lemma norm_zero_eq_zero (S : add_subgroup M) (hS : is_closed (S : set M)) (m : M)
  (h : ∥mk' S m∥ = 0) : m ∈ S :=
by rwa [quotient_norm_eq_zero_iff, hS.closure_eq] at h

lemma quotient_nhd_basis (S : add_subgroup M) :
  (𝓝 (0 : M ⧸ S)).has_basis (λ ε : ℝ, 0 < ε) (λ ε, {x | ∥x∥ < ε}) :=
⟨begin
  intros U,
  split,
  { intros U_in,
    rw ← (mk' S).map_zero at U_in,
    have := preimage_nhds_coinduced U_in,
    rcases metric.mem_nhds_iff.mp this with ⟨ε, ε_pos, H⟩,
    use [ε/2, half_pos ε_pos],
    intros x x_in,
    dsimp at x_in,
    rcases norm_mk_lt x (half_pos ε_pos) with ⟨y, rfl, ry⟩,
    apply H,
    rw ball_zero_eq,
    dsimp,
    linarith },
  { rintros ⟨ε, ε_pos, h⟩,
    have : (mk' S) '' (ball (0 : M) ε) ⊆ {x | ∥x∥ < ε},
    { rintros - ⟨x, x_in, rfl⟩,
      rw mem_ball_zero_iff at x_in,
      exact lt_of_le_of_lt (quotient_norm_mk_le S x) x_in },
    apply filter.mem_of_superset _ (set.subset.trans this h),
    clear h U this,
    apply is_open.mem_nhds,
    { change is_open ((mk' S) ⁻¹' _),
      erw quotient_add_group.preimage_image_coe,
      apply is_open_Union,
      rintros ⟨s, s_in⟩,
      exact (continuous_add_right s).is_open_preimage _ is_open_ball },
    { exact ⟨(0 : M), mem_ball_self ε_pos, (mk' S).map_zero⟩ } },
end⟩

/-- The seminormed group structure on the quotient by an additive subgroup. -/
noncomputable
instance add_subgroup.semi_normed_group_quotient (S : add_subgroup M) :
  semi_normed_group (M ⧸ S) :=
{ dist               := λ x y, ∥x - y∥,
  dist_self          := λ x, by simp only [norm_mk_zero, sub_self],
  dist_comm          := quotient_norm_sub_rev,
  dist_triangle      := λ x y z,
  begin
    unfold dist,
    have : x - z = (x - y) + (y - z) := by abel,
    rw this,
    exact quotient_norm_add_le S (x - y) (y - z)
  end,
  dist_eq := λ x y, rfl,
  to_uniform_space   := topological_add_group.to_uniform_space (M ⧸ S),
  uniformity_dist    :=
  begin
    rw uniformity_eq_comap_nhds_zero',
    have := (quotient_nhd_basis S).comap (λ (p : (M ⧸ S) × M ⧸ S), p.2 - p.1),
    apply this.eq_of_same_basis,
    have : ∀ ε : ℝ, (λ (p : (M ⧸ S) × M ⧸ S), p.snd - p.fst) ⁻¹' {x | ∥x∥ < ε} =
      {p : (M ⧸ S) × M ⧸ S | ∥p.fst - p.snd∥ < ε},
    { intro ε,
      ext x,
      dsimp,
      rw quotient_norm_sub_rev },
    rw funext this,
    refine filter.has_basis_binfi_principal _ set.nonempty_Ioi,
    rintros ε (ε_pos : 0 < ε) η (η_pos : 0 < η),
    refine ⟨min ε η, lt_min ε_pos η_pos, _, _⟩,
    { suffices : ∀ (a b : M ⧸ S), ∥a - b∥ < ε → ∥a - b∥ < η → ∥a - b∥ < ε, by simpa,
      exact λ a b h h', h },
    { simp }
  end }

-- This is a sanity check left here on purpose to ensure that potential refactors won't destroy
-- this important property.
example (S : add_subgroup M) : (quotient.topological_space : topological_space $ M ⧸ S) =
S.semi_normed_group_quotient.to_uniform_space.to_topological_space :=
rfl

/-- The quotient in the category of normed groups. -/
noncomputable
instance add_subgroup.normed_group_quotient (S : add_subgroup M) [hS : is_closed (S : set M)] :
  normed_group (M ⧸ S) :=
{ eq_of_dist_eq_zero :=
  begin
    rintros ⟨m⟩ ⟨m'⟩ (h : ∥mk' S m - mk' S m'∥ = 0),
    erw [← (mk' S).map_sub, quotient_norm_eq_zero_iff, hS.closure_eq,
         ← quotient_add_group.eq_iff_sub_mem] at h,
    exact h
  end,
  .. add_subgroup.semi_normed_group_quotient S }

-- This is a sanity check left here on purpose to ensure that potential refactors won't destroy
-- this important property.
example (S : add_subgroup M) [is_closed (S : set M)] :
  S.semi_normed_group_quotient = normed_group.to_semi_normed_group := rfl

namespace add_subgroup

open normed_group_hom

/-- The morphism from a seminormed group to the quotient by a subgroup. -/
noncomputable
def normed_mk (S : add_subgroup M) : normed_group_hom M (M ⧸ S) :=
{ bound' := ⟨1, λ m, by simpa [one_mul] using quotient_norm_mk_le  _ m⟩,
  .. quotient_add_group.mk' S }

/-- `S.normed_mk` agrees with `quotient_add_group.mk' S`. -/
@[simp]
lemma normed_mk.apply (S : add_subgroup M) (m : M) : normed_mk S m = quotient_add_group.mk' S m :=
rfl

/-- `S.normed_mk` is surjective. -/
lemma surjective_normed_mk (S : add_subgroup M) : function.surjective (normed_mk S) :=
surjective_quot_mk _

/-- The kernel of `S.normed_mk` is `S`. -/
lemma ker_normed_mk (S : add_subgroup M) : S.normed_mk.ker = S :=
quotient_add_group.ker_mk  _

/-- The operator norm of the projection is at most `1`. -/
lemma norm_normed_mk_le (S : add_subgroup M) : ∥S.normed_mk∥ ≤ 1 :=
normed_group_hom.op_norm_le_bound _ zero_le_one (λ m, by simp [quotient_norm_mk_le'])

/-- The operator norm of the projection is `1` if the subspace is not dense. -/
lemma norm_normed_mk (S : add_subgroup M) (h : (S.topological_closure : set M) ≠ univ) :
  ∥S.normed_mk∥ = 1 :=
begin
  obtain ⟨x, hx⟩ := set.nonempty_compl.2 h,
  let y := S.normed_mk x,
  have hy : ∥y∥ ≠ 0,
  { intro h0,
    exact set.not_mem_of_mem_compl hx ((quotient_norm_eq_zero_iff S x).1 h0) },
  refine le_antisymm (norm_normed_mk_le S) (le_of_forall_pos_le_add (λ ε hε, _)),
  suffices : 1 ≤ ∥S.normed_mk∥ + min ε ((1 : ℝ)/2),
  { exact le_add_of_le_add_left this (min_le_left ε ((1 : ℝ)/2)) },
  have hδ := sub_pos.mpr (lt_of_le_of_lt (min_le_right ε ((1 : ℝ)/2)) one_half_lt_one),
  have hδpos : 0 < min ε ((1 : ℝ)/2) := lt_min hε one_half_pos,
  have hδnorm := mul_pos (div_pos hδpos hδ) (lt_of_le_of_ne (norm_nonneg y) hy.symm),
  obtain ⟨m, hm, hlt⟩ := norm_mk_lt y hδnorm,
  have hrw : ∥y∥ + min ε (1 / 2) / (1 - min ε (1 / 2)) * ∥y∥ =
    ∥y∥ * (1 + min ε (1 / 2) / (1 - min ε (1 / 2))) := by ring,
  rw [hrw] at hlt,
  have hm0 : ∥m∥ ≠ 0,
  { intro h0,
    have hnorm := quotient_norm_mk_le S m,
    rw [h0, hm] at hnorm,
    replace hnorm := le_antisymm hnorm (norm_nonneg _),
    simpa [hnorm] using hy },
  replace hlt := (div_lt_div_right (lt_of_le_of_ne (norm_nonneg m) hm0.symm)).2 hlt,
  simp only [hm0, div_self, ne.def, not_false_iff] at hlt,
  have hrw₁ : ∥y∥ * (1 + min ε (1 / 2) / (1 - min ε (1 / 2))) / ∥m∥ =
    (∥y∥ / ∥m∥) * (1 + min ε (1 / 2) / (1 - min ε (1 / 2))) := by ring,
  rw [hrw₁] at hlt,
  replace hlt := (inv_pos_lt_iff_one_lt_mul (lt_trans (div_pos hδpos hδ) (lt_one_add _))).2 hlt,
  suffices : ∥S.normed_mk∥ ≥ 1 - min ε (1 / 2),
  { exact sub_le_iff_le_add.mp this },
  calc ∥S.normed_mk∥ ≥ ∥(S.normed_mk) m∥ / ∥m∥ : ratio_le_op_norm S.normed_mk m
  ...  = ∥y∥ / ∥m∥ : by rw [normed_mk.apply, hm]
  ... ≥ (1 + min ε (1 / 2) / (1 - min ε (1 / 2)))⁻¹ : le_of_lt hlt
  ... = 1 - min ε (1 / 2) : by field_simp [(ne_of_lt hδ).symm]
end

/-- The operator norm of the projection is `0` if the subspace is dense. -/
lemma norm_trivial_quotient_mk (S : add_subgroup M)
  (h : (S.topological_closure : set M) = set.univ) : ∥S.normed_mk∥ = 0 :=
begin
  refine le_antisymm (op_norm_le_bound _ (le_refl _) (λ x, _)) (norm_nonneg _),
  have hker : x ∈ (S.normed_mk).ker.topological_closure,
  { rw [S.ker_normed_mk],
    exact set.mem_of_eq_of_mem h trivial },
  rw [ker_normed_mk] at hker,
  simp only [(quotient_norm_eq_zero_iff S x).mpr hker, normed_mk.apply, zero_mul],
end

end add_subgroup

namespace normed_group_hom

/-- `is_quotient f`, for `f : M ⟶ N` means that `N` is isomorphic to the quotient of `M`
by the kernel of `f`. -/
structure is_quotient (f : normed_group_hom M N) : Prop :=
(surjective : function.surjective f)
(norm : ∀ x, ∥f x∥ = Inf ((λ m, ∥x + m∥) '' f.ker))

/-- Given  `f : normed_group_hom M N` such that `f s = 0` for all `s ∈ S`, where,
`S : add_subgroup M` is closed, the induced morphism `normed_group_hom (M ⧸ S) N`. -/
noncomputable
def lift {N : Type*} [semi_normed_group N] (S : add_subgroup M)
  (f : normed_group_hom M N) (hf : ∀ s ∈ S, f s = 0) :
  normed_group_hom (M ⧸ S) N :=
{ bound' :=
  begin
    obtain ⟨c : ℝ, hcpos : (0 : ℝ) < c, hc : ∀ x, ∥f x∥ ≤ c * ∥x∥⟩ := f.bound,
    refine ⟨c, λ mbar, le_of_forall_pos_le_add (λ ε hε, _)⟩,
    obtain ⟨m : M, rfl : mk' S m = mbar, hmnorm : ∥m∥ < ∥mk' S m∥ + ε/c⟩ :=
      norm_mk_lt mbar (div_pos hε hcpos),
    calc ∥f m∥ ≤ c * ∥m∥ : hc m
    ... ≤ c*(∥mk' S m∥ + ε/c) : ((mul_lt_mul_left hcpos).mpr hmnorm).le
    ... = c * ∥mk' S m∥ + ε : by rw [mul_add, mul_div_cancel' _ hcpos.ne.symm]
  end,
  .. quotient_add_group.lift S f.to_add_monoid_hom hf }

lemma lift_mk {N : Type*} [semi_normed_group N] (S : add_subgroup M)
  (f : normed_group_hom M N) (hf : ∀ s ∈ S, f s = 0) (m : M) :
  lift S f hf (S.normed_mk m) = f m := rfl

lemma lift_unique {N : Type*} [semi_normed_group N] (S : add_subgroup M)
  (f : normed_group_hom M N) (hf : ∀ s ∈ S, f s = 0)
  (g : normed_group_hom (M ⧸ S) N) :
  g.comp (S.normed_mk) = f → g = lift S f hf :=
begin
  intro h,
  ext,
  rcases add_subgroup.surjective_normed_mk _ x with ⟨x,rfl⟩,
  change (g.comp (S.normed_mk) x) = _,
  simpa only [h]
end

/-- `S.normed_mk` satisfies `is_quotient`. -/
lemma is_quotient_quotient (S : add_subgroup M) : is_quotient (S.normed_mk) :=
⟨S.surjective_normed_mk, λ m, by simpa [S.ker_normed_mk] using quotient_norm_mk_eq _ m⟩

lemma is_quotient.norm_lift {f : normed_group_hom M N} (hquot : is_quotient f) {ε : ℝ} (hε : 0 < ε)
  (n : N) : ∃ (m : M), f m = n ∧ ∥m∥ < ∥n∥ + ε :=
begin
  obtain ⟨m, rfl⟩ := hquot.surjective n,
  have nonemp : ((λ m', ∥m + m'∥) '' f.ker).nonempty,
  { rw set.nonempty_image_iff,
    exact ⟨0, f.ker.zero_mem⟩ },
  rcases real.lt_Inf_add_pos nonemp hε with
    ⟨_, ⟨⟨x, hx, rfl⟩, H : ∥m + x∥ < Inf ((λ (m' : M), ∥m + m'∥) '' f.ker) + ε⟩⟩,
  exact ⟨m+x, by rw [f.map_add,(normed_group_hom.mem_ker f x).mp hx, add_zero],
               by rwa hquot.norm⟩,
end

lemma is_quotient.norm_le {f : normed_group_hom M N} (hquot : is_quotient f) (m : M) :
  ∥f m∥ ≤ ∥m∥ :=
begin
  rw hquot.norm,
  apply cInf_le,
  { use 0,
    rintros _ ⟨m', hm', rfl⟩,
    apply norm_nonneg },
  { exact ⟨0, f.ker.zero_mem, by simp⟩ }
end

lemma lift_norm_le {N : Type*} [semi_normed_group N] (S : add_subgroup M)
  (f : normed_group_hom M N) (hf : ∀ s ∈ S, f s = 0)
  {c : ℝ≥0} (fb : ∥f∥ ≤ c) :
  ∥lift S f hf∥ ≤ c :=
begin
  apply op_norm_le_bound _ c.coe_nonneg,
  intros x,
  by_cases hc : c = 0,
  { simp only [hc, nnreal.coe_zero, zero_mul] at fb ⊢,
    obtain ⟨x, rfl⟩ := surjective_quot_mk _ x,
    show ∥f x∥ ≤ 0,
    calc ∥f x∥ ≤ 0 * ∥x∥ : f.le_of_op_norm_le fb x
          ... = 0 : zero_mul _ },
  { replace hc : 0 < c := pos_iff_ne_zero.mpr hc,
    apply le_of_forall_pos_le_add,
    intros ε hε,
    have aux : 0 < (ε / c) := div_pos hε hc,
    obtain ⟨x, rfl, Hx⟩ : ∃ x', S.normed_mk x' = x ∧ ∥x'∥ < ∥x∥ + (ε / c) :=
      (is_quotient_quotient _).norm_lift aux _,
    rw lift_mk,
    calc ∥f x∥ ≤ c * ∥x∥ : f.le_of_op_norm_le fb x
          ... ≤ c * (∥S.normed_mk x∥ + ε / c) : (mul_le_mul_left _).mpr Hx.le
          ... = c * _ + ε : _,
    { exact_mod_cast hc },
    { rw [mul_add, mul_div_cancel'], exact_mod_cast hc.ne' } },
end

lemma lift_norm_noninc {N : Type*} [semi_normed_group N] (S : add_subgroup M)
  (f : normed_group_hom M N) (hf : ∀ s ∈ S, f s = 0)
  (fb : f.norm_noninc) :
  (lift S f hf).norm_noninc :=
λ x,
begin
  have fb' : ∥f∥ ≤ (1 : ℝ≥0) := norm_noninc.norm_noninc_iff_norm_le_one.mp fb,
  simpa using le_of_op_norm_le _ (f.lift_norm_le _ _ fb') _,
end

end normed_group_hom


/-
Copyright (c) 2021 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä
-/
import topology.algebra.module

/-!
# Weak dual topology

This file defines the weak-* topology on duals of suitable topological modules `E` over suitable
topological semirings `𝕜`. The (weak) dual consists of continuous linear functionals `E →L[𝕜] 𝕜`
from `E` to scalars `𝕜`. The weak-* topology is the coarsest topology on this dual
`weak_dual 𝕜 E := (E →L[𝕜] 𝕜)` w.r.t. which the evaluation maps at all `z : E` are continuous.

The weak dual is a module over `𝕜` if the semiring `𝕜` is commutative.

## Main definitions

The main definitions are the type `weak_dual 𝕜 E` and a topology instance on it.

* `weak_dual 𝕜 E` is a type synonym for `dual 𝕜 E` (when the latter is defined): both are equal to
  the type `E →L[𝕜] 𝕜` of continuous linear maps from a module `E` over `𝕜` to the ring `𝕜`.
* The instance `weak_dual.topological_space` is the weak-* topology on `weak_dual 𝕜 E`, i.e., the
  coarsest topology making the evaluation maps at all `z : E` continuous.

## Main results

We establish that `weak_dual 𝕜 E` has the following structure:
* `weak_dual.has_continuous_add`: The addition in `weak_dual 𝕜 E` is continuous.
* `weak_dual.module`: If the scalars `𝕜` are a commutative semiring, then `weak_dual 𝕜 E` is a
  module over `𝕜`.
* `weak_dual.has_continuous_smul`: If the scalars `𝕜` are a commutative semiring, then the scalar
  multiplication by `𝕜` in `weak_dual 𝕜 E` is continuous.

We prove the following results characterizing the weak-* topology:
* `weak_dual.eval_continuous`: For any `z : E`, the evaluation mapping `weak_dual 𝕜 E → 𝕜` taking
  `x'`to `x' z` is continuous.
* `weak_dual.continuous_of_continuous_eval`: For a mapping to `weak_dual 𝕜 E` to be continuous,
  it suffices that its compositions with evaluations at all points `z : E` are continuous.
* `weak_dual.tendsto_iff_forall_eval_tendsto`: Convergence in `weak_dual 𝕜 E` can be characterized
  in terms of convergence of the evaluations at all points `z : E`.

## Notations

No new notation is introduced.

## Implementation notes

The weak-* topology is defined as the induced topology under the mapping that associates to a dual
element `x'` the functional `E → 𝕜`, when the space `E → 𝕜` of functionals is equipped with the
topology of pointwise convergence (product topology).

Typically one might assume that `𝕜` is a topological semiring in the sense of the typeclasses
`topological_space 𝕜`, `semiring 𝕜`, `has_continuous_add 𝕜`, `has_continuous_mul 𝕜`,
and that the space `E` is a topological module over `𝕜` in the sense of the typeclasses
`topological_space E`, `add_comm_monoid E`, `has_continuous_add E`, `module 𝕜 E`,
`has_continuous_smul 𝕜 E`. The definitions and results are, however, given with weaker assumptions
when possible.

## References

* https://en.wikipedia.org/wiki/Weak_topology#Weak-*_topology

## Tags

weak-star, weak dual

-/

noncomputable theory
open filter
open_locale topological_space

universes u v

section weak_star_topology
/-!
### Weak star topology on duals of topological modules
-/

variables (𝕜 : Type*) [topological_space 𝕜] [semiring 𝕜]
variables (E : Type*) [topological_space E] [add_comm_monoid E] [module 𝕜 E]

/-- The weak dual of a topological module `E` over a topological semiring `𝕜` consists of
continuous linear functionals from `E` to scalars `𝕜`. It is a type synonym with the usual dual
(when the latter is defined), but will be equipped with a different topology. -/
@[derive [inhabited, λ α, has_coe_to_fun α (λ _, E → 𝕜)]]
def weak_dual := E →L[𝕜] 𝕜

instance [has_continuous_add 𝕜] : add_comm_monoid (weak_dual 𝕜 E) :=
continuous_linear_map.add_comm_monoid

namespace weak_dual

/-- The weak-* topology instance `weak_dual.topological_space` on the dual of a topological module
`E` over a topological semiring `𝕜` is defined as the induced topology under the mapping that
associates to a dual element `x' : weak_dual 𝕜 E` the functional `E → 𝕜`, when the space `E → 𝕜`
of functionals is equipped with the topology of pointwise convergence (product topology). -/
instance : topological_space (weak_dual 𝕜 E) :=
topological_space.induced (λ x' : weak_dual 𝕜 E, λ z : E, x' z) Pi.topological_space

lemma coe_fn_continuous :
  continuous (λ (x' : (weak_dual 𝕜 E)), (λ (z : E), x' z)) :=
continuous_induced_dom

lemma eval_continuous (z : E) : continuous (λ (x' : weak_dual 𝕜 E), x' z) :=
(continuous_pi_iff.mp (coe_fn_continuous 𝕜 E)) z

lemma continuous_of_continuous_eval {α : Type u} [topological_space α]
  {g : α → weak_dual 𝕜 E} (h : ∀ z, continuous (λ a, g a z)) : continuous g :=
continuous_induced_rng (continuous_pi_iff.mpr h)

theorem tendsto_iff_forall_eval_tendsto {γ : Type u} {F : filter γ}
  {ψs : γ → weak_dual 𝕜 E} {ψ : weak_dual 𝕜 E} :
  tendsto ψs F (𝓝 ψ) ↔ ∀ z : E, tendsto (λ i, ψs i z) F (𝓝 (ψ z)) :=
begin
  rw ← tendsto_pi_nhds,
  split,
  { intros weak_star_conv,
    exact (((coe_fn_continuous 𝕜 E).tendsto ψ).comp weak_star_conv), },
  { intro h_lim_forall,
    rwa [nhds_induced, tendsto_comap_iff], },
end

/-- Addition in `weak_dual 𝕜 E` is continuous. -/
instance [has_continuous_add 𝕜] : has_continuous_add (weak_dual 𝕜 E) :=
{ continuous_add := begin
    apply continuous_of_continuous_eval,
    intros z,
    have h : continuous (λ p : 𝕜 × 𝕜, p.1 + p.2) := continuous_add,
    exact h.comp ((eval_continuous 𝕜 E z).prod_map (eval_continuous 𝕜 E z)),
  end, }

/-- If the scalars `𝕜` are a commutative semiring, then `weak_dual 𝕜 E` is a module over `𝕜`. -/
instance (𝕜 : Type u) [topological_space 𝕜] [comm_semiring 𝕜]
  [has_continuous_add 𝕜] [has_continuous_mul 𝕜]
  (E : Type*) [topological_space E] [add_comm_group E] [module 𝕜 E] :
  module 𝕜 (weak_dual 𝕜 E) :=
continuous_linear_map.module

/-- Scalar multiplication in `weak_dual 𝕜 E` is continuous (when `𝕜` is a commutative
semiring). -/
instance (𝕜 : Type u) [topological_space 𝕜] [comm_semiring 𝕜]
  [has_continuous_add 𝕜] [has_continuous_mul 𝕜]
  (E : Type*) [topological_space E] [add_comm_group E]
  [module 𝕜 E] :
  has_continuous_smul 𝕜 (weak_dual 𝕜 E) :=
{ continuous_smul := begin
    apply continuous_of_continuous_eval,
    intros z,
    have h : continuous (λ p : 𝕜 × 𝕜, p.1 * p.2) := continuous_mul,
    exact h.comp ((continuous_id').prod_map (eval_continuous 𝕜 E z)),
  end, }

end weak_dual

end weak_star_topology


/-
Copyright (c) 2022 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers, Heather Macbeth

! This file was ported from Lean 3 source module geometry.euclidean.angle.oriented.rotation
! leanprover-community/mathlib commit 46b633fd842bef9469441c0209906f6dddd2b4f5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Geometry.Euclidean.Angle.Oriented.Basic

/-!
# Rotations by oriented angles.

This file defines rotations by oriented angles in real inner product spaces.

## Main definitions

* `orientation.rotation` is the rotation by an oriented angle with respect to an orientation.

-/

noncomputable section

open FiniteDimensional Complex

open Real RealInnerProductSpace ComplexConjugate

namespace Orientation

attribute [local instance] fact_finite_dimensional_of_finrank_eq_succ

attribute [local instance] Complex.finrank_real_complex_fact

variable {V V' : Type _}

variable [NormedAddCommGroup V] [NormedAddCommGroup V']

variable [InnerProductSpace ℝ V] [InnerProductSpace ℝ V']

variable [Fact (finrank ℝ V = 2)] [Fact (finrank ℝ V' = 2)] (o : Orientation ℝ V (Fin 2))

-- mathport name: exprJ
local notation "J" => o.rightAngleRotation

/-- Auxiliary construction to build a rotation by the oriented angle `θ`. -/
def rotationAux (θ : Real.Angle) : V →ₗᵢ[ℝ] V :=
  LinearMap.isometryOfInner
    (Real.Angle.cos θ • LinearMap.id + Real.Angle.sin θ • ↑(LinearIsometryEquiv.toLinearEquiv J))
    (by
      intro x y
      simp only [IsROrC.conj_to_real, id.def, LinearMap.smul_apply, LinearMap.add_apply,
        LinearMap.id_coe, LinearEquiv.coe_coe, LinearIsometryEquiv.coe_toLinearEquiv,
        Orientation.areaForm_rightAngleRotation_left, Orientation.inner_rightAngleRotation_left,
        Orientation.inner_rightAngleRotation_right, inner_add_left, inner_smul_left,
        inner_add_right, inner_smul_right]
      linear_combination inner x y * θ.cos_sq_add_sin_sq)
#align orientation.rotation_aux Orientation.rotationAux

@[simp]
theorem rotationAux_apply (θ : Real.Angle) (x : V) :
    o.rotationAux θ x = Real.Angle.cos θ • x + Real.Angle.sin θ • J x :=
  rfl
#align orientation.rotation_aux_apply Orientation.rotationAux_apply

/-- A rotation by the oriented angle `θ`. -/
def rotation (θ : Real.Angle) : V ≃ₗᵢ[ℝ] V :=
  LinearIsometryEquiv.ofLinearIsometry (o.rotationAux θ)
    (Real.Angle.cos θ • LinearMap.id - Real.Angle.sin θ • ↑(LinearIsometryEquiv.toLinearEquiv J))
    (by
      ext x
      convert congr_arg (fun t : ℝ => t • x) θ.cos_sq_add_sin_sq using 1
      · simp only [o.right_angle_rotation_right_angle_rotation, o.rotation_aux_apply,
          Function.comp_apply, id.def, LinearEquiv.coe_coe, LinearIsometry.coe_toLinearMap,
          LinearIsometryEquiv.coe_toLinearEquiv, map_smul, map_sub, LinearMap.coe_comp,
          LinearMap.id_coe, LinearMap.smul_apply, LinearMap.sub_apply, ← mul_smul, add_smul,
          smul_add, smul_neg, smul_sub, mul_comm, sq]
        abel
      · simp)
    (by
      ext x
      convert congr_arg (fun t : ℝ => t • x) θ.cos_sq_add_sin_sq using 1
      · simp only [o.right_angle_rotation_right_angle_rotation, o.rotation_aux_apply,
          Function.comp_apply, id.def, LinearEquiv.coe_coe, LinearIsometry.coe_toLinearMap,
          LinearIsometryEquiv.coe_toLinearEquiv, map_add, map_smul, LinearMap.coe_comp,
          LinearMap.id_coe, LinearMap.smul_apply, LinearMap.sub_apply, add_smul, ← mul_smul,
          mul_comm, smul_add, smul_neg, sq]
        abel
      · simp)
#align orientation.rotation Orientation.rotation

theorem rotation_apply (θ : Real.Angle) (x : V) :
    o.rotation θ x = Real.Angle.cos θ • x + Real.Angle.sin θ • J x :=
  rfl
#align orientation.rotation_apply Orientation.rotation_apply

theorem rotation_symm_apply (θ : Real.Angle) (x : V) :
    (o.rotation θ).symm x = Real.Angle.cos θ • x - Real.Angle.sin θ • J x :=
  rfl
#align orientation.rotation_symm_apply Orientation.rotation_symm_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `«expr!![ » -/
/- ./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation -/
theorem rotation_eq_matrix_toLin (θ : Real.Angle) {x : V} (hx : x ≠ 0) :
    (o.rotation θ).toLinearMap =
      Matrix.toLin (o.basisRightAngleRotation x hx) (o.basisRightAngleRotation x hx)
        («expr!![ »
          "./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation") :=
  by
  apply (o.basis_right_angle_rotation x hx).ext
  intro i
  fin_cases i
  · rw [Matrix.toLin_self]
    simp [rotation_apply, Fin.sum_univ_succ]
  · rw [Matrix.toLin_self]
    simp [rotation_apply, Fin.sum_univ_succ, add_comm]
#align orientation.rotation_eq_matrix_to_lin Orientation.rotation_eq_matrix_toLin

/-- The determinant of `rotation` (as a linear map) is equal to `1`. -/
@[simp]
theorem det_rotation (θ : Real.Angle) : (o.rotation θ).toLinearMap.det = 1 :=
  by
  haveI : Nontrivial V :=
    FiniteDimensional.nontrivial_of_finrank_eq_succ (Fact.out (finrank ℝ V = 2))
  obtain ⟨x, hx⟩ : ∃ x, x ≠ (0 : V) := exists_ne (0 : V)
  rw [o.rotation_eq_matrix_to_lin θ hx]
  simpa [sq] using θ.cos_sq_add_sin_sq
#align orientation.det_rotation Orientation.det_rotation

/-- The determinant of `rotation` (as a linear equiv) is equal to `1`. -/
@[simp]
theorem linearEquiv_det_rotation (θ : Real.Angle) : (o.rotation θ).toLinearEquiv.det = 1 :=
  Units.ext <| o.det_rotation θ
#align orientation.linear_equiv_det_rotation Orientation.linearEquiv_det_rotation

/-- The inverse of `rotation` is rotation by the negation of the angle. -/
@[simp]
theorem rotation_symm (θ : Real.Angle) : (o.rotation θ).symm = o.rotation (-θ) := by
  ext <;> simp [o.rotation_apply, o.rotation_symm_apply, sub_eq_add_neg]
#align orientation.rotation_symm Orientation.rotation_symm

/-- Rotation by 0 is the identity. -/
@[simp]
theorem rotation_zero : o.rotation 0 = LinearIsometryEquiv.refl ℝ V := by ext <;> simp [rotation]
#align orientation.rotation_zero Orientation.rotation_zero

/-- Rotation by π is negation. -/
@[simp]
theorem rotation_pi : o.rotation π = LinearIsometryEquiv.neg ℝ :=
  by
  ext x
  simp [rotation]
#align orientation.rotation_pi Orientation.rotation_pi

/-- Rotation by π is negation. -/
theorem rotation_pi_apply (x : V) : o.rotation π x = -x := by simp
#align orientation.rotation_pi_apply Orientation.rotation_pi_apply

/-- Rotation by π / 2 is the "right-angle-rotation" map `J`. -/
theorem rotation_pi_div_two : o.rotation (π / 2 : ℝ) = J :=
  by
  ext x
  simp [rotation]
#align orientation.rotation_pi_div_two Orientation.rotation_pi_div_two

/-- Rotating twice is equivalent to rotating by the sum of the angles. -/
@[simp]
theorem rotation_rotation (θ₁ θ₂ : Real.Angle) (x : V) :
    o.rotation θ₁ (o.rotation θ₂ x) = o.rotation (θ₁ + θ₂) x :=
  by
  simp only [o.rotation_apply, ← mul_smul, Real.Angle.cos_add, Real.Angle.sin_add, add_smul,
    sub_smul, LinearIsometryEquiv.trans_apply, smul_add, LinearIsometryEquiv.map_add,
    LinearIsometryEquiv.map_smul, right_angle_rotation_right_angle_rotation, smul_neg]
  ring_nf
  abel
#align orientation.rotation_rotation Orientation.rotation_rotation

/-- Rotating twice is equivalent to rotating by the sum of the angles. -/
@[simp]
theorem rotation_trans (θ₁ θ₂ : Real.Angle) :
    (o.rotation θ₁).trans (o.rotation θ₂) = o.rotation (θ₂ + θ₁) :=
  LinearIsometryEquiv.ext fun _ => by rw [← rotation_rotation, LinearIsometryEquiv.trans_apply]
#align orientation.rotation_trans Orientation.rotation_trans

/-- Rotating the first of two vectors by `θ` scales their Kahler form by `cos θ - sin θ * I`. -/
@[simp]
theorem kahler_rotation_left (x y : V) (θ : Real.Angle) :
    o.kahler (o.rotation θ x) y = conj (θ.expMapCircle : ℂ) * o.kahler x y :=
  by
  simp only [o.rotation_apply, map_add, map_mul, LinearMap.map_smulₛₗ, RingHom.id_apply,
    LinearMap.add_apply, LinearMap.smul_apply, real_smul, kahler_right_angle_rotation_left,
    Real.Angle.coe_expMapCircle, IsROrC.conj_of_real, conj_I]
  ring
#align orientation.kahler_rotation_left Orientation.kahler_rotation_left

/-- Negating a rotation is equivalent to rotation by π plus the angle. -/
theorem neg_rotation (θ : Real.Angle) (x : V) : -o.rotation θ x = o.rotation (π + θ) x := by
  rw [← o.rotation_pi_apply, rotation_rotation]
#align orientation.neg_rotation Orientation.neg_rotation

/-- Negating a rotation by -π / 2 is equivalent to rotation by π / 2. -/
@[simp]
theorem neg_rotation_neg_pi_div_two (x : V) :
    -o.rotation (-π / 2 : ℝ) x = o.rotation (π / 2 : ℝ) x := by
  rw [neg_rotation, ← Real.Angle.coe_add, neg_div, ← sub_eq_add_neg, sub_half]
#align orientation.neg_rotation_neg_pi_div_two Orientation.neg_rotation_neg_pi_div_two

/-- Negating a rotation by π / 2 is equivalent to rotation by -π / 2. -/
theorem neg_rotation_pi_div_two (x : V) : -o.rotation (π / 2 : ℝ) x = o.rotation (-π / 2 : ℝ) x :=
  (neg_eq_iff_eq_neg.mp <| o.neg_rotation_neg_pi_div_two _).symm
#align orientation.neg_rotation_pi_div_two Orientation.neg_rotation_pi_div_two

/-- Rotating the first of two vectors by `θ` scales their Kahler form by `cos (-θ) + sin (-θ) * I`.
-/
theorem kahler_rotation_left' (x y : V) (θ : Real.Angle) :
    o.kahler (o.rotation θ x) y = (-θ).expMapCircle * o.kahler x y := by
  simpa [coe_inv_circle_eq_conj, -kahler_rotation_left] using o.kahler_rotation_left x y θ
#align orientation.kahler_rotation_left' Orientation.kahler_rotation_left'

/-- Rotating the second of two vectors by `θ` scales their Kahler form by `cos θ + sin θ * I`. -/
@[simp]
theorem kahler_rotation_right (x y : V) (θ : Real.Angle) :
    o.kahler x (o.rotation θ y) = θ.expMapCircle * o.kahler x y :=
  by
  simp only [o.rotation_apply, map_add, LinearMap.map_smulₛₗ, RingHom.id_apply, real_smul,
    kahler_right_angle_rotation_right, Real.Angle.coe_expMapCircle]
  ring
#align orientation.kahler_rotation_right Orientation.kahler_rotation_right

/-- Rotating the first vector by `θ` subtracts `θ` from the angle between two vectors. -/
@[simp]
theorem oangle_rotation_left {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : Real.Angle) :
    o.oangle (o.rotation θ x) y = o.oangle x y - θ :=
  by
  simp only [oangle, o.kahler_rotation_left']
  rw [Complex.arg_mul_coe_angle, Real.Angle.arg_expMapCircle]
  · abel
  · exact ne_zero_of_mem_circle _
  · exact o.kahler_ne_zero hx hy
#align orientation.oangle_rotation_left Orientation.oangle_rotation_left

/-- Rotating the second vector by `θ` adds `θ` to the angle between two vectors. -/
@[simp]
theorem oangle_rotation_right {x y : V} (hx : x ≠ 0) (hy : y ≠ 0) (θ : Real.Angle) :
    o.oangle x (o.rotation θ y) = o.oangle x y + θ :=
  by
  simp only [oangle, o.kahler_rotation_right]
  rw [Complex.arg_mul_coe_angle, Real.Angle.arg_expMapCircle]
  · abel
  · exact ne_zero_of_mem_circle _
  · exact o.kahler_ne_zero hx hy
#align orientation.oangle_rotation_right Orientation.oangle_rotation_right

/-- The rotation of a vector by `θ` has an angle of `-θ` from that vector. -/
@[simp]
theorem oangle_rotation_self_left {x : V} (hx : x ≠ 0) (θ : Real.Angle) :
    o.oangle (o.rotation θ x) x = -θ := by simp [hx]
#align orientation.oangle_rotation_self_left Orientation.oangle_rotation_self_left

/-- A vector has an angle of `θ` from the rotation of that vector by `θ`. -/
@[simp]
theorem oangle_rotation_self_right {x : V} (hx : x ≠ 0) (θ : Real.Angle) :
    o.oangle x (o.rotation θ x) = θ := by simp [hx]
#align orientation.oangle_rotation_self_right Orientation.oangle_rotation_self_right

/-- Rotating the first vector by the angle between the two vectors results an an angle of 0. -/
@[simp]
theorem oangle_rotation_oangle_left (x y : V) : o.oangle (o.rotation (o.oangle x y) x) y = 0 :=
  by
  by_cases hx : x = 0
  · simp [hx]
  · by_cases hy : y = 0
    · simp [hy]
    · simp [hx, hy]
#align orientation.oangle_rotation_oangle_left Orientation.oangle_rotation_oangle_left

/-- Rotating the first vector by the angle between the two vectors and swapping the vectors
results an an angle of 0. -/
@[simp]
theorem oangle_rotation_oangle_right (x y : V) : o.oangle y (o.rotation (o.oangle x y) x) = 0 :=
  by
  rw [oangle_rev]
  simp
#align orientation.oangle_rotation_oangle_right Orientation.oangle_rotation_oangle_right

/-- Rotating both vectors by the same angle does not change the angle between those vectors. -/
@[simp]
theorem oangle_rotation (x y : V) (θ : Real.Angle) :
    o.oangle (o.rotation θ x) (o.rotation θ y) = o.oangle x y := by
  by_cases hx : x = 0 <;> by_cases hy : y = 0 <;> simp [hx, hy]
#align orientation.oangle_rotation Orientation.oangle_rotation

/-- A rotation of a nonzero vector equals that vector if and only if the angle is zero. -/
@[simp]
theorem rotation_eq_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : Real.Angle) :
    o.rotation θ x = x ↔ θ = 0 := by
  constructor
  · intro h
    rw [eq_comm]
    simpa [hx, h] using o.oangle_rotation_right hx hx θ
  · intro h
    simp [h]
#align orientation.rotation_eq_self_iff_angle_eq_zero Orientation.rotation_eq_self_iff_angle_eq_zero

/-- A nonzero vector equals a rotation of that vector if and only if the angle is zero. -/
@[simp]
theorem eq_rotation_self_iff_angle_eq_zero {x : V} (hx : x ≠ 0) (θ : Real.Angle) :
    x = o.rotation θ x ↔ θ = 0 := by rw [← o.rotation_eq_self_iff_angle_eq_zero hx, eq_comm]
#align orientation.eq_rotation_self_iff_angle_eq_zero Orientation.eq_rotation_self_iff_angle_eq_zero

/-- A rotation of a vector equals that vector if and only if the vector or the angle is zero. -/
theorem rotation_eq_self_iff (x : V) (θ : Real.Angle) : o.rotation θ x = x ↔ x = 0 ∨ θ = 0 := by
  by_cases h : x = 0 <;> simp [h]
#align orientation.rotation_eq_self_iff Orientation.rotation_eq_self_iff

/-- A vector equals a rotation of that vector if and only if the vector or the angle is zero. -/
theorem eq_rotation_self_iff (x : V) (θ : Real.Angle) : x = o.rotation θ x ↔ x = 0 ∨ θ = 0 := by
  rw [← rotation_eq_self_iff, eq_comm]
#align orientation.eq_rotation_self_iff Orientation.eq_rotation_self_iff

/-- Rotating a vector by the angle to another vector gives the second vector if and only if the
norms are equal. -/
@[simp]
theorem rotation_oangle_eq_iff_norm_eq (x y : V) : o.rotation (o.oangle x y) x = y ↔ ‖x‖ = ‖y‖ :=
  by
  constructor
  · intro h
    rw [← h, LinearIsometryEquiv.norm_map]
  · intro h
    rw [o.eq_iff_oangle_eq_zero_of_norm_eq] <;> simp [h]
#align orientation.rotation_oangle_eq_iff_norm_eq Orientation.rotation_oangle_eq_iff_norm_eq

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by the ratio of the norms. -/
theorem oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
    (θ : Real.Angle) : o.oangle x y = θ ↔ y = (‖y‖ / ‖x‖) • o.rotation θ x :=
  by
  have hp := div_pos (norm_pos_iff.2 hy) (norm_pos_iff.2 hx)
  constructor
  · rintro rfl
    rw [← LinearIsometryEquiv.map_smul, ← o.oangle_smul_left_of_pos x y hp, eq_comm,
      rotation_oangle_eq_iff_norm_eq, norm_smul, Real.norm_of_nonneg hp.le,
      div_mul_cancel _ (norm_ne_zero_iff.2 hx)]
  · intro hye
    rw [hye, o.oangle_smul_right_of_pos _ _ hp, o.oangle_rotation_self_right hx]
#align orientation.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero Orientation.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero

/-- The angle between two nonzero vectors is `θ` if and only if the second vector is the first
rotated by `θ` and scaled by a positive real. -/
theorem oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero {x y : V} (hx : x ≠ 0) (hy : y ≠ 0)
    (θ : Real.Angle) : o.oangle x y = θ ↔ ∃ r : ℝ, 0 < r ∧ y = r • o.rotation θ x :=
  by
  constructor
  · intro h
    rw [o.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero hx hy] at h
    exact ⟨‖y‖ / ‖x‖, div_pos (norm_pos_iff.2 hy) (norm_pos_iff.2 hx), h⟩
  · rintro ⟨r, hr, rfl⟩
    rw [o.oangle_smul_right_of_pos _ _ hr, o.oangle_rotation_self_right hx]
#align orientation.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero Orientation.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by the ratio of the norms, or `θ` and at least one of the
vectors are zero. -/
theorem oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero {x y : V} (θ : Real.Angle) :
    o.oangle x y = θ ↔ x ≠ 0 ∧ y ≠ 0 ∧ y = (‖y‖ / ‖x‖) • o.rotation θ x ∨ θ = 0 ∧ (x = 0 ∨ y = 0) :=
  by
  by_cases hx : x = 0
  · simp [hx, eq_comm]
  · by_cases hy : y = 0
    · simp [hy, eq_comm]
    · rw [o.oangle_eq_iff_eq_norm_div_norm_smul_rotation_of_ne_zero hx hy]
      simp [hx, hy]
#align orientation.oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero Orientation.oangle_eq_iff_eq_norm_div_norm_smul_rotation_or_eq_zero

/-- The angle between two vectors is `θ` if and only if they are nonzero and the second vector
is the first rotated by `θ` and scaled by a positive real, or `θ` and at least one of the
vectors are zero. -/
theorem oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero {x y : V} (θ : Real.Angle) :
    o.oangle x y = θ ↔
      (x ≠ 0 ∧ y ≠ 0 ∧ ∃ r : ℝ, 0 < r ∧ y = r • o.rotation θ x) ∨ θ = 0 ∧ (x = 0 ∨ y = 0) :=
  by
  by_cases hx : x = 0
  · simp [hx, eq_comm]
  · by_cases hy : y = 0
    · simp [hy, eq_comm]
    · rw [o.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero hx hy]
      simp [hx, hy]
#align orientation.oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero Orientation.oangle_eq_iff_eq_pos_smul_rotation_or_eq_zero

/-- Any linear isometric equivalence in `V` with positive determinant is `rotation`. -/
theorem exists_linearIsometryEquiv_eq_of_det_pos {f : V ≃ₗᵢ[ℝ] V}
    (hd : 0 < (f.toLinearEquiv : V →ₗ[ℝ] V).det) : ∃ θ : Real.Angle, f = o.rotation θ :=
  by
  haveI : Nontrivial V :=
    FiniteDimensional.nontrivial_of_finrank_eq_succ (Fact.out (finrank ℝ V = 2))
  obtain ⟨x, hx⟩ : ∃ x, x ≠ (0 : V) := exists_ne (0 : V)
  use o.oangle x (f x)
  apply LinearIsometryEquiv.toLinearEquiv_injective
  apply LinearEquiv.toLinearMap_injective
  apply (o.basis_right_angle_rotation x hx).ext
  intro i
  symm
  fin_cases i
  · simp
  have : o.oangle (J x) (f (J x)) = o.oangle x (f x) := by
    simp only [oangle, o.linear_isometry_equiv_comp_right_angle_rotation f hd,
      o.kahler_comp_right_angle_rotation]
  simp [← this]
#align orientation.exists_linear_isometry_equiv_eq_of_det_pos Orientation.exists_linearIsometryEquiv_eq_of_det_pos

theorem rotation_map (θ : Real.Angle) (f : V ≃ₗᵢ[ℝ] V') (x : V') :
    (Orientation.map (Fin 2) f.toLinearEquiv o).rotation θ x = f (o.rotation θ (f.symm x)) := by
  simp [rotation_apply, o.right_angle_rotation_map]
#align orientation.rotation_map Orientation.rotation_map

@[simp]
protected theorem Complex.rotation (θ : Real.Angle) (z : ℂ) :
    Complex.orientation.rotation θ z = θ.expMapCircle * z :=
  by
  simp only [rotation_apply, Complex.rightAngleRotation, Real.Angle.coe_expMapCircle, real_smul]
  ring
#align complex.rotation Complex.rotation

/-- Rotation in an oriented real inner product space of dimension 2 can be evaluated in terms of a
complex-number representation of the space. -/
theorem rotation_map_complex (θ : Real.Angle) (f : V ≃ₗᵢ[ℝ] ℂ)
    (hf : Orientation.map (Fin 2) f.toLinearEquiv o = Complex.orientation) (x : V) :
    f (o.rotation θ x) = θ.expMapCircle * f x :=
  by
  rw [← Complex.rotation, ← hf, o.rotation_map]
  simp
#align orientation.rotation_map_complex Orientation.rotation_map_complex

/-- Negating the orientation negates the angle in `rotation`. -/
theorem rotation_neg_orientation_eq_neg (θ : Real.Angle) : (-o).rotation θ = o.rotation (-θ) :=
  LinearIsometryEquiv.ext <| by simp [rotation_apply]
#align orientation.rotation_neg_orientation_eq_neg Orientation.rotation_neg_orientation_eq_neg

/-- The inner product between a `π / 2` rotation of a vector and that vector is zero. -/
@[simp]
theorem inner_rotation_pi_div_two_left (x : V) : ⟪o.rotation (π / 2 : ℝ) x, x⟫ = 0 := by
  rw [rotation_pi_div_two, inner_right_angle_rotation_self]
#align orientation.inner_rotation_pi_div_two_left Orientation.inner_rotation_pi_div_two_left

/-- The inner product between a vector and a `π / 2` rotation of that vector is zero. -/
@[simp]
theorem inner_rotation_pi_div_two_right (x : V) : ⟪x, o.rotation (π / 2 : ℝ) x⟫ = 0 := by
  rw [real_inner_comm, inner_rotation_pi_div_two_left]
#align orientation.inner_rotation_pi_div_two_right Orientation.inner_rotation_pi_div_two_right

/-- The inner product between a multiple of a `π / 2` rotation of a vector and that vector is
zero. -/
@[simp]
theorem inner_smul_rotation_pi_div_two_left (x : V) (r : ℝ) :
    ⟪r • o.rotation (π / 2 : ℝ) x, x⟫ = 0 := by
  rw [inner_smul_left, inner_rotation_pi_div_two_left, MulZeroClass.mul_zero]
#align orientation.inner_smul_rotation_pi_div_two_left Orientation.inner_smul_rotation_pi_div_two_left

/-- The inner product between a vector and a multiple of a `π / 2` rotation of that vector is
zero. -/
@[simp]
theorem inner_smul_rotation_pi_div_two_right (x : V) (r : ℝ) :
    ⟪x, r • o.rotation (π / 2 : ℝ) x⟫ = 0 := by
  rw [real_inner_comm, inner_smul_rotation_pi_div_two_left]
#align orientation.inner_smul_rotation_pi_div_two_right Orientation.inner_smul_rotation_pi_div_two_right

/-- The inner product between a `π / 2` rotation of a vector and a multiple of that vector is
zero. -/
@[simp]
theorem inner_rotation_pi_div_two_left_smul (x : V) (r : ℝ) :
    ⟪o.rotation (π / 2 : ℝ) x, r • x⟫ = 0 := by
  rw [inner_smul_right, inner_rotation_pi_div_two_left, MulZeroClass.mul_zero]
#align orientation.inner_rotation_pi_div_two_left_smul Orientation.inner_rotation_pi_div_two_left_smul

/-- The inner product between a multiple of a vector and a `π / 2` rotation of that vector is
zero. -/
@[simp]
theorem inner_rotation_pi_div_two_right_smul (x : V) (r : ℝ) :
    ⟪r • x, o.rotation (π / 2 : ℝ) x⟫ = 0 := by
  rw [real_inner_comm, inner_rotation_pi_div_two_left_smul]
#align orientation.inner_rotation_pi_div_two_right_smul Orientation.inner_rotation_pi_div_two_right_smul

/-- The inner product between a multiple of a `π / 2` rotation of a vector and a multiple of
that vector is zero. -/
@[simp]
theorem inner_smul_rotation_pi_div_two_smul_left (x : V) (r₁ r₂ : ℝ) :
    ⟪r₁ • o.rotation (π / 2 : ℝ) x, r₂ • x⟫ = 0 := by
  rw [inner_smul_right, inner_smul_rotation_pi_div_two_left, MulZeroClass.mul_zero]
#align orientation.inner_smul_rotation_pi_div_two_smul_left Orientation.inner_smul_rotation_pi_div_two_smul_left

/-- The inner product between a multiple of a vector and a multiple of a `π / 2` rotation of
that vector is zero. -/
@[simp]
theorem inner_smul_rotation_pi_div_two_smul_right (x : V) (r₁ r₂ : ℝ) :
    ⟪r₂ • x, r₁ • o.rotation (π / 2 : ℝ) x⟫ = 0 := by
  rw [real_inner_comm, inner_smul_rotation_pi_div_two_smul_left]
#align orientation.inner_smul_rotation_pi_div_two_smul_right Orientation.inner_smul_rotation_pi_div_two_smul_right

/-- The inner product between two vectors is zero if and only if the first vector is zero or
the second is a multiple of a `π / 2` rotation of that vector. -/
theorem inner_eq_zero_iff_eq_zero_or_eq_smul_rotation_pi_div_two {x y : V} :
    ⟪x, y⟫ = 0 ↔ x = 0 ∨ ∃ r : ℝ, r • o.rotation (π / 2 : ℝ) x = y :=
  by
  rw [← o.eq_zero_or_oangle_eq_iff_inner_eq_zero]
  refine' ⟨fun h => _, fun h => _⟩
  · rcases h with (rfl | rfl | h | h)
    · exact Or.inl rfl
    · exact Or.inr ⟨0, zero_smul _ _⟩
    · obtain ⟨r, hr, rfl⟩ :=
        (o.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero (o.left_ne_zero_of_oangle_eq_pi_div_two h)
              (o.right_ne_zero_of_oangle_eq_pi_div_two h) _).1
          h
      exact Or.inr ⟨r, rfl⟩
    · obtain ⟨r, hr, rfl⟩ :=
        (o.oangle_eq_iff_eq_pos_smul_rotation_of_ne_zero
              (o.left_ne_zero_of_oangle_eq_neg_pi_div_two h)
              (o.right_ne_zero_of_oangle_eq_neg_pi_div_two h) _).1
          h
      refine' Or.inr ⟨-r, _⟩
      rw [neg_smul, ← smul_neg, o.neg_rotation_pi_div_two]
  · rcases h with (rfl | ⟨r, rfl⟩)
    · exact Or.inl rfl
    · by_cases hx : x = 0
      · exact Or.inl hx
      rcases lt_trichotomy r 0 with (hr | rfl | hr)
      · refine' Or.inr (Or.inr (Or.inr _))
        rw [o.oangle_smul_right_of_neg _ _ hr, o.neg_rotation_pi_div_two,
          o.oangle_rotation_self_right hx]
      · exact Or.inr (Or.inl (zero_smul _ _))
      · refine' Or.inr (Or.inr (Or.inl _))
        rw [o.oangle_smul_right_of_pos _ _ hr, o.oangle_rotation_self_right hx]
#align orientation.inner_eq_zero_iff_eq_zero_or_eq_smul_rotation_pi_div_two Orientation.inner_eq_zero_iff_eq_zero_or_eq_smul_rotation_pi_div_two

end Orientation