module FDB::Tuple

def self._code_for(v)

def self._code_for(v)
  if v.nil?
    @@NULL_CODE
  elsif v.kind_of? String
    if v.encoding == Encoding::BINARY || v.encoding == Encoding::ASCII
      @@BYTES_CODE
    elsif v.encoding == Encoding::UTF_8
      @@STRING_CODE
    else
      raise ArgumentError, "unsupported encoding #{v.encoding.name}"
    end
  elsif v.kind_of? Integer
     @@INT_ZERO_CODE
  elsif v.kind_of? TrueClass
    @@TRUE_CODE
  elsif v.kind_of? FalseClass
    @@FALSE_CODE
  elsif v.kind_of? SingleFloat
    @@FLOAT_CODE
  elsif v.kind_of? Float
    @@DOUBLE_CODE
  elsif v.kind_of? UUID
    @@UUID_CODE
  elsif v.kind_of? Array
    @@NESTED_CODE
  else
    raise ArgumentError, "unsupported type #{v.class}"
  end
end

def self._compare_elems(v1, v2)

def self._compare_elems(v1, v2)
  c1 = _code_for(v1)
  c2 = _code_for(v2)
  return c1 <=> c2 unless c1 == c2
  if c1 == @@NULL_CODE
    0
  elsif c1 == @@DOUBLE_CODE
    _compare_floats(v1, v2, true)
  elsif c1 == @@NESTED_CODE
    compare(v1, v2) # recurse
  else
    v1 <=> v2
  end
end

def self._compare_floats(f1, f2, is_double)

def self._compare_floats(f1, f2, is_double)
  # This converts the floats to their byte representation and then
  # does the comparison. Why?
  #   1) NaN comparison - Ruby doesn't really do this
  #   2) -0.0 == 0.0 in Ruby but not in our representation
  # It would be better to just take the floats and compare them, but
  # this way handles the edge cases correctly.
  b1 = float_adjust([f1].pack(is_double ? ">G" : ">g"), 0, (is_double ? 8 : 4), true)
  b2 = float_adjust([f2].pack(is_double ? ">G" : ">g"), 0, (is_double ? 8 : 4), true)
  b1 <=> b2
end

def self.bisect_left(list, item)

def self.bisect_left(list, item)
  count = 0
  list.each{|i|
    return count if i >= item
    count += 1
  }
  nil
end

def self.compare(tuple1, tuple2)

def self.compare(tuple1, tuple2)
  i = 0
  while i < tuple1.length && i < tuple2.length
    c = self._compare_elems(tuple1[i], tuple2[i])
    return c unless c == 0
    i += 1
  end
  tuple1.length <=> tuple2.length
end

def self.decode(v, pos)

def self.decode(v, pos)
  code = v.getbyte(pos)
  if code == @@NULL_CODE
    [nil, pos+1]
  elsif code == @@BYTES_CODE
    epos = find_terminator(v, pos+1)
    [v.slice(pos+1, epos-pos-1).gsub("\x00\xFF", "\x00"), epos+1]
  elsif code == @@STRING_CODE
    epos = find_terminator(v, pos+1)
    [v.slice(pos+1, epos-pos-1).gsub("\x00\xFF", "\x00").force_encoding("UTF-8"), epos+1]
  elsif code >= @@INT_ZERO_CODE && code < @@POS_INT_END
    n = code - @@INT_ZERO_CODE
    [("\x00" * (8-n) + v.slice(pos+1, n)).unpack("Q>")[0], pos+n+1]
  elsif code > @@NEG_INT_START and code < @@INT_ZERO_CODE
    n = @@INT_ZERO_CODE - code
    [("\x00" * (8-n) + v.slice(pos+1, n)).unpack("Q>")[0]-@@size_limits[n], pos+n+1]
  elsif code == @@POS_INT_END
    length = v.getbyte(pos+1)
    val = 0
    length.times do |i|
      val = val << 8
      val += v.getbyte(pos+2+i)
    end
    [val, pos+length+2]
  elsif code == @@NEG_INT_START
    length = v.getbyte(pos+1) ^ 0xff
    val = 0
    length.times do |i|
      val = val << 8
      val += v.getbyte(pos+2+i)
    end
    [val - (1 << (length*8)) + 1, pos+length+2]
  elsif code == @@FALSE_CODE
    [false, pos+1]
  elsif code == @@TRUE_CODE
    [true, pos+1]
  elsif code == @@FLOAT_CODE
    [SingleFloat.new(float_adjust(v, pos+1, 4, false).unpack("g")[0]), pos+5]
  elsif code == @@DOUBLE_CODE
    [float_adjust(v, pos+1, 8, false).unpack("G")[0], pos+9]
  elsif code == @@UUID_CODE
    [UUID.new(v.slice(pos+1, 16)), pos+17]
  elsif code == @@NESTED_CODE
    epos = pos+1
    nested = []
    while epos < v.length
      if v.getbyte(epos) == @@NULL_CODE 
        if epos+1 < v.length and v.getbyte(epos+1) == 0xFF
          nested << nil
          epos += 2
        else
          break
        end
      else
        r, epos = decode(v, epos)
        nested << r
      end
    end
    [nested, epos+1]
  else
    raise "Unknown data type in DB: " + code.ord.to_s
  end
end

def self.encode(v, nested=false)

def self.encode(v, nested=false)
  if v.nil?
    if nested
      "\x00\xFF"
    else
      @@NULL_CODE.chr
    end
  elsif v.kind_of? String
    if v.encoding == Encoding::BINARY || v.encoding == Encoding::ASCII
      @@BYTES_CODE.chr + v.gsub("\x00", "\x00\xFF") + 0.chr
    elsif v.encoding == Encoding::UTF_8
      @@STRING_CODE.chr + v.dup.force_encoding("BINARY").gsub("\x00", "\x00\xFF") + 0.chr
    else
      raise ArgumentError, "unsupported encoding #{v.encoding.name}"
    end
  elsif v.kind_of? Integer
    raise RangeError, "Integer magnitude is too large (more than 255 bytes)" if v < -2**2040+1 || v > 2**2040-1
    if v == 0
      @@INT_ZERO_CODE.chr
    elsif v > 0
      if v > @@size_limits[-1]
        length = (v.bit_length + 7) / 8
        result = @@POS_INT_END.chr + length.chr
        length.times do |i|
          result << ((v >> (8 * (length-i-1))) & 0xff)
        end
        result
      else
        n = bisect_left( @@size_limits, v )
        (@@INT_ZERO_CODE+n).chr + [v].pack("Q>").slice(8-n, n)
      end
    else
      if -v > @@size_limits[-1]
        length = ((-v).bit_length + 7) / 8
        v += (1 << (length * 8)) - 1
        result = @@NEG_INT_START.chr + (length ^ 0xff).chr
        length.times do |i|
          result << ((v >> (8 * (length-i-1))) & 0xff)
        end
        result
      else
        n = bisect_left( @@size_limits, -v )
        (@@INT_ZERO_CODE-n).chr + [@@size_limits[n]+v].pack("Q>").slice(8-n, n)
      end
    end
  elsif v.kind_of? TrueClass
    @@TRUE_CODE.chr
  elsif v.kind_of? FalseClass
    @@FALSE_CODE.chr
  elsif v.kind_of? SingleFloat
    @@FLOAT_CODE.chr + float_adjust([v.value].pack("g"), 0, 4, true)
  elsif v.kind_of? Float
    @@DOUBLE_CODE.chr + float_adjust([v].pack("G"), 0, 8, true)
  elsif v.kind_of? UUID
    @@UUID_CODE.chr + v.data
  elsif v.kind_of? Array
    @@NESTED_CODE.chr + (v.map { |el| encode(el, true).force_encoding("BINARY") }).join + 0.chr
  else
    raise ArgumentError, "unsupported type #{v.class}"
  end
end

def self.find_terminator(v, pos)

def self.find_terminator(v, pos)
  while true
    pos = v.index("\x00", pos)
    if !pos
      return v.length
    elsif pos+1 == v.length || v[pos+1] != "\xff"
      return pos
    end
    pos += 2
  end
end

def self.float_adjust(v, pos, length, encode)

def self.float_adjust(v, pos, length, encode)
  if (encode and v[pos].ord & 0x80 != 0x00) or (not encode and v[pos].ord & 0x80 == 0x00)
    v.slice(pos, length).chars.map { |b| (b.ord ^ 0xff).chr } .join
  else
    ret = v.slice(pos, length)
    ret[0] = (ret[0].ord ^ 0x80).chr
    ret
  end
end

def self.pack(t)

def self.pack(t)
  (t.each_with_index.map {|el, i|
     begin
       (encode el).force_encoding("BINARY")
     rescue
       raise $!, "#{$!} at index #{i}", $!.backtrace
     end
   }).join
end

def self.range(tuple=[])

def self.range(tuple=[])
  p = pack(tuple)
  [p+"\x00", p+"\xFF"]
end

def self.unpack(key)

def self.unpack(key)
  key = key.dup.force_encoding("BINARY")
  pos = 0
  res = []
  while pos < key.length
    r, pos = decode(key, pos)
    res << r
  end
  res
end