import .myRing
import tactic.basic

-- import tactic
open myRing
variables {R : Type} [myRing R]
variables {O : Type} [ordered_ring O ]

--  variable is_positive : O → Prop
/-
P1. Suppose a, z ∈ ℤ. If a+z=a then z=0. So "zero"
  is uniquely defined. Similarly, "one" is uniquely
  defined.
-/
theorem zero_is_unique: ∀( z : R), (∀ (a : R), a + z = a) → (z = 0)  := begin
  intros z a,
  have x:  z + 0 = z := add_zero z,
  have y:  z + 0 = 0 := begin
  rw add_comm,
  rw a,
  end,
  rw ← x,
  rw y,
end

theorem one_is_unique: ∀( o : R), (∀(a : R), a*o = a) → o = 1  := begin
  intros o a,
  have q : o * 1 = o := mul_one o,
  have r : 1 * o = 1 := a 1,
  rw mul_comm at r,
  rw q at r,
  exact r,
end

/-
P3. Suppose a,b, b' ∈ ℤ. The a+b=a+b' → b=b'
-/
theorem left_cancel: ∀ {a b c : R}, a+b=a+c → b=c := begin
  intros a b c eq,
  rw [ ← add_zero  b],
  cases has_inv a  ,
  rw [←  h,←  add_assoc,add_comm b a,eq,add_comm a c,add_assoc,h,add_zero],
end

/- similarly, you can cancel on the right-/
-- @[simp]
lemma right_cancel: ∀ {a b c : R}, b+a=c+a → b=c := begin
  intros a b c eq,
  rw add_comm at eq,
  have : c + a = a+c := begin
    rw add_comm,
  end,
  rw this at eq,
  have : b=c := begin
    exact left_cancel eq,
  end,
  exact this,
end
@[simp]
lemma right_cancel_simp: ∀ {a b c : R}, b+a=c+a ↔ b=c := begin
  intros a b c,
  split,{
    exact right_cancel,
  },
  intro bc,
  rw bc,
end

/- anything times zero is zero -/

@[simp]
lemma mul_zero : ∀{a : R}, a * 0 = 0 :=
begin
  intro a,
  have : a * (1 + 0) = a + 0,
  {
    rw add_zero,
    rw mul_one,
    rw add_zero,
  },
  rw mul_add at this,
  rw mul_one at this,
  exact left_cancel this,
end

/-
P2. Use P1 to deduce that 0 ≠ 1
False. 0 = 1 in the trivial myRing.`
P2 SALVAGE. Zero is only one in the trivial myRing.
-/
theorem zero_is_one_implies_trivial : ((0: R) = 1) → (∀(a : R), a = 0) :=
begin
  intros h a,
  have q : a * 1 = a := mul_one a,
  rw ← h at q,
  rw mul_zero at q,
  symmetry,
  exact q,
end

/-
  P4. Gvien a ∈ ℤ, let -a be the unique solution
  x to a+x=0 (why is it unique?)
  Then : -(-a)  = a and
  -(ab)=(-a)b = a(-b).
  Moreover (-a)(-b) = ab
  and
  -a = (-1)*a
-/
@[simp]
theorem neg_neg_a : ∀ {a : R}, -(-a)=a := begin
  intro a,
  have na := add_neg a,
  have nna := add_neg (-a),
  rw add_comm at nna,
  rw ← na at nna,
  exact right_cancel nna,
end

@[simp]
theorem dist_neg_right : ∀ (a b : R), (a)*(-b)=-(a*b) := begin
  intros a b,
  have x: 0 = a*0 := begin
  rw mul_zero,
  end,
  rw ← add_neg b at x,
  rw mul_add at x,
  rw add_neg b at x,
  have y: a*b + (-(a*b)) = 0 := begin
  rw add_neg (a*b),
  end,
  rw ←  y at x,
  have := left_cancel x,
  symmetry,
  exact this,
end

@[simp]
theorem mul_neg_one : ∀ (a : R), (-1)*a=-a := begin
  intro a,
  have x: a*0=0 := begin
  exact mul_zero,
  end,
  rw ←  add_neg (1:R) at x,
  rw mul_add at x,
  rw mul_one at x,
  rw add_neg at x,
  have z: a + -a = 0 := begin
  rw add_neg,
  end,
  rw ← z at x,
  have z : a*(-1) = -a := begin
  exact left_cancel x,
  end,
  rw mul_comm,
  exact z,
  end
@[simp]
theorem dist_neg_left : ∀ (a b : R), (-a)*(b)=-(a*b) := begin
  intros a b,
  rw ← mul_neg_one a,
  rw mul_comm (-1:R) a,
  rw mul_assoc,
  rw  mul_neg_one b,
  exact dist_neg_right a b,
end
@[simp]
theorem dist_neg_both : ∀ (a b : R), (-a)*(-b)=(a*b) := begin
  intros a b,
  rw dist_neg_left (a) (-b),
  rw dist_neg_right (a) (b),
  rw neg_neg_a,
end
theorem neg1_times_neg1 : (-(1 :R))*(-1) = 1 := begin
  have x: (-1:R)*(0:R)=0 := begin
    exact mul_zero,
  end,
  rw ←  add_neg (1 :R ) at x,
  rw mul_add at x,
  rw mul_one at x,
  rw add_comm at x,
  have : (-1 :R)* (-1)  = 1 := begin
    rw right_cancel x,
  end,
  exact this,
end
/-
  P7
  1 ∈ P and -1 ∉ P
-/

/-
  lemma: if a is positive and b is not positive then a is not b
-/
lemma pos_diff : ∀ (a b : O), is_positive (a) ∧ ¬ is_positive (b) → ¬ a=b :=
begin
  intros a b c,
  cases c with d e,
  by_contradiction,
  rw h at d,
  apply e,
  exact d,
end
/-
  lemma: if a is positive then a is not 0
-/
lemma pos_not_z : ∀ (a : O), is_positive (a) → ¬ a=0 :=
begin
  intros a b c,
  have nontriv := nontriviality,
  apply nontriv,
  rw c at b,
  exact b,
end

/-
  -1 ∉ P : in other words, -1 is not positive
-/
theorem not_neg_one_pos : ¬ is_positive(-1:O) := begin
  intros a,
  have notz := pos_not_z (-1:O),
  have xf := notz a,
  have trich := trichotomy (-1:O),
  have ewf := pos_times_pos a a,
  rw neg1_times_neg1 at ewf,
  cases trich with f g, {
    cases f with d n,
    cases n with d s,
    rw neg_neg_a at s,
    apply s,
    exact ewf,
  },
  cases g with fn dw,{
    cases fn with dj dw,
    cases dw with dn fh,
    rw neg_neg_a at fh,
    apply fh,
    exact ewf,
  },
  cases dw with fs wh,
  cases wh with od wx,
  apply fs,
  exact a,
end
/-
  1 ∈ P : in other words 1 is positive
-/
theorem one_pos : is_positive(1:O) := begin
  have ed : ¬is_positive(-1:O) := not_neg_one_pos,
  cases trichotomy (1:O),
  {
    cases h with h1 h2,
    exact h1,
  },
  {
    cases h,
    cases h with h1 h2,
    cases h2 with h2 h3,
    have h4 : 0 = (1 : O),
    {
      symmetry,
      exact h2,
    },
    {
      have allzero := zero_is_one_implies_trivial h4,
      have zeronotpos : ∃(a : O), is_positive a := nonempty_pos,
      rw h2 at h1,
      cases zeronotpos with a q,
      have azero := allzero a,
      rw ← h2 at azero,
      rw azero at q,
      exact q,
    },
    {
      cases h with h1 h2,
      cases h2 with h2 h3,
      exfalso,
      apply ed,
      exact h3,
    },
  }
end

theorem trans_lt : ∀{a b c : O}, a < b → b <  c → a < c := begin
intros a b c a_le_b b_le_c,
rw less_than at a_le_b,
rw less_than at b_le_c,
rcases a_le_b with ⟨p1, p1pos, eq1⟩ ,
rcases b_le_c with ⟨p2, p2pos, eq2⟩ ,

rw less_than,
use p1 + p2,
split,
exact pos_plus_pos p1pos p2pos,
rw ← eq1 at eq2,
rw add_assoc at eq2,
exact eq2,
end
/-
  P8 Prove the transitivity of ≤
-/
theorem trans_le : ∀{a b c : O}, a ≤ b → b ≤ c → a ≤ c :=
begin
  intros a b c ab bc,
  rw less_eq at ab,
  rw less_eq at bc,
  rw less_eq,
  cases ab,
  {
    cases bc,
    {
      rw less_than at ab,
      rw less_than at bc,
      cases bc with Pbc bc,
      cases ab with Pab ab,
      cases bc with Pbc bc,
      cases ab with Pab ab,
      rw ← ab at bc,
      rw add_assoc at bc,
      have := pos_plus_pos Pab Pbc,
      have alc : a < c,
      {
        rw less_than,
        split,
        exact ⟨this, bc⟩,
      },
      left,
      exact alc,
    },
    {
      left,
      rw ← bc,
      exact ab,
    },
  },
  {
    cases bc,
    {
      left,
      rw ab,
      exact bc,
    },
    {
      right,
      rw ab,
      exact bc,
    }
  },
end
@[simp]
/- 0 + something = something -/
lemma zero_add : ∀{a : R}, 0 + a = a :=
begin
  intro a,
  rw add_comm,
  rw add_zero,
end
/- move stuff across the equal sign-/
lemma move : ∀{a b c: R}, a + b = c → a = c + (-b) :=
begin
  intros a b c h,
  rw ← h,
  rw add_assoc,
  rw add_neg,
  rw add_zero,
end
/- you can multiply by stuff on the right-/
lemma mul_right : ∀{a b : R}, ∀(c : R), a = b → a * c = b * c :=
begin
  intros a b c h,
  rw h,
end
lemma mul_left : ∀{a b : R}, ∀(c : R), a = b → c*a = c*b :=
begin
  intros a b c h,
  rw h,
end

/-
  P9 ∀a, b ∈ ℤ, exactly one of the following is true: a < b or a = b or a > b
  in other words, the integers are a total order.
-/
theorem trichotomy_lt : ∀(a b : O),
  (a < b ∧ a ≠ b ∧ ¬(b < a)) ∨
  (¬(a < b) ∧ a = b ∧ ¬(b < a)) ∨
  (¬(a < b) ∧ a ≠ b ∧ (b < a)) :=
begin
  intros a b,
  have : a + (b + (-a)) = b,
  {
    rw add_comm,
    rw add_assoc,
    rw add_comm (-a) a,
    rw add_neg,
    rw add_zero,
  },
  cases trichotomy (b + (-a)),
  {
    cases h with h1 h2,
    cases h2 with h2 h3,
    left,
    split,
    {
      rw less_than,
      split,
      exact ⟨h1, this⟩,
    },
    split,
    {
      by_contra,
      rw h at h1,
      rw add_neg at h1,
      exact nontriviality h1,
    },
    {
      by_contra,
      rw less_than at h,
      cases h with P h,
      cases h with h4 h5,
      rw ← this at h5,
      rw ← add_zero a at h5,
      rw add_assoc a 0 at h5,
      rw zero_add at h5,
      rw add_assoc at h5,
      have q := left_cancel h5,
      rw add_zero at q,
      rw add_comm at q,
      have q2 := move q,
      rw zero_add at q2,
      rw q2 at h4,
      apply h3,
      exact h4,
    },
  },
  cases h,
  {
    right,
    left,
    cases h with h1 h2,
    cases h2 with h2 h3,
    have h4 := move h2,
    rw neg_neg_a at h4,
    rw zero_add at h4,
    split,
    {
      by_contra,
      rw less_than at h,
      cases h with P h,
      cases h with h5 h6,
      rw h4 at h6,
      rw ← add_zero a at h6,
      rw add_assoc at h6,
      have q := left_cancel h6,
      rw zero_add at q,
      rw q at h5,
      exact nontriviality h5,
    },
    split,
    {
      symmetry,
      exact h4,
    },
    {
      by_contra,
      rw less_than at h,
      cases h with P h,
      cases h with h5 h6,
      rw h4 at h6,
      rw ← add_zero a at h6,
      rw add_assoc at h6,
      have q := left_cancel h6,
      rw zero_add at q,
      rw q at h5,
      exact nontriviality h5,
    },
  },
  {
    right,
    right,
    cases h with h1 h2,
    cases h2 with h2 h3,
    split,
    {
      by_contra,
      rw less_than at h,
      cases h with P h4,
      cases h4 with h4 h5,
      rw ← this at h5,
      have q := left_cancel h5,
      rw ← q at h1,
      apply h1,
      exact h4,
    },
    split,
    {
      by_contra,
      apply h2,
      rw h,
      rw add_neg,
    },
    {
      rw less_than,
      have that := move this,
      split,
      split,
      exact h3,
      symmetry,
      exact that,
    },
  },
end
/-
  P10
  ∀a, b, x, y ∈ ℤ, a ≤ b and x ≤ y → a + x ≤ b + y
  and SALVAGE a ≤ b and x ≤ y and a, x ∈ P implies a * x ≤ b * y
-/
theorem add_le_add: ∀{a b x y : O}, a ≤ b → x ≤ y → a + x ≤ b + y :=
begin
  intros a b x y ab xy,
  rw less_eq,
  rw less_eq at ab,
  rw less_eq at xy,
  rw less_than at ab,
  rw less_than at xy,
  rw less_than,
  cases ab,
  {
    cases xy,
    {
      left,
      cases ab with P1 ab,
      cases xy with P2 xy,
      cases ab with P1pos ab,
      cases xy with P2pos xy,
      have : a + x + (P1 + P2) = b + y,
      {
        rw add_assoc,
        rw ← add_assoc x,
        rw add_comm x,
        rw add_assoc,
        rw xy,
        rw ← add_assoc,
        rw ab,
      },
      have that := pos_plus_pos P1pos P2pos,
      split,
      split,
      exact that,
      exact this,
    },
    {
      left,
      cases ab with P ab,
      cases ab with Ppos ab,
      split,
      split,
      exact Ppos,
      rw xy,
      rw add_assoc,
      rw add_comm y,
      rw ← add_assoc,
      rw ab,
    },
  },
  {
    cases xy,
    {
      left,
      cases xy with P xy,
      cases xy with Ppos xy,
      split,
      split,
      exact Ppos,
      rw add_assoc,
      rw xy,
      rw ab,
    },
    {
      right,
      rw xy,
      rw ab,
    },
  },
end
@[simp]
theorem one_mul: ∀ (a :R), 1*a=a := begin
intro a,
rw mul_comm,
exact mul_one a,
end
theorem no_lt_self: ∀ (a:O), ¬  (a  <a) := begin
intros a b,
rw less_than at b,
cases b,
cases b_h,
rw ← add_zero (a)  at b_h_right,
rw add_assoc at b_h_right,
have := left_cancel b_h_right,
rw zero_add at this,
rw this at b_h_left,
have := nontriviality,
apply this,
exact b_h_left,
end
theorem mul_le :∀ (a b c :O), is_positive c → a < b → a*c < b*c := begin
intros a b c pc alb ,
rw less_than,
rw less_than at alb,
cases alb,
 cases alb_h,
  have := mul_right c alb_h_right,
    rw mul_comm at this,
    rw mul_add at this,

split, {
  split, {

--  cases alb_h,

--
--     -- split, {
      exact pos_times_pos pc alb_h_left,
--     -- },
-- sorry,
  },
  rw mul_comm at this,
  exact this,
  --  cases alb_h,

},
-- sorry,
end
theorem mul_le_mul : ∀{a b x y : O}, is_positive a → is_positive x → a ≤ b → x ≤ y → a * x ≤ b * y :=
begin
  intros a b x y ap xp ab xy,
  rw less_eq,
  rw less_eq at ab,
  rw less_eq at xy,
  rw less_than at ab,
  rw less_than at xy,
  rw less_than,
  cases ab,
  {
    cases xy,
    {
      left,
      cases ab with P1 ab,
      cases xy with P2 xy,
      cases ab with P1pos ab,
      cases xy with P2pos xy,
      have : x*a + x*P1+ (P2*a + P2*P1) = b*y,
      {
        rw ← ab,
        rw ← xy,
        rw mul_add,
        rw mul_comm (a + P1) x,
        rw mul_add,
        rw mul_comm (a + P1) P2,
        rw mul_add,
      },
      have pp: is_positive (x*P1+ (P2*a + P2*P1)),
      {
        have q := pos_times_pos P2pos P1pos,
        have q2 := pos_times_pos P2pos ap,
        have q3 := pos_times_pos xp P1pos,
        have q4 := pos_plus_pos q2 q,
        exact pos_plus_pos q3 q4,
      },
      split,
      split,
      exact pp,
      rw ← add_assoc,
      rw mul_comm a x,
      exact this,
    },
    {
      left,
      cases ab with P ab,
      cases ab with Ppos ab,
      have := mul_right x ab,
      rw mul_comm at this,
      rw mul_add at this,
      have xppos := pos_times_pos xp Ppos,
      split,
      split,
      exact xppos,
      rw xy,
      rw xy at this,
      rw mul_comm,
      exact this,
    },
  },
  {
    cases xy,
    {
      left,
      cases xy with P xy,
      cases xy with Ppos xy,
      have := mul_right a xy,
      rw mul_comm at this,
      rw mul_add at this,
      have appos := pos_times_pos ap Ppos,
      split,
      split,
      exact appos,
      rw ← ab,
      rw mul_comm a y,
      exact this,
    },
    {
      right,
      rw ab,
      rw xy,
    },
  },
end

theorem le_all: ∀(a :O), a ≤ a := begin
intro a,
rw less_eq,
right,
refl,
end
theorem mul_lt_mul : ∀{a b x y : O},
  is_positive a →
  is_positive x →
  a < b →
  x < y →
  a * x < b * y :=
begin
 intros a b x y a_pos x_pos aleb xley,
 rw less_than at aleb,
 rw less_than at xley,
 rw less_than,
 rcases aleb with ⟨p1,p1pos, eq1 ⟩,
rcases xley with ⟨p2,p2pos, eq2 ⟩,
have eq3 : (a + p1)*(x+p2) = b*y := begin
rw eq2,
rw eq1,
end,
rw mul_add at eq3,
rw mul_comm at eq3,
rw mul_comm (a+p1) p2 at eq3,
rw mul_add at eq3,
rw mul_add at eq3,
rw  add_assoc at eq3,
use x * p1 + p2 * a + p2 * p1,
split,
exact (pos_plus_pos (pos_plus_pos (pos_times_pos x_pos p1pos) (pos_times_pos p2pos a_pos)) (pos_times_pos p2pos p1pos)),
rw mul_comm a x,
rw add_assoc,
exact eq3,
end
@[simp]
theorem add_lt_add : ∀(a b x : O),
  a+x < b+x ↔
  a  < b :=
begin
intros a b c,
split,{
  intro eq,
  rw less_than at eq,
  rcases eq with ⟨ w,x,y⟩,
  rw add_comm at y,
  rw ←  add_assoc  at y,
  simp at y,
  rw less_than,
  use w,
  rw add_comm,
  rw y,
  exact ⟨ x,refl b⟩,
},
intro x,
rw less_than,
rw less_than at x,
rcases x with ⟨f,g,h ⟩,
use f,
rw ← h,
rw add_assoc a f c,
rw add_comm f c,
rw ← add_assoc,
exact ⟨g, refl (a+c+f) ⟩,
end
/- If a is positive then it isn't zero -/
lemma pos_not_zero: ∀ {a : O}, is_positive a → a ≠ 0 := begin
  intros a b,
  by_contradiction,
  rw h at b,
  have := nontriviality,
  apply this,
  exact b,
end
/- modus tollens + demorgans law-/
lemma thing : ∀ {P Q R : Prop}, (¬ P ∧  ¬ Q → ¬ R) → (R →  P ∨  Q) := begin
  intros P Q R pqr r,
  by_cases P, --classical logic
  left,
  exact h,
  by_cases Q,
  right,
  exact h,
  have : ¬P ∧ ¬Q,
  split ; assumption,
  have that := pqr this,
  exfalso,
  apply that,
  exact r,
end
@[simp]
/- -0 = 0-/
lemma neg_zero : -(0 : R) = 0 :=
begin
  rw ← mul_neg_one,
  rw mul_zero,
end
@[simp] lemma sub_zero: ∀{a:R}, a-0=a := begin
intros a,
rw subtr a (0:R),
rw neg_zero,
rw add_zero,
end
@[simp] lemma sub_self: ∀{a:R}, a-a=0 := begin
intros a,
rw subtr a (a),
exact add_neg a,
end
/-

  P6: SALVAGE: In an ordered myRing:  ab = 0 → a = 0 or b = 0
  we proved the contrapositive because it was easier and a constructive proof
  the lemma "thing" was used to recover the original version
-/
lemma zero_or' : ∀(a b : O),   a ≠ 0 ∧  b ≠ 0 → a *b ≠  (0:O) :=
begin
  intros a b c,
  cases c with d e,
  have ta := trichotomy a,
  have tb := trichotomy b,
  cases ta with s f, {
    cases tb with d g, {
      cases s with asd fsa,
      cases d with sdd asd,
      cases asd with fds awe,
      cases fsa with sdad qwe,
      exact pos_not_zero (pos_times_pos asd sdd),
    },
    cases s with asd fdj,
    cases g with daskd weq, {
      cases daskd with das qwem,
      cases fdj with as xz,
      cases qwem with oasd we,
      exfalso,
      rw oasd at e,
      apply e,
      refl,
    },
    {
      cases fdj with asds wqer,
      cases weq with asdd tewoirm,
      cases tewoirm with  gfs qowi,
      have := pos_not_zero (pos_times_pos asd qowi),
      rw dist_neg_right at this,
      intro h,
      apply this,
      have that := mul_right (-1 : O) h,
      rw mul_comm at that,
      rw mul_neg_one at that,
      rw mul_comm (0 : O) (-1) at that,
      rw mul_zero at that,
      exact that,
    },
  },
  {
    cases f with w q,
    {
      cases w with junk w,
      cases w with w junk2,
      exfalso,
      apply d,
      exact w,
    },
    {
      cases tb with tb1 tb2,
      {
        cases q with junk q,
        cases q with junk2 negapos,
        cases tb1 with bpos junk3,
        have := pos_not_zero (pos_times_pos negapos bpos),
        intro h,
        apply this,
        rw dist_neg_left,
        rw h,
        simp,
      },
      cases tb2 with tb2 tb3,
      {
        cases tb2 with junk3 tb2,
        cases tb2 with tb2 junk4,
        exfalso,
        apply e,
        exact tb2,
      },
      {
        cases q with junk q,
        cases q with junk2 negapos,
        cases tb3 with junk3 tb3,
        cases tb3 with junk4 negbpos,
        have := pos_not_zero (pos_times_pos negapos negbpos),
        rw dist_neg_both at this,
        exact this,
      },
    },
  },
end
/- The actually useful version of the lemma -/
lemma zero_or : ∀{a b : O},  a *b= (0:O) → a=0 ∨ b=0:=
begin
  intros a b,
  have  x1:= zero_or' a b,
  have  x2 := thing x1,
  exact x2,
end
/-
  P6: SALVAGE : In an ordered myRing you can cancel multiplication,
  in other words: if c ≠ 0 and a * c = b * c, then a = b
-/
theorem mul_cancel : ∀{a b c : O},  a * c = b * c  → c ≠ 0 → a = b:=
begin
  intros a b c d cnotzero,
  have start : a * 0 = 0 := by exact mul_zero,
  rw ← add_neg c at start,
  rw mul_add at start,
  rw d at start,
  rw add_neg at start,
  rw dist_neg_right at start,
  rw ← dist_neg_left at start,
  rw mul_comm at start,
  rw mul_comm (-a) c at start,
  rw ← mul_add at start,
  have := zero_or start,
  cases this, {
    exfalso,
    apply cnotzero,
    exact this,
  },
  {
    have that := move this,
    simp at that,
    symmetry,
    exact that,
  }
end
theorem pos_div_pos: ∀ (a b p: O ), is_positive a  → is_positive b → a*p=b → is_positive p := begin
intros a b p a_pos b_pos equ,
-- rw divs at a_divs_b,
-- cases a_divs_b with p equ,
have p_pos : is_positive p, {
by_contradiction,
have trich := trichotomy p,
cases trich, {
cases trich,{
apply h,
exact trich_left,
},

},
cases trich, {
cases trich, {
cases  trich_right, {
rw trich_right_left at equ,
rw mul_zero at equ,
have x:=pos_not_zero b_pos,
apply x,
symmetry,
exact equ,
},
},
},
cases trich,{
cases trich_right,{
have pp := pos_times_pos a_pos trich_right_right,
rw dist_neg_right at pp,
rw equ at pp,
have trichb := trichotomy b,
cases trichb, {
cases trichb, {
cases trichb_right, {
apply trichb_right_right,
exact pp,
},
},
},
cases trichb, {
cases trichb, {
apply trichb_left,
exact b_pos,
},
},
cases trichb ,{
apply trichb_left,
exact b_pos,
},
},
},
},
exact p_pos,
end
/- P5: Subtraction is associative: WRONG and UNSALVAGEABLE -/


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.pi
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Lebesgue measure on the real line and on `ℝⁿ`
-/

namespace measure_theory

/-!
### Preliminary definitions
-/

/-- Length of an interval. This is the largest monotonic function which correctly
  measures all intervals. -/
def lebesgue_length (s : set ℝ) : ennreal :=
  infi fun (a : ℝ) => infi fun (b : ℝ) => infi fun (h : s ⊆ set.Ico a b) => ennreal.of_real (b - a)

@[simp] theorem lebesgue_length_empty : lebesgue_length ∅ = 0 := sorry

@[simp] theorem lebesgue_length_Ico (a : ℝ) (b : ℝ) :
    lebesgue_length (set.Ico a b) = ennreal.of_real (b - a) :=
  sorry

theorem lebesgue_length_mono {s₁ : set ℝ} {s₂ : set ℝ} (h : s₁ ⊆ s₂) :
    lebesgue_length s₁ ≤ lebesgue_length s₂ :=
  sorry

theorem lebesgue_length_eq_infi_Ioo (s : set ℝ) :
    lebesgue_length s =
        infi
          fun (a : ℝ) =>
            infi fun (b : ℝ) => infi fun (h : s ⊆ set.Ioo a b) => ennreal.of_real (b - a) :=
  sorry

@[simp] theorem lebesgue_length_Ioo (a : ℝ) (b : ℝ) :
    lebesgue_length (set.Ioo a b) = ennreal.of_real (b - a) :=
  sorry

theorem lebesgue_length_eq_infi_Icc (s : set ℝ) :
    lebesgue_length s =
        infi
          fun (a : ℝ) =>
            infi fun (b : ℝ) => infi fun (h : s ⊆ set.Icc a b) => ennreal.of_real (b - a) :=
  sorry

@[simp] theorem lebesgue_length_Icc (a : ℝ) (b : ℝ) :
    lebesgue_length (set.Icc a b) = ennreal.of_real (b - a) :=
  sorry

/-- The Lebesgue outer measure, as an outer measure of ℝ. -/
def lebesgue_outer : outer_measure ℝ :=
  outer_measure.of_function lebesgue_length lebesgue_length_empty

theorem lebesgue_outer_le_length (s : set ℝ) : coe_fn lebesgue_outer s ≤ lebesgue_length s :=
  outer_measure.of_function_le s

theorem lebesgue_length_subadditive {a : ℝ} {b : ℝ} {c : ℕ → ℝ} {d : ℕ → ℝ}
    (ss : set.Icc a b ⊆ set.Union fun (i : ℕ) => set.Ioo (c i) (d i)) :
    ennreal.of_real (b - a) ≤ tsum fun (i : ℕ) => ennreal.of_real (d i - c i) :=
  sorry

@[simp] theorem lebesgue_outer_Icc (a : ℝ) (b : ℝ) :
    coe_fn lebesgue_outer (set.Icc a b) = ennreal.of_real (b - a) :=
  sorry

@[simp] theorem lebesgue_outer_singleton (a : ℝ) : coe_fn lebesgue_outer (singleton a) = 0 := sorry

@[simp] theorem lebesgue_outer_Ico (a : ℝ) (b : ℝ) :
    coe_fn lebesgue_outer (set.Ico a b) = ennreal.of_real (b - a) :=
  sorry

@[simp] theorem lebesgue_outer_Ioo (a : ℝ) (b : ℝ) :
    coe_fn lebesgue_outer (set.Ioo a b) = ennreal.of_real (b - a) :=
  sorry

@[simp] theorem lebesgue_outer_Ioc (a : ℝ) (b : ℝ) :
    coe_fn lebesgue_outer (set.Ioc a b) = ennreal.of_real (b - a) :=
  sorry

theorem is_lebesgue_measurable_Iio {c : ℝ} :
    measurable_space.is_measurable' (outer_measure.caratheodory lebesgue_outer) (set.Iio c) :=
  sorry

theorem lebesgue_outer_trim : outer_measure.trim lebesgue_outer = lebesgue_outer := sorry

theorem borel_le_lebesgue_measurable : borel ℝ ≤ outer_measure.caratheodory lebesgue_outer := sorry

/-!
### Definition of the Lebesgue measure and lengths of intervals
-/

/-- Lebesgue measure on the Borel sets

The outer Lebesgue measure is the completion of this measure. (TODO: proof this)
-/
protected instance real.measure_space : measure_space ℝ :=
  measure_space.mk (measure.mk lebesgue_outer sorry lebesgue_outer_trim)

@[simp] theorem lebesgue_to_outer_measure : measure.to_outer_measure volume = lebesgue_outer := rfl

end measure_theory

namespace real

theorem volume_val (s : set ℝ) : coe_fn volume s = coe_fn measure_theory.lebesgue_outer s := rfl

protected instance has_no_atoms_volume : measure_theory.has_no_atoms volume :=
  measure_theory.has_no_atoms.mk measure_theory.lebesgue_outer_singleton

@[simp] theorem volume_Ico {a : ℝ} {b : ℝ} :
    coe_fn volume (set.Ico a b) = ennreal.of_real (b - a) :=
  measure_theory.lebesgue_outer_Ico a b

@[simp] theorem volume_Icc {a : ℝ} {b : ℝ} :
    coe_fn volume (set.Icc a b) = ennreal.of_real (b - a) :=
  measure_theory.lebesgue_outer_Icc a b

@[simp] theorem volume_Ioo {a : ℝ} {b : ℝ} :
    coe_fn volume (set.Ioo a b) = ennreal.of_real (b - a) :=
  measure_theory.lebesgue_outer_Ioo a b

@[simp] theorem volume_Ioc {a : ℝ} {b : ℝ} :
    coe_fn volume (set.Ioc a b) = ennreal.of_real (b - a) :=
  measure_theory.lebesgue_outer_Ioc a b

@[simp] theorem volume_singleton {a : ℝ} : coe_fn volume (singleton a) = 0 :=
  measure_theory.lebesgue_outer_singleton a

@[simp] theorem volume_interval {a : ℝ} {b : ℝ} :
    coe_fn volume (set.interval a b) = ennreal.of_real (abs (b - a)) :=
  sorry

@[simp] theorem volume_Ioi {a : ℝ} : coe_fn volume (set.Ioi a) = ⊤ := sorry

@[simp] theorem volume_Ici {a : ℝ} : coe_fn volume (set.Ici a) = ⊤ := sorry

@[simp] theorem volume_Iio {a : ℝ} : coe_fn volume (set.Iio a) = ⊤ := sorry

@[simp] theorem volume_Iic {a : ℝ} : coe_fn volume (set.Iic a) = ⊤ := sorry

protected instance locally_finite_volume : measure_theory.locally_finite_measure volume :=
  measure_theory.locally_finite_measure.mk
    fun (x : ℝ) =>
      Exists.intro (set.Ioo (x - 1) (x + 1))
        (Exists.intro
          (mem_nhds_sets is_open_Ioo
            { left := sub_lt_self x zero_lt_one, right := lt_add_of_pos_right x zero_lt_one })
          (eq.mpr
            (id
              (Eq.trans
                ((fun (ᾰ ᾰ_1 : ennreal) (e_2 : ᾰ = ᾰ_1) (ᾰ_2 ᾰ_3 : ennreal) (e_3 : ᾰ_2 = ᾰ_3) =>
                    congr (congr_arg Less e_2) e_3)
                  (coe_fn volume (set.Ioo (x - 1) (x + 1))) (ennreal.of_real (x + 1 - (x - 1)))
                  volume_Ioo ⊤ ⊤ (Eq.refl ⊤))
                (propext (iff_true_intro ennreal.of_real_lt_top))))
            trivial))

/-!
### Volume of a box in `ℝⁿ`
-/

theorem volume_Icc_pi {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ} :
    coe_fn volume (set.Icc a b) =
        finset.prod finset.univ fun (i : ι) => ennreal.of_real (b i - a i) :=
  sorry

@[simp] theorem volume_Icc_pi_to_real {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ}
    (h : a ≤ b) :
    ennreal.to_real (coe_fn volume (set.Icc a b)) =
        finset.prod finset.univ fun (i : ι) => b i - a i :=
  sorry

theorem volume_pi_Ioo {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ} :
    coe_fn volume (set.pi set.univ fun (i : ι) => set.Ioo (a i) (b i)) =
        finset.prod finset.univ fun (i : ι) => ennreal.of_real (b i - a i) :=
  Eq.trans (measure_theory.measure_congr measure_theory.measure.univ_pi_Ioo_ae_eq_Icc) volume_Icc_pi

@[simp] theorem volume_pi_Ioo_to_real {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ}
    (h : a ≤ b) :
    ennreal.to_real (coe_fn volume (set.pi set.univ fun (i : ι) => set.Ioo (a i) (b i))) =
        finset.prod finset.univ fun (i : ι) => b i - a i :=
  sorry

theorem volume_pi_Ioc {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ} :
    coe_fn volume (set.pi set.univ fun (i : ι) => set.Ioc (a i) (b i)) =
        finset.prod finset.univ fun (i : ι) => ennreal.of_real (b i - a i) :=
  Eq.trans (measure_theory.measure_congr measure_theory.measure.univ_pi_Ioc_ae_eq_Icc) volume_Icc_pi

@[simp] theorem volume_pi_Ioc_to_real {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ}
    (h : a ≤ b) :
    ennreal.to_real (coe_fn volume (set.pi set.univ fun (i : ι) => set.Ioc (a i) (b i))) =
        finset.prod finset.univ fun (i : ι) => b i - a i :=
  sorry

theorem volume_pi_Ico {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ} :
    coe_fn volume (set.pi set.univ fun (i : ι) => set.Ico (a i) (b i)) =
        finset.prod finset.univ fun (i : ι) => ennreal.of_real (b i - a i) :=
  Eq.trans (measure_theory.measure_congr measure_theory.measure.univ_pi_Ico_ae_eq_Icc) volume_Icc_pi

@[simp] theorem volume_pi_Ico_to_real {ι : Type u_1} [fintype ι] {a : ι → ℝ} {b : ι → ℝ}
    (h : a ≤ b) :
    ennreal.to_real (coe_fn volume (set.pi set.univ fun (i : ι) => set.Ico (a i) (b i))) =
        finset.prod finset.univ fun (i : ι) => b i - a i :=
  sorry

/-!
### Images of the Lebesgue measure under translation/multiplication/...
-/

theorem map_volume_add_left (a : ℝ) :
    coe_fn (measure_theory.measure.map (Add.add a)) volume = volume :=
  sorry

theorem map_volume_add_right (a : ℝ) :
    coe_fn (measure_theory.measure.map fun (_x : ℝ) => _x + a) volume = volume :=
  sorry

theorem smul_map_volume_mul_left {a : ℝ} (h : a ≠ 0) :
    ennreal.of_real (abs a) • coe_fn (measure_theory.measure.map (Mul.mul a)) volume = volume :=
  sorry

theorem map_volume_mul_left {a : ℝ} (h : a ≠ 0) :
    coe_fn (measure_theory.measure.map (Mul.mul a)) volume = ennreal.of_real (abs (a⁻¹)) • volume :=
  sorry

theorem smul_map_volume_mul_right {a : ℝ} (h : a ≠ 0) :
    ennreal.of_real (abs a) • coe_fn (measure_theory.measure.map fun (_x : ℝ) => _x * a) volume =
        volume :=
  sorry

theorem map_volume_mul_right {a : ℝ} (h : a ≠ 0) :
    coe_fn (measure_theory.measure.map fun (_x : ℝ) => _x * a) volume =
        ennreal.of_real (abs (a⁻¹)) • volume :=
  sorry

@[simp] theorem map_volume_neg : coe_fn (measure_theory.measure.map Neg.neg) volume = volume :=
  sorry

end real

theorem filter.eventually.volume_pos_of_nhds_real {p : ℝ → Prop} {a : ℝ}
    (h : filter.eventually (fun (x : ℝ) => p x) (nhds a)) :
    0 < coe_fn volume (set_of fun (x : ℝ) => p x) :=
  sorry

end Mathlib


theorem tst0 (x : Nat) : x + 0 = x + 0 :=
by {
  generalize x + 0 = y;
  exact (Eq.refl y)
}

theorem tst1 (x : Nat) : x + 0 = x + 0 :=
by {
  generalize h : x + 0 = y;
  exact (Eq.refl y)
}

theorem tst2 (x y w : Nat) (h : y = w) : (x + x) + w  = (x + x) + y :=
by {
  generalize h' : x + x = z;
  subst y;
  exact Eq.refl $ z + w
}

theorem tst3 (x y w : Nat) (h : x + x = y) : (x + x) + (x+x)  = (x + x) + y :=
by {
  generalize h' : x + x = z;
  subst z;
  subst y;
  exact rfl
}

theorem tst4 (x y w : Nat) (h : y = w) : (x + x) + w  = (x + x) + y :=
by {
  generalize h' : x + y = z; -- just add equality
  subst h;
  exact rfl
}


/-
Copyright (c) 2021 Ashvni Narayanan. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ashvni Narayanan, David Loeffler
-/
import data.polynomial.algebra_map
import data.polynomial.derivative
import data.nat.choose.cast
import number_theory.bernoulli

/-!
# Bernoulli polynomials

The [Bernoulli polynomials](https://en.wikipedia.org/wiki/Bernoulli_polynomials)
are an important tool obtained from Bernoulli numbers.

## Mathematical overview

The $n$-th Bernoulli polynomial is defined as
$$ B_n(X) = ∑_{k = 0}^n {n \choose k} (-1)^k  B_k  X^{n - k} $$
where $B_k$ is the $k$-th Bernoulli number. The Bernoulli polynomials are generating functions,
$$ \frac{t  e^{tX} }{ e^t - 1} = ∑_{n = 0}^{\infty} B_n(X)  \frac{t^n}{n!} $$

## Implementation detail

Bernoulli polynomials are defined using `bernoulli`, the Bernoulli numbers.

## Main theorems

- `sum_bernoulli`: The sum of the $k^\mathrm{th}$ Bernoulli polynomial with binomial
  coefficients up to `n` is `(n + 1) * X^n`.
- `polynomial.bernoulli_generating_function`: The Bernoulli polynomials act as generating functions
  for the exponential.

## TODO

- `bernoulli_eval_one_neg` : $$ B_n(1 - x) = (-1)^n B_n(x) $$

-/

noncomputable theory
open_locale big_operators
open_locale nat polynomial

open nat finset

namespace polynomial

/-- The Bernoulli polynomials are defined in terms of the negative Bernoulli numbers. -/
def bernoulli (n : ℕ) : ℚ[X] :=
  ∑ i in range (n + 1), polynomial.monomial (n - i) ((_root_.bernoulli i) * (choose n i))

lemma bernoulli_def (n : ℕ) : bernoulli n =
  ∑ i in range (n + 1), polynomial.monomial i ((_root_.bernoulli (n - i)) * (choose n i)) :=
begin
  rw [←sum_range_reflect, add_succ_sub_one, add_zero, bernoulli],
  apply sum_congr rfl,
  rintros x hx,
  rw mem_range_succ_iff at hx, rw [choose_symm hx, tsub_tsub_cancel_of_le hx],
end

/-
### examples
-/

section examples

@[simp] lemma bernoulli_zero : bernoulli 0 = 1 :=
by simp [bernoulli]

@[simp] lemma bernoulli_eval_zero (n : ℕ) : (bernoulli n).eval 0 = _root_.bernoulli n :=
begin
 rw [bernoulli, eval_finset_sum, sum_range_succ],
  have : ∑ (x : ℕ) in range n, _root_.bernoulli x * (n.choose x) * 0 ^ (n - x) = 0,
  { apply sum_eq_zero (λ x hx, _),
    have h : 0 < n - x := tsub_pos_of_lt (mem_range.1 hx),
    simp [h] },
  simp [this],
end

@[simp] lemma bernoulli_eval_one (n : ℕ) : (bernoulli n).eval 1 = _root_.bernoulli' n :=
begin
  simp only [bernoulli, eval_finset_sum],
  simp only [←succ_eq_add_one, sum_range_succ, mul_one, cast_one, choose_self,
    (_root_.bernoulli _).mul_comm, sum_bernoulli, one_pow, mul_one, eval_C, eval_monomial],
  by_cases h : n = 1,
  { norm_num [h], },
  { simp [h],
    exact bernoulli_eq_bernoulli'_of_ne_one h, }
end

end examples

lemma derivative_bernoulli_add_one (k : ℕ) :
  (bernoulli (k + 1)).derivative = (k + 1) * bernoulli k :=
begin
  simp_rw [bernoulli, derivative_sum, derivative_monomial, nat.sub_sub, nat.add_sub_add_right],
  -- LHS sum has an extra term, but the coefficient is zero:
  rw [range_add_one, sum_insert not_mem_range_self, tsub_self, cast_zero, mul_zero, map_zero,
    zero_add, mul_sum],
  -- the rest of the sum is termwise equal:
  refine sum_congr (by refl) (λ m hm, _),
  conv_rhs { rw [←nat.cast_one, ←nat.cast_add, ←C_eq_nat_cast, C_mul_monomial, mul_comm], },
  rw [mul_assoc, mul_assoc, ←nat.cast_mul, ←nat.cast_mul],
  congr' 3,
  rw [(choose_mul_succ_eq k m).symm, mul_comm],
end

lemma derivative_bernoulli (k : ℕ) : (bernoulli k).derivative = k * bernoulli (k - 1) :=
begin
  cases k,
  { rw [nat.cast_zero, zero_mul, bernoulli_zero, derivative_one], },
  { exact_mod_cast derivative_bernoulli_add_one k, }
end

@[simp] theorem sum_bernoulli (n : ℕ) :
  ∑ k in range (n + 1), ((n + 1).choose k : ℚ) • bernoulli k = monomial n (n + 1 : ℚ) :=
begin
 simp_rw [bernoulli_def, finset.smul_sum, finset.range_eq_Ico, ←finset.sum_Ico_Ico_comm,
    finset.sum_Ico_eq_sum_range],
  simp only [add_tsub_cancel_left, tsub_zero, zero_add, linear_map.map_add],
  simp_rw [smul_monomial, mul_comm (_root_.bernoulli _) _, smul_eq_mul, ←mul_assoc],
  conv_lhs { apply_congr, skip, conv
    { apply_congr, skip,
      rw [← nat.cast_mul, choose_mul ((le_tsub_iff_left $ mem_range_le H).1
        $ mem_range_le H_1) (le.intro rfl), nat.cast_mul, add_comm x x_1, add_tsub_cancel_right,
        mul_assoc, mul_comm, ←smul_eq_mul, ←smul_monomial] },
    rw [←sum_smul], },
  rw [sum_range_succ_comm],
  simp only [add_right_eq_self, mul_one, cast_one, cast_add, add_tsub_cancel_left,
    choose_succ_self_right, one_smul, _root_.bernoulli_zero, sum_singleton, zero_add,
    linear_map.map_add, range_one],
  apply sum_eq_zero (λ x hx, _),
  have f : ∀ x ∈ range n, ¬ n + 1 - x = 1,
  { rintros x H, rw [mem_range] at H,
    rw [eq_comm],
    exact ne_of_lt (nat.lt_of_lt_of_le one_lt_two (le_tsub_of_add_le_left (succ_le_succ H))) },
  rw [sum_bernoulli],
  have g : (ite (n + 1 - x = 1) (1 : ℚ) 0) = 0,
  { simp only [ite_eq_right_iff, one_ne_zero],
    intro h₁,
    exact (f x hx) h₁, },
  rw [g, zero_smul],
end

/-- Another version of `polynomial.sum_bernoulli`. -/
lemma bernoulli_eq_sub_sum (n : ℕ) : (n.succ : ℚ) • bernoulli n = monomial n (n.succ : ℚ) -
  ∑ k in finset.range n, ((n + 1).choose k : ℚ) • bernoulli k :=
by rw [nat.cast_succ, ← sum_bernoulli n, sum_range_succ, add_sub_cancel',
  choose_succ_self_right, nat.cast_succ]

/-- Another version of `bernoulli.sum_range_pow`. -/
lemma sum_range_pow_eq_bernoulli_sub (n p : ℕ) :
  (p + 1 : ℚ) * ∑ k in range n, (k : ℚ) ^ p = (bernoulli p.succ).eval n -
  (_root_.bernoulli p.succ) :=
begin
  rw [sum_range_pow, bernoulli_def, eval_finset_sum, ←sum_div, mul_div_cancel' _ _],
  { simp_rw [eval_monomial],
    symmetry,
    rw [←sum_flip _, sum_range_succ],
    simp only [tsub_self, tsub_zero, choose_zero_right, cast_one, mul_one, pow_zero,
      add_tsub_cancel_right],
    apply sum_congr rfl (λ x hx, _),
    apply congr_arg2 _ (congr_arg2 _ _ _) rfl,
    { rw nat.sub_sub_self (mem_range_le hx), },
    { rw ←choose_symm (mem_range_le hx), }, },
  { norm_cast, apply succ_ne_zero _, },
end

/-- Rearrangement of `polynomial.sum_range_pow_eq_bernoulli_sub`. -/
lemma bernoulli_succ_eval (n p : ℕ) : (bernoulli p.succ).eval n =
  _root_.bernoulli (p.succ) + (p + 1 : ℚ) * ∑ k in range n, (k : ℚ) ^ p :=
by { apply eq_add_of_sub_eq', rw sum_range_pow_eq_bernoulli_sub, }

lemma bernoulli_eval_one_add (n : ℕ) (x : ℚ) :
  (bernoulli n).eval (1 + x) = (bernoulli n).eval x + n * x^(n - 1) :=
begin
  apply nat.strong_induction_on n (λ d hd, _),
  have nz : ((d.succ : ℕ): ℚ) ≠ 0,
  { norm_cast, exact d.succ_ne_zero, },
  apply (mul_right_inj' nz).1,
  rw [← smul_eq_mul, ←eval_smul, bernoulli_eq_sub_sum, mul_add, ←smul_eq_mul,
    ←eval_smul, bernoulli_eq_sub_sum, eval_sub, eval_finset_sum],
  conv_lhs { congr, skip, apply_congr, skip, rw [eval_smul, hd x_1 (mem_range.1 H)], },
  rw [eval_sub, eval_finset_sum],
  simp_rw [eval_smul, smul_add],
  rw [sum_add_distrib, sub_add, sub_eq_sub_iff_sub_eq_sub, _root_.add_sub_sub_cancel],
  conv_rhs { congr, skip, congr, rw [succ_eq_add_one, ←choose_succ_self_right d], },
  rw [nat.cast_succ, ← smul_eq_mul, ←sum_range_succ _ d, eval_monomial_one_add_sub],
  simp_rw [smul_eq_mul],
end

open power_series
variables {A : Type*} [comm_ring A] [algebra ℚ A]

-- TODO: define exponential generating functions, and use them here
-- This name should probably be updated afterwards

/-- The theorem that $(e^X - 1) * ∑ Bₙ(t)* X^n/n! = Xe^{tX}$ -/
theorem bernoulli_generating_function (t : A) :
  mk (λ n, aeval t ((1 / n! : ℚ) • bernoulli n)) * (exp A - 1) =
    power_series.X * rescale t (exp A) :=
begin
  -- check equality of power series by checking coefficients of X^n
  ext n,
  -- n = 0 case solved by `simp`
  cases n, { simp },
  -- n ≥ 1, the coefficients is a sum to n+2, so use `sum_range_succ` to write as
  -- last term plus sum to n+1
  rw [coeff_succ_X_mul, coeff_rescale, coeff_exp, power_series.coeff_mul,
    nat.sum_antidiagonal_eq_sum_range_succ_mk, sum_range_succ],
  -- last term is zero so kill with `add_zero`
  simp only [ring_hom.map_sub, tsub_self, constant_coeff_one, constant_coeff_exp,
    coeff_zero_eq_constant_coeff, mul_zero, sub_self, add_zero],
  -- Let's multiply both sides by (n+1)! (OK because it's a unit)
  have hnp1 : is_unit ((n+1)! : ℚ) := is_unit.mk0 _ (by exact_mod_cast factorial_ne_zero (n+1)),
  rw ←(hnp1.map (algebra_map ℚ A)).mul_right_inj,
  -- do trivial rearrangements to make RHS (n+1)*t^n
  rw [mul_left_comm, ←ring_hom.map_mul],
  change _ = t^n * algebra_map ℚ A (((n+1)*n! : ℕ)*(1/n!)),
  rw [cast_mul, mul_assoc, mul_one_div_cancel
    (show (n! : ℚ) ≠ 0, from cast_ne_zero.2 (factorial_ne_zero n)), mul_one, mul_comm (t^n),
    ← aeval_monomial, cast_add, cast_one],
  -- But this is the RHS of `sum_bernoulli_poly`
  rw [← sum_bernoulli, finset.mul_sum, alg_hom.map_sum],
  -- and now we have to prove a sum is a sum, but all the terms are equal.
  apply finset.sum_congr rfl,
  -- The rest is just trivialities, hampered by the fact that we're coercing
  -- factorials and binomial coefficients between ℕ and ℚ and A.
  intros i hi,
  -- deal with coefficients of e^X-1
  simp only [nat.cast_choose ℚ (mem_range_le hi), coeff_mk,
    if_neg (mem_range_sub_ne_zero hi), one_div, alg_hom.map_smul, power_series.coeff_one,
    coeff_exp, sub_zero, linear_map.map_sub, algebra.smul_mul_assoc, algebra.smul_def,
    mul_right_comm _ ((aeval t) _), ←mul_assoc, ← ring_hom.map_mul, succ_eq_add_one,
    ← polynomial.C_eq_algebra_map, polynomial.aeval_mul, polynomial.aeval_C],
  -- finally cancel the Bernoulli polynomial and the algebra_map
  congr',
  apply congr_arg,
  rw [mul_assoc, div_eq_mul_inv, ← mul_inv],
end

end polynomial


import Lean4Axiomatic.Metric
import Lean4Axiomatic.Rational.Order

/-! # Rational numbers: metric functions -/

namespace Lean4Axiomatic.Rational

open Metric (abs dist MetricSpace)
open Signed (sgn)

/-! ## Axioms -/

/-- Operations pertaining to metrics on rational numbers. -/
class Metric.Ops (ℚ : Type) :=
  /-- Absolute value. -/
  _abs : ℚ → ℚ

  /-- Distance. -/
  _dist : ℚ → ℚ → ℚ

/-- Enables the use of the standard names for absolute value and distance. -/
instance metric_space_inst {ℚ : Type} [Metric.Ops ℚ] : MetricSpace ℚ := {
  abs := Metric.Ops._abs
  dist := Metric.Ops._dist
}

/-- Properties of rational number metrics. -/
class Metric.Props
    {ℕ ℤ : outParam Type} [Natural ℕ] [Integer (ℕ := ℕ) ℤ]
    (ℚ : Type)
      [Core (ℤ := ℤ) ℚ] [Addition ℚ] [Multiplication ℚ]
      [Negation ℚ] [Sign ℚ] [Subtraction ℚ] [Ops ℚ]
    :=
  /--
  The absolute value of a rational number is equivalent to the product of that
  number with its sign.
  -/
  abs_sgn {p : ℚ} : abs p ≃ p * sgn p

  /--
  The distance between two rational numbers is the absolute value of their
  difference.
   -/
  dist_abs {p q : ℚ} : dist p q ≃ abs (p - q)

export Metric.Props (abs_sgn dist_abs)

/-- All rational number metric axioms. -/
class Metric
    {ℕ ℤ : outParam Type} [Natural ℕ] [Integer (ℕ := ℕ) ℤ]
    (ℚ : Type)
      [Core (ℤ := ℤ) ℚ] [Addition ℚ] [Multiplication ℚ]
      [Negation ℚ] [Sign ℚ] [Subtraction ℚ]
    :=
  toOps : Metric.Ops ℚ
  toProps : Metric.Props ℚ

attribute [instance] Metric.toOps
attribute [instance] Metric.toProps

/-! ## Derived properties -/

variable {ℕ ℤ ℚ : Type}
  [Natural ℕ] [Integer (ℕ := ℕ) ℤ]
  [Core (ℤ := ℤ) ℚ] [Addition ℚ] [Negation ℚ] [Subtraction ℚ]
  [Multiplication ℚ] [Reciprocation ℚ] [Division ℚ]
  [Sign ℚ] [Order ℚ] [Metric ℚ]

/--
The absolute value function preserves equivalence over its argument.

**Property intuition**: This must be the case for `abs` to be a function.

**Proof intuition**: Expand `abs` into its `sgn` definition, and use
substitution on multiplication and `sgn`.
-/
theorem abs_subst {p₁ p₂ : ℚ} : p₁ ≃ p₂ → abs p₁ ≃ abs p₂ := by
  intro (_ : p₁ ≃ p₂)
  show abs p₁ ≃ abs p₂
  calc
    abs p₁      ≃ _ := abs_sgn
    p₁ * sgn p₁ ≃ _ := mul_substL ‹p₁ ≃ p₂›
    p₂ * sgn p₁ ≃ _ := mul_substR (from_integer_subst (sgn_subst ‹p₁ ≃ p₂›))
    p₂ * sgn p₂ ≃ _ := eqv_symm abs_sgn
    abs p₂      ≃ _ := eqv_refl

/--
The sign of a rational number's absolute value is the squared sign of the
rational number.

**Property and proof intuition**: The absolute value of a number is that number
times its sign; taking the `sgn` of that gives the result.
-/
theorem sgn_abs {p : ℚ} : sgn (abs p) ≃ sgn p * sgn p := calc
  sgn (abs p)             ≃ _ := sgn_subst abs_sgn
  sgn (p * sgn p)         ≃ _ := sgn_compat_mul
  sgn p * sgn (sgn p : ℚ) ≃ _ := AA.substR sgn_from_integer
  sgn p * sgn (sgn p)     ≃ _ := AA.substR sgn_idemp
  sgn p * sgn p           ≃ _ := Rel.refl

/--
The absolute value of a rational number is greater than or equivalent to zero.

**Property intuition**: The absolute value discards the sign of a number and
returns the magnitude, so we'd expect it to be nonnegative.

**Proof intuition**: The sign of a rational number's absolute value is that
number's sign squared. A square can never be negative, thus the absolute value
must be positive or zero.
-/
theorem abs_ge_zero {p : ℚ} : abs p ≥ 0 := by
  have : sgn (p * p) ≃ sgn (abs p) := calc
    sgn (p * p)     ≃ _ := sgn_compat_mul
    sgn p * sgn p   ≃ _ := Rel.symm sgn_abs
    sgn (abs p)     ≃ _ := Rel.refl
  have : sgn (abs p) ≄ -1 := AA.neqv_substL this nonneg_square
  have : abs p ≥ 0 := ge_zero_sgn.mpr this
  exact this

/--
Zero is the only rational number that has an absolute value of zero.

**Property intuition**: This fits the description of absolute value as
"distance from zero".

**Proof intuition**: In the forward direction, `abs p` expands to `p * sgn p`;
both factors imply that `p ≃ 0`. In the reverse direction, `p * sgn p` is
trivially zero when `p` is.
-/
theorem abs_zero {p : ℚ} : abs p ≃ 0 ↔ p ≃ 0 := by
  apply Iff.intro
  case mp =>
    intro (_ : abs p ≃ 0)
    show p ≃ 0
    have : p * sgn p ≃ 0 := AA.eqv_substL abs_sgn ‹abs p ≃ 0›
    have : p ≃ 0 ∨ (sgn p : ℚ) ≃ 0 := mul_split_zero.mp this
    match this with
    | Or.inl (_ : p ≃ 0) =>
      exact ‹p ≃ 0›
    | Or.inr (_ : (sgn p : ℚ) ≃ 0) =>
      have : sgn p ≃ 0 := from_integer_inject ‹(sgn p : ℚ) ≃ 0›
      have : p ≃ 0 := sgn_zero.mpr this
      exact this
  case mpr =>
    intro (_ : p ≃ 0)
    show abs p ≃ 0
    calc
      abs p           ≃ _ := abs_sgn
      p * sgn p       ≃ _ := mul_substL ‹p ≃ 0›
      (0 : ℚ) * sgn p ≃ _ := mul_absorbL
      0               ≃ _ := eqv_refl

end Lean4Axiomatic.Rational


import ...common.int ...common.atom ...common.list

namespace lia

open list

variables {α β : Type}

inductive atom : Type
| le : int → list int → atom
| dvd : int → int → list int → atom
| ndvd : int → int → list int → atom

-- | (atom.le i ks) := sorry
-- | (atom.dvd d i ks) := sorry
-- | (atom.ndvd d i ks) := sorry

meta def coeffs_to_format : nat → list int → format
| _ [] := "_"
| n [k] := to_fmt k ++ "x" ++ to_fmt n
| n (k1::k2::ks) := to_fmt k1 ++ "x" ++ to_fmt n ++ " + " ++ coeffs_to_format (n+1) (k2::ks)

meta def atom_to_format : atom → format
| (atom.le i ks) := to_fmt i ++ " ≤ " ++ coeffs_to_format 0 ks
| (atom.dvd d i ks) := to_fmt d ++ " | " ++ to_fmt i ++ " + " ++ coeffs_to_format 0 ks
| (atom.ndvd d i ks) := "¬(" ++ atom_to_format (atom.dvd d i ks) ++ ")"

meta instance : has_to_format atom :=
⟨atom_to_format⟩

meta instance : has_to_tactic_format atom :=
has_to_format_to_has_to_tactic_format _

meta instance : has_reflect int :=
by tactic.mk_has_reflect_instance

meta instance has_reflect_atom : has_reflect atom :=
by tactic.mk_has_reflect_instance

instance dec_eq : decidable_eq atom :=
by tactic.mk_dec_eq_instance

open atom

def val : list int → atom → Prop
| xs (le i ks) := i ≤ list.dot_prod ks xs
| xs (dvd d i ks) := has_dvd.dvd d (i + list.dot_prod ks xs)
| xs (ndvd d i ks) := ¬ (has_dvd.dvd d (i + list.dot_prod ks xs))

def neg : atom → fm atom
| (le i ks) := fm.atom (atom.le (1 - i) (list.map has_neg.neg ks))
| (dvd d i ks)  := fm.atom (ndvd d i ks)
| (ndvd d i ks) := fm.atom (dvd d i ks)

def neg_prsv : ∀ (a : atom) (xs : list ℤ), interp val xs (neg a) ↔ interp val xs (¬' A' a)
| (le i ks)     xs :=
  begin
    unfold neg, unfold interp,
    unfold val,
    apply
    (calc
          (1 - i ≤ list.dot_prod (list.map has_neg.neg ks) xs)
        ↔ (has_neg.neg (dot_prod (list.map has_neg.neg ks) xs) ≤ has_neg.neg (1 - i)) :
          by {apply iff.intro, apply neg_le_neg, apply le_of_neg_le_neg}
    ... ↔ (dot_prod ks xs ≤ has_neg.neg (1 - i)) :
          begin rewrite (@neg_dot_prod int _ ks xs), simp, end
    ... ↔ (dot_prod ks xs ≤ i - 1) :
           by rewrite neg_sub
    ... ↔ (dot_prod ks xs < i) :
          begin
            apply iff.intro,
            apply int.lt_of_le_sub_one,
            apply int.le_sub_one_of_lt
          end
    ... ↔ ¬i ≤ dot_prod ks xs :
          begin
            apply iff.intro,
            apply not_le_of_gt,
            apply lt_of_not_ge
          end )
  end
| (dvd d i ks)  xs := by refl
| (ndvd d i ks) xs := by apply iff_not_not

def decr : atom → atom
| (le i ks)     := le i (list.tail ks)
| (dvd d i ks)  := dvd d i (list.tail ks)
| (ndvd d i ks) := ndvd d i (list.tail ks)

def hd_coeff : atom → int
| (le i ks)     := list.head_dft 0 ks
| (dvd d i ks)  := list.head_dft 0 ks
| (ndvd d i ks) := list.head_dft 0 ks

def dep0 (a) := hd_coeff a ≠ 0

meta def decr_prsv_aux : tactic unit :=
`[unfold decr, unfold val,
  cases ks with k ks,
  simp, repeat {rewrite nil_dot_prod},
  unfold dep0 at h, unfold hd_coeff at h,
  unfold list.head_dft at h,
  have h' := classical.by_contradiction h,
  clear h, subst h', cases bs with b' bs',
  simp, rewrite cons_dot_prod_cons,
  rewrite zero_mul, rewrite zero_add, simp]

lemma decr_prsv : ∀ (a : atom), ¬dep0 a → ∀ (b : ℤ) (bs : list ℤ),
  val bs (decr a) ↔ val (b :: bs) a
| (le i ks)      h b bs := by decr_prsv_aux
| (dvd d i ks)   h b bs := by decr_prsv_aux
| (ndvd d i ks)  h b bs := by decr_prsv_aux

def normal : atom → Prop
| (le i ks)     := true
| (dvd d i ks)  := d ≠ 0
| (ndvd d i ks) := d ≠ 0

def divisor : atom → int
| (le i ks)     := 1
| (dvd d i ks)  := d
| (ndvd d i ks) := d

lemma normal_iff_divisor_nonzero {a : atom} :
  normal a ↔ divisor a ≠ 0 :=
begin
  cases a with i ks d i ks d i ks,
  apply true_iff_true, trivial,
  intro hc, cases hc, refl, refl
end

def dec_normal : decidable_pred normal
| (le i ks)     := decidable.is_true trivial
| (dvd d i ks)  := by apply dec_not_pred_of_dec_pred
| (ndvd d i ks) := by apply dec_not_pred_of_dec_pred

meta def neg_prsv_normal_aux :=
  `[unfold neg at hb, unfold atoms at hb,
    rewrite (eq_of_mem_singleton hb), trivial]

lemma neg_prsv_normal : ∀ (a : atom), normal a → ∀ (b : atom), b ∈ @atoms _ _ (neg a) → normal b
| (le i ks)     h b hb := by neg_prsv_normal_aux
| (dvd d i ks)  h b hb := by neg_prsv_normal_aux
| (ndvd d i ks) h b hb := by neg_prsv_normal_aux

lemma decr_prsv_normal : ∀ (a : atom), normal a → ¬dep0 a → normal (decr a)
| (le i ks)     hn hd := by unfold decr
| (dvd d i ks)  hn hd := begin intro hc, apply hn hc end
| (ndvd d i ks) hn hd := begin intro hc, apply hn hc end

instance : atom_type atom int :=
{ val := val,
  neg := neg,
  neg_nqfree :=
    begin intro a, cases a; trivial, end,
  neg_prsv := neg_prsv,
  dep0 := dep0,
  dec_dep0 :=
    begin intro a, apply dec_not_pred_of_dec_pred end,
  decr := decr,
  decr_prsv := decr_prsv,
  inh := 0,
  dec_eq := _,
  normal := normal,
  dec_normal := dec_normal,
  neg_prsv_normal := neg_prsv_normal,
  decr_prsv_normal := decr_prsv_normal }

def asubst (i') (ks') : atom → atom
| (le i (k::ks))     := le (i - (k * i')) (comp_add (map_mul k ks') ks)
| (dvd d i (k::ks))  := dvd d (i + (k * i')) (comp_add (map_mul k ks') ks)
| (ndvd d i (k::ks)) := ndvd d (i + (k * i')) (comp_add (map_mul k ks') ks)
| a := a

meta def asubst_prsv_tac :=
`[unfold asubst, unfold val, rewrite add_assoc,
  have he : (i' * k + dot_prod (comp_add (map_mul k ks') ks) xs)
            = (dot_prod (k :: ks) ((i' + dot_prod ks' xs) :: xs)),
  rewrite cons_dot_prod_cons,
  rewrite mul_add, rewrite mul_comm, simp,
  rewrite comp_add_dot_prod,
  simp, rewrite mul_comm at he, rewrite he]

meta def asubst_prsv_aux :=
`[unfold asubst, unfold val,
  repeat {rewrite nil_dot_prod}]

lemma asubst_prsv (i' ks' xs) :
  ∀ a, val xs (asubst i' ks' a) ↔ val ((i' + dot_prod ks' xs)::xs) a
| (le i (k::ks))     :=
  begin
    unfold asubst, simp, unfold val,
    rewrite add_le_iff_le_sub, simp,
    have he : (i' * k + dot_prod (comp_add (map_mul k ks') ks) xs)
               = (dot_prod (k :: ks) ((i' + dot_prod ks' xs) :: xs)),
    rewrite cons_dot_prod_cons,
    rewrite mul_add, rewrite mul_comm, simp,
    rewrite comp_add_dot_prod,
    simp, rewrite mul_comm at he,
    simp at *, rewrite he
  end
| (dvd d i (k::ks))  := by asubst_prsv_tac
| (ndvd d i (k::ks)) := by asubst_prsv_tac
| (le i [])     := by asubst_prsv_aux
| (dvd d i [])  := by asubst_prsv_aux
| (ndvd d i []) := by asubst_prsv_aux

end lia


/-
Copyright (c) 2021 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import data.set.lattice
import order.zorn
import tactic.by_contra

/-!
# Extend a partial order to a linear order

This file constructs a linear order which is an extension of the given partial order, using Zorn's
lemma.
-/

universes u
open set classical
open_locale classical

/--
Any partial order can be extended to a linear order.
-/
theorem extend_partial_order {α : Type u} (r : α → α → Prop) [is_partial_order α r] :
  ∃ (s : α → α → Prop) (_ : is_linear_order α s), r ≤ s :=
begin
  let S := {s | is_partial_order α s},
  have hS : ∀ c, c ⊆ S → is_chain (≤) c → ∀ y ∈ c, (∃ ub ∈ S, ∀ z ∈ c, z ≤ ub),
  { rintro c hc₁ hc₂ s hs,
    haveI := (hc₁ hs).1,
    refine ⟨Sup c, _, λ z hz, le_Sup hz⟩,
    refine { refl := _, trans := _, antisymm := _ }; simp_rw binary_relation_Sup_iff,
    { intro x,
      exact ⟨s, hs, refl x⟩ },
    { rintro x y z ⟨s₁, h₁s₁, h₂s₁⟩ ⟨s₂, h₁s₂, h₂s₂⟩,
      haveI : is_partial_order _ _ := hc₁ h₁s₁,
      haveI : is_partial_order _ _ := hc₁ h₁s₂,
      cases hc₂.total h₁s₁ h₁s₂,
      { exact ⟨s₂, h₁s₂, trans (h _ _ h₂s₁) h₂s₂⟩ },
      { exact ⟨s₁, h₁s₁, trans h₂s₁ (h _ _ h₂s₂)⟩ } },
    { rintro x y ⟨s₁, h₁s₁, h₂s₁⟩ ⟨s₂, h₁s₂, h₂s₂⟩,
      haveI : is_partial_order _ _ := hc₁ h₁s₁,
      haveI : is_partial_order _ _ := hc₁ h₁s₂,
      cases hc₂.total h₁s₁ h₁s₂,
      { exact antisymm (h _ _ h₂s₁) h₂s₂ },
      { apply antisymm h₂s₁ (h _ _ h₂s₂) } } },
  obtain ⟨s, hs₁ : is_partial_order _ _, rs, hs₂⟩ := zorn_nonempty_partial_order₀ S hS r ‹_›,
  resetI,
  refine ⟨s, { total := _ }, rs⟩,
  intros x y,
  by_contra' h,
  let s' := λ x' y', s x' y' ∨ s x' x ∧ s y y',
  rw ←hs₂ s' _ (λ _ _, or.inl) at h,
  { apply h.1 (or.inr ⟨refl _, refl _⟩) },
  { refine
      { refl := λ x, or.inl (refl _),
        trans := _,
        antisymm := _ },
    { rintro a b c (ab | ⟨ax : s a x, yb : s y b⟩) (bc | ⟨bx : s b x, yc : s y c⟩),
      { exact or.inl (trans ab bc), },
      { exact or.inr ⟨trans ab bx, yc⟩ },
      { exact or.inr ⟨ax, trans yb bc⟩ },
      { exact or.inr ⟨ax, yc⟩ } },
    { rintro a b (ab | ⟨ax : s a x, yb : s y b⟩) (ba | ⟨bx : s b x, ya : s y a⟩),
      { exact antisymm ab ba },
      { exact (h.2 (trans ya (trans ab bx))).elim },
      { exact (h.2 (trans yb (trans ba ax))).elim },
      { exact (h.2 (trans yb bx)).elim } } },
end

/-- A type alias for `α`, intended to extend a partial order on `α` to a linear order. -/
def linear_extension (α : Type u) : Type u := α

noncomputable instance {α : Type u} [partial_order α] : linear_order (linear_extension α) :=
{ le := (extend_partial_order ((≤) : α → α → Prop)).some,
  le_refl := (extend_partial_order ((≤) : α → α → Prop)).some_spec.some.1.1.1.1,
  le_trans := (extend_partial_order ((≤) : α → α → Prop)).some_spec.some.1.1.2.1,
  le_antisymm := (extend_partial_order ((≤) : α → α → Prop)).some_spec.some.1.2.1,
  le_total := (extend_partial_order ((≤) : α → α → Prop)).some_spec.some.2.1,
  decidable_le := classical.dec_rel _ }

/-- The embedding of `α` into `linear_extension α` as a relation homomorphism. -/
def to_linear_extension {α : Type u} [partial_order α] :
  ((≤) : α → α → Prop) →r ((≤) : linear_extension α → linear_extension α → Prop) :=
{ to_fun := λ x, x,
  map_rel' := λ a b, (extend_partial_order ((≤) : α → α → Prop)).some_spec.some_spec _ _ }

instance {α : Type u} [inhabited α] : inhabited (linear_extension α) :=
⟨(default : α)⟩


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Chris Hughes, Floris van Doorn, Yaël Dillies

! This file was ported from Lean 3 source module data.nat.factorial.basic
! leanprover-community/mathlib commit d012cd09a9b256d870751284dd6a29882b0be105
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Nat.Basic
import Mathlib.Data.Nat.Pow

/-!
# Factorial and variants

This file defines the factorial, along with the ascending and descending variants.

## Main declarations

* `Nat.factorial`: The factorial.
* `Nat.ascFactorial`: The ascending factorial. Note that it runs from `n + 1` to `n + k`
  and *not* from `n` to `n + k - 1`. We might want to change that in the future.
* `Nat.descFactorial`: The descending factorial. It runs from `n - k` to `n`.
-/

namespace Nat

/-- `Nat.factorial n` is the factorial of `n`. -/
@[simp]
def factorial : ℕ → ℕ
  | 0 => 1
  | succ n => succ n * factorial n
#align nat.factorial Nat.factorial

/-- factorial notation `n!` -/
scoped notation:10000 n "!" => Nat.factorial n

section Factorial

variable {m n : ℕ}

@[simp]
theorem factorial_zero : 0! = 1 :=
  rfl
#align nat.factorial_zero Nat.factorial_zero

@[simp]
theorem factorial_succ (n : ℕ) : n.succ ! = (n + 1) * n ! :=
  rfl
#align nat.factorial_succ Nat.factorial_succ

-- Porting note: can be proved by simp, @[simp] removed
theorem factorial_one : 1! = 1 :=
  rfl
#align nat.factorial_one Nat.factorial_one

-- Porting note: can be proved by simp, @[simp] removed
theorem factorial_two : 2! = 2 :=
  rfl
#align nat.factorial_two Nat.factorial_two

theorem mul_factorial_pred (hn : 0 < n) : n * (n - 1)! = n ! :=
  tsub_add_cancel_of_le (Nat.succ_le_of_lt hn) ▸ rfl
#align nat.mul_factorial_pred Nat.mul_factorial_pred

theorem factorial_pos : ∀ n, 0 < n !
  | 0 => zero_lt_one
  | succ n => mul_pos (succ_pos _) (factorial_pos n)
#align nat.factorial_pos Nat.factorial_pos

theorem factorial_ne_zero (n : ℕ) : n ! ≠ 0 :=
  ne_of_gt (factorial_pos _)
#align nat.factorial_ne_zero Nat.factorial_ne_zero

theorem factorial_dvd_factorial {m n} (h : m ≤ n) : m ! ∣ n ! := by
  induction' n with n IH
  · simp [Nat.eq_zero_of_le_zero h]
  obtain rfl | hl := h.eq_or_lt
  · simp
  exact (IH (le_of_lt_succ hl)).mul_left _
#align nat.factorial_dvd_factorial Nat.factorial_dvd_factorial

theorem dvd_factorial : ∀ {m n}, 0 < m → m ≤ n → m ∣ n !
  | succ _, _, _, h => dvd_of_mul_right_dvd (factorial_dvd_factorial h)
#align nat.dvd_factorial Nat.dvd_factorial

@[mono]
theorem factorial_le {m n} (h : m ≤ n) : m ! ≤ n ! :=
  le_of_dvd (factorial_pos _) (factorial_dvd_factorial h)
#align nat.factorial_le Nat.factorial_le

-- Porting note: Interconversion between `succ` and `· + 1` has to be done manually
theorem factorial_mul_pow_le_factorial : ∀ {m n : ℕ}, m ! * m.succ ^ n ≤ (m + n)!
  | m, 0 => by simp
  | m, n + 1 => by
    rw [← add_assoc, ← Nat.succ_eq_add_one (m + n), Nat.factorial_succ, pow_succ',
        mul_comm (_ + 1), mul_comm (succ m), ← mul_assoc]
    exact
      mul_le_mul factorial_mul_pow_le_factorial (Nat.succ_le_succ (Nat.le_add_right _ _))
        (Nat.zero_le _) (Nat.zero_le _)
#align nat.factorial_mul_pow_le_factorial Nat.factorial_mul_pow_le_factorial

theorem monotone_factorial : Monotone factorial := fun _ _ => factorial_le
#align nat.monotone_factorial Nat.monotone_factorial

theorem factorial_lt (hn : 0 < n) : n ! < m ! ↔ n < m := by
  refine' ⟨fun h => not_le.mp fun hmn => not_le_of_lt h (factorial_le hmn), fun h => _⟩
  have : ∀ {n}, 0 < n → n ! < n.succ ! := by
    intro k hk
    rw [factorial_succ, succ_mul, lt_add_iff_pos_left]
    exact mul_pos hk k.factorial_pos
  induction' h with k hnk ih generalizing hn
  · exact this hn
  · exact (ih hn).trans (this <| hn.trans <| lt_of_succ_le hnk)
#align nat.factorial_lt Nat.factorial_lt

theorem one_lt_factorial : 1 < n ! ↔ 1 < n :=
  factorial_lt one_pos
#align nat.one_lt_factorial Nat.one_lt_factorial

-- Porting note: `(_ | _)` notation for introduction with cases does not appear to be supported
theorem factorial_eq_one : n ! = 1 ↔ n ≤ 1 := by
  apply Iff.intro <;> intro
  · rw [← not_lt, ← one_lt_factorial, ‹n ! = 1›]
    apply lt_irrefl
  · cases ‹n ≤ 1›
    · rfl
    · cases ‹n ≤ 0›; rfl
#align nat.factorial_eq_one Nat.factorial_eq_one

theorem factorial_inj (hn : 1 < n !) : n ! = m ! ↔ n = m := by
  refine' ⟨fun h => _, congr_arg _⟩
  obtain hnm | rfl | hnm := lt_trichotomy n m
  · rw [← factorial_lt <| pos_of_gt <| one_lt_factorial.mp hn, h] at hnm
    cases lt_irrefl _ hnm
  · rfl
  rw [h, one_lt_factorial] at hn
  rw [← factorial_lt (lt_trans one_pos hn), h] at hnm
  cases lt_irrefl _ hnm
#align nat.factorial_inj Nat.factorial_inj

theorem self_le_factorial : ∀ n : ℕ, n ≤ n !
  | 0 => zero_le_one
  | k + 1 => le_mul_of_one_le_right k.zero_lt_succ.le (Nat.one_le_of_lt <| Nat.factorial_pos _)
#align nat.self_le_factorial Nat.self_le_factorial

-- Porting note: `zero_lt_two` does not work since `ZeroLEOneClass` fails to be synthesised.
-- Porting note: `0 < 2` is proved `by decide` instead
theorem lt_factorial_self {n : ℕ} (hi : 3 ≤ n) : n < n ! := by
  have : 0 < n := (by decide : 0 < 2).trans (succ_le_iff.mp hi)
  have : 1 < pred n := le_pred_of_lt (succ_le_iff.mp hi)
  rw [← succ_pred_eq_of_pos ‹0 < n›, factorial_succ]
  exact
    lt_mul_of_one_lt_right (pred n).succ_pos
      ((‹1 < pred n›).trans_le (self_le_factorial _))
#align nat.lt_factorial_self Nat.lt_factorial_self

theorem add_factorial_succ_lt_factorial_add_succ {i : ℕ} (n : ℕ) (hi : 2 ≤ i) :
    i + (n + 1)! < (i + n + 1)! := by
  rw [← Nat.succ_eq_add_one (i + _), factorial_succ (i + _), add_mul, one_mul]
  have : i ≤ i + n := le.intro rfl
  exact
    add_lt_add_of_lt_of_le
      (this.trans_lt
        ((lt_mul_iff_one_lt_right ((by decide : 0 < 2).trans_le (hi.trans this))).mpr
          (lt_iff_le_and_ne.mpr
            ⟨(i + n).factorial_pos, fun g =>
              Nat.not_succ_le_self 1 ((hi.trans this).trans (factorial_eq_one.mp g.symm))⟩)))
      (factorial_le
        ((le_of_eq (add_comm n 1)).trans
        ((add_le_add_iff_right n).mpr ((by decide : 1 ≤ 2).trans hi))))
#align nat.add_factorial_succ_lt_factorial_add_succ Nat.add_factorial_succ_lt_factorial_add_succ

theorem add_factorial_lt_factorial_add {i n : ℕ} (hi : 2 ≤ i) (hn : 1 ≤ n) :
    i + n ! < (i + n)! := by
  cases hn
  · rw [factorial_one]
    exact lt_factorial_self (succ_le_succ hi)
  exact add_factorial_succ_lt_factorial_add_succ _ hi
#align nat.add_factorial_lt_factorial_add Nat.add_factorial_lt_factorial_add

theorem add_factorial_succ_le_factorial_add_succ (i : ℕ) (n : ℕ) :
    i + (n + 1)! ≤ (i + (n + 1))! := by
  cases (le_or_lt (2 : ℕ) i)
  · rw [← add_assoc]
    apply Nat.le_of_lt
    apply add_factorial_succ_lt_factorial_add_succ
    assumption
  · match i with
    | 0 => simp
    | 1 =>
      rw [← add_assoc, ← Nat.succ_eq_add_one (1 + n), factorial_succ (1 + n),
        add_mul, one_mul, add_comm 1 n, add_le_add_iff_right]
      apply one_le_mul
      · apply Nat.le_add_left
      · apply factorial_pos
    | succ (succ n) => contradiction
#align nat.add_factorial_succ_le_factorial_add_succ Nat.add_factorial_succ_le_factorial_add_succ

theorem add_factorial_le_factorial_add (i : ℕ) {n : ℕ} (n1 : 1 ≤ n) : i + n ! ≤ (i + n)! := by
  cases' n1 with h
  · exact self_le_factorial _
  exact add_factorial_succ_le_factorial_add_succ i h
#align nat.add_factorial_le_factorial_add Nat.add_factorial_le_factorial_add

end Factorial

/-! ### Ascending and descending factorials -/

section AscFactorial

/-- `n.ascFactorial k = (n + k)! / n!` (as seen in `Nat.ascFactorial_eq_div`), but implemented
recursively to allow for "quick" computation when using `norm_num`. This is closely related to
`pochhammer`, but much less general. -/
def ascFactorial (n : ℕ) : ℕ → ℕ
  | 0 => 1
  | k + 1 => (n + k + 1) * ascFactorial n k
#align nat.asc_factorial Nat.ascFactorial

@[simp]
theorem ascFactorial_zero (n : ℕ) : n.ascFactorial 0 = 1 :=
  rfl
#align nat.asc_factorial_zero Nat.ascFactorial_zero

@[simp]
theorem zero_ascFactorial (k : ℕ) : (0 : ℕ).ascFactorial k = k ! := by
  induction' k with t ht
  · rfl
  rw [ascFactorial, ht, zero_add, Nat.factorial_succ]
#align nat.zero_asc_factorial Nat.zero_ascFactorial

theorem ascFactorial_succ {n k : ℕ} : n.ascFactorial k.succ = (n + k + 1) * n.ascFactorial k :=
  rfl
#align nat.asc_factorial_succ Nat.ascFactorial_succ

-- Porting note: Explicit arguments are required to show that the recursion terminates
theorem succ_ascFactorial (n : ℕ) :
    ∀ k, (n + 1) * n.succ.ascFactorial k = (n + k + 1) * n.ascFactorial k
  | 0 => by rw [add_zero, ascFactorial_zero, ascFactorial_zero]
  | k + 1 => by
    rw [ascFactorial, mul_left_comm, succ_ascFactorial n k, ascFactorial,
      succ_add, ← add_assoc, succ_eq_add_one]
#align nat.succ_asc_factorial Nat.succ_ascFactorial

/-- `n.ascFactorial k = (n + k)! / n!` but without ℕ-division. See `Nat.ascFactorial_eq_div` for
the version with ℕ-division. -/
-- Porting note: Explicit arguments are required to show that the recursion terminates
-- Porting note: Interconversion between `succ` and `· + 1` has to be done manually
theorem factorial_mul_ascFactorial (n : ℕ) : ∀ k, n ! * n.ascFactorial k = (n + k)!
  | 0 => by rw [ascFactorial, add_zero, mul_one]
  | k + 1 => by
    rw [ascFactorial_succ, mul_left_comm, factorial_mul_ascFactorial n k,
      ← add_assoc, ← Nat.succ_eq_add_one (n + k), factorial]
#align nat.factorial_mul_asc_factorial Nat.factorial_mul_ascFactorial

/-- Avoid in favor of `Nat.factorial_mul_ascFactorial` if you can. ℕ-division isn't worth it. -/
theorem ascFactorial_eq_div (n k : ℕ) : n.ascFactorial k = (n + k)! / n ! := by
  apply mul_left_cancel₀ n.factorial_ne_zero
  rw [factorial_mul_ascFactorial]
  exact (Nat.mul_div_cancel' <| factorial_dvd_factorial <| le.intro rfl).symm
#align nat.asc_factorial_eq_div Nat.ascFactorial_eq_div

theorem ascFactorial_of_sub {n k : ℕ} (h : k < n) :
    (n - k) * (n - k).ascFactorial k = (n - (k + 1)).ascFactorial (k + 1) := by
  let t := n - k.succ
  let ht : t = n - k.succ := rfl
  suffices h' : n - k = t.succ; · rw [← ht, h', succ_ascFactorial, ascFactorial_succ]
  rw [ht, succ_eq_add_one, ← tsub_tsub_assoc (succ_le_of_lt h) (succ_pos _), succ_sub_one]
#align nat.asc_factorial_of_sub Nat.ascFactorial_of_sub

theorem pow_succ_le_ascFactorial (n : ℕ) : ∀ k : ℕ, (n + 1) ^ k ≤ n.ascFactorial k
  | 0 => by rw [ascFactorial_zero, pow_zero]
  | k + 1 => by
    rw [pow_succ, mul_comm]
    exact Nat.mul_le_mul (Nat.add_le_add_right le_self_add _) (pow_succ_le_ascFactorial _ k)
#align nat.pow_succ_le_asc_factorial Nat.pow_succ_le_ascFactorial

theorem pow_lt_ascFactorial' (n k : ℕ) : (n + 1) ^ (k + 2) < n.ascFactorial (k + 2) := by
  rw [pow_succ, ascFactorial, mul_comm]
  exact
    Nat.mul_lt_mul (Nat.add_lt_add_right (Nat.lt_add_of_pos_right succ_pos') 1)
      (pow_succ_le_ascFactorial n _) (pow_pos succ_pos' _)
#align nat.pow_lt_asc_factorial' Nat.pow_lt_ascFactorial'

theorem pow_lt_ascFactorial (n : ℕ) : ∀ {k : ℕ}, 2 ≤ k → (n + 1) ^ k < n.ascFactorial k
  | 0 => by rintro ⟨⟩
  | 1 => by intro; contradiction
  | k + 2 => fun _ => pow_lt_ascFactorial' n k
#align nat.pow_lt_asc_factorial Nat.pow_lt_ascFactorial

theorem ascFactorial_le_pow_add (n : ℕ) : ∀ k : ℕ, n.ascFactorial k ≤ (n + k) ^ k
  | 0 => by rw [ascFactorial_zero, pow_zero]
  | k + 1 => by
    rw [ascFactorial_succ, pow_succ, ← add_assoc,
    ← Nat.succ_eq_add_one (n + k), mul_comm _ (succ (n + k))]
    exact
      Nat.mul_le_mul_of_nonneg_left
        ((ascFactorial_le_pow_add _ k).trans (Nat.pow_le_pow_of_le_left (le_succ _) _))
#align nat.asc_factorial_le_pow_add Nat.ascFactorial_le_pow_add

theorem ascFactorial_lt_pow_add (n : ℕ) : ∀ {k : ℕ}, 2 ≤ k → n.ascFactorial k < (n + k) ^ k
  | 0 => by rintro ⟨⟩
  | 1 => by intro; contradiction
  | k + 2 => fun _ => by
    rw [ascFactorial_succ, pow_succ]
    rw [add_assoc n (k + 1) 1, mul_comm <| (n + (k + 2)) ^ (k + 1)]
    refine'
      Nat.mul_lt_mul' le_rfl
        ((ascFactorial_le_pow_add n _).trans_lt
          (pow_lt_pow_of_lt_left (lt_add_one _) (succ_pos _)))
        (succ_pos _)
#align nat.asc_factorial_lt_pow_add Nat.ascFactorial_lt_pow_add

theorem ascFactorial_pos (n k : ℕ) : 0 < n.ascFactorial k :=
  (pow_pos (succ_pos n) k).trans_le (pow_succ_le_ascFactorial n k)
#align nat.asc_factorial_pos Nat.ascFactorial_pos

end AscFactorial

section DescFactorial

/-- `n.descFactorial k = n! / (n - k)!` (as seen in `Nat.descFactorial_eq_div`), but
implemented recursively to allow for "quick" computation when using `norm_num`. This is closely
related to `pochhammer`, but much less general. -/
def descFactorial (n : ℕ) : ℕ → ℕ
  | 0 => 1
  | k + 1 => (n - k) * descFactorial n k
#align nat.desc_factorial Nat.descFactorial

@[simp]
theorem descFactorial_zero (n : ℕ) : n.descFactorial 0 = 1 :=
  rfl
#align nat.desc_factorial_zero Nat.descFactorial_zero

@[simp]
theorem descFactorial_succ (n k : ℕ) : n.descFactorial k.succ = (n - k) * n.descFactorial k :=
  rfl
#align nat.desc_factorial_succ Nat.descFactorial_succ

theorem zero_descFactorial_succ (k : ℕ) : (0 : ℕ).descFactorial k.succ = 0 := by
  rw [descFactorial_succ, zero_tsub, zero_mul]
#align nat.zero_desc_factorial_succ Nat.zero_descFactorial_succ

/- Porting note: simp removed because this can be proved by
simp only [Nat.descFactorial_succ, nonpos_iff_eq_zero, tsub_zero, Nat.descFactorial_zero, mul_one]
-/
-- @[simp]
theorem descFactorial_one (n : ℕ) : n.descFactorial 1 = n := by
  rw [descFactorial_succ, descFactorial_zero, mul_one, tsub_zero]
#align nat.desc_factorial_one Nat.descFactorial_one

/- Porting note: simp removed because the lhs simplifies,
according to the linter:
Left-hand side simplifies from
  Nat.descFactorial (n + 1) (k + 1)
to
  (n + 1 - k) * Nat.descFactorial (n + 1) k
using
  simp only [Nat.descFactorial_succ]
-/
-- @[simp]
theorem succ_descFactorial_succ (n : ℕ) :
    ∀ k : ℕ, (n + 1).descFactorial (k + 1) = (n + 1) * n.descFactorial k
  | 0 => by rw [descFactorial_zero, descFactorial_one, mul_one]
  | succ k => by
    rw [descFactorial_succ, succ_descFactorial_succ _ k, descFactorial_succ, succ_sub_succ,
      mul_left_comm]
#align nat.succ_desc_factorial_succ Nat.succ_descFactorial_succ

theorem succ_descFactorial (n : ℕ) :
    ∀ k, (n + 1 - k) * (n + 1).descFactorial k = (n + 1) * n.descFactorial k
  | 0 => by rw [tsub_zero, descFactorial_zero, descFactorial_zero]
  | k + 1 => by
    rw [descFactorial, succ_descFactorial _ k, descFactorial_succ, succ_sub_succ, mul_left_comm]
#align nat.succ_desc_factorial Nat.succ_descFactorial

theorem descFactorial_self : ∀ n : ℕ, n.descFactorial n = n !
  | 0 => by rw [descFactorial_zero, factorial_zero]
  | succ n => by rw [succ_descFactorial_succ, descFactorial_self n, factorial_succ]
#align nat.desc_factorial_self Nat.descFactorial_self

@[simp]
theorem descFactorial_eq_zero_iff_lt {n : ℕ} : ∀ {k : ℕ}, n.descFactorial k = 0 ↔ n < k
  | 0 => by simp only [descFactorial_zero, Nat.one_ne_zero, Nat.not_lt_zero]
  | succ k => by
    rw [descFactorial_succ, mul_eq_zero, descFactorial_eq_zero_iff_lt, lt_succ_iff,
      tsub_eq_zero_iff_le, lt_iff_le_and_ne, or_iff_left_iff_imp, and_imp]
    exact fun h _ => h
#align nat.desc_factorial_eq_zero_iff_lt Nat.descFactorial_eq_zero_iff_lt

alias descFactorial_eq_zero_iff_lt ↔ _ descFactorial_of_lt
#align nat.desc_factorial_of_lt Nat.descFactorial_of_lt

theorem add_descFactorial_eq_ascFactorial (n : ℕ) :
    ∀ k : ℕ, (n + k).descFactorial k = n.ascFactorial k
  | 0 => by rw [ascFactorial_zero, descFactorial_zero]
  | succ k => by
    rw [Nat.add_succ, Nat.succ_eq_add_one, Nat.succ_eq_add_one,
        succ_descFactorial_succ, ascFactorial_succ, add_descFactorial_eq_ascFactorial _ k]
#align nat.add_desc_factorial_eq_asc_factorial Nat.add_descFactorial_eq_ascFactorial

/-- `n.descFactorial k = n! / (n - k)!` but without ℕ-division. See `Nat.descFactorial_eq_div`
for the version using ℕ-division. -/
theorem factorial_mul_descFactorial : ∀ {n k : ℕ}, k ≤ n → (n - k)! * n.descFactorial k = n !
  | n, 0 => fun _ => by rw [descFactorial_zero, mul_one, tsub_zero]
  | 0, succ k => fun h => by
    exfalso
    exact not_succ_le_zero k h
  | succ n, succ k => fun h => by
    rw [succ_descFactorial_succ, succ_sub_succ, ← mul_assoc, mul_comm (n - k)!, mul_assoc,
      factorial_mul_descFactorial (Nat.succ_le_succ_iff.1 h), factorial_succ]
#align nat.factorial_mul_desc_factorial Nat.factorial_mul_descFactorial

/-- Avoid in favor of `Nat.factorial_mul_descFactorial` if you can. ℕ-division isn't worth it. -/
theorem descFactorial_eq_div {n k : ℕ} (h : k ≤ n) : n.descFactorial k = n ! / (n - k)! := by
  apply mul_left_cancel₀ (factorial_ne_zero (n - k))
  rw [factorial_mul_descFactorial h]
  exact (Nat.mul_div_cancel' <| factorial_dvd_factorial <| Nat.sub_le n k).symm
#align nat.desc_factorial_eq_div Nat.descFactorial_eq_div

theorem pow_sub_le_descFactorial (n : ℕ) : ∀ k : ℕ, (n + 1 - k) ^ k ≤ n.descFactorial k
  | 0 => by rw [descFactorial_zero, pow_zero]
  | k + 1 => by
    rw [descFactorial_succ, pow_succ, succ_sub_succ, mul_comm]
    apply Nat.mul_le_mul_of_nonneg_left
    exact   (le_trans (Nat.pow_le_pow_of_le_left (tsub_le_tsub_right (le_succ _) _) k)
          (pow_sub_le_descFactorial n k))
#align nat.pow_sub_le_desc_factorial Nat.pow_sub_le_descFactorial

theorem pow_sub_lt_descFactorial' {n : ℕ} :
    ∀ {k : ℕ}, k + 2 ≤ n → (n - (k + 1)) ^ (k + 2) < n.descFactorial (k + 2)
  | 0 => fun h => by
    rw [descFactorial_succ, pow_succ, pow_one, descFactorial_one]
    exact
      Nat.mul_lt_mul_of_pos_left (tsub_lt_self (lt_of_lt_of_le zero_lt_two h) zero_lt_one)
        (tsub_pos_of_lt h)
  | k + 1 => fun h => by
    rw [descFactorial_succ, pow_succ, mul_comm]
    apply Nat.mul_lt_mul_of_pos_left
    · refine' ((Nat.pow_le_pow_of_le_left (tsub_le_tsub_right (le_succ n) _) _).trans_lt _)
      rw [succ_sub_succ]
      exact pow_sub_lt_descFactorial' ((le_succ _).trans h)
    · apply tsub_pos_of_lt; apply h
#align nat.pow_sub_lt_desc_factorial' Nat.pow_sub_lt_descFactorial'

theorem pow_sub_lt_descFactorial {n : ℕ} :
    ∀ {k : ℕ}, 2 ≤ k → k ≤ n → (n + 1 - k) ^ k < n.descFactorial k
  | 0 => by rintro ⟨⟩
  | 1 => by intro; contradiction
  | k + 2 => fun _ h => by
    rw [succ_sub_succ]
    exact pow_sub_lt_descFactorial' h
#align nat.pow_sub_lt_desc_factorial Nat.pow_sub_lt_descFactorial

theorem descFactorial_le_pow (n : ℕ) : ∀ k : ℕ, n.descFactorial k ≤ n ^ k
  | 0 => by rw [descFactorial_zero, pow_zero]
  | k + 1 => by
    rw [descFactorial_succ, pow_succ, mul_comm _ n]
    exact Nat.mul_le_mul (Nat.sub_le _ _) (descFactorial_le_pow _ k)
#align nat.desc_factorial_le_pow Nat.descFactorial_le_pow

theorem descFactorial_lt_pow {n : ℕ} (hn : 1 ≤ n) : ∀ {k : ℕ}, 2 ≤ k → n.descFactorial k < n ^ k
  | 0 => by rintro ⟨⟩
  | 1 => by intro; contradiction
  | k + 2 => fun _ => by
    rw [descFactorial_succ, pow_succ', mul_comm, mul_comm n]
    exact Nat.mul_lt_mul' (descFactorial_le_pow _ _) (tsub_lt_self hn k.zero_lt_succ)
      (pow_pos (Nat.lt_of_succ_le hn) _)
#align nat.desc_factorial_lt_pow Nat.descFactorial_lt_pow

end DescFactorial

end Nat


/-
Fill in the sorry.

section
  variable U : Type
  variables A B : U → Prop

  example : (∃ x, A x) → ∃ x, A x ∨ B x :=
  sorry
end
-/

section
  variable U : Type
  variables A B : U → Prop

  example : (∃ x, A x) → ∃ x, A x ∨ B x :=
  assume h: (∃ x, A x),
  exists.elim(h)(
    assume (x: U) (hAx: A(x)),
    have hAxoBx: A(x) ∨ B(x), from or.inl(hAx),
    show ∃ x, A x ∨ B x, from exists.intro(x)(hAxoBx)
  )
end


import tactic.norm_num data.nat.basic tactic.ring algebra.archimedean .limits data.nat.binomial
section a
open real nat is_absolute_value finset
noncomputable theory
local attribute [instance, priority 0] classical.prop_decidable

lemma pow_inv' {α : Type*} [discrete_field α] (a : α) (n : ℕ) : (a ^ n)⁻¹ = a⁻¹ ^ n :=
by induction n; simp [_root_.pow_succ, *, mul_inv', mul_comm]

lemma pow_incrs_of_gt_one {α : Type*}  [linear_ordered_semiring α] {x : α} {n m : ℕ}
  (h1x : 1 < x) (hnm : n < m) : x ^ n < x ^ m :=
begin
  rw [← nat.sub_add_cancel hnm, _root_.pow_add, _root_.pow_succ, ← mul_assoc],
  refine (lt_mul_iff_one_lt_left (pow_pos (lt_trans (by norm_num) h1x) _)).2 _,
  rw ← one_mul (1 : α),
  refine mul_lt_mul' (one_le_pow_of_one_le (le_of_lt h1x) _) h1x (by norm_num)
    (pow_pos (lt_trans (by norm_num) h1x) _)
end

lemma pow_dcrs_of_lt_one_of_pos {α : Type*} [discrete_linear_ordered_field α] {x : α} {n m : ℕ}
  (hx1 : x < 1) (h0x : 0 < x) (hnm : n < m) : x ^ m < x ^ n :=
begin
  refine (inv_lt_inv _ _).1 _,
  { exact pow_pos h0x _ },
  { exact pow_pos h0x _ },
  { rw [pow_inv', pow_inv'],
    refine pow_incrs_of_gt_one (one_lt_inv h0x hx1) hnm }
end

open finset

lemma geo_series_eq {α : Type*} [field α] {x : α} (n : ℕ) (hx1 : x ≠ 1) :
  (range n).sum (λ m, x ^ m) = (1 - x ^ n) / (1 - x) :=
have 1 - x ≠ 0 := mt sub_eq_zero_iff_eq.1 hx1.symm,
begin
  induction n with n ih,
  { simp },
  { rw [sum_range_succ, ← mul_div_cancel (x ^ n) this, ih, ← add_div, _root_.pow_succ],
    refine congr_fun (congr_arg _ _) _,
    ring }
end

variables {α : Type*} {β : Type*}
  [discrete_linear_ordered_field α] [archimedean α] {abv : β → α}

lemma forall_ge_le_of_forall_le_succ {α : Type*} [preorder α] (f : ℕ → α) {m : ℕ}
  (h : ∀ n ≥ m, f (succ n) ≤ f n) : ∀ l, ∀ k ≥ m, k ≤ l → f l ≤ f k :=
begin
  assume l k hkm hkl,
  generalize hp : l - k = p,
  have : l = k + p := add_comm p k ▸ (nat.sub_eq_iff_eq_add hkl).1 hp,
  subst this,
  clear hkl hp,
  induction p with p ih,
  { simp },
  { exact le_trans (h _ (le_trans hkm (nat.le_add_right _ _))) ih }
end

lemma is_cau_of_dcrs_bounded {α : Type*} [discrete_linear_ordered_field α]
  [archimedean α] (f : ℕ → α)
  {a : α} {m : ℕ} (ham : ∀ n ≥ m, abs (f n) ≤ a)
  (hnm : ∀ n ≥ m, f (succ n) ≤ f n) : is_cau_seq abs f :=
λ ε ε0,
let ⟨k, hk⟩ := archimedean.arch a ε0 in
have h : ∃ l, ∀ n ≥ m, a - add_monoid.smul l ε < f n :=
  ⟨k + k + 1, λ n hnm, lt_of_lt_of_le
    (show a - add_monoid.smul (k + (k + 1)) ε < -abs (f n),
      from lt_neg.1 $ lt_of_le_of_lt (ham n hnm) (begin
        rw [neg_sub, lt_sub_iff_add_lt, add_monoid.add_smul],
        exact add_lt_add_of_le_of_lt hk (lt_of_le_of_lt hk
          (lt_add_of_pos_left _ ε0)),
      end))
    (neg_le.2 $ (abs_neg (f n)) ▸ le_abs_self _)⟩,
let l := nat.find h in
have hl : ∀ (n : ℕ), n ≥ m → f n > a - add_monoid.smul l ε := nat.find_spec h,
have hl0 : l ≠ 0 := λ hl0, not_lt_of_ge (ham m (le_refl _))
  (lt_of_lt_of_le (by have := hl m (le_refl m); simpa [hl0] using this) (le_abs_self (f m))),
begin
  cases classical.not_forall.1
    (nat.find_min h (pred_lt hl0)) with i hi,
  rw [not_imp, not_lt] at hi,
  existsi i,
  assume j hj,
  have hfij : f j ≤ f i := forall_ge_le_of_forall_le_succ f hnm _ _ hi.1 hj,
  rw [abs_of_nonpos (sub_nonpos.2 hfij), neg_sub, sub_lt_iff_lt_add'],
  exact calc f i ≤ a - add_monoid.smul (pred l) ε : hi.2
    ... = a - add_monoid.smul l ε + ε :
      by conv {to_rhs, rw [← succ_pred_eq_of_pos (nat.pos_of_ne_zero hl0), succ_smul',
        sub_add, add_sub_cancel] }
    ... < f j + ε : add_lt_add_right (hl j (le_trans hi.1 hj)) _
end

lemma is_cau_of_incrs_bounded {α : Type*} [discrete_linear_ordered_field α]
  [archimedean α] (f : ℕ → α)
  {a : α} {m : ℕ} (ham : ∀ n ≥ m, abs (f n) ≤ a)
  (hnm : ∀ n ≥ m, f n ≤ f (succ n)) : is_cau_seq abs f :=
begin
  refine @eq.rec_on (ℕ → α) _ (is_cau_seq abs) _ _
    (-⟨_, @is_cau_of_dcrs_bounded _ _ _ (λ n, -f n) a m (by simpa) (by simpa)⟩ : cau_seq α abs).2,
  ext,
  exact neg_neg _
end

lemma series_cau_of_abv_le_cau {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β] {f : ℕ → β}
    {g : ℕ → α} {abv : β → α} [is_absolute_value abv] (n : ℕ) : (∀ m, n ≤ m → abv (f m) ≤ g m) →
    is_cau_seq abs (λ n, (range n).sum g) → is_cau_seq abv (λ n, (range n).sum f) := begin
  assume hm hg ε ε0,cases hg (ε / 2) (div_pos ε0 (by norm_num)) with i hi,
  existsi max n i,
  assume j ji,
  have hi₁ := hi j (le_trans (le_max_right n i) ji),
  have hi₂ := hi (max n i) (le_max_right n i),
  have sub_le := abs_sub_le ((range j).sum g) ((range i).sum g) ((range (max n i)).sum g),
  have := add_lt_add hi₁ hi₂,
  rw abs_sub ((range (max n i)).sum g) at this,
  rw add_halves ε at this,
  refine lt_of_le_of_lt (le_trans (le_trans _ (le_abs_self _)) sub_le) this,
  generalize hk : j - max n i = k,clear this hi₂ hi₁ hi ε0 ε hg sub_le,
  rw nat.sub_eq_iff_eq_add ji at hk,rw hk, clear hk ji j,
  induction k with k' hi,
  { simp [abv_zero abv] },
  { dsimp at *,
    rw [succ_add, sum_range_succ, sum_range_succ, add_assoc, add_assoc],
    refine le_trans (abv_add _ _ _) _,
    exact add_le_add (hm _ (le_add_of_nonneg_of_le (nat.zero_le _) (le_max_left _ _))) hi },
end

lemma pow_abv {α β : Type*} [discrete_linear_ordered_field α] [domain β] (abv : β → α) [is_absolute_value abv]
  (a : β) (n : ℕ) : abv (a ^ n) = abv a ^ n :=
by induction n; simp [abv_mul abv, _root_.pow_succ, abv_one abv, *]

lemma series_cau_of_abv_cau {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β] {abv : β → α} {f : ℕ → β}
    [is_absolute_value abv] : is_cau_seq abs (λ m, (range m).sum (λ n, abv (f n))) → is_cau_seq abv (λ m, (range m).sum f) :=
   λ h, series_cau_of_abv_le_cau 0 (λ n h, le_refl _) h

lemma geo_series_cau {α β : Type*} [discrete_linear_ordered_field α] [archimedean α] [field β]
  {abv : β → α} [is_absolute_value abv]
   (x : β) (hx1 : abv x < 1) : is_cau_seq abv (λ n, (range n).sum (λ m, x ^ m)) :=
have hx1' : abv x ≠ 1 := λ h, by simpa [h, lt_irrefl] using hx1,
series_cau_of_abv_cau
begin
  simp only [pow_abv abv, geo_series_eq _ hx1'] {eta := ff},
  refine @is_cau_of_incrs_bounded _ _ _ _ ((1 : α) / (1 - abv x)) 0 _ _,
  { assume n hn,
    rw abs_of_nonneg ,
    refine div_le_div_of_le_of_pos (sub_le_self _ (pow_abv abv x n ▸ abv_nonneg _ _))
      (sub_pos.2 hx1),
    refine div_nonneg (sub_nonneg.2 _) (sub_pos.2 hx1),
    clear hn,
    induction n with n ih,
    { simp },
    { rw [_root_.pow_succ, ← one_mul (1 : α)],
      refine mul_le_mul (le_of_lt hx1) ih (pow_abv abv x n ▸ abv_nonneg _ _) (by norm_num) } },
  { assume n hn,
    refine div_le_div_of_le_of_pos (sub_le_sub_left _ _) (sub_pos.2 hx1),
    rw [← one_mul (_ ^ n), _root_.pow_succ],
    exact mul_le_mul_of_nonneg_right (le_of_lt hx1) (pow_nonneg (abv_nonneg _ _) _) }
end

lemma geo_series_const_cau {α : Type*} [discrete_linear_ordered_field α] [archimedean α] (a x : α) :
  abs x < 1 → is_cau_seq abs (λ m, (range m).sum (λ n, a * x ^ n)) :=
λ hx1, begin
  have : is_cau_seq abs (λ m, a * (range m).sum (λ n, x ^ n)) := (cau_seq.const abs a * ⟨_, geo_series_cau x hx1⟩).2,
  simpa only [mul_sum] using this
end

-- The form of ratio test with  0 ≤ r < 1, and abv (f (succ m)) ≤ r * abv (f m) handled zero terms of series the best
lemma series_ratio_test {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β]
    [archimedean α] {abv : β → α} [is_absolute_value abv] {f : ℕ → β} (n : ℕ) (r : α) :
    0 ≤ r → r < 1 → (∀ m, n ≤ m → abv (f (succ m)) ≤ r * abv (f m)) → is_cau_seq abv (λ m, (range m).sum f)
  := begin
  assume r0 r1 h,
  refine series_cau_of_abv_le_cau (succ n) _ (geo_series_const_cau (abv (f (succ n)) * r⁻¹ ^ (succ n)) r _),
  assume m mn,
  generalize hk : m - (succ n) = k,rw nat.sub_eq_iff_eq_add mn at hk,
  cases classical.em (r = 0) with r_zero r_pos,have m_pos := lt_of_lt_of_le (succ_pos n) mn,
  have := pred_le_pred mn,simp at this,
  have := h (pred m) this,simp[r_zero,succ_pred_eq_of_pos m_pos] at this,
  refine le_trans this _,refine mul_nonneg _ _,
  refine mul_nonneg (abv_nonneg _ _) (pow_nonneg (inv_nonneg.mpr r0) _),exact pow_nonneg r0 _,
  replace r_pos : 0 < r,cases lt_or_eq_of_le r0 with h h,exact h,exact absurd h.symm r_pos,
  revert m n,
  induction k with k' hi,assume m n h mn hk,
  rw [hk,zero_add,mul_right_comm,←pow_inv',←div_eq_mul_inv,mul_div_cancel],
  exact (ne_of_lt (pow_pos r_pos _)).symm,
  assume m n h mn hk,rw [hk,succ_add],
  have kn : k' + (succ n) ≥ (succ n), rw ←zero_add (succ n),refine add_le_add _ _,exact zero_le _,simp,
  replace hi := hi (k' + (succ n)) n h kn rfl,
  rw [(by simp [_root_.pow_succ'] : r ^ (succ (k' + succ n)) = r ^ (k' + succ n) * r),←mul_assoc],
  replace h := h (k' + succ n) (le_of_succ_le kn),rw mul_comm at h,
  exact le_trans h (mul_le_mul_of_nonneg_right hi r0),
  rwa abs_of_nonneg r0,
end

lemma sum_range_diag_flip {α : Type*} [add_comm_monoid α] (n : ℕ) (f : ℕ → ℕ → α) :
    (range n).sum (λ m, (range (m + 1)).sum (λ k, f k (m - k))) =
    (range n).sum (λ m, (range (n - m)).sum (f m)) :=
have h₁ : ((range n).sigma (range ∘ succ)).sum
    (λ (a : Σ m, ℕ), f (a.2) (a.1 - a.2)) =
    (range n).sum (λ m, (range (m + 1)).sum
    (λ k, f k (m - k))) := sum_sigma,
have h₂ : ((range n).sigma (λ m, range (n - m))).sum (λ a : Σ (m : ℕ), ℕ, f (a.1) (a.2)) =
    (range n).sum (λ m, sum (range (n - m)) (f m)) := sum_sigma,
h₁ ▸ h₂ ▸ sum_bij
(λ a _, ⟨a.2, a.1 - a.2⟩)
(λ a ha, have h₁ : a.1 < n := mem_range.1 (mem_sigma.1 ha).1,
  have h₂ : a.2 < succ a.1 := mem_range.1 (mem_sigma.1 ha).2,
    mem_sigma.2 ⟨mem_range.2 (lt_of_lt_of_le h₂ h₁),
    mem_range.2 ((nat.sub_lt_sub_right_iff (le_of_lt_succ h₂)).2 h₁)⟩)
(λ _ _, rfl)
(λ ⟨a₁, a₂⟩ ⟨b₁, b₂⟩ ha hb h,
  have ha : a₁ < n ∧ a₂ ≤ a₁ :=
      ⟨mem_range.1 (mem_sigma.1 ha).1, le_of_lt_succ (mem_range.1 (mem_sigma.1 ha).2)⟩,
  have hb : b₁ < n ∧ b₂ ≤ b₁ :=
      ⟨mem_range.1 (mem_sigma.1 hb).1, le_of_lt_succ (mem_range.1 (mem_sigma.1 hb).2)⟩,
  have h : a₂ = b₂ ∧ _ := sigma.mk.inj h,
  have h' : a₁ = b₁ - b₂ + a₂ := (nat.sub_eq_iff_eq_add ha.2).1 (eq_of_heq h.2),
  sigma.mk.inj_iff.2
    ⟨nat.sub_add_cancel hb.2 ▸ h'.symm ▸ h.1 ▸ rfl,
    (heq_of_eq h.1)⟩)
(λ ⟨a₁, a₂⟩ ha,
  have ha : a₁ < n ∧ a₂ < n - a₁ :=
      ⟨mem_range.1 (mem_sigma.1 ha).1, (mem_range.1 (mem_sigma.1 ha).2)⟩,
  ⟨⟨a₂ + a₁, a₁⟩, ⟨mem_sigma.2 ⟨mem_range.2 ((nat.lt_sub_right_iff_add_lt
      (le_of_lt ha.1)).1 ha.2),
    mem_range.2 (lt_succ_of_le (le_add_left _ _))⟩,
  sigma.mk.inj_iff.2 ⟨rfl, heq_of_eq (nat.add_sub_cancel _ _).symm⟩⟩⟩)

lemma abv_sum_le_sum_abv {α β γ : Type*} [discrete_linear_ordered_field α] [ring β]
  (abv : β → α) [is_absolute_value abv] (f : γ → β) (s : finset γ) : abv (s.sum f) ≤ s.sum (abv ∘ f) :=
finset.induction_on s (by simp [abv_zero abv]) $ λ a s has ih,
by rw [sum_insert has, sum_insert has];
  exact le_trans (abv_add abv _ _) (add_le_add_left ih _)

lemma sum_nonneg {α β : Type*} [ordered_comm_monoid α] {f : β → α} {s : finset β} :
  (∀ x ∈ s, 0 ≤ f x) → 0 ≤ s.sum f :=
finset.induction_on s (by simp) $ λ a s has ih h, begin
  rw [sum_insert has],
  exact add_nonneg' (h a (by simp)) (ih (λx hx, by simp *)),
end

lemma neg_sum {α β : Type*} [add_comm_group α] {f : β → α} {s : finset β} :
  -s.sum f = s.sum (λ x, -f x) :=
finset.induction_on s (by simp) (by simp {contextual := tt})

@[simp] lemma filter_true {α : Type*} (s : finset α) : s.filter (λ _, true) = s :=
finset.ext.2 $ by simp

lemma sum_range_sub_sum_range {α : Type*} [add_comm_group α] {f : ℕ → α}
  {n m : ℕ} (hnm : n ≤ m) : (range m).sum f - (range n).sum f =
  ((range m).filter (λ k, n ≤ k)).sum f :=
begin
  rw [← sum_sdiff (@filter_subset _ (λ k, n ≤ k) _ (range m)),
    sub_eq_iff_eq_add, ← eq_sub_iff_add_eq, add_sub_cancel'],
  refine finset.sum_congr
    (finset.ext.2 $ λ a, ⟨λ h, by simp at *; finish,
    λ h, have ham : a < m := lt_of_lt_of_le (mem_range.1 h) hnm,
      by simp * at *⟩)
    (λ _ _, rfl),
end

lemma series_cauchy_prod {α β : Type*} [discrete_linear_ordered_field α] [ring β] {a b : ℕ → β}
  {abv : β → α} [is_absolute_value abv] : is_cau_seq abs (λ m, (range m).sum (λ n, abv (a n))) → is_cau_seq abv (λ m, (range m).sum b) →
  ∀ ε : α, 0 < ε → ∃ i : ℕ, ∀ j ≥ i, abv ((range j).sum a * (range j).sum b - (range j).sum (λ n,
  (range (n + 1)).sum (λ m, a m * b (n - m))) ) < ε :=
begin
-- slightly adapted version of theorem 9.4.7 from "The Real Numbers and Real Analysis", Ethan D. Bloch
  assume ha hb ε ε0,
  cases cau_seq.bounded ⟨_, hb⟩ with Q hQ,simp at hQ,
  cases cau_seq.bounded ⟨_, ha⟩ with P hP,simp at hP,
  have P0 : 0 < P,exact lt_of_le_of_lt (abs_nonneg _) (hP 0),
  have Pε0 := div_pos ε0 (mul_pos (show (2 : α) > 0, from by norm_num) P0),
  cases cau_seq.cauchy₂ ⟨_, hb⟩ Pε0 with N hN,simp at hN,
  have Qε0 := div_pos ε0 (mul_pos (show (4 : α) > 0, from by norm_num) (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))),
  cases cau_seq.cauchy₂ ⟨_, ha⟩ Qε0 with M hM,simp at hM,
  existsi 2 * (max N M + 1),
  assume K hK,
  have := sum_range_diag_flip K (λ m n, a m * b n),
  dsimp at this,
  rw this,
  have : (λ (i : ℕ), (range (K - i)).sum (λ (k : ℕ), a i * b k)) = (λ (i : ℕ), a i * (range (K - i)).sum b),
    by simp [finset.mul_sum],
  rw this,clear this,
  have : (range K).sum (λ (i : ℕ), a i * (range (K - i)).sum b) =
    (range K).sum (λ (i : ℕ), a i * ((range (K - i)).sum b - (range K).sum b))
    + (range K).sum (λ i, a i * (range K).sum b),
    {rw ←sum_add_distrib,simp[(mul_add _ _ _).symm]},
  rw this, clear this,
  rw sum_mul, simp,
  rw abv_neg abv,
  refine lt_of_le_of_lt (abv_sum_le_sum_abv _ _ _) _,
  simp [abv_mul abv],
  suffices : (range (max N M + 1)).sum (λ (i : ℕ), abv (a i) * abv ((range (K - i)).sum b - (range K).sum b)) +
    ((range K).sum (λ (i : ℕ), abv (a i) * abv ((range (K - i)).sum b - (range K).sum b)) -(range (max N M + 1)).sum
     (λ (i : ℕ),
    abv (a i) * abv ((range (K - i)).sum b - (range K).sum b))) < ε / (2 * P) * P + ε / (4 * Q) * (2 * Q),
  { simp [(div_div_eq_div_mul _ _ _).symm] at this,
    rwa[div_mul_cancel _ (ne_of_lt P0).symm,(by norm_num : (4 : α) = 2 * 2),←div_div_eq_div_mul,mul_comm (2 : α),←_root_.mul_assoc,
    div_mul_cancel _ (ne_of_lt (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))).symm,div_mul_cancel,add_halves] at this,
    norm_num},
  refine add_lt_add _ _,
  {have : (range (max N M + 1)).sum (λ (i : ℕ), abv (a i) *
    abv ((range (K - i)).sum b - (range K).sum b)) ≤ (range (max N M + 1)).sum
  (λ (i : ℕ), abv (a i) * (ε / (2 * P))) := by
    {refine sum_le_sum _,assume m mJ,refine mul_le_mul_of_nonneg_left _ _,
      {refine le_of_lt (hN (K - m) K _ _),{
      refine nat.le_sub_left_of_add_le (le_trans _ hK),
      rw[succ_mul,_root_.one_mul],
      exact add_le_add (le_of_lt (mem_range.1 mJ)) (le_trans (le_max_left _ _) (le_of_lt (lt_add_one _)))},
      {refine le_trans _ hK,rw ←_root_.one_mul N,
      refine mul_le_mul (by norm_num) (by rw _root_.one_mul;exact le_trans (le_max_left _ _)
      (le_of_lt (lt_add_one _))) (zero_le _) (zero_le _)}},
      exact abv_nonneg abv _},
  refine lt_of_le_of_lt this _,
  rw [← sum_mul, mul_comm],
  specialize hP (max N M + 1),rwa abs_of_nonneg at hP,
  exact (mul_lt_mul_left Pε0).mpr hP,
  exact sum_nonneg (λ x h, abv_nonneg abv _)},
  {have hNMK : max N M + 1 < K := by
    {refine lt_of_lt_of_le _ hK,
    rw [succ_mul,_root_.one_mul,←add_zero (max N M + 1)],
    refine add_lt_add_of_le_of_lt (le_refl _) _,rw add_zero,
    refine add_pos_of_nonneg_of_pos (zero_le _) (by norm_num)},
  rw sum_range_sub_sum_range (le_of_lt hNMK),
  exact calc sum (filter (λ (k : ℕ), max N M + 1 ≤ k) (range K))
      (λ (i : ℕ), abv (a i) * abv (sum (range (K - i)) b - sum (range K) b))
      ≤ sum (filter (λ (k : ℕ), max N M + 1 ≤ k) (range K)) (λ i, abv (a i) * (2 * Q)) :
    sum_le_sum (begin
      assume n hn,
      refine mul_le_mul_of_nonneg_left _ (abv_nonneg _ _),
      rw sub_eq_add_neg,
      refine le_trans (abv_add _ _ _) _,
      rw [two_mul, abv_neg abv],
      refine add_le_add (le_of_lt (hQ _)) (le_of_lt (hQ _)),
    end)
    ... < _ : begin
      rw [← sum_mul, ← sum_range_sub_sum_range (le_of_lt hNMK)],
      refine (mul_lt_mul_right _).2 _,
      rw two_mul,
      refine add_pos (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))
        (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0)),
      refine lt_of_le_of_lt (le_abs_self _) _,
      refine hM _ _ (le_trans (le_succ_of_le (le_max_right _ _)) (le_of_lt hNMK))
        (le_succ_of_le (le_max_right _ _))
     end }
end

end a

open nat finset
lemma complex.exp_series_abs_cau (z : ℂ) : is_cau_seq abs (λ n, (range n).sum (λ m, complex.abs (z ^ m / fact m))) := begin
  cases exists_nat_gt (complex.abs z) with n hn,
  have n_pos : (0 : ℝ) < n := lt_of_le_of_lt (complex.abs_nonneg _) hn,
  refine series_ratio_test n (complex.abs z / n) _ _ _,exact div_nonneg_of_nonneg_of_pos (complex.abs_nonneg _) n_pos,rwa [div_lt_iff n_pos,one_mul],
  assume m mn,rw [abs_of_nonneg (complex.abs_nonneg _),abs_of_nonneg (complex.abs_nonneg _)],
  unfold fact,simp only [_root_.pow_succ, complex.abs_div,complex.abs_mul,div_eq_mul_inv,mul_inv',
    nat.cast_mul,complex.abs_inv],
  have : complex.abs z * complex.abs (z ^ m) * ((complex.abs ↑(fact m))⁻¹ * (complex.abs ↑(succ m))⁻¹) = complex.abs z *
    complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹ * (complex.abs ↑(succ m))⁻¹,ring,rw this,
  have : complex.abs z * (↑n)⁻¹ * (complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹) = complex.abs z * complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹ * (↑n)⁻¹,ring,
  rw this,
  rw[(by simp : (succ m : ℂ) = ((succ m : ℝ) : ℂ)),complex.abs_of_nonneg],
  refine mul_le_mul_of_nonneg_left _ _,
  rw [inv_le_inv,nat.cast_le],exact le_succ_of_le mn,
  rw [←nat.cast_zero,nat.cast_lt],exact succ_pos _,exact n_pos,rw[←complex.abs_inv,←complex.abs_mul,←complex.abs_mul],
  exact complex.abs_nonneg _,rw[←nat.cast_zero,nat.cast_le],exact zero_le _,
end

lemma complex.exp_series_cau (z : ℂ) : is_cau_seq complex.abs (λ n, (range n).sum (λ m, z ^ m / fact m)) :=
  series_cau_of_abv_cau (complex.exp_series_abs_cau z)

def exp' (z : ℂ) : cau_seq ℂ complex.abs := ⟨_, complex.exp_series_cau z⟩

open complex
def exp (z : ℂ) : ℂ := complex.lim (exp' z)

def sin (z : ℂ) : ℂ := (exp (I * z) - exp (-I * z)) / (2 * I)

def cos (z : ℂ) : ℂ := (exp (I * z) + exp (-I * z)) / 2

def tan (z : ℂ) : ℂ := sin z / cos z

def sinh (z : ℂ) : ℂ := (exp z - exp (-z)) / 2

def cosh (z : ℂ) : ℂ := (exp z + exp (-z)) / 2

def tanh (z : ℂ) : ℂ := sinh z / cosh z

@[simp] lemma exp_zero : exp 0 = 1 := begin
  unfold exp exp',
  refine lim_eq_of_equiv_const _,
  assume ε ε0,
  existsi 1,
  assume j hj,
  dsimp [exp'],
  suffices : complex.abs (sum (range j) (λ (m : ℕ), 0 ^ m / ↑(fact m)) + -1) = 0,
    rwa this,
  cases j,
  { exact absurd hj (by norm_num) },
  { induction j,
    { simpa },
    { rw ← j_ih dec_trivial,
      simp only [sum_range_succ, _root_.pow_succ],
      simp } }
end

lemma exp_add (x y : ℂ) : exp (x + y) = exp x * exp y :=
show complex.lim (⟨_, complex.exp_series_cau (x + y)⟩ : cau_seq ℂ abs) =
  complex.lim (show cau_seq ℂ abs, from ⟨_, complex.exp_series_cau x⟩)
  * complex.lim (show cau_seq ℂ abs, from ⟨_, complex.exp_series_cau y⟩),
begin
 have hxa := complex.exp_series_abs_cau x,
 have hx := complex.exp_series_cau x,
 have hy := complex.exp_series_cau y,
 have hxy := complex.exp_series_cau (x + y),
   rw complex.lim_mul_lim,
 have hj : ∀ j : ℕ, (range j).sum (λ (m : ℕ),
    (x + y) ^ m / ↑(fact m)) = (range j).sum
    (λ i, (range (i + 1)).sum (λ k, x ^ k / fact k *
    (y ^ (i - k) / fact (i - k)))),
    { assume j,
      refine finset.sum_congr rfl (λ m hm, _),
      rw [add_pow, div_eq_mul_inv, sum_mul],
      refine finset.sum_congr rfl (λ i hi, _),
      have := choose_mul_fact_mul_fact (le_of_lt_succ $ finset.mem_range.1 hi),
      rw [← this, nat.cast_mul, nat.cast_mul, mul_inv', mul_inv'],
      simp only [mul_left_comm (choose m i : ℂ), mul_assoc, mul_left_comm (choose m i : ℂ)⁻¹,
        mul_comm (choose m i : ℂ)],
      have : (choose m i : ℂ) ≠ 0 := nat.cast_ne_zero.2 (λ h,
        by have := choose_pos (le_of_lt_succ (mem_range.1 hi)); simpa [h, lt_irrefl] using this),
      rw inv_mul_cancel this,
      simp [div_eq_mul_inv, mul_comm, mul_assoc, mul_left_comm] },
  have hf := funext hj, have hxy1 := hxy, rw hf at hxy1,
  have := series_cauchy_prod hxa hy,
  refine eq.symm (lim_eq_lim_of_equiv _),
  assume ε ε0,
  dsimp,
  simp only [hj],
  exact this ε ε0,
end