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

## Lagrange's four square theorem

The main result in this file is `sum_four_squares`,
a proof that every natural number is the sum of four square numbers.

# Implementation Notes

The proof used is close to Lagrange's original proof.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.group_power.identities
import Mathlib.data.zmod.basic
import Mathlib.field_theory.finite.basic
import Mathlib.data.int.parity
import Mathlib.data.fintype.card
import Mathlib.PostPort

namespace Mathlib

namespace int

theorem sum_two_squares_of_two_mul_sum_two_squares {m : ℤ} {x : ℤ} {y : ℤ} (h : bit0 1 * m = x ^ bit0 1 + y ^ bit0 1) : m = ((x - y) / bit0 1) ^ bit0 1 + ((x + y) / bit0 1) ^ bit0 1 := sorry

theorem exists_sum_two_squares_add_one_eq_k (p : ℕ) [hp : fact (nat.prime p)] : ∃ (a : ℤ), ∃ (b : ℤ), ∃ (k : ℕ), a ^ bit0 1 + b ^ bit0 1 + 1 = ↑k * ↑p ∧ k < p := sorry

end int

namespace nat

theorem sum_four_squares (n : ℕ) : ∃ (a : ℕ), ∃ (b : ℕ), ∃ (c : ℕ), ∃ (d : ℕ), a ^ bit0 1 + b ^ bit0 1 + c ^ bit0 1 + d ^ bit0 1 = n := sorry


/-
Name:Justin Cai, jc5pz

This exam has 44 questions. Most are short, but you should
still move along. Don't get hung up for too long on any one
question. Most questions don't depend on other questions. A
good strategy is to make a pass over the exam, answer all of
the questions that are easy for you to answer, then repeat
this procedure with the remaining questions until you are
done or the time is up.

We will penalize late submissions at the rate of 3 points per
minute. Be sure to upload, check, and if necessary fix, your
submission well before the end of the exam period.
-/

/-
I.LOGIC
-/

/-
A. Types

A logic is a formal language. The logic of Lean is typed.
Every correctly formed term in Lean has a type. Lean also
supports the definition of new types. The terms (or values)
of a type are defined by its constructors. The next few
problems test your ability to define your own types in Lean,
including enumerated types, polymorphic types, and recursive
types.
-/

/- 1. Enumerated types [2 points].

Define a type, Food, with exactly the following values:
steak, chicken, applepie, cookie, salad.
-/
inductive Food : Type
| steak : Food
| chicken : Food
| applepie : Food
| cookie : Food
| salad : Food

/- 2. Polymorphic types [2 points]

Define a type, Box T, where T is any type, with just one
constructor, called mk, taking just one argument, of type T.
You can think of a value of this type as being like a box
containing the given value as its contents. Once you
define the type, define two values, the first called
aBoxedNat, containing the natural number, zero, and the
second called aBoxedString, containing the string, "Hello,
Lean!".
-/

inductive Box {T : Type}
| mk : T → Box

def aBoxedNat := Box.mk 0
def aBoxedString := Box.mk "Hello, Lean!"

/- 3. Recursive types [2 points]

Define a type called Tree_nat. A value of this type
represents a "tree" of natural numbers. Such a tree is either
empty, or it is a natural number and two smaller trees of the
same type. Your type will have two constructors. Call them
empty and mk. Once you define your type, give two example
values, the first (called eTree) being empty, and the second,
called aTree having the value 0 and two smaller trees, the
first empty and the second having the value 1 and two smaller
trees, both empty. Do not open the Tree_nat namespace.
-/

inductive Tree_nat : Type
| empty : Tree_nat
| mk : ℕ → Tree_nat → Tree_nat → Tree_nat
                            -- start adding constructors here
def eTree := Tree_nat.empty
def aTree :=
Tree_nat.mk 0 Tree_nat.empty (Tree_nat.mk 1 Tree_nat.empty Tree_nat.empty)

/- 4. [2 points]

Define a polymorphic type, Tree T, where T is any type.
Start with the partial solution we've provided. Note that T
is an explicit parameter of the polymorphic Tree type in this
definition. A value of this type represents a "tree" of
objects of type T. Such a tree is either empty, or it is a
value of type T and two smaller trees of the same Tree type.
Your type will have two constructors. Call them empty and mk.

Once you define your type, give two example values, called
eTree' and aTree', respectively. The first must be the empty
tree of natural numbers. The second must be a non-empty tree
with the value 0 and two smaller trees, the first empty and
the second having the value 1 and two empty trees.

Note: you will have to give an explicit type parameter when
you create an empty tree. The mk constructor won't require
one. It's a good idea to put each argument to a constructor
on a separate line. It's also a good idea to use parentheses
around tree construction expressions to make sure Lean knows
how to "parse" your logic.
-/

inductive Tree (T : Type): Type
| empty : Tree
| mk : T → Tree → Tree → Tree                            -- start adding constructors here

def eTree' := Tree.empty (nat)
def aTree' := Tree.mk 0 (Tree.empty(nat)) (Tree.mk 1 (Tree.empty(nat)) (Tree.empty(nat))

/- 5. [2 points]

What is the type of a function that takes a natural number
and a string, in that order, and that returns true if the
given string has the given natural number at its length and
that return false otherwise? Use function type notation here.

Answer:
ℕ → string → Prop
-/

/- B. Functions

The formal language of predicate logic allows for the use of
functions. Lean allows you to define new functions that you
can then use in logical expressions (and for ordinary
programming as well). This section of the exam tests your
ability to read, write, and reason about functions in
predicate logic.
-/

/- 6. [2 points]

Rewrite the following function using lambda-style notation.
Do this by filling in the holes in the second definition.
-/

def mult (n1 n2 : ℕ) : ℕ := n1 * n2

def mult_lambda_style : ℕ → ℕ → ℕ :=
    λ n1 n2,
        n1 * n2

/- 7. [3 points] Here we first remind you of the definition
of a type for representing lists of natural numbers. We open
the name space and give an example, representing the list [4,
3,2,1]. Now write a recursive function, reduce_list_sum, that
computes the sum of the values in a given list of natural
numbers. Define your function by cases, with one case for an
empty list (in which case the sum is zero), and one case for
a non-empty list, in which case the list has a natural number
at its head and a smaller list as its tail. When you've
gotten it right, the test case we provide will pass.
-/

-- type for representing lists of natural numbers
inductive list_nat : Type
| nil_nat : list_nat
| cons_nat : ℕ → list_nat → list_nat

-- open the namespace
open list_nat

-- a term representing the list, [4,3,2,1]
def list_test_case : list_nat :=
    cons_nat
        4
        (cons_nat
            3
            (cons_nat
                2
                (cons_nat
                    1
                    nil_nat
                )
            )
        )

-- Your function definition goes here
def list_nat_reduce : list_nat → ℕ
| nil_nat := 0
| (cons_nat n l) := n + list_nat_reduce l                       -- replace with your cases

-- test case
example : list_nat_reduce list_test_case = 10 := rfl

/- C. Propositions and Predicates

The language of predicate logic includes propositions and
predicates. Predicates can be thought of as propositions with
parameters. A predicate with no parameters left to fill in is
simply a proposition. In this section, we test your
understanding of basic concepts involving propositions and
predicates. We introduce a few assumptions to start with,
which we then use in the following questions.
-/

-- Assume there are people
axiom Person : Type

-- Assume there are people called Tom, Mary, etc.
axioms (Tom Mary Jane Hamid Yu : Person)

-- Assume being nice is a property that a person can have
axiom nice : Person → Prop

/-
Assume that one person can like another person. For example,
we'd write the proposition, Tom likes Mary, as (likes Tom
Mary).
-/
axiom likes : Person → Person → Prop

/--/
Write formal propositions in Lean for each of the following
informal propositions. Replace the holes (underscores) in the
following incomplete Lean definitions with your answers.
-/

-- 8. [1 point] Tom is nice
def prob_0 := nice Tom

-- 9. [1 point] If everyone is nice, Tom is nice
def prob_1 := (∀ P : Person, nice P) → nice Tom

-- 10. [2 points] Tom is nice and Mary is nice
def prob_2 := nice Tom ∧ nice Mary

-- 11. [2 points] Tom is not nice
def prob_3 := ¬(nice Tom)

-- 12. [2 points] Tom is nice or Hamid is nice
def prob_4 := nice Tom ∨ nice Hamid

/-
13. [2 points] One but not both of Tom and Hamid are nice
(use parentheses to make intent clear)
-/
def prob_5 := (nice Tom ∧ ¬nice Hamid) ∨ (nice Hamid ∧ ¬ nice Tom)

/-
14. [2 points] Everyone likes anyone who is nice (think in
terms of "if ... then ...")
-/
def prob_6 := ∀ P1: Person, nice P1 → ∀ P: Person, likes P P1

/-
15. [2 points] No one likes anyone who is not nice
(implications might help here)
-/
def prob_7 := ∀ P : Person, ¬nice P → ¬(∀ P1: Person, likes P1 P)

-- 16. [2 points] Everyone likes him or herself
def prob_8 := ∀ P: Person, likes P P

/-
17. [2 points] Let p1, p2, p3 be people. If p1 does not like
p2, and p2 does not like p3, then p1 likes p3. This is a
logical statement of the adage that the enemy of my enemy is
my friend.
-/

def prob_9 := ∀ p1 p2 p3: Person, ¬likes p1 p2 ∧ ¬likes p2 p3 → likes p1 p3

/-
18. [2 points] No one likes Tom
-/
def prob_10 := ∀ P: Person, ¬likes P Tom

/-
19. [2 points] Not everyone likes Tom
-/

def prob_11 := ¬ (∀ P: Person, likes P Tom)

/-
20. [2 points] A person likes Tom if and only if that person
likes Yu.
-/

def prob_12 := ∀ P: Person, likes P Tom ↔ likes P Yu

/-
Translate the following three logical propositions into
natural language.
-/

-- 21. [2 points]
def prob_13 := ∃ p1, ∀ p2, likes p2 p1

-- Answer: there is someone everyone likes

-- 22. [2 points]
def prob_14 :=
    (∀ p1 p2, likes p1 p2 ↔ likes p2 p1) →
    likes Tom Yu →
    likes Yu Tom

-- Answer: if people only like others if and only if they are liked back, then
-- if Tom likes Yu then Yu likes Tom

-- 23. [extra credit]
def prob_15 := ¬ ∀ p1, ∃ p2, ¬ likes p1 p2

-- Answer: not everyone dislikes someone

/-
24. [2 points] Define a predicate, dislikes, so that p1
dislikes p2 (dislikes p1 p2) means that p1 does not
like p2
-/

-- Answer below
def dislikes (p1 p2 : Person) := ¬likes p1 p2

/-
25. [2 points] Define a predicate, friendOfFriend, that is
true of two people, p1 and p2, if there is someone, p, who
p1 likes, and where p, in turn, likes p2.
-/

-- Answer:
def friendOfFriend (p1 p2 : Person) := ∃ p: Person, likes p1 p ∧ likes p p2

/-
I. PROOFS
-/

/-
Some of the following exercises use the
following additional assumptions: Tom, Mary,
and Jane are nice; Hamid and Yu are not nice;
and Tom likes Mary. We now formalize these facts.
-/
axioms
    (nT: nice Tom)
    (nM: nice Mary)
    (nJ: nice Jane)
    (nnHamid: ¬ nice Hamid)
    (nnYu: ¬ nice Yu)
    (likesTM: likes Tom Mary)

/-
26. [3 points] Prove that there is someone who is not nice.
-/

example : ∃ p, ¬ nice p :=
begin
    apply exists.intro Hamid,
    exact nnHamid,
end

/-
27. [5 points] Prove the following. The proposition could
be expressed in English as follows: If whenever any two
people, p1 and p2, are nice, then p1 likes p2; then if
there exist two nice people, x and y; then there exist
two people, p and q, where p likes q. Write a brief
comment in English before each line of your proof
script to explain what logical inference rule you're
using.
-/

example :
    (∀ p1 p2, nice p1 → nice p2 → likes p1 p2) →
    (∃ x y : Person, nice x ∧ nice y) →
    ∃ p q, likes p q  :=
begin
  --assuming if whenever two people are nice, then one likes the other
    assume ppnice,
--assuming there exists two nice people
    assume xnice,
--eliminating the first nice person that exists
    apply exists.elim xnice,
--assuming the first nice person is x
    assume x,
--assuming there exists another nice person
    assume ynice,
--eliminating the second nice person that exists
    apply exists.elim ynice,
--assuming the second nice person is y
    assume y,
--assuming there are two nice people x and y
    assume func,
--introducing x and y as the two people that exist where x likes y
    apply exists.intro x,
    apply exists.intro y,
--defining the function that says if x is nice and y is nice then x likes y
    have xynice := ppnice x y,
--defining xnice to be a proof of x being nice and ynice to be a proof of y is nice
    have xnice := func.1,
    have ynice := func.2,
--calling the xynice function using the proofs of x is nice and y is nice to get
--a proof of x liking y
    exact xynice (xnice) (ynice),
end

/-
28. [3 points] Prove the following proposition.
-/

example : likes Tom Yu ∨ likes Tom Mary → ∃ p, likes Tom p :=
begin
    assume TYoTM,
    cases TYoTM with TY TM,
        apply exists.intro Yu,
        assumption,
        apply exists.intro Mary,
        assumption,
end

/- 29. [3 points] Using "example", state and prove the
proposition (formally) that Tom is nice and Tom likes Mary.
Remember, again, that you have certain proofs already defined
(as axioms); you can use them here.
-/

-- Answer here
example : nice Tom ∧ likes Tom Mary :=
begin
    split,
        exact nT,
        exact likesTM,
end
/-
30. [3 points] Prove that not everyone is not nice. Hint:
remember that you have some people and proofs
that they are nice, or not nice, as assumptions.
-/

example : ¬ (∀ p, ¬ nice p) :=
begin
    assume notnice,
    have TomNotNice := notnice Tom,
    exact TomNotNice nT,
end

/-
31. [3 points] Prove that the following formula can be
satisfied using only the propositions, true and false, as
witnesses.
-/

example : ∃ (P Q : Prop), (P ∨ Q) ∧ (P ∧ ¬ Q) :=
begin
    apply exists.intro true,
    apply exists.intro false,
    simp,
end

/-
32. [3 points] Prove the following by induction. Be sure
to use the induction tactic. Note: you might not need to use
the induction hypothesis!
-/

example : ∀ (n : ℕ), n = 0 ∨ ∃ n', n = nat.succ n' :=
begin
    assume n,
    induction n,
    apply or.inl,
    trivial,
    cases n_ih,
    apply or.inr,
    apply exists.intro 0,
    rw n_ih,
    apply or.inr,
    apply exists.elim n_ih,
    assume n,
    assume nfunc,
    rw nfunc,
    apply exists.intro (nat.succ n),
    exact rfl,
end
/-
33. [3 points] Prove the same thing without using induction
(hint: case analysis on n).
-/
example : ∀ (n : ℕ), n = 0 ∨ ∃ n', n = nat.succ n' :=
begin
    assume n,
    cases n,
    apply or.inl,
    exact rfl,
    apply or.inr,
    apply exists.intro n,
    exact rfl,
end

/-
III. Informal Proofs
-/

/- 34. [3 points]
First in Lean write a predicate, even : ℕ → Prop, expressing
what it means for a natural number, n, to be even. Then write
a concise *informal* proof of the proposition that for any
two natural numbers, n and m, if n and m are even, then n + m
is even. Make reference to the formal definition of what it
means to be even from within your informal proof. Hint: A
direct proof in which you start by assuming that m and n are
even and then go from there is easiest.
-/

def even : ℕ → Prop := λ n, ∃ m, n = 2 * m

/-
Your proof here.
We're trying to prove that if even(n) ∧ even(m), then even(n+m)
    1. assume n is even, which means there exists a nat N where n = N*2
        a. apply exists.elim to assume the nat N
    2. assume m is even, which means there exists a nat M where m = M*2
        a. apply exists.elim to assume the nat M
    3. we're trying to prove even(n+m); this means there exists
    some value NM where n+m = 2*NM
        a. to get to this point, unfold even
    4. introduce NM to be N + M (where N * 2 = n and M*2 = m)
        a. by applying exists.intro (N+M)
    5. substitute n in the equation we're trying to prove for 2*N
        a. rewrite
    6. substitute m in the equation we're trying to prove for 2 * M
        a. rewrite
    7. Thus, we now have the equation 2*N + 2*M = 2*(N+M)
    8. Distribute on the right side
    9. We now have 2*N + 2*M = 2*N + 2*M
    10. This equation is equal, thus the goal has been proved
-/

/-
IV. Set Theory
-/

/-
We introduce a few sets and ask you to then answer several
questions. Give your answers as plain text. You do not have
to use Lean syntax to answer the questions in this section of
the exam.
-/

def s1 : set ℕ := { 1, 2, 3 }
def s2 : set ℕ := { 3, 6 }
def s3 : set ℕ := {}
def s4 : set string := {"ILove", "Logic"}

/-
35. [2 points] Using display notation (curly braces around a
list of values), what is the union of s1 and s2?

Answer: {1, 2, 3, 6}
-/

/-
36. [2 points] Using display notation, what is the
intersection of s1 and s2?

Answer: {3}
-/

/-
37. [2 points] Using display notation, what is the set
difference, s1 - s2?

Answer: {1, 2}
-/

/-
38. [3 points] Suppose prime and even are two predicates over
the natural numbers, such that the proposition, "prime n", is
true if and only if n is prime in the usual sense, and the
proposition, "even n", is true if and only if n is even in
the usual sense. Use *set comprehension notation* to
represent the set of natural numbers that are both even and
prime.

Answer: set nat := {n | prime n ∧ even n}

Now write down this set using display notation. Hint: You do
have to figure out what specific elements are in this set.

Answer:
{2}
-/

/-
39. [3 points] Use display notation to write the powerset of
s2.

Answer:
{
    {},
    {3},
    {6},
    {3, 6}
}
-/

/-
40. [3 points] Use display notation to write the product set
(aka Cartesian product) of s2 and s4.

Answer:
{(3, "ILove"), (3, "Logic"), (6, "ILove"), (6, "Logic")}
-/

/-
41. [3 points] How many elements are in the powerset of the
product set of s2 and s4?

Answer: 16
-/

/-
42. [3 points] An element of the powerset of the product set
of s2 and s4 is some subset of the product set. Give one such
value (element) that represents a function from s2 to s4 that
is injective but not surjective.

Answer: {(3, "ILove"), (6, "ILove")}
-/

/-
43. [3 points] Give two values from the powerset of the
product set, each of which represents a bijective function
from s2 to s4.

Answer: {(3, "ILove"), (6, "Logic")}
        {(3, "Logic", (6, "ILove")}
-/

/-
44. [3 points] The biggest open problem in computer science
is whether two classes of problems are equivalent. The
problem goes by the name, P=NP. No one knows whether P=NP or
not. We'll introduce PeqNP as a value of type Prop to
represent the proposition that P=NP. Prove the following
theorem. You may use classical reasoning.
-/

open classical

axiom PeqNP : Prop
example : PeqNP ∨ ¬ PeqNP :=
begin
    cases em PeqNP,
        exact or.inl h,
        exact or.inr h,
end

/-
45. Extra Credit: Give a concise but still complete informal
proof that the square root of two is irrational. Hints: First
you might rephrase the proposition in the form of a negation.
The use the method of proof by negation. This involves making
an assumption. The key is then to deduce consequences of that
assumption, and show that it leads to a contradiction. Only a
complete correct proof will receive credit.

Answer:
1. we're trying to prove that the square root of two is NOT rational
2. thus, we're really trying to prove false
    a. we get to this point by assuming the premise that
    root 2 is rational
3. the definition of a rational number is that it has to be
expressible by the ratio of two integers, and can't be infinite
and nonrecurring when expressed as a decimal.
4. obtain the proof/definition of a rational number
5. root 2 is an infinite and recurring decimal and cannot
be expressed by the ratio of two integers
6. the proof of root two being rational contradicts the
definition of a rational number
7. thus, we have a contradiction and the negation is proved.
-/


section
  variable A: Prop
  variable h: A ∨ ¬A

  example: (¬A → false) → A :=
    assume hna,
    have h₁: A → A, from assume :A, this,
    have h₂: ¬A → A, from assume :¬A, false.elim (hna this),
    or.elim h h₁ h₂
end

section
  variables A B: Prop
  variable h: ¬A ∨ ¬B

  example: ¬(A ∧ B) :=
    assume h₁,
    have ha : A, from and.left h₁,
    have hb : B, from and.right h₁,
    have h₂: ¬A → false, from assume :¬A, this ha,
    have h₃: ¬B → false, from assume :¬B, this hb,
    or.elim h h₂ h₃
end

section
  open classical

  variables A B: Prop
  variable h: ¬(A ∧ B)

  example: ¬A ∨ ¬B:=
    have A → ¬B, from
      assume :A,
      assume :B,
      have A ∧ B, from and.intro ‹A› ‹B›,
      h ‹A ∧ B›,
    have ¬(¬A ∨ ¬B) → ¬A, from
      assume h₁,
      assume :A,
      have ¬B, from ‹A → ¬B› ‹A›,
      have ¬A ∨ ¬B, from or.intro_right (¬A) ‹¬B›,
      h₁ ‹¬A ∨ ¬B›,
    have ¬(¬A ∨ ¬B) → false, from
      assume h₁,
      have ¬A, from ‹¬(¬A ∨ ¬B) → ¬A› h₁,
      have ¬A ∨ ¬B, from or.intro_left (¬B) ‹¬A›,
      h₁ ‹¬A ∨ ¬B›,
    by_contradiction ‹¬(¬A ∨ ¬B) → false›
end

section
  open classical
  variables P Q R: Prop
  variable h: ¬P → (Q ∨ R)
  variable nQ: ¬Q
  variables nR: ¬R

  example: P :=
    have nnP: ¬P → false, from
      assume : ¬P,
      have Q ∨ R, from h this,
      have h₁: Q → false, from
        assume :Q, nQ this,
      have h₂: R → false, from
        assume :R, nR this,
      show false, from or.elim ‹Q ∨ R› h₁ h₂,
    show P, from by_contradiction nnP
end

section
  open classical
  variables A B: Prop
  variable h: A → B

  example: ¬A ∨ B :=
    have h₁: A ∨ ¬A, from em A,
    have h₂: ¬A → ¬A ∨ B, from
      assume :¬A, or.intro_left B this,
    have h₃: A → ¬A ∨ B, from
      assume :A, or.intro_right (¬A) (h this),
    or.elim h₁ h₃ h₂
end

section
  open classical
  variables A B: Prop

  example: A → ((A ∧ B) ∨ (A ∧ ¬B)):=
    assume :A,
    have emB: B ∨ ¬B, from em B,
    have h₁: B → ((A ∧ B) ∨ (A ∧ ¬B)), from
      assume :B,
      have h_and: A ∧ B, from and.intro ‹A› ‹B›,
      or.intro_left (A ∧ ¬B) h_and,
    have h₂: ¬B → ((A ∧ B) ∨ (A ∧ ¬B)), from
      assume :¬B,
      have h_and: A ∧ ¬B, from and.intro ‹A› ‹¬B›,
      or.intro_right (A ∧ B) h_and,
    or.elim emB h₁ h₂
end

section
  open classical
  variables {A B C : Prop}

  lemma step1 (h₁ : ¬ (A ∧ B)) (h₂ : A) : ¬ A ∨ ¬ B :=
  have ¬ B, from
    assume :B,
    have A ∧ B, from and.intro h₂ ‹B›,
    h₁ this,
  show ¬ A ∨ ¬ B, from or.inr this

  lemma step2 (h₁ : ¬ (A ∧ B)) (h₂ : ¬ (¬ A ∨ ¬ B)) : false :=
  have ¬ A, from
    assume : A,
    have ¬ A ∨ ¬ B, from step1 h₁ ‹A›,
    show false, from h₂ this,
  show false, from
    have ¬ A ∨ ¬ B, from or.inl ‹¬A›,
    h₂ this

  theorem step3 (h : ¬ (A ∧ B)) : ¬ A ∨ ¬ B :=
  by_contradiction
    (assume h' : ¬ (¬ A ∨ ¬ B),
      show false, from step2 h h')
end

section
  open classical
  variables {A B C : Prop}

  example (h : ¬ B → ¬ A) : A → B :=
    assume :A,
    show B, from
      have h₁: ¬B → false, from
        assume :¬B,
        have ¬A, from h this,
        this ‹A›,
      by_contradiction h₁

  example (h : A → B) : ¬ A ∨ B :=
    have h₁: ¬(¬ A ∨ B) → false, from
      assume h₂:¬(¬ A ∨ B),
      have ¬A, from
        assume :A,
        have B, from h this,
        have ¬A ∨ B, from or.inr ‹B›,
        h₂ this,
      have ¬A ∨ B, from or.inl ‹¬A›,
      h₂ this,
    by_contradiction h₁
end


/-
Copyright (c) 2021 Yakov Pechersky All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yakov Pechersky, Mario Carneiro
-/

import data.fin.basic
import tactic.norm_num

/-!
# `norm_fin`

This file defines functions for dealing with `fin n` numbers as expressions.

## Main definitions

* `tactic.norm_fin.eval_ineq` is a `norm_num` plugin for normalizing equalities and inequalities of
  type `fin n`.

* `tactic.interactive.norm_fin` is a standalone tactic like `norm_num` for normalizing `fin n`
  expressions anywhere in the goal.

-/

namespace tactic
namespace norm_fin
open norm_num

/-- `normalize_fin n a b` means that `a : fin n` is equivalent to `b : ℕ` in the modular sense -
that is, `↑a ≡ b (mod n)`. This is used for translating the algebraic operations: addition,
multiplication, zero and one, which use modulo for reduction. -/
def normalize_fin (n : ℕ) (a : fin n) (b : ℕ) := a.1 = b % n

/-- `normalize_fin_lt n a b` means that `a : fin n` is equivalent to `b : ℕ` in the embedding
sense - that is, `↑a = b`. This is used for operations that treat `fin n` as the subset
`{0, ..., n-1}` of `ℕ`. For example, `fin.succ : fin n → fin (n+1)` is thought of as the successor
function, but it does not lift to a map `zmod n → zmod (n+1)`; this addition only makes sense if
the input is strictly less than `n`.

`normalize_fin_lt n a b` is equivalent to `normalize_fin n a b ∧ b < n`. -/
def normalize_fin_lt (n : ℕ) (a : fin n) (b : ℕ) := a.1 = b

theorem normalize_fin_lt.coe {n} {a : fin n} {b : ℕ} (h : normalize_fin_lt n a b) : ↑a = b := h

theorem normalize_fin_iff {n : ℕ} [ne_zero n] {a b} :
  normalize_fin n a b ↔ a = fin.of_nat' b :=
iff.symm (fin.eq_iff_veq _ _)

theorem normalize_fin_lt.mk {n a b n'} (hn : n = n')
  (h : normalize_fin n a b) (h2 : b < n') : normalize_fin_lt n a b :=
h.trans $ nat.mod_eq_of_lt $ by rw hn; exact h2

theorem normalize_fin_lt.lt {n a b} (h : normalize_fin_lt n a b) : b < n :=
by rw ← h.coe; exact a.2

theorem normalize_fin_lt.of {n a b} (h : normalize_fin_lt n a b) : normalize_fin n a b :=
h.trans $ eq.symm $ nat.mod_eq_of_lt h.lt

theorem normalize_fin.zero (n : ℕ) [ne_zero n] :
  normalize_fin n 0 0 := by { rw normalize_fin, norm_num }
theorem normalize_fin_lt.zero (n : ℕ) [ne_zero n] : normalize_fin_lt n 0 0 := refl _
theorem normalize_fin.one (n : ℕ) [ne_zero n] : normalize_fin n 1 1 := refl _
theorem normalize_fin.add {n} {a b : fin n} {a' b' c' : ℕ}
  (ha : normalize_fin n a a') (hb : normalize_fin n b b')
  (h : a' + b' = c') : normalize_fin n (a + b) c' :=
by simp only [normalize_fin, ← h] at *; rw [nat.add_mod, ← ha, ← hb, fin.add_def]
theorem normalize_fin.mul {n} {a b : fin n} {a' b' c' : ℕ}
  (ha : normalize_fin n a a') (hb : normalize_fin n b b')
  (h : a' * b' = c') : normalize_fin n (a * b) c' :=
by simp only [normalize_fin, ← h] at *; rw [nat.mul_mod, ← ha, ← hb, fin.mul_def]
theorem normalize_fin.bit0 {n} {a : fin n} {a' : ℕ}
  (h : normalize_fin n a a') : normalize_fin n (bit0 a) (bit0 a') := h.add h rfl
theorem normalize_fin.bit1 {n : ℕ} [ne_zero n] {a : fin n} {a' : ℕ}
  (h : normalize_fin n a a') : normalize_fin n (bit1 a) (bit1 a') :=
h.bit0.add (normalize_fin.one _) rfl

theorem normalize_fin_lt.succ {n} {a : fin n} {a' b : ℕ}
  (h : normalize_fin_lt n a a') (e : a' + 1 = b) : normalize_fin_lt n.succ (fin.succ a) b :=
by simpa [normalize_fin_lt, ← e] using h

theorem normalize_fin_lt.cast_lt {n m} {a : fin m} {ha} {a' : ℕ}
  (h : normalize_fin_lt m a a') : normalize_fin_lt n (fin.cast_lt a ha) a' :=
by simpa [normalize_fin_lt] using h

theorem normalize_fin_lt.cast_le {n m} {nm} {a : fin m} {a' : ℕ}
  (h : normalize_fin_lt m a a') : normalize_fin_lt n (fin.cast_le nm a) a' :=
by simpa [normalize_fin_lt] using h

theorem normalize_fin_lt.cast {n m} {nm} {a : fin m} {a' : ℕ}
  (h : normalize_fin_lt m a a') : normalize_fin_lt n (fin.cast nm a) a' :=
by simpa [normalize_fin_lt] using h

theorem normalize_fin.cast {n m} {nm} {a : fin m} {a' : ℕ}
  (h : normalize_fin m a a') : normalize_fin n (fin.cast nm a) a' :=
by convert ← normalize_fin_lt.cast h

theorem normalize_fin_lt.cast_add {n m} {a : fin n} {a' : ℕ}
  (h : normalize_fin_lt n a a') : normalize_fin_lt (n + m) (fin.cast_add m a) a' :=
by simpa [normalize_fin_lt] using h

theorem normalize_fin_lt.cast_succ {n} {a : fin n} {a' : ℕ}
  (h : normalize_fin_lt n a a') : normalize_fin_lt (n+1) (fin.cast_succ a) a' :=
normalize_fin_lt.cast_add h

theorem normalize_fin_lt.add_nat {n m m'} (hm : m = m') {a : fin n} {a' b : ℕ}
  (h : normalize_fin_lt n a a') (e : a' + m' = b) :
  normalize_fin_lt (n+m) (@fin.add_nat n m a) b :=
by simpa [normalize_fin_lt, ← e, ← hm] using h

theorem normalize_fin_lt.nat_add {n m n'} (hn : n = n') {a : fin m} {a' b : ℕ}
  (h : normalize_fin_lt m a a') (e : n' + a' = b) :
  normalize_fin_lt (n+m) (@fin.nat_add n m a) b :=
by simpa [normalize_fin_lt, ← e, ← hn] using h

theorem normalize_fin.reduce {n} {a : fin n} {n' a' b k nk : ℕ}
  (hn : n = n') (h : normalize_fin n a a') (e1 : n' * k = nk) (e2 : nk + b = a') :
  normalize_fin n a b :=
by rwa [← e2, ← e1, ← hn, normalize_fin, add_comm, nat.add_mul_mod_self_left] at h

theorem normalize_fin_lt.reduce {n} {a : fin n} {n' a' b k nk : ℕ}
  (hn : n = n') (h : normalize_fin n a a') (e1 : n' * k = nk) (e2 : nk + b = a') (hl : b < n') :
  normalize_fin_lt n a b :=
normalize_fin_lt.mk hn (h.reduce hn e1 e2) hl

/-- The monad for the `norm_fin` internal tactics. The state consists of an instance cache for `ℕ`,
and a tuple `(nn, n', p)` where `p` is a proof of `n = n'` and `nn` is `n` evaluated to a natural
number. (`n` itself is implicit.)  It is in an `option` because it is lazily initialized - for many
`n` we will never need this information, and indeed eagerly computing it would make some reductions
fail spuriously if `n` is not a numeral. -/
@[derive [monad, alternative]]
meta def eval_fin_m (α : Type) : Type :=
state_t (instance_cache × option (ℕ × expr × expr)) tactic α

/-- Lifts a tactic into the `eval_fin_m` monad. -/
@[inline] meta def eval_fin_m.lift {α} (m : tactic α) : eval_fin_m α :=
⟨λ ⟨ic, r⟩, do a ← m, pure (a, ic, r)⟩

meta instance {α} : has_coe (tactic α) (eval_fin_m α) := ⟨eval_fin_m.lift⟩

/-- Lifts an `instance_cache` tactic into the `eval_fin_m` monad. -/
@[inline] meta def eval_fin_m.lift_ic {α}
  (m : instance_cache → tactic (instance_cache × α)) : eval_fin_m α :=
⟨λ ⟨ic, r⟩, do (ic, a) ← m ic, pure (a, ic, r)⟩

/-- Evaluates a monadic action with a fresh `n` cache, and restore the old cache on completion of
the action. This is used when evaluating a tactic in the context of a different `n` than the parent
context. For example if we are evaluating `fin.succ a`, then `a : fin n` and
`fin.succ a : fin (n+1)`, so the parent cache will be about `n+1` and we need a separate cache for
`n`. -/
@[inline] meta def eval_fin_m.reset {α} (m : eval_fin_m α) : eval_fin_m α :=
⟨λ ⟨ic, r⟩, do (a, ic, _) ← m.run ⟨ic, none⟩, pure (a, ic, r)⟩

/-- Given `n`, returns a tuple `(nn, n', p)` where `p` is a proof of `n = n'` and `nn` is `n`
evaluated to a natural number. The result of the evaluation is cached for future references.
Future calls to this function must use the same value of `n`, unless it is in a sub-context
created by `eval_fin_m.reset`. -/
meta def eval_fin_m.eval_n (n : expr) : eval_fin_m (ℕ × expr × expr) :=
⟨λ ⟨ic, r⟩, match r with
  | none := do
    (n', p) ← or_refl_conv norm_num.derive n,
    nn ← n'.to_nat,
    let np := (nn, n', p),
    pure (np, ic, some np)
  | some np := pure (np, ic, some np)
  end⟩

/-- Run an `eval_fin_m` action with a new cache and discard the cache after evaluation. -/
@[inline] meta def eval_fin_m.run {α} (m : eval_fin_m α) : tactic α :=
do ic ← mk_instance_cache `(ℕ), (a, _) ← state_t.run m (ic, none), pure a

/-- The expression constructors recognized by the `eval_fin` evaluator. This is used instead of a
direct expr pattern match because expr pattern matches generate very large terms under the
hood so going via an intermediate inductive type like this is more efficient. -/
meta inductive match_fin_result
| zero (n : expr) (n0 : expr)   -- `(0 : fin n)`
| one (n : expr) (n0 : expr)    -- `(1 : fin n)`
| add (n a b : expr)            -- `(a + b : fin n)`
| mul (n a b : expr)            -- `(a * b : fin n)`
| bit0 (n a : expr)             -- `(bit0 a : fin n)`
| bit1 (n a : expr) (n0 : expr) -- `(bit1 a : fin n)`
| succ (n a : expr)             -- `(fin.succ a : fin n.succ)`
| cast_lt (n m i h : expr)      -- `(fin.cast_lt (i : fin m) (h : i.val < n) : fin n)`
| cast_le (n m h a : expr)      -- `(fin.cast_le (h : n ≤ m) (a : fin n) : fin m)`
| cast (n m h a : expr)         -- `(fin.cast_le (h : n = m) (a : fin n) : fin m)`
| cast_add (n m a : expr)       -- `(fin.cast_add m (a : fin n) : fin (n + m))`
| cast_succ (n a : expr)        -- `(fin.cast_succ (a : fin n) : fin (n + 1))`
| add_nat (n m a : expr)        -- `(fin.add_nat m (a : fin n) : fin (n + m))`
| nat_add (n m a : expr)        -- `(fin.nat_add n (a : fin m) : fin (n + m))`

section
open match_fin_result
/-- Match a fin expression of the form `(coe_fn f a)` where `f` is some fin function. Several fin
functions are written this way: for example `cast_le : n ≤ m → fin n ↪o fin m` is not actually a
function but rather an order embedding with a coercion to a function. -/
meta def match_fin_coe_fn (a : expr) : expr → option match_fin_result
| `(@fin.cast_le %%n %%m %%h) := some (cast_le n m h a)
| `(@fin.cast %%m %%n %%h) := some (cast n m h a)
| `(@fin.cast_add %%n %%m) := some (cast_add n m a)
| `(@fin.cast_succ %%n) := some (cast_succ n a)
| `(@fin.add_nat %%n %%m) := some (add_nat n m a)
| `(@fin.nat_add %%n %%m) := some (nat_add n m a)
| _ := none

/-- Match a fin expression to a `match_fin_result`, for easier pattern matching in the
evaluator. -/
meta def match_fin : expr → option match_fin_result
| `(@has_zero.zero ._ (@fin.has_zero_of_ne_zero %%n %%n0)) := some (zero n n0)
| `(@has_one.one ._ (@fin.has_one_of_ne_zero %%n %%n0)) := some (one n n0)
| `(@has_add.add (fin %%n) ._ %%a %%b) := some (add n a b)
| `(@has_mul.mul (fin %%n) ._ %%a %%b) := some (mul n a b)
| `(@_root_.bit0 (fin %%n) ._ %%a) := some (bit0 n a)
| `(@_root_.bit1 ._ (@fin.has_one_of_ne_zero %%n %%n0) ._ %%a) := some (bit1 n a n0)
| `(@fin.succ %%n %%a) := some (succ n a)
| `(@fin.cast_lt %%n %%m %%a %%h) := some (cast_lt n m a h)
| (expr.app `(@coe_fn ._ ._ ._ %%f) a) := match_fin_coe_fn a f
| _ := none
end

/-- `reduce_fin lt n a (a', pa)` expects that `pa : normalize_fin n a a'` where `a'`
is a natural numeral, and produces `(b, pb)` where `pb : normalize_fin n a b` if `lt` is false, or
`pb : normalize_fin_lt n a b` if `lt` is true. In either case, `b` will be chosen to be less than
`n`, but if `lt` is true then we also prove it. This requires that `n` can be evaluated to a
numeral. -/
meta def reduce_fin' : bool → expr → expr → expr × expr → eval_fin_m (expr × expr)
| lt n a (a', pa) := do
  (nn, n', pn) ← eval_fin_m.eval_n n,
  na ← expr.to_nat a',
  if na < nn then
    if lt then do
      p ← eval_fin_m.lift_ic (λ ic, prove_lt_nat ic a' n'),
      pure (a', `(@normalize_fin_lt.mk).mk_app [n, a, a', n', pn, pa, p])
    else pure (a', pa)
  else
    let nb := na % nn, nk := (na - nb) / nn in
    eval_fin_m.lift_ic $ λ ic, do
      (ic, k) ← ic.of_nat nk,
      (ic, b) ← ic.of_nat nb,
      (ic, nk, pe1) ← prove_mul_nat ic n' k,
      (ic, pe2) ← prove_add_nat ic nk b a',
      if lt then do
        (ic, p) ← prove_lt_nat ic b n',
        pure (ic, b,
          `(@normalize_fin_lt.reduce).mk_app [n, a, n', a', b, k, nk, pn, pa, pe1, pe2, p])
      else pure (ic, b,
          `(@normalize_fin.reduce).mk_app [n, a, n', a', b, k, nk, pn, pa, pe1, pe2])

/-- `eval_fin_lt' eval_fin n a` expects that `a : fin n`, and produces `(b, p)` where
`p : normalize_fin_lt n a b`. (It is mutually recursive with `eval_fin` which is why it takes the
function as an argument.) -/
meta def eval_fin_lt' (eval_fin : expr → eval_fin_m (expr × expr)) :
  expr → expr → eval_fin_m (expr × expr)
| n a := do
  e ← match_fin a,
  match e with
  | match_fin_result.succ n a := do
    (a', pa) ← (eval_fin_lt' n a).reset,
    (b, pb) ← eval_fin_m.lift_ic (λ ic, prove_succ' ic a'),
    pure (b, `(@normalize_fin_lt.succ).mk_app [n, a, a', b, pa, pb])
  | match_fin_result.cast_lt _ m a h := do
    (a', pa) ← (eval_fin_lt' m a).reset,
    pure (a', `(@normalize_fin_lt.cast_lt).mk_app [n, m, a, h, a', pa])
  | match_fin_result.cast_le _ m nm a := do
    (a', pa) ← (eval_fin_lt' m a).reset,
    pure (a', `(@normalize_fin_lt.cast_le).mk_app [n, m, nm, a, a', pa])
  | match_fin_result.cast m _ nm a := do
    (a', pa) ← (eval_fin_lt' m a).reset,
    pure (a', `(@normalize_fin_lt.cast).mk_app [n, m, nm, a, a', pa])
  | match_fin_result.cast_add n m a := do
    (a', pa) ← (eval_fin_lt' m a).reset,
    pure (a', `(@normalize_fin_lt.cast_add).mk_app [n, m, a, a', pa])
  | match_fin_result.cast_succ n a := do
    (a', pa) ← (eval_fin_lt' n a).reset,
    pure (a', `(@normalize_fin_lt.cast_succ).mk_app [n, a, a', pa])
  | match_fin_result.add_nat n m a := do
    (a', pa) ← (eval_fin_lt' n a).reset,
    (m', pm) ← or_refl_conv norm_num.derive m,
    (b, pb) ← eval_fin_m.lift_ic (λ ic, prove_add_nat' ic a' m'),
    pure (b, `(@normalize_fin_lt.add_nat).mk_app [n, m, m', pm, a, a', b, pa, pb])
  | match_fin_result.nat_add n m a := do
    (a', pa) ← (eval_fin_lt' m a).reset,
    (n', pn) ← or_refl_conv norm_num.derive n,
    (b, pb) ← eval_fin_m.lift_ic (λ ic, prove_add_nat' ic n' a'),
    pure (b, `(@normalize_fin_lt.nat_add).mk_app [n, m, n', pn, a, a', b, pa, pb])
  | _ := do
    (_, n', pn) ← eval_fin_m.eval_n n,
    (a', pa) ← eval_fin a >>= reduce_fin' tt n a,
    p ← eval_fin_m.lift_ic (λ ic, prove_lt_nat ic a' n'),
    pure (a', `(@normalize_fin_lt.mk).mk_app [n, a, a', n', pn, pa, p])
  end

/-- Get `n` such that `a : fin n`. -/
meta def get_fin_type (a : expr) : tactic expr := do `(fin %%n) ← infer_type a, pure n

/-- Given `a : fin n`, `eval_fin a` returns `(b, p)` where `p : normalize_fin n a b`. This function
does no reduction of the numeral `b`; for example `eval_fin (5 + 5 : fin 6)` returns `10`. It works
even if `n` is a variable, for example `eval_fin (5 + 5 : fin (n+1))` also returns `10`. -/
meta def eval_fin : expr → eval_fin_m (expr × expr)
| a := do
  m ← match_fin a,
  match m with
  | match_fin_result.zero n n0 := pure (`(0 : ℕ), `(normalize_fin.zero).mk_app [n, n0])
  | match_fin_result.one n n0 := pure (`(1 : ℕ), `(normalize_fin.one).mk_app [n, n0])
  | match_fin_result.add n a b := do
    (a', pa) ← eval_fin a,
    (b', pb) ← eval_fin b,
    (c, pc) ← eval_fin_m.lift_ic (λ ic, prove_add_nat' ic a' b'),
    pure (c, `(@normalize_fin.add).mk_app [n, a, b, a', b', c, pa, pb, pc])
  | match_fin_result.mul n a b := do
    (a', pa) ← eval_fin a,
    (b', pb) ← eval_fin b,
    (c, pc) ← eval_fin_m.lift_ic (λ ic, prove_mul_nat ic a' b'),
    pure (c, `(@normalize_fin.mul).mk_app [n, a, b, a', b', c, pa, pb, pc])
  | match_fin_result.bit0 n a := do
    (a', pa) ← eval_fin a,
    pure (`(@bit0 ℕ _).mk_app [a'], `(@normalize_fin.bit0).mk_app [n, a, a', pa])
  | match_fin_result.bit1 n a n0 := do
    (a', pa) ← eval_fin a,
    pure (`(@bit1 ℕ _ _).mk_app [a'], `(@normalize_fin.bit1).mk_app [n, n0, a, a', pa])
  | match_fin_result.cast m n nm a := do
    (a', pa) ← (eval_fin a).reset,
    pure (a', `(@normalize_fin.cast).mk_app [n, m, nm, a, a', pa])
  | _ := do
    n ← get_fin_type a,
    (a', pa) ← eval_fin_lt' eval_fin n a,
    pure (a', `(@normalize_fin_lt.of).mk_app [n, a, a', pa])
  end

/-- `eval_fin_lt n a` expects that `a : fin n`, and produces `(b, p)` where
`p : normalize_fin_lt n a b`. -/
meta def eval_fin_lt : expr → expr → eval_fin_m (expr × expr) := eval_fin_lt' eval_fin

/-- Given `a : fin n`, `eval_fin ff n a` returns `(b, p)` where `p : normalize_fin n a b`, and
`eval_fin tt n a` returns `p : normalize_fin_lt n a b`. Unlike `eval_fin`, this also does reduction
of the numeral `b`; for example `reduce_fin ff 6 (5 + 5 : fin 6)` returns `4`. As a result, it
fails if `n` is a variable, for example `reduce_fin ff (n+1) (5 + 5 : fin (n+1))` fails. -/
meta def reduce_fin (lt : bool) (n a : expr) : eval_fin_m (expr × expr) :=
eval_fin a >>= reduce_fin' lt n a

/-- If `a b : fin n` and `a'` and `b'` are as returned by `eval_fin`,
then `prove_lt_fin' n a b a' b'` proves `a < b`. -/
meta def prove_lt_fin' : expr → expr → expr → expr × expr → expr × expr → eval_fin_m expr
| n a b a' b' := do
  (a', pa) ← reduce_fin' ff n a a',
  (b', pb) ← reduce_fin' tt n b b',
  p ← eval_fin_m.lift_ic (λ ic, prove_lt_nat ic a' b'),
  pure (`(@normalize_fin.lt).mk_app [n, a, b, a', b', pa, pb, p])

/-- If `a b : fin n` and `a'` and `b'` are as returned by `eval_fin`,
then `prove_le_fin' n a b a' b'` proves `a ≤ b`. -/
meta def prove_le_fin' : expr → expr → expr → expr × expr → expr × expr → eval_fin_m expr
| n a b a' b' := do
  (a', pa) ← reduce_fin' ff n a a',
  (b', pb) ← reduce_fin' tt n b b',
  p ← eval_fin_m.lift_ic (λ ic, prove_le_nat ic a' b'),
  pure (`(@normalize_fin.le).mk_app [n, a, b, a', b', pa, pb, p])

/-- If `a b : fin n` and `a'` and `b'` are as returned by `eval_fin`,
then `prove_eq_fin' n a b a' b'` proves `a = b`. -/
meta def prove_eq_fin' : expr → expr → expr → expr × expr → expr × expr → eval_fin_m expr
| n a b (a', pa) (b', pb) :=
  if a' =ₐ b' then do
    pure (`(@normalize_fin.eq).mk_app [n, a, b, a', pa, pb])
  else do
    (a', pa) ← reduce_fin' ff n a (a', pa),
    (b', pb) ← reduce_fin' ff n b (b', pb),
    guard (a' =ₐ b'),
    pure (`(@normalize_fin.eq).mk_app [n, a, b, a', pa, pb])

/-- Given a function with the type of `prove_eq_fin'`, evaluates it with the given `a` and `b`. -/
meta def eval_prove_fin
  (f : expr → expr → expr → expr × expr → expr × expr → eval_fin_m expr)
  (a b : expr) : tactic expr :=
do n ← get_fin_type a, eval_fin_m.run $ eval_fin a >>= λ a', eval_fin b >>= f n a b a'

/-- If `a b : fin n`, then `prove_eq_fin a b` proves `a = b`. -/
meta def prove_eq_fin : expr → expr → tactic expr := eval_prove_fin prove_eq_fin'
/-- If `a b : fin n`, then `prove_lt_fin a b` proves `a < b`. -/
meta def prove_lt_fin : expr → expr → tactic expr := eval_prove_fin prove_lt_fin'
/-- If `a b : fin n`, then `prove_le_fin a b` proves `a ≤ b`. -/
meta def prove_le_fin : expr → expr → tactic expr := eval_prove_fin prove_le_fin'

section
open norm_num.match_numeral_result

/-- Given expressions `n` and `m` such that `n` is definitionally equal to `m.succ`, and
a natural numeral `a`, proves `(b, ⊢ normalize_fin n b a)`, where `n` and `m` are both used
in the construction of the numeral `b : fin n`. -/
meta def mk_fin_numeral (n m : expr) : expr → option (expr × expr)
| a := match match_numeral a with
  | zero := some (
    expr.app `(@has_zero.zero (fin %%n)) `(@fin.has_zero %%m),
    `(normalize_fin.zero).mk_app [n, `(@ne_zero.succ %%m)])
  | one := some (
    expr.app `(@has_one.one (fin %%n)) `(@fin.has_one %%m),
    `(normalize_fin.one).mk_app [n, `(@ne_zero.succ %%m)])
  | bit0 a := do
    (a', p) ← mk_fin_numeral a,
    some (`(bit0 %%a' : fin %%n), `(@normalize_fin.bit0).mk_app [n, a', a, p])
  | bit1 a := do
    (a', p) ← mk_fin_numeral a,
    some (
      `(@_root_.bit1 (fin %%n)).mk_app [`(@fin.has_one %%m), `(@fin.has_add %%n), a'],
      `(@normalize_fin.bit1).mk_app [n, `(@ne_zero.succ %%m), a', a, p])
  | _ := none
  end
end

/-- The common prep work for the cases in `eval_ineq`. Given inputs `a b : fin n`, it calls
`f n a' b' na nb` where `a'` and `b'` are the result of `eval_fin` and `na` and `nb` are
`a' % n` and `b' % n` as natural numbers. -/
meta def eval_rel {α} (a b : expr)
  (f : expr → expr × expr → expr × expr → ℕ → ℕ → eval_fin_m α) : tactic α :=
do n ← get_fin_type a,
  eval_fin_m.run $ do
    (nn, n', pn) ← eval_fin_m.eval_n n,
    (a', pa) ← eval_fin a,
    (b', pb) ← eval_fin b,
    na ← eval_fin_m.lift a'.to_nat,
    nb ← eval_fin_m.lift b'.to_nat,
    f n (a', pa) (b', pb) (na % nn) (nb % nn)

/-- Given `a b : fin n`, proves either `(n, tt, p)` where `p : a < b` or
`(n, ff, p)` where `p : b ≤ a`. -/
meta def prove_lt_ge_fin : expr → expr → tactic (expr × bool × expr)
| a b := eval_rel a b $ λ n a' b' na nb,
  if na < nb then prod.mk n <$> prod.mk tt <$> prove_lt_fin' n a b a' b'
  else prod.mk n <$> prod.mk ff <$> prove_le_fin' n b a b' a'

/-- Given `a b : fin n`, proves either `(n, tt, p)` where `p : a = b` or
`(n, ff, p)` where `p : a ≠ b`. -/
meta def prove_eq_ne_fin : expr → expr → tactic (expr × bool × expr)
| a b := eval_rel a b $ λ n a' b' na nb,
  if na = nb then prod.mk n <$> prod.mk tt <$> prove_eq_fin' n a b a' b'
  else if na < nb then do
    p ← prove_lt_fin' n a b a' b',
    pure (n, ff, `(@ne_of_lt (fin %%n) _).mk_app [a, b, p])
  else do
    p ← prove_lt_fin' n b a b' a',
    pure (n, ff, `(@ne_of_gt (fin %%n) _).mk_app [a, b, p])

/-- A `norm_num` extension that evaluates equalities and inequalities on the type `fin n`.

```
example : (5 : fin 7) = fin.succ (fin.succ 3) := by norm_num
```
-/
@[norm_num] meta def eval_ineq : expr → tactic (expr × expr)
| `(%%a < %%b) := do
  (n, lt, p) ← prove_lt_ge_fin a b,
  if lt then true_intro p else false_intro (`(@not_lt_of_ge (fin %%n) _).mk_app [a, b, p])
| `(%%a ≤ %%b) := do
  (n, lt, p) ← prove_lt_ge_fin b a,
  if lt then false_intro (`(@not_le_of_gt (fin %%n) _).mk_app [a, b, p]) else true_intro p
| `(%%a = %%b) := do
  (n, eq, p) ← prove_eq_ne_fin a b,
  if eq then true_intro p else false_intro p
| `(%%a > %%b) := mk_app ``has_lt.lt [b, a] >>= eval_ineq
| `(%%a ≥ %%b) := mk_app ``has_le.le [b, a] >>= eval_ineq
| `(%%a ≠ %%b) := do
  (n, eq, p) ← prove_eq_ne_fin a b,
  if eq then false_intro `(not_not_intro (%%p : (%%a : fin %%n) = %%b)) else true_intro p
| _ := failed

/-- Evaluates `e : fin n` to a natural number less than `n`. Returns `none` if it is not a natural
number or greater than `n`. -/
meta def as_numeral (n e : expr) : eval_fin_m (option ℕ) :=
match e.to_nat with
| none := pure none
| some ne := do
  (nn, _) ← eval_fin_m.eval_n n,
  pure $ if ne < nn then some ne else none
end

/-- Given `a : fin n`, returns `(b, ⊢ a = b)` where `b` is a normalized fin numeral. Fails if `a`
is already normalized. -/
meta def eval_fin_num (a : expr) : tactic (expr × expr) :=
do n ← get_fin_type a,
  eval_fin_m.run $ do
    as_numeral n a >>= (λ o, guardb o.is_none),
    (a', pa) ← eval_fin a,
    (a', pa) ← reduce_fin' ff n a (a', pa) <|> pure (a', pa),
    (nm + 1, _) ← eval_fin_m.eval_n n | failure,
    m' ← eval_fin_m.lift_ic (λ ic, ic.of_nat nm),
    n' ← eval_fin_m.lift_ic (λ ic, ic.of_nat (nm+1)),
    (b, pb) ← mk_fin_numeral n' m' a',
    pure (b, `(@normalize_fin.eq).mk_app [n, a, b, a', pa, pb])

end norm_fin

namespace interactive

setup_tactic_parser

/-- Rewrites occurrences of fin expressions to normal form anywhere in the goal.
The `norm_num` extension will only rewrite fin expressions if they appear in equalities and
inequalities. For example if the goal is `P (2 + 2 : fin 3)` then `norm_num` will not do anything
but `norm_fin` will reduce the goal to `P 1`.

(The reason this is not part of `norm_num` is because evaluation of fin numerals uses a top down
evaluation strategy while `norm_num` works bottom up; also determining whether a normalization
will work is expensive, meaning that unrelated uses of `norm_num` would be slowed down with this
as a plugin.) -/
meta def norm_fin (hs : parse simp_arg_list) : tactic unit :=
try (simp_top_down tactic.norm_fin.eval_fin_num) >> try (norm_num hs (loc.ns [none]))

/--
Rewrites occurrences of fin expressions to normal form anywhere in the goal.
The `norm_num` extension will only rewrite fin expressions if they appear in equalities and
inequalities. For example if the goal is `P (2 + 2 : fin 3)` then `norm_num` will not do anything
but `norm_fin` will reduce the goal to `P 1`.

```lean
example : (5 : fin 7) = fin.succ (fin.succ 3) := by norm_num
example (P : fin 7 → Prop) (h : P 5) : P (fin.succ (fin.succ 3)) := by norm_fin; exact h
```
-/
add_tactic_doc
{ name        := "norm_fin",
  category    := doc_category.tactic,
  decl_names  := [`tactic.interactive.norm_fin],
  tags        := ["arithmetic", "decision procedure"] }

end interactive
end tactic


import data.int.basic

open int
open nat

-- quotient / remainder theorem
theorem qt : ∀ n m : ℤ, m > 0 → ∃ q r : ℤ, n = m * q + r ∧ (0 ≤ r ∧ r < m) :=
assume n m h,

exists.intro (n / m) $ exists.intro (n % m) $

  have HH:  n = m * (n / m)  + (n % m), from calc
    n = n % m + m * (n / m) : by rw [int.mod_add_div]
    ... = m * (n / m) + (n % m) : by rw add_comm,

  have HH1: 0 ≤ (n % m), from int.mod_nonneg n (ne_of_gt h),

  have HH2: (n % m) < m, from calc
    (n % m) < abs m : int.mod_lt n (ne_of_gt h)
    ... = m : abs_of_pos h,

  ⟨ HH , ⟨ HH1 , HH2 ⟩ ⟩


-- import the definition of the gcd maze
import mazes.gcd_maze.definition
import data.int.gcd
open maze direction

/-

# Bezout's Theorem Example maze.

You are in a maze of integers, all distinct.

You can go north, south east or west.

North adds 8 to your integer, South subtracts 8.
East adds 5 to your integer, West subtracts 5

The exit is at 1, the gcd of 8 and 5.
When you're at the exit, type `out`.

Solver remark : there are infinitely many rooms.
-/

/- Lemma : no-side-bar
Can you prove this case of Bezout's Theorem?
-/
example : can_escape 8 5 0 :=
begin
  iterate 2 {n},
  iterate 3 {w},
  out
end

-- #eval nat.xgcd 8 5 -- (2, -3) -- hide


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Simon Hudon

! This file was ported from Lean 3 source module data.list.tfae
! leanprover-community/mathlib commit 5a3e819569b0f12cbec59d740a2613018e7b8eec
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.List.Basic
import Mathlib.Init.Data.List.Basic

/-!
# The Following Are Equivalent

This file allows to state that all propositions in a list are equivalent. It is used by
`Mathlib.Tactic.Tfae`.
`TFAE l` means `∀ x ∈ l, ∀ y ∈ l, x ↔ y`. This is equivalent to `pairwise (↔) l`.
-/

namespace List

/-- TFAE: The Following (propositions) Are Equivalent.

The `tfae_have` and `tfae_finish` tactics can be useful in proofs with `TFAE` goals.
-/
def TFAE (l : List Prop) : Prop :=
  ∀ x ∈ l, ∀ y ∈ l, x ↔ y
#align list.tfae List.TFAE

theorem tfae_nil : TFAE [] :=
  forall_mem_nil _
#align list.tfae_nil List.tfae_nil

theorem tfae_singleton (p) : TFAE [p] := by simp [TFAE, -eq_iff_iff]
#align list.tfae_singleton List.tfae_singleton

theorem tfae_cons_of_mem {a b} {l : List Prop} (h : b ∈ l) : TFAE (a :: l) ↔ (a ↔ b) ∧ TFAE l :=
  ⟨fun H => ⟨H a (by simp) b (Mem.tail a h),
    fun p hp q hq => H _ (Mem.tail a hp) _ (Mem.tail a hq)⟩,
    by
    rintro ⟨ab, H⟩ p (_ | ⟨_, hp⟩) q (_ | ⟨_, hq⟩)
    · rfl
    · exact ab.trans (H _ h _ hq)
    · exact (ab.trans (H _ h _ hp)).symm
    · exact H _ hp _ hq⟩
#align list.tfae_cons_of_mem List.tfae_cons_of_mem

theorem tfae_cons_cons {a b} {l : List Prop} : TFAE (a :: b :: l) ↔ (a ↔ b) ∧ TFAE (b :: l) :=
  tfae_cons_of_mem (Mem.head _)
#align list.tfae_cons_cons List.tfae_cons_cons

theorem tfae_of_forall (b : Prop) (l : List Prop) (h : ∀ a ∈ l, a ↔ b) : TFAE l :=
  fun _a₁ h₁ _a₂ h₂ => (h _ h₁).trans (h _ h₂).symm
#align list.tfae_of_forall List.tfae_of_forall

theorem tfae_of_cycle {a b} {l : List Prop} :
    List.Chain (· → ·) a (b :: l) → (ilast' b l → a) → TFAE (a :: b :: l) := by
  induction' l with c l IH generalizing a b <;>
    simp only [tfae_cons_cons, tfae_singleton, and_true_iff, chain_cons, Chain.nil] at *
  · intro a b
    exact Iff.intro a b
  rintro ⟨ab, ⟨bc, ch⟩⟩ la
  have := IH ⟨bc, ch⟩ (ab ∘ la)
  exact ⟨⟨ab, la ∘ (this.2 c (Mem.head _) _ (ilast'_mem _ _)).1 ∘ bc⟩, this⟩
#align list.tfae_of_cycle List.tfae_of_cycle

theorem TFAE.out {l} (h : TFAE l) (n₁ n₂) {a b} (h₁ : List.get? l n₁ = some a := by rfl)
    (h₂ : List.get? l n₂ = some b := by rfl) : a ↔ b :=
  h _ (List.get?_mem h₁) _ (List.get?_mem h₂)
#align list.tfae.out List.TFAE.out

end List


import data.set

/- #1

Formally prove that if there's an object, a, of some
type, α, having some property (satisfying a predicate),
P, then not every object of type α fails to have property,
P. Add a brief comment before each line of your proof
script to provide what amounts to the outline of a good
English language proof.
-/

example (α : Type) (P : α → Prop) : (∃ a, P a) → (¬(∀ x, ¬ P x)) :=
begin
end

/- Extra credit.

The converse of this proposition is clasically true. If
not every object lacks propery, P, then there must be some
object that has it. If you try to prove the converse in
our constructive logic, what happens? Show you work, and
then briefly but clearly explain exactly what goes wrong.
-/

/- #2

Consider the following binary relation, r, with domain
and co-domain both being ℕ. For each following question,
answer yes/no then briefly justify your answer.

( domain = ℕ, r = {(0,0),(1,1),(2,2)}, co-domain=ℕ )

A. Is this relation reflexive?
B. Is this relation symmetric?
C. Is this relation transitive?
D. Is this relation an equivalence relation?
-/

/- #3

A binary relation, r, is said to be *anti-symetric*
if, for all values in its domain, a and b, if r a b
and if r b a then a = b. Give an example of a familiar
arithmetic relation that's anti-symmetric, and briefly
explain why it's so.
-/

/- #4
A binary relation, r, is said to be *asymmetric* if
whenever, for any a and b, if r a b then ¬ r b a. Be
careful to note that asymmetry and antisymmetry are
different properties.  Answer each of the following
sub-questions. We give you a formal definition of anti
-/

def is_asymmetric
  {α : Type}
  (r : α → α → Prop) : Prop
  := ∀ (a b : α), r a b → ¬ r b a

/- A.

Name a familar arithmetic relation that's asymmetric
and briefly explain why you think it's asymmetric.

Answer here:
-/

/- C:

An object cannot be related to itself in an asymmetric
relation. First, complete the following informal proof
of this statement.

Proof: Assume α, r, and a are as given (and in particular
assume that r is asymmetric). Now assume r a a. <finish
the proof>.

Answer here (rest of proof):
-/

/- D.

Now prove a closely related proposition formally.
Add a comment to each line of your formal proof
so as to construct a good skeleton for a fluent
English language proof.
-/

example
  (α : Type)
  (r : α → α → Prop)
  (h : is_asymmetric r) :
¬ ∃ (a : α), r a a :=
begin
-- proof by negation

end

/- #5
Prove that equality on an inhabited (non-empty) type
is not assymetric. In the following formalization we
assume there is a value (a : α), which establishes
that α is inhabited.
-/

example (α : Type) (a : α): ¬ is_asymmetric (@eq α) :=
begin
end

/- Extra credit: What exactly goes wrong in a formal
proof if you drop the "inhibitedness" condition? Give
as much of a formal proof as you can then explain why
it can't be completed (if it can't!).
-/

/- #6
Two natural numbers, p and q, are said to be
"equivalent mod m" if p % m = q % m, which makes
"equivalence mod m" a binary relation on natural
numbers. Here's a formal definition of this binary
relation on the natural numbers (given an m).
-/

def equiv_mod_m (m : ℕ) : ℕ → ℕ → Prop :=
  λ p q : ℕ, p % m = q % m

/-
Prove using Lean's definition of "equivalence" that
equiv_mod_m is an equivalence relation for any natural
number, m. Here you'll also use Lean's definitions of
reflexive, symmetric, and transitive. They are as we
have covered in class.
-/

example : ∀ m : ℕ, equivalence (equiv_mod_m m) :=
begin
end

/- #7
Consider the relation, tin_rel, that associates people
with U.S. taxpayer id numbers, which we'll represent as
natural numbers here.

Assume this relation is single-valued. Which of the
following properties does this relation have? Give
a very brief justification of each answer. Assume
the domain is all living persons, and the co-domain
is all natural numbers.

-- it's a function:
-- it's total:
-- it's injective (where "):
-- it's surjective (where the co-domain is all ℕ):
-- it's strictly partial:
-- it's bijective:
-/

/- #8
Suppose r is the empty relation on the natural
numbers. Which of the following properties does
it have? Explain each answer enough to show you
know why your answer is correct.

-- reflexive:
-- symmetric:
-- transitive:
-/

/- #9
Here's a formal definition of this empty relation.
That there are no constructors given here means there
are no proofs, which is to say that no pair can be
proved to be in this relation, so it must be empty.
-/

inductive empty_rel : ℕ → ℕ → Prop

/-
Formally state and prove you answer for each of the
three properties. That is, for each property assert
either that empty_rel does have it or does not have it,
then prove your assertion. Include English-language
comments on each line to give the essential elements
of a good English language proof.
-/

example : ¬reflexive empty_rel :=
begin
unfold reflexive,
assume h,
let x := h 0,
cases x,
end

example : symmetric empty_rel :=
begin
unfold symmetric,
assume a b h,
cases h,
end

example : transitive empty_rel :=
begin
assume a b c h k,
cases h,
end

/- #10
A relation, r, is said to have the property of being
a *partial order* if it's reflexive, anti-symmetric,
and transitive. Give a brief English language proof
that the subset relation on the subsets of any set,
S (of objects of some type), is a partial order.

Pf:
Suppose S is a set, with a ⊆ S and b ⊆ S subsets. Then

1.
2.
3.

QED.
-/

/- #11
Finally we return again to DeMorgan's laws, but
now for sets. You'll recall that these "laws" as we
have seent them so far tell us how to distribute ¬
over ∨ and over ∧. You will also recall that set
intersection (∩) is defined by the conjunction (∧)
of the membership predicates, set union (∪) by the
disjunction (∨), and set complement (Sᶜ for a set,
S), by negation (¬). It makes sense to hypothesize
that we can recast DeMorgan's laws in terms of the
distribution of complement over set union and set
intersection. Indeed we can. Your job is to state
and prove (formally) the first DeMorgan laws for
sets, which states that the complement of a union
of any two sets equals the intersection of their
complements.

Hint: To prove that two sets are equal, S = T, use
the ext tactic. It applies a bew axiom (called set
extensionality) that states that to prove S = T it
suffices to prove S ↔ T, viewing the sets as being
defined by their logical membership predicates. You
know how to handle bi-implications. The rest you
can do by seeing "not," "and," and "or" in place
of complement, conjunction, and disjuction, resp.
-/

example
  (α : Type)
  (a b: set α) :
  (a ∪ b)ᶜ = aᶜ ∩ bᶜ :=
begin
end


/-
Copyright (c) Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Yourong Zang
-/
import analysis.calculus.cont_diff
import analysis.complex.conformal
import analysis.calculus.conformal.normed_space

/-! # Real differentiability of complex-differentiable functions

`has_deriv_at.real_of_complex` expresses that, if a function on `ℂ` is differentiable (over `ℂ`),
then its restriction to `ℝ` is differentiable over `ℝ`, with derivative the real part of the
complex derivative.

`differentiable_at.conformal_at` states that a real-differentiable function with a nonvanishing
differential from the complex plane into an arbitrary complex-normed space is conformal at a point
if it's holomorphic at that point. This is a version of Cauchy-Riemann equations.

`conformal_at_iff_differentiable_at_or_differentiable_at_comp_conj` proves that a real-differential
function with a nonvanishing differential between the complex plane is conformal at a point if and
only if it's holomorphic or antiholomorphic at that point.

## TODO

* The classical form of Cauchy-Riemann equations
* On a connected open set `u`, a function which is `conformal_at` each point is either holomorphic
throughout or antiholomorphic throughout.

## Warning

We do NOT require conformal functions to be orientation-preserving in this file.
-/

section real_deriv_of_complex
/-! ### Differentiability of the restriction to `ℝ` of complex functions -/
open complex
variables {e : ℂ → ℂ} {e' : ℂ} {z : ℝ}

/-- If a complex function is differentiable at a real point, then the induced real function is also
differentiable at this point, with a derivative equal to the real part of the complex derivative. -/
theorem has_strict_deriv_at.real_of_complex (h : has_strict_deriv_at e e' z) :
  has_strict_deriv_at (λx:ℝ, (e x).re) e'.re z :=
begin
  have A : has_strict_fderiv_at (coe : ℝ → ℂ) of_real_clm z := of_real_clm.has_strict_fderiv_at,
  have B : has_strict_fderiv_at e
    ((continuous_linear_map.smul_right 1 e' : ℂ →L[ℂ] ℂ).restrict_scalars ℝ)
    (of_real_clm z) :=
    h.has_strict_fderiv_at.restrict_scalars ℝ,
  have C : has_strict_fderiv_at re re_clm (e (of_real_clm z)) := re_clm.has_strict_fderiv_at,
  simpa using (C.comp z (B.comp z A)).has_strict_deriv_at
end

/-- If a complex function `e` is differentiable at a real point, then the function `ℝ → ℝ` given by
the real part of `e` is also differentiable at this point, with a derivative equal to the real part
of the complex derivative. -/
theorem has_deriv_at.real_of_complex (h : has_deriv_at e e' z) :
  has_deriv_at (λx:ℝ, (e x).re) e'.re z :=
begin
  have A : has_fderiv_at (coe : ℝ → ℂ) of_real_clm z := of_real_clm.has_fderiv_at,
  have B : has_fderiv_at e ((continuous_linear_map.smul_right 1 e' : ℂ →L[ℂ] ℂ).restrict_scalars ℝ)
    (of_real_clm z) :=
    h.has_fderiv_at.restrict_scalars ℝ,
  have C : has_fderiv_at re re_clm (e (of_real_clm z)) := re_clm.has_fderiv_at,
  simpa using (C.comp z (B.comp z A)).has_deriv_at
end

theorem cont_diff_at.real_of_complex {n : ℕ∞} (h : cont_diff_at ℂ n e z) :
  cont_diff_at ℝ n (λ x : ℝ, (e x).re) z :=
begin
  have A : cont_diff_at ℝ n (coe : ℝ → ℂ) z,
    from of_real_clm.cont_diff.cont_diff_at,
  have B : cont_diff_at ℝ n e z := h.restrict_scalars ℝ,
  have C : cont_diff_at ℝ n re (e z), from re_clm.cont_diff.cont_diff_at,
  exact C.comp z (B.comp z A)
end

theorem cont_diff.real_of_complex {n : ℕ∞} (h : cont_diff ℂ n e) :
  cont_diff ℝ n (λ x : ℝ, (e x).re) :=
cont_diff_iff_cont_diff_at.2 $ λ x,
  h.cont_diff_at.real_of_complex

variables {E : Type*} [normed_add_comm_group E] [normed_space ℂ E]

lemma has_strict_deriv_at.complex_to_real_fderiv' {f : ℂ → E} {x : ℂ} {f' : E}
  (h : has_strict_deriv_at f f' x) :
  has_strict_fderiv_at f (re_clm.smul_right f' + I • im_clm.smul_right f') x :=
by simpa only [complex.restrict_scalars_one_smul_right']
  using h.has_strict_fderiv_at.restrict_scalars ℝ

lemma has_deriv_at.complex_to_real_fderiv' {f : ℂ → E} {x : ℂ} {f' : E} (h : has_deriv_at f f' x) :
  has_fderiv_at f (re_clm.smul_right f' + I • im_clm.smul_right f') x :=
by simpa only [complex.restrict_scalars_one_smul_right']
  using h.has_fderiv_at.restrict_scalars ℝ

lemma has_deriv_within_at.complex_to_real_fderiv' {f : ℂ → E} {s : set ℂ} {x : ℂ} {f' : E}
  (h : has_deriv_within_at f f' s x) :
  has_fderiv_within_at f (re_clm.smul_right f' + I • im_clm.smul_right f') s x :=
by simpa only [complex.restrict_scalars_one_smul_right']
  using h.has_fderiv_within_at.restrict_scalars ℝ

lemma has_strict_deriv_at.complex_to_real_fderiv {f : ℂ → ℂ} {f' x : ℂ}
  (h : has_strict_deriv_at f f' x) :
  has_strict_fderiv_at f (f' • (1 : ℂ →L[ℝ] ℂ)) x :=
by simpa only [complex.restrict_scalars_one_smul_right]
  using h.has_strict_fderiv_at.restrict_scalars ℝ

lemma has_deriv_at.complex_to_real_fderiv {f : ℂ → ℂ} {f' x : ℂ} (h : has_deriv_at f f' x) :
  has_fderiv_at f (f' • (1 : ℂ →L[ℝ] ℂ)) x :=
by simpa only [complex.restrict_scalars_one_smul_right]
  using h.has_fderiv_at.restrict_scalars ℝ

lemma has_deriv_within_at.complex_to_real_fderiv {f : ℂ → ℂ} {s : set ℂ} {f' x : ℂ}
  (h : has_deriv_within_at f f' s x) :
  has_fderiv_within_at f (f' • (1 : ℂ →L[ℝ] ℂ)) s x :=
by simpa only [complex.restrict_scalars_one_smul_right]
  using h.has_fderiv_within_at.restrict_scalars ℝ

/-- If a complex function `e` is differentiable at a real point, then its restriction to `ℝ` is
differentiable there as a function `ℝ → ℂ`, with the same derivative. -/
lemma has_deriv_at.comp_of_real (hf : has_deriv_at e e' ↑z) : has_deriv_at (λ (y:ℝ), e ↑y) e' z :=
by simpa only [of_real_clm_apply, of_real_one, mul_one]
  using hf.comp z of_real_clm.has_deriv_at

/-- If a function `f : ℝ → ℝ` is differentiable at a (real) point `x`, then it is also
differentiable as a function `ℝ → ℂ`. -/
lemma has_deriv_at.of_real_comp {f : ℝ → ℝ} {u : ℝ} (hf : has_deriv_at f u z) :
has_deriv_at (λ (y:ℝ), ↑(f y) : ℝ → ℂ) u z :=
by simpa only [of_real_clm_apply, of_real_one, real_smul, mul_one]
  using of_real_clm.has_deriv_at.scomp z hf

end real_deriv_of_complex

section conformality
/-! ### Conformality of real-differentiable complex maps -/
open complex continuous_linear_map
open_locale complex_conjugate

variables {E : Type*} [normed_add_comm_group E] [normed_space ℂ E] {z : ℂ} {f : ℂ → E}

/-- A real differentiable function of the complex plane into some complex normed space `E` is
    conformal at a point `z` if it is holomorphic at that point with a nonvanishing differential.
    This is a version of the Cauchy-Riemann equations. -/
lemma differentiable_at.conformal_at (h : differentiable_at ℂ f z) (hf' : deriv f z ≠ 0) :
  conformal_at f z :=
begin
  rw [conformal_at_iff_is_conformal_map_fderiv, (h.has_fderiv_at.restrict_scalars ℝ).fderiv],
  apply is_conformal_map_complex_linear,
  simpa only [ne.def, ext_ring_iff]
end

/-- A complex function is conformal if and only if the function is holomorphic or antiholomorphic
    with a nonvanishing differential. -/
lemma conformal_at_iff_differentiable_at_or_differentiable_at_comp_conj {f : ℂ → ℂ} {z : ℂ} :
  conformal_at f z ↔
  (differentiable_at ℂ f z ∨ differentiable_at ℂ (f ∘ conj) (conj z)) ∧ fderiv ℝ f z ≠ 0 :=
begin
  rw conformal_at_iff_is_conformal_map_fderiv,
  rw is_conformal_map_iff_is_complex_or_conj_linear,
  apply and_congr_left,
  intros h,
  have h_diff := h.imp_symm fderiv_zero_of_not_differentiable_at,
  apply or_congr,
  { rw differentiable_at_iff_restrict_scalars ℝ h_diff },
  rw ← conj_conj z at h_diff,
  rw differentiable_at_iff_restrict_scalars ℝ (h_diff.comp _ conj_cle.differentiable_at),
  refine exists_congr (λ g, rfl.congr _),
  have : fderiv ℝ conj (conj z) = _ := conj_cle.fderiv,
  simp [fderiv.comp _ h_diff conj_cle.differentiable_at, this, conj_conj],
end

end conformality