class Foobara::LruCache

def cached(key)

def cached(key)
  mutex.synchronize do
    if @key_to_node.key?(key)
      node = @key_to_node[key]
      move_node_to_front(node)
      return node.value
    end
  end
  value = yield
  mutex.synchronize do
    if @key_to_node.key?(key)
      node = @key_to_node[key]
      move_node_to_front(node)
    else
      node = Node.new(key, value)
      @key_to_node[key] = node
      prepend_node(node)
    end
  end
  value
end

def get(key)

def get(key)
  mutex.synchronize do
    if @key_to_node.key?(key)
      node = @key_to_node[key]
      move_node_to_front(node)
      return true, node.value
    end
  end
end

def initialize(capacity = 10)

def initialize(capacity = 10)
  @capacity = capacity
  @size = 0
  @key_to_node = {}
  @head = @tail = nil
end

def key?(key)

def key?(key)
  @key_to_node.key?(key)
end

def move_node_to_front(node)

def move_node_to_front(node)
  return if node == @head
  prev_node = node.prev
  next_node = node.next
  if node == @tail
    @tail = prev_node
    @tail.next = nil
  else
    prev_node.next = next_node
    next_node.prev = prev_node
  end
  @head.prev = node
  node.next = @head
  node.prev = nil
  @head = node
end

def mutex

def mutex
  @mutex ||= Mutex.new
end

def prepend_node(node)

def prepend_node(node)
  if @size == 0
    @head = @tail = node
    @size = 1
  else
    @head.prev = node
    node.next = @head
    @head = node
    if @size < @capacity
      @size += 1
    else
      @key_to_node.delete(@tail.key)
      prev_node = @tail.prev
      if prev_node
        prev_node.next = nil
        @tail = prev_node
      end
    end
  end
end

def reset!

def reset!
  mutex.synchronize do
    @size = 0
    @key_to_node.clear
    @head = @tail = nil
  end
end

def set_if_missing(key, value)

def set_if_missing(key, value)
  mutex.synchronize do
    unless @key_to_node.key?(key)
      node = Node.new(key, value)
      @key_to_node[key] = node
      prepend_node(node)
    end
  end
end