import WadrayVerification.Load
import WadrayVerification.Aux
import CorelibVerification

def RAY_SCALE : ℕ := 1000000000000000000000000000

add

protected def add : Ray := r.toZMod + r'.toZMod

toZMod

protected def toZMod : UInt128 := r

Ray

def Ray : Type := UInt128

toRat

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

def WAD_SCALE : ℕ := 1000000000000000000

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

Wad.toRay

def Wad.toRay (w : Wad) : Ray := w.toZMod * (Ray.DIFF : UInt128)

DIFF

def DIFF : ℕ := 1000000000

toZMod

protected def toZMod : UInt128 := w

toRat

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

sub

protected def sub : Ray := r.toZMod - r'.toZMod

ofZMod

protected def ofZMod (a : UInt128) : Ray := a

def Wad.MAX_CONVERTIBLE_WAD : ℕ := 340282366920938463463374607431

protected def ofZMod (a : UInt128) : Wad := a

Wad

def Wad : Type := UInt128

toWad

def toWad : Wad := r.toZMod.ndiv DIFF

toRat

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

SignedWad

def SignedWad := UInt128 × (Unit ⊕ Unit)

SignedRay

def SignedRay := UInt128 × (Unit ⊕ Unit)

mul

protected def mul : Ray := (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

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

div

protected def div : Ray := (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

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

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD r < @ZMod.val U128_MOD r') :
    r.toRat < r'.toRat

theorem RAY_SCALE_rat_pos : 0 < (RAY_SCALE : ℚ)

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD w < @ZMod.val U128_MOD w') :
    w.toRat < w'.toRat

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

theorem toRat_le_toRat_of_val_le_val (h : @ZMod.val U128_MOD r ≤ @ZMod.val U128_MOD r') :
    r.toRat ≤ r'.toRat

theorem toRat_le_toRat_of_val_le_val (h : @ZMod.val U128_MOD w ≤ @ZMod.val U128_MOD w') :
    w.toRat ≤ w'.toRat

theorem zero_def : (0 : SignedWad) = (0, false.toSierraBool)

theorem zero_def : (0 : SignedRay) = (0, false.toSierraBool)

protected theorem mul_def :
    r * r' = (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

theorem mul_def :
    w₁ * w₂ = ⟨Ray.mul w₁.1 w₂.1, Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

protected theorem mul_def :
    w * w' = (w.toZMod.val * w'.toZMod.val / WAD_SCALE  : UInt128)

protected theorem div_def :
    r / r' = (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

theorem mul_def :
    w₁ * w₂ = ⟨Wad.mul (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

protected theorem div_def :
    w / w' = (w.toZMod.val * WAD_SCALE / w'.toZMod.val : UInt128)

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

theorem RAY_SCALE_pos : 0 < RAY_SCALE

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))⟩

theorem Bool.toSierraBool_def (b : Bool) : b.toSierraBool = if b then .inr () else .inl ()

import CorelibVerification

import WadrayVerification.Aux

import WadrayVerification.Load

open Sierra

:= rfl

aegis_spec "wadray::wadray::u128_rmul" :=
  fun _ _ a b _ ρ =>
  (a.val * b.val / Ray.RAY_SCALE < U128_MOD ∧ ρ = .inl (a.val * b.val / Ray.RAY_SCALE))
  ∨ (U128_MOD ≤ a.val * b.val / Ray.RAY_SCALE ∧ ρ.isRight)

aegis_prove "wadray::wadray::u128_rmul" :=
  fun _ _ a b _ ρ => by
  unfold «spec_wadray::wadray::u128_rmul»
  sierra_simp'
  rintro ⟨_,(⟨h₁,rfl⟩|⟨h₁,h₁'⟩),h₂⟩
  · simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
    rcases h₂ with (⟨_,_,h₂,h₃,h₄⟩|h₂)
    · injection h₂ with h₂; subst h₂
      rcases h₃ with (⟨_,_,rfl,h₅⟩|⟨h₃,_⟩)
      · rcases h₄ with (⟨⟨wₗ,wₕ⟩,v,h₄,h₆,h₇⟩|h₄)
        · injection h₄ with h₄; subst h₄
          simp [UInt256.val, UInt256.mul_def, ← ZMod.val_mul_val_eq_hmul] at h₅  -- TODO
          rw [UInt256.val_lt_U128_MOD_iff, UInt256.U128_MOD_le_val_iff] at h₆
          rcases h₆ with (⟨rfl,rfl⟩|⟨h₆,rfl⟩)
          · rw [ZMod.val_zero, mul_zero, zero_add] at h₅
            simp only [Sum.inl.injEq, exists_eq_left, Nat.cast_ofNat, Int.cast_ofNat,
              List.nil_append, false_and, exists_const, or_false] at h₇; cases h₇
            erw [← h₅]
            simp only [wₗ.val_lt, Sum.inl.injEq, true_and, Sum.isRight_inl, and_false, or_false]
            rw [@ZMod.natCast_zmod_val]
          · simp only [false_and, exists_const, Nat.cast_ofNat, Int.cast_ofNat, List.nil_append,
              true_and, false_or] at h₇; cases h₇
            rw [← UInt256.U128_MOD_le_val_iff] at h₆
            erw [← h₅]
            refine .inr ⟨h₆, ?_⟩
            simp
        · simp only [false_and, exists_false] at h₄
      · injection h₃ with h₃
        rw [ZMod.intCast_zmod_eq_zero_iff_dvd] at h₃
        norm_num[U128_MOD] at h₃
    · simp at h₂
  · simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
    right
    constructor
    · rw [Nat.le_div_iff_mul_le' Ray.RAY_SCALE_pos]
      apply le_trans _ h₁
      norm_num [U128_MOD, Ray.RAY_SCALE, U256_MOD]
    · aesop

aegis_spec "wadray::wadray::rmul" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) =>
  (a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE < U128_MOD ∧ ρ = .inl (a * b))
  ∨ (U128_MOD ≤ a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE ∧ ρ.isRight)

aegis_prove "wadray::wadray::rmul" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::rmul»
  sierra_simp'
  aesop

aegis_spec "wadray::wadray::WadSub::sub" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) =>
  (b.toRat ≤ a.toRat ∧ ρ = .inl (a - b))
  ∨ (a.toRat < b.toRat ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadSub::sub" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) => by
  unfold «spec_wadray::wadray::WadSub::sub»
  aesop (add safe forward [Wad.toRat_le_toRat_of_val_le_val, Wad.toRat_lt_toRat_of_val_lt_val])

aegis_spec "wadray::wadray::WadAdd::add" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) =>
  (a.toZMod.val + b.toZMod.val < U128_MOD ∧ ρ = .inl (a + b))
  ∨ (U128_MOD ≤ a.toZMod.val + b.toZMod.val ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadAdd::add" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) => by
  unfold «spec_wadray::wadray::WadAdd::add»
  aesop

aegis_spec "wadray::wadray::RaySub::sub" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) =>
  (b.toRat ≤ a.toRat ∧ ρ = .inl (a - b))
  ∨ (a.toRat < b.toRat ∧ ρ.isRight)

aegis_prove "wadray::wadray::RaySub::sub" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::RaySub::sub»
  aesop (add safe forward [Ray.toRat_le_toRat_of_val_le_val, Ray.toRat_lt_toRat_of_val_lt_val])

aegis_spec "wadray::wadray::RayAdd::add" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) =>
  (a.toZMod.val + b.toZMod.val < U128_MOD ∧ ρ = .inl (a + b))
  ∨ (U128_MOD ≤ a.toZMod.val + b.toZMod.val ∧ ρ.isRight)

aegis_prove "wadray::wadray::RayAdd::add" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::RayAdd::add»
  aesop

aegis_spec "wadray::wadray::WadZeroable::is_non_zero" :=
  fun _ (a : Wad) ρ =>
  ρ = Bool.toSierraBool (a.toRat ≠ 0)

aegis_prove "wadray::wadray::WadZeroable::is_non_zero" :=
  fun _ (a : Wad) ρ => by
  unfold «spec_wadray::wadray::WadZeroable::is_non_zero»
  aesop (add simp norm [Wad.toRat, Wad.toZMod, ZMod.cast_rat_eq_zero_iff])

aegis_spec "wadray::wadray::WadZeroable::is_zero" :=
  fun _ (a : Wad) ρ =>
  ρ = Bool.toSierraBool (a.toRat = 0)

aegis_prove "wadray::wadray::WadZeroable::is_zero" :=
  fun _ (a : Wad) ρ => by
  unfold «spec_wadray::wadray::WadZeroable::is_zero»
  aesop (add simp norm [Bool.toSierraBool_def, Wad.toRat, ZMod.cast_rat_eq_zero_iff, Wad.toZMod])

aegis_spec "wadray::wadray::RayZeroable::is_zero" :=
  fun _ (a : Ray) ρ =>
  ρ = Bool.toSierraBool (a.toRat = 0)

aegis_prove "wadray::wadray::RayZeroable::is_zero" :=
  fun _ (a : Ray) ρ => by
  unfold «spec_wadray::wadray::RayZeroable::is_zero»
  aesop (add simp norm [Bool.toSierraBool_def, Ray.toRat, ZMod.cast_rat_eq_zero_iff, Ray.toZMod])

aegis_spec "wadray::wadray::RayZeroable::is_non_zero" :=
  fun _ (a : Ray) ρ =>
  ρ = Bool.toSierraBool (a.toRat ≠ 0)

aegis_prove "wadray::wadray::RayZeroable::is_non_zero" :=
  fun _ (a : Ray) ρ => by
  unfold «spec_wadray::wadray::RayZeroable::is_non_zero»
  aesop (add simp norm [Ray.toRat, Ray.toZMod, ZMod.cast_rat_eq_zero_iff])

aegis_spec "wadray::wadray::BoundedWad::min" :=
  fun _ ρ =>
  ρ = 0

aegis_prove "wadray::wadray::BoundedWad::min" :=
  fun _ ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::BoundedRay::min" :=
  fun _ ρ =>
  ρ = 0

aegis_prove "wadray::wadray::BoundedRay::min" :=
  fun _ ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::BoundedWad::max" :=
  fun _ ρ =>
  ρ = U128_MOD - 1

aegis_prove "wadray::wadray::BoundedWad::max" :=
  fun _ ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::BoundedRay::max" :=
  fun _ ρ =>
  ρ = U128_MOD - 1

aegis_prove "wadray::wadray::BoundedRay::max" :=
  fun _ ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::WadAddEq::add_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) =>
  (a.toZMod.val + b.toZMod.val < U128_MOD ∧ ρ = .inl (a + b, ()))
  ∨ (U128_MOD ≤ a.toZMod.val + b.toZMod.val ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadAddEq::add_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) => by
  unfold «spec_wadray::wadray::WadAddEq::add_eq»
  aesop

aegis_spec "wadray::wadray::WadSubEq::sub_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) =>
  (b.toRat ≤ a.toRat ∧ ρ = .inl (a - b, ()))
  ∨ (a.toRat < b.toRat ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadSubEq::sub_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) => by
  unfold «spec_wadray::wadray::WadSubEq::sub_eq»
  aesop

aegis_spec "wadray::wadray::WadMulEq::mul_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) =>
  (a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE < U128_MOD ∧ ρ = .inl (a * b, ()))
  ∨ (U128_MOD ≤ a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadMulEq::mul_eq" :=
  fun _ _ (a b : Wad) _ (ρ : Wad × _ ⊕ _) => by
  unfold «spec_wadray::wadray::WadMulEq::mul_eq»
  aesop

aegis_spec "wadray::wadray::TIntoWad<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into" :=
  fun _ a (ρ : Wad) =>
  ρ = a

aegis_prove "wadray::wadray::TIntoWad<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into" :=
  fun _ a (ρ : Wad) => by
  unfold «spec_wadray::wadray::TIntoWad<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into»
  aesop

aegis_spec "wadray::wadray::TIntoRay<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into" :=
  fun _ a (ρ : Ray) =>
  ρ = a

aegis_prove "wadray::wadray::TIntoRay<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into" :=
  fun _ a (ρ : Ray) => by
  unfold «spec_wadray::wadray::TIntoRay<core::integer::u128, core::traits::TIntoT<core::integer::u128>>::into»
  aesop

aegis_spec "wadray::wadray::u128_wdiv" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) =>
  a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val < U128_MOD
    ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (a / b)
  ∨ (U128_MOD ≤ a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight

aegis_prove "wadray::wadray::u128_wdiv" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) => by
  sierra_simp'
  unfold «spec_wadray::wadray::u128_wdiv»
  rintro ⟨(x|x),h₁,h₂⟩
  · simp_all only [Nat.cast_ofNat, Int.cast_ofNat, Sum.inl.injEq, Sum.isRight_inl,
      Bool.false_eq_true, and_false, or_false, ne_eq, exists_and_left, List.nil_append,
      exists_and_right, Sum.exists, exists_eq_left, exists_const, false_and, and_true, false_or,
      true_and, Prod.exists, exists_eq_left', Prod.mk.injEq, Sum.isRight_inr, Sum.inr.injEq,
      UInt256.val_mul_of_low, ZMod.val_eq_zero]
    rcases h₁ with ⟨h₁,rfl⟩
    simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
    rcases h₂ with (⟨_,_,⟨h₂,h₄⟩,_,_,⟨rfl,rfl⟩,h₃⟩|⟨h₂,_,_,rfl⟩)
    · simp [h₄, UInt256.val] at h₃
      simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add,
        UInt256.mul_def, mul_zero, add_zero, zero_mul] at h₄
      rcases ρ with (ρ|ρ)
      · simp only [Sum.inl.injEq, Sum.isRight_inl, and_false, or_false] at h₃ ⊢
        rcases h₃ with ⟨_,⟨h₃,rfl⟩,rfl⟩
        refine ⟨?_,?_,?_⟩
        · rwa [h₄] at h₃
        · simp only [UInt256.zero_def] at h₂
          simp only [Wad.toZMod]
          apply Aesop.BuiltinRules.not_intro
          intro h
          subst h
          simp_all only [not_true]
        · simp only [Wad.div_def, Wad.WAD_SCALE]
          erw [← h₄]; clear h₄
          simp [Nat.eq_zero_of_mul_lt_right (Nat.lt_of_add_lt h₃)]
      · simp only [and_false, exists_false, Sum.inr.injEq, false_or] at h₃
        simp only [and_false, Sum.isRight_inr, and_true, false_or]
        left
        rcases h₃ with ⟨h₃,rfl⟩
        rwa [h₄] at h₃
    · simp only [and_false, Sum.isRight_inr, and_true, false_or]
      right
      injection h₂
  · simp_all only [Nat.cast_ofNat, Int.cast_ofNat, and_false, Sum.isRight_inr, and_true, false_or,
      ne_eq, exists_and_left, List.nil_append, exists_and_right, Sum.exists, Sum.inl.injEq, or_false,
      exists_eq_left, exists_const, false_and, true_and, Prod.exists, Sum.inr.injEq, ZMod.val_eq_zero]
    subst h₂
    simp only [and_false, Sum.isRight_inr, and_true, false_or]
    by_cases h₃ : b.toZMod.val = 0
    · right
      rwa [ZMod.val_eq_zero] at h₃
    · left
      conv => lhs; rw [← U256_MOD_div]
      trans; apply Nat.div_le_div_left (le_of_lt b.toZMod.val_lt) (Nat.pos_of_ne_zero h₃)
      simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
      apply Nat.div_le_div_right h₁

aegis_spec "wadray::wadray::u128_rdiv" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) =>
  (a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val < U128_MOD ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (a / b))
  ∨ ((U128_MOD ≤ a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight)

aegis_prove "wadray::wadray::u128_rdiv" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) => by
  sierra_simp'
  unfold «spec_wadray::wadray::u128_rdiv»
  rintro ⟨(x|x),h₁,h₂⟩
  · simp_all only [Nat.cast_ofNat, Int.cast_ofNat, Sum.inl.injEq, Sum.isRight_inl,
      Bool.false_eq_true, and_false, or_false, ne_eq, exists_and_left, List.nil_append,
      exists_and_right, Sum.exists, exists_eq_left, exists_const, false_and, and_true, false_or,
      true_and, Prod.exists, exists_eq_left', Prod.mk.injEq, Sum.isRight_inr, Sum.inr.injEq,
      UInt256.val_mul_of_low, ZMod.val_eq_zero]
    rcases h₁ with ⟨h₁,rfl⟩
    simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
    rcases h₂ with (⟨_,_,⟨h₂,h₄⟩,_,_,⟨rfl,rfl⟩,h₃⟩|⟨h₂,_,_,rfl⟩)
    · simp [h₄, UInt256.val] at h₃
      simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add,
        UInt256.mul_def, mul_zero, add_zero, zero_mul] at h₄
      rcases ρ with (ρ|ρ)
      · simp only [Sum.inl.injEq, Sum.isRight_inl, and_false, or_false] at h₃ ⊢
        rcases h₃ with ⟨_,⟨h₃,rfl⟩,rfl⟩
        refine ⟨?_,?_,?_⟩
        · rwa [h₄] at h₃
        · simp only [UInt256.zero_def] at h₂
          simp only [Ray.toZMod]
          apply Aesop.BuiltinRules.not_intro
          intro h
          subst h
          simp_all only [not_true]
        · simp only [Ray.div_def, Ray.RAY_SCALE]
          erw [← h₄]; clear h₄
          simp [Nat.eq_zero_of_mul_lt_right (Nat.lt_of_add_lt h₃)]
      · simp only [and_false, exists_false, Sum.inr.injEq, false_or] at h₃
        simp only [and_false, Sum.isRight_inr, and_true, false_or]
        left
        rcases h₃ with ⟨h₃,rfl⟩
        rwa [h₄] at h₃
    · simp only [and_false, Sum.isRight_inr, and_true, false_or]
      right
      injection h₂
  · simp_all only [Nat.cast_ofNat, Int.cast_ofNat, and_false, Sum.isRight_inr, and_true, false_or,
      ne_eq, exists_and_left, List.nil_append, exists_and_right, Sum.exists, Sum.inl.injEq, or_false,
      exists_eq_left, exists_const, false_and, true_and, Prod.exists, Sum.inr.injEq, ZMod.val_eq_zero]
    subst h₂
    simp only [and_false, Sum.isRight_inr, and_true, false_or]
    by_cases h₃ : b.toZMod.val = 0
    · right
      rwa [ZMod.val_eq_zero] at h₃
    · left
      conv => lhs; rw [← U256_MOD_div]
      trans; apply Nat.div_le_div_left (le_of_lt b.toZMod.val_lt) (Nat.pos_of_ne_zero h₃)
      simp only [UInt256.val, ZMod.val_zero, mul_zero, zero_add] at h₁
      apply Nat.div_le_div_right h₁

aegis_spec "wadray::wadray::wdiv" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) =>
  (a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val < U128_MOD ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (a / b))
  ∨ ((U128_MOD ≤ a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight)

aegis_prove "wadray::wadray::wdiv" :=
  fun _ _ (a b : Wad) _ (ρ : Wad ⊕ _) => by
  unfold «spec_wadray::wadray::wdiv»
  aesop

aegis_spec "wadray::wadray::WadDivEq::div_eq" :=
  fun _ _ (a b : Wad) _ (ρ : (Wad × _) ⊕ _) =>
  (a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val < U128_MOD ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (a / b, ()))
  ∨ ((U128_MOD ≤ a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight)

aegis_prove "wadray::wadray::WadDivEq::div_eq" :=
  fun _ _ (a b : Wad) _ (ρ : (Wad × _) ⊕ _) => by
  unfold «spec_wadray::wadray::WadDivEq::div_eq»
  aesop

aegis_spec "wadray::wadray::rdiv" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) =>
  (a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val < U128_MOD ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (a / b))
  ∨ ((U128_MOD ≤ a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight)

aegis_prove "wadray::wadray::rdiv" :=
  fun _ _ (a b : Ray) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::rdiv»
  aesop

aegis_spec "wadray::wadray::RayIntoWad::into" :=
  fun _ _ (a : Ray) _ ρ =>
  ρ = .inl a.toWad

aegis_prove "wadray::wadray::RayIntoWad::into" :=
  fun _ _ (a : Ray) _ ρ => by
  unfold «spec_wadray::wadray::RayIntoWad::into»
  rintro ⟨_,_,_,(h|h),h'⟩
  · rcases h' with (⟨rfl,rfl⟩|⟨rfl,rfl⟩)
    · rcases h with ⟨_,ρ',h₁,h₂⟩
      cases h₁
      congr
      simp only [Ray.toWad, ZMod.ndiv, U128_MOD]
      apply ZMod.val_injective
      exact h₂
    · simp at h
  · rcases h with ⟨h,-⟩
    rw [ZMod.intCast_zmod_eq_zero_iff_dvd] at h
    norm_num [U128_MOD] at h

aegis_spec "wadray::wadray::WadIntoU256::into" :=
  fun _ a ρ =>
  ρ = (a, 0)

aegis_prove "wadray::wadray::WadIntoU256::into" :=
  fun _ a ρ => by
  unfold «spec_wadray::wadray::WadIntoU256::into»
  rintro rfl
  rfl

aegis_spec "wadray::wadray::U256TryIntoWad::try_into" :=
  fun _ (a : UInt256) ρ =>
  (a.val < U128_MOD ∧ ρ = .inl a.1)
  ∨ (U128_MOD ≤ a.val ∧ ρ = .inr ())

aegis_prove "wadray::wadray::U256TryIntoWad::try_into" :=
  fun _ (a : UInt256) ρ => by
  unfold «spec_wadray::wadray::U256TryIntoWad::try_into»
  aesop

aegis_spec "wadray::wadray::TIntoRay<core::integer::u64, core::integer::UpcastableInto<core::integer::u64, core::integer::u128, core::integer::UpcastableU64U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoRay<core::integer::u64, core::integer::UpcastableInto<core::integer::u64, core::integer::u128, core::integer::UpcastableU64U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoRay<core::integer::u32, core::integer::UpcastableInto<core::integer::u32, core::integer::u128, core::integer::UpcastableU32U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoRay<core::integer::u32, core::integer::UpcastableInto<core::integer::u32, core::integer::u128, core::integer::UpcastableU32U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoRay<core::integer::u16, core::integer::UpcastableInto<core::integer::u16, core::integer::u128, core::integer::UpcastableU16U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoRay<core::integer::u16, core::integer::UpcastableInto<core::integer::u16, core::integer::u128, core::integer::UpcastableU16U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoRay<core::integer::u8, core::integer::UpcastableInto<core::integer::u8, core::integer::u128, core::integer::UpcastableU8U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoRay<core::integer::u8, core::integer::UpcastableInto<core::integer::u8, core::integer::u128, core::integer::UpcastableU8U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoWad<core::integer::u64, core::integer::UpcastableInto<core::integer::u64, core::integer::u128, core::integer::UpcastableU64U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoWad<core::integer::u64, core::integer::UpcastableInto<core::integer::u64, core::integer::u128, core::integer::UpcastableU64U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoWad<core::integer::u32, core::integer::UpcastableInto<core::integer::u32, core::integer::u128, core::integer::UpcastableU32U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoWad<core::integer::u32, core::integer::UpcastableInto<core::integer::u32, core::integer::u128, core::integer::UpcastableU32U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoWad<core::integer::u16, core::integer::UpcastableInto<core::integer::u16, core::integer::u128, core::integer::UpcastableU16U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoWad<core::integer::u16, core::integer::UpcastableInto<core::integer::u16, core::integer::u128, core::integer::UpcastableU16U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::TIntoWad<core::integer::u8, core::integer::UpcastableInto<core::integer::u8, core::integer::u128, core::integer::UpcastableU8U128>>::into" :=
  fun _ a ρ =>
  ρ = a.cast

aegis_prove "wadray::wadray::TIntoWad<core::integer::u8, core::integer::UpcastableInto<core::integer::u8, core::integer::u128, core::integer::UpcastableU8U128>>::into" :=
  fun _ a ρ => by
  rintro rfl
  rfl

aegis_spec "wadray::wadray::WadTryIntoRay::try_into" :=
  fun _ _ (a : Wad) _ (ρ : (Ray ⊕ _) ⊕ _) =>
  ρ = if (a.toZMod.val ≤ Wad.MAX_CONVERTIBLE_WAD) then .inl (.inl a.toRay)
      else .inl (.inr ())

aegis_prove "wadray::wadray::WadTryIntoRay::try_into" :=
  fun _ _ (a : Wad) _ (ρ : (Ray ⊕ _) ⊕ _) => by
  unfold «spec_wadray::wadray::WadTryIntoRay::try_into»
  have h₁ : ZMod.val (340282366920938463463374607431 : UInt128) = Wad.MAX_CONVERTIBLE_WAD := rfl
  have h₂ : ZMod.val (1000000000 : UInt128) = 1000000000 := rfl
  rintro ⟨_,_,_,(h|h)⟩
  · rcases h with ⟨h₃,(h₄|h₄),h₅⟩
    · aesop
    · rcases h₅ with (h₅|⟨rfl,rfl⟩)
      · aesop
      · simp only [Int.ofNat_eq_coe, Nat.cast_ofNat, Int.cast_ofNat, Sum.isRight_inr, and_true] at h₃ h₄
        rw [h₂] at h₄
        rw [h₁] at h₃
        replace h₄ := h₄.trans (Nat.mul_le_mul_right 1000000000 h₃)
        norm_num [U128_MOD, Wad.MAX_CONVERTIBLE_WAD] at h₄
  · aesop

aegis_spec "wadray::wadray::scale_u128_by_ray" :=
  fun _ _ a (b : Ray) _ ρ =>
  (a.val * b.toZMod.val / Ray.RAY_SCALE < U128_MOD ∧ ρ = .inl (a.val * b.toZMod.val / Ray.RAY_SCALE))
  ∨ (U128_MOD ≤ a.val * b.toZMod.val / Ray.RAY_SCALE ∧ ρ.isRight)

aegis_prove "wadray::wadray::scale_u128_by_ray" :=
  fun _ _ a (b : Ray) _ ρ => by
  unfold «spec_wadray::wadray::scale_u128_by_ray»
  aesop

aegis_spec "wadray::wadray::div_u128_by_ray" :=
  fun _ _ a (b : Ray) _ ρ =>
  a.val * Ray.RAY_SCALE / b.toZMod.val < U128_MOD ∧ b.toZMod.val ≠ 0
    ∧ ρ = .inl (a.val * Ray.RAY_SCALE / b.toZMod.val)
  ∨ (U128_MOD ≤ a.val * Ray.RAY_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight

aegis_prove "wadray::wadray::div_u128_by_ray" :=
  fun _ _ a (b : Ray) _ ρ => by
  unfold «spec_wadray::wadray::div_u128_by_ray»
  aesop

aegis_spec "wadray::wadray::wmul_wr" :=
  fun _ _ (a : Wad) (b : Ray) _ (ρ : Ray ⊕ _) =>
  a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE < U128_MOD
    ∧ ρ = .inl (Ray.ofZMod (a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE))
  ∨ U128_MOD ≤ a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE ∧ ρ.isRight

aegis_prove "wadray::wadray::wmul_wr" :=
  fun _ _ (a : Wad) (b : Ray) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::wmul_wr»
  aesop

aegis_spec "wadray::wadray::wmul_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Ray ⊕ _) =>
  a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE < U128_MOD
    ∧ ρ = .inl (Ray.ofZMod (a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE))
  ∨ U128_MOD ≤ a.toZMod.val * b.toZMod.val / Wad.WAD_SCALE ∧ ρ.isRight

aegis_prove "wadray::wadray::wmul_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::wmul_rw»
  aesop (add simp [mul_comm])

aegis_spec "wadray::wadray::rmul_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Wad ⊕ _) =>
  a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE < U128_MOD
    ∧ ρ = .inl (Ray.ofZMod (a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE))
  ∨ U128_MOD ≤ a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE ∧ ρ.isRight

aegis_prove "wadray::wadray::rmul_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::rmul_rw»
  aesop (add simp [mul_comm])

aegis_spec "wadray::wadray::rmul_wr" :=
  fun _ _ (a : Wad) (b : Ray) _ (ρ : Wad ⊕ _) =>
  a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE < U128_MOD
    ∧ ρ = .inl (Ray.ofZMod (a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE))
  ∨ U128_MOD ≤ a.toZMod.val * b.toZMod.val / Ray.RAY_SCALE ∧ ρ.isRight

aegis_prove "wadray::wadray::rmul_wr" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::rmul_wr»
  aesop (add simp [mul_comm])

aegis_spec "wadray::wadray::rdiv_wr" :=
  fun _ _ (a : Wad) (b : Ray) _ (ρ : Wad ⊕ _) =>
  a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val < U128_MOD
    ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (Wad.ofZMod (a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val))
  ∨ (U128_MOD ≤ a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight

aegis_prove "wadray::wadray::rdiv_wr" :=
  fun _ _ (a : Wad) (b : Ray) _ (ρ : Wad ⊕ _) => by
  unfold «spec_wadray::wadray::rdiv_wr»
  aesop

aegis_spec "wadray::wadray::wdiv_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Ray ⊕ _) =>
  a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val < U128_MOD
    ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (Ray.ofZMod (a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val))
  ∨ (U128_MOD ≤ a.toZMod.val * Wad.WAD_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight

aegis_prove "wadray::wadray::wdiv_rw" :=
  fun _ _ (a : Ray) (b : Wad) _ (ρ : Wad ⊕ _) => by
  unfold «spec_wadray::wadray::wdiv_rw»
  aesop

aegis_spec "wadray::wadray::rdiv_ww" :=
  fun _ _ (a b : Wad) _ (ρ : Ray ⊕ _) =>
  a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val < U128_MOD
    ∧ b.toZMod.val ≠ 0 ∧ ρ = .inl (Wad.ofZMod (a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val))
  ∨ (U128_MOD ≤ a.toZMod.val * Ray.RAY_SCALE / b.toZMod.val ∨ b.toZMod.val = 0) ∧ ρ.isRight

aegis_prove "wadray::wadray::rdiv_ww" :=
  fun _ _ (a b : Wad) _ (ρ : Ray ⊕ _) => by
  unfold «spec_wadray::wadray::rdiv_ww»
  aesop


import Aegis.Aux.Bool
import CorelibVerification.Aux.ZMod
import Aegis.Aux.ZMod.DivMod

Wad.div

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

instance : Div Wad := ⟨Wad.div⟩

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

Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Wad.toRat

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

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

theorem WAD_SCALE_rat_ne_zero : (WAD_SCALE : ℚ) ≠ 0

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

theorem toRat_div (h : w.toZMod.val * WAD_SCALE / w'.toZMod.val < U128_MOD)
    (h' : w'.toZMod.val ≠ 0) :
    |(w / w').toRat - w.toRat / w'.toRat| < 1 / WAD_SCALE :=

:= by
  have h'' : 0 < w'.toZMod.val := Nat.pos_of_ne_zero h'
  have h''' : (0 : ℚ) < w'.toZMod.val := Nat.cast_pos.mpr h''
  simp only [Wad.toRat, Wad.toZMod, Wad.div_def, ZMod.val_natCast] at *
  rw [Nat.mod_eq_of_lt h, Rat.nat_cast_div_eq, sub_div, Nat.cast_mul,
    div_div, mul_div_mul_right _ _ WAD_SCALE_rat_ne_zero,
    div_div_div_cancel_right _ WAD_SCALE_rat_ne_zero, sub_sub_cancel_left,
    abs_neg, abs_div, Nat.abs_cast, div_lt_div_right WAD_SCALE_rat_pos,
    abs_div, Nat.abs_cast, Nat.abs_cast, div_lt_one h''', Nat.cast_lt]
  apply Nat.mod_lt _ h''


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

SignedWad.toRat

theorem WAD_SCALE_pos : 0 < WAD_SCALE

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 mul : Wad := (w.toZMod.val * w'.toZMod.val / WAD_SCALE : UInt128)

end Wad

namespace Ray

variable (r r' : Ray)

end Ray

def SignedWad := UInt128 × (Unit ⊕ Unit)

namespace SignedWad

instance : Inhabited SignedWad := ⟨default, default⟩

variable (w w₁ w₂ : SignedWad)

def sign : ℤ := if SierraBool.toBool w.2 then -1 else 1

instance : Zero SignedWad := ⟨(0, false.toSierraBool)⟩

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

protected def mul : SignedWad :=
⟨Wad.mul (w₁.1 : Wad) (w₂.1 : Wad), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

instance : Mul SignedWad := ⟨SignedWad.mul⟩

theorem toRat_mul (h₁ : w₁.1.val * w₂.1.val / Wad.WAD_SCALE < U128_MOD ):
    |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₁
    <;> simp [mul_def, toRat, Wad.mul, 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, mul_div_assoc,
      sub_div, div_mul_eq_mul_div]
    <;> [rw [sub_right_comm, sub_self, zero_sub, abs_neg];
       rw [neg_sub, sub_add_cancel];
       rw [neg_sub, sub_add_cancel];
       rw [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, Nat.abs_cast,
      Nat.abs_cast, div_lt_one Wad.WAD_SCALE_rat_pos, Nat.cast_lt]
    <;> apply Nat.mod_lt _ Wad.WAD_SCALE_pos


Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedRay.toRat

theorem RAY_SCALE_pos : 0 < RAY_SCALE

theorem RAY_SCALE_rat_pos : 0 < (RAY_SCALE : ℚ)

theorem mul_def :
    w₁ * w₂ = ⟨Ray.mul w₁.1 w₂.1, Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

namespace Wad

variable (w w' : Wad)

end Wad

def Ray : Type := UInt128

namespace Ray

def RAY_SCALE : ℕ := 1000000000000000000000000000

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

protected def mul : Ray := (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

end Ray

namespace SignedWad

variable (w w₁ w₂ : SignedWad)

end SignedWad

def SignedRay := UInt128 × (Unit ⊕ Unit)

namespace SignedRay

instance : Inhabited SignedRay := ⟨default, default⟩

variable (w w₁ w₂ : SignedRay)

def sign : ℤ := if SierraBool.toBool w.2 then -1 else 1

instance : Zero SignedRay := ⟨(0, false.toSierraBool)⟩

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

protected def mul : SignedRay :=
⟨Ray.mul w₁.1 w₂.1, Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

instance : Mul SignedRay := ⟨SignedRay.mul⟩

theorem toRat_mul (h₁ : w₁.1.val * w₂.1.val / Ray.RAY_SCALE < U128_MOD ):
    |SignedRay.toRat (w₁ * w₂) - SignedRay.toRat w₁ * SignedRay.toRat w₂| < 1 / Ray.RAY_SCALE :=

:= by
  rcases w₁ with ⟨w₁, s₁⟩
  rcases w₂ with ⟨w₂, s₂⟩
  rcases s₁ with (⟨⟨⟩⟩|⟨⟨⟩⟩) <;> rcases s₂ with (⟨⟨⟩⟩|⟨⟨⟩⟩)
    <;> dsimp only at h₁
    <;> simp [mul_def, toRat, Ray.mul, Ray.toRat, Ray.toZMod, Nat.mod_eq_of_lt h₁, -one_div]
    <;> rw [Rat.nat_cast_div_eq, Nat.cast_mul, ZMod.natCast_val, ZMod.natCast_val, mul_div_assoc,
      sub_div, div_mul_eq_mul_div]
    <;> [rw [sub_right_comm, sub_self, zero_sub, abs_neg];
       rw [neg_sub, sub_add_cancel];
       rw [neg_sub, sub_add_cancel];
       rw [sub_right_comm, sub_self, zero_sub, abs_neg]]
    <;> rw [abs_div,
      Nat.abs_cast, div_lt_div_right Ray.RAY_SCALE_rat_pos, abs_div, Nat.abs_cast,
      Nat.abs_cast, div_lt_one Ray.RAY_SCALE_rat_pos, Nat.cast_lt]
    <;> apply Nat.mod_lt _ Ray.RAY_SCALE_pos


Wad.mul

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

instance : Mul Wad := ⟨Wad.mul⟩

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

Wad

def Wad : Type := UInt128

def WAD_SCALE : ℕ := 1000000000000000000

Wad.toZMod

protected def toZMod : UInt128 := w

Wad.toRat

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

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

theorem WAD_SCALE_pos : 0 < WAD_SCALE

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

protected theorem mul_def :
    w * w' = (w.toZMod.val * w'.toZMod.val / WAD_SCALE  : UInt128)

theorem toRat_mul (h : w.toZMod.val * w'.toZMod.val / WAD_SCALE < U128_MOD) :
    |(w * w').toRat - w.toRat * w'.toRat| < 1 / WAD_SCALE :=

:= by
  simp only [Wad.toRat, Wad.toZMod, Wad.mul_def, Int.natCast_natAbs] at *
  simp only [ZMod.val_natCast, ZMod.natCast_val] at *
  rw [Nat.mod_eq_of_lt h, div_mul_div_comm, ← div_div, ← sub_div, abs_div,
    Nat.abs_cast, div_lt_div_right WAD_SCALE_rat_pos, Rat.nat_cast_div_eq]
  simp only [Nat.cast_mul, ZMod.natCast_val, sub_sub_cancel_left, abs_neg]
  rw [abs_div, Nat.abs_cast, Nat.abs_cast, div_lt_one WAD_SCALE_rat_pos,
    Nat.cast_lt]
  apply Nat.mod_lt _ WAD_SCALE_pos


Ray.mul

protected def Ray.mul : Ray := (r.toZMod.val * r'.toZMod.val / RAY_SCALE : UInt128)

instance : Mul Ray := ⟨Ray.mul⟩

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

theorem RAY_SCALE_pos : 0 < RAY_SCALE

protected theorem mul_def :
    r * r' = (r.toZMod.val * r'.toZMod.val / RAY_SCALE  : UInt128)

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

namespace Wad

variable (w w' : Wad)

end Wad

def Ray : Type := UInt128

namespace Ray

def RAY_SCALE : ℕ := 1000000000000000000000000000

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

theorem toRat_mul (h : r.toZMod.val * r'.toZMod.val / RAY_SCALE < U128_MOD) :
    |(r * r').toRat - r.toRat * r'.toRat| < 1 / RAY_SCALE :=

:= by
  simp only [Ray.toRat, Ray.toZMod, Ray.mul_def] at *
  simp only [ZMod.val_natCast]
  rw [Nat.mod_eq_of_lt h, div_mul_div_comm, ← div_div, ← sub_div, abs_div,
    Nat.abs_cast, div_lt_div_right RAY_SCALE_rat_pos, Rat.nat_cast_div_eq]
  simp only [Nat.cast_mul, ZMod.val_natCast, sub_sub_cancel_left, abs_neg]
  rw [abs_div, Nat.abs_cast, Nat.abs_cast, div_lt_one RAY_SCALE_rat_pos,
    Nat.cast_lt]
  apply Nat.mod_lt _ RAY_SCALE_pos


SignedWad.div

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))⟩

instance : Div SignedWad := ⟨SignedWad.div⟩

Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

SignedWad.toRat

theorem WAD_SCALE_rat_ne_zero : (WAD_SCALE : ℚ) ≠ 0

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))⟩

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

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₂)


SignedRay.div

protected def SignedRay.div : SignedRay :=
⟨Ray.div (w₁.1 : Ray) (w₂.1 : Ray), Bool.toSierraBool (Bool.xor (SierraBool.toBool w₁.2) (SierraBool.toBool w₂.2))⟩

instance : Div SignedRay := ⟨SignedRay.div⟩

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

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedRay.toRat

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

theorem RAY_SCALE_rat_pos : 0 < (RAY_SCALE : ℚ)

theorem RAY_SCALE_rat_ne_zero : (RAY_SCALE : ℚ) ≠ 0

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

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

namespace Wad

variable (w w' : Wad)

end Wad

def Ray : Type := UInt128

namespace Ray

def RAY_SCALE : ℕ := 1000000000000000000000000000

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

protected def div : Ray := (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

end Ray

namespace SignedWad

variable (w w₁ w₂ : SignedWad)

end SignedWad

def SignedRay := UInt128 × (Unit ⊕ Unit)

namespace SignedRay

variable (w w₁ w₂ : SignedRay)

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

theorem toRat_div (h₁ : w₁.1.val * Ray.RAY_SCALE / w₂.1.val < U128_MOD)
    (h₂ : w₂.1.val ≠ 0):
    |SignedRay.toRat (w₁ / w₂) - SignedRay.toRat w₁ / SignedRay.toRat w₂| < 1 / Ray.RAY_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, Ray.div, Ray.toRat, Ray.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 _ Ray.RAY_SCALE_rat_ne_zero, div_div,
         mul_div_mul_right _ _ Ray.RAY_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 _ Ray.RAY_SCALE_rat_ne_zero,
         div_div (w₁.cast * _), mul_div_mul_right _ _ Ray.RAY_SCALE_rat_ne_zero, sub_add_cancel];
       rw [neg_sub, neg_div, sub_neg_eq_add, div_div_div_cancel_right _ Ray.RAY_SCALE_rat_ne_zero,
         div_div (w₁.cast * _), mul_div_mul_right _ _ Ray.RAY_SCALE_rat_ne_zero, sub_add_cancel];
       rw [div_div_div_cancel_right _ Ray.RAY_SCALE_rat_ne_zero, div_div,
         mul_div_mul_right _ _ Ray.RAY_SCALE_rat_ne_zero,
         sub_right_comm, sub_self, zero_sub, abs_neg]]
    <;> rw [abs_div,
      Nat.abs_cast, div_lt_div_right Ray.RAY_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₂)


Wad

def Wad : Type := UInt128

def WAD_SCALE : ℕ := 1000000000000000000

Wad.toZMod

protected def toZMod : UInt128 := w

Wad.toRat

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

theorem ZMod.cast_rat_nonneg [NeZero n] (a : ZMod n) : 0 ≤ (a.cast : ℚ)

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

:= by
  simp [Wad.toRat]
  rw [le_div_iff WAD_SCALE_rat_pos, zero_mul]
  exact ZMod.cast_rat_nonneg (Wad.toZMod w)


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedWad.toRat

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD w < @ZMod.val U128_MOD w') :
    w.toRat < w'.toRat

theorem toRat_injective : Function.Injective Wad.toRat

theorem toRat_nonneg : 0 ≤ w.toRat

@[simp]
theorem toRat_zero : (0 : Wad).toRat = 0

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

end Wad

def Ray : Type := UInt128

namespace Ray

variable (r r' : Ray)

protected def toZMod : UInt128 := r

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

theorem val_eq_of_toRat_eq : w₁.toRat = w₂.toRat → w₁.1 = w₂.1 :=

:= by
  rcases w₁ with ⟨w₁, s₁⟩
  rcases w₂ with ⟨w₂, s₂⟩
  intro h
  cases s₁ <;> cases s₂
  · have := Wad.toRat_injective h
    cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inl, ite_false, SierraBool_toBool_inr, ite_true] at *
    have h' : Wad.toRat w₁ = 0 := by
      apply le_antisymm _ _
      · rw [h, Left.neg_nonpos_iff]
        apply Wad.toRat_nonneg
      · apply Wad.toRat_nonneg
    rw [h', zero_eq_neg, ← Wad.toRat_zero] at h
    have := Wad.toRat_injective h; cases this
    rw [← Wad.toRat_zero] at h'
    have := Wad.toRat_injective h'; cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inr, ite_true, SierraBool_toBool_inl, ite_false] at *
    have h' : Wad.toRat w₂ = 0 := by
      apply le_antisymm _ _
      · rw [← h, Left.neg_nonpos_iff]
        apply Wad.toRat_nonneg
      · apply Wad.toRat_nonneg
    rw [h', eq_comm, zero_eq_neg, ← Wad.toRat_zero] at h
    have := Wad.toRat_injective h; cases this
    rw [← Wad.toRat_zero] at h'
    have := Wad.toRat_injective h'; cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inr, ite_true, neg_inj] at h
    have := Wad.toRat_injective h
    cases this
    rfl


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

Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedRay.toRat

theorem RAY_SCALE_rat_pos : 0 < (RAY_SCALE : ℚ)

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD r < @ZMod.val U128_MOD r') :
    r.toRat < r'.toRat

theorem toRat_injective : Function.Injective Ray.toRat

theorem toRat_nonneg : 0 ≤ r.toRat

 @[simp]
theorem toRat_zero : (0 : Ray).toRat = 0

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

def Wad : Type := UInt128

namespace Wad

variable (w w' : Wad)

protected def toZMod : UInt128 := w

end Wad

def Ray : Type := UInt128

namespace Ray

def RAY_SCALE : ℕ := 1000000000000000000000000000

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

end Ray

namespace SignedWad

variable (w w₁ w₂ : SignedWad)

end SignedWad

def SignedRay := UInt128 × (Unit ⊕ Unit)

namespace SignedRay

variable (w w₁ w₂ : SignedRay)

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

:= by
  rcases w₁ with ⟨w₁, s₁⟩
  rcases w₂ with ⟨w₂, s₂⟩
  intro h
  cases s₁ <;> cases s₂
  · have := Ray.toRat_injective h
    cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inl, ite_false, SierraBool_toBool_inr, ite_true] at *
    have h' : Ray.toRat w₁ = 0 := by
      apply le_antisymm _ _
      · rw [h, Left.neg_nonpos_iff]
        apply Ray.toRat_nonneg
      · apply Ray.toRat_nonneg
    rw [h', zero_eq_neg, ← Ray.toRat_zero] at h
    have := Ray.toRat_injective h; cases this
    rw [← Ray.toRat_zero] at h'
    have := Ray.toRat_injective h'; cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inr, ite_true, SierraBool_toBool_inl, ite_false] at *
    have h' : Ray.toRat w₂ = 0 := by
      apply le_antisymm _ _
      · rw [← h, Left.neg_nonpos_iff]
        apply Ray.toRat_nonneg
      · apply Ray.toRat_nonneg
    rw [h', eq_comm, zero_eq_neg, ← Ray.toRat_zero] at h
    have := Ray.toRat_injective h; cases this
    rw [← Ray.toRat_zero] at h'
    have := Ray.toRat_injective h'; cases this
    rfl
  · simp only [toRat, SierraBool_toBool_inr, ite_true, neg_inj] at h
    have := Ray.toRat_injective h
    cases this
    rfl


Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

:= by
  simp [Ray.toRat]
  rw [le_div_iff RAY_SCALE_rat_pos, zero_mul]
  exact ZMod.cast_rat_nonneg (Ray.toZMod r)


Ray.div

protected def Ray.div : Ray := (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

instance : Div Ray := ⟨Ray.div⟩

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

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

theorem RAY_SCALE_rat_ne_zero : (RAY_SCALE : ℚ) ≠ 0

protected theorem div_def :
    r / r' = (r.toZMod.val * RAY_SCALE / r'.toZMod.val : UInt128)

theorem toRat_div (h : r.toZMod.val * RAY_SCALE / r'.toZMod.val < U128_MOD)
    (h' : r'.toZMod.val ≠ 0) :
    |(r / r').toRat - r.toRat / r'.toRat| < 1 / RAY_SCALE :=

:= by
  have h'' : 0 < r'.toZMod.val := Nat.pos_of_ne_zero h'
  have h''' : (0 : ℚ) < r'.toZMod.val := Nat.cast_pos.mpr h''
  simp only [Ray.toRat, Ray.toZMod, Ray.div_def, ZMod.val_natCast] at *
  rw [Nat.mod_eq_of_lt h, Rat.nat_cast_div_eq, sub_div, Nat.cast_mul,
    div_div, mul_div_mul_right _ _ RAY_SCALE_rat_ne_zero,
    div_div_div_cancel_right _ RAY_SCALE_rat_ne_zero, sub_sub_cancel_left,
    abs_neg, abs_div, Nat.abs_cast, div_lt_div_right RAY_SCALE_rat_pos,
    abs_div, Nat.abs_cast, Nat.abs_cast, div_lt_one h''', Nat.cast_lt]
  apply Nat.mod_lt _ h''


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedWad.toRat

theorem WAD_SCALE_pos : 0 < WAD_SCALE

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

end Wad

def Ray : Type := UInt128

namespace Ray

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def add : Ray := r.toZMod + r'.toZMod

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

theorem toRat_eq_zero_iff : w.toRat = 0 ↔ w.1 = 0 :=

:= by
  have := Wad.WAD_SCALE_pos
  rcases w with ⟨w, (s|s)⟩ <;> cases s
  <;> simp only [SignedWad.toRat, Wad.toRat, Wad.toZMod]
  <;> aesop (add simp ZMod.cast_rat_eq_zero_iff)


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

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

SignedRay.toRat

theorem RAY_SCALE_pos : 0 < RAY_SCALE

import CorelibVerification.Aux.ZMod

import Aegis.Aux.Bool

import Aegis.Aux.ZMod.DivMod

open Sierra

namespace Wad

variable (w w' : Wad)

end Wad

def Ray : Type := UInt128

namespace Ray

def RAY_SCALE : ℕ := 1000000000000000000000000000

variable (r r' : Ray)

protected def toZMod : UInt128 := r

protected def toRat : ℚ := r.toZMod.val / RAY_SCALE

protected def add : Ray := r.toZMod + r'.toZMod

end Ray

namespace SignedWad

variable (w w₁ w₂ : SignedWad)

end SignedWad

def SignedRay := UInt128 × (Unit ⊕ Unit)

namespace SignedRay

variable (w w₁ w₂ : SignedRay)

def toRat : ℚ := if SierraBool.toBool w.2 then -(Ray.toRat w.1) else Ray.toRat w.1

:= by
  have := Ray.RAY_SCALE_pos
  rcases w with ⟨w, (s|s)⟩ <;> cases s
  <;> simp only [SignedRay.toRat, Ray.toRat, Ray.toZMod]
  <;> aesop (add simp ZMod.cast_rat_eq_zero_iff)


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

theorem WAD_SCALE_rat_pos : 0 < (WAD_SCALE : ℚ)

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD w < @ZMod.val U128_MOD w') :
    w.toRat < w'.toRat

theorem toRat_injective : Function.Injective Wad.toRat

@[simp]
theorem toRat_zero : (0 : Wad).toRat = 0

theorem val_eq_zero_of_toRat_neg (x : Wad) (p q : Unit)
  (h : SignedWad.toRat ((x, .inl p) : SignedWad) = SignedWad.toRat ((x, .inr q) : SignedWad)) :
    x = 0 :=

:= by
  simp only [toRat, SierraBool_toBool_inl, ite_false, SierraBool_toBool_inr, ite_true,
    eq_neg_self_iff] at h
  rw [← Wad.toRat_zero] at h
  exact Wad.toRat_injective h


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

Ray

def Ray : Type := UInt128

Ray.toZMod

protected def toZMod : UInt128 := r

theorem val_eq_zero_of_toRat_neg' (x : Wad) (p q : Unit)
  (h : SignedWad.toRat ((x, .inr p) : SignedWad) = SignedWad.toRat ((x, .inl q) : SignedWad)) :
    x = 0 :=

:= by
  simp only [toRat, SierraBool_toBool_inr, ite_true, SierraBool_toBool_inl, ite_false,
    neg_eq_self_iff] at h
  rw [← Wad.toRat_zero] at h
  exact Wad.toRat_injective h


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

theorem toRat_le_toRat_of_val_le_val (h : @ZMod.val U128_MOD w ≤ @ZMod.val U128_MOD w') :
    w.toRat ≤ w'.toRat :=

:= by
  simp only [Wad.toRat]
  apply div_le_div
  · exact Nat.cast_nonneg (ZMod.val (Wad.toZMod w'))
  · rwa [Nat.cast_le]
  · exact WAD_SCALE_rat_pos
  · apply le_of_eq; rfl


Wad

def Wad : Type := UInt128

Wad.toZMod

protected def toZMod : UInt128 := w

theorem toRat_lt_toRat_of_val_lt_val (h : @ZMod.val U128_MOD w < @ZMod.val U128_MOD w') :
    w.toRat < w'.toRat :=

:= by
  simp only [Wad.toRat]
  apply div_lt_div
  · rwa [Nat.cast_lt, Wad.toZMod, Wad.toZMod]
  · apply le_of_eq; rfl
  · apply Nat.cast_nonneg
  · exact WAD_SCALE_rat_pos