{"record":{"id":"82e277ea32abae3b","repo":"krahets/hello-algo","slug":"heap-is-empty-82e277","errorCode":null,"errorMessage":"Heap is empty","messagePattern":"Heap is empty","errorType":"exception","errorClass":"IndexError","httpStatus":null,"severity":"error","filePath":"en/codes/ruby/chapter_heap/my_heap.rb","lineNumber":83,"sourceCode":"\n  ### Heapify from node i, bottom to top ###\n  def sift_up(i)\n    loop do\n      # Get parent node of node i\n      p = parent(i)\n      # When \"crossing root node\" or \"node needs no repair\", end heapify\n      break if p < 0 || @max_heap[i] <= @max_heap[p]\n      # Swap two nodes\n      swap(i, p)\n      # Loop upward heapify\n      i = p\n    end\n  end\n\n  ### Pop element from heap ###\n  def pop\n    # Handle empty case\n    raise IndexError, \"Heap is empty\" if is_empty?\n    # Delete node\n    swap(0, size - 1)\n    # Remove node\n    val = @max_heap.pop\n    # Return top element\n    sift_down(0)\n    # Return heap top element\n    val\n  end\n\n  ### Heapify from node i, top to bottom ###\n  def sift_down(i)\n    loop do\n      # If node i is largest or indices l, r are out of bounds, no need to continue heapify, break\n      l, r, ma = left(i), right(i), i\n      ma = l if l < size && @max_heap[l] > @max_heap[ma]\n      ma = r if r < size && @max_heap[r] > @max_heap[ma]\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/krahets/hello-algo/blob/69932aed1891a7b7f6a0de88cd116d3fe13e7032/en/codes/ruby/chapter_heap/my_heap.rb#L65-L101","documentation":"Raised by `MyMaxHeap#pop` (my_heap.rb:83, English version) when `is_empty?` is true. Identical contract to the Chinese counterpart: the method swaps root with last leaf, removes the last element, then sifts down. On an empty heap there is nothing to swap or remove, so the guard rejects the call before touching `@max_heap`.","triggerScenarios":"Calling `heap.pop` when the heap's internal `@max_heap` array is empty. Occurs after draining the heap, before any push, or in a heap-sort loop with an off-by-one count.","commonSituations":"Heap-sort driver that pops `n+1` times; priority queue drained to empty then popped; test that pops without checking size.","solutions":["Guard every pop with `unless heap.is_empty?`.","Drive the drain loop with `until heap.is_empty?`.","Rescue IndexError if empty pops are recoverable."],"exampleFix":"# before\nwhile i < n\n  heap.pop\nend\n\n# after\nuntil heap.is_empty?\n  heap.pop\nend","handlingStrategy":"validation","validationCode":"return nil if heap.is_empty?\nheap.pop","typeGuard":"def heap_popable?(heap)\n  heap.respond_to?(:is_empty?) && heap.respond_to?(:pop) && !heap.is_empty?\nend","tryCatchPattern":"begin\n  heap.pop\nrescue IndexError\n  nil\nend","preventionTips":["Check is_empty? before pop.","Drive drain loops with until heap.is_empty?.","Do not cache size across pops."],"tags":["ruby","heap","precondition","empty-state"],"backgroundTag":null,"analyzedSha":"69932aed1891a7b7f6a0de88cd116d3fe13e7032","analyzedAt":"2026-08-13T23:02:37.581Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}