-- WadrayVerification/Aux.lean

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

def Wad : Type := UInt128

namespace Wad

def WAD_SCALE : ℕ := 1000000000000000000

variable (w w' : Wad)

protected def toZMod : UInt128 := w

protected def toRat : ℚ := w.toZMod.val / WAD_SCALE

protected def div : Wad := (w.toZMod.val * WAD_SCALE / w'.toZMod.val : UInt128)

end Wad

namespace Ray

variable (r r' : Ray)

end Ray

def SignedWad := UInt128 × (Unit ⊕ Unit)

namespace SignedWad

variable (w w₁ w₂ : SignedWad)

def toRat : ℚ := if w.2 then -(Wad.toRat w.1) else Wad.toRat w.1

-- Repository definitions used:
content: protected def SignedWad.div : SignedWad :=
⟨Wad.div (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

content: instance : Div SignedWad := ⟨SignedWad.div⟩

-- Repository lemmas used:
content: theorem List.getElem_append_left{α : Type u_1} {l₁ l₂ : List α} {i : Nat} (hn : i < l₁.length) :\n(l₁ ++ l₂)[i] = l₁[i]

-- Local definitions used:
content: def Wad : Type := UInt128

content: def WAD_SCALE : ℕ := 1000000000000000000

content: protected def toZMod : UInt128 := w

content: protected def toRat : ℚ := w.toZMod.val / WAD_SCALE

content: protected def div : Wad := (w.toZMod.val * WAD_SCALE / w'.toZMod.val : UInt128)

content: def SignedWad := UInt128 × (Unit ⊕ Unit)

content: def toRat : ℚ := if w.2 then -(Wad.toRat w.1) else Wad.toRat w.1

-- Local lemmas used:
content: theorem Rat.nat_cast_div_eq {a b : ℕ} :
    ↑(a / b) = (a : ℚ) / (b : ℚ) - ↑(a % b) / (b : ℚ)

content: theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

content: theorem WAD_SCALE_rat_ne_zero : (WAD_SCALE : ℚ) ≠ 0

content: theorem div_def :
    w₁ / w₂ = ⟨Wad.div (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

-- Target theorem and proof:
theorem toRat_div (h₁ : w₁.1.val * Wad.WAD_SCALE / w₂.1.val < U128_MOD)
    (h₂ : w₂.1.val ≠ 0):
    |SignedWad.toRat (w₁ / w₂) - SignedWad.toRat w₁ / SignedWad.toRat w₂| < 1 / Wad.WAD_SCALE
:= by
  rcases w₁ with ⟨w₁, s₁⟩
  rcases w₂ with ⟨w₂, s₂⟩
  rcases s₁ with (⟨⟨⟩⟩|⟨⟨⟩⟩) <;> rcases s₂ with (⟨⟨⟩⟩|⟨⟨⟩⟩)
    <;> dsimp only at h₁ h₂
    <;> simp [div_def, toRat, Wad.div, Wad.toRat, Wad.toZMod, Nat.mod_eq_of_lt h₁, -one_div]
    <;> rw [Rat.nat_cast_div_eq, Nat.cast_mul, ZMod.natCast_val, ZMod.natCast_val, sub_div]
    <;> [rw [div_div_div_cancel_right _ Wad.WAD_SCALE_rat_ne_zero, div_div,
         mul_div_mul_right _ _ Wad.WAD_SCALE_rat_ne_zero,
         sub_right_comm, sub_self, zero_sub, abs_neg];
       rw [neg_sub, div_neg, sub_neg_eq_add, div_div_div_cancel_right _ Wad.WAD_SCALE_rat_ne_zero,
         div_div (w₁.cast * _), mul_div_mul_right _ _ Wad.WAD_SCALE_rat_ne_zero, sub_add_cancel];
       rw [neg_sub, neg_div, sub_neg_eq_add, div_div_div_cancel_right _ Wad.WAD_SCALE_rat_ne_zero,
         div_div (w₁.cast * _), mul_div_mul_right _ _ Wad.WAD_SCALE_rat_ne_zero, sub_add_cancel];
       rw [div_div_div_cancel_right _ Wad.WAD_SCALE_rat_ne_zero, div_div,
         mul_div_mul_right _ _ Wad.WAD_SCALE_rat_ne_zero,
         sub_right_comm, sub_self, zero_sub, abs_neg]]
    <;> rw [abs_div,
      Nat.abs_cast, div_lt_div_right Wad.WAD_SCALE_rat_pos, abs_div, ← ZMod.natCast_val, Nat.abs_cast,
      Nat.abs_cast, div_lt_one (by rw [← Nat.cast_zero, Nat.cast_lt]; apply Nat.pos_of_ne_zero h₂), Nat.cast_lt]
    <;> apply Nat.mod_lt _ (Nat.pos_of_ne_zero h₂)