-- 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 ofBitVecSext {w} (x : BitVec w) : BitStream :=
  fun i => if i < w then x.getLsbD i else x.msb

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 ofBitVecSext {w} (x : BitVec w) : BitStream :=
  fun i => if i < w then x.getLsbD i else x.msb

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: 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 ofBitVec_add : ofBitVecSext (x + y) ≈ʷ (ofBitVecSext x) + (ofBitVecSext y)
:= by
  intros n a
  have add_lemma : ⟨(x + y).getLsbD n, BitVec.carry (n + 1) x y false ⟩ = (ofBitVecSext x).addAux (ofBitVecSext y) n := by
    induction n
    case zero =>
      simp [addAux, BitVec.adcb, BitVec.carry, BitVec.getLsbD, a,
        two_le_add_iff_odd_and_odd, add_odd_iff_neq]
      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]