variables p q r : Prop

namespace mth1001

section and_elimination

/-
In the following examples, we have a single premise `h : p ∧ q`.
This can be read as '`h` a proof (or the assumption) of `p ∧ q`'.
From this premise, we deduce `p`.

Note: `∧` can be entered by typing `\and`
-/

/-
The first example presents a 'term-style' proof, as opposed to the 'tactic-style' proofs in
the previous section. In standard maths, this says:

  Given `h : p ∧ q`, `p` follows by left and elimination on `h`.

We can even omit the label of the premise in standard maths and write:

  Given `p ∧ q`, `p` follows by left and elimination.

-/
example (h : p ∧ q) : p :=
h.left

-- The second term-style proof is similar, but with `1` in place of `left`.
example (h : p ∧ q) : p :=
h.1

-- Exercise 007:
-- Replace the `sorry` below with a proof and write out the example in standard maths.
example (h : p ∧ r) : p :=
sorry
-- Exercise 008:
-- Guess what to write instead of `h.left` in the next example!
example (h : p ∧ q) : q :=
sorry

-- Exercise 009:
example (k : p ∧ q) : p :=
sorry

-- Exercise 010:
-- Replace each occurrence of `sorry` below with appropriate text to
-- give a proof of `r` from the premise `k : q ∧ r`.
example (k : q ∧ r) :sorry :=
sorry

/-
Lean's 'tactic mode' admits a more relaxed proof-writing style. We'll later see that it gives
access to Lean's powerful proof automation features.

As an exercise, list the tactics you've seen so far.
-/

/-
The `exact` tactic is used to close the goal using one of the premises or statements Lean already
knows.
-/

/-
In stanard maths, the following states:

  Given `h₁ : 3 + 3 = 7` and `h₂ : 1 + 1 = 2`, `3 + 3 = 7` follows, by `h₁`.

or

  Given `3 + 3 = 7` and `1 + 1 = 2`, `3 + 3 = 7` follows, by the premise `3 + 3 = 7`.

In Lean, we close the goal using `exact h₁`.
-/
example (h₁ : 3 + 3 = 7) (h₂ : 1 + 1 = 2) : 3 + 3 = 7 :=
by exact h₁

/-
In the example above, we had two premises. The set of premises and other statements proved during
the course of a proof is called the _context_. Usually, as we work our way through a proof, we
add intermediate statements to the context as we prove them.
-/

-- Exercise 011:
-- Complete the Lean proof and translate into standard maths.
example (h₁ : 4 ≤ 0) (h₂ : 7 = 3 + 4) (h₃ : 10 ≠ 6) : 7 = 3 + 4 :=
sorry

/-
As used below, the `cases` tactic decomposese `h` into its consituents, introducing new assertions
`hp : p` and `hq : q` into the context.

This is our first multi-line tactic proof. Such proofs are enclosed between `begin` and `end`,
rather than following the word `by`.
-/
example (h : p ∧ q) : p :=
begin
  cases h with hp hq,
  exact hp,
end
/-
In standard maths, the above translates to:

  Given the premise `h : p ∧ q`, `p` follows.
  Proof: `h` decomposes into `hp : p` and `hq : q`.
  `p` follows from `hp`.

or, without labelling the premises and assertions:

  Given the premise `p ∧ q`, `p` follows.
  Proof: the premise `p ∧ q` decomposes into assertions `p` and `q`.
  `p` follows from the assertion `p`.

-/

-- There is nothing special about the names `h`, `hp`, or `hq`.
example (bob : p ∧ q) : p :=
begin
  cases bob with jane jill,
  exact jane,
end

-- As we've seen, we can use `sorry` as a placeholder that Lean expects us to
-- replace later with a term or tactic.

example (h : p ∧ q) : p :=
begin
  cases h with hp hq,
  sorry
end

example (h : p ∧ q) : p :=
begin
  cases h with hp hq,
  exact sorry
end

-- Replace the `sorry` with a valid term proof or tactic proof in the following examples.
-- As usual, also translate into standard maths.

-- Exercise 012:
example (h : (p ∧ q) ∧ r ) : q :=
begin
  sorry
end

-- Exercise 013:
-- The next example requires a proof term. It's helpful to note that applications of `.left` or
-- `.right` can be nested.
example (h : (p ∧ q) ∧ r ) : q :=
sorry
-- Exercise 014:
example (h : p ∧ (q ∧ r) ) : q :=
begin
  sorry
end

-- Exercise 015:
example (h : p ∧ p) : p :=
begin
  sorry
end

end and_elimination

/-
SUMMARY:

* And elimination (left and right).
* Tactic blocks using `begin` and `end`.
* The `exact` tactic.
* The `cases` tactic.

-/

end mth1001


import ..lectures.love05_inductive_predicates_demo
import ..lectures.love13_rational_and_real_numbers_demo

/-! # LoVe Exercise 13: Rational and Real Numbers -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1: Rationals
1.1. Prove the following lemma.
Hint: The lemma `fraction.mk.inj_eq` might be useful. -/

#check fraction.mk.inj_eq

lemma fraction.ext (a b : fraction) (hnum : fraction.num a = fraction.num b)
    (hdenom : fraction.denom a = fraction.denom b) :
  a = b :=
begin
  cases' a,
  cases' b,
  rw fraction.mk.inj_eq,
  exact and.intro hnum hdenom
end

/-! 1.2. Extending the `fraction.has_mul` instance from the lecture, declare
`fraction` as an instance of `semigroup`.
Hint: Use the lemma `fraction.ext` above, and possibly `fraction.mul_num`, and
`fraction.mul_denom`. -/

#check fraction.ext
#check fraction.mul_num
#check fraction.mul_denom

@[instance] def fraction.semigroup : semigroup fraction :=
{ mul_assoc :=
    begin
      intros,
      apply fraction.ext,
      repeat {
        simp [fraction.mul_num, fraction.mul_denom],
        cc }
    end,
  ..fraction.has_mul }

/-! 1.3. Extending the `rat.has_mul` instance from the lecture, declare `rat` as
an instance of `semigroup`. -/

@[instance] def rat.semigroup : semigroup rat :=
{ mul_assoc :=
    begin
      intros x y z,
      apply quotient.induction_on x,
      apply quotient.induction_on y,
      apply quotient.induction_on z,
      intros a b c,
      apply quotient.sound,
      rw mul_assoc
    end,
  ..rat.has_mul }

end LoVe


/-
Copyright (c) 2021 James Arthur, Benjamin Davidson, Andrew Souther. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: James Arthur, Benjamin Davidson, Andrew Souther
-/
import measure_theory.interval_integral
import analysis.special_functions.sqrt

/-!
# Freek № 9: The Area of a Circle

In this file we show that the area of a disc with nonnegative radius `r` is `π * r^2`. The main
tools our proof uses are `volume_region_between_eq_integral`, which allows us to represent the area
of the disc as an integral, and `interval_integral.integral_eq_sub_of_has_deriv_at'_of_le`, the
second fundamental theorem of calculus.

We begin by defining `disc` in `ℝ × ℝ`, then show that `disc` can be represented as the
`region_between` two functions.

Though not necessary for the main proof, we nonetheless choose to include a proof of the
measurability of the disc in order to convince the reader that the set whose volume we will be
calculating is indeed measurable and our result is therefore meaningful.

In the main proof, `area_disc`, we use `volume_region_between_eq_integral` followed by
`interval_integral.integral_of_le` to reduce our goal to a single `interval_integral`:
  `∫ (x : ℝ) in -r..r, 2 * sqrt (r ^ 2 - x ^ 2) = π * r ^ 2`.
After disposing of the trivial case `r = 0`, we show that `λ x, 2 * sqrt (r ^ 2 - x ^ 2)` is equal
to the derivative of `λ x, r ^ 2 * arcsin (x / r) + x * sqrt (r ^ 2 - x ^ 2)` everywhere on
`Ioo (-r) r` and that those two functions are continuous, then apply the second fundamental theorem
of calculus with those facts. Some simple algebra then completes the proof.

Note that we choose to define `disc` as a set of points in `ℝ ⨯ ℝ`. This is admittedly not ideal; it
would be more natural to define `disc` as a `metric.ball` in `euclidean_space ℝ (fin 2)` (as well as
to provide a more general proof in higher dimensions). However, our proof indirectly relies on a
number of theorems (particularly `measure_theory.measure.prod_apply`) which do not yet exist for
Euclidean space, thus forcing us to use this less-preferable definition. As `measure_theory.pi`
continues to develop, it should eventually become possible to redefine `disc` and extend our proof
to the n-ball.
-/

open set real measure_theory interval_integral
open_locale real nnreal

/-- A disc of radius `r` is defined as the collection of points `(p.1, p.2)` in `ℝ × ℝ` such that
  `p.1 ^ 2 + p.2 ^ 2 < r ^ 2`.
  Note that this definition is not equivalent to `metric.ball (0 : ℝ × ℝ) r`. This was done
  intentionally because `dist` in `ℝ × ℝ` is defined as the uniform norm, making the `metric.ball`
  in `ℝ × ℝ` a square, not a disc.
  See the module docstring for an explanation of why we don't define the disc in Euclidean space. -/
def disc (r : ℝ) := {p : ℝ × ℝ | p.1 ^ 2 + p.2 ^ 2 < r ^ 2}

variable (r : ℝ≥0)

/-- A disc of radius `r` can be represented as the region between the two curves
  `λ x, - sqrt (r ^ 2 - x ^ 2)` and `λ x, sqrt (r ^ 2 - x ^ 2)`. -/
lemma disc_eq_region_between :
  disc r = region_between (λ x, -sqrt (r^2 - x^2)) (λ x, sqrt (r^2 - x^2)) (Ioc (-r) r) :=
begin
  ext p,
  simp only [disc, region_between, mem_set_of_eq, mem_Ioo, mem_Ioc, pi.neg_apply],
  split;
  intro h,
  { cases abs_lt_of_sq_lt_sq' (lt_of_add_lt_of_nonneg_left h (sq_nonneg p.2)) r.2,
    rw [add_comm, ← lt_sub_iff_add_lt] at h,
    exact ⟨⟨left, right.le⟩, sq_lt.mp h⟩ },
  { rw [add_comm, ← lt_sub_iff_add_lt],
    exact sq_lt.mpr h.2 },
end

/-- The disc is a `measurable_set`. -/
theorem measurable_set_disc : measurable_set (disc r) :=
by apply measurable_set_lt; apply continuous.measurable; continuity

/-- The area of a disc with radius `r` is `π * r ^ 2`. -/
theorem area_disc : volume (disc r) = nnreal.pi * r ^ 2 :=
begin
  let f := λ x, sqrt (r ^ 2 - x ^ 2),
  let F := λ x, (r:ℝ) ^ 2 * arcsin (r⁻¹ * x) + x * sqrt (r ^ 2 - x ^ 2),
  have hf : continuous f := by continuity,
  suffices : ∫ x in -r..r, 2 * f x = nnreal.pi * r ^ 2,
  { have h : integrable_on f (Ioc (-r) r) :=
      (hf.integrable_on_compact compact_Icc).mono_set Ioc_subset_Icc_self,
    calc  volume (disc r)
        = volume (region_between (λ x, -f x) f (Ioc (-r) r)) : by rw disc_eq_region_between
    ... = ennreal.of_real (∫ x in Ioc (-r:ℝ) r, (f - has_neg.neg ∘ f) x) :
          volume_region_between_eq_integral
            h.neg h measurable_set_Ioc (λ x hx, neg_le_self (sqrt_nonneg _))
    ... = ennreal.of_real (∫ x in (-r:ℝ)..r, 2 * f x) : by simp [two_mul, integral_of_le]
    ... = nnreal.pi * r ^ 2 : by rw_mod_cast [this, ← ennreal.coe_nnreal_eq], },
  obtain ⟨hle, (heq | hlt)⟩ := ⟨nnreal.coe_nonneg r, hle.eq_or_lt⟩, { simp [← heq] },
  have hderiv : ∀ x ∈ Ioo (-r:ℝ) r, has_deriv_at F (2 * f x) x,
  { rintros x ⟨hx1, hx2⟩,
    convert ((has_deriv_at_const x ((r:ℝ)^2)).mul ((has_deriv_at_arcsin _ _).comp x
      ((has_deriv_at_const x (r:ℝ)⁻¹).mul (has_deriv_at_id' x)))).add
        ((has_deriv_at_id' x).mul (((has_deriv_at_id' x).pow.const_sub ((r:ℝ)^2)).sqrt _)),
    { have h : sqrt (1 - x ^ 2 / r ^ 2) * r = sqrt (r ^ 2 - x ^ 2),
      { rw [← sqrt_sq hle, ← sqrt_mul, sub_mul, sqrt_sq hle, div_mul_eq_mul_div_comm,
            div_self (pow_ne_zero 2 hlt.ne'), one_mul, mul_one],
        simpa [sqrt_sq hle, div_le_one (pow_pos hlt 2)] using sq_le_sq' hx1.le hx2.le },
      field_simp,
      rw [h, mul_left_comm, ← sq, neg_mul_eq_mul_neg, mul_div_mul_left (-x^2) _ two_ne_zero,
          add_left_comm, div_add_div_same, tactic.ring.add_neg_eq_sub, div_sqrt, two_mul] },
    { suffices : -(1:ℝ) < r⁻¹ * x, by exact this.ne',
      calc -(1:ℝ) = r⁻¹ * -r : by simp [hlt.ne']
              ... < r⁻¹ * x : by nlinarith [inv_pos.mpr hlt] },
    { suffices : (r:ℝ)⁻¹ * x < 1, by exact this.ne,
      calc (r:ℝ)⁻¹ * x < r⁻¹ * r : by nlinarith [inv_pos.mpr hlt]
                   ... = 1 : inv_mul_cancel hlt.ne' },
    { nlinarith } },
  have hcont := (by continuity : continuous F).continuous_on,
  have hcont' := (continuous_const.mul hf).continuous_on,
  calc  ∫ x in -r..r, 2 * f x
      = F r - F (-r) : integral_eq_sub_of_has_deriv_at'_of_le (neg_le_self r.2) hcont hderiv hcont'
  ... = nnreal.pi * r ^ 2 : by norm_num [F, inv_mul_cancel hlt.ne', ← mul_div_assoc, mul_comm π],
end


/-
Copyright (c) 2023 Peter Nelson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Peter Nelson
-/
import data.finite.card
import algebra.big_operators.finprod

/-!
# Noncomputable Set Cardinality

We define the cardinality `set.ncard s` of a set `s` as a natural number. This function is
noncomputable (being defined in terms of `nat.card`) and takes the value `0` if `s` is infinite.

This can be seen as an API for `nat.card α` in the special case where `α` is a subtype arising from
a set. It is intended as an alternative to `finset.card` and `fintype.card`,  both of which contain
data in their definition that can cause awkwardness when using `set.to_finset`.  Using `set.ncard`
allows cardinality computations to avoid `finset`/`fintype` completely, staying in `set` and letting
finiteness be handled explicitly, or (where a `finite α` instance is present and the sets are
in `set α`) via `auto_param`s.

## Main Definitions

* `set.ncard s` is the cardinality of the set `s` as a natural number, provided `s` is finite.
  If `s` is infinite, then `set.ncard s = 0`.
* `to_finite_tac` is a tactic that tries to synthesize an `set.finite s` argument with
  `set.to_finite`. This will work for `s : set α` where there is a `finite α` instance.

## Implementation Notes

The lemmas in this file are very similar to those in `data.finset.card`, but with `set` operations
instead of `finset`; most of the proofs invoke their `finset` analogues. Nearly all the lemmas
require finiteness of one or more of their arguments. We provide this assumption with an
`auto_param` argument of the form `(hs : s.finite . to_finite_tac)`, where `to_finite_tac` will find
a `finite s` term in the cases where `s` is a set in a `finite` type.

Often, where there are two set arguments `s` and `t`, the finiteness of one follows from the other
in the context of the lemma, in which case we only include the ones that are needed, and derive the
other inside the proof. A few of the lemmas, such as `ncard_union_le` do not require finiteness
arguments; they are are true by coincidence due to junk values.
-/

open_locale classical
open_locale big_operators

variables {α β : Type*} {s t : set α} {a b x y : α} {f : α → β}

namespace set

/-- The cardinality of `s : set α` . Has the junk value `0` if `s` is infinite -/
noncomputable def ncard (s : set α) := nat.card s

/-- A tactic that finds a `t.finite` term for a set `t` in a `finite` type. -/
meta def to_finite_tac : tactic unit := `[exact set.to_finite _]

lemma ncard_def (s : set α) : s.ncard = nat.card s := rfl

lemma ncard_eq_to_finset_card (s : set α) (hs : s.finite . to_finite_tac) :
  s.ncard = hs.to_finset.card :=
by rw [ncard_def, @nat.card_eq_fintype_card _ hs.fintype,
  @finite.card_to_finset _ _ hs.fintype hs]

lemma ncard_le_of_subset (hst : s ⊆ t) (ht : t.finite . to_finite_tac) :
  s.ncard ≤ t.ncard :=
@finite.card_le_of_embedding _ _ (finite_coe_iff.mpr ht) (set.embedding_of_subset _ _ hst)

lemma ncard_mono [finite α] :
  @monotone (set α) _ _ _ ncard :=
λ _ _, ncard_le_of_subset

@[simp] lemma ncard_eq_zero (hs : s.finite . to_finite_tac) :
  s.ncard = 0 ↔ s = ∅ :=
by simp [ncard_def, @finite.card_eq_zero_iff _ hs.to_subtype]

@[simp] lemma ncard_coe_finset (s : finset α) :
  (s : set α).ncard = s.card :=
by rw [ncard_eq_to_finset_card, finset.finite_to_set_to_finset]

lemma infinite.ncard (hs : s.infinite) :
  s.ncard = 0 :=
@nat.card_eq_zero_of_infinite _ hs.to_subtype

lemma ncard_univ (α : Type*):
  (univ : set α).ncard = nat.card α :=
begin
  cases finite_or_infinite α with h h,
  { haveI := @fintype.of_finite α h,
    rw [ncard_eq_to_finset_card, finite.to_finset_univ, finset.card_univ,
      nat.card_eq_fintype_card]},
  rw [(@infinite_univ _ h).ncard, @nat.card_eq_zero_of_infinite _ h],
end

@[simp] lemma ncard_empty (α : Type*) :
  (∅ : set α).ncard = 0 :=
by simp only [ncard_eq_zero]

lemma ncard_pos (hs : s.finite . to_finite_tac) :
  0 < s.ncard ↔ s.nonempty :=
by rw [pos_iff_ne_zero, ne.def, ncard_eq_zero hs, nonempty_iff_ne_empty]

lemma ncard_ne_zero_of_mem (h : a ∈ s) (hs : s.finite . to_finite_tac):
  s.ncard ≠ 0 :=
((ncard_pos hs).mpr ⟨a,h⟩).ne.symm

lemma finite_of_ncard_ne_zero (hs : s.ncard ≠ 0) :
  s.finite :=
s.finite_or_infinite.elim id (λ h, (hs h.ncard).elim)

lemma finite_of_ncard_pos (hs : 0 < s.ncard) :
  s.finite :=
finite_of_ncard_ne_zero hs.ne.symm

lemma nonempty_of_ncard_ne_zero (hs : s.ncard ≠ 0) :
  s.nonempty :=
by {rw nonempty_iff_ne_empty, rintro rfl, simpa using hs}

@[simp] lemma ncard_singleton (a : α) :
  ({a} : set α).ncard = 1 :=
by simp [ncard_eq_to_finset_card]

lemma ncard_singleton_inter : ({a} ∩ s).ncard ≤ 1 :=
begin
  rw [←inter_self {a}, inter_assoc, ncard_eq_to_finset_card,
    finite.to_finset_inter, finite.to_finset_singleton],
  { apply finset.card_singleton_inter},
  all_goals {apply to_finite},
end

section insert_erase

@[simp] lemma ncard_insert_of_not_mem (h : a ∉ s) (hs : s.finite . to_finite_tac) :
  (insert a s).ncard = s.ncard + 1 :=
begin
  haveI := hs.fintype,
  rw [ncard_eq_to_finset_card, ncard_eq_to_finset_card, finite.to_finset_insert,
    finset.card_insert_of_not_mem],
  rwa [finite.mem_to_finset],
end

lemma ncard_insert_of_mem (h : a ∈ s) :
  ncard (insert a s) = s.ncard :=
by rw insert_eq_of_mem h

lemma card_insert_le (a : α) (s : set α) : (insert a s).ncard ≤ s.ncard + 1 :=
begin
  obtain (hs | hs) := s.finite_or_infinite,
  { exact (em (a ∈ s)).elim (λ h, (ncard_insert_of_mem h).trans_le (nat.le_succ _))
      (λ h, by rw ncard_insert_of_not_mem h hs)},
  rw (hs.mono (subset_insert a s)).ncard,
  exact nat.zero_le _,
end

lemma ncard_insert_eq_ite (hs : s.finite . to_finite_tac) :
  ncard (insert a s) = if a ∈ s then s.ncard else s.ncard + 1 :=
begin
  by_cases h : a ∈ s,
  { rw [ncard_insert_of_mem h, if_pos h] },
  { rw [ncard_insert_of_not_mem h hs, if_neg h] }
end

@[simp] lemma card_doubleton (h : a ≠ b) : ({a, b} : set α).ncard = 2 :=
by {rw [ncard_insert_of_not_mem, ncard_singleton], simpa}

@[simp] lemma ncard_diff_singleton_add_one (h : a ∈ s) (hs : s.finite . to_finite_tac) :
  (s \ {a}).ncard + 1 = s.ncard :=
begin
  have h' : a ∉ s \ {a}, by {rw [mem_diff_singleton], tauto},
  rw ←ncard_insert_of_not_mem h' (hs.diff {a}),
  congr',
  simpa,
end

@[simp] lemma ncard_diff_singleton_of_mem (h : a ∈ s) (hs : s.finite . to_finite_tac) :
  (s \ {a}).ncard = s.ncard - 1 :=
eq_tsub_of_add_eq (ncard_diff_singleton_add_one h hs)

lemma ncard_diff_singleton_lt_of_mem (h : a ∈ s) (hs : s.finite . to_finite_tac) :
  (s \ {a}).ncard < s.ncard :=
by {rw [←ncard_diff_singleton_add_one h hs], apply lt_add_one}

lemma ncard_diff_singleton_le (s : set α) (a : α) :
  (s \ {a}).ncard ≤ s.ncard :=
begin
  obtain (hs | hs) := s.finite_or_infinite,
  { apply ncard_le_of_subset (diff_subset _ _) hs},
  convert zero_le _,
  exact (hs.diff (by simp : set.finite {a})).ncard,
end

lemma pred_ncard_le_ncard_diff_singleton (s : set α) (a : α) :
  s.ncard - 1 ≤ (s \ {a}).ncard :=
begin
  cases s.finite_or_infinite with hs hs,
  { by_cases h : a ∈ s,
    { rw ncard_diff_singleton_of_mem h hs, },
    rw diff_singleton_eq_self h,
    apply nat.pred_le},
  convert nat.zero_le _,
  rw hs.ncard,
end

lemma ncard_exchange (ha : a ∉ s) (hb : b ∈ s) :
  (insert a (s \ {b})).ncard = s.ncard :=
begin
  cases s.finite_or_infinite with h h,
  { haveI := h.to_subtype,
    rw [ncard_insert_of_not_mem, ncard_diff_singleton_add_one hb],
    simpa only [mem_diff, not_and] using ha},
  rw [((h.diff (set.to_finite {b})).mono (subset_insert _ _)).ncard, h.ncard],
end

lemma ncard_exchange' (ha : a ∉ s) (hb : b ∈ s) :
  ((insert a s) \ {b}).ncard = s.ncard :=
by rw [←ncard_exchange ha hb, ←singleton_union, ←singleton_union, union_diff_distrib,
    @diff_singleton_eq_self _ b {a} (λ h, ha (by rwa ← mem_singleton_iff.mp h) )]

end insert_erase

lemma ncard_image_le (hs : s.finite . to_finite_tac) :
  (f '' s).ncard ≤ s.ncard :=
begin
  rw ncard_eq_to_finset_card s hs,
  haveI := hs.fintype,
  convert @finset.card_image_le _ _ s.to_finset f _,
  rw [ncard_eq_to_finset_card, finite.to_finset_image _ hs],
  { congr', rw [←finset.coe_inj, finite.coe_to_finset, coe_to_finset]},
  { apply_instance},
  rw [←finset.coe_inj, finite.coe_to_finset, coe_to_finset],
end

lemma ncard_image_of_inj_on (H : set.inj_on f s) :
  (f '' s).ncard = s.ncard :=
begin
  cases s.finite_or_infinite,
  { haveI := @fintype.of_finite s h.to_subtype,
    haveI := @fintype.of_finite _ (h.image f).to_subtype,
    convert card_image_of_inj_on H; simp [ncard_def]},
  rw [h.ncard, ((infinite_image_iff H).mpr h).ncard],
end

lemma inj_on_of_ncard_image_eq (h : (f '' s).ncard = s.ncard) (hs : s.finite . to_finite_tac) :
  set.inj_on f s :=
begin
  haveI := hs.fintype,
  haveI := ((to_finite s).image f).fintype,
  simp_rw ncard_eq_to_finset_card at h,
  rw ← coe_to_finset s,
  apply finset.inj_on_of_card_image_eq,
  convert h,
  ext,
  simp,
end

lemma ncard_image_iff (hs : s.finite . to_finite_tac) :
  (f '' s).ncard = s.ncard ↔ set.inj_on f s :=
⟨λ h, inj_on_of_ncard_image_eq h hs, ncard_image_of_inj_on⟩

lemma ncard_image_of_injective (s : set α) (H : f.injective) :
  (f '' s).ncard = s.ncard :=
ncard_image_of_inj_on $ λ x _ y _ h, H h

lemma ncard_preimage_of_injective_subset_range {s : set β} (H : f.injective)
(hs : s ⊆ set.range f) :
  (f ⁻¹' s).ncard = s.ncard :=
by rw [←ncard_image_of_injective _ H, image_preimage_eq_iff.mpr hs]

lemma fiber_ncard_ne_zero_iff_mem_image {y : β} (hs : s.finite . to_finite_tac) :
  {x ∈ s | f x = y}.ncard ≠ 0 ↔ y ∈ f '' s :=
begin
  refine ⟨nonempty_of_ncard_ne_zero, _⟩,
  rintros ⟨z,hz,rfl⟩,
  exact @ncard_ne_zero_of_mem _ {x ∈ s | f x = f z} z (mem_sep hz rfl)
    (hs.subset (sep_subset _ _)),
end

@[simp] lemma ncard_map (f : α ↪ β) :
  (f '' s).ncard = s.ncard :=
ncard_image_of_injective _ f.injective

@[simp] lemma ncard_subtype (P : α → Prop) (s : set α) :
  {x : subtype P | (x : α) ∈ s}.ncard = (s ∩ (set_of P)).ncard :=
begin
  convert (ncard_image_of_injective _ (@subtype.coe_injective _ P)).symm,
  ext, rw inter_comm, simp,
end

lemma ncard_inter_le_ncard_left (s t : set α) (hs : s.finite . to_finite_tac) :
  (s ∩ t).ncard ≤ s.ncard :=
ncard_le_of_subset (inter_subset_left _ _) hs

lemma ncard_inter_le_ncard_right (s t : set α) (ht : t.finite . to_finite_tac) :
  (s ∩ t).ncard ≤ t.ncard :=
ncard_le_of_subset (inter_subset_right _ _) ht

lemma eq_of_subset_of_ncard_le (h : s ⊆ t) (h' : t.ncard ≤ s.ncard)
(ht : t.finite . to_finite_tac) :
  s = t :=
begin
  haveI := ht.fintype,
  haveI := (ht.subset h).fintype,
  rw ←@to_finset_inj,
  apply finset.eq_of_subset_of_card_le,
  { simpa, },
  rw [ncard_eq_to_finset_card _ ht, ncard_eq_to_finset_card _ (ht.subset h)] at h',
  convert h',
end

lemma subset_iff_eq_of_ncard_le (h : t.ncard ≤ s.ncard) (ht : t.finite . to_finite_tac) :
  s ⊆ t ↔ s = t :=
⟨λ hst, eq_of_subset_of_ncard_le hst h ht, eq.subset'⟩

lemma map_eq_of_subset {f : α ↪ α} (h : f '' s ⊆ s) (hs : s.finite . to_finite_tac) :
  f '' s = s :=
eq_of_subset_of_ncard_le h (ncard_map _).ge hs

lemma sep_of_ncard_eq {P : α → Prop} (h : {x ∈ s | P x}.ncard = s.ncard) (ha : a ∈ s)
(hs : s.finite . to_finite_tac) :
  P a :=
sep_eq_self_iff_mem_true.mp (eq_of_subset_of_ncard_le (by simp) h.symm.le hs) _ ha

lemma ncard_lt_ncard (h : s ⊂ t) (ht : t.finite . to_finite_tac) :
  s.ncard < t.ncard :=
begin
  rw [ncard_eq_to_finset_card _ (ht.subset h.subset), ncard_eq_to_finset_card t ht],
  refine finset.card_lt_card _,
  rwa [finite.to_finset_ssubset_to_finset],
end

lemma ncard_strict_mono [finite α] :
  @strict_mono (set α) _ _ _ ncard :=
λ _ _ h, ncard_lt_ncard h

lemma ncard_eq_of_bijective {n : ℕ} (f : ∀ i, i < n → α)
  (hf : ∀ a ∈ s, ∃ i, ∃ h : i < n, f i h = a)
  (hf' : ∀ i (h : i < n), f i h ∈ s)
  (f_inj : ∀ i j (hi : i < n) (hj : j < n), f i hi = f j hj → i = j)
  (hs : s.finite . to_finite_tac) :
  s.ncard = n :=
begin
  rw ncard_eq_to_finset_card _ hs,
  apply finset.card_eq_of_bijective,
  all_goals {simpa},
end

lemma ncard_congr {t : set β} (f : Π a ∈ s, β) (h₁ : ∀ a ha, f a ha ∈ t)
(h₂ : ∀ a b ha hb, f a ha = f b hb → a = b) (h₃ : ∀ b ∈ t, ∃ a ha, f a ha = b)
(hs : s.finite . to_finite_tac) :
  s.ncard = t.ncard :=
begin
  set f' : s → t := λ x, ⟨f x.1 x.2, h₁ _ _⟩ with hf',
  have hbij : f'.bijective,
  { split,
    { rintros ⟨x,hx⟩ ⟨y,hy⟩ hxy,
      simp only [hf', subtype.val_eq_coe, subtype.coe_mk, subtype.mk_eq_mk] at hxy ⊢,
      apply h₂ _ _ hx hy hxy},
    rintro ⟨y,hy⟩,
    obtain ⟨a, ha, rfl⟩ := h₃ y hy,
    simp only [subtype.val_eq_coe, subtype.coe_mk, subtype.mk_eq_mk, set_coe.exists],
    exact ⟨_,ha,rfl⟩},
  haveI := hs.to_subtype,
  haveI := @fintype.of_finite _ (finite.of_bijective hbij),
  haveI := fintype.of_finite s,
  convert fintype.card_of_bijective hbij,
  rw [ncard_def, nat.card_eq_fintype_card],
  rw [ncard_def, nat.card_eq_fintype_card],
end

lemma ncard_le_ncard_of_inj_on {t : set β} (f : α → β) (hf : ∀ a ∈ s, f a ∈ t)
(f_inj : inj_on f s) (ht : t.finite . to_finite_tac) :
  s.ncard ≤ t.ncard :=
begin
  cases s.finite_or_infinite,
  { haveI := h.to_subtype,
    rw [ncard_eq_to_finset_card _ ht, ncard_eq_to_finset_card _ (to_finite s)],
    exact finset.card_le_card_of_inj_on f (by simpa) (by simpa)},
  convert nat.zero_le _,
  rw h.ncard,
end

lemma exists_ne_map_eq_of_ncard_lt_of_maps_to {t : set β} (hc : t.ncard < s.ncard)
{f : α → β} (hf : ∀ a ∈ s, f a ∈ t) (ht : t.finite . to_finite_tac) :
  ∃ (x ∈ s) (y ∈ s), x ≠ y ∧ f x = f y :=
begin
  by_contra h',
  simp only [ne.def, exists_prop, not_exists, not_and, not_imp_not] at h',
  exact (ncard_le_ncard_of_inj_on f hf h' ht).not_lt hc,
end

lemma le_ncard_of_inj_on_range {n : ℕ} (f : ℕ → α) (hf : ∀ i < n, f i ∈ s)
  (f_inj : ∀ (i < n) (j < n), f i = f j → i = j) (hs : s.finite . to_finite_tac):
  n ≤ s.ncard :=
by {rw ncard_eq_to_finset_card _ hs, apply finset.le_card_of_inj_on_range; simpa}

lemma surj_on_of_inj_on_of_ncard_le {t : set β} (f : Π a ∈ s, β)
(hf : ∀ a ha, f a ha ∈ t) (hinj : ∀ a₁ a₂ ha₁ ha₂, f a₁ ha₁ = f a₂ ha₂ → a₁ = a₂)
(hst : t.ncard ≤ s.ncard) (ht : t.finite . to_finite_tac) :
  ∀ b ∈ t, ∃ a ha, b = f a ha :=
begin
  intros b hb,
  set f' : s → t := λ x, ⟨f x.1 x.2, hf _ _⟩ with hf',
  have finj: f'.injective,
  { rintros ⟨x,hx⟩ ⟨y,hy⟩ hxy,
    simp only [hf', subtype.val_eq_coe, subtype.coe_mk, subtype.mk_eq_mk] at hxy ⊢,
    apply hinj _ _ hx hy hxy},
  haveI := ht.fintype,
  haveI := fintype.of_injective f' finj,
  simp_rw [ncard_eq_to_finset_card] at hst,
  set f'' : ∀ a, a ∈ s.to_finset → β := λ a h, f a (by simpa using h) with hf'',
  convert @finset.surj_on_of_inj_on_of_card_le _ _ _ t.to_finset f'' (by simpa) (by simpa)
    (by convert hst) b (by simpa),
  simp,
end

lemma inj_on_of_surj_on_of_ncard_le {t : set β} (f : Π a ∈ s, β) (hf : ∀ a ha, f a ha ∈ t)
(hsurj : ∀ b ∈ t, ∃ a ha, b = f a ha) (hst : s.ncard ≤ t.ncard) ⦃a₁ a₂⦄ (ha₁ : a₁ ∈ s)
(ha₂ : a₂ ∈ s) (ha₁a₂: f a₁ ha₁ = f a₂ ha₂) (hs : s.finite . to_finite_tac) :
  a₁ = a₂ :=
begin
   set f' : s → t := λ x, ⟨f x.1 x.2, hf _ _⟩ with hf',
  have hsurj : f'.surjective,
  { rintro ⟨y,hy⟩,
    obtain ⟨a, ha, rfl⟩ := hsurj y hy,
    simp only [subtype.val_eq_coe, subtype.coe_mk, subtype.mk_eq_mk, set_coe.exists],
    exact ⟨_,ha,rfl⟩},
  haveI := hs.fintype,
  haveI := fintype.of_surjective _ hsurj,
  simp_rw [ncard_eq_to_finset_card] at hst,
  set f'' : ∀ a, a ∈ s.to_finset → β := λ a h, f a (by simpa using h) with hf'',
  exact @finset.inj_on_of_surj_on_of_card_le _ _ _ t.to_finset f'' (by simpa) (by simpa)
    (by convert hst) a₁ a₂ (by simpa) (by simpa) (by simpa),
end

section lattice

lemma ncard_union_add_ncard_inter (s t : set α) (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac) :
  (s ∪ t).ncard + (s ∩ t).ncard = s.ncard + t.ncard :=
begin
  have hu := hs.union ht,
  have hi := (hs.subset (inter_subset_left s t)),
  rw [ncard_eq_to_finset_card _ hs, ncard_eq_to_finset_card _ ht, ncard_eq_to_finset_card _ hu,
    ncard_eq_to_finset_card _ hi, finite.to_finset_union, finite.to_finset_inter],
  { exact finset.card_union_add_card_inter _ _},
end

lemma ncard_inter_add_ncard_union (s t : set α)
(hs : s.finite . to_finite_tac) (ht : t.finite . to_finite_tac) :
  (s ∩ t).ncard + (s ∪ t).ncard = s.ncard + t.ncard :=
by rw [add_comm, ncard_union_add_ncard_inter _ _ hs ht]

lemma ncard_union_le (s t : set α) :
  (s ∪ t).ncard ≤ s.ncard + t.ncard :=
begin
  cases (s ∪ t).finite_or_infinite,
  { have hs := h.subset (subset_union_left s t),
    have ht := h.subset (subset_union_right s t),
    rw [ncard_eq_to_finset_card _ hs, ncard_eq_to_finset_card _ ht, ncard_eq_to_finset_card _ h,
      finite.to_finset_union],
    exact finset.card_union_le _ _},
  convert nat.zero_le _,
  rw h.ncard,
end

lemma ncard_union_eq (h : disjoint s t) (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac) :
  (s ∪ t).ncard = s.ncard + t.ncard :=
begin
  rw [ncard_eq_to_finset_card _ hs, ncard_eq_to_finset_card _ ht,
    ncard_eq_to_finset_card _ (hs.union ht),finite.to_finset_union],
  refine finset.card_union_eq _,
  rwa [finite.disjoint_to_finset],
end

lemma ncard_diff_add_ncard_eq_ncard (h : s ⊆ t) (ht : t.finite . to_finite_tac) :
  (t \ s).ncard + s.ncard = t.ncard :=
begin
  rw [ncard_eq_to_finset_card _ ht, ncard_eq_to_finset_card _ (ht.subset h),
      ncard_eq_to_finset_card _ (ht.diff s), finite.to_finset_diff],
  refine finset.card_sdiff_add_card_eq_card _,
  rwa finite.to_finset_subset_to_finset,
end

lemma ncard_diff (h : s ⊆ t) (ht : t.finite . to_finite_tac) :
  (t \ s).ncard = t.ncard - s.ncard :=
by rw [←ncard_diff_add_ncard_eq_ncard h ht, add_tsub_cancel_right]

lemma ncard_le_ncard_diff_add_ncard (s t : set α) (ht : t.finite . to_finite_tac) :
  s.ncard ≤ (s \ t).ncard + t.ncard :=
begin
  cases s.finite_or_infinite,
  { rw [←diff_inter_self_eq_diff, ←ncard_diff_add_ncard_eq_ncard (inter_subset_right t s) h,
      add_le_add_iff_left],
    apply ncard_inter_le_ncard_left _ _ ht,},
  convert nat.zero_le _,
  rw h.ncard,
end

lemma le_ncard_diff (s t : set α) (hs : s.finite . to_finite_tac) :
  t.ncard - s.ncard ≤ (t \ s).ncard :=
begin
  refine tsub_le_iff_left.mpr _,
  rw add_comm,
  apply ncard_le_ncard_diff_add_ncard _ _ hs,
end

lemma ncard_diff_add_ncard (s t : set α) (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac):
  (s \ t).ncard + t.ncard = (s ∪ t).ncard :=
by rw [←union_diff_right,ncard_diff_add_ncard_eq_ncard (subset_union_right s t) (hs.union ht)]

lemma diff_nonempty_of_ncard_lt_ncard (h : s.ncard < t.ncard) (hs : s.finite . to_finite_tac) :
  (t \ s).nonempty :=
begin
  rw [set.nonempty_iff_ne_empty, ne.def, diff_eq_empty],
  exact λ h', h.not_le (ncard_le_of_subset h' hs),
end

lemma exists_mem_not_mem_of_ncard_lt_ncard (h : s.ncard < t.ncard) (hs : s.finite . to_finite_tac) :
  ∃ e, e ∈ t ∧ e ∉ s :=
diff_nonempty_of_ncard_lt_ncard h hs

@[simp] lemma ncard_inter_add_ncard_diff_eq_ncard (s t : set α) (hs : s.finite . to_finite_tac) :
  (s ∩ t).ncard + (s \ t).ncard = s.ncard :=
by rw [←ncard_diff_add_ncard_eq_ncard (diff_subset s t) hs, sdiff_sdiff_right_self, inf_eq_inter]

lemma ncard_eq_ncard_iff_ncard_diff_eq_ncard_diff (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac) :
  s.ncard = t.ncard ↔ (s \ t).ncard = (t \ s).ncard :=
by rw [←ncard_inter_add_ncard_diff_eq_ncard s t hs, ←ncard_inter_add_ncard_diff_eq_ncard t s ht,
    inter_comm, add_right_inj]

lemma ncard_le_ncard_iff_ncard_diff_le_ncard_diff (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac)  :
  s.ncard ≤ t.ncard ↔ (s \ t).ncard ≤ (t \ s).ncard :=
by rw [←ncard_inter_add_ncard_diff_eq_ncard s t hs, ←ncard_inter_add_ncard_diff_eq_ncard t s ht,
     inter_comm, add_le_add_iff_left]

lemma ncard_lt_ncard_iff_ncard_diff_lt_ncard_diff (hs : s.finite . to_finite_tac)
(ht : t.finite . to_finite_tac)  :
  s.ncard < t.ncard ↔ (s \ t).ncard < (t \ s).ncard :=
by rw [←ncard_inter_add_ncard_diff_eq_ncard s t hs, ←ncard_inter_add_ncard_diff_eq_ncard t s ht,
     inter_comm, add_lt_add_iff_left]

lemma ncard_add_ncard_compl (s : set α) (hs : s.finite . to_finite_tac)
(hsc : sᶜ.finite . to_finite_tac) :
  s.ncard + sᶜ.ncard = nat.card α :=
by rw [←ncard_univ, ←ncard_union_eq (@disjoint_compl_right _ _ s) hs hsc, union_compl_self]

end lattice

/-- Given a set `t` and a set `s` inside it, we can shrink `t` to any appropriate size, and keep `s`
    inside it. -/
lemma exists_intermediate_set (i : ℕ) (h₁ : i + s.ncard ≤ t.ncard) (h₂ : s ⊆ t) :
  ∃ (r : set α), s ⊆ r ∧ r ⊆ t ∧ r.ncard = i + s.ncard :=
begin
  cases t.finite_or_infinite with ht ht,
  { haveI := ht.to_subtype,
    haveI := (ht.subset h₂).to_subtype,
    simp_rw [ncard_eq_to_finset_card] at h₁ ⊢,
    obtain ⟨r', hsr', hr't, hr'⟩ := finset.exists_intermediate_set _ h₁ (by simpa),
    exact ⟨r', by simpa using hsr', by simpa using hr't, by rw [←hr', ncard_coe_finset]⟩},
  rw [ht.ncard] at h₁,
  have h₁' := nat.eq_zero_of_le_zero h₁,
  rw [add_eq_zero_iff] at h₁',
  exact ⟨t, h₂, rfl.subset, by rw [ht.ncard, h₁'.1, h₁'.2]⟩
end

lemma exists_intermediate_set' {m : ℕ} (hs : s.ncard ≤ m) (ht : m ≤ t.ncard) (h : s ⊆ t) :
  ∃ (r : set α), s ⊆ r ∧ r ⊆ t ∧ r.ncard = m :=
begin
  obtain ⟨r,hsr,hrt,hc⟩ :=
    exists_intermediate_set (m - s.ncard) (by rwa [tsub_add_cancel_of_le hs]) h,
  rw tsub_add_cancel_of_le hs at hc,
  exact ⟨r,hsr,hrt,hc⟩,
end

/-- We can shrink `s` to any smaller size. -/
lemma exists_smaller_set (s : set α) (i : ℕ) (h₁ : i ≤ s.ncard) :
  ∃ (t : set α), t ⊆ s ∧ t.ncard = i :=
(exists_intermediate_set i (by simpa) (empty_subset s)).imp
  (λ t ht, ⟨ht.2.1,by simpa using ht.2.2⟩)

lemma exists_subset_or_subset_of_two_mul_lt_ncard {n : ℕ} (hst : 2 * n < (s ∪ t).ncard) :
  ∃ (r : set α), n < r.ncard ∧ (r ⊆ s ∨ r ⊆ t) :=
begin
  have hu := (finite_of_ncard_ne_zero ((nat.zero_le _).trans_lt hst).ne.symm),
  rw [ncard_eq_to_finset_card _ hu, finite.to_finset_union
    (hu.subset (subset_union_left _ _)) (hu.subset (subset_union_right _ _))] at hst,
  obtain ⟨r', hnr', hr'⟩ := finset.exists_subset_or_subset_of_two_mul_lt_card hst,
  exact ⟨r', by simpa , by simpa using hr'⟩,
end

/-! ### Explicit description of a set from its cardinality -/

@[simp] lemma ncard_eq_one : s.ncard = 1 ↔ ∃ a, s = {a} :=
begin
  refine ⟨λ h, _,by {rintro ⟨a,rfl⟩, rw [ncard_singleton]}⟩,
  haveI := (finite_of_ncard_ne_zero (ne_zero_of_eq_one h)).to_subtype,
  rw [ncard_eq_to_finset_card, finset.card_eq_one] at h,
  exact h.imp (λ a ha, by rwa [←finite.to_finset_singleton, finite.to_finset_inj] at ha),
end

lemma exists_eq_insert_iff_ncard (hs : s.finite . to_finite_tac) :
  (∃ a ∉ s, insert a s = t) ↔ s ⊆ t ∧ s.ncard + 1 = t.ncard :=
begin
  split,
  { rintro ⟨a, ha, rfl⟩,
    rw [ncard_eq_to_finset_card _ hs, ncard_eq_to_finset_card _ (hs.insert a),
      finite.to_finset_insert, ←@finite.to_finset_subset_to_finset _ _ _ hs (hs.insert a),
      finite.to_finset_insert],
    refine (@finset.exists_eq_insert_iff _ _ hs.to_finset (insert a hs.to_finset)).mp _,
    exact ⟨a, by rwa finite.mem_to_finset, rfl⟩},
  rintro ⟨hst, h⟩,
  have ht := @finite_of_ncard_pos _ t (by {rw ←h, apply nat.zero_lt_succ}),

  rw [ncard_eq_to_finset_card _ hs, ncard_eq_to_finset_card _ ht] at h,
  obtain ⟨a,has, ha⟩ := (finset.exists_eq_insert_iff.mpr ⟨by {simpa},h⟩),
  have hsa := hs.insert a,
  rw ←finite.to_finset_insert at ha,
  exact ⟨a, by {rwa finite.mem_to_finset at has}, by {rwa ←@finite.to_finset_inj _ _ _ hsa ht}⟩,
end

lemma ncard_le_one (hs : s.finite . to_finite_tac) :
  s.ncard ≤ 1 ↔ ∀ (a ∈ s) (b ∈ s), a = b :=
by simp_rw [ncard_eq_to_finset_card _ hs, finset.card_le_one, finite.mem_to_finset]

lemma ncard_le_one_iff (hs : s.finite . to_finite_tac) :
  s.ncard ≤ 1 ↔ ∀ {a b}, a ∈ s → b ∈ s → a = b :=
by { rw ncard_le_one hs, tauto}

lemma ncard_le_one_iff_subset_singleton [nonempty α] (hs : s.finite . to_finite_tac) :
  s.ncard ≤ 1 ↔ ∃ (x : α), s ⊆ {x} :=
by simp_rw [ncard_eq_to_finset_card _ hs, finset.card_le_one_iff_subset_singleton,
  finite.to_finset_subset, finset.coe_singleton]

/-- A `set` of a subsingleton type has cardinality at most one. -/
lemma ncard_le_one_of_subsingleton [subsingleton α] (s : set α) :
  s.ncard ≤ 1 :=
by {rw [ncard_eq_to_finset_card], exact finset.card_le_one_of_subsingleton _}

lemma one_lt_ncard (hs : s.finite . to_finite_tac) :
  1 < s.ncard ↔ ∃ (a ∈ s) (b ∈ s), a ≠ b :=
by simp_rw [ncard_eq_to_finset_card _ hs, finset.one_lt_card, finite.mem_to_finset]

lemma one_lt_ncard_iff (hs : s.finite . to_finite_tac) :
  1 < s.ncard ↔ ∃ a b, a ∈ s ∧ b ∈ s ∧ a ≠ b :=
by { rw one_lt_ncard hs, simp only [exists_prop, exists_and_distrib_left] }

lemma two_lt_ncard_iff (hs : s.finite . to_finite_tac) :
  2 < s.ncard ↔ ∃ a b c, a ∈ s ∧ b ∈ s ∧ c ∈ s ∧ a ≠ b ∧ a ≠ c ∧ b ≠ c :=
by simp_rw [ncard_eq_to_finset_card _ hs, finset.two_lt_card_iff, finite.mem_to_finset]

lemma two_lt_card (hs : s.finite . to_finite_tac) :
  2 < s.ncard ↔ ∃ (a ∈ s) (b ∈ s) (c ∈ s), a ≠ b ∧ a ≠ c ∧ b ≠ c :=
by simp only [two_lt_ncard_iff hs, exists_and_distrib_left, exists_prop]

lemma exists_ne_of_one_lt_ncard (hs : 1 < s.ncard) (a : α) : ∃ b, b ∈ s ∧ b ≠ a :=
begin
  haveI := (finite_of_ncard_ne_zero (zero_lt_one.trans hs).ne.symm).to_subtype,
  rw [ncard_eq_to_finset_card] at hs,
  simpa only [finite.mem_to_finset] using finset.exists_ne_of_one_lt_card hs a,
end

lemma eq_insert_of_ncard_eq_succ {n : ℕ} (h : s.ncard = n + 1) :
  ∃ a t, a ∉ t ∧ insert a t = s ∧ t.ncard = n :=
begin
  haveI := @fintype.of_finite _ (finite_of_ncard_pos (n.zero_lt_succ.trans_eq h.symm)).to_subtype,
  rw [ncard_eq_to_finset_card, finset.card_eq_succ] at h,
  obtain ⟨a,t,hat,hts,rfl⟩ := h,
  refine ⟨a,t,hat,_,by rw ncard_coe_finset⟩,
  rw [←to_finset_inj],
  convert hts,
  simp only [to_finset_insert, finset.to_finset_coe],
end

lemma ncard_eq_succ {n : ℕ} (hs : s.finite . to_finite_tac) :
  s.ncard = n + 1 ↔ ∃ a t, a ∉ t ∧ insert a t = s ∧ t.ncard = n :=
begin
  refine ⟨eq_insert_of_ncard_eq_succ, _⟩,
  rintro ⟨a,t,hat,h,rfl⟩,
  rw [← h, ncard_insert_of_not_mem hat (hs.subset ((subset_insert a t).trans_eq h))]
end

lemma ncard_eq_two :
  s.ncard = 2 ↔ ∃ x y, x ≠ y ∧ s = {x, y} :=
begin
  refine ⟨λ h, _, _⟩,
  { obtain ⟨x,t,hxt,rfl,ht⟩ :=  eq_insert_of_ncard_eq_succ h,
    obtain ⟨y,rfl⟩ := ncard_eq_one.mp ht,
    rw mem_singleton_iff at hxt,
    exact ⟨_,_,hxt,rfl⟩},
  rintro ⟨x,y,hxy,rfl⟩,
  rw [ncard_eq_to_finset_card, finset.card_eq_two],
  exact ⟨x,y,hxy, by {ext, simp}⟩,
end

lemma ncard_eq_three :
  s.ncard = 3 ↔ ∃ x y z, x ≠ y ∧ x ≠ z ∧ y ≠ z ∧ s = {x, y, z} :=
begin
  refine ⟨λ h, _, _⟩,
  { obtain ⟨x,t,hxt,rfl,ht⟩ :=  eq_insert_of_ncard_eq_succ h,
    obtain ⟨y,z,hyz,rfl⟩ := ncard_eq_two.mp ht,
    rw [mem_insert_iff, mem_singleton_iff, not_or_distrib] at hxt,
    exact ⟨x,y,z,hxt.1,hxt.2,hyz,rfl⟩},
  rintro ⟨x, y, z, xy, xz, yz, rfl⟩,
  rw [ncard_insert_of_not_mem, ncard_insert_of_not_mem, ncard_singleton],
  { rwa mem_singleton_iff},
  rw [mem_insert_iff, mem_singleton_iff],
  tauto,
end

end set


/-
This is a section.
It contains 00DZ, 00E0, 00E1 and 00E2 and 00E3 and 00E4 and 00E5 and 00E6 and 00E7 and 00E8 and 04PM

It also contains the following useful claim, just under Lemma 10.16.2 (tag 00E0):

The sets D(f) are open and form a basis for this topology (on Spec(R))

-/

import Kenny_comm_alg.temp analysis.topology.topological_space Kenny_comm_alg.Zariski

universe u
variables α : Type u
variable t : topological_space α
#check t
#print topological_space

def topological_space.is_topological_basis' {α : Type u} [t : topological_space α] (s : set (set α)) :=
(∀ U : set α, U ∈ s → t.is_open U) ∧
(∀ U : set α, t.is_open U → (∀ x, x ∈ U → ∃ V : set α, V ∈ s ∧ x ∈ V ∧ V ⊆ U))

#print topological_space.generate_open

lemma topological_space.generate_from_apply {α : Type u} [t : topological_space α] (s : set (set α)) (U : set α) :
  topological_space.is_open (topological_space.generate_from s) U ↔ topological_space.generate_open s U := iff.rfl

lemma basis_is_basis' {α : Type u} [t : topological_space α] (s : set (set α)) :
  topological_space.is_topological_basis s ↔ topological_space.is_topological_basis' s :=
begin
  split,
  { intro H,
    split,
    { intros U HU,
      rw H.right.right,
      exact topological_space.generate_open.basic U HU },
    { intros U HU x Hx,
      unfold topological_space.is_topological_basis at H,
      rw H.2.2 at HU,
      have H3 : topological_space.generate_open s U := HU,
      induction H3 with U4 H5 U6 U7 H8 H9 H10 H11 UU12 H13 H14,
      { existsi U4,
        split,exact H5,
        split,exact Hx,
        exact set.subset.refl U4
      },
      { have H4 := H.2.1,
        have H5 : x ∈ ⋃₀ s,
          rw H4,unfold set.univ,
        cases H5 with V HV,
        cases HV with H6 H7,
        existsi V,
        split,exact H6,
        split,exact H7,
        exact set.subset_univ V,
      },
      { have H12 := H10 (set.inter_subset_left U6 U7 Hx) H8,
        have H13 := H11 (set.inter_subset_right U6 U7 Hx) H9,
        cases H12 with V14 H14,
        cases H13 with V15 H15,
        have H16 := H.1 V14 H14.1 V15 H15.1 x ⟨H14.2.1,H15.2.1⟩,
        cases H16 with V H17,
        cases H17 with H18 H19,
        existsi V,
        split,exact H18,
        split,exact H19.1,
        refine set.subset.trans H19.2 _,
        intro z,
        apply and.imp,
        { intro hz, exact H14.2.2 hz },
        { intro hz, exact H15.2.2 hz }
      },
      { cases Hx with V HV,
        cases HV with H15 H16,
        rcases H14 V H15 H16 (H13 V H15) with ⟨W, H17, H18, H19⟩,
        refine ⟨W, H17, H18, _⟩,
        intros z hz,
        exact ⟨V, H15, H19 hz⟩
      }
    }
  },
  { intro H,
    split,
    { intros U1 H1 U2 H2 x H3,
      have H4 := H.1 U1 H1,
      have H5 := H.1 U2 H2,
      have H6 := H.2 (U1 ∩ U2) (topological_space.is_open_inter t U1 U2 H4 H5) x H3,
      rcases H6 with ⟨V, H7, H8, H9⟩,
      exact ⟨V, H7, H8, H9⟩
    },
    split,
    { apply set.ext,
      intro x,
      rw iff_true_right (set.mem_univ x),
      have H1 := H.2 set.univ (topological_space.is_open_univ t) x trivial,
      rcases H1 with ⟨V, H2, H3, H4⟩,
      existsi V,
      existsi H2,
      exact H3
    },
    { apply topological_space_eq,
      apply funext,
      intro U,
      apply propext,
      rw topological_space.generate_from_apply,
      split,
      { intro H1,
        have H2 := H.2 U H1,
        have H3 : U = ⋃₀ {V | ∃ x ∈ U, V ∈ s ∧ x ∈ V ∧ V ⊆ U},
        { apply set.ext,
          intro x,
          split,
          { intro H3,
            have H4 := H2 x H3,
            rcases H4 with ⟨V, H4⟩,
            existsi V,
            fapply exists.intro,
            exact ⟨x, H3, H4⟩,
            exact H4.2.1
          },
          { intro H3,
            rcases H3 with ⟨U1, H3, H4⟩,
            rcases H3 with ⟨y, H3, H5, H6, H7⟩,
            exact H7 H4
          }
        },
        rw H3,
        apply topological_space.generate_open.sUnion,
        intros U1 H4,
        rcases H4 with ⟨U1, H4, H5, H6, H7⟩,
        apply topological_space.generate_open.basic,
        exact H5
      },
      { exact generate_from_le H.1 U }
    }
  }
end

lemma D_f_form_basis (R : Type) [comm_ring R] :
  topological_space.is_topological_basis {U : set (X R) | ∃ f : R, U = Spec.D'(f)} :=
begin
  rw basis_is_basis',
  split,
  { intros U H,
    cases H with f Hf,
    existsi ({f} : set R),
    rw Hf,
    unfold Spec.D',
    unfold Spec.V,
    unfold Spec.V',
    rw set.compl_compl,
    simp
  },
  { intros U H x H1,
    cases H with U1 H,
    have H2 : U = -Spec.V U1,
    { rw [H, set.compl_compl] },
    rw set.set_eq_def at H2,
    have H3 := H2 x,
    rw iff_true_left H1 at H3,
    simp [Spec.V, has_subset.subset, set.subset] at H3,
    rw classical.not_forall at H3,
    cases H3 with f H3,
    rw @@not_imp (classical.prop_decidable _) at H3,
    cases H3 with H3 H4,
    existsi Spec.D' f,
    split,
    { existsi f,
      refl
    },
    split,
    { exact H4 },
    { intros y H5,
      rw H2,
      intro H6,
      apply H5,
      exact H6 H3
    }
  }
end


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

import group_theory.perm.cycle_type

/-!
# Alternating Groups

The alternating group on a finite type `α` is the subgroup of the permutation group `perm α`
consisting of the even permutations.

## Main definitions

* `alternating_group α` is the alternating group on `α`, defined as a `subgroup (perm α)`.

## Main results
* `two_mul_card_alternating_group` shows that the alternating group is half as large as
  the permutation group it is a subgroup of.

* `closure_three_cycles_eq_alternating` shows that the alternating group is
  generated by three-cycles.

## Tags
alternating group permutation

## TODO
* Show that `alternating_group α` is simple if and only if `fintype.card α ≠ 4`.

-/

open equiv equiv.perm subgroup fintype
variables (α : Type*) [fintype α] [decidable_eq α]

/-- The alternating group on a finite type, realized as a subgroup of `equiv.perm`.
  For $A_n$, use `alternating_group (fin n)`. -/
@[derive fintype] def alternating_group : subgroup (perm α) :=
sign.ker

instance [subsingleton α] : unique (alternating_group α) :=
⟨⟨1⟩, λ ⟨p, hp⟩, subtype.eq (subsingleton.elim p _)⟩

variables {α}

lemma alternating_group_eq_sign_ker : alternating_group α = sign.ker := rfl

namespace equiv.perm

@[simp]
lemma mem_alternating_group {f : perm α} :
  f ∈ alternating_group α ↔ sign f = 1 :=
sign.mem_ker

lemma prod_list_swap_mem_alternating_group_iff_even_length {l : list (perm α)}
  (hl : ∀ g ∈ l, is_swap g) :
  l.prod ∈ alternating_group α ↔ even l.length :=
begin
  rw [mem_alternating_group, sign_prod_list_swap hl, ← units.coe_eq_one, units.coe_pow,
    units.coe_neg_one, nat.neg_one_pow_eq_one_iff_even],
  dec_trivial
end

end equiv.perm

lemma two_mul_card_alternating_group [nontrivial α] :
  2 * card (alternating_group α) = card (perm α) :=
begin
  let := (quotient_group.quotient_ker_equiv_of_surjective _ (sign_surjective α)).to_equiv,
  rw [←fintype.card_units_int, ←fintype.card_congr this],
  exact (subgroup.card_eq_card_quotient_mul_card_subgroup _).symm,
end

instance alternating_group_normal : (alternating_group α).normal := sign.normal_ker

namespace equiv.perm

@[simp]
theorem closure_three_cycles_eq_alternating :
  closure {σ : perm α | is_three_cycle σ} = alternating_group α :=
closure_eq_of_le _ (λ σ hσ, mem_alternating_group.2 hσ.sign) $ λ σ hσ, begin
  suffices hind : ∀ (n : ℕ) (l : list (perm α)) (hl : ∀ g, g ∈ l → is_swap g)
    (hn : l.length = 2 * n), l.prod ∈ closure {σ : perm α | is_three_cycle σ},
  { obtain ⟨l, rfl, hl⟩ := trunc_swap_factors σ,
    obtain ⟨n, hn⟩ := (prod_list_swap_mem_alternating_group_iff_even_length hl).1 hσ,
    exact hind n l hl hn },
  intro n,
  induction n with n ih; intros l hl hn,
  { simp [list.length_eq_zero.1 hn, one_mem] },
  rw [nat.mul_succ] at hn,
  obtain ⟨a, l, rfl⟩ := l.exists_of_length_succ hn,
  rw [list.length_cons, nat.succ_inj'] at hn,
  obtain ⟨b, l, rfl⟩ := l.exists_of_length_succ hn,
  rw [list.prod_cons, list.prod_cons, ← mul_assoc],
  rw [list.length_cons, nat.succ_inj'] at hn,
  exact mul_mem _ (is_swap.mul_mem_closure_three_cycles (hl a (list.mem_cons_self a _))
    (hl b (list.mem_cons_of_mem a (l.mem_cons_self b))))
    (ih _ (λ g hg, hl g (list.mem_cons_of_mem _ (list.mem_cons_of_mem _ hg))) hn),
end

end equiv.perm


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

The integers, with addition, multiplication, and subtraction.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.basic
import Mathlib.algebra.order_functions
import Mathlib.PostPort

universes u_1 u

namespace Mathlib

namespace int

protected instance inhabited : Inhabited ℤ :=
  { default := int.zero }

protected instance nontrivial : nontrivial ℤ :=
  nontrivial.mk (Exists.intro 0 (Exists.intro 1 int.zero_ne_one))

protected instance comm_ring : comm_ring ℤ :=
  comm_ring.mk int.add int.add_assoc int.zero int.zero_add int.add_zero int.neg int.sub int.add_left_neg int.add_comm
    int.mul int.mul_assoc int.one int.one_mul int.mul_one int.distrib_left int.distrib_right int.mul_comm

/-! ### Extra instances to short-circuit type class resolution -/

-- instance : has_sub int           := by apply_instance -- This is in core

protected instance add_comm_monoid : add_comm_monoid ℤ :=
  add_comm_group.to_add_comm_monoid ℤ

protected instance add_monoid : add_monoid ℤ :=
  sub_neg_monoid.to_add_monoid ℤ

protected instance monoid : monoid ℤ :=
  ring.to_monoid ℤ

protected instance comm_monoid : comm_monoid ℤ :=
  comm_semiring.to_comm_monoid ℤ

protected instance comm_semigroup : comm_semigroup ℤ :=
  comm_ring.to_comm_semigroup ℤ

protected instance semigroup : semigroup ℤ :=
  monoid.to_semigroup ℤ

protected instance add_comm_semigroup : add_comm_semigroup ℤ :=
  add_comm_monoid.to_add_comm_semigroup ℤ

protected instance add_semigroup : add_semigroup ℤ :=
  add_monoid.to_add_semigroup ℤ

protected instance comm_semiring : comm_semiring ℤ :=
  comm_ring.to_comm_semiring

protected instance semiring : semiring ℤ :=
  ring.to_semiring

protected instance ring : ring ℤ :=
  comm_ring.to_ring ℤ

protected instance distrib : distrib ℤ :=
  ring.to_distrib ℤ

protected instance linear_ordered_comm_ring : linear_ordered_comm_ring ℤ :=
  linear_ordered_comm_ring.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add comm_ring.add_zero
    comm_ring.neg comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm comm_ring.mul comm_ring.mul_assoc comm_ring.one
    comm_ring.one_mul comm_ring.mul_one comm_ring.left_distrib comm_ring.right_distrib linear_order.le linear_order.lt
    linear_order.le_refl linear_order.le_trans linear_order.le_antisymm int.add_le_add_left sorry int.mul_pos
    linear_order.le_total linear_order.decidable_le linear_order.decidable_eq linear_order.decidable_lt
    nontrivial.exists_pair_ne comm_ring.mul_comm

protected instance linear_ordered_add_comm_group : linear_ordered_add_comm_group ℤ :=
  linear_ordered_ring.to_linear_ordered_add_comm_group

theorem abs_eq_nat_abs (a : ℤ) : abs a = ↑(nat_abs a) :=
  int.cases_on a (fun (a : ℕ) => idRhs (abs ↑a = ↑a) (abs_of_nonneg (coe_zero_le a)))
    fun (a : ℕ) => idRhs (abs (Int.negSucc a) = -Int.negSucc a) (abs_of_nonpos (le_of_lt (neg_succ_lt_zero a)))

theorem nat_abs_abs (a : ℤ) : nat_abs (abs a) = nat_abs a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs (abs a) = nat_abs a)) (abs_eq_nat_abs a))) (Eq.refl (nat_abs ↑(nat_abs a)))

theorem sign_mul_abs (a : ℤ) : sign a * abs a = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (sign a * abs a = a)) (abs_eq_nat_abs a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (sign a * ↑(nat_abs a) = a)) (sign_mul_nat_abs a))) (Eq.refl a))

@[simp] theorem default_eq_zero : Inhabited.default = 0 :=
  rfl

@[simp] theorem add_def {a : ℤ} {b : ℤ} : int.add a b = a + b :=
  rfl

@[simp] theorem mul_def {a : ℤ} {b : ℤ} : int.mul a b = a * b :=
  rfl

@[simp] theorem coe_nat_mul_neg_succ (m : ℕ) (n : ℕ) : ↑m * Int.negSucc n = -(↑m * ↑(Nat.succ n)) :=
  rfl

@[simp] theorem neg_succ_mul_coe_nat (m : ℕ) (n : ℕ) : Int.negSucc m * ↑n = -(↑(Nat.succ m) * ↑n) :=
  rfl

@[simp] theorem neg_succ_mul_neg_succ (m : ℕ) (n : ℕ) : Int.negSucc m * Int.negSucc n = ↑(Nat.succ m) * ↑(Nat.succ n) :=
  rfl

@[simp] theorem coe_nat_le {m : ℕ} {n : ℕ} : ↑m ≤ ↑n ↔ m ≤ n :=
  coe_nat_le_coe_nat_iff m n

@[simp] theorem coe_nat_lt {m : ℕ} {n : ℕ} : ↑m < ↑n ↔ m < n :=
  coe_nat_lt_coe_nat_iff m n

@[simp] theorem coe_nat_inj' {m : ℕ} {n : ℕ} : ↑m = ↑n ↔ m = n :=
  int.coe_nat_eq_coe_nat_iff m n

@[simp] theorem coe_nat_pos {n : ℕ} : 0 < ↑n ↔ 0 < n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 < ↑n ↔ 0 < n)) (Eq.symm int.coe_nat_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑0 < ↑n ↔ 0 < n)) (propext coe_nat_lt))) (iff.refl (0 < n)))

@[simp] theorem coe_nat_eq_zero {n : ℕ} : ↑n = 0 ↔ n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑n = 0 ↔ n = 0)) (Eq.symm int.coe_nat_zero)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑n = ↑0 ↔ n = 0)) (propext coe_nat_inj'))) (iff.refl (n = 0)))

theorem coe_nat_ne_zero {n : ℕ} : ↑n ≠ 0 ↔ n ≠ 0 :=
  not_congr coe_nat_eq_zero

@[simp] theorem coe_nat_nonneg (n : ℕ) : 0 ≤ ↑n :=
  iff.mpr coe_nat_le (nat.zero_le n)

theorem coe_nat_ne_zero_iff_pos {n : ℕ} : ↑n ≠ 0 ↔ 0 < n :=
  { mp := fun (h : ↑n ≠ 0) => nat.pos_of_ne_zero (iff.mp coe_nat_ne_zero h),
    mpr := fun (h : 0 < n) => ne.symm (ne_of_lt (iff.mpr coe_nat_lt h)) }

theorem coe_nat_succ_pos (n : ℕ) : 0 < ↑(Nat.succ n) :=
  iff.mpr coe_nat_pos (nat.succ_pos n)

@[simp] theorem coe_nat_abs (n : ℕ) : abs ↑n = ↑n :=
  abs_of_nonneg (coe_nat_nonneg n)

/-! ### succ and pred -/

/-- Immediate successor of an integer: `succ n = n + 1` -/
def succ (a : ℤ) : ℤ :=
  a + 1

/-- Immediate predecessor of an integer: `pred n = n - 1` -/
def pred (a : ℤ) : ℤ :=
  a - 1

theorem nat_succ_eq_int_succ (n : ℕ) : ↑(Nat.succ n) = succ ↑n :=
  rfl

theorem pred_succ (a : ℤ) : pred (succ a) = a :=
  add_sub_cancel a 1

theorem succ_pred (a : ℤ) : succ (pred a) = a :=
  sub_add_cancel a 1

theorem neg_succ (a : ℤ) : -succ a = pred (-a) :=
  neg_add a 1

theorem succ_neg_succ (a : ℤ) : succ (-succ a) = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (succ (-succ a) = -a)) (neg_succ a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (succ (pred (-a)) = -a)) (succ_pred (-a)))) (Eq.refl (-a)))

theorem neg_pred (a : ℤ) : -pred a = succ (-a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-pred a = succ (-a))) (eq_neg_of_eq_neg (Eq.symm (neg_succ (-a))))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-pred a = -pred ( --a))) (neg_neg a))) (Eq.refl (-pred a)))

theorem pred_neg_pred (a : ℤ) : pred (-pred a) = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (pred (-pred a) = -a)) (neg_pred a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (pred (succ (-a)) = -a)) (pred_succ (-a)))) (Eq.refl (-a)))

theorem pred_nat_succ (n : ℕ) : pred ↑(Nat.succ n) = ↑n :=
  pred_succ ↑n

theorem neg_nat_succ (n : ℕ) : -↑(Nat.succ n) = pred (-↑n) :=
  neg_succ ↑n

theorem succ_neg_nat_succ (n : ℕ) : succ (-↑(Nat.succ n)) = -↑n :=
  succ_neg_succ ↑n

theorem lt_succ_self (a : ℤ) : a < succ a :=
  lt_add_of_pos_right a zero_lt_one

theorem pred_self_lt (a : ℤ) : pred a < a :=
  sub_lt_self a zero_lt_one

theorem add_one_le_iff {a : ℤ} {b : ℤ} : a + 1 ≤ b ↔ a < b :=
  iff.rfl

theorem lt_add_one_iff {a : ℤ} {b : ℤ} : a < b + 1 ↔ a ≤ b :=
  add_le_add_iff_right 1

theorem le_add_one {a : ℤ} {b : ℤ} (h : a ≤ b) : a ≤ b + 1 :=
  le_of_lt (iff.mpr lt_add_one_iff h)

theorem sub_one_lt_iff {a : ℤ} {b : ℤ} : a - 1 < b ↔ a ≤ b :=
  iff.trans sub_lt_iff_lt_add lt_add_one_iff

theorem le_sub_one_iff {a : ℤ} {b : ℤ} : a ≤ b - 1 ↔ a < b :=
  le_sub_iff_add_le

protected theorem induction_on {p : ℤ → Prop} (i : ℤ) (hz : p 0) (hp : ∀ (i : ℕ), p ↑i → p (↑i + 1)) (hn : ∀ (i : ℕ), p (-↑i) → p (-↑i - 1)) : p i := sorry

/-- Inductively define a function on `ℤ` by defining it at `b`, for the `succ` of a number greater
  than `b`, and the `pred` of a number less than `b`. -/
protected def induction_on' {C : ℤ → Sort u_1} (z : ℤ) (b : ℤ) : C b → ((k : ℤ) → b ≤ k → C k → C (k + 1)) → ((k : ℤ) → k ≤ b → C k → C (k - 1)) → C z :=
  fun (H0 : C b) (Hs : (k : ℤ) → b ≤ k → C k → C (k + 1)) (Hp : (k : ℤ) → k ≤ b → C k → C (k - 1)) =>
    eq.mpr sorry
      (Int.rec
        (fun (n : ℕ) =>
          Nat.rec (eq.mpr sorry (eq.mpr sorry H0))
            (fun (n : ℕ) (ih : C (Int.ofNat n + b)) =>
              eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hs (Int.ofNat n + b) sorry ih)))))
            n)
        (fun (n : ℕ) =>
          Nat.rec (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hp b sorry H0))))))
            (fun (n : ℕ) (ih : C (Int.negSucc n + b)) =>
              eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (eq.mpr sorry (Hp (Int.negSucc n + b) sorry ih)))))
            n)
        (z - b))

/-! ### nat abs -/

theorem nat_abs_add_le (a : ℤ) (b : ℤ) : nat_abs (a + b) ≤ nat_abs a + nat_abs b := sorry

theorem nat_abs_neg_of_nat (n : ℕ) : nat_abs (neg_of_nat n) = n :=
  nat.cases_on n (Eq.refl (nat_abs (neg_of_nat 0))) fun (n : ℕ) => Eq.refl (nat_abs (neg_of_nat (Nat.succ n)))

theorem nat_abs_mul (a : ℤ) (b : ℤ) : nat_abs (a * b) = nat_abs a * nat_abs b := sorry

theorem nat_abs_mul_nat_abs_eq {a : ℤ} {b : ℤ} {c : ℕ} (h : a * b = ↑c) : nat_abs a * nat_abs b = c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a * nat_abs b = c)) (Eq.symm (nat_abs_mul a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs (a * b) = c)) h))
      (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs ↑c = c)) (nat_abs_of_nat c))) (Eq.refl c)))

@[simp] theorem nat_abs_mul_self' (a : ℤ) : ↑(nat_abs a) * ↑(nat_abs a) = a * a :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (↑(nat_abs a) * ↑(nat_abs a) = a * a)) (Eq.symm (int.coe_nat_mul (nat_abs a) (nat_abs a)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(nat_abs a * nat_abs a) = a * a)) nat_abs_mul_self)) (Eq.refl (a * a)))

theorem neg_succ_of_nat_eq' (m : ℕ) : Int.negSucc m = -↑m - 1 := sorry

theorem nat_abs_ne_zero_of_ne_zero {z : ℤ} (hz : z ≠ 0) : nat_abs z ≠ 0 :=
  fun (h : nat_abs z = 0) => hz (eq_zero_of_nat_abs_eq_zero h)

@[simp] theorem nat_abs_eq_zero {a : ℤ} : nat_abs a = 0 ↔ a = 0 :=
  { mp := eq_zero_of_nat_abs_eq_zero, mpr := fun (h : a = 0) => Eq.symm h ▸ rfl }

theorem nat_abs_lt_nat_abs_of_nonneg_of_lt {a : ℤ} {b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) : nat_abs a < nat_abs b := sorry

theorem nat_abs_eq_iff_mul_self_eq {a : ℤ} {b : ℤ} : nat_abs a = nat_abs b ↔ a * a = b * b := sorry

theorem nat_abs_lt_iff_mul_self_lt {a : ℤ} {b : ℤ} : nat_abs a < nat_abs b ↔ a * a < b * b := sorry

theorem nat_abs_le_iff_mul_self_le {a : ℤ} {b : ℤ} : nat_abs a ≤ nat_abs b ↔ a * a ≤ b * b := sorry

theorem nat_abs_eq_iff_sq_eq {a : ℤ} {b : ℤ} : nat_abs a = nat_abs b ↔ a ^ bit0 1 = b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a = nat_abs b ↔ a ^ bit0 1 = b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a = nat_abs b ↔ a * a = b ^ bit0 1)) (pow_two b)))
      nat_abs_eq_iff_mul_self_eq)

theorem nat_abs_lt_iff_sq_lt {a : ℤ} {b : ℤ} : nat_abs a < nat_abs b ↔ a ^ bit0 1 < b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a < nat_abs b ↔ a ^ bit0 1 < b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a < nat_abs b ↔ a * a < b ^ bit0 1)) (pow_two b)))
      nat_abs_lt_iff_mul_self_lt)

theorem nat_abs_le_iff_sq_le {a : ℤ} {b : ℤ} : nat_abs a ≤ nat_abs b ↔ a ^ bit0 1 ≤ b ^ bit0 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a ≤ nat_abs b ↔ a ^ bit0 1 ≤ b ^ bit0 1)) (pow_two a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (nat_abs a ≤ nat_abs b ↔ a * a ≤ b ^ bit0 1)) (pow_two b)))
      nat_abs_le_iff_mul_self_le)

/-! ### `/`  -/

@[simp] theorem of_nat_div (m : ℕ) (n : ℕ) : Int.ofNat (m / n) = Int.ofNat m / Int.ofNat n :=
  rfl

@[simp] theorem coe_nat_div (m : ℕ) (n : ℕ) : ↑(m / n) = ↑m / ↑n :=
  rfl

theorem neg_succ_of_nat_div (m : ℕ) {b : ℤ} (H : 0 < b) : Int.negSucc m / b = -(↑m / b + 1) := sorry

@[simp] protected theorem div_neg (a : ℤ) (b : ℤ) : a / -b = -(a / b) := sorry

theorem div_of_neg_of_pos {a : ℤ} {b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b = -((-a - 1) / b + 1) := sorry

protected theorem div_nonneg {a : ℤ} {b : ℤ} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b := sorry

protected theorem div_nonpos {a : ℤ} {b : ℤ} (Ha : 0 ≤ a) (Hb : b ≤ 0) : a / b ≤ 0 :=
  nonpos_of_neg_nonneg
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ -(a / b))) (Eq.symm (int.div_neg a b))))
      (int.div_nonneg Ha (neg_nonneg_of_nonpos Hb)))

theorem div_neg' {a : ℤ} {b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 := sorry

-- Will be generalized to Euclidean domains.

protected theorem zero_div (b : ℤ) : 0 / b = 0 :=
  int.cases_on b
    (fun (b : ℕ) => nat.cases_on b (idRhs (0 / 0 = 0 / 0) rfl) fun (b : ℕ) => idRhs (0 / ↑(b + 1) = 0 / ↑(b + 1)) rfl)
    fun (b : ℕ) => idRhs (0 / Int.negSucc b = 0 / Int.negSucc b) rfl

protected theorem div_zero (a : ℤ) : a / 0 = 0 :=
  int.cases_on a
    (fun (a : ℕ) => nat.cases_on a (idRhs (0 / 0 = 0 / 0) rfl) fun (a : ℕ) => idRhs (↑(a + 1) / 0 = ↑(a + 1) / 0) rfl)
    fun (a : ℕ) => idRhs (Int.negSucc a / 0 = Int.negSucc a / 0) rfl

@[simp] protected theorem div_one (a : ℤ) : a / 1 = a := sorry

theorem div_eq_zero_of_lt {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a / b = 0 := sorry

theorem div_eq_zero_of_lt_abs {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < abs b) : a / b = 0 := sorry

protected theorem add_mul_div_right (a : ℤ) (b : ℤ) {c : ℤ} (H : c ≠ 0) : (a + b * c) / c = a / c + b := sorry

protected theorem add_mul_div_left (a : ℤ) {b : ℤ} (c : ℤ) (H : b ≠ 0) : (a + b * c) / b = a / b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b * c) / b = a / b + c)) (mul_comm b c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + c * b) / b = a / b + c)) (int.add_mul_div_right a c H))) (Eq.refl (a / b + c)))

protected theorem add_div_of_dvd_right {a : ℤ} {b : ℤ} {c : ℤ} (H : c ∣ b) : (a + b) / c = a / c + b / c := sorry

protected theorem add_div_of_dvd_left {a : ℤ} {b : ℤ} {c : ℤ} (H : c ∣ a) : (a + b) / c = a / c + b / c := sorry

@[simp] protected theorem mul_div_cancel (a : ℤ) {b : ℤ} (H : b ≠ 0) : a * b / b = a :=
  eq.mp (Eq._oldrec (Eq.refl (a * b / b = 0 + a)) (zero_add a))
    (eq.mp (Eq._oldrec (Eq.refl (a * b / b = 0 / b + a)) (int.zero_div b))
      (eq.mp (Eq._oldrec (Eq.refl ((0 + a * b) / b = 0 / b + a)) (zero_add (a * b))) (int.add_mul_div_right 0 a H)))

@[simp] protected theorem mul_div_cancel_left {a : ℤ} (b : ℤ) (H : a ≠ 0) : a * b / a = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / a = b)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / a = b)) (int.mul_div_cancel b H))) (Eq.refl b))

@[simp] protected theorem div_self {a : ℤ} (H : a ≠ 0) : a / a = 1 :=
  eq.mp (Eq._oldrec (Eq.refl (1 * a / a = 1)) (one_mul a)) (int.mul_div_cancel 1 H)

/-! ### mod -/

theorem of_nat_mod (m : ℕ) (n : ℕ) : ↑m % ↑n = Int.ofNat (m % n) :=
  rfl

@[simp] theorem coe_nat_mod (m : ℕ) (n : ℕ) : ↑(m % n) = ↑m % ↑n :=
  rfl

theorem neg_succ_of_nat_mod (m : ℕ) {b : ℤ} (bpos : 0 < b) : Int.negSucc m % b = b - 1 - ↑m % b := sorry

@[simp] theorem mod_neg (a : ℤ) (b : ℤ) : a % -b = a % b := sorry

@[simp] theorem mod_abs (a : ℤ) (b : ℤ) : a % abs b = a % b :=
  abs_by_cases (fun (i : ℤ) => a % i = a % b) rfl (mod_neg a b)

theorem zero_mod (b : ℤ) : 0 % b = 0 :=
  congr_arg Int.ofNat (nat.zero_mod (nat_abs b))

theorem mod_zero (a : ℤ) : a % 0 = a :=
  int.cases_on a (fun (a : ℕ) => idRhs (Int.ofNat (a % 0) = Int.ofNat a) (congr_arg Int.ofNat (nat.mod_zero a)))
    fun (a : ℕ) => idRhs (Int.negSucc (a % 0) = Int.negSucc a) (congr_arg Int.negSucc (nat.mod_zero a))

theorem mod_one (a : ℤ) : a % 1 = 0 := sorry

theorem mod_eq_of_lt {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a % b = a := sorry

theorem mod_nonneg (a : ℤ) {b : ℤ} : b ≠ 0 → 0 ≤ a % b := sorry

theorem mod_lt_of_pos (a : ℤ) {b : ℤ} (H : 0 < b) : a % b < b := sorry

theorem mod_lt (a : ℤ) {b : ℤ} (H : b ≠ 0) : a % b < abs b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a % b < abs b)) (Eq.symm (mod_abs a b)))) (mod_lt_of_pos a (iff.mpr abs_pos H))

theorem mod_add_div_aux (m : ℕ) (n : ℕ) : ↑n - (↑m % ↑n + 1) - (↑n * (↑m / ↑n) + ↑n) = Int.negSucc m := sorry

theorem mod_add_div (a : ℤ) (b : ℤ) : a % b + b * (a / b) = a := sorry

theorem div_add_mod (a : ℤ) (b : ℤ) : b * (a / b) + a % b = a :=
  Eq.trans (add_comm (b * (a / b)) (a % b)) (mod_add_div a b)

theorem mod_def (a : ℤ) (b : ℤ) : a % b = a - b * (a / b) :=
  eq_sub_of_add_eq (mod_add_div a b)

@[simp] theorem add_mul_mod_self {a : ℤ} {b : ℤ} {c : ℤ} : (a + b * c) % c = a % c := sorry

@[simp] theorem add_mul_mod_self_left (a : ℤ) (b : ℤ) (c : ℤ) : (a + b * c) % b = a % b :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b * c) % b = a % b)) (mul_comm b c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + c * b) % b = a % b)) add_mul_mod_self)) (Eq.refl (a % b)))

@[simp] theorem add_mod_self {a : ℤ} {b : ℤ} : (a + b) % b = a % b :=
  eq.mp (Eq._oldrec (Eq.refl ((a + b * 1) % b = a % b)) (mul_one b)) (add_mul_mod_self_left a b 1)

@[simp] theorem add_mod_self_left {a : ℤ} {b : ℤ} : (a + b) % a = b % a :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % a = b % a)) (add_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((b + a) % a = b % a)) add_mod_self)) (Eq.refl (b % a)))

@[simp] theorem mod_add_mod (m : ℤ) (n : ℤ) (k : ℤ) : (m % n + k) % n = (m + k) % n := sorry

@[simp] theorem add_mod_mod (m : ℤ) (n : ℤ) (k : ℤ) : (m + n % k) % k = (m + n) % k :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((m + n % k) % k = (m + n) % k)) (add_comm m (n % k))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((n % k + m) % k = (m + n) % k)) (mod_add_mod n k m)))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((n + m) % k = (m + n) % k)) (add_comm n m))) (Eq.refl ((m + n) % k))))

theorem add_mod (a : ℤ) (b : ℤ) (n : ℤ) : (a + b) % n = (a % n + b % n) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % n = (a % n + b % n) % n)) (add_mod_mod (a % n) b n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a + b) % n = (a % n + b) % n)) (mod_add_mod a n b))) (Eq.refl ((a + b) % n)))

theorem add_mod_eq_add_mod_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) (H : m % n = k % n) : (m + i) % n = (k + i) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((m + i) % n = (k + i) % n)) (Eq.symm (mod_add_mod m n i))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((m % n + i) % n = (k + i) % n)) (Eq.symm (mod_add_mod k n i))))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((m % n + i) % n = (k % n + i) % n)) H)) (Eq.refl ((k % n + i) % n))))

theorem add_mod_eq_add_mod_left {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) (H : m % n = k % n) : (i + m) % n = (i + k) % n :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((i + m) % n = (i + k) % n)) (add_comm i m)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((m + i) % n = (i + k) % n)) (add_mod_eq_add_mod_right i H)))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((k + i) % n = (i + k) % n)) (add_comm k i))) (Eq.refl ((i + k) % n))))

theorem mod_add_cancel_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) : (m + i) % n = (k + i) % n ↔ m % n = k % n := sorry

theorem mod_add_cancel_left {m : ℤ} {n : ℤ} {k : ℤ} {i : ℤ} : (i + m) % n = (i + k) % n ↔ m % n = k % n := sorry

theorem mod_sub_cancel_right {m : ℤ} {n : ℤ} {k : ℤ} (i : ℤ) : (m - i) % n = (k - i) % n ↔ m % n = k % n :=
  mod_add_cancel_right (-i)

theorem mod_eq_mod_iff_mod_sub_eq_zero {m : ℤ} {n : ℤ} {k : ℤ} : m % n = k % n ↔ (m - k) % n = 0 := sorry

@[simp] theorem mul_mod_left (a : ℤ) (b : ℤ) : a * b % b = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b % b = 0)) (Eq.symm (zero_add (a * b)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((0 + a * b) % b = 0)) add_mul_mod_self))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 % b = 0)) (zero_mod b))) (Eq.refl 0)))

@[simp] theorem mul_mod_right (a : ℤ) (b : ℤ) : a * b % a = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b % a = 0)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a % a = 0)) (mul_mod_left b a))) (Eq.refl 0))

theorem mul_mod (a : ℤ) (b : ℤ) (n : ℤ) : a * b % n = a % n * (b % n) % n := sorry

@[simp] theorem neg_mod_two (i : ℤ) : -i % bit0 1 = i % bit0 1 := sorry

theorem mod_self {a : ℤ} : a % a = 0 :=
  eq.mp (Eq._oldrec (Eq.refl (1 * a % a = 0)) (one_mul a)) (mul_mod_left 1 a)

@[simp] theorem mod_mod_of_dvd (n : ℤ) {m : ℤ} {k : ℤ} (h : m ∣ k) : n % k % m = n % m := sorry

@[simp] theorem mod_mod (a : ℤ) (b : ℤ) : a % b % b = a % b := sorry

theorem sub_mod (a : ℤ) (b : ℤ) (n : ℤ) : (a - b) % n = (a % n - b % n) % n := sorry

/-! ### properties of `/` and `%` -/

@[simp] theorem mul_div_mul_of_pos {a : ℤ} (b : ℤ) (c : ℤ) (H : 0 < a) : a * b / (a * c) = b / c := sorry

@[simp] theorem mul_div_mul_of_pos_left (a : ℤ) {b : ℤ} (c : ℤ) (H : 0 < b) : a * b / (c * b) = a / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / (c * b) = a / c)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / (c * b) = a / c)) (mul_comm c b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / (b * c) = a / c)) (mul_div_mul_of_pos a c H))) (Eq.refl (a / c))))

@[simp] theorem mul_mod_mul_of_pos {a : ℤ} (b : ℤ) (c : ℤ) (H : 0 < a) : a * b % (a * c) = a * (b % c) := sorry

theorem lt_div_add_one_mul_self (a : ℤ) {b : ℤ} (H : 0 < b) : a < (a / b + 1) * b := sorry

theorem abs_div_le_abs (a : ℤ) (b : ℤ) : abs (a / b) ≤ abs a := sorry

theorem div_le_self {a : ℤ} (b : ℤ) (Ha : 0 ≤ a) : a / b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (a / b ≤ abs a)) (abs_of_nonneg Ha)) (le_trans (le_abs_self (a / b)) (abs_div_le_abs a b))

theorem mul_div_cancel_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : a % b = 0) : b * (a / b) = a :=
  eq.mp (Eq._oldrec (Eq.refl (0 + b * (a / b) = a)) (zero_add (b * (a / b))))
    (eq.mp (Eq._oldrec (Eq.refl (a % b + b * (a / b) = a)) H) (mod_add_div a b))

theorem div_mul_cancel_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : a % b = 0) : a / b * b = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b * b = a)) (mul_comm (a / b) b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * (a / b) = a)) (mul_div_cancel_of_mod_eq_zero H))) (Eq.refl a))

theorem mod_two_eq_zero_or_one (n : ℤ) : n % bit0 1 = 0 ∨ n % bit0 1 = 1 := sorry

/-! ### dvd -/

theorem coe_nat_dvd {m : ℕ} {n : ℕ} : ↑m ∣ ↑n ↔ m ∣ n := sorry

theorem coe_nat_dvd_left {n : ℕ} {z : ℤ} : ↑n ∣ z ↔ n ∣ nat_abs z := sorry

theorem coe_nat_dvd_right {n : ℕ} {z : ℤ} : z ∣ ↑n ↔ nat_abs z ∣ n := sorry

theorem dvd_antisymm {a : ℤ} {b : ℤ} (H1 : 0 ≤ a) (H2 : 0 ≤ b) : a ∣ b → b ∣ a → a = b := sorry

theorem dvd_of_mod_eq_zero {a : ℤ} {b : ℤ} (H : b % a = 0) : a ∣ b :=
  Exists.intro (b / a) (Eq.symm (mul_div_cancel_of_mod_eq_zero H))

theorem mod_eq_zero_of_dvd {a : ℤ} {b : ℤ} : a ∣ b → b % a = 0 := sorry

theorem dvd_iff_mod_eq_zero (a : ℤ) (b : ℤ) : a ∣ b ↔ b % a = 0 :=
  { mp := mod_eq_zero_of_dvd, mpr := dvd_of_mod_eq_zero }

/-- If `a % b = c` then `b` divides `a - c`. -/
theorem dvd_sub_of_mod_eq {a : ℤ} {b : ℤ} {c : ℤ} (h : a % b = c) : b ∣ a - c := sorry

theorem nat_abs_dvd {a : ℤ} {b : ℤ} : ↑(nat_abs a) ∣ b ↔ a ∣ b := sorry

theorem dvd_nat_abs {a : ℤ} {b : ℤ} : a ∣ ↑(nat_abs b) ↔ a ∣ b := sorry

protected instance decidable_dvd : DecidableRel has_dvd.dvd :=
  fun (a n : ℤ) => decidable_of_decidable_of_iff (int.decidable_eq (n % a) 0) sorry

protected theorem div_mul_cancel {a : ℤ} {b : ℤ} (H : b ∣ a) : a / b * b = a :=
  div_mul_cancel_of_mod_eq_zero (mod_eq_zero_of_dvd H)

protected theorem mul_div_cancel' {a : ℤ} {b : ℤ} (H : a ∣ b) : a * (b / a) = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * (b / a) = b)) (mul_comm a (b / a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b / a * a = b)) (int.div_mul_cancel H))) (Eq.refl b))

protected theorem mul_div_assoc (a : ℤ) {b : ℤ} {c : ℤ} : c ∣ b → a * b / c = a * (b / c) := sorry

protected theorem mul_div_assoc' (b : ℤ) {a : ℤ} {c : ℤ} (h : c ∣ a) : a * b / c = a / c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * b / c = a / c * b)) (mul_comm a b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a / c = a / c * b)) (int.mul_div_assoc b h)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * (a / c) = a / c * b)) (mul_comm b (a / c)))) (Eq.refl (a / c * b))))

theorem div_dvd_div {a : ℤ} {b : ℤ} {c : ℤ} (H1 : a ∣ b) (H2 : b ∣ c) : b / a ∣ c / a := sorry

protected theorem eq_mul_of_div_eq_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ∣ a) (H2 : a / b = c) : a = b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (Eq.symm H2)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * (a / b))) (int.mul_div_cancel' H1))) (Eq.refl a))

protected theorem div_eq_of_eq_mul_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ≠ 0) (H2 : a = b * c) : a / b = c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b = c)) H2))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * c / b = c)) (int.mul_div_cancel_left c H1))) (Eq.refl c))

protected theorem eq_div_of_mul_eq_right {a : ℤ} {b : ℤ} {c : ℤ} (H1 : a ≠ 0) (H2 : a * b = c) : b = c / a :=
  Eq.symm (int.div_eq_of_eq_mul_right H1 (Eq.symm H2))

protected theorem div_eq_iff_eq_mul_right {a : ℤ} {b : ℤ} {c : ℤ} (H : b ≠ 0) (H' : b ∣ a) : a / b = c ↔ a = b * c :=
  { mp := int.eq_mul_of_div_eq_right H', mpr := int.div_eq_of_eq_mul_right H }

protected theorem div_eq_iff_eq_mul_left {a : ℤ} {b : ℤ} {c : ℤ} (H : b ≠ 0) (H' : b ∣ a) : a / b = c ↔ a = c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a / b = c ↔ a = c * b)) (mul_comm c b))) (int.div_eq_iff_eq_mul_right H H')

protected theorem eq_mul_of_div_eq_left {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ∣ a) (H2 : a / b = c) : a = c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = c * b)) (mul_comm c b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (int.eq_mul_of_div_eq_right H1 H2))) (Eq.refl (b * c)))

protected theorem div_eq_of_eq_mul_left {a : ℤ} {b : ℤ} {c : ℤ} (H1 : b ≠ 0) (H2 : a = c * b) : a / b = c :=
  int.div_eq_of_eq_mul_right H1
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * c)) (mul_comm b c)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a = c * b)) H2)) (Eq.refl (c * b))))

theorem neg_div_of_dvd {a : ℤ} {b : ℤ} (H : b ∣ a) : -a / b = -(a / b) := sorry

theorem sub_div_of_dvd {a : ℤ} {b : ℤ} {c : ℤ} (hcb : c ∣ b) : (a - b) / c = a / c - b / c := sorry

theorem sub_div_of_dvd_sub {a : ℤ} {b : ℤ} {c : ℤ} (hcab : c ∣ a - b) : (a - b) / c = a / c - b / c :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((a - b) / c = a / c - b / c)) (propext eq_sub_iff_add_eq)))
    (eq.mpr (id (Eq._oldrec (Eq.refl ((a - b) / c + b / c = a / c)) (Eq.symm (int.add_div_of_dvd_left hcab))))
      (eq.mpr (id (Eq._oldrec (Eq.refl ((a - b + b) / c = a / c)) (sub_add_cancel a b))) (Eq.refl (a / c))))

theorem div_sign (a : ℤ) (b : ℤ) : a / sign b = a * sign b := sorry

@[simp] theorem sign_mul (a : ℤ) (b : ℤ) : sign (a * b) = sign a * sign b := sorry

protected theorem sign_eq_div_abs (a : ℤ) : sign a = a / abs a := sorry

theorem mul_sign (i : ℤ) : i * sign i = ↑(nat_abs i) := sorry

theorem le_of_dvd {a : ℤ} {b : ℤ} (bpos : 0 < b) (H : a ∣ b) : a ≤ b := sorry

theorem eq_one_of_dvd_one {a : ℤ} (H : 0 ≤ a) (H' : a ∣ 1) : a = 1 := sorry

theorem eq_one_of_mul_eq_one_right {a : ℤ} {b : ℤ} (H : 0 ≤ a) (H' : a * b = 1) : a = 1 :=
  eq_one_of_dvd_one H (Exists.intro b (Eq.symm H'))

theorem eq_one_of_mul_eq_one_left {a : ℤ} {b : ℤ} (H : 0 ≤ b) (H' : a * b = 1) : b = 1 :=
  eq_one_of_mul_eq_one_right H
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = 1)) (mul_comm b a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a * b = 1)) H')) (Eq.refl 1)))

theorem of_nat_dvd_of_dvd_nat_abs {a : ℕ} {z : ℤ} (haz : a ∣ nat_abs z) : ↑a ∣ z := sorry

theorem dvd_nat_abs_of_of_nat_dvd {a : ℕ} {z : ℤ} (haz : ↑a ∣ z) : a ∣ nat_abs z := sorry

theorem pow_dvd_of_le_of_pow_dvd {p : ℕ} {m : ℕ} {n : ℕ} {k : ℤ} (hmn : m ≤ n) (hdiv : ↑(p ^ n) ∣ k) : ↑(p ^ m) ∣ k := sorry

theorem dvd_of_pow_dvd {p : ℕ} {k : ℕ} {m : ℤ} (hk : 1 ≤ k) (hpk : ↑(p ^ k) ∣ m) : ↑p ∣ m :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑p ∣ m)) (Eq.symm (pow_one p)))) (pow_dvd_of_le_of_pow_dvd hk hpk)

/-- If `n > 0` then `m` is not divisible by `n` iff it is between `n * k` and `n * (k + 1)`
  for some `k`. -/
theorem exists_lt_and_lt_iff_not_dvd (m : ℤ) {n : ℤ} (hn : 0 < n) : (∃ (k : ℤ), n * k < m ∧ m < n * (k + 1)) ↔ ¬n ∣ m := sorry

/-! ### `/` and ordering -/

protected theorem div_mul_le (a : ℤ) {b : ℤ} (H : b ≠ 0) : a / b * b ≤ a :=
  le_of_sub_nonneg
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a - a / b * b)) (mul_comm (a / b) b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a - b * (a / b))) (Eq.symm (mod_def a b)))) (mod_nonneg a H)))

protected theorem div_le_of_le_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a ≤ b * c) : a / c ≤ b :=
  le_of_mul_le_mul_right (le_trans (int.div_mul_le a (ne_of_gt H)) H') H

protected theorem mul_lt_of_lt_div {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H3 : a < b / c) : a * c < b :=
  lt_of_not_ge (mt (int.div_le_of_le_mul H) (not_le_of_gt H3))

protected theorem mul_le_of_le_div {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a ≤ b / c) : a * c ≤ b :=
  le_trans (mul_le_mul_of_nonneg_right H2 (le_of_lt H1)) (int.div_mul_le b (ne_of_gt H1))

protected theorem le_div_of_mul_le {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a * c ≤ b) : a ≤ b / c :=
  le_of_lt_add_one (lt_of_mul_lt_mul_right (lt_of_le_of_lt H2 (lt_div_add_one_mul_self b H1)) (le_of_lt H1))

protected theorem le_div_iff_mul_le {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) : a ≤ b / c ↔ a * c ≤ b :=
  { mp := int.mul_le_of_le_div H, mpr := int.le_div_of_mul_le H }

protected theorem div_le_div {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a ≤ b) : a / c ≤ b / c :=
  int.le_div_of_mul_le H (le_trans (int.div_mul_le a (ne_of_gt H)) H')

protected theorem div_lt_of_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) (H' : a < b * c) : a / c < b :=
  lt_of_not_ge (mt (int.mul_le_of_le_div H) (not_le_of_gt H'))

protected theorem lt_mul_of_div_lt {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 < c) (H2 : a / c < b) : a < b * c :=
  lt_of_not_ge (mt (int.le_div_of_mul_le H1) (not_le_of_gt H2))

protected theorem div_lt_iff_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} (H : 0 < c) : a / c < b ↔ a < b * c :=
  { mp := int.lt_mul_of_div_lt H, mpr := int.div_lt_of_lt_mul H }

protected theorem le_mul_of_div_le {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 ≤ b) (H2 : b ∣ a) (H3 : a / b ≤ c) : a ≤ c * b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ c * b)) (Eq.symm (int.div_mul_cancel H2)))) (mul_le_mul_of_nonneg_right H3 H1)

protected theorem lt_div_of_mul_lt {a : ℤ} {b : ℤ} {c : ℤ} (H1 : 0 ≤ b) (H2 : b ∣ c) (H3 : a * b < c) : a < c / b :=
  lt_of_not_ge (mt (int.le_mul_of_div_le H1 H2) (not_le_of_gt H3))

protected theorem lt_div_iff_mul_lt {a : ℤ} {b : ℤ} (c : ℤ) (H : 0 < c) (H' : c ∣ b) : a < b / c ↔ a * c < b :=
  { mp := int.mul_lt_of_lt_div H, mpr := int.lt_div_of_mul_lt (le_of_lt H) H' }

theorem div_pos_of_pos_of_dvd {a : ℤ} {b : ℤ} (H1 : 0 < a) (H2 : 0 ≤ b) (H3 : b ∣ a) : 0 < a / b :=
  int.lt_div_of_mul_lt H2 H3 (eq.mpr (id (Eq._oldrec (Eq.refl (0 * b < a)) (zero_mul b))) H1)

theorem div_eq_div_of_mul_eq_mul {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (H2 : d ∣ c) (H3 : b ≠ 0) (H4 : d ≠ 0) (H5 : a * d = b * c) : a / b = c / d :=
  int.div_eq_of_eq_mul_right H3
    (eq.mpr (id (Eq._oldrec (Eq.refl (a = b * (c / d))) (Eq.symm (int.mul_div_assoc b H2))))
      (Eq.symm (int.div_eq_of_eq_mul_left H4 (Eq.symm H5))))

theorem eq_mul_div_of_mul_eq_mul_of_dvd_left {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hb : b ≠ 0) (hbc : b ∣ c) (h : b * a = c * d) : a = c / b * d := sorry

/-- If an integer with larger absolute value divides an integer, it is
zero. -/
theorem eq_zero_of_dvd_of_nat_abs_lt_nat_abs {a : ℤ} {b : ℤ} (w : a ∣ b) (h : nat_abs b < nat_abs a) : b = 0 := sorry

theorem eq_zero_of_dvd_of_nonneg_of_lt {a : ℤ} {b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) (h : b ∣ a) : a = 0 :=
  eq_zero_of_dvd_of_nat_abs_lt_nat_abs h (nat_abs_lt_nat_abs_of_nonneg_of_lt w₁ w₂)

/-- If two integers are congruent to a sufficiently large modulus,
they are equal. -/
theorem eq_of_mod_eq_of_nat_abs_sub_lt_nat_abs {a : ℤ} {b : ℤ} {c : ℤ} (h1 : a % b = c) (h2 : nat_abs (a - c) < nat_abs b) : a = c :=
  eq_of_sub_eq_zero (eq_zero_of_dvd_of_nat_abs_lt_nat_abs (dvd_sub_of_mod_eq h1) h2)

theorem of_nat_add_neg_succ_of_nat_of_lt {m : ℕ} {n : ℕ} (h : m < Nat.succ n) : Int.ofNat m + Int.negSucc n = Int.negSucc (n - m) := sorry

theorem of_nat_add_neg_succ_of_nat_of_ge {m : ℕ} {n : ℕ} (h : Nat.succ n ≤ m) : Int.ofNat m + Int.negSucc n = Int.ofNat (m - Nat.succ n) := sorry

@[simp] theorem neg_add_neg (m : ℕ) (n : ℕ) : Int.negSucc m + Int.negSucc n = Int.negSucc (Nat.succ (m + n)) :=
  rfl

/-! ### to_nat -/

theorem to_nat_eq_max (a : ℤ) : ↑(to_nat a) = max a 0 :=
  int.cases_on a (fun (a : ℕ) => idRhs (↑a = max (↑a) 0) (Eq.symm (max_eq_left (coe_zero_le a))))
    fun (a : ℕ) => idRhs (0 = max (Int.negSucc a) 0) (Eq.symm (max_eq_right (le_of_lt (neg_succ_lt_zero a))))

@[simp] theorem to_nat_zero : to_nat 0 = 0 :=
  rfl

@[simp] theorem to_nat_one : to_nat 1 = 1 :=
  rfl

@[simp] theorem to_nat_of_nonneg {a : ℤ} (h : 0 ≤ a) : ↑(to_nat a) = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(to_nat a) = a)) (to_nat_eq_max a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (max a 0 = a)) (max_eq_left h))) (Eq.refl a))

@[simp] theorem to_nat_sub_of_le (a : ℤ) (b : ℤ) (h : b ≤ a) : ↑(to_nat (a + -b)) = a + -b :=
  to_nat_of_nonneg (sub_nonneg_of_le h)

@[simp] theorem to_nat_coe_nat (n : ℕ) : to_nat ↑n = n :=
  rfl

@[simp] theorem to_nat_coe_nat_add_one {n : ℕ} : to_nat (↑n + 1) = n + 1 :=
  rfl

theorem le_to_nat (a : ℤ) : a ≤ ↑(to_nat a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ ↑(to_nat a))) (to_nat_eq_max a))) (le_max_left a 0)

@[simp] theorem to_nat_le {a : ℤ} {n : ℕ} : to_nat a ≤ n ↔ a ≤ ↑n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (to_nat a ≤ n ↔ a ≤ ↑n)) (propext (iff.symm (coe_nat_le_coe_nat_iff (to_nat a) n)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(to_nat a) ≤ ↑n ↔ a ≤ ↑n)) (to_nat_eq_max a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (max a 0 ≤ ↑n ↔ a ≤ ↑n)) (propext max_le_iff))) (and_iff_left (coe_zero_le n))))

@[simp] theorem lt_to_nat {n : ℕ} {a : ℤ} : n < to_nat a ↔ ↑n < a :=
  iff.mp le_iff_le_iff_lt_iff_lt to_nat_le

theorem to_nat_le_to_nat {a : ℤ} {b : ℤ} (h : a ≤ b) : to_nat a ≤ to_nat b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (to_nat a ≤ to_nat b)) (propext to_nat_le))) (le_trans h (le_to_nat b))

theorem to_nat_lt_to_nat {a : ℤ} {b : ℤ} (hb : 0 < b) : to_nat a < to_nat b ↔ a < b := sorry

theorem lt_of_to_nat_lt {a : ℤ} {b : ℤ} (h : to_nat a < to_nat b) : a < b :=
  iff.mp (to_nat_lt_to_nat (iff.mp lt_to_nat (lt_of_le_of_lt (nat.zero_le (to_nat a)) h))) h

theorem to_nat_add {a : ℤ} {b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) : to_nat (a + b) = to_nat a + to_nat b := sorry

theorem to_nat_add_one {a : ℤ} (h : 0 ≤ a) : to_nat (a + 1) = to_nat a + 1 :=
  to_nat_add h zero_le_one

/-- If `n : ℕ`, then `int.to_nat' n = some n`, if `n : ℤ` is negative, then `int.to_nat' n = none`.
-/
def to_nat' : ℤ → Option ℕ :=
  sorry

theorem mem_to_nat' (a : ℤ) (n : ℕ) : n ∈ to_nat' a ↔ a = ↑n := sorry

theorem to_nat_zero_of_neg {z : ℤ} : z < 0 → to_nat z = 0 := sorry

/-! ### units -/

@[simp] theorem units_nat_abs (u : units ℤ) : nat_abs ↑u = 1 := sorry

theorem units_eq_one_or (u : units ℤ) : u = 1 ∨ u = -1 := sorry

theorem units_inv_eq_self (u : units ℤ) : u⁻¹ = u :=
  or.elim (units_eq_one_or u) (fun (h : u = 1) => Eq.symm h ▸ rfl) fun (h : u = -1) => Eq.symm h ▸ rfl

@[simp] theorem units_mul_self (u : units ℤ) : u * u = 1 :=
  or.elim (units_eq_one_or u) (fun (h : u = 1) => Eq.symm h ▸ rfl) fun (h : u = -1) => Eq.symm h ▸ rfl

-- `units.coe_mul` is a "wrong turn" for the simplifier, this undoes it and simplifies further

@[simp] theorem units_coe_mul_self (u : units ℤ) : ↑u * ↑u = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑u * ↑u = 1)) (Eq.symm (units.coe_mul u u))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(u * u) = 1)) (units_mul_self u)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (↑1 = 1)) units.coe_one)) (Eq.refl 1)))

/-! ### bitwise ops -/

@[simp] theorem bodd_zero : bodd 0 = false :=
  rfl

@[simp] theorem bodd_one : bodd 1 = tt :=
  rfl

theorem bodd_two : bodd (bit0 1) = false :=
  rfl

@[simp] theorem bodd_coe (n : ℕ) : bodd ↑n = nat.bodd n :=
  rfl

@[simp] theorem bodd_sub_nat_nat (m : ℕ) (n : ℕ) : bodd (sub_nat_nat m n) = bxor (nat.bodd m) (nat.bodd n) := sorry

@[simp] theorem bodd_neg_of_nat (n : ℕ) : bodd (neg_of_nat n) = nat.bodd n := sorry

@[simp] theorem bodd_neg (n : ℤ) : bodd (-n) = bodd n := sorry

@[simp] theorem bodd_add (m : ℤ) (n : ℤ) : bodd (m + n) = bxor (bodd m) (bodd n) := sorry

@[simp] theorem bodd_mul (m : ℤ) (n : ℤ) : bodd (m * n) = bodd m && bodd n := sorry

theorem bodd_add_div2 (n : ℤ) : cond (bodd n) 1 0 + bit0 1 * div2 n = n := sorry

theorem div2_val (n : ℤ) : div2 n = n / bit0 1 :=
  int.cases_on n
    (fun (n : ℕ) => idRhs (Int.ofNat (nat.div2 n) = Int.ofNat (n / bit0 1)) (congr_arg Int.ofNat (nat.div2_val n)))
    fun (n : ℕ) => idRhs (Int.negSucc (nat.div2 n) = Int.negSucc (n / bit0 1)) (congr_arg Int.negSucc (nat.div2_val n))

theorem bit0_val (n : ℤ) : bit0 n = bit0 1 * n :=
  Eq.symm (two_mul n)

theorem bit1_val (n : ℤ) : bit1 n = bit0 1 * n + 1 :=
  congr_arg (fun (_x : ℤ) => _x + 1) (bit0_val n)

theorem bit_val (b : Bool) (n : ℤ) : bit b n = bit0 1 * n + cond b 1 0 :=
  bool.cases_on b (Eq.trans (bit0_val n) (Eq.symm (add_zero (bit0 1 * n)))) (bit1_val n)

theorem bit_decomp (n : ℤ) : bit (bodd n) (div2 n) = n :=
  Eq.trans (bit_val (bodd n) (div2 n)) (Eq.trans (add_comm (bit0 1 * div2 n) (cond (bodd n) 1 0)) (bodd_add_div2 n))

/-- Defines a function from `ℤ` conditionally, if it is defined for odd and even integers separately
  using `bit`. -/
def bit_cases_on {C : ℤ → Sort u} (n : ℤ) (h : (b : Bool) → (n : ℤ) → C (bit b n)) : C n :=
  eq.mpr sorry (h (bodd n) (div2 n))

@[simp] theorem bit_zero : bit false 0 = 0 :=
  rfl

@[simp] theorem bit_coe_nat (b : Bool) (n : ℕ) : bit b ↑n = ↑(nat.bit b n) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (bit b ↑n = ↑(nat.bit b n))) (bit_val b ↑n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (bit0 1 * ↑n + cond b 1 0 = ↑(nat.bit b n))) (nat.bit_val b n)))
      (bool.cases_on b (Eq.refl (bit0 1 * ↑n + cond false 1 0)) (Eq.refl (bit0 1 * ↑n + cond tt 1 0))))

@[simp] theorem bit_neg_succ (b : Bool) (n : ℕ) : bit b (Int.negSucc n) = Int.negSucc (nat.bit (!b) n) := sorry

@[simp] theorem bodd_bit (b : Bool) (n : ℤ) : bodd (bit b n) = b := sorry

@[simp] theorem bodd_bit0 (n : ℤ) : bodd (bit0 n) = false :=
  bodd_bit false n

@[simp] theorem bodd_bit1 (n : ℤ) : bodd (bit1 n) = tt :=
  bodd_bit tt n

@[simp] theorem div2_bit (b : Bool) (n : ℤ) : div2 (bit b n) = n := sorry

theorem bit0_ne_bit1 (m : ℤ) (n : ℤ) : bit0 m ≠ bit1 n := sorry

theorem bit1_ne_bit0 (m : ℤ) (n : ℤ) : bit1 m ≠ bit0 n :=
  ne.symm (bit0_ne_bit1 n m)

theorem bit1_ne_zero (m : ℤ) : bit1 m ≠ 0 := sorry

@[simp] theorem test_bit_zero (b : Bool) (n : ℤ) : test_bit (bit b n) 0 = b := sorry

@[simp] theorem test_bit_succ (m : ℕ) (b : Bool) (n : ℤ) : test_bit (bit b n) (Nat.succ m) = test_bit n m := sorry

theorem bitwise_or : bitwise bor = lor := sorry

theorem bitwise_and : bitwise band = land := sorry

theorem bitwise_diff : (bitwise fun (a b : Bool) => a && !b) = ldiff := sorry

theorem bitwise_xor : bitwise bxor = lxor := sorry

@[simp] theorem bitwise_bit (f : Bool → Bool → Bool) (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) : bitwise f (bit a m) (bit b n) = bit (f a b) (bitwise f m n) := sorry

@[simp] theorem lor_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) : lor (bit a m) (bit b n) = bit (a || b) (lor m n) := sorry

@[simp] theorem land_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) : land (bit a m) (bit b n) = bit (a && b) (land m n) := sorry

@[simp] theorem ldiff_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) : ldiff (bit a m) (bit b n) = bit (a && !b) (ldiff m n) := sorry

@[simp] theorem lxor_bit (a : Bool) (m : ℤ) (b : Bool) (n : ℤ) : lxor (bit a m) (bit b n) = bit (bxor a b) (lxor m n) := sorry

@[simp] theorem lnot_bit (b : Bool) (n : ℤ) : lnot (bit b n) = bit (!b) (lnot n) := sorry

@[simp] theorem test_bit_bitwise (f : Bool → Bool → Bool) (m : ℤ) (n : ℤ) (k : ℕ) : test_bit (bitwise f m n) k = f (test_bit m k) (test_bit n k) := sorry

@[simp] theorem test_bit_lor (m : ℤ) (n : ℤ) (k : ℕ) : test_bit (lor m n) k = test_bit m k || test_bit n k := sorry

@[simp] theorem test_bit_land (m : ℤ) (n : ℤ) (k : ℕ) : test_bit (land m n) k = test_bit m k && test_bit n k := sorry

@[simp] theorem test_bit_ldiff (m : ℤ) (n : ℤ) (k : ℕ) : test_bit (ldiff m n) k = test_bit m k && !test_bit n k := sorry

@[simp] theorem test_bit_lxor (m : ℤ) (n : ℤ) (k : ℕ) : test_bit (lxor m n) k = bxor (test_bit m k) (test_bit n k) := sorry

@[simp] theorem test_bit_lnot (n : ℤ) (k : ℕ) : test_bit (lnot n) k = !test_bit n k := sorry

theorem shiftl_add (m : ℤ) (n : ℕ) (k : ℤ) : shiftl m (↑n + k) = shiftl (shiftl m ↑n) k := sorry

theorem shiftl_sub (m : ℤ) (n : ℕ) (k : ℤ) : shiftl m (↑n - k) = shiftr (shiftl m ↑n) k :=
  shiftl_add m n (-k)

@[simp] theorem shiftl_neg (m : ℤ) (n : ℤ) : shiftl m (-n) = shiftr m n :=
  rfl

@[simp] theorem shiftr_neg (m : ℤ) (n : ℤ) : shiftr m (-n) = shiftl m n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (shiftr m (-n) = shiftl m n)) (Eq.symm (shiftl_neg m (-n)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (shiftl m ( --n) = shiftl m n)) (neg_neg n))) (Eq.refl (shiftl m n)))

@[simp] theorem shiftl_coe_nat (m : ℕ) (n : ℕ) : shiftl ↑m ↑n = ↑(nat.shiftl m n) :=
  rfl

@[simp] theorem shiftr_coe_nat (m : ℕ) (n : ℕ) : shiftr ↑m ↑n = ↑(nat.shiftr m n) :=
  nat.cases_on n (Eq.refl (shiftr ↑m ↑0)) fun (n : ℕ) => Eq.refl (shiftr ↑m ↑(Nat.succ n))

@[simp] theorem shiftl_neg_succ (m : ℕ) (n : ℕ) : shiftl (Int.negSucc m) ↑n = Int.negSucc (nat.shiftl' tt m n) :=
  rfl

@[simp] theorem shiftr_neg_succ (m : ℕ) (n : ℕ) : shiftr (Int.negSucc m) ↑n = Int.negSucc (nat.shiftr m n) :=
  nat.cases_on n (Eq.refl (shiftr (Int.negSucc m) ↑0)) fun (n : ℕ) => Eq.refl (shiftr (Int.negSucc m) ↑(Nat.succ n))

theorem shiftr_add (m : ℤ) (n : ℕ) (k : ℕ) : shiftr m (↑n + ↑k) = shiftr (shiftr m ↑n) ↑k := sorry

theorem shiftl_eq_mul_pow (m : ℤ) (n : ℕ) : shiftl m ↑n = m * ↑(bit0 1 ^ n) := sorry

theorem shiftr_eq_div_pow (m : ℤ) (n : ℕ) : shiftr m ↑n = m / ↑(bit0 1 ^ n) := sorry

theorem one_shiftl (n : ℕ) : shiftl 1 ↑n = ↑(bit0 1 ^ n) :=
  congr_arg coe (nat.one_shiftl n)

@[simp] theorem zero_shiftl (n : ℤ) : shiftl 0 n = 0 :=
  int.cases_on n (fun (n : ℕ) => idRhs (↑(nat.shiftl 0 n) = ↑0) (congr_arg coe (nat.zero_shiftl n)))
    fun (n : ℕ) => idRhs (↑(nat.shiftr 0 (Nat.succ n)) = ↑0) (congr_arg coe (nat.zero_shiftr (Nat.succ n)))

@[simp] theorem zero_shiftr (n : ℤ) : shiftr 0 n = 0 :=
  zero_shiftl (-n)

/-! ### Least upper bound property for integers -/

theorem exists_least_of_bdd {P : ℤ → Prop} (Hbdd : ∃ (b : ℤ), ∀ (z : ℤ), P z → b ≤ z) (Hinh : ∃ (z : ℤ), P z) : ∃ (lb : ℤ), P lb ∧ ∀ (z : ℤ), P z → lb ≤ z := sorry

theorem exists_greatest_of_bdd {P : ℤ → Prop} (Hbdd : ∃ (b : ℤ), ∀ (z : ℤ), P z → z ≤ b) (Hinh : ∃ (z : ℤ), P z) : ∃ (ub : ℤ), P ub ∧ ∀ (z : ℤ), P z → z ≤ ub := sorry


-- ignore this part: we are building the notion of a set
-- go to line 36

inductive zfc : Type
| empty  : zfc
| insert : zfc → zfc → zfc

instance : has_emptyc zfc := ⟨zfc.empty⟩
instance : has_insert zfc zfc := ⟨zfc.insert⟩

instance : has_zero zfc := ⟨{}⟩
instance : has_one zfc := ⟨{0}⟩

def succ (a : zfc) : zfc := has_insert.insert a a

def zfc_add : zfc → zfc → zfc
| m 0 := m
| m (zfc.insert n p) := succ (zfc_add m n)

instance : has_add zfc := ⟨zfc_add⟩

inductive member : zfc → zfc → Prop
| left {a b c : zfc} : member a c → member a (has_insert.insert b c)
| right {a b : zfc} : member a (has_insert.insert a b)

instance : has_mem zfc zfc := ⟨member⟩
instance : has_subset zfc := ⟨(λ a b:zfc, (∀ z:zfc, z ∈ a → z ∈ b))⟩
infix `⊈`:50 := (λ a b, ¬(a ⊆ b))
infix `⊉`:50 := (λ a b, ¬(b ⊆ a))

axiom member.ext (a b : zfc) : (∀ x, x ∈ a ↔ x ∈ b) ↔ a=b

export zfc
export member

-- start here

/-

USAGE:
1. ¬P is shorthand of (P → false).
2. A ∉ B is shorthand of ¬(A ∈ B), which is shorthand of (A ∈ B → false) per 1.
3. A ⊆ B is shorthand of (∀ z, z ∈ A → z ∈ B).
4. A ⊈ B is shorthand of ¬(A ⊆ B), and then use 1 and 3.
5. If the current goal is x ∈ {a,b,c,d,e}, then using "left" or "apply left"
   will make the goal x ∈ {a,b,c,d}.
6. If the current goal is x ∈ {a,b,c,d,x}, then using "right" or "apply right"
   or "exact right" will prove the goal.
7. If one wishes to disprove (i.e. derive false from) one of the current hypotheses
   of the form h : x ∈ {a,b,c,d,e}, using "cases h" will replace the current hypothesis
   with a_1 : x ∈ {a,b,c,d}, provided that x and e are not equal. When the set is empty,
   "cases h" will directly prove false.

HINT: To prove a statement of the form "P → P", use "exact id".

RECAP:
1. To prove a statement of the form "∀ z, P z", type "intro x" and then prove "P x".
2. To prove a statement of the form "P → Q", type "intro HP" and then prove "Q".
3. If the current goal is P ∨ Q, using "left" will reduce the goal to P.

NOTE:
Somehow the goal will become messy. I tried to fix it but it just wouldn't work.
Try to work with simple cases where the goal still works, and then trust in the
force when it doesn't.

-/

definition A : zfc := {1,2,{1,2}}
definition B : zfc := {1,2,A}

-- prove one and delete the other for each part.

theorem M1F_Sheet01_Q06a_is_true : (1:zfc) ∈ A := sorry
theorem M1F_Sheet01_Q06a_is_false : (1:zfc) ∈ A := sorry

theorem M1F_Sheet01_Q06b_is_true: ({1}:zfc) ∈ A := sorry
theorem M1F_Sheet01_Q06b_is_false: ({1}:zfc) ∉ A := sorry

theorem M1F_Sheet01_Q06c_is_true: ({1,2}:zfc) ∈ A := sorry
theorem M1F_Sheet01_Q06c_is_false: ({1,2}:zfc) ∉ A := sorry

-- goal generator becomes messy for (d)

theorem M1F_Sheet01_Q06d_is_true: ({1,2}:zfc) ⊆ A := sorry
theorem M1F_Sheet01_Q06d_is_false: ({1,2}:zfc) ⊆ A := sorry

theorem M1F_Sheet01_Q06e_is_true: (1:zfc) ∈ B := sorry
theorem M1F_Sheet01_Q06e_is_false: (1:zfc) ∉ B := sorry

theorem M1F_Sheet01_Q06f_is_true: ({1}:zfc) ∈ B := sorry
theorem M1F_Sheet01_Q06f_is_false: ({1}:zfc) ∉ B := sorry

theorem M1F_Sheet01_Q06g_is_true: ({1,2}:zfc) ∈ B → (1:zfc) ∈ A := sorry
theorem M1F_Sheet01_Q06g_is_false: ¬(({1,2}:zfc) ∈ B → (1:zfc) ∈ A) := sorry

-- goal generator becomes messy for (h)

theorem M1F_Sheet01_Q06h_is_true: ({1,2}:zfc) ⊆ B ∨ (1:zfc) ∉ A := sorry
theorem M1F_Sheet01_Q06h_is_false: ¬(({1,2}:zfc) ⊆ B ∨ (1:zfc) ∉ A) := sorry


/-
Copyright (c) 2022 Jireh Loreaux. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jireh Loreaux
-/
import analysis.normed_space.star.basic
import algebra.star.module
import analysis.special_functions.exponential

/-! # The exponential map from selfadjoint to unitary
In this file, we establish various propreties related to the map `λ a, exp ℂ A (I • a)` between the
subtypes `self_adjoint A` and `unitary A`.

## TODO

* Show that any exponential unitary is path-connected in `unitary A` to `1 : unitary A`.
* Prove any unitary whose distance to `1 : unitary A` is less than `1` can be expressed as an
  exponential unitary.
* A unitary is in the path component of `1` if and only if it is a finite product of exponential
  unitaries.
-/

section star

variables {A : Type*}
[normed_ring A] [normed_algebra ℂ A] [star_ring A] [cstar_ring A] [complete_space A]
[star_module ℂ A]

open complex

lemma self_adjoint.exp_i_smul_unitary {a : A} (ha : a ∈ self_adjoint A) :
  exp ℂ A (I • a) ∈ unitary A :=
begin
  rw [unitary.mem_iff, star_exp],
  simp only [star_smul, is_R_or_C.star_def, self_adjoint.mem_iff.mp ha, conj_I, neg_smul],
  rw ←@exp_add_of_commute ℂ A _ _ _ _ _ _ ((commute.refl (I • a)).neg_left),
  rw ←@exp_add_of_commute ℂ A _ _ _ _ _ _ ((commute.refl (I • a)).neg_right),
  simpa only [add_right_neg, add_left_neg, and_self] using (exp_zero : exp ℂ A 0 = 1),
end

/-- The map from the selfadjoint real subspace to the unitary group. This map only makes sense
over ℂ. -/
@[simps]
noncomputable def self_adjoint.exp_unitary (a : self_adjoint A) : unitary A :=
⟨exp ℂ A (I • a), self_adjoint.exp_i_smul_unitary (a.property)⟩

open self_adjoint

lemma commute.exp_unitary_add {a b : self_adjoint A} (h : commute (a : A) (b : A)) :
  exp_unitary (a + b) = exp_unitary a * exp_unitary b :=
begin
  ext,
  have hcomm : commute (I • (a : A)) (I • (b : A)),
  calc _ = _ : by simp only [h.eq, algebra.smul_mul_assoc, algebra.mul_smul_comm],
  simpa only [exp_unitary_coe, add_subgroup.coe_add, smul_add] using exp_add_of_commute hcomm,
end

lemma commute.exp_unitary {a b : self_adjoint A} (h : commute (a : A) (b : A)) :
  commute (exp_unitary a) (exp_unitary b) :=
calc (exp_unitary a) * (exp_unitary b) = (exp_unitary b) * (exp_unitary a)
  : by rw [←h.exp_unitary_add, ←h.symm.exp_unitary_add, add_comm]

end star