# This class and two external methods provide container for long addresses -
# from 0 to 2^64-1. It is necessary because of JavaScript limitations for int.
class Addr64
  # @private
  left: undefined
  # @private
  right: undefined
  # @private
  limit: undefined

  # Constructor.
  #
  # @param left left part of the number (adds left * 2^32 to the whole address).
  # @param right right part of the number.
  @create$int$int: (left, right, o) ->
    if !o? then o = new Addr64
    o.left = left
    o.right = right
    o.limit = Math.pow 2, 32
    return o

  # Makes a copy of this object.
  #
  # @return similar Addr64 object
  copy: () ->
    return Addr64.create$int$int @left, @right

  # Adds number to this Addr64.
  #
  # @param num number to add.
  # @return new address.
  plus$int: (num) ->
    newNum = num + @right
    left = @left
    right = 0
    if newNum < 0
      k = Math.floor(newNum / @limit)
      right = newNum - k * @limit
      left += k
    else if newNum < @limit
      right = newNum
    else
      right = newNum % @limit
      left += Math.floor(newNum / @limit)
    return Addr64.create$int$int left, right

  # Operation &.
  #
  # @param mask another Addr64 object.
  # @return new address.
  logAnd$Addr64: (mask) ->
    left = @left & mask.left
    right = @right & mask.right
    return Addr64.create$int$int left, right

  # Converts this Addr64 to float (for test purposes only).
  #
  # @return number.
  toFloat: () ->
    return @left * @limit + @right

  # Calculates difference between this Addr64 and the second one.
  #
  # @param second Addr64.
  # @result the number that is equal to this - second.
  diff$Addr64: (second) ->
    return (@left - second.left) * @limit + @right - second.right

  # Equality comparison.
  #
  # @param addr another Addr64 object.
  # @return true or false.
  equals$Addr64: (addr) ->
    return @left == addr.left && @right == addr.right

  # Less comparison
  #
  # @param addr another Addr64 object.
  # @return true or false.
  less$Addr64: (addr) ->
    return (@left == addr.left && @right < addr.right) || @left < addr.left

  # Create null Address, i.e. equal to 2^64 - 1.
  #
  # @return null Address
  @nullAddress: () ->
    return Addr64.create$int$int @limit - 1, @limit - 1

  # Converts Addr64 to string.
  #
  # @param number Addr64 object to convert.
  # @param base the base of convertion.
  # @return string of length of 2^64 is this base. If necessary the string is
  # filled up by zeroes in the beginning.
  @addr64ToString$Addr64$int: (number, base) ->
    len = Math.ceil(64 * (Math.log 2) / Math.log base)
    maxInt = Math.pow 2, 32
    strTemp = ""
    str = ""
    left = number.left
    right = number.right
    while (left != 0)
      rbase = (left % base) * maxInt + right
      left = Math.floor(left / base) +
      Math.floor(Math.floor(rbase / base) / maxInt)
      right = Math.floor(rbase / base) % maxInt
      digit = rbase % base
      strTemp = digit.toString(base).concat strTemp
    if (right != 0)
      strTemp = right.toString(base).concat strTemp
    for i in [0..len - strTemp.length - 1]
      str = str.concat "0"
    str = str.concat strTemp
    return str

  # Convert string to Addr64.
  #
  # @param str string, must be not shorter than the length of 2^64 is this base.
  # If string is longer then all the other symbols are ignored.
  # @param base the base of convertion.
  # @return Addr64 object.
  @stringToAddr64$String$int: (str, base) ->
    len = Math.ceil(64 * (Math.log 2) / Math.log base)
    maxInt = Math.pow 2, 32
    left = 0
    right = 0
    for i in [0..len - 1]
      digit = str.substr i, 1
      rbase = right * base + parseInt digit, base
      if rbase < maxInt
        right = rbase
      else
        right = rbase % maxInt
        left = left + Math.floor(rbase / maxInt)
    return Addr64.create$int$int left, right

  toString: () ->
    return Addr64.addr64ToString$Addr64$int this, 16

exports.Addr64 = Addr64

