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

Extended reals.
-/
import data.real
open real eq.ops classical

-- This is a hack, to get around the fact that the top level names are inaccessible when
-- defining these theorems in the ereal namespace. Is there a better way?
private definition zero_mul' := @zero_mul
private definition mul_zero' := @mul_zero
private definition neg_neg'  := @neg_neg

noncomputable theory

inductive ereal : Type :=
| of_real : ℝ → ereal
| infty : ereal
| neginfty : ereal

attribute ereal.of_real [coercion]
notation `∞` := ereal.infty
notation `-∞` := ereal.neginfty

namespace ereal

protected definition prio := num.pred real.prio

/- arithmetic operations on the ereals -/

definition ereal_has_zero [instance] [priority ereal.prio] : has_zero ereal :=
has_zero.mk (of_real 0)

definition ereal_has_one [instance] [priority ereal.prio] : has_one ereal :=
has_one.mk (of_real 1)

protected definition add : ereal → ereal → ereal
| (of_real x) (of_real y) := of_real (x + y)
| ∞ _                     := ∞
| _ ∞                     := ∞
| -∞ _                    := -∞
| _ -∞                    := -∞

protected definition neg : ereal → ereal
| (of_real x) := of_real (-x)
| ∞           := -∞
| -∞          := ∞

private definition blow_up [reducible] : ereal → ereal
| (of_real x) := if x = 0 then of_real 0 else if x > 0 then ∞ else -∞
| ∞           := ∞
| -∞          := -∞

protected definition mul : ereal → ereal → ereal
| (of_real x) (of_real y) := of_real (x * y)
| ∞ a                     := blow_up a
| a ∞                     := blow_up a
| -∞ a                    := ereal.neg (blow_up a)
| a -∞                    := ereal.neg (blow_up a)

definition ereal_has_add [instance] [priority ereal.prio] : has_add ereal :=
has_add.mk ereal.add

definition ereal_has_neg [instance] [priority ereal.prio] : has_neg ereal :=
has_neg.mk ereal.neg

protected definition sub (u v : ereal) : ereal := u + -v

definition ereal_has_sub [instance] [priority ereal.prio] : has_sub ereal :=
has_sub.mk ereal.sub

definition ereal_has_mul [instance] [priority ereal.prio] : has_mul ereal :=
has_mul.mk ereal.mul

protected theorem zero_def : (0 : ereal) = of_real 0 := rfl

protected theorem one_def : (1 : ereal) = of_real 1 := rfl

protected theorem add_def (x y : ereal) : x + y = ereal.add x y := rfl

protected theorem neg_def (x : ereal) : -x = ereal.neg x := rfl

protected theorem sub_eq_add_neg (u v : ereal) : u - v = u + -v := rfl

protected theorem mul_def (x y : ereal) : x * y = ereal.mul x y := rfl

theorem of_real.inj {x y : real} (H : of_real x = of_real y) : x = y :=
ereal.no_confusion H (assume H1, H1)

abbreviation eq_of_of_real_eq_of_real := @of_real.inj

theorem of_real_add (x y : real) : of_real (x + y) = of_real x + of_real y := rfl

theorem of_real_mul (x y : real) : of_real (x * y) = of_real x * of_real y := rfl

theorem infty_ne_neg_infty : ∞ ≠ -∞ := ereal.no_confusion

theorem infty_ne_of_real (x : real) : ∞ ≠ of_real x := ereal.no_confusion

theorem neg_infty_ne_of_real (x : real) : -∞ ≠ of_real x := ereal.no_confusion

/- properties of the arithmetic operations -/

protected theorem add_comm : ∀ u v : ereal, u + v = v + u
| (of_real x) (of_real y) := congr_arg of_real !add.comm
| ∞ v                     := by rewrite[*ereal.add_def, ↑ereal.add]
| u ∞                     := by rewrite[*ereal.add_def, ↑ereal.add]
| -∞ v                    := by rewrite[*ereal.add_def, ↑ereal.add]
| u -∞                    := by rewrite[*ereal.add_def, ↑ereal.add]

theorem infty_add : ∀ u, ∞ + u = ∞
| (of_real x) := rfl
| ∞           := rfl
| -∞          := rfl

theorem add_infty : ∀ u, u + ∞ = ∞
| (of_real x) := rfl
| ∞           := rfl
| -∞          := rfl

protected theorem add_assoc : ∀ u v w : ereal, (u + v) + w = u + (v + w)
| (of_real x) (of_real y) (of_real z) := congr_arg of_real !add.assoc
| ∞ v w                               := by rewrite [*infty_add, *add_infty]
| u ∞ w                               := by rewrite [*infty_add, *add_infty, infty_add]
| u v ∞                               := by rewrite [*infty_add, *add_infty]
| (of_real x) (of_real y) -∞          := by rewrite[*ereal.add_def, ↑ereal.add]
| (of_real x) -∞ (of_real z)          := by rewrite[*ereal.add_def, ↑ereal.add]
| -∞ (of_real y) (of_real z)          := by rewrite[*ereal.add_def, ↑ereal.add]
| (of_real x) -∞ -∞                   := by rewrite[*ereal.add_def, ↑ereal.add]
| -∞ (of_real y) -∞                   := rfl
| -∞ -∞ (of_real z)                   := by rewrite[*ereal.add_def, ↑ereal.add]
| -∞ -∞ -∞                            := rfl

protected theorem zero_add : ∀ u : ereal, 0 + u = u
| (of_real x) := congr_arg of_real !real.zero_add
| ∞           := rfl
| -∞          := rfl

protected theorem add_zero : ∀ u : ereal, u + 0 = u :=
by intro u; rewrite [ereal.add_comm, ereal.zero_add]

protected theorem mul_comm : ∀ u v : ereal, u * v = v * u
| (of_real x) (of_real y) := congr_arg of_real !mul.comm
| ∞ a                     := by rewrite [*ereal.mul_def, ↑ereal.mul]
| a ∞                     := by rewrite [*ereal.mul_def, ↑ereal.mul]
| -∞ a                    := by rewrite [*ereal.mul_def, ↑ereal.mul]
| a -∞                    := by rewrite [*ereal.mul_def, ↑ereal.mul]

protected theorem neg_neg : ∀ u : ereal, -(-u) = u
| ∞           := rfl
| (of_real x) := by rewrite [*ereal.neg_def, ↑ereal.neg, ▸*,
                             (neg_neg' x)]
| -∞          := rfl

theorem neg_infty : -∞ = - ∞  := rfl

protected theorem neg_zero : -(0 : ereal) = 0 := rfl

theorem infty_mul_pos {x : real} (H : x > 0) : ∞ * x = ∞ :=
have H1 : x ≠ 0, from ne_of_gt H,
by+ rewrite [*ereal.mul_def, ↑ereal.mul, if_neg H1, if_pos H]

theorem pos_mul_infty {x : real} (H : x > 0) : x * ∞ = ∞ :=
by rewrite [ereal.mul_comm, infty_mul_pos H]

theorem infty_mul_neg {x : real} (H : x < 0) : ∞ * x = -∞ :=
have H1 : x ≠ 0, from ne_of_lt H,
have H2 : ¬ x > 0, from not_lt_of_gt H,
by+ rewrite [*ereal.mul_def, ↑ereal.mul, if_neg H1, if_neg H2]

theorem neg_mul_infty {x : real} (H : x < 0) : x * ∞ = -∞ :=
by rewrite [ereal.mul_comm, infty_mul_neg H]

private theorem infty_mul_zero : ∞ * 0 = 0 :=
by rewrite [*ereal.mul_def, ↑ereal.mul, ereal.zero_def, ↑blow_up, if_pos rfl]

private theorem zero_mul_infty : 0 * ∞ = 0 :=
by rewrite [ereal.mul_comm, infty_mul_zero]

theorem infty_mul_infty : ∞ * ∞ = ∞ := rfl

protected theorem neg_of_real (x : real) : -(of_real x) = of_real (-x) :=
rfl

private theorem aux1 : ∀ v : ereal, -∞ * v = -(∞ * v)
| ∞           := rfl
| (of_real x) := rfl
| -∞          := rfl

private theorem aux2 : ∀ u : ereal, -u * ∞ = -(u * ∞)
| ∞ := rfl
| (of_real x) := lt.by_cases
                   (assume H : x < 0,
                      by rewrite [ereal.neg_of_real, pos_mul_infty (neg_pos_of_neg H),
                                  neg_mul_infty H])
                   (assume H : x = 0,
                      by krewrite [H, ereal.neg_zero, *zero_mul_infty, ereal.neg_zero])
                   (assume H : x > 0,
                      by rewrite [ereal.neg_of_real, neg_mul_infty (neg_neg_of_pos H),
                                   pos_mul_infty H])
| -∞ := rfl

theorem ereal_neg_mul : ∀ u v : ereal, -u * v = -(u * v)
| ∞ v                     := aux1 v
| -∞ v                    := by rewrite [aux1, *ereal.neg_neg]
| u ∞                     := by rewrite [-aux2]
| u -∞                    := by rewrite [ereal.mul_comm, ereal.mul_comm u,
                                         *aux1, ereal.mul_comm, aux2, *ereal.neg_neg]
| (of_real x) (of_real y) := congr_arg of_real (eq.symm (neg_mul_eq_neg_mul x y))

theorem ereal_mul_neg (u v : ereal) : u * -v = -(u * v) :=
by rewrite [*ereal.mul_comm u, ereal_neg_mul]

protected theorem mul_zero : ∀ u : ereal, u * 0 = 0
| ∞ := infty_mul_zero
| -∞ := by rewrite [neg_infty, ereal_neg_mul, infty_mul_zero]
| (of_real x) := congr_arg of_real (mul_zero' x)

protected theorem zero_mul (u : ereal) : 0 * u = 0 :=
by rewrite [ereal.mul_comm, ereal.mul_zero]

private theorem aux3 : ∀ u, ∞ * (∞ * u) = ∞ * u
| ∞           := rfl
| (of_real x) := if H : x = 0 then
                   by rewrite [*ereal.mul_def, ↑ereal.mul, ↑blow_up, *H, *if_pos rfl]
                 else if H1 : x > 0 then
                   by rewrite [*ereal.mul_def, ↑ereal.mul, ↑blow_up, if_neg H, if_pos H1]
                 else
                   by rewrite [*ereal.mul_def, ↑ereal.mul, ↑blow_up, if_neg H, if_neg H1]
| -∞          := rfl

private theorem aux4 (x y : real) : ∞ * x * y = ∞ * (x * y) :=
lt.by_cases
  (assume H : x < 0,
    lt.by_cases
      (assume H1 : y < 0, by rewrite [infty_mul_neg H, neg_infty, ereal_neg_mul, -of_real_mul,
                                     infty_mul_neg H1, infty_mul_pos (mul_pos_of_neg_of_neg H H1)])
      (assume H1 : y = 0, by krewrite [H1, *ereal.mul_zero])
      (assume H1 : y > 0, by rewrite [infty_mul_neg H, neg_infty, *ereal_neg_mul, -of_real_mul,
                                     infty_mul_pos H1, infty_mul_neg (mul_neg_of_neg_of_pos H H1)]))
  (assume H : x = 0,
    by krewrite [H, ereal.mul_zero, *ereal.zero_mul, ereal.mul_zero])
  (assume H : x > 0,
    lt.by_cases
      (assume H1 : y < 0, by rewrite [infty_mul_pos H, infty_mul_neg H1, -of_real_mul,
                                     infty_mul_neg (mul_neg_of_pos_of_neg H H1)])
      (assume H1 : y = 0, by krewrite [H1, *ereal.mul_zero])
      (assume H1 : y > 0, by rewrite [infty_mul_pos H, infty_mul_pos H1, -of_real_mul,
                                     infty_mul_pos (mul_pos H H1)]))

private theorem aux5 : ∀ u v, ∞ * u * v = ∞ * (u * v)
| ∞ v  := by rewrite [infty_mul_infty, aux3]
| u ∞  := by rewrite [-*ereal.mul_comm ∞]
| -∞ v := by rewrite [neg_infty, *ereal_neg_mul, *ereal_mul_neg, ereal_neg_mul, infty_mul_infty,
                      aux3]
| u -∞ := by rewrite [neg_infty, *ereal_mul_neg]
| (of_real x) (of_real y) := aux4 x y

protected theorem mul_assoc : ∀ u v w : ereal, u * v * w = u * (v * w)
| ∞ v w  := !aux5
| u ∞ w  := by rewrite [-*ereal.mul_comm ∞, *ereal.mul_comm u, *aux5, *ereal.mul_comm u]
| u v ∞  := by rewrite [-*ereal.mul_comm ∞, *ereal.mul_comm u, aux5]
| -∞ v w := by rewrite [neg_infty, *ereal_neg_mul, aux5]
| u -∞ w := by rewrite [neg_infty, *ereal_mul_neg, *ereal_neg_mul, ereal_mul_neg, *ereal.mul_comm u,
                        *aux5, ereal.mul_comm u]
| u v -∞ := by rewrite [neg_infty, *ereal_mul_neg, *ereal.mul_comm u, -*ereal.mul_comm ∞, aux5]
| (of_real x) (of_real y) (of_real z) := congr_arg of_real (mul.assoc x y z)

protected theorem one_mul : ∀ u : ereal, of_real 1 * u = u
| (of_real x) := !real.one_mul ▸ rfl
| ∞           := pos_mul_infty zero_lt_one
| -∞          := by rewrite [neg_infty, ereal_mul_neg, pos_mul_infty zero_lt_one]

protected theorem mul_one (u : ereal) : u * 1 = u :=
by krewrite [ereal.mul_comm, ereal.one_mul]

/- instantiating arithmetic structures -/

-- Note that distributivity fails, e.g. ∞ ⬝ (-1 + 1) ≠ ∞ * -1 + ∞ * 1

protected definition comm_monoid [trans_instance] : comm_monoid ereal :=
⦃comm_monoid,
  mul       := ereal.mul,
  mul_assoc := ereal.mul_assoc,
  one       := 1,
  one_mul   := ereal.one_mul,
  mul_one   := ereal.mul_one,
  mul_comm  := ereal.mul_comm
⦄

protected definition add_comm_monoid [trans_instance] : add_comm_monoid ereal :=
⦃add_comm_monoid,
  add       := ereal.add,
  add_assoc := ereal.add_assoc,
  zero      := 0,
  zero_add  := ereal.zero_add,
  add_zero  := ereal.add_zero,
  add_comm  := ereal.add_comm
⦄

/- ordering on the ereals -/

protected definition le : ereal → ereal → Prop
| u ∞                     := true
| -∞ v                    := true
| (of_real x) (of_real y) := x ≤ y
| (of_real x) -∞          := false
| ∞ (of_real y)           := false
| ∞ -∞                    := false

definition ereal_has_le [instance] [priority ereal.prio] : has_le ereal :=
has_le.mk ereal.le

theorem of_real_le_of_real (x y : real) : of_real x ≤ of_real y ↔ x ≤ y :=
!iff.refl

theorem le_infty : ∀ u, u ≤ ∞
| ∞           := trivial
| (of_real x) := trivial
| -∞          := trivial

theorem neg_infty_le : ∀ v, -∞ ≤ v
| ∞           := trivial
| (of_real x) := trivial
| -∞          := trivial

protected theorem le_refl : ∀ u : ereal, u ≤ u
| ∞           := trivial
| -∞          := trivial
| (of_real x) := by rewrite [of_real_le_of_real]; apply le.refl

protected theorem le_trans : ∀ u v w : ereal, u ≤ v → v ≤ w → u ≤ w
| u v ∞            H1 H2 := !le_infty
| -∞ v w           H1 H2 := !neg_infty_le
| u ∞ (of_real x)  H1 H2 := false.elim H2
| ∞ (of_real x) v  H1 H2 := false.elim H1
| ∞ -∞ v           H1 H2 := false.elim H1
| u (of_real x) -∞ H1 H2 := false.elim H2
| u ∞ -∞           H1 H2 := false.elim H2
| (of_real x) -∞ v H1 H2 := false.elim H1
| (of_real x) (of_real y) (of_real z) H1 H2 :=
    iff.mpr !of_real_le_of_real
      (le.trans (iff.mp !of_real_le_of_real H1) (iff.mp !of_real_le_of_real H2))

protected theorem le_antisymm : ∀ u v : ereal, u ≤ v → v ≤ u → u = v
| ∞ ∞            H1 H2 := rfl
| ∞ (of_real x)  H1 H2 := false.elim H1
| ∞ -∞           H1 H2 := false.elim H1
| -∞ -∞          H1 H2 := rfl
| -∞ (of_real x) H1 H2 := false.elim H2
| -∞ ∞           H1 H2 := false.elim H2
| (of_real x) ∞  H1 H2 := false.elim H2
| (of_real x) -∞ H1 H2 := false.elim H1
| (of_real x) (of_real y) H1 H2 :=
    congr_arg of_real (le.antisymm (iff.mp !of_real_le_of_real H1) (iff.mp !of_real_le_of_real H2))

protected definition lt (x y : ereal) : Prop := x ≤ y ∧ x ≠ y

definition ereal_has_lt [instance] [priority ereal.prio] :
  has_lt ereal :=
has_lt.mk ereal.lt

protected theorem le_iff_lt_or_eq (u v : ereal) : u ≤ v ↔ u < v ∨ u = v :=
iff.intro
  (assume H : u ≤ v,
    by_cases
      (assume H1 : u = v, or.inr H1)
      (assume H1 : u ≠ v, or.inl (and.intro H H1)))
  (assume H : u < v ∨ u = v,
    or.elim H
      (assume H1 : u < v, and.left H1)
      (assume H1 : u = v, by rewrite H1; apply ereal.le_refl))

protected theorem le_total : ∀ u v : ereal, u ≤ v ∨ v ≤ u
| u ∞  := or.inl (le_infty u)
| u -∞ := or.inr (neg_infty_le u)
| ∞ v  := or.inr (le_infty v)
| -∞ v := or.inl (neg_infty_le v)
| (of_real x) (of_real y) :=
    or.elim (le.total x y)
      (assume H : x ≤[real] y, or.inl (iff.mpr !of_real_le_of_real H))
      (assume H : x ≥[real] y, or.inr (iff.mpr !of_real_le_of_real H))

theorem neg_infty_lt_infty : -∞ < ∞ := and.intro trivial (ne.symm infty_ne_neg_infty)

theorem neg_infty_lt_of_real (x : real) : -∞ < of_real x :=  and.intro trivial !neg_infty_ne_of_real

theorem of_real_lt_infty (x : real) : of_real x < ∞ := and.intro trivial (ne.symm !infty_ne_of_real)

protected definition decidable_linear_order [trans_instance] : decidable_linear_order ereal :=
⦃decidable_linear_order,
  le              := ereal.le,
  le_refl         := ereal.le_refl,
  le_trans        := ereal.le_trans,
  le_antisymm     := ereal.le_antisymm,
  lt              := ereal.lt,
  le_iff_lt_or_eq := ereal.le_iff_lt_or_eq,
  lt_irrefl       := abstract λ u H, and.right H rfl end,
  decidable_lt    := abstract λ u v : ereal, prop_decidable (u < v) end,
  le_total        := ereal.le_total
⦄

-- TODO : we still need some properties relating the arithmetic operations and the order.

end ereal
