/-
Copyright (c) 2015 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Robert Y. Lewis
The real numbers, constructed as equivalence classes of Cauchy sequences of rationals.
This construction follows Bishop and Bridges (1985).

To do:
 o Rename things and possibly make theorems private
-/
import data.real.basic data.rat data.nat
open rat nat eq pnat

local postfix `⁻¹` := pnat.inv

local attribute real.real_has_zero [instance] [priority real.prio]
local attribute real.real_has_one [instance] [priority real.prio]

namespace rat_seq
definition pos (s : seq) := ∃ n : ℕ+, n⁻¹ < (s n)

definition nonneg (s : seq) := ∀ n : ℕ+, -(n⁻¹) ≤ s n

theorem sub_sub_comm (a b c : ℚ) : a - b - c = a - c - b :=
  by rewrite [+sub_eq_add_neg, add.assoc, {-b+_}add.comm, -add.assoc]

theorem bdd_away_of_pos {s : seq} (Hs : regular s) (H : pos s) :
        ∃ N : ℕ+, ∀ n : ℕ+, n ≥ N → (s n) ≥ N⁻¹ :=
  begin
    cases H with [n, Hn],
    cases sep_by_inv Hn with [N, HN],
    existsi N,
    intro m Hm,
    have Habs : abs (s m - s n) ≥ s n - s m, by rewrite abs_sub; apply le_abs_self,
    have Habs' : s m + abs (s m - s n) ≥ s n, from (iff.mpr (le_add_iff_sub_left_le _ _ _)) Habs,
    have HN' : N⁻¹ + N⁻¹ ≤ s n - n⁻¹, begin
        rewrite sub_eq_add_neg,
        apply iff.mpr (le_add_iff_sub_right_le _ _ _),
        rewrite [sub_neg_eq_add, add.comm, -add.assoc],
        apply le_of_lt HN
      end,
    rewrite add.comm at Habs',
    have Hin : s m ≥ N⁻¹, from calc
      s m ≥ s n - abs (s m - s n) : (iff.mp (le_add_iff_sub_left_le _ _ _)) Habs'
      ... ≥ s n - (m⁻¹ + n⁻¹) : sub_le_sub_left !Hs
      ... = s n - m⁻¹ - n⁻¹ : by rewrite sub_add_eq_sub_sub
      ... = s n - n⁻¹ - m⁻¹ : by rewrite sub_sub_comm
      ... ≥ s n - n⁻¹ - N⁻¹ : sub_le_sub_left (inv_ge_of_le Hm)
      ... ≥ N⁻¹ + N⁻¹ - N⁻¹ : sub_le_sub_right HN'
      ... = N⁻¹ : by rewrite add_sub_cancel,
    apply Hin
  end

theorem pos_of_bdd_away {s : seq} (H : ∃ N : ℕ+, ∀ n : ℕ+, n ≥ N → (s n) ≥ N⁻¹) : pos s :=
  begin
    cases H with [N, HN],
    existsi (N + pone),
    apply lt_of_lt_of_le,
    apply inv_add_lt_left,
    apply HN,
    apply le_of_lt,
    apply lt_add_left
  end

theorem bdd_within_of_nonneg {s : seq} (Hs : regular s) (H : nonneg s) :
        ∀ n : ℕ+, ∃ N : ℕ+, ∀ m : ℕ+, m ≥ N → s m ≥ -n⁻¹ :=
  begin
    intros,
    existsi n,
    intro m Hm,
    apply le.trans,
    apply neg_le_neg,
    apply inv_ge_of_le,
    apply Hm,
    apply H
  end

theorem nonneg_of_bdd_within {s : seq} (Hs : regular s)
        (H : ∀n : ℕ+, ∃ N : ℕ+, ∀ m : ℕ+, m ≥ N → s m ≥ -n⁻¹) : nonneg s :=
  begin
    rewrite ↑nonneg,
    intro k,
    apply ge_of_forall_ge_sub,
    intro ε Hε,
    cases H (pceil ((2) / ε)) with [N, HN],
    apply le.trans,
    rotate 1,
    apply sub_le_of_abs_sub_le_left,
    apply Hs,
    apply (max (pceil ((2)/ε)) N),
    rewrite [+sub_eq_add_neg, neg_add, {_ + (-k⁻¹ + _)}add.comm, *add.assoc],
    apply rat.add_le_add_left,
    apply le.trans,
    rotate 1,
    apply add_le_add,
    rotate 1,
    apply HN (max (pceil ((2)/ε)) N) !le_max_right,
    rotate_right 1,
    apply neg_le_neg,
    apply inv_ge_of_le,
    apply le_max_left,
    rewrite -neg_add,
    apply neg_le_neg,
    apply le.trans,
    apply add_le_add,
    repeat (apply inv_pceil_div;
    apply add_pos;
    repeat apply zero_lt_one;
    exact Hε),
    rewrite [add_halves],
    apply rat.le_refl
  end

theorem pos_of_pos_equiv {s t : seq} (Hs : regular s) (Heq : s ≡ t) (Hp : pos s) : pos t :=
  begin
    cases (bdd_away_of_pos Hs Hp) with [N, HN],
    existsi 2 * 2 * N,
    apply lt_of_lt_of_le,
    rotate 1,
    apply sub_le_of_abs_sub_le_right,
    apply Heq,
    have Hs4 : N⁻¹ ≤ s (2 * 2 * N), from HN _ (!pnat.mul_le_mul_left),
    apply lt_of_lt_of_le,
    rotate 1,
    rewrite sub_eq_add_neg,
    apply iff.mpr !add_le_add_right_iff,
    apply Hs4,
    rewrite [*pnat.mul_assoc, pnat.add_halves, -(pnat.add_halves N), -sub_eq_add_neg, add_sub_cancel],
    apply inv_two_mul_lt_inv
  end

theorem nonneg_of_nonneg_equiv {s t : seq} (Hs : regular s) (Ht : regular t) (Heq : s ≡ t)
        (Hp : nonneg s) : nonneg t :=
  begin
    apply nonneg_of_bdd_within,
    apply Ht,
    intros,
    cases bdd_within_of_nonneg Hs Hp (2 * 2 * n) with [Ns, HNs],
    existsi max Ns (2 * 2 * n),
    intro m Hm,
    apply le.trans,
    rotate 1,
    apply sub_le_of_abs_sub_le_right,
    apply Heq,
    apply le.trans,
    rotate 1,
    apply sub_le_sub_right,
    apply HNs,
    apply le.trans,
    rotate 1,
    apply Hm,
    rotate_right 1,
    apply le_max_left,
    have Hms : m⁻¹ ≤ (2 * 2 * n)⁻¹, begin
         apply inv_ge_of_le,
         apply le.trans,
         rotate 1,
         apply Hm;
         apply le_max_right
      end,
    have Hms' : m⁻¹ + m⁻¹ ≤ (2 * 2 * n)⁻¹ + (2 * 2 * n)⁻¹, from add_le_add Hms Hms,
    apply le.trans,
    rotate 1,
    apply sub_le_sub_left,
    apply Hms',
    rewrite [*pnat.mul_assoc, pnat.add_halves, -neg_sub, -pnat.add_halves n, sub_neg_eq_add],
    apply neg_le_neg,
    apply add_le_add_left,
    apply inv_two_mul_le_inv
  end

definition s_le (a b : seq) := nonneg (sadd b (sneg a))
definition s_lt (a b : seq) := pos (sadd b (sneg a))

theorem zero_nonneg : nonneg zero :=
  begin
    intros,
    apply neg_nonpos_of_nonneg,
    apply rat.le_of_lt,
    apply pnat.inv_pos
  end

theorem s_zero_lt_one : s_lt zero one :=
  begin
    rewrite [↑s_lt, ↑zero, ↑sadd, ↑sneg, ↑one, neg_zero, add_zero, ↑pos],
    existsi 2,
    apply inv_lt_one_of_gt,
    apply one_lt_two
  end

protected theorem le_refl {s : seq} (Hs : regular s) : s_le s s :=
  begin
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply equiv.symm,
    apply neg_s_cancel s Hs,
    apply zero_nonneg,
    apply zero_is_reg,
    apply reg_add_reg Hs (reg_neg_reg Hs)
  end

theorem s_nonneg_of_pos {s : seq} (Hs : regular s) (H : pos s) : nonneg s :=
  begin
    apply nonneg_of_bdd_within,
    apply Hs,
    intros,
    cases bdd_away_of_pos Hs H with [N, HN],
    existsi N,
    intro m Hm,
    apply le.trans,
    rotate 1,
    apply HN,
    apply Hm,
    apply le.trans,
    rotate 1,
    apply rat.le_of_lt,
    apply pnat.inv_pos,
    rewrite -neg_zero,
    apply neg_le_neg,
    apply rat.le_of_lt,
    apply pnat.inv_pos
  end

theorem s_le_of_s_lt {s t : seq} (Hs : regular s) (Ht : regular t) (H : s_lt s t) : s_le s t :=
  begin
    rewrite [↑s_le, ↑s_lt at *],
    apply s_nonneg_of_pos,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end

theorem s_neg_add_eq_s_add_neg (s t : seq) : sneg (sadd s t) ≡ sadd (sneg s) (sneg t) :=
  begin
    rewrite [↑equiv, ↑sadd, ↑sneg],
    intros,
    rewrite [neg_add, sub_self, abs_zero],
    apply add_invs_nonneg
  end

theorem equiv_cancel_middle {s t u : seq} (Hs : regular s) (Ht : regular t)
        (Hu : regular u) : sadd (sadd u t) (sneg (sadd u s)) ≡ sadd t (sneg s) :=
  begin
    note Hz := zero_is_reg,
    apply equiv.trans,
    rotate 3,
    apply add_well_defined,
    rotate 4,
    apply s_add_comm,
    apply s_neg_add_eq_s_add_neg,
    apply equiv.trans,
    rotate 3,
    apply s_add_assoc,
    rotate 2,
    apply add_well_defined,
    rotate 4,
    apply equiv.refl,
    apply equiv.trans,
    rotate 4,
    apply equiv.refl,
    rotate_right 1,
    apply equiv.trans,
    rotate 3,
    apply equiv.symm,
    apply s_add_assoc,
    rotate 2,
    apply equiv.trans,
    rotate 4,
    apply s_zero_add,
    rotate_right 1,
    apply add_well_defined,
    rotate 4,
    apply neg_s_cancel,
    rotate 1,
    apply equiv.refl,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end

protected theorem add_le_add_of_le_right {s t : seq} (Hs : regular s) (Ht : regular t)
          (Lst : s_le s t) : ∀ u : seq, regular u → s_le (sadd u s) (sadd u t) :=
  begin
    intro u Hu,
    rewrite [↑s_le at *],
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply equiv.symm,
    apply equiv_cancel_middle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end

theorem s_add_lt_add_left {s t : seq} (Hs : regular s) (Ht : regular t) (Hst : s_lt s t) {u : seq}
        (Hu : regular u) : s_lt (sadd u s) (sadd u t) :=
  begin
    rewrite ↑s_lt at *,
    apply pos_of_pos_equiv,
    rotate 1,
    apply equiv.symm,
    apply equiv_cancel_middle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end

protected theorem add_nonneg_of_nonneg {s t : seq} (Hs : nonneg s) (Ht : nonneg t) :
          nonneg (sadd s t) :=
  begin
    intros,
    rewrite [-pnat.add_halves, neg_add],
    apply add_le_add,
    apply Hs,
    apply Ht
  end

protected theorem le_trans {s t u : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
          (Lst : s_le s t) (Ltu : s_le t u) : s_le s u :=
  begin
    rewrite ↑s_le at *,
    note Rz := zero_is_reg,
    have Hsum : nonneg (sadd (sadd u (sneg t)) (sadd t (sneg s))),
                from rat_seq.add_nonneg_of_nonneg Ltu Lst,
    have H' : nonneg (sadd (sadd u (sadd (sneg t) t)) (sneg s)), begin
      apply nonneg_of_nonneg_equiv,
      rotate 2,
      apply add_well_defined,
      rotate 4,
      apply s_add_assoc,
      repeat (apply reg_add_reg | apply reg_neg_reg | assumption),
      apply equiv.refl,
      apply nonneg_of_nonneg_equiv,
      rotate 2,
      apply equiv.symm,
      apply s_add_assoc,
      rotate 2,
      repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
    end,
    have H'' : sadd (sadd u (sadd (sneg t) t)) (sneg s) ≡ sadd u (sneg s), begin
      apply add_well_defined,
      rotate 4,
      apply equiv.trans,
      rotate 3,
      apply add_well_defined,
      rotate 4,
      apply equiv.refl,
      apply s_neg_cancel,
      rotate 1,
      apply s_add_zero,
      rotate 1,
      apply equiv.refl,
      repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
    end,
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply H'',
    apply H',
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end

theorem equiv_of_le_of_ge {s t : seq} (Hs : regular s) (Ht : regular t) (Lst : s_le s t)
        (Lts : s_le t s) : s ≡ t :=
  begin
    apply equiv_of_diff_equiv_zero,
    rotate 2,
    rewrite [↑s_le at *, ↑nonneg at *, ↑equiv, ↑sadd at *, ↑sneg at *],
    intros,
    rewrite [↑zero, sub_zero],
    apply abs_le_of_le_of_neg_le,
    apply le_of_neg_le_neg,
    rewrite [2 neg_add, neg_neg],
    apply rat.le_trans,
    apply neg_add_neg_le_neg_of_pos,
    apply pnat.inv_pos,
    rewrite add.comm,
    apply Lst,
    apply le_of_neg_le_neg,
    rewrite [neg_add, neg_neg],
    apply rat.le_trans,
    apply neg_add_neg_le_neg_of_pos,
    apply pnat.inv_pos,
    apply Lts,
    repeat assumption
  end

definition sep (s t : seq) := s_lt s t ∨ s_lt t s
local infix `≢` : 50 := sep

theorem le_and_sep_of_lt {s t : seq} (Hs : regular s) (Ht : regular t) (Lst : s_lt s t) :
        s_le s t ∧ sep s t :=
  begin
    apply and.intro,
    intros,
    cases Lst with [N, HN],
    let Rns := reg_neg_reg Hs,
    let Rtns := reg_add_reg Ht Rns,
    note Habs := sub_le_of_abs_sub_le_right (Rtns N n),
    rewrite [sub_add_eq_sub_sub at Habs],
    exact (calc
      sadd t (sneg s) n ≥ sadd t (sneg s) N -  N⁻¹ - n⁻¹ : Habs
      ... ≥ 0 - n⁻¹: begin
                       apply sub_le_sub_right,
                       apply rat.le_of_lt,
                       apply (iff.mpr (sub_pos_iff_lt _ _)),
                       apply HN
                     end
      ... = -n⁻¹ : by rewrite zero_sub),
    exact or.inl Lst
  end

theorem lt_of_le_and_sep {s t : seq} (Hs : regular s) (Ht : regular t) (H : s_le s t ∧ sep s t) :
        s_lt s t :=
  begin
    note Le := and.left H,
    cases and.right H with [P, Hlt],
    exact P,
    rewrite [↑s_le at Le, ↑nonneg at Le, ↑s_lt at Hlt, ↑pos at Hlt],
    apply exists.elim Hlt,
    intro N HN,
    let LeN := Le N,
    note HN' := (iff.mpr !neg_lt_neg_iff_lt) HN,
    rewrite [↑sadd at HN', ↑sneg at HN', neg_add at HN', neg_neg at HN', add.comm at HN'],
    let HN'' := not_le_of_gt HN',
    apply absurd LeN HN''
  end

theorem lt_iff_le_and_sep {s t : seq} (Hs : regular s) (Ht : regular t) :
        s_lt s t ↔ s_le s t ∧ sep s t :=
  iff.intro (le_and_sep_of_lt Hs Ht) (lt_of_le_and_sep Hs Ht)

theorem s_neg_zero : sneg zero ≡ zero :=
  begin
    rewrite ↑[sneg, zero, equiv],
    intros,
    rewrite [sub_zero, abs_neg, abs_zero],
    apply add_invs_nonneg
  end

theorem s_sub_zero {s : seq} (Hs : regular s) : sadd s (sneg zero) ≡ s :=
  begin
    apply equiv.trans,
    rotate 3,
    apply add_well_defined,
    rotate 4,
    apply equiv.refl,
    apply s_neg_zero,
    apply s_add_zero,
    repeat (assumption | apply reg_add_reg | apply reg_neg_reg | apply zero_is_reg)
  end

theorem s_pos_of_gt_zero {s : seq} (Hs : regular s) (Hgz : s_lt zero s) : pos s :=
  begin
    rewrite [↑s_lt at *],
    apply pos_of_pos_equiv,
    rotate 1,
    apply s_sub_zero,
    repeat (assumption | apply reg_add_reg | apply reg_neg_reg),
    apply zero_is_reg
  end

theorem s_gt_zero_of_pos {s : seq} (Hs : regular s) (Hp : pos s) : s_lt zero s :=
  begin
    rewrite ↑s_lt,
    apply pos_of_pos_equiv,
    rotate 1,
    apply equiv.symm,
    apply s_sub_zero,
    repeat assumption
  end

theorem s_nonneg_of_ge_zero {s : seq} (Hs : regular s) (Hgz : s_le zero s) : nonneg s :=
  begin
    rewrite ↑s_le at *,
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply s_sub_zero,
    repeat (assumption | apply reg_add_reg | apply reg_neg_reg | apply zero_is_reg)
  end

theorem s_ge_zero_of_nonneg {s : seq} (Hs : regular s) (Hn : nonneg s) : s_le zero s :=
  begin
    rewrite ↑s_le,
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply equiv.symm,
    apply s_sub_zero,
    repeat (assumption | apply reg_add_reg | apply reg_neg_reg | apply zero_is_reg)
  end

theorem s_mul_pos_of_pos {s t : seq} (Hs : regular s) (Ht : regular t) (Hps : pos s)
        (Hpt : pos t) : pos (smul s t) :=
  begin
    rewrite [↑pos at *],
    cases bdd_away_of_pos Hs Hps with [Ns, HNs],
    cases bdd_away_of_pos Ht Hpt with [Nt, HNt],
    existsi 2 * max Ns Nt * max Ns Nt,
    rewrite ↑smul,
    apply lt_of_lt_of_le,
    rotate 1,
    apply mul_le_mul,
    apply HNs,
    apply le.trans,
    apply le_max_left Ns Nt,
    rewrite -pnat.mul_assoc,
    apply pnat.mul_le_mul_left,
    apply HNt,
    apply le.trans,
    apply le_max_right Ns Nt,
    rewrite -pnat.mul_assoc,
    apply pnat.mul_le_mul_left,
    apply rat.le_of_lt,
    apply pnat.inv_pos,
    apply rat.le_trans,
    rotate 1,
    apply HNs,
    apply le.trans,
    apply le_max_left Ns Nt,
    rewrite -pnat.mul_assoc,
    apply pnat.mul_le_mul_left,
    rewrite pnat.inv_mul_eq_mul_inv,
    apply mul_lt_mul,
    rewrite [pnat.inv_mul_eq_mul_inv, -one_mul Ns⁻¹],
    apply mul_lt_mul,
    apply inv_lt_one_of_gt,
    apply dec_trivial,
    apply inv_ge_of_le,
    apply le_max_left,
    apply pnat.inv_pos,
    apply rat.le_of_lt zero_lt_one,
    apply inv_ge_of_le,
    apply le_max_right,
    apply pnat.inv_pos,
    repeat (apply le_of_lt; apply pnat.inv_pos)
  end

theorem s_mul_gt_zero_of_gt_zero {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hzs : s_lt zero s) (Hzt : s_lt zero t) : s_lt zero (smul s t) :=
  s_gt_zero_of_pos
    (reg_mul_reg Hs Ht)
    (s_mul_pos_of_pos Hs Ht (s_pos_of_gt_zero Hs Hzs) (s_pos_of_gt_zero Ht Hzt))

theorem le_of_lt_or_equiv {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hor : (s_lt s t) ∨ (s ≡ t)) : s_le s t :=
  or.elim Hor
    (begin
      intro Hlt,
      apply s_le_of_s_lt Hs Ht Hlt
    end)
    (begin
      intro Heq,
      rewrite ↑s_le,
      apply nonneg_of_nonneg_equiv,
      rotate 3,
      apply zero_nonneg,
      apply zero_is_reg,
      apply reg_add_reg Ht (reg_neg_reg Hs),
      apply equiv.symm,
      apply diff_equiv_zero_of_equiv,
      rotate 2,
      apply equiv.symm,
      apply Heq,
      repeat assumption
    end)

theorem s_zero_mul {s : seq} : smul s zero ≡ zero :=
  begin
    rewrite [↑equiv, ↑smul, ↑zero],
    intros,
    rewrite [mul_zero, sub_zero, abs_zero],
    apply add_invs_nonneg
  end

theorem s_mul_nonneg_of_pos_of_zero {s t : seq} (Hs : regular s) (Ht : regular t)
       (Hps : pos s) (Hpt : zero ≡ t) : nonneg (smul s t) :=
  begin
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply mul_well_defined,
    rotate 4,
    apply equiv.refl,
    apply Hpt,
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply equiv.symm,
    apply s_zero_mul,
    apply zero_nonneg,
    repeat (assumption | apply reg_mul_reg | apply zero_is_reg)
  end

theorem s_mul_nonneg_of_nonneg {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hps : nonneg s) (Hpt : nonneg t) : nonneg (smul s t) :=
  begin
    intro n,
    rewrite ↑smul,
    apply rat.le_by_cases 0 (s (((K₂ s t) * 2) * n)),
    intro Hsp,
    apply rat.le_by_cases 0 (t (((K₂ s t) * 2) * n)),
    intro Htp,
    apply rat.le_trans,
    rotate 1,
    apply rat.mul_nonneg Hsp Htp,
    rotate_right 1,
    apply le_of_lt,
    apply neg_neg_of_pos,
    apply pnat.inv_pos,
      intro Htn,
      apply rat.le_trans,
      rotate 1,
      apply mul_le_mul_of_nonpos_right,
      apply rat.le_trans,
      apply le_abs_self,
      apply canon_2_bound_left s t Hs,
      apply Htn,
      rotate_right 1,
      apply rat.le_trans,
      rotate 1,
      apply mul_le_mul_of_nonneg_left,
      apply Hpt,
      apply le_of_lt,
      apply rat_of_pnat_is_pos,
      rotate 1,
      rewrite -neg_mul_eq_mul_neg,
      apply neg_le_neg,
      rewrite [*pnat.mul_assoc, pnat.inv_mul_eq_mul_inv, -mul.assoc, pnat.inv_cancel_left, one_mul],
      apply inv_ge_of_le,
      apply pnat.mul_le_mul_left,
     intro Hsn,
     apply rat.le_by_cases 0 (t (((K₂ s t) * 2) * n)),
     intro Htp,
     apply rat.le_trans,
     rotate 1,
     apply mul_le_mul_of_nonpos_left,
     apply rat.le_trans,
     apply le_abs_self,
     apply canon_2_bound_right s t Ht,
     apply Hsn,
     rotate_right 1,
     apply rat.le_trans,
     rotate 1,
     apply mul_le_mul_of_nonneg_right,
     apply Hps,
     apply le_of_lt,
     apply rat_of_pnat_is_pos,
     rotate 1,
     rewrite -neg_mul_eq_neg_mul,
     apply neg_le_neg,
     rewrite [+pnat.mul_assoc, pnat.inv_mul_eq_mul_inv, mul.comm, -mul.assoc, pnat.inv_cancel_left,
             one_mul],
     apply inv_ge_of_le,
     apply pnat.mul_le_mul_left,
    intro Htn,
    apply le.trans,
    rotate 1,
    apply mul_nonneg_of_nonpos_of_nonpos,
    apply Hsn,
    apply Htn,
    apply le_of_lt,
    apply neg_neg_of_pos,
    apply pnat.inv_pos
  end

theorem s_mul_ge_zero_of_ge_zero {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hzs : s_le zero s) (Hzt : s_le zero t) : s_le zero (smul s t) :=
  begin
    note Hzs' := s_nonneg_of_ge_zero Hs Hzs,
    note Htz' := s_nonneg_of_ge_zero Ht Hzt,
    apply s_ge_zero_of_nonneg,
    rotate 1,
    apply s_mul_nonneg_of_nonneg,
    repeat assumption,
    apply reg_mul_reg Hs Ht
  end

protected theorem not_lt_self (s : seq) : ¬ s_lt s s :=
  begin
    intro Hlt,
    rewrite [↑s_lt at Hlt, ↑pos at Hlt],
    apply exists.elim Hlt,
    intro n Hn, esimp at Hn,
    rewrite [↑sadd at Hn,↑sneg at Hn, -sub_eq_add_neg at Hn, sub_self at Hn],
    apply absurd Hn (not_lt_of_ge (rat.le_of_lt !pnat.inv_pos))
  end

theorem not_sep_self (s : seq) : ¬ s ≢ s :=
  begin
    intro Hsep,
    rewrite ↑sep at Hsep,
    let Hsep' := (iff.mp !or_self) Hsep,
    apply absurd Hsep' (!rat_seq.not_lt_self)
  end

theorem le_well_defined {s t u v : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
        (Hv : regular v) (Hsu : s ≡ u) (Htv : t ≡ v) :  s_le s t ↔ s_le u v :=
  iff.intro
  (begin
    intro Hle,
    rewrite [↑s_le at *],
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply add_well_defined,
    rotate 4,
    apply Htv,
    apply neg_well_defined,
    apply Hsu,
    apply Hle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end)
  (begin
    intro Hle,
    rewrite [↑s_le at *],
    apply nonneg_of_nonneg_equiv,
    rotate 2,
    apply add_well_defined,
    rotate 4,
    apply equiv.symm, apply Htv,
    apply neg_well_defined,
    apply equiv.symm, apply Hsu,
    apply Hle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end)

theorem lt_well_defined {s t u v : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
        (Hv : regular v) (Hsu : s ≡ u) (Htv : t ≡ v) : s_lt s t ↔ s_lt u v :=
  iff.intro
  (begin
    intro Hle,
    rewrite [↑s_lt at *],
    apply pos_of_pos_equiv,
    rotate 1,
    apply add_well_defined,
    rotate 4,
    apply Htv,
    apply neg_well_defined,
    apply Hsu,
    apply Hle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end)
  (begin
    intro Hle,
    rewrite [↑s_lt at *],
    apply pos_of_pos_equiv,
    rotate 1,
    apply add_well_defined,
    rotate 4,
    apply equiv.symm, apply Htv,
    apply neg_well_defined,
    apply equiv.symm, apply Hsu,
    apply Hle,
    repeat (apply reg_add_reg | apply reg_neg_reg | assumption)
  end)

theorem sep_well_defined {s t u v : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
        (Hv : regular v) (Hsu : s ≡ u) (Htv : t ≡ v) : s ≢ t ↔ u ≢ v :=
  begin
    rewrite ↑sep,
    apply iff.intro,
    intro Hor,
    apply or.elim Hor,
    intro Hlt,
    apply or.inl,
    apply iff.mp (lt_well_defined Hs Ht Hu Hv Hsu Htv),
    assumption,
    intro Hlt,
    apply or.inr,
    apply iff.mp (lt_well_defined Ht Hs Hv Hu Htv Hsu),
    assumption,
    intro Hor,
    apply or.elim Hor,
    intro Hlt,
    apply or.inl,
    apply iff.mpr (lt_well_defined Hs Ht Hu Hv Hsu Htv),
    assumption,
    intro Hlt,
    apply or.inr,
    apply iff.mpr (lt_well_defined Ht Hs Hv Hu Htv Hsu),
    assumption
  end

theorem s_lt_of_lt_of_le {s t u : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
        (Hst : s_lt s t) (Htu : s_le t u) : s_lt s u :=
  begin
    let Rtns := reg_add_reg Ht (reg_neg_reg Hs),
    let Runt := reg_add_reg Hu (reg_neg_reg Ht),
    have Hcan : ∀ m, sadd u (sneg s) m = (sadd t (sneg s)) m + (sadd u (sneg t)) m, begin
      intro m,
      rewrite [↑sadd, ↑sneg, -*sub_eq_add_neg, -sub_eq_sub_add_sub]
    end,
    rewrite [↑s_lt at *, ↑s_le at *],
    cases bdd_away_of_pos Rtns Hst with [Nt, HNt],
    cases bdd_within_of_nonneg Runt Htu (2 * Nt) with [Nu, HNu],
    apply pos_of_bdd_away,
    existsi max (2 * Nt) Nu,
    intro n Hn,
    rewrite Hcan,
    apply rat.le_trans,
    rotate 1,
    apply add_le_add,
    apply HNt,
    apply le.trans,
    apply pnat.mul_le_mul_left 2,
    apply le.trans,
    rotate 1,
    apply Hn,
    rotate_right 1,
    apply le_max_left,
    apply HNu,
    apply le.trans,
    rotate 1,
    apply Hn,
    rotate_right 1,
    apply le_max_right,
    rewrite [-pnat.add_halves Nt, -sub_eq_add_neg, add_sub_cancel],
    apply inv_ge_of_le,
    apply le_max_left
  end

theorem s_lt_of_le_of_lt {s t u : seq} (Hs : regular s) (Ht : regular t) (Hu : regular u)
        (Hst : s_le s t) (Htu : s_lt t u) : s_lt s u :=
  begin
    let Rtns := reg_add_reg Ht (reg_neg_reg Hs),
    let Runt := reg_add_reg Hu (reg_neg_reg Ht),
    have Hcan : ∀ m, sadd u (sneg s) m = (sadd t (sneg s)) m + (sadd u (sneg t)) m, begin
      intro m,
      rewrite [↑sadd, ↑sneg, -*sub_eq_add_neg, -sub_eq_sub_add_sub]
    end,
    rewrite [↑s_lt at *, ↑s_le at *],
    cases bdd_away_of_pos Runt Htu with [Nu, HNu],
    cases bdd_within_of_nonneg Rtns Hst (2 * Nu) with [Nt, HNt],
    apply pos_of_bdd_away,
    existsi max (2 * Nu) Nt,
    intro n Hn,
    rewrite Hcan,
    apply rat.le_trans,
    rotate 1,
    apply add_le_add,
    apply HNt,
    apply le.trans,
    rotate 1,
    apply Hn,
    rotate_right 1,
    apply le_max_right,
    apply HNu,
    apply le.trans,
    apply pnat.mul_le_mul_left 2,
    apply le.trans,
    rotate 1,
    apply Hn,
    rotate_right 1,
    apply le_max_left,
    rewrite [-pnat.add_halves Nu, neg_add_cancel_left],
    apply inv_ge_of_le,
    apply le_max_left
  end

theorem le_of_le_reprs {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hle : ∀ n : ℕ+, s_le s (const (t n))) : s_le s t :=
  by intro m; apply Hle (2 * m) m

theorem le_of_reprs_le {s t : seq} (Hs : regular s) (Ht : regular t)
        (Hle : ∀ n : ℕ+, s_le (const (t n)) s) : s_le t s :=
  by intro m; apply Hle (2 * m) m

-----------------------------
-- of_rat theorems

theorem const_le_const_of_le {a b : ℚ} (H : a ≤ b) : s_le (const a) (const b) :=
  begin
    rewrite [↑s_le, ↑nonneg],
    intro n,
    rewrite [↑sadd, ↑sneg, ↑const],
    apply le.trans,
    apply neg_nonpos_of_nonneg,
    apply rat.le_of_lt,
    apply pnat.inv_pos,
    apply iff.mpr !sub_nonneg_iff_le,
    apply H
  end

theorem le_of_const_le_const {a b : ℚ} (H : s_le (const a) (const b)) : a ≤ b :=
  begin
    rewrite [↑s_le at H, ↑nonneg at H, ↑sadd at H, ↑sneg at H, ↑const at H],
    apply iff.mp !sub_nonneg_iff_le,
    apply nonneg_of_ge_neg_invs _ H
  end

theorem nat_inv_lt_rat {a : ℚ} (H : a > 0) : ∃ n : ℕ+, n⁻¹ < a :=
  begin
    existsi (pceil (1 / (a / (2)))),
    apply lt_of_le_of_lt,
    rotate 1,
    apply div_two_lt_of_pos H,
    rewrite -(one_div_one_div (a / (1 + 1))),
    apply pceil_helper,
    apply le.refl,
    apply one_div_pos_of_pos,
    apply div_pos_of_pos_of_pos H dec_trivial
  end

theorem const_lt_const_of_lt {a b : ℚ} (H : a < b) : s_lt (const a) (const b) :=
  begin
    rewrite [↑s_lt, ↑pos, ↑sadd, ↑sneg, ↑const],
    apply nat_inv_lt_rat,
    apply (iff.mpr !sub_pos_iff_lt H)
  end

theorem lt_of_const_lt_const {a b : ℚ} (H : s_lt (const a) (const b)) : a < b :=
  begin
    rewrite [↑s_lt at H, ↑pos at H, ↑const at H, ↑sadd at H, ↑sneg at H],
    cases H with [n, Hn],
    apply (iff.mp !sub_pos_iff_lt),
    apply lt.trans,
    rotate 1,
    exact Hn,
    apply pnat.inv_pos
  end

theorem s_le_of_le_pointwise {s t : seq} (Hs : regular s) (Ht : regular t)
        (H : ∀ n : ℕ+, s n ≤ t n) : s_le s t :=
  begin
    rewrite [↑s_le, ↑nonneg, ↑sadd, ↑sneg],
    intros,
    apply le.trans,
    apply iff.mpr !neg_nonpos_iff_nonneg,
    apply le_of_lt,
    apply pnat.inv_pos,
    apply iff.mpr !sub_nonneg_iff_le,
    apply H
  end

-------- lift to reg_seqs
definition r_lt (s t : reg_seq) := s_lt (reg_seq.sq s) (reg_seq.sq t)
definition r_le (s t : reg_seq) := s_le (reg_seq.sq s) (reg_seq.sq t)
definition r_sep (s t : reg_seq) := sep (reg_seq.sq s) (reg_seq.sq t)

theorem r_le_well_defined (s t u v : reg_seq) (Hsu : requiv s u) (Htv : requiv t v)
        : r_le s t = r_le u v :=
  propext (le_well_defined (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u)
                  (reg_seq.is_reg v) Hsu Htv)

theorem r_lt_well_defined (s t u v : reg_seq) (Hsu : requiv s u) (Htv : requiv t v)
        : r_lt s t = r_lt u v :=
  propext (lt_well_defined (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u)
                  (reg_seq.is_reg v) Hsu Htv)

theorem r_sep_well_defined (s t u v : reg_seq) (Hsu : requiv s u) (Htv : requiv t v)
        : r_sep s t = r_sep u v :=
  propext (sep_well_defined (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u)
                  (reg_seq.is_reg v) Hsu Htv)

theorem r_le.refl (s : reg_seq) : r_le s s := rat_seq.le_refl (reg_seq.is_reg s)

theorem r_le.trans {s t u : reg_seq} (Hst : r_le s t) (Htu : r_le t u) : r_le s u :=
  rat_seq.le_trans (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u) Hst Htu

theorem r_equiv_of_le_of_ge {s t : reg_seq} (Hs : r_le s t) (Hu : r_le t s) :
        requiv s t :=
  equiv_of_le_of_ge (reg_seq.is_reg s) (reg_seq.is_reg t) Hs Hu

theorem r_lt_iff_le_and_sep (s t : reg_seq) : r_lt s t ↔ r_le s t ∧ r_sep s t :=
  lt_iff_le_and_sep (reg_seq.is_reg s) (reg_seq.is_reg t)

theorem r_add_le_add_of_le_right {s t : reg_seq} (H : r_le s t) (u : reg_seq) :
        r_le (u + s) (u + t) :=
  rat_seq.add_le_add_of_le_right (reg_seq.is_reg s) (reg_seq.is_reg t) H
                                        (reg_seq.sq u) (reg_seq.is_reg u)

theorem r_add_le_add_of_le_right_var (s t u : reg_seq) (H : r_le s t) :
        r_le (u + s) (u + t) := r_add_le_add_of_le_right H u

theorem r_mul_pos_of_pos {s t : reg_seq} (Hs : r_lt r_zero s) (Ht : r_lt r_zero t) :
        r_lt r_zero (s * t) :=
  s_mul_gt_zero_of_gt_zero (reg_seq.is_reg s) (reg_seq.is_reg t) Hs Ht

theorem r_mul_nonneg_of_nonneg {s t : reg_seq} (Hs : r_le r_zero s) (Ht : r_le r_zero t) :
        r_le r_zero (s * t) :=
  s_mul_ge_zero_of_ge_zero (reg_seq.is_reg s) (reg_seq.is_reg t) Hs Ht

theorem r_not_lt_self (s : reg_seq) : ¬ r_lt s s :=
  rat_seq.not_lt_self (reg_seq.sq s)

theorem r_not_sep_self (s : reg_seq) : ¬ r_sep s s :=
  not_sep_self (reg_seq.sq s)

theorem r_le_of_lt {s t : reg_seq} (H : r_lt s t) : r_le s t :=
  s_le_of_s_lt (reg_seq.is_reg s) (reg_seq.is_reg t) H

theorem r_lt_of_le_of_lt {s t u : reg_seq} (Hst : r_le s t) (Htu : r_lt t u) : r_lt s u :=
  s_lt_of_le_of_lt (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u) Hst Htu

theorem r_lt_of_lt_of_le {s t u : reg_seq} (Hst : r_lt s t) (Htu : r_le t u) : r_lt s u :=
  s_lt_of_lt_of_le (reg_seq.is_reg s) (reg_seq.is_reg t) (reg_seq.is_reg u) Hst Htu

theorem r_add_lt_add_left (s t : reg_seq) (H : r_lt s t) (u : reg_seq) : r_lt (u + s) (u + t) :=
  s_add_lt_add_left (reg_seq.is_reg s) (reg_seq.is_reg t) H (reg_seq.is_reg u)

theorem r_add_lt_add_left_var (s t u : reg_seq) (H : r_lt s t) : r_lt (u + s) (u + t) :=
  r_add_lt_add_left s t H u

theorem r_zero_lt_one : r_lt r_zero r_one := s_zero_lt_one

theorem r_le_of_lt_or_eq (s t : reg_seq) (H : r_lt s t ∨ requiv s t) : r_le s t :=
  le_of_lt_or_equiv (reg_seq.is_reg s) (reg_seq.is_reg t) H

theorem r_const_le_const_of_le {a b : ℚ} (H : a ≤ b) : r_le (r_const a) (r_const b) :=
  const_le_const_of_le H

theorem r_le_of_const_le_const {a b : ℚ} (H : r_le (r_const a) (r_const b)) : a ≤ b :=
  le_of_const_le_const H

theorem r_const_lt_const_of_lt {a b : ℚ} (H : a < b) : r_lt (r_const a) (r_const b) :=
  const_lt_const_of_lt H

theorem r_lt_of_const_lt_const {a b : ℚ} (H : r_lt (r_const a) (r_const b)) : a < b :=
  lt_of_const_lt_const H

theorem r_le_of_le_reprs (s t : reg_seq) (Hle : ∀ n : ℕ+, r_le s (r_const (reg_seq.sq t n))) : r_le s t :=
  le_of_le_reprs (reg_seq.is_reg s) (reg_seq.is_reg t) Hle

theorem r_le_of_reprs_le (s t : reg_seq) (Hle : ∀ n : ℕ+, r_le (r_const (reg_seq.sq t n)) s) :
        r_le t s :=
  le_of_reprs_le (reg_seq.is_reg s) (reg_seq.is_reg t) Hle

end rat_seq

open real
open [class] rat_seq
namespace real

protected definition lt (x y : ℝ) :=
  quot.lift_on₂ x y (λ a b, rat_seq.r_lt a b) rat_seq.r_lt_well_defined
protected definition le (x y : ℝ) :=
  quot.lift_on₂ x y (λ a b, rat_seq.r_le a b) rat_seq.r_le_well_defined

definition real_has_lt [instance] [priority real.prio] : has_lt ℝ :=
has_lt.mk real.lt

definition real_has_le [instance] [priority real.prio] : has_le ℝ :=
has_le.mk real.le

definition sep (x y : ℝ) := quot.lift_on₂ x y (λ a b, rat_seq.r_sep a b) rat_seq.r_sep_well_defined
infix `≢` : 50 := sep

protected theorem le_refl (x : ℝ) : x ≤ x :=
  quot.induction_on x (λ t, rat_seq.r_le.refl t)

protected theorem le_trans {x y z : ℝ} : x ≤ y → y ≤ z → x ≤ z :=
  quot.induction_on₃ x y z (λ s t u, rat_seq.r_le.trans)

protected theorem eq_of_le_of_ge {x y : ℝ} : x ≤ y → y ≤ x → x = y :=
  quot.induction_on₂ x y (λ s t Hst Hts, quot.sound (rat_seq.r_equiv_of_le_of_ge Hst Hts))

theorem lt_iff_le_and_sep (x y : ℝ) : x < y ↔ x ≤ y ∧ x ≢ y :=
  quot.induction_on₂ x y (λ s t, rat_seq.r_lt_iff_le_and_sep s t)

protected theorem add_le_add_left' (x y z : ℝ) : x ≤ y → z + x ≤ z + y :=
  quot.induction_on₃ x y z (λ s t u, rat_seq.r_add_le_add_of_le_right_var s t u)

protected theorem add_le_add_left (x y : ℝ) : x ≤ y → ∀ z : ℝ, z + x ≤ z + y :=
  take H z, real.add_le_add_left' x y z H

protected theorem mul_pos (x y : ℝ) : 0 < x → 0 < y → 0 < x * y :=
  quot.induction_on₂ x y (λ s t, rat_seq.r_mul_pos_of_pos)

protected theorem mul_nonneg (x y : ℝ) : 0 ≤ x → 0 ≤ y → 0 ≤ x * y :=
  quot.induction_on₂ x y (λ s t, rat_seq.r_mul_nonneg_of_nonneg)

theorem not_sep_self (x : ℝ) : ¬ x ≢ x :=
  quot.induction_on x (λ s, rat_seq.r_not_sep_self s)

protected theorem lt_irrefl (x : ℝ) : ¬ x < x :=
  quot.induction_on x (λ s, rat_seq.r_not_lt_self s)

protected theorem le_of_lt {x y : ℝ} : x < y → x ≤ y :=
  quot.induction_on₂ x y (λ s t H', rat_seq.r_le_of_lt H')

protected theorem lt_of_le_of_lt {x y z : ℝ} : x ≤ y → y < z → x < z :=
  quot.induction_on₃ x y z (λ s t u H H', rat_seq.r_lt_of_le_of_lt H H')

protected theorem lt_of_lt_of_le {x y z : ℝ} : x < y → y ≤ z → x < z :=
  quot.induction_on₃ x y z (λ s t u H H', rat_seq.r_lt_of_lt_of_le H H')

protected theorem add_lt_add_left' (x y z : ℝ) : x < y → z + x < z + y :=
  quot.induction_on₃ x y z (λ s t u, rat_seq.r_add_lt_add_left_var s t u)

protected theorem add_lt_add_left (x y : ℝ) : x < y → ∀ z : ℝ, z + x < z + y :=
  take H z, real.add_lt_add_left' x y z H

protected theorem zero_lt_one : (0 : ℝ) < (1 : ℝ) := rat_seq.r_zero_lt_one

protected theorem le_of_lt_or_eq (x y : ℝ) : x < y ∨ x = y → x ≤ y :=
    (quot.induction_on₂ x y (λ s t H, or.elim H (take H', begin
        apply rat_seq.r_le_of_lt_or_eq,
        apply or.inl H'
      end)
      (take H', begin
        apply rat_seq.r_le_of_lt_or_eq,
        apply (or.inr (quot.exact H'))
      end)))

definition ordered_ring [trans_instance] : ordered_ring ℝ :=
⦃ ordered_ring, real.comm_ring,
  le_refl := real.le_refl,
  le_trans := @real.le_trans,
  mul_pos := real.mul_pos,
  mul_nonneg := real.mul_nonneg,
  zero_ne_one := real.zero_ne_one,
  add_le_add_left := real.add_le_add_left,
  le_antisymm := @real.eq_of_le_of_ge,
  lt_irrefl := real.lt_irrefl,
  lt_of_le_of_lt := @real.lt_of_le_of_lt,
  lt_of_lt_of_le := @real.lt_of_lt_of_le,
  le_of_lt := @real.le_of_lt,
  add_lt_add_left := real.add_lt_add_left
⦄

open int
theorem of_rat_sub (a b : ℚ) : of_rat (a - b) = of_rat a - of_rat b := rfl

theorem of_int_sub (a b : ℤ) : of_int (a - b) = of_int a - of_int b :=
  by rewrite [of_int_eq, rat.of_int_sub, of_rat_sub]

theorem of_rat_le_of_rat_of_le {a b : ℚ} : a ≤ b → of_rat a ≤ of_rat b :=
  rat_seq.r_const_le_const_of_le

theorem le_of_of_rat_le_of_rat {a b : ℚ} : of_rat a ≤ of_rat b → a ≤ b :=
  rat_seq.r_le_of_const_le_const

theorem of_rat_le_of_rat_iff (a b : ℚ) : of_rat a ≤ of_rat b ↔ a ≤ b :=
  iff.intro le_of_of_rat_le_of_rat of_rat_le_of_rat_of_le

theorem of_rat_lt_of_rat_of_lt {a b : ℚ} : a < b → of_rat a < of_rat b :=
  rat_seq.r_const_lt_const_of_lt

theorem lt_of_of_rat_lt_of_rat {a b : ℚ} : of_rat a < of_rat b → a < b :=
  rat_seq.r_lt_of_const_lt_const

theorem of_rat_lt_of_rat_iff (a b : ℚ) : of_rat a < of_rat b ↔ a < b :=
  iff.intro lt_of_of_rat_lt_of_rat of_rat_lt_of_rat_of_lt

theorem of_int_le_of_int_iff (a b : ℤ) : of_int a ≤ of_int b ↔ (a ≤ b) :=
  begin rewrite [+of_int_eq, of_rat_le_of_rat_iff], apply rat.of_int_le_of_int_iff end

theorem of_int_le_of_int_of_le {a b : ℤ} : (a ≤ b) → of_int a ≤ of_int b :=
  iff.mpr !of_int_le_of_int_iff

theorem le_of_of_int_le_of_int {a b : ℤ} : of_int a ≤ of_int b → (a ≤ b) :=
  iff.mp !of_int_le_of_int_iff

theorem of_int_lt_of_int_iff (a b : ℤ) : of_int a < of_int b ↔ (a < b) :=
  by rewrite [*of_int_eq, of_rat_lt_of_rat_iff]; apply rat.of_int_lt_of_int_iff

theorem of_int_lt_of_int_of_lt {a b : ℤ} : (a < b) → of_int a < of_int b :=
  iff.mpr !of_int_lt_of_int_iff

theorem lt_of_of_int_lt_of_int {a b : ℤ} : of_int a < of_int b → (a < b) :=
  iff.mp !of_int_lt_of_int_iff

theorem of_nat_le_of_nat_iff (a b : ℕ) : of_nat a ≤ of_nat b ↔ (a ≤ b) :=
  by rewrite [*of_nat_eq, of_rat_le_of_rat_iff]; apply rat.of_nat_le_of_nat_iff

theorem of_nat_le_of_nat_of_le {a b : ℕ} : (a ≤ b) → of_nat a ≤ of_nat b :=
  iff.mpr !of_nat_le_of_nat_iff

theorem le_of_of_nat_le_of_nat {a b : ℕ} : of_nat a ≤ of_nat b → (a ≤ b) :=
  iff.mp !of_nat_le_of_nat_iff

theorem of_nat_lt_of_nat_iff (a b : ℕ) : of_nat a < of_nat b ↔ (a < b) :=
  by rewrite [*of_nat_eq, of_rat_lt_of_rat_iff]; apply rat.of_nat_lt_of_nat_iff

theorem of_nat_lt_of_nat_of_lt {a b : ℕ} : (a < b) → of_nat a < of_nat b :=
  iff.mpr !of_nat_lt_of_nat_iff

theorem lt_of_of_nat_lt_of_nat {a b : ℕ} : of_nat a < of_nat b → (a < b) :=
  iff.mp !of_nat_lt_of_nat_iff

theorem of_rat_pos_of_pos {q : ℚ} (Hq : q > 0) : of_rat q > 0 :=
  of_rat_lt_of_rat_of_lt Hq

theorem of_rat_nonneg_of_nonneg {q : ℚ} (Hq : q ≥ 0) : of_rat q ≥ 0 :=
  of_rat_le_of_rat_of_le Hq

theorem of_rat_neg_of_neg {q : ℚ} (Hq : q < 0) : of_rat q < 0 :=
  of_rat_lt_of_rat_of_lt Hq

theorem of_rat_nonpos_of_nonpos {q : ℚ} (Hq : q ≤ 0) : of_rat q ≤ 0 :=
  of_rat_le_of_rat_of_le Hq

theorem of_nat_nonneg (a : ℕ) : of_nat a ≥ 0 :=
of_rat_le_of_rat_of_le !rat.of_nat_nonneg

theorem of_nat_succ_pos (k : ℕ) : 0 < of_nat k + 1 :=
  add_pos_of_nonneg_of_pos (of_nat_nonneg k) real.zero_lt_one

theorem of_rat_pow (a : ℚ) (n : ℕ) : of_rat (a^n) = (of_rat a)^n :=
begin
  induction n with n ih,
    apply eq.refl,
  rewrite [2 pow_succ, of_rat_mul, ih]
end

theorem of_int_pow (a : ℤ) (n : ℕ) : of_int (#int a^n) = (of_int a)^n :=
by rewrite [of_int_eq, rat.of_int_pow, of_rat_pow]

theorem of_nat_pow (a : ℕ) (n : ℕ) : of_nat (#nat a^n) = (of_nat a)^n :=
by rewrite [of_nat_eq, rat.of_nat_pow, of_rat_pow]

open rat_seq
theorem le_of_le_reprs (x : ℝ) (t : seq) (Ht : regular t) : (∀ n : ℕ+, x ≤ t n) →
        x ≤ quot.mk (reg_seq.mk t Ht) :=
  quot.induction_on x (take s Hs,
    show r_le s (reg_seq.mk t Ht), from
      have H' : ∀ n : ℕ+, r_le s (r_const (t n)), from Hs,
      by apply r_le_of_le_reprs; apply Hs)

theorem le_of_reprs_le (x : ℝ) (t : seq) (Ht : regular t) : (∀ n : ℕ+, t n ≤ x) →
        x ≥ ((quot.mk (reg_seq.mk t Ht)) : ℝ) :=
  quot.induction_on x (take s Hs,
    show r_le (reg_seq.mk t Ht) s, from
      have H' : ∀ n : ℕ+, r_le (r_const (t n)) s, from Hs,
      by apply r_le_of_reprs_le; apply Hs)

end real


/-
Copyright (c) 2020 Aaron Anderson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson
-/
import algebra.big_operators.ring
import number_theory.divisors
import algebra.squarefree
import algebra.invertible
import data.nat.factorization

/-!
# Arithmetic Functions and Dirichlet Convolution

This file defines arithmetic functions, which are functions from `ℕ` to a specified type that map 0
to 0. In the literature, they are often instead defined as functions from `ℕ+`. These arithmetic
functions are endowed with a multiplication, given by Dirichlet convolution, and pointwise addition,
to form the Dirichlet ring.

## Main Definitions
 * `arithmetic_function R` consists of functions `f : ℕ → R` such that `f 0 = 0`.
 * An arithmetic function `f` `is_multiplicative` when `x.coprime y → f (x * y) = f x * f y`.
 * The pointwise operations `pmul` and `ppow` differ from the multiplication
  and power instances on `arithmetic_function R`, which use Dirichlet multiplication.
 * `ζ` is the arithmetic function such that `ζ x = 1` for `0 < x`.
 * `σ k` is the arithmetic function such that `σ k x = ∑ y in divisors x, y ^ k` for `0 < x`.
 * `pow k` is the arithmetic function such that `pow k x = x ^ k` for `0 < x`.
 * `id` is the identity arithmetic function on `ℕ`.
 * `ω n` is the number of distinct prime factors of `n`.
 * `Ω n` is the number of prime factors of `n` counted with multiplicity.
 * `μ` is the Möbius function.

## Main Results
 * Several forms of Möbius inversion:
 * `sum_eq_iff_sum_mul_moebius_eq` for functions to a `comm_ring`
 * `sum_eq_iff_sum_smul_moebius_eq` for functions to an `add_comm_group`
 * `prod_eq_iff_prod_pow_moebius_eq` for functions to a `comm_group`
 * `prod_eq_iff_prod_pow_moebius_eq_of_nonzero` for functions to a `comm_group_with_zero`

## Notation
The arithmetic functions `ζ` and `σ` have Greek letter names, which are localized notation in
the namespace `arithmetic_function`.

## Tags
arithmetic functions, dirichlet convolution, divisors

-/

open finset
open_locale big_operators

namespace nat
variable (R : Type*)

/-- An arithmetic function is a function from `ℕ` that maps 0 to 0. In the literature, they are
  often instead defined as functions from `ℕ+`. Multiplication on `arithmetic_functions` is by
  Dirichlet convolution. -/
@[derive [has_zero, inhabited]]
def arithmetic_function [has_zero R] := zero_hom ℕ R

variable {R}

namespace arithmetic_function

section has_zero
variable [has_zero R]

instance : has_coe_to_fun (arithmetic_function R) (λ _, ℕ → R) := zero_hom.has_coe_to_fun

@[simp] lemma to_fun_eq (f : arithmetic_function R) : f.to_fun = f := rfl

@[simp]
lemma map_zero {f : arithmetic_function R} : f 0 = 0 :=
zero_hom.map_zero' f

theorem coe_inj {f g : arithmetic_function R} : (f : ℕ → R) = g ↔ f = g :=
⟨λ h, zero_hom.coe_inj h, λ h, h ▸ rfl⟩

@[simp]
lemma zero_apply {x : ℕ} : (0 : arithmetic_function R) x = 0 :=
zero_hom.zero_apply x

@[ext] theorem ext ⦃f g : arithmetic_function R⦄ (h : ∀ x, f x = g x) : f = g :=
zero_hom.ext h

theorem ext_iff {f g : arithmetic_function R} : f = g ↔ ∀ x, f x = g x :=
zero_hom.ext_iff

section has_one
variable [has_one R]

instance : has_one (arithmetic_function R) := ⟨⟨λ x, ite (x = 1) 1 0, rfl⟩⟩

@[simp]
lemma one_one : (1 : arithmetic_function R) 1 = 1 := rfl

@[simp]
lemma one_apply_ne {x : ℕ} (h : x ≠ 1) : (1 : arithmetic_function R) x = 0 := if_neg h

end has_one
end has_zero

instance nat_coe [has_zero R] [has_one R] [has_add R] :
  has_coe (arithmetic_function ℕ) (arithmetic_function R) :=
⟨λ f, ⟨↑(f : ℕ → ℕ), by { transitivity ↑(f 0), refl, simp }⟩⟩

@[simp]
lemma nat_coe_nat (f : arithmetic_function ℕ) :
  (↑f : arithmetic_function ℕ) = f :=
ext $ λ _, cast_id _

@[simp]
lemma nat_coe_apply [has_zero R] [has_one R] [has_add R] {f : arithmetic_function ℕ} {x : ℕ} :
  (f : arithmetic_function R) x = f x := rfl

instance int_coe [has_zero R] [has_one R] [has_add R] [has_neg R] :
  has_coe (arithmetic_function ℤ) (arithmetic_function R) :=
⟨λ f, ⟨↑(f : ℕ → ℤ), by { transitivity ↑(f 0), refl, simp }⟩⟩

@[simp]
lemma int_coe_int (f : arithmetic_function ℤ) :
  (↑f : arithmetic_function ℤ) = f :=
ext $ λ _, int.cast_id _

@[simp]
lemma int_coe_apply [has_zero R] [has_one R] [has_add R] [has_neg R]
  {f : arithmetic_function ℤ} {x : ℕ} :
  (f : arithmetic_function R) x = f x := rfl

@[simp]
lemma coe_coe [has_zero R] [has_one R] [has_add R] [has_neg R] {f : arithmetic_function ℕ} :
  ((f : arithmetic_function ℤ) : arithmetic_function R) = f :=
by { ext, simp, }

section add_monoid

variable [add_monoid R]

instance : has_add (arithmetic_function R) := ⟨λ f g, ⟨λ n, f n + g n, by simp⟩⟩

@[simp]
lemma add_apply {f g : arithmetic_function R} {n : ℕ} : (f + g) n = f n + g n := rfl

instance : add_monoid (arithmetic_function R) :=
{ add_assoc := λ _ _ _, ext (λ _, add_assoc _ _ _),
  zero_add := λ _, ext (λ _, zero_add _),
  add_zero := λ _, ext (λ _, add_zero _),
  .. arithmetic_function.has_zero R,
  .. arithmetic_function.has_add }

end add_monoid

instance [add_comm_monoid R] : add_comm_monoid (arithmetic_function R) :=
{ add_comm := λ _ _, ext (λ _, add_comm _ _),
  .. arithmetic_function.add_monoid }

instance [add_group R] : add_group (arithmetic_function R) :=
{ neg := λ f, ⟨λ n, - f n, by simp⟩,
  add_left_neg := λ _, ext (λ _, add_left_neg _),
  .. arithmetic_function.add_monoid }

instance [add_comm_group R] : add_comm_group (arithmetic_function R) :=
{ .. arithmetic_function.add_comm_monoid,
  .. arithmetic_function.add_group }

section has_scalar
variables {M : Type*} [has_zero R] [add_comm_monoid M] [has_scalar R M]

/-- The Dirichlet convolution of two arithmetic functions `f` and `g` is another arithmetic function
  such that `(f * g) n` is the sum of `f x * g y` over all `(x,y)` such that `x * y = n`. -/
instance : has_scalar (arithmetic_function R) (arithmetic_function M) :=
⟨λ f g, ⟨λ n, ∑ x in divisors_antidiagonal n, f x.fst • g x.snd, by simp⟩⟩

@[simp]
lemma smul_apply {f : arithmetic_function R} {g : arithmetic_function M} {n : ℕ} :
  (f • g) n = ∑ x in divisors_antidiagonal n, f x.fst • g x.snd := rfl

end has_scalar

/-- The Dirichlet convolution of two arithmetic functions `f` and `g` is another arithmetic function
  such that `(f * g) n` is the sum of `f x * g y` over all `(x,y)` such that `x * y = n`. -/
instance [semiring R] : has_mul (arithmetic_function R) := ⟨(•)⟩

@[simp]
lemma mul_apply [semiring R] {f g : arithmetic_function R} {n : ℕ} :
  (f * g) n = ∑ x in divisors_antidiagonal n, f x.fst * g x.snd := rfl

section module
variables {M : Type*} [semiring R] [add_comm_monoid M] [module R M]

lemma mul_smul' (f g : arithmetic_function R) (h : arithmetic_function M) :
  (f * g) • h = f • g • h :=
begin
  ext n,
  simp only [mul_apply, smul_apply, sum_smul, mul_smul, smul_sum, finset.sum_sigma'],
  apply finset.sum_bij,
  swap 5,
  { rintros ⟨⟨i,j⟩, ⟨k,l⟩⟩ H, exact ⟨(k, l*j), (l, j)⟩ },
  { rintros ⟨⟨i,j⟩, ⟨k,l⟩⟩ H,
    simp only [finset.mem_sigma, mem_divisors_antidiagonal] at H ⊢,
    rcases H with ⟨⟨rfl, n0⟩, rfl, i0⟩,
    refine ⟨⟨(mul_assoc _ _ _).symm, n0⟩, rfl, _⟩,
    rw mul_ne_zero_iff at *,
    exact ⟨i0.2, n0.2⟩, },
  { rintros ⟨⟨i,j⟩, ⟨k,l⟩⟩ H, simp only [mul_assoc] },
  { rintros ⟨⟨a,b⟩, ⟨c,d⟩⟩ ⟨⟨i,j⟩, ⟨k,l⟩⟩ H₁ H₂,
    simp only [finset.mem_sigma, mem_divisors_antidiagonal,
      and_imp, prod.mk.inj_iff, add_comm, heq_iff_eq] at H₁ H₂ ⊢,
    rintros rfl h2 rfl rfl,
    exact ⟨⟨eq.trans H₁.2.1.symm H₂.2.1, rfl⟩, rfl, rfl⟩ },
  { rintros ⟨⟨i,j⟩, ⟨k,l⟩⟩ H, refine ⟨⟨(i*k, l), (i, k)⟩, _, _⟩,
    { simp only [finset.mem_sigma, mem_divisors_antidiagonal] at H ⊢,
      rcases H with ⟨⟨rfl, n0⟩, rfl, j0⟩,
      refine ⟨⟨mul_assoc _ _ _, n0⟩, rfl, _⟩,
      rw mul_ne_zero_iff at *,
      exact ⟨n0.1, j0.1⟩ },
    { simp only [true_and, mem_divisors_antidiagonal, and_true, prod.mk.inj_iff, eq_self_iff_true,
        ne.def, mem_sigma, heq_iff_eq] at H ⊢,
      rw H.2.1 } }
end

lemma one_smul' (b : arithmetic_function M) :
  (1 : arithmetic_function R) • b = b :=
begin
  ext,
  rw smul_apply,
  by_cases x0 : x = 0, {simp [x0]},
  have h : {(1,x)} ⊆ divisors_antidiagonal x := by simp [x0],
  rw ← sum_subset h, {simp},
  intros y ymem ynmem,
  have y1ne : y.fst ≠ 1,
  { intro con,
    simp only [con, mem_divisors_antidiagonal, one_mul, ne.def] at ymem,
    simp only [mem_singleton, prod.ext_iff] at ynmem,
    tauto },
  simp [y1ne],
end

end module

section semiring
variable [semiring R]

instance : monoid (arithmetic_function R) :=
{ one_mul := one_smul',
  mul_one := λ f,
  begin
    ext,
    rw mul_apply,
    by_cases x0 : x = 0, {simp [x0]},
    have h : {(x,1)} ⊆ divisors_antidiagonal x := by simp [x0],
    rw ← sum_subset h, {simp},
    intros y ymem ynmem,
    have y2ne : y.snd ≠ 1,
    { intro con,
      simp only [con, mem_divisors_antidiagonal, mul_one, ne.def] at ymem,
      simp only [mem_singleton, prod.ext_iff] at ynmem,
      tauto },
    simp [y2ne],
  end,
  mul_assoc := mul_smul',
  .. arithmetic_function.has_one,
  .. arithmetic_function.has_mul }

instance : semiring (arithmetic_function R) :=
{ zero_mul := λ f, by { ext, simp only [mul_apply, zero_mul, sum_const_zero, zero_apply] },
  mul_zero := λ f, by { ext, simp only [mul_apply, sum_const_zero, mul_zero, zero_apply] },
  left_distrib := λ a b c, by { ext, simp only [←sum_add_distrib, mul_add, mul_apply, add_apply] },
  right_distrib := λ a b c, by { ext, simp only [←sum_add_distrib, add_mul, mul_apply, add_apply] },
  .. arithmetic_function.has_zero R,
  .. arithmetic_function.has_mul,
  .. arithmetic_function.has_add,
  .. arithmetic_function.add_comm_monoid,
  .. arithmetic_function.monoid }

end semiring

instance [comm_semiring R] : comm_semiring (arithmetic_function R) :=
{ mul_comm := λ f g, by { ext,
    rw [mul_apply, ← map_swap_divisors_antidiagonal, sum_map],
    simp [mul_comm] },
  .. arithmetic_function.semiring }

instance [comm_ring R] : comm_ring (arithmetic_function R) :=
{ .. arithmetic_function.add_comm_group,
  .. arithmetic_function.comm_semiring }

instance {M : Type*} [semiring R] [add_comm_monoid M] [module R M] :
  module (arithmetic_function R) (arithmetic_function M) :=
{ one_smul := one_smul',
  mul_smul := mul_smul',
  smul_add := λ r x y, by { ext, simp only [sum_add_distrib, smul_add, smul_apply, add_apply] },
  smul_zero := λ r, by { ext, simp only [smul_apply, sum_const_zero, smul_zero, zero_apply] },
  add_smul := λ r s x, by { ext, simp only [add_smul, sum_add_distrib, smul_apply, add_apply] },
  zero_smul := λ r, by { ext, simp only [smul_apply, sum_const_zero, zero_smul, zero_apply] }, }

section zeta

/-- `ζ 0 = 0`, otherwise `ζ x = 1`. The Dirichlet Series is the Riemann ζ.  -/
def zeta : arithmetic_function ℕ :=
⟨λ x, ite (x = 0) 0 1, rfl⟩

localized "notation `ζ` := nat.arithmetic_function.zeta" in arithmetic_function

@[simp]
lemma zeta_apply {x : ℕ} : ζ x = if (x = 0) then 0 else 1 := rfl

lemma zeta_apply_ne {x : ℕ} (h : x ≠ 0) : ζ x = 1 := if_neg h

@[simp]
theorem coe_zeta_mul_apply [semiring R] {f : arithmetic_function R} {x : ℕ} :
  (↑ζ * f) x = ∑ i in divisors x, f i :=
begin
  rw mul_apply,
  transitivity ∑ i in divisors_antidiagonal x, f i.snd,
  { apply sum_congr rfl,
    intros i hi,
    rcases mem_divisors_antidiagonal.1 hi with ⟨rfl, h⟩,
    rw [nat_coe_apply, zeta_apply_ne (left_ne_zero_of_mul h), cast_one, one_mul] },
  { apply sum_bij (λ i h, prod.snd i),
    { rintros ⟨a, b⟩ h, simp [snd_mem_divisors_of_mem_antidiagonal h] },
    { rintros ⟨a, b⟩ h, refl },
    { rintros ⟨a1, b1⟩ ⟨a2, b2⟩ h1 h2 h,
      dsimp at h,
      rw h at *,
      rw mem_divisors_antidiagonal at *,
      ext, swap, {refl},
      simp only [prod.fst, prod.snd] at *,
      apply nat.eq_of_mul_eq_mul_right _ (eq.trans h1.1 h2.1.symm),
      rcases h1 with ⟨rfl, h⟩,
      apply nat.pos_of_ne_zero (right_ne_zero_of_mul h) },
    { intros a ha,
      rcases mem_divisors.1 ha with ⟨⟨b, rfl⟩, ne0⟩,
      use (b, a),
      simp [ne0, mul_comm] } }
end

theorem coe_zeta_smul_apply {M : Type*} [comm_ring R] [add_comm_group M] [module R M]
  {f : arithmetic_function M} {x : ℕ} :
  ((↑ζ : arithmetic_function R) • f) x = ∑ i in divisors x, f i :=
begin
  rw smul_apply,
  transitivity ∑ i in divisors_antidiagonal x, f i.snd,
  { apply sum_congr rfl,
    intros i hi,
    rcases mem_divisors_antidiagonal.1 hi with ⟨rfl, h⟩,
    rw [nat_coe_apply, zeta_apply_ne (left_ne_zero_of_mul h), cast_one, one_smul] },
  { apply sum_bij (λ i h, prod.snd i),
    { rintros ⟨a, b⟩ h, simp [snd_mem_divisors_of_mem_antidiagonal h] },
    { rintros ⟨a, b⟩ h, refl },
    { rintros ⟨a1, b1⟩ ⟨a2, b2⟩ h1 h2 h,
      dsimp at h,
      rw h at *,
      rw mem_divisors_antidiagonal at *,
      ext, swap, {refl},
      simp only [prod.fst, prod.snd] at *,
      apply nat.eq_of_mul_eq_mul_right _ (eq.trans h1.1 h2.1.symm),
      rcases h1 with ⟨rfl, h⟩,
      apply nat.pos_of_ne_zero (right_ne_zero_of_mul h) },
    { intros a ha,
      rcases mem_divisors.1 ha with ⟨⟨b, rfl⟩, ne0⟩,
      use (b, a),
      simp [ne0, mul_comm] } }
end

@[simp]
theorem coe_mul_zeta_apply [semiring R] {f : arithmetic_function R} {x : ℕ} :
  (f * ζ) x = ∑ i in divisors x, f i :=
begin
  apply mul_opposite.op_injective,
  rw [op_sum],
  convert @coe_zeta_mul_apply Rᵐᵒᵖ _ { to_fun := mul_opposite.op ∘ f, map_zero' := by simp} x,
  rw [mul_apply, mul_apply, op_sum],
  conv_lhs { rw ← map_swap_divisors_antidiagonal, },
  rw sum_map,
  apply sum_congr rfl,
  intros y hy,
  by_cases h1 : y.fst = 0,
  { simp [function.comp_apply, h1] },
  { simp only [h1, mul_one, one_mul, prod.fst_swap, function.embedding.coe_fn_mk, prod.snd_swap,
      if_false, zeta_apply, zero_hom.coe_mk, nat_coe_apply, cast_one] }
end

theorem zeta_mul_apply {f : arithmetic_function ℕ} {x : ℕ} :
  (ζ * f) x = ∑ i in divisors x, f i :=
by rw [← nat_coe_nat ζ, coe_zeta_mul_apply]

theorem mul_zeta_apply {f : arithmetic_function ℕ} {x : ℕ} :
  (f * ζ) x = ∑ i in divisors x, f i :=
by rw [← nat_coe_nat ζ, coe_mul_zeta_apply]

end zeta

open_locale arithmetic_function

section pmul

/-- This is the pointwise product of `arithmetic_function`s. -/
def pmul [mul_zero_class R] (f g : arithmetic_function R) :
  arithmetic_function R :=
⟨λ x, f x * g x, by simp⟩

@[simp]
lemma pmul_apply [mul_zero_class R] {f g : arithmetic_function R} {x : ℕ} :
  f.pmul g x = f x * g x := rfl

lemma pmul_comm [comm_monoid_with_zero R] (f g : arithmetic_function R) :
  f.pmul g = g.pmul f :=
by { ext, simp [mul_comm] }

variable [semiring R]

@[simp]
lemma pmul_zeta (f : arithmetic_function R) : f.pmul ↑ζ = f :=
begin
  ext x,
  cases x;
  simp [nat.succ_ne_zero],
end

@[simp]
lemma zeta_pmul (f : arithmetic_function R) : (ζ : arithmetic_function R).pmul f = f :=
begin
  ext x,
  cases x;
  simp [nat.succ_ne_zero],
end

/-- This is the pointwise power of `arithmetic_function`s. -/
def ppow (f : arithmetic_function R) (k : ℕ) :
  arithmetic_function R :=
if h0 : k = 0 then ζ else ⟨λ x, (f x) ^ k,
  by { rw [map_zero], exact zero_pow (nat.pos_of_ne_zero h0) }⟩

@[simp]
lemma ppow_zero {f : arithmetic_function R} : f.ppow 0 = ζ :=
by rw [ppow, dif_pos rfl]

@[simp]
lemma ppow_apply {f : arithmetic_function R} {k x : ℕ} (kpos : 0 < k) :
  f.ppow k x = (f x) ^ k :=
by { rw [ppow, dif_neg (ne_of_gt kpos)], refl }

lemma ppow_succ {f : arithmetic_function R} {k : ℕ} :
  f.ppow (k + 1) = f.pmul (f.ppow k) :=
begin
  ext x,
  rw [ppow_apply (nat.succ_pos k), pow_succ],
  induction k; simp,
end

lemma ppow_succ' {f : arithmetic_function R} {k : ℕ} {kpos : 0 < k} :
  f.ppow (k + 1) = (f.ppow k).pmul f :=
begin
  ext x,
  rw [ppow_apply (nat.succ_pos k), pow_succ'],
  induction k; simp,
end

end pmul

/-- Multiplicative functions -/
def is_multiplicative [monoid_with_zero R] (f : arithmetic_function R) : Prop :=
f 1 = 1 ∧ (∀ {m n : ℕ}, m.coprime n → f (m * n) = f m * f n)

namespace is_multiplicative

section monoid_with_zero
variable [monoid_with_zero R]

@[simp]
lemma map_one {f : arithmetic_function R} (h : f.is_multiplicative) : f 1 = 1 :=
h.1

@[simp]
lemma map_mul_of_coprime {f : arithmetic_function R} (hf : f.is_multiplicative)
  {m n : ℕ} (h : m.coprime n) : f (m * n) = f m * f n :=
hf.2 h

end monoid_with_zero

lemma nat_cast {f : arithmetic_function ℕ} [semiring R] (h : f.is_multiplicative) :
  is_multiplicative (f : arithmetic_function R) :=
⟨by simp [h], λ m n cop, by simp [cop, h]⟩

lemma int_cast {f : arithmetic_function ℤ} [ring R] (h : f.is_multiplicative) :
  is_multiplicative (f : arithmetic_function R) :=
⟨by simp [h], λ m n cop, by simp [cop, h]⟩

lemma mul [comm_semiring R] {f g : arithmetic_function R}
  (hf : f.is_multiplicative) (hg : g.is_multiplicative) :
  is_multiplicative (f * g) :=
⟨by { simp [hf, hg], }, begin
  simp only [mul_apply],
  intros m n cop,
  rw sum_mul_sum,
  symmetry,
  apply sum_bij (λ (x : (ℕ × ℕ) × ℕ × ℕ) h, (x.1.1 * x.2.1, x.1.2 * x.2.2)),
  { rintros ⟨⟨a1, a2⟩, ⟨b1, b2⟩⟩ h,
    simp only [mem_divisors_antidiagonal, ne.def, mem_product] at h,
    rcases h with ⟨⟨rfl, ha⟩, ⟨rfl, hb⟩⟩,
    simp only [mem_divisors_antidiagonal, nat.mul_eq_zero, ne.def],
    split, {ring},
    rw nat.mul_eq_zero at *,
    apply not_or ha hb },
  { rintros ⟨⟨a1, a2⟩, ⟨b1, b2⟩⟩ h,
    simp only [mem_divisors_antidiagonal, ne.def, mem_product] at h,
    rcases h with ⟨⟨rfl, ha⟩, ⟨rfl, hb⟩⟩,
    dsimp only,
    rw [hf.map_mul_of_coprime cop.coprime_mul_right.coprime_mul_right_right,
        hg.map_mul_of_coprime cop.coprime_mul_left.coprime_mul_left_right],
    ring, },
  { rintros ⟨⟨a1, a2⟩, ⟨b1, b2⟩⟩ ⟨⟨c1, c2⟩, ⟨d1, d2⟩⟩ hab hcd h,
    simp only [mem_divisors_antidiagonal, ne.def, mem_product] at hab,
    rcases hab with ⟨⟨rfl, ha⟩, ⟨rfl, hb⟩⟩,
    simp only [mem_divisors_antidiagonal, ne.def, mem_product] at hcd,
    simp only [prod.mk.inj_iff] at h,
    ext; dsimp only,
    { transitivity nat.gcd (a1 * a2) (a1 * b1),
      { rw [nat.gcd_mul_left, cop.coprime_mul_left.coprime_mul_right_right.gcd_eq_one, mul_one] },
      { rw [← hcd.1.1, ← hcd.2.1] at cop,
        rw [← hcd.1.1, h.1, nat.gcd_mul_left,
            cop.coprime_mul_left.coprime_mul_right_right.gcd_eq_one, mul_one] } },
    { transitivity nat.gcd (a1 * a2) (a2 * b2),
      { rw [mul_comm, nat.gcd_mul_left, cop.coprime_mul_right.coprime_mul_left_right.gcd_eq_one,
            mul_one] },
      { rw [← hcd.1.1, ← hcd.2.1] at cop,
        rw [← hcd.1.1, h.2, mul_comm, nat.gcd_mul_left,
            cop.coprime_mul_right.coprime_mul_left_right.gcd_eq_one, mul_one] } },
    { transitivity nat.gcd (b1 * b2) (a1 * b1),
      { rw [mul_comm, nat.gcd_mul_right,
           cop.coprime_mul_right.coprime_mul_left_right.symm.gcd_eq_one, one_mul] },
      { rw [← hcd.1.1, ← hcd.2.1] at cop,
        rw [← hcd.2.1, h.1, mul_comm c1 d1, nat.gcd_mul_left,
            cop.coprime_mul_right.coprime_mul_left_right.symm.gcd_eq_one, mul_one] } },
    { transitivity nat.gcd (b1 * b2) (a2 * b2),
      { rw [nat.gcd_mul_right,
           cop.coprime_mul_left.coprime_mul_right_right.symm.gcd_eq_one, one_mul] },
      { rw [← hcd.1.1, ← hcd.2.1] at cop,
        rw [← hcd.2.1, h.2, nat.gcd_mul_right,
            cop.coprime_mul_left.coprime_mul_right_right.symm.gcd_eq_one, one_mul] } } },
  { rintros ⟨b1, b2⟩ h,
    simp only [mem_divisors_antidiagonal, ne.def, mem_product] at h,
    use ((b1.gcd m, b2.gcd m), (b1.gcd n, b2.gcd n)),
    simp only [exists_prop, prod.mk.inj_iff, ne.def, mem_product, mem_divisors_antidiagonal],
    rw [← cop.gcd_mul _, ← cop.gcd_mul _, ← h.1, nat.gcd_mul_gcd_of_coprime_of_mul_eq_mul cop h.1,
        nat.gcd_mul_gcd_of_coprime_of_mul_eq_mul cop.symm _],
    { rw [nat.mul_eq_zero, decidable.not_or_iff_and_not] at h, simp [h.2.1, h.2.2] },
    rw [mul_comm n m, h.1] }
end⟩

lemma pmul [comm_semiring R] {f g : arithmetic_function R}
  (hf : f.is_multiplicative) (hg : g.is_multiplicative) :
  is_multiplicative (f.pmul g) :=
⟨by { simp [hf, hg], }, λ m n cop, begin
  simp only [pmul_apply, hf.map_mul_of_coprime cop, hg.map_mul_of_coprime cop],
  ring,
end⟩

/-- For any multiplicative function `f` and any `n > 0`,
we can evaluate `f n` by evaluating `f` at `p ^ k` over the factorization of `n` -/
lemma multiplicative_factorization [comm_monoid_with_zero R] (f : arithmetic_function R)
  (hf : f.is_multiplicative) :
  ∀ {n : ℕ}, n ≠ 0 → f n = n.factorization.prod (λ p k, f (p ^ k)) :=
λ n hn, multiplicative_factorization f hf.2 hf.1 hn

end is_multiplicative

section special_functions

/-- The identity on `ℕ` as an `arithmetic_function`.  -/
def id : arithmetic_function ℕ := ⟨id, rfl⟩

@[simp]
lemma id_apply {x : ℕ} : id x = x := rfl

/-- `pow k n = n ^ k`, except `pow 0 0 = 0`. -/
def pow (k : ℕ) : arithmetic_function ℕ := id.ppow k

@[simp]
lemma pow_apply {k n : ℕ} : pow k n = if (k = 0 ∧ n = 0) then 0 else n ^ k :=
begin
  cases k,
  { simp [pow] },
  simp [pow, (ne_of_lt (nat.succ_pos k)).symm],
end

/-- `σ k n` is the sum of the `k`th powers of the divisors of `n` -/
def sigma (k : ℕ) : arithmetic_function ℕ :=
⟨λ n, ∑ d in divisors n, d ^ k, by simp⟩

localized "notation `σ` := nat.arithmetic_function.sigma" in arithmetic_function

@[simp]
lemma sigma_apply {k n : ℕ} : σ k n = ∑ d in divisors n, d ^ k := rfl

lemma sigma_one_apply {n : ℕ} : σ 1 n = ∑ d in divisors n, d := by simp

lemma zeta_mul_pow_eq_sigma {k : ℕ} : ζ * pow k = σ k :=
begin
  ext,
  rw [sigma, zeta_mul_apply],
  apply sum_congr rfl,
  intros x hx,
  rw [pow_apply, if_neg (not_and_of_not_right _ _)],
  contrapose! hx,
  simp [hx],
end

lemma is_multiplicative_zeta : is_multiplicative ζ :=
⟨by simp, λ m n cop, begin
  cases m, {simp},
  cases n, {simp},
  simp [nat.succ_ne_zero]
end⟩

lemma is_multiplicative_id : is_multiplicative arithmetic_function.id :=
⟨rfl, λ _ _ _, rfl⟩

lemma is_multiplicative.ppow [comm_semiring R] {f : arithmetic_function R}
  (hf : f.is_multiplicative) {k : ℕ} :
  is_multiplicative (f.ppow k) :=
begin
  induction k with k hi,
  { exact is_multiplicative_zeta.nat_cast },
  { rw ppow_succ,
    apply hf.pmul hi },
end

lemma is_multiplicative_pow {k : ℕ} : is_multiplicative (pow k) :=
is_multiplicative_id.ppow

lemma is_multiplicative_sigma {k : ℕ} :
  is_multiplicative (sigma k) :=
begin
  rw [← zeta_mul_pow_eq_sigma],
  apply ((is_multiplicative_zeta).mul is_multiplicative_pow)
end

/-- `Ω n` is the number of prime factors of `n`. -/
def card_factors : arithmetic_function ℕ :=
⟨λ n, n.factors.length, by simp⟩

localized "notation `Ω` := nat.arithmetic_function.card_factors" in arithmetic_function

lemma card_factors_apply {n : ℕ} :
  Ω n = n.factors.length := rfl

@[simp]
lemma card_factors_one : Ω 1 = 0 := by simp [card_factors]

lemma card_factors_eq_one_iff_prime {n : ℕ} :
  Ω n = 1 ↔ n.prime :=
begin
  refine ⟨λ h, _, λ h, list.length_eq_one.2 ⟨n, factors_prime h⟩⟩,
  cases n,
  { contrapose! h,
    simp },
  rcases list.length_eq_one.1 h with ⟨x, hx⟩,
  rw [← prod_factors n.succ_pos, hx, list.prod_singleton],
  apply prime_of_mem_factors,
  rw [hx, list.mem_singleton]
end

lemma card_factors_mul {m n : ℕ} (m0 : m ≠ 0) (n0 : n ≠ 0) :
  Ω (m * n) = Ω m + Ω n :=
by rw [card_factors_apply, card_factors_apply, card_factors_apply, ← multiset.coe_card,
  ← factors_eq, unique_factorization_monoid.normalized_factors_mul m0 n0, factors_eq, factors_eq,
  multiset.card_add, multiset.coe_card, multiset.coe_card]

lemma card_factors_multiset_prod {s : multiset ℕ} (h0 : s.prod ≠ 0) :
  Ω s.prod = (multiset.map Ω s).sum :=
begin
  revert h0,
  apply s.induction_on, by simp,
  intros a t h h0,
  rw [multiset.prod_cons, mul_ne_zero_iff] at h0,
  simp [h0, card_factors_mul, h],
end

/-- `ω n` is the number of distinct prime factors of `n`. -/
def card_distinct_factors : arithmetic_function ℕ :=
⟨λ n, n.factors.erase_dup.length, by simp⟩

localized "notation `ω` := nat.arithmetic_function.card_distinct_factors" in arithmetic_function

lemma card_distinct_factors_zero : ω 0 = 0 := by simp

lemma card_distinct_factors_apply {n : ℕ} :
  ω n = n.factors.erase_dup.length := rfl

lemma card_distinct_factors_eq_card_factors_iff_squarefree {n : ℕ} (h0 : n ≠ 0) :
  ω n = Ω n ↔ squarefree n :=
begin
  rw [squarefree_iff_nodup_factors h0, card_distinct_factors_apply],
  split; intro h,
  { rw ← list.eq_of_sublist_of_length_eq n.factors.erase_dup_sublist h,
    apply list.nodup_erase_dup },
  { rw h.erase_dup,
    refl }
end

/-- `μ` is the Möbius function. If `n` is squarefree with an even number of distinct prime factors,
  `μ n = 1`. If `n` is squarefree with an odd number of distinct prime factors, `μ n = -1`.
  If `n` is not squarefree, `μ n = 0`. -/
def moebius : arithmetic_function ℤ :=
⟨λ n, if squarefree n then (-1) ^ (card_factors n) else 0, by simp⟩

localized "notation `μ` := nat.arithmetic_function.moebius" in arithmetic_function

@[simp]
lemma moebius_apply_of_squarefree {n : ℕ} (h : squarefree n): μ n = (-1) ^ (card_factors n) :=
if_pos h

@[simp]
lemma moebius_eq_zero_of_not_squarefree {n : ℕ} (h : ¬ squarefree n): μ n = 0 := if_neg h

lemma moebius_ne_zero_iff_squarefree {n : ℕ} : μ n ≠ 0 ↔ squarefree n :=
begin
  split; intro h,
  { contrapose! h,
    simp [h] },
  { simp [h, pow_ne_zero] }
end

lemma moebius_ne_zero_iff_eq_or {n : ℕ} : μ n ≠ 0 ↔ μ n = 1 ∨ μ n = -1 :=
begin
  split; intro h,
  { rw moebius_ne_zero_iff_squarefree at h,
    rw moebius_apply_of_squarefree h,
    apply neg_one_pow_eq_or },
  { rcases h with h | h; simp [h] }
end

open unique_factorization_monoid

@[simp] lemma coe_moebius_mul_coe_zeta [comm_ring R] : (μ * ζ : arithmetic_function R) = 1 :=
begin
  ext x,
  cases x,
  { simp only [divisors_zero, sum_empty, ne.def, not_false_iff, coe_mul_zeta_apply,
      zero_ne_one, one_apply_ne] },
  cases x,
  { simp only [moebius_apply_of_squarefree, card_factors_one, squarefree_one, divisors_one,
      int.cast_one, sum_singleton, coe_mul_zeta_apply, one_one, int_coe_apply, pow_zero] },
  rw [coe_mul_zeta_apply, one_apply_ne (ne_of_gt (succ_lt_succ (nat.succ_pos _)))],
  simp_rw [int_coe_apply],
  rw [←int.cast_sum, ← sum_filter_ne_zero],
  convert int.cast_zero,
  simp only [moebius_ne_zero_iff_squarefree],
  suffices :
    ∑ (y : finset ℕ) in
      (unique_factorization_monoid.normalized_factors x.succ.succ).to_finset.powerset,
    ite (squarefree y.val.prod) ((-1:ℤ) ^ Ω y.val.prod) 0 = 0,
  { have h : ∑ i in _, ite (squarefree i) ((-1:ℤ) ^ Ω i) 0 = _ :=
      (sum_divisors_filter_squarefree (nat.succ_ne_zero _)),
    exact (eq.trans (by congr') h).trans this },
  apply eq.trans (sum_congr rfl _) (sum_powerset_neg_one_pow_card_of_nonempty _),
  { intros y hy,
    rw [finset.mem_powerset, ← finset.val_le_iff, multiset.to_finset_val] at hy,
    have h : unique_factorization_monoid.normalized_factors y.val.prod = y.val,
    { apply factors_multiset_prod_of_irreducible,
      intros z hz,
      apply irreducible_of_normalized_factor _ (multiset.subset_of_le
        (le_trans hy (multiset.erase_dup_le _)) hz) },
    rw [if_pos],
    { rw [card_factors_apply, ← multiset.coe_card, ← factors_eq, h, finset.card] },
    rw [unique_factorization_monoid.squarefree_iff_nodup_normalized_factors, h],
    { apply y.nodup },
    rw [ne.def, multiset.prod_eq_zero_iff],
    intro con,
    rw ← h at con,
    exact not_irreducible_zero (irreducible_of_normalized_factor 0 con) },
  { rw finset.nonempty,
    rcases wf_dvd_monoid.exists_irreducible_factor _ (nat.succ_ne_zero _) with ⟨i, hi⟩,
    { rcases exists_mem_normalized_factors_of_dvd (nat.succ_ne_zero _) hi.1 hi.2 with ⟨j, hj, hj2⟩,
      use j,
      apply multiset.mem_to_finset.2 hj },
    rw nat.is_unit_iff,
    norm_num },
end

@[simp] lemma coe_zeta_mul_coe_moebius [comm_ring R] : (ζ * μ : arithmetic_function R) = 1 :=
by rw [mul_comm, coe_moebius_mul_coe_zeta]

@[simp]

@[simp] lemma coe_zeta_mul_moebius : (ζ * μ : arithmetic_function ℤ) = 1 :=
by rw [← int_coe_int μ, coe_zeta_mul_coe_moebius]

section comm_ring
variable [comm_ring R]

instance : invertible (ζ : arithmetic_function R) :=
{ inv_of := μ,
  inv_of_mul_self := coe_moebius_mul_coe_zeta,
  mul_inv_of_self := coe_zeta_mul_coe_moebius}

/-- A unit in `arithmetic_function R` that evaluates to `ζ`, with inverse `μ`. -/
def zeta_unit : (arithmetic_function R)ˣ :=
⟨ζ, μ, coe_zeta_mul_coe_moebius, coe_moebius_mul_coe_zeta⟩

@[simp]
lemma coe_zeta_unit :
  ((zeta_unit : (arithmetic_function R)ˣ) : arithmetic_function R) = ζ := rfl

@[simp]
lemma inv_zeta_unit :
  ((zeta_unit⁻¹ : (arithmetic_function R)ˣ) : arithmetic_function R) = μ := rfl

end comm_ring

/-- Möbius inversion for functions to an `add_comm_group`. -/
theorem sum_eq_iff_sum_smul_moebius_eq
  [add_comm_group R] {f g : ℕ → R} :
  (∀ (n : ℕ), 0 < n → ∑ i in (n.divisors), f i = g n) ↔
    ∀ (n : ℕ), 0 < n → ∑ (x : ℕ × ℕ) in n.divisors_antidiagonal, μ x.fst • g x.snd = f n :=
begin
  let f' : arithmetic_function R := ⟨λ x, if x = 0 then 0 else f x, if_pos rfl⟩,
  let g' : arithmetic_function R := ⟨λ x, if x = 0 then 0 else g x, if_pos rfl⟩,
  transitivity (ζ : arithmetic_function ℤ) • f' = g',
  { rw ext_iff,
    apply forall_congr,
    intro n,
    cases n, { simp },
    rw coe_zeta_smul_apply,
    simp only [n.succ_ne_zero, forall_prop_of_true, succ_pos', if_false, zero_hom.coe_mk],
    rw sum_congr rfl (λ x hx, _),
    rw (if_neg (ne_of_gt (nat.pos_of_mem_divisors hx))) },
  transitivity μ • g' = f',
  { split; intro h,
    { rw [← h, ← mul_smul, moebius_mul_coe_zeta, one_smul] },
    { rw [← h, ← mul_smul, coe_zeta_mul_moebius, one_smul] } },
  { rw ext_iff,
    apply forall_congr,
    intro n,
    cases n, { simp },
    simp only [n.succ_ne_zero, forall_prop_of_true, succ_pos', smul_apply,
      if_false, zero_hom.coe_mk],
    rw sum_congr rfl (λ x hx, _),
    rw (if_neg (ne_of_gt (nat.pos_of_mem_divisors (snd_mem_divisors_of_mem_antidiagonal hx)))) },
end

/-- Möbius inversion for functions to a `comm_ring`. -/
theorem sum_eq_iff_sum_mul_moebius_eq [comm_ring R] {f g : ℕ → R} :
  (∀ (n : ℕ), 0 < n → ∑ i in (n.divisors), f i = g n) ↔
    ∀ (n : ℕ), 0 < n → ∑ (x : ℕ × ℕ) in n.divisors_antidiagonal, (μ x.fst : R) * g x.snd = f n :=
begin
  rw sum_eq_iff_sum_smul_moebius_eq,
  apply forall_congr,
  intro a,
  apply imp_congr (iff.refl _) (eq.congr_left (sum_congr rfl (λ x hx, _))),
  rw [zsmul_eq_mul],
end

/-- Möbius inversion for functions to a `comm_group`. -/
theorem prod_eq_iff_prod_pow_moebius_eq [comm_group R] {f g : ℕ → R} :
  (∀ (n : ℕ), 0 < n → ∏ i in (n.divisors), f i = g n) ↔
    ∀ (n : ℕ), 0 < n → ∏ (x : ℕ × ℕ) in n.divisors_antidiagonal, g x.snd ^ (μ x.fst) = f n :=
@sum_eq_iff_sum_smul_moebius_eq (additive R) _ _ _

/-- Möbius inversion for functions to a `comm_group_with_zero`. -/
theorem prod_eq_iff_prod_pow_moebius_eq_of_nonzero [comm_group_with_zero R] {f g : ℕ → R}
  (hf : ∀ (n : ℕ), 0 < n → f n ≠ 0) (hg : ∀ (n : ℕ), 0 < n → g n ≠ 0) :
  (∀ (n : ℕ), 0 < n → ∏ i in (n.divisors), f i = g n) ↔
    ∀ (n : ℕ), 0 < n → ∏ (x : ℕ × ℕ) in n.divisors_antidiagonal, g x.snd ^ (μ x.fst) = f n :=
begin
  refine iff.trans (iff.trans (forall_congr (λ n, _)) (@prod_eq_iff_prod_pow_moebius_eq Rˣ _
    (λ n, if h : 0 < n then units.mk0 (f n) (hf n h) else 1)
    (λ n, if h : 0 < n then units.mk0 (g n) (hg n h) else 1))) (forall_congr (λ n, _));
  refine imp_congr_right (λ hn, _),
  { dsimp,
    rw [dif_pos hn, ← units.eq_iff, ← units.coe_hom_apply, monoid_hom.map_prod, units.coe_mk0,
      prod_congr rfl _],
    intros x hx,
    rw [dif_pos (nat.pos_of_mem_divisors hx), units.coe_hom_apply, units.coe_mk0] },
  { dsimp,
    rw [dif_pos hn, ← units.eq_iff, ← units.coe_hom_apply, monoid_hom.map_prod, units.coe_mk0,
      prod_congr rfl _],
    intros x hx,
    rw [dif_pos (nat.pos_of_mem_divisors (nat.snd_mem_divisors_of_mem_antidiagonal hx)),
      units.coe_hom_apply, units.coe_zpow₀, units.coe_mk0] }
end

end special_functions
end arithmetic_function
end nat


/-
Copyright (c) 2022 Eric Rodriguez. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Rodriguez
-/
import data.fin.basic
import order.succ_pred.basic

/-!
# Successors and predecessors of `fin n`

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

In this file, we show that `fin n` is both a `succ_order` and a `pred_order`. Note that they are
also archimedean, but this is derived from the general instance for well-orderings as opposed
to a specific `fin` instance.

-/

namespace fin

instance : ∀ {n : ℕ}, succ_order (fin n)
| 0 := by constructor; exact elim0
| (n+1) :=
_root_.succ_order.of_core (λ i, if i < fin.last n then i + 1 else i)
begin
  intros a ha b,
  rw [is_max_iff_eq_top, eq_top_iff, not_le, top_eq_last] at ha,
  rw [if_pos ha, lt_iff_coe_lt_coe, le_iff_coe_le_coe, coe_add_one_of_lt ha],
  exact nat.lt_iff_add_one_le
end
begin
  intros a ha,
  rw [is_max_iff_eq_top, top_eq_last] at ha,
  rw [if_neg ha.not_lt],
end

@[simp] lemma succ_eq {n : ℕ} : succ_order.succ = λ a, if a < fin.last n then a + 1 else a := rfl
@[simp] lemma succ_apply {n : ℕ} (a) :
  succ_order.succ a = if a < fin.last n then a + 1 else a := rfl

instance : ∀ {n : ℕ}, pred_order (fin n)
| 0 := by constructor; exact elim0
| (n+1) :=
_root_.pred_order.of_core (λ x, if x = 0 then 0 else x - 1)
begin
  intros a ha b,
  rw [is_min_iff_eq_bot, eq_bot_iff, not_le, bot_eq_zero] at ha,
  rw [if_neg ha.ne', lt_iff_coe_lt_coe, le_iff_coe_le_coe, coe_sub_one,
      if_neg ha.ne', le_tsub_iff_right, iff.comm],
  exact nat.lt_iff_add_one_le,
  exact ha
end
begin
  intros a ha,
  rw [is_min_iff_eq_bot, bot_eq_zero] at ha,
  rwa [if_pos ha, eq_comm],
end

@[simp] lemma pred_eq {n} : pred_order.pred = λ a : fin (n + 1), if a = 0 then 0 else a - 1 := rfl
@[simp] lemma pred_apply {n : ℕ} (a : fin (n + 1)) :
  pred_order.pred a = if a = 0 then 0 else a - 1 := rfl

end fin


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_314
  (n : ℕ)
  (h₀ : n = 11) :
  (1 / 4)^(n + 1) * 2^(2 * n) = 1 / 4 :=
begin
  rw h₀,
  norm_num,
end