view UInt {
  source Binary
  target UIntView
  into uint_view
  out uint_unview
  roundtrip uint_view_unview
  inverse uint_unview_view
}

opaque define div2 : (fn UInt -> UInt) = fun b:UInt {
      switch b {
        case bzero {
          0
        }
        case dub_inc(x) {
          inc(x)
        }
        case inc_dub(x) {
          x
        }
      }
    }

opaque recursive fromNat(Nat) -> UInt{
  fromNat(zero) = 0
  fromNat(suc(n)) = inc(fromNat(n))
}

opaque define log : (fn UInt -> UInt) = fun b:UInt {
      cnt_dubs(pred(b))
    }

define max : (fn (UInt, UInt) -> UInt) = fun x:UInt, y:UInt {
      if x < y then
        y
      else
        x
    }

define min : (fn (UInt, UInt) -> UInt) = fun x:UInt, y:UInt {
      if x < y then
        x
      else
        y
    }

opaque recursive operator *(UInt,UInt) -> UInt{
  operator *(bzero, y) = 0
  operator *(dub_inc(x), y) = 
    switch y {
      case bzero {
        0
      }
      case dub_inc(y') {
        dub(dub_inc((x + y') + x * y'))
      }
      case inc_dub(y') {
        dub_inc(x + dub(y' + x * y'))
      }
    }
  operator *(inc_dub(x), y) = 
    switch y {
      case bzero {
        0
      }
      case dub_inc(y') {
        dub_inc((dub(x) + y') + dub(x * y'))
      }
      case inc_dub(y') {
        inc_dub((x + y') + dub(x * y'))
      }
    }
}

opaque recursive operator +(UInt,UInt) -> UInt{
  operator +(bzero, y) = y
  operator +(dub_inc(x), y) = 
    switch y {
      case bzero {
        dub_inc(x)
      }
      case dub_inc(y') {
        dub_inc(inc(x + y'))
      }
      case inc_dub(y') {
        inc(dub_inc(x + y'))
      }
    }
  operator +(inc_dub(x), y) = 
    switch y {
      case bzero {
        inc_dub(x)
      }
      case dub_inc(y') {
        inc(dub_inc(x + y'))
      }
      case inc_dub(y') {
        inc(inc_dub(x + y'))
      }
    }
}

opaque recursive operator <(UInt,UInt) -> bool{
  operator <(bzero, y) = 
    switch y {
      case bzero {
        false
      }
      case dub_inc(y') {
        true
      }
      case inc_dub(y') {
        true
      }
    }
  operator <(dub_inc(x'), y) = 
    switch y {
      case bzero {
        false
      }
      case dub_inc(y') {
        x' < y'
      }
      case inc_dub(y') {
        x' < y'
      }
    }
  operator <(inc_dub(x'), y) = 
    switch y {
      case bzero {
        false
      }
      case dub_inc(y') {
        ((x' < y') or (x' = y'))
      }
      case inc_dub(y') {
        x' < y'
      }
    }
}

define operator > : (fn (UInt, UInt) -> bool) = fun x:UInt, y:UInt {
      y < x
    }

opaque define operator ^ : (fn (UInt, UInt) -> UInt) = fun a:UInt, b:UInt {
      expt(b, a)
    }

opaque recursive operator ∸(UInt,UInt) -> UInt{
  operator ∸(bzero, y) = 0
  operator ∸(dub_inc(x), y) = 
    switch y {
      case bzero {
        dub_inc(x)
      }
      case dub_inc(y') {
        dub(x  y')
      }
      case inc_dub(y') {
        (if x < y' then 0 else inc_dub(x  y'))
      }
    }
  operator ∸(inc_dub(x), y) = 
    switch y {
      case bzero {
        inc_dub(x)
      }
      case dub_inc(y') {
        (if x < y' then 0 else pred(dub(x  y')))
      }
      case inc_dub(y') {
        dub(x  y')
      }
    }
}

define operator ≤ : (fn (UInt, UInt) -> bool) = fun x:UInt, y:UInt {
      ((x < y) or (x = y))
    }

define operator ≥ : (fn (UInt, UInt) -> bool) = fun x:UInt, y:UInt {
      y  x
    }

define sqr : (fn UInt -> UInt) = fun a:UInt {
      a * a
    }

opaque recursive toNat(UInt) -> Nat{
  toNat(bzero) = ℕ0
  toNat(dub_inc(x)) = ℕ2 * suc(toNat(x))
  toNat(inc_dub(x)) = suc(ℕ2 * toNat(x))
}