codedebugger / data /bug_dataset.py
psdhanushkumar's picture
Upload folder using huggingface_hub
f361447 verified
Raw
History Blame Contribute Delete
35.1 kB
"""
CodeDebugger Bug Dataset β€” 30 buggy Python problems.
Schema per problem: id, difficulty, title, description, buggy_code,
fixed_code, test_cases, error_type, hint.
Each problem has exactly 4 test cases.
"""
# ═══════════════════════════════════════════════════════════════
# EASY BUGS (1–10)
# ═══════════════════════════════════════════════════════════════
EASY_BUGS = [
# 1
{
"id": "bug_001",
"difficulty": "easy",
"title": "Off-by-one in list index",
"description": "Fix the bug in this function that retrieves the last element.",
"buggy_code": (
"def get_last(lst):\n"
" return lst[len(lst)]\n"
),
"fixed_code": (
"def get_last(lst):\n"
" return lst[len(lst) - 1]\n"
),
"test_cases": [
{"input": "[1, 2, 3]", "expected": "3"},
{"input": "[10]", "expected": "10"},
{"input": "[5, 6, 7, 8]", "expected": "8"},
{"input": "[0]", "expected": "0"},
],
"error_type": "IndexError",
"hint": "list indices are 0-based; the last valid index is len(lst)-1.",
},
# 2
{
"id": "bug_002",
"difficulty": "easy",
"title": "Wrong comparison operator (> instead of >=)",
"description": "Fix the function that checks whether a score is passing (>= 50).",
"buggy_code": (
"def is_passing(score):\n"
" return score > 50\n"
),
"fixed_code": (
"def is_passing(score):\n"
" return score >= 50\n"
),
"test_cases": [
{"input": "75", "expected": "True"},
{"input": "50", "expected": "True"},
{"input": "49", "expected": "False"},
{"input": "100", "expected": "True"},
],
"error_type": "LogicError",
"hint": "A score of exactly 50 should pass; use >= not >.",
},
# 3
{
"id": "bug_003",
"difficulty": "easy",
"title": "Missing return statement",
"description": "Fix the function that should return the square of a number.",
"buggy_code": (
"def square(n):\n"
" result = n * n\n"
),
"fixed_code": (
"def square(n):\n"
" result = n * n\n"
" return result\n"
),
"test_cases": [
{"input": "4", "expected": "16"},
{"input": "0", "expected": "0"},
{"input": "-3", "expected": "9"},
{"input": "10", "expected": "100"},
],
"error_type": "LogicError",
"hint": "The function computes the result but never returns it.",
},
# 4
{
"id": "bug_004",
"difficulty": "easy",
"title": "Wrong variable name used",
"description": "Fix the function that converts Celsius to Fahrenheit.",
"buggy_code": (
"def celsius_to_fahrenheit(c):\n"
" f = c * 9 / 5 + 32\n"
" return c\n"
),
"fixed_code": (
"def celsius_to_fahrenheit(c):\n"
" f = c * 9 / 5 + 32\n"
" return f\n"
),
"test_cases": [
{"input": "0", "expected": "32.0"},
{"input": "100", "expected": "212.0"},
{"input": "37", "expected": "98.6"},
{"input": "-40", "expected": "-40.0"},
],
"error_type": "LogicError",
"hint": "The formula stores the result in `f` but the wrong variable is returned.",
},
# 5
{
"id": "bug_005",
"difficulty": "easy",
"title": "Wrong string method: split vs strip",
"description": "Fix the function that removes leading/trailing whitespace from a string.",
"buggy_code": (
"def clean(s):\n"
" return s.split()\n"
),
"fixed_code": (
"def clean(s):\n"
" return s.strip()\n"
),
"test_cases": [
{"input": "' hello '", "expected": "hello"},
{"input": "'world'", "expected": "world"},
{"input": "' '", "expected": ""},
{"input": "' a b '", "expected": "a b"},
],
"error_type": "TypeError",
"hint": "strip() removes whitespace; split() splits into a list.",
},
# 6
{
"id": "bug_006",
"difficulty": "easy",
"title": "Missing -1 in length calculation",
"description": "Fix the function that returns the second-to-last element of a list.",
"buggy_code": (
"def second_to_last(lst):\n"
" return lst[len(lst) - 1]\n"
),
"fixed_code": (
"def second_to_last(lst):\n"
" return lst[len(lst) - 2]\n"
),
"test_cases": [
{"input": "[1, 2, 3]", "expected": "2"},
{"input": "[10, 20]", "expected": "10"},
{"input": "[5, 6, 7, 8]", "expected": "7"},
{"input": "[0, 1]", "expected": "0"},
],
"error_type": "LogicError",
"hint": "The second-to-last index is len(lst)-2, not len(lst)-1.",
},
# 7
{
"id": "bug_007",
"difficulty": "easy",
"title": "Wrong list method: append vs extend",
"description": "Fix the function that adds all elements of one list into another.",
"buggy_code": (
"def combine(base, items):\n"
" base.append(items)\n"
" return base\n"
),
"fixed_code": (
"def combine(base, items):\n"
" base.extend(items)\n"
" return base\n"
),
"test_cases": [
{"input": "[1, 2], [3, 4]", "expected": "[1, 2, 3, 4]"},
{"input": "[], [1]", "expected": "[1]"},
{"input": "[0], []", "expected": "[0]"},
{"input": "[1], [2, 3, 4]", "expected": "[1, 2, 3, 4]"},
],
"error_type": "LogicError",
"hint": "append() adds the list as a single element; extend() unpacks it.",
},
# 8
{
"id": "bug_008",
"difficulty": "easy",
"title": "Comparing to None with == instead of is",
"description": "Fix the function that checks whether a value is None.",
"buggy_code": (
"def is_empty(val):\n"
" return val == None\n"
),
"fixed_code": (
"def is_empty(val):\n"
" return val is None\n"
),
"test_cases": [
{"input": "None", "expected": "True"},
{"input": "0", "expected": "False"},
{"input": "''", "expected": "False"},
{"input": "[]", "expected": "False"},
],
"error_type": "LogicError",
"hint": "Use `is None` for identity check, not `== None`.",
},
# 9
{
"id": "bug_009",
"difficulty": "easy",
"title": "Integer division instead of float division",
"description": "Fix the function that calculates the average of a list.",
"buggy_code": (
"def average(nums):\n"
" return sum(nums) // len(nums)\n"
),
"fixed_code": (
"def average(nums):\n"
" return sum(nums) / len(nums)\n"
),
"test_cases": [
{"input": "[1, 2, 3, 4]", "expected": "2.5"},
{"input": "[10, 20]", "expected": "15.0"},
{"input": "[7]", "expected": "7.0"},
{"input": "[1, 1, 1, 1]", "expected": "1.0"},
],
"error_type": "LogicError",
"hint": "Use / for true division; // performs floor division.",
},
# 10
{
"id": "bug_010",
"difficulty": "easy",
"title": "Wrong list index: -1 vs 0",
"description": "Fix the function that returns the first element of a list.",
"buggy_code": (
"def get_first(lst):\n"
" return lst[-1]\n"
),
"fixed_code": (
"def get_first(lst):\n"
" return lst[0]\n"
),
"test_cases": [
{"input": "[1, 2, 3]", "expected": "1"},
{"input": "[10]", "expected": "10"},
{"input": "[5, 6, 7, 8]", "expected": "5"},
{"input": "[99, 0]", "expected": "99"},
],
"error_type": "LogicError",
"hint": "Index 0 is the first element; -1 is the last.",
},
]
# ═══════════════════════════════════════════════════════════════
# MEDIUM BUGS (11–20)
# ═══════════════════════════════════════════════════════════════
MEDIUM_BUGS = [
# 11
{
"id": "bug_011",
"difficulty": "medium",
"title": "Wrong loop termination condition",
"description": "Fix the function that counts down from n to 1 and returns the sum.",
"buggy_code": (
"def countdown_sum(n):\n"
" total = 0\n"
" while n > 1:\n"
" total += n\n"
" n -= 1\n"
" return total\n"
),
"fixed_code": (
"def countdown_sum(n):\n"
" total = 0\n"
" while n >= 1:\n"
" total += n\n"
" n -= 1\n"
" return total\n"
),
"test_cases": [
{"input": "5", "expected": "15"},
{"input": "1", "expected": "1"},
{"input": "10", "expected": "55"},
{"input": "3", "expected": "6"},
],
"error_type": "LogicError",
"hint": "`n > 1` stops too early and skips adding 1; use `n >= 1` to include it.",
},
# 12
{
"id": "bug_012",
"difficulty": "medium",
"title": "Wrong recursion base case value",
"description": "Fix the recursive factorial function that returns the wrong result for n=0.",
"buggy_code": (
"def factorial(n):\n"
" if n == 0:\n"
" return 0\n"
" return n * factorial(n - 1)\n"
),
"fixed_code": (
"def factorial(n):\n"
" if n == 0:\n"
" return 1\n"
" return n * factorial(n - 1)\n"
),
"test_cases": [
{"input": "0", "expected": "1"},
{"input": "1", "expected": "1"},
{"input": "5", "expected": "120"},
{"input": "7", "expected": "5040"},
],
"error_type": "LogicError",
"hint": "0! is defined as 1, not 0. The base case must return 1.",
},
# 13
{
"id": "bug_013",
"difficulty": "medium",
"title": "Wrong sort order (missing reverse=True)",
"description": "Fix the function that should return a list sorted in descending order.",
"buggy_code": (
"def sort_desc(lst):\n"
" return sorted(lst)\n"
),
"fixed_code": (
"def sort_desc(lst):\n"
" return sorted(lst, reverse=True)\n"
),
"test_cases": [
{"input": "[3, 1, 2]", "expected": "[3, 2, 1]"},
{"input": "[5, 5, 5]", "expected": "[5, 5, 5]"},
{"input": "[1]", "expected": "[1]"},
{"input": "[10, 3, 7, 1]", "expected": "[10, 7, 3, 1]"},
],
"error_type": "LogicError",
"hint": "sorted() returns ascending order by default; pass reverse=True for descending.",
},
# 14
{
"id": "bug_014",
"difficulty": "medium",
"title": "Missing edge case for empty list",
"description": "Fix the function that returns the max of a list β€” it crashes on empty input.",
"buggy_code": (
"def safe_max(lst):\n"
" return max(lst)\n"
),
"fixed_code": (
"def safe_max(lst):\n"
" if not lst:\n"
" return None\n"
" return max(lst)\n"
),
"test_cases": [
{"input": "[]", "expected": "None"},
{"input": "[3]", "expected": "3"},
{"input": "[1, 5, 2]", "expected": "5"},
{"input": "[-1, -3, -2]", "expected": "-1"},
],
"error_type": "ValueError",
"hint": "max() raises ValueError on an empty sequence; guard with an empty-list check.",
},
# 15
{
"id": "bug_015",
"difficulty": "medium",
"title": "Wrong string format (% vs .format)",
"description": "Fix the function that formats a greeting using .format().",
"buggy_code": (
"def greet(name, age):\n"
" return 'Hello %s, you are %d years old' % (name,)\n"
),
"fixed_code": (
"def greet(name, age):\n"
" return 'Hello {}, you are {} years old'.format(name, age)\n"
),
"test_cases": [
{"input": "'Alice', 30", "expected": "Hello Alice, you are 30 years old"},
{"input": "'Bob', 25", "expected": "Hello Bob, you are 25 years old"},
{"input": "'Z', 1", "expected": "Hello Z, you are 1 years old"},
{"input": "'Dev', 99", "expected": "Hello Dev, you are 99 years old"},
],
"error_type": "TypeError",
"hint": "The % format string has two placeholders but only one argument is passed.",
},
# 16
{
"id": "bug_016",
"difficulty": "medium",
"title": "Wrong dictionary key access",
"description": "Fix the function that retrieves a value from a dict safely.",
"buggy_code": (
"def get_value(d, key):\n"
" return d[key]\n"
),
"fixed_code": (
"def get_value(d, key):\n"
" return d.get(key, None)\n"
),
"test_cases": [
{"input": "{'a': 1}, 'a'", "expected": "1"},
{"input": "{'b': 2}, 'x'", "expected": "None"},
{"input": "{}, 'k'", "expected": "None"},
{"input": "{'z': 0}, 'z'", "expected": "0"},
],
"error_type": "KeyError",
"hint": "Use dict.get(key, default) to avoid KeyError on missing keys.",
},
# 17
{
"id": "bug_017",
"difficulty": "medium",
"title": "Wrong boolean logic (and vs or)",
"description": "Fix the function that returns True only when both flags are True.",
"buggy_code": (
"def both_true(a, b):\n"
" return a or b\n"
),
"fixed_code": (
"def both_true(a, b):\n"
" return a and b\n"
),
"test_cases": [
{"input": "True, True", "expected": "True"},
{"input": "True, False", "expected": "False"},
{"input": "False, True", "expected": "False"},
{"input": "False, False", "expected": "False"},
],
"error_type": "LogicError",
"hint": "`or` returns True if either is True; use `and` to require both.",
},
# 18
{
"id": "bug_018",
"difficulty": "medium",
"title": "Wrong range bounds (range(n) vs range(n+1))",
"description": "Fix the function that sums integers from 0 to n inclusive.",
"buggy_code": (
"def sum_to_n(n):\n"
" return sum(range(n))\n"
),
"fixed_code": (
"def sum_to_n(n):\n"
" return sum(range(n + 1))\n"
),
"test_cases": [
{"input": "5", "expected": "15"},
{"input": "0", "expected": "0"},
{"input": "1", "expected": "1"},
{"input": "10", "expected": "55"},
],
"error_type": "LogicError",
"hint": "range(n) stops before n; use range(n+1) to include n.",
},
# 19
{
"id": "bug_019",
"difficulty": "medium",
"title": "Missing int() type conversion",
"description": "Fix the function that reads a number from a string and doubles it.",
"buggy_code": (
"def double_str(s):\n"
" return s * 2\n"
),
"fixed_code": (
"def double_str(s):\n"
" return int(s) * 2\n"
),
"test_cases": [
{"input": "'5'", "expected": "10"},
{"input": "'0'", "expected": "0"},
{"input": "'3'", "expected": "6"},
{"input": "'21'", "expected": "42"},
],
"error_type": "TypeError",
"hint": "Multiplying a string by 2 repeats it; convert to int first.",
},
# 20
{
"id": "bug_020",
"difficulty": "medium",
"title": "Wrong list slicing ([1:] vs [:1])",
"description": "Fix the function that returns all elements except the first.",
"buggy_code": (
"def tail(lst):\n"
" return lst[:1]\n"
),
"fixed_code": (
"def tail(lst):\n"
" return lst[1:]\n"
),
"test_cases": [
{"input": "[1, 2, 3]", "expected": "[2, 3]"},
{"input": "[10]", "expected": "[]"},
{"input": "[5, 6, 7, 8]", "expected": "[6, 7, 8]"},
{"input": "[0, 0]", "expected": "[0]"},
],
"error_type": "LogicError",
"hint": "lst[:1] returns only the first element; lst[1:] skips it.",
},
]
# ═══════════════════════════════════════════════════════════════
# HARD BUGS (21–30)
# ═══════════════════════════════════════════════════════════════
HARD_BUGS = [
# 21
{
"id": "bug_021",
"difficulty": "hard",
"title": "Binary search off-by-one in mid update",
"description": "Fix the binary search that infinite-loops when the target is absent.",
"buggy_code": (
"def binary_search(arr, target):\n"
" lo, hi = 0, len(arr) - 1\n"
" while lo <= hi:\n"
" mid = (lo + hi) // 2\n"
" if arr[mid] == target:\n"
" return mid\n"
" elif arr[mid] < target:\n"
" lo = mid\n"
" else:\n"
" hi = mid\n"
" return -1\n"
),
"fixed_code": (
"def binary_search(arr, target):\n"
" lo, hi = 0, len(arr) - 1\n"
" while lo <= hi:\n"
" mid = (lo + hi) // 2\n"
" if arr[mid] == target:\n"
" return mid\n"
" elif arr[mid] < target:\n"
" lo = mid + 1\n"
" else:\n"
" hi = mid - 1\n"
" return -1\n"
),
"test_cases": [
{"input": "[1, 2, 3, 4, 5], 3", "expected": "2"},
{"input": "[1, 2, 3, 4, 5], 6", "expected": "-1"},
{"input": "[10, 20, 30], 10", "expected": "0"},
{"input": "[7], 7", "expected": "0"},
],
"error_type": "InfiniteLoop",
"hint": "lo and hi must actually shrink: use mid+1 and mid-1.",
},
# 22
{
"id": "bug_022",
"difficulty": "hard",
"title": "Wrong DP state transition (Fibonacci)",
"description": "Fix the DP Fibonacci that stores the wrong value at each step.",
"buggy_code": (
"def fib_dp(n):\n"
" if n <= 1:\n"
" return n\n"
" dp = [0] * (n + 1)\n"
" dp[1] = 1\n"
" for i in range(2, n + 1):\n"
" dp[i] = dp[i - 1] + dp[i - 1]\n"
" return dp[n]\n"
),
"fixed_code": (
"def fib_dp(n):\n"
" if n <= 1:\n"
" return n\n"
" dp = [0] * (n + 1)\n"
" dp[1] = 1\n"
" for i in range(2, n + 1):\n"
" dp[i] = dp[i - 1] + dp[i - 2]\n"
" return dp[n]\n"
),
"test_cases": [
{"input": "0", "expected": "0"},
{"input": "1", "expected": "1"},
{"input": "6", "expected": "8"},
{"input": "10", "expected": "55"},
],
"error_type": "LogicError",
"hint": "Fibonacci recurrence is dp[i-1] + dp[i-2], not dp[i-1] + dp[i-1].",
},
# 23
{
"id": "bug_023",
"difficulty": "hard",
"title": "Wrong graph traversal: visited check in wrong place",
"description": "Fix BFS that revisits nodes because it marks them visited too late.",
"buggy_code": (
"from collections import deque\n"
"def bfs_count(graph, start):\n"
" visited = set()\n"
" queue = deque([start])\n"
" count = 0\n"
" while queue:\n"
" node = queue.popleft()\n"
" if node in visited:\n"
" continue\n"
" visited.add(node)\n"
" count += 1\n"
" for nb in graph.get(node, []):\n"
" queue.append(nb)\n"
" return count\n"
),
"fixed_code": (
"from collections import deque\n"
"def bfs_count(graph, start):\n"
" visited = set([start])\n"
" queue = deque([start])\n"
" count = 0\n"
" while queue:\n"
" node = queue.popleft()\n"
" count += 1\n"
" for nb in graph.get(node, []):\n"
" if nb not in visited:\n"
" visited.add(nb)\n"
" queue.append(nb)\n"
" return count\n"
),
"test_cases": [
{"input": "{0:[1,2],1:[],2:[]}, 0", "expected": "3"},
{"input": "{0:[1],1:[0]}, 0", "expected": "2"},
{"input": "{0:[]}, 0", "expected": "1"},
{"input": "{0:[1,2],1:[2],2:[0]}, 0", "expected": "3"},
],
"error_type": "LogicError",
"hint": "Mark nodes visited when enqueuing, not when dequeuing.",
},
# 24
{
"id": "bug_024",
"difficulty": "hard",
"title": "Missing memoization causing exponential recursion",
"description": "Fix the recursive function that recomputes overlapping subproblems.",
"buggy_code": (
"def fib_memo(n, memo={}):\n"
" if n <= 1:\n"
" return n\n"
" return fib_memo(n - 1) + fib_memo(n - 2)\n"
),
"fixed_code": (
"def fib_memo(n, memo={}):\n"
" if n <= 1:\n"
" return n\n"
" if n in memo:\n"
" return memo[n]\n"
" memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)\n"
" return memo[n]\n"
),
"test_cases": [
{"input": "0", "expected": "0"},
{"input": "1", "expected": "1"},
{"input": "10", "expected": "55"},
{"input": "20", "expected": "6765"},
],
"error_type": "LogicError",
"hint": "Check memo before recursing and store the result after computing.",
},
# 25
{
"id": "bug_025",
"difficulty": "hard",
"title": "Wrong two-pointer movement direction",
"description": "Fix two-pointer sum that moves the wrong pointer.",
"buggy_code": (
"def two_sum_sorted(arr, target):\n"
" lo, hi = 0, len(arr) - 1\n"
" while lo < hi:\n"
" s = arr[lo] + arr[hi]\n"
" if s == target:\n"
" return [lo, hi]\n"
" elif s < target:\n"
" hi -= 1\n"
" else:\n"
" lo += 1\n"
" return []\n"
),
"fixed_code": (
"def two_sum_sorted(arr, target):\n"
" lo, hi = 0, len(arr) - 1\n"
" while lo < hi:\n"
" s = arr[lo] + arr[hi]\n"
" if s == target:\n"
" return [lo, hi]\n"
" elif s < target:\n"
" lo += 1\n"
" else:\n"
" hi -= 1\n"
" return []\n"
),
"test_cases": [
{"input": "[1, 2, 3, 4, 6], 6", "expected": "[1, 3]"},
{"input": "[1, 3, 5, 7], 8", "expected": "[0, 3]"},
{"input": "[1, 2], 3", "expected": "[0, 1]"},
{"input": "[1, 2, 3], 10", "expected": "[]"},
],
"error_type": "LogicError",
"hint": "Sum too small β†’ move lo up (increase); sum too large β†’ move hi down.",
},
# 26
{
"id": "bug_026",
"difficulty": "hard",
"title": "Wrong greedy choice (min vs max)",
"description": "Fix the greedy function that should pick the largest item that fits.",
"buggy_code": (
"def greedy_knapsack(weights, capacity):\n"
" weights = sorted(weights)\n"
" total = 0\n"
" for w in weights:\n"
" if total + w <= capacity:\n"
" total += w\n"
" return total\n"
),
"fixed_code": (
"def greedy_knapsack(weights, capacity):\n"
" weights = sorted(weights, reverse=True)\n"
" total = 0\n"
" for w in weights:\n"
" if total + w <= capacity:\n"
" total += w\n"
" return total\n"
),
"test_cases": [
{"input": "[4, 3, 1], 5", "expected": "5"},
{"input": "[1, 2, 3], 4", "expected": "4"},
{"input": "[5], 5", "expected": "5"},
{"input": "[3, 3, 3], 6", "expected": "6"},
],
"error_type": "LogicError",
"hint": "Sort descending to pick heaviest items first for max fill.",
},
# 27
{
"id": "bug_027",
"difficulty": "hard",
"title": "Off-by-one in sliding window boundary",
"description": "Fix the sliding window that computes max sum of k consecutive elements.",
"buggy_code": (
"def max_window_sum(arr, k):\n"
" window = sum(arr[:k])\n"
" best = window\n"
" for i in range(1, len(arr) - k):\n"
" window += arr[i + k - 1] - arr[i - 1]\n"
" best = max(best, window)\n"
" return best\n"
),
"fixed_code": (
"def max_window_sum(arr, k):\n"
" window = sum(arr[:k])\n"
" best = window\n"
" for i in range(1, len(arr) - k + 1):\n"
" window += arr[i + k - 1] - arr[i - 1]\n"
" best = max(best, window)\n"
" return best\n"
),
"test_cases": [
{"input": "[1, 3, -1, -3, 5, 3], 3", "expected": "5"},
{"input": "[2, 1, 5, 1, 3, 2], 3", "expected": "9"},
{"input": "[1, 2, 3], 2", "expected": "5"},
{"input": "[5], 1", "expected": "5"},
],
"error_type": "LogicError",
"hint": "The range should go to len(arr)-k+1 to include the last window.",
},
# 28
{
"id": "bug_028",
"difficulty": "hard",
"title": "Wrong merge logic in merge sort",
"description": "Fix the merge step that uses the wrong comparison operator.",
"buggy_code": (
"def merge(left, right):\n"
" result = []\n"
" i = j = 0\n"
" while i < len(left) and j < len(right):\n"
" if left[i] > right[j]:\n"
" result.append(left[i])\n"
" i += 1\n"
" else:\n"
" result.append(right[j])\n"
" j += 1\n"
" result.extend(left[i:])\n"
" result.extend(right[j:])\n"
" return result\n"
),
"fixed_code": (
"def merge(left, right):\n"
" result = []\n"
" i = j = 0\n"
" while i < len(left) and j < len(right):\n"
" if left[i] <= right[j]:\n"
" result.append(left[i])\n"
" i += 1\n"
" else:\n"
" result.append(right[j])\n"
" j += 1\n"
" result.extend(left[i:])\n"
" result.extend(right[j:])\n"
" return result\n"
),
"test_cases": [
{"input": "[1, 3], [2, 4]", "expected": "[1, 2, 3, 4]"},
{"input": "[1], [2]", "expected": "[1, 2]"},
{"input": "[], [1, 2]", "expected": "[1, 2]"},
{"input": "[1, 2], []", "expected": "[1, 2]"},
],
"error_type": "LogicError",
"hint": "Use <= to keep the merge stable and ascending.",
},
# 29
{
"id": "bug_029",
"difficulty": "hard",
"title": "Wrong heap operation (push vs pop)",
"description": "Fix the function that finds the k-th largest element using a min-heap.",
"buggy_code": (
"import heapq\n"
"def kth_largest(nums, k):\n"
" heap = []\n"
" for n in nums:\n"
" heapq.heappush(heap, n)\n"
" if len(heap) > k:\n"
" heapq.heappush(heap, heapq.heappop(heap))\n"
" return heap[0]\n"
),
"fixed_code": (
"import heapq\n"
"def kth_largest(nums, k):\n"
" heap = []\n"
" for n in nums:\n"
" heapq.heappush(heap, n)\n"
" if len(heap) > k:\n"
" heapq.heappop(heap)\n"
" return heap[0]\n"
),
"test_cases": [
{"input": "[3, 2, 1, 5, 6, 4], 2", "expected": "5"},
{"input": "[3, 2, 3, 1, 2, 4, 5, 5, 6], 4", "expected": "4"},
{"input": "[1], 1", "expected": "1"},
{"input": "[5, 4, 3, 2, 1], 3", "expected": "3"},
],
"error_type": "LogicError",
"hint": "When the heap exceeds k elements just pop (remove smallest), don't re-push.",
},
# 30
{
"id": "bug_030",
"difficulty": "hard",
"title": "Wrong backtracking: missing undo step",
"description": "Fix the backtracking permutation generator that forgets to un-choose.",
"buggy_code": (
"def permutations(nums):\n"
" result = []\n"
" def backtrack(path, remaining):\n"
" if not remaining:\n"
" result.append(path[:])\n"
" return\n"
" for i in range(len(remaining)):\n"
" path.append(remaining[i])\n"
" backtrack(path, remaining[:i] + remaining[i+1:])\n"
" backtrack([], nums)\n"
" return sorted(result)\n"
),
"fixed_code": (
"def permutations(nums):\n"
" result = []\n"
" def backtrack(path, remaining):\n"
" if not remaining:\n"
" result.append(path[:])\n"
" return\n"
" for i in range(len(remaining)):\n"
" path.append(remaining[i])\n"
" backtrack(path, remaining[:i] + remaining[i+1:])\n"
" path.pop()\n"
" backtrack([], nums)\n"
" return sorted(result)\n"
),
"test_cases": [
{"input": "[1, 2]", "expected": "[[1, 2], [2, 1]]"},
{"input": "[1]", "expected": "[[1]]"},
{"input": "[1, 2, 3]", "expected": "[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]"},
{"input": "[3, 1]", "expected": "[[1, 3], [3, 1]]"},
],
"error_type": "LogicError",
"hint": "After each recursive call, pop the last element to undo the choice.",
},
]
# ═══════════════════════════════════════════════════════════════
# Module-level exports
# ═══════════════════════════════════════════════════════════════
ALL_PROBLEMS = EASY_BUGS + MEDIUM_BUGS + HARD_BUGS
TRAINING_SCENARIOS = sorted(
ALL_PROBLEMS,
key=lambda x: ["easy", "medium", "hard"].index(x["difficulty"]),
)
BUG_DATASET = ALL_PROBLEMS # backward-compat alias
# ── Helper functions ────────────────────────────────────────────
def get_problem(problem_id: str) -> dict | None:
"""Return a single problem by ID, or None if not found."""
for p in ALL_PROBLEMS:
if p["id"] == problem_id:
return p
return None
def get_all_problems() -> list[dict]:
"""Return all 30 problems."""
return ALL_PROBLEMS
def get_problem_ids() -> list[str]:
"""Return all problem IDs in order."""
return [p["id"] for p in ALL_PROBLEMS]
def get_problems_by_difficulty(difficulty: str) -> list[dict]:
"""Return problems filtered by difficulty ('easy'|'medium'|'hard')."""
return [p for p in ALL_PROBLEMS if p["difficulty"] == difficulty]