@compiler >= 4.3

namespace Bitwise =

  // bit shift 'x' right 'n' postions
  function bsr(n : int, x : int) : int =
    let step = 2^n
    let res  = x / step
    if (x >= 0 || x mod step == 0)
      res
    else
      res - 1

  // bit shift 'x' left 'n' positions
  function bsl(n : int, x : int) : int =
    x * 2^n

  // bit shift 'x' left 'n' positions, limit at 'lim' bits
  function bsli(n : int, x : int, lim : int) : int =
    (x * 2^n) mod (2^lim)

  // bitwise 'and' for arbitrary precision integers
  function band(a : int, b : int) : int =
      if (a >= 0 && b >= 0)
        uband_(a, b)
      elif (b >= 0)
        ubnand_(b, -1 - a)
      elif (a >= 0)
        ubnand_(a, -1 - b)
      else
        -1 - ubor_(-1 - a, -1 - b)

  // bitwise 'or' for arbitrary precision integers
  function
    bor : (int, int) => int
    bor(0, b) = b
    bor(a, 0) = a
    bor(a : int, b : int) : int =
      if (a >= 0 && b >= 0)
        ubor_(a, b)
      elif (b >= 0)
        -1 - ubnand_(-1 - a, b)
      elif (a >= 0)
        -1 - ubnand_(-1 - b, a)
      else
        -1 - uband_(-1 - a, -1 - b)

  // bitwise 'xor' for arbitrary precision integers
  function
    bxor : (int, int) => int
    bxor(0, b) = b
    bxor(a, 0) = a
    bxor(a, b) =
      if (a >= 0 && b >= 0)
        ubxor_(a, b)
      elif (b >= 0)
        -1 - ubxor_(-1 - a, b)
      elif (a >= 0)
        -1 - ubxor_(a, -1 - b)
      else
        ubxor_(-1 - a, -1 - b)

  // bitwise 'not' for arbitrary precision integers
  function bnot(a : int) = bxor(a, -1)

  // Bitwise 'and' for non-negative integers
  function uband(a : int, b : int) : int =
    require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
    switch((a, b))
      (0, _) => 0
      (_, 0) => 0
      _      => uband__(a, b, 1, 0)

  private function uband_(a, b) = uband__(a, b, 1, 0)

  private function
    uband__(0, b, val, acc) = acc
    uband__(a, 0, val, acc) = acc
    uband__(a, b, val, acc) =
      switch (a mod 2 + b mod 2)
        2 => uband__(a / 2, b / 2, val * 2, acc + val)
        _ => uband__(a / 2, b / 2, val * 2, acc)

  // Bitwise 'or' for non-negative integers
  function ubor(a, b) =
    require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
    switch((a, b))
      (0, _) => b
      (_, 0) => a
      _      => ubor__(a, b, 1, 0)

  private function ubor_(a, b) = ubor__(a, b, 1, 0)

  private function
    ubor__(0, 0, val, acc) = acc
    ubor__(a, b, val, acc) =
      switch (a mod 2 + b mod 2)
        0 => ubor__(a / 2, b / 2, val * 2, acc)
        _ => ubor__(a / 2, b / 2, val * 2, acc + val)

  //Bitwise 'xor' for non-negative integers
  function
    ubxor : (int, int) => int
    ubxor(0, b) = b
    ubxor(a, 0) = a
    ubxor(a, b) =
      require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
      ubxor__(a, b, 1, 0)

  private function ubxor_(a, b) = ubxor__(a, b, 1, 0)

  private function
    ubxor__(0, 0, val, acc) = acc
    ubxor__(a, b, val, acc) =
      switch(a mod 2 + b mod 2)
        1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
        _ => ubxor__(a / 2, b / 2, val * 2, acc)

  private function ubnand_(a, b) = ubnand__(a, b, 1, 0)

  private function
    ubnand__(0, b, val, acc) = acc
    ubnand__(a, b, val, acc) =
      switch((a mod 2, b mod 2))
        (1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
        _      => ubnand__(a / 2, b / 2, val * 2, acc)
