lib/treetop/runtime/interval_skip_list/interval_skip_list.rb



class IntervalSkipList
  attr_reader :probability

  def initialize
    @head = HeadNode.new(max_height)
    @ranges = {}
    @probability = 0.5
  end

  def max_height
    3
  end

  def empty?
    head.forward[0].nil?
  end

  def expire(range, length_change)
    expired_markers, first_node_after_range = overlapping(range)
    expired_markers.each { |marker| delete(marker) }
    first_node_after_range.propagate_length_change(length_change)    
  end

  def overlapping(range)
    markers, first_node = containing_with_node(range.first)

    cur_node = first_node
    begin
      markers.concat(cur_node.forward_markers.flatten)
      cur_node = cur_node.forward[0]
    end while cur_node.key < range.last

    return markers.uniq, cur_node
  end

  def containing(n)
    containing_with_node(n).first
  end

  def insert(range, marker)
    ranges[marker] = range
    first_node = insert_node(range.first)
    first_node.endpoint_of.push(marker)
    last_node = insert_node(range.last)
    last_node.endpoint_of.push(marker)

    cur_node = first_node
    cur_level = first_node.top_level
    while next_node_at_level_inside_range?(cur_node, cur_level, range)
      while can_ascend_from?(cur_node, cur_level) && next_node_at_level_inside_range?(cur_node, cur_level + 1, range)
        cur_level += 1
      end
      cur_node = mark_forward_path_at_level(cur_node, cur_level, marker)
    end

    while node_inside_range?(cur_node, range)
      while can_descend_from?(cur_level) && next_node_at_level_outside_range?(cur_node, cur_level, range)
        cur_level -= 1 
      end
      cur_node = mark_forward_path_at_level(cur_node, cur_level, marker)
    end
  end

  def delete(marker)
    range = ranges[marker]
    path_to_first_node = make_path
    first_node = find(range.first, path_to_first_node)

    cur_node = first_node
    cur_level = first_node.top_level
    while next_node_at_level_inside_range?(cur_node, cur_level, range)
      while can_ascend_from?(cur_node, cur_level) && next_node_at_level_inside_range?(cur_node, cur_level + 1, range)
        cur_level += 1
      end
      cur_node = unmark_forward_path_at_level(cur_node, cur_level, marker)
    end

    while node_inside_range?(cur_node, range)
      while can_descend_from?(cur_level) && next_node_at_level_outside_range?(cur_node, cur_level, range)
        cur_level -= 1
      end
      cur_node = unmark_forward_path_at_level(cur_node, cur_level, marker)
    end
    last_node = cur_node

    first_node.endpoint_of.delete(marker)
    if first_node.endpoint_of.empty?
      first_node.delete(path_to_first_node)
    end

    last_node.endpoint_of.delete(marker)
    if last_node.endpoint_of.empty?
      path_to_last_node = make_path
      find(range.last, path_to_last_node)
      last_node.delete(path_to_last_node)
    end
  end

  protected
  attr_reader :head, :ranges

  def insert_node(key)
    path = make_path
    found_node = find(key, path)
    if found_node && found_node.key == key
      return found_node
    else
      return Node.new(key, next_node_height, path)
    end
  end  

  def containing_with_node(n)
    containing = []
    cur_node = head
    (max_height - 1).downto(0) do |cur_level|
      while (next_node = cur_node.forward[cur_level]) && next_node.key <= n
        cur_node = next_node
        if cur_node.key == n
          return containing + (cur_node.markers - cur_node.endpoint_of), cur_node
        end
      end
      containing.concat(cur_node.forward_markers[cur_level])
    end

    return containing, cur_node
  end

  def delete_node(key)
    path = make_path
    found_node = find(key, path)
    found_node.delete(path) if found_node.key == key
  end
  
  def find(key, path)
    cur_node = head
    (max_height - 1).downto(0) do |cur_level|
      while (next_node = cur_node.forward[cur_level]) && next_node.key < key
        cur_node = next_node
      end
      path[cur_level] = cur_node
    end
    cur_node.forward[0]
  end

  def make_path
    Array.new(max_height, nil)
  end

  def next_node_height
    height = 1
    while rand < probability && height < max_height
      height += 1
    end
    height
  end

  def can_ascend_from?(node, level)
    level < node.top_level
  end

  def can_descend_from?(level)
    level > 0
  end

  def node_inside_range?(node, range)
    node.key < range.last
  end

  def next_node_at_level_inside_range?(node, level, range)
    node.forward[level] && node.forward[level].key <= range.last
  end

  def next_node_at_level_outside_range?(node, level, range)
    (node.forward[level].nil? || node.forward[level].key > range.last)
  end

  def mark_forward_path_at_level(node, level, marker)
    node.forward_markers[level].push(marker)
    next_node = node.forward[level]
    next_node.markers.push(marker)
    node = next_node
  end

  def unmark_forward_path_at_level(node, level, marker)
    node.forward_markers[level].delete(marker)
    next_node = node.forward[level]
    next_node.markers.delete(marker)
    node = next_node
  end

  def nodes
    nodes = []
    cur_node = head.forward[0]
    until cur_node.nil?
      nodes << cur_node
      cur_node = cur_node.forward[0]
    end
    nodes
  end 
end