prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  docs: infinite build when using `mkdocs serve`
  It appears that when using `mkdocs serve` the docs are repeatedly rebuilt to no end.
  
  I suspect there's a file that we're generating (maybe the operation matrix?) that is being considered new and triggering a rebuild.
  
  </issue>
  <code>
  [start of gen_matrix.py]
  1 from pathlib import Path
  2 
  3 import pandas as pd
  4 import tomli
  5 
  6 import ibis
  7 import ibis.expr.operations as ops
  8 
  9 
  10 def get_backends():
  11     pyproject = tomli.loads(Path("pyproject.toml").read_text())
  12     backends = pyproject["tool"]["poetry"]["plugins"]["ibis.backends"]
  13     del backends["spark"]
  14     return [(backend, getattr(ibis, backend)) for backend in sorted(backends.keys())]
  15 
  16 
  17 def get_leaf_classes(op):
  18     for child_class in op.__subclasses__():
  19         if not child_class.__subclasses__():
  20             yield child_class
  21         else:
  22             yield from get_leaf_classes(child_class)
  23 
  24 
  25 EXCLUDED_OPS = {
  26     # Never translates into anything
  27     ops.UnresolvedExistsSubquery,
  28     ops.UnresolvedNotExistsSubquery,
  29     ops.ScalarParameter,
  30 }
  31 
  32 INCLUDED_OPS = {
  33     # Parent class of MultiQuantile so it's ignored by `get_backends()`
  34     ops.Quantile,
  35 }
  36 
  37 
  38 ICONS = {
  39     True: ":material-check-decagram:{ .verified }",
  40     False: ":material-cancel:{ .cancel }",
  41 }
  42 
  43 
  44 def main():
  45     possible_ops = (
  46         frozenset(get_leaf_classes(ops.Value)) | INCLUDED_OPS
  47     ) - EXCLUDED_OPS
  48 
  49     support = {"operation": [f"`{op.__name__}`" for op in possible_ops]}
  50     support.update(
  51         (name, list(map(backend.has_operation, possible_ops)))
  52         for name, backend in get_backends()
  53     )
  54 
  55     df = pd.DataFrame(support).set_index("operation").sort_index()
  56 
  57     counts = df.sum().sort_values(ascending=False)
  58     num_ops = len(possible_ops)
  59     coverage = (
  60         counts.map(lambda n: f"_{n} ({round(100 * n / num_ops)}%)_")
  61         .to_frame(name="**API Coverage**")
  62         .T
  63     )
  64 
  65     ops_table = df.loc[:, counts.index].replace(ICONS)
  66     table = pd.concat([coverage, ops_table])
  67     dst = Path(__file__).parent.joinpath(
  68         "docs",
  69         "backends",
  70         "support_matrix.csv",
  71     )
  72     table.to_csv(dst, index_label="Backends")
  73 
  74 
  75 main()
  76
  [end of gen_matrix.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/gen_matrix.py b/gen_matrix.py
  --- a/gen_matrix.py
  +++ b/gen_matrix.py
  @@ -69,7 +69,15 @@
           "backends",
           "support_matrix.csv",
       )
  -    table.to_csv(dst, index_label="Backends")
  +
  +    if dst.exists():
  +        old = pd.read_csv(dst, index_col="Backends")
  +        should_write = not old.equals(table)
  +    else:
  +        should_write = True
  +
  +    if should_write:
  +        table.to_csv(dst, index_label="Backends")
   
   
   main()
  
verification_info: {"golden_diff": "diff --git a/gen_matrix.py b/gen_matrix.py\n--- a/gen_matrix.py\n+++ b/gen_matrix.py\n@@ -69,7 +69,15 @@\n         \"backends\",\n         \"support_matrix.csv\",\n     )\n-    table.to_csv(dst, index_label=\"Backends\")\n+\n+    if dst.exists():\n+        old = pd.read_csv(dst, index_col=\"Backends\")\n+        should_write = not old.equals(table)\n+    else:\n+        should_write = True\n+\n+    if should_write:\n+        table.to_csv(dst, index_label=\"Backends\")\n \n \n main()\n", "issue": "docs: infinite build when using `mkdocs serve`\nIt appears that when using `mkdocs serve` the docs are repeatedly rebuilt to no end.\r\n\r\nI suspect there's a file that we're generating (maybe the operation matrix?) that is being considered new and triggering a rebuild.\n", "before_files": [{"content": "from pathlib import Path\n\nimport pandas as pd\nimport tomli\n\nimport ibis\nimport ibis.expr.operations as ops\n\n\ndef get_backends():\n    pyproject = tomli.loads(Path(\"pyproject.toml\").read_text())\n    backends = pyproject[\"tool\"][\"poetry\"][\"plugins\"][\"ibis.backends\"]\n    del backends[\"spark\"]\n    return [(backend, getattr(ibis, backend)) for backend in sorted(backends.keys())]\n\n\ndef get_leaf_classes(op):\n    for child_class in op.__subclasses__():\n        if not child_class.__subclasses__():\n            yield child_class\n        else:\n            yield from get_leaf_classes(child_class)\n\n\nEXCLUDED_OPS = {\n    # Never translates into anything\n    ops.UnresolvedExistsSubquery,\n    ops.UnresolvedNotExistsSubquery,\n    ops.ScalarParameter,\n}\n\nINCLUDED_OPS = {\n    # Parent class of MultiQuantile so it's ignored by `get_backends()`\n    ops.Quantile,\n}\n\n\nICONS = {\n    True: \":material-check-decagram:{ .verified }\",\n    False: \":material-cancel:{ .cancel }\",\n}\n\n\ndef main():\n    possible_ops = (\n        frozenset(get_leaf_classes(ops.Value)) | INCLUDED_OPS\n    ) - EXCLUDED_OPS\n\n    support = {\"operation\": [f\"`{op.__name__}`\" for op in possible_ops]}\n    support.update(\n        (name, list(map(backend.has_operation, possible_ops)))\n        for name, backend in get_backends()\n    )\n\n    df = pd.DataFrame(support).set_index(\"operation\").sort_index()\n\n    counts = df.sum().sort_values(ascending=False)\n    num_ops = len(possible_ops)\n    coverage = (\n        counts.map(lambda n: f\"_{n} ({round(100 * n / num_ops)}%)_\")\n        .to_frame(name=\"**API Coverage**\")\n        .T\n    )\n\n    ops_table = df.loc[:, counts.index].replace(ICONS)\n    table = pd.concat([coverage, ops_table])\n    dst = Path(__file__).parent.joinpath(\n        \"docs\",\n        \"backends\",\n        \"support_matrix.csv\",\n    )\n    table.to_csv(dst, index_label=\"Backends\")\n\n\nmain()\n", "path": "gen_matrix.py"}]}