import Base
import Nat
import UIntDefs
import UIntToFrom
import UIntLess

theorem toNat_add: all x:UInt, y:UInt.
  toNat(x + y) = toNat(x) + toNat(y)
proof
  induction UInt
  case bzero {
    arbitrary y:UInt
    evaluate
  }
  case dub_inc(x') assume IH {
    arbitrary y:UInt
    expand operator+
    switch y {
      case bzero { evaluate }
      case dub_inc(y') {
        expand toNat
        replace toNat_inc | nat_suc_one_add | nat_suc_one_add | nat_suc_one_add
        show ℕ4 + ℕ2 * toNat(x' + y') = ℕ2 + ℕ2 * toNat(x') + ℕ2 + ℕ2 * toNat(y')
        replace IH[y'] | add_commute[ℕ2 * toNat(x'), ℕ2].
      }
      case inc_dub(y') {
        expand inc | toNat
        replace toNat_inc | nat_suc_one_add | nat_suc_one_add
        replace IH[y'] | add_commute[ℕ2 * toNat(x'), ℕ1].
      }
    }
  }
  case inc_dub(x') assume IH {
    arbitrary y:UInt
    switch y {
      case bzero { evaluate }
      case dub_inc(y') {
        expand operator+ | toNat | inc
        replace toNat_inc | nat_suc_one_add | nat_suc_one_add
        replace IH[y'] | add_commute[ℕ2 * toNat(x'), ℕ2].
      }
      case inc_dub(y') {
        expand operator+ | toNat | inc
        replace nat_suc_one_add | nat_suc_one_add
        replace IH[y'] | add_commute[ℕ2 * toNat(x'), ℕ1].
      }
    }
  }
end

theorem uint_add_commute: all x:UInt, y:UInt.
  x + y = y + x
proof
  arbitrary x:UInt, y:UInt
  suffices toNat(x + y) = toNat(y + x) by toNat_injective
  equations
	    toNat(x + y)
        = toNat(x) + toNat(y)   by toNat_add
    ... = toNat(y) + toNat(x)   by add_commute
    ... = #toNat(y + x)#        by replace toNat_add.
end

theorem uint_add_assoc: all x:UInt, y:UInt, z:UInt.
  (x + y) + z = x + (y + z)
proof
  arbitrary x:UInt, y:UInt, z:UInt
  suffices toNat((x + y) + z) = toNat(x + (y + z)) by toNat_injective
  equations
	    toNat((x + y) + z)
        = toNat(x + y) + toNat(z)              by toNat_add
    ... = toNat(x) + toNat(y) + toNat(z)       by replace toNat_add.
    ... = toNat(x) + #toNat(y + z)#            by replace toNat_add.
    ... = #toNat(x + (y + z))#                 by replace toNat_add.
end

associative operator+ in UInt

lemma check_assoc: all x:UInt, y:UInt, z:UInt.
  (x + y) + z = x + (y + z)
proof
  .
end

theorem uint_zero_add: all x:UInt.
  0 + x = x
proof
  arbitrary x:UInt
  suffices toNat(bzero + x) = toNat(x) by toNat_injective
  equations
  	toNat(bzero + x) 
      = toNat(bzero) + toNat(x)    by toNat_add
  ... = toNat(x)                   by expand toNat evaluate
end

auto uint_zero_add

theorem uint_add_zero: all x:UInt.
  x + 0 = x
proof
  arbitrary x:UInt
  suffices toNat(x + bzero) = toNat(x) by toNat_injective
  equations
  	toNat(x + bzero) 
      = toNat(x) + toNat(bzero)    by toNat_add
  ... = toNat(x)                   by { expand toNat evaluate } // bug?
end

auto uint_add_zero

theorem uint_zero_less_one_add: all n:UInt.
  0 < 1 + n
proof
  arbitrary n:UInt
  suffices toNat(0) < toNat(1 + n) by less_toNat
  replace toNat_add
  conclude toNat(0) < toNat(1) + toNat(n) by {
    expand 2*toNat
    nat_zero_less_one_add[toNat(n)]
  }
end

theorem uint_add_both_sides_of_equal: all x:UInt, y:UInt, z:UInt.
  x + y = x + z  y = z
proof
  arbitrary x:UInt, y:UInt, z:UInt
  have fwd: if x + y = x + z then y = z by {
    assume prem
    have xy_xz: toNat(x + y) = toNat(x + z)
      by replace prem.
    have xy_xz2: toNat(x) + toNat(y) = toNat(x) + toNat(z)
      by replace toNat_add in xy_xz
    have yz: toNat(y) = toNat(z) by apply add_both_sides_of_equal to xy_xz2
    conclude y = z by apply toNat_injective to yz
  }
  have bkwd: if y = z then x + y = x + z by {
    assume yz
    replace yz.
  }
  fwd, bkwd
end

theorem uint_add_to_zero: all n:UInt, m:UInt.
  if n + m = 0
  then n = 0 and m = 0
proof
  arbitrary n:UInt, m:UInt
  assume prem
  have nm_z: toNat(n + m) = toNat(bzero) by replace prem.
  have nm_z2: toNat(n) + toNat(m) = ℕ0 by expand toNat in replace toNat_add in nm_z
  have nz_mz: toNat(n) = ℕ0 and toNat(m) = ℕ0 by {
    apply nat_add_to_zero[toNat(n), toNat(m)] to nm_z2
  }
  have nz: toNat(n) = toNat(bzero) by expand toNat nz_mz
  have mz: toNat(m) = toNat(bzero) by expand toNat nz_mz
  (apply toNat_injective to nz), (apply toNat_injective to mz)
end

theorem uint_less_equal_add: all x:UInt, y:UInt.
  x  x + y
proof
  arbitrary x:UInt, y:UInt
  suffices toNat(x)  toNat(x + y) by less_equal_toNat[x, x+y]
  replace toNat_add
  less_equal_add
end

theorem uint_less_add_pos: all x:UInt, y:UInt.
  if 0 < y
  then x < x + y
proof
  arbitrary x:UInt, y:UInt
  assume y_pos
  have ny_pos: ℕ0 < toNat(y) by expand toNat in apply toNat_less to y_pos
  have A: toNat(x) < toNat(x) + toNat(y) by {
    apply nat_less_add_pos[toNat(x), toNat(y)] to ny_pos
  }
  have B: toNat(x) < toNat(x + y) by { replace toNat_add A }
  conclude x < x + y by apply less_toNat to B
end