-- Blase/Blase/Fast/BitStream.lean

import Mathlib.Tactic.NormNum

import Mathlib.Logic.Function.Iterate

section UpStream

namespace Int

end Int

end UpStream

def BitStream : Type := Nat → Bool

namespace BitStream

section Basic

section Lemmas

end Lemmas

end Basic

section OfNat

end OfNat

section ToBitVec

abbrev ofBitVecZext {w} (x : BitVec w) : BitStream :=
  fun i => x.getLsbD i

section Lemmas

end Lemmas

end ToBitVec

section BitwiseOps

section Lemmas

variable {w : Nat}

variable (x y : BitStream) (i : Nat)

variable (x y : BitVec (w+1))

end Lemmas

end BitwiseOps

section Scan

end Scan

section FindIndex

section Arith

def addAux (x y : BitStream) (i : Nat) :  Bool × Bool :=
  let carryIn : Bool := match i with
    | 0 => false
    | i + 1 => (addAux x y i).2
  Prod.swap (BitVec.adcb (x i) (y i) carryIn)

def add (x y : BitStream) : BitStream :=
  fun n => (addAux x y n).1

abbrev zero   : BitStream := fun _ => false

section Lemmas

variable {w : Nat} {x y : BitVec w} {a b a' b' : BitStream}

local infix:20 " ≈ʷ " => EqualUpTo w

-- Repository definitions used:
content: ...

-- 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 BitStream : Type := Nat → Bool

content: abbrev ofBitVecZext {w} (x : BitVec w) : BitStream :=
  fun i => x.getLsbD i

content: def addAux (x y : BitStream) (i : Nat) :  Bool × Bool :=
  let carryIn : Bool := match i with
    | 0 => false
    | i + 1 => (addAux x y i).2
  Prod.swap (BitVec.adcb (x i) (y i) carryIn)

content: def add (x y : BitStream) : BitStream :=
  fun n => (addAux x y n).1

content: abbrev zero   : BitStream := fun _ => false

-- Local lemmas used:
content: @[simp]
theorem ofBitVecZext_eq_getLsbD (x : BitVec w) (i : Nat) :
  ofBitVecZext x i = x.getLsbD i

content: @[simp] theorem addAux_zero (x y : BitStream) : (x.addAux y 0) =
  ((x 0) ^^ (y 0), (x 0) && (y 0))

content: private theorem two_le_add_iff_odd_and_odd (n m : Nat) :
    2 ≤ n % 2 + m % 2 ↔ n % 2 = 1 ∧ m % 2 = 1

content: private theorem add_odd_iff_neq (n m : Nat) :
    (n + m) % 2 = 1 ↔ (n % 2 = 1) ≠ (m % 2 = 1)

-- Target theorem and proof:
theorem ofBitVecZext_add_EqualUpTo :
    ofBitVecZext (x + y) ≈ʷ (ofBitVecZext x) + (ofBitVecZext y)
:= by
  intros n a
  have add_lemma : ⟨(x + y).getLsbD n, BitVec.carry (n + 1) x y false ⟩ = (ofBitVecZext x).addAux (ofBitVecZext y) n := by
    induction n
    case zero =>
      simp only [zero_add, addAux_zero, ofBitVecZext_eq_getLsbD, Prod.mk.injEq]
      simp only [BitVec.getLsbD, BitVec.toNat_add, Nat.testBit_zero, Nat.mod_two_pos_mod_two_eq_one,
        a, true_and]
      simp only [add_odd_iff_neq, ne_eq, eq_iff_iff, decide_not, Bool.decide_iff_dist,
        Bool.not_eq_eq_eq_not, BitVec.carry, pow_one, Bool.toNat_false, add_zero, ge_iff_le,
        two_le_add_iff_odd_and_odd, Bool.decide_and, and_true]
      bv_decide
    case succ i ih =>
      simp [addAux, ← ih (by omega), BitVec.adcb, a, BitVec.carry_succ, BitVec.getElem_add];
  simp [HAdd.hAdd, Add.add, BitStream.add, ← add_lemma, a, -BitVec.add_eq, -Nat.add_eq]