prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Replace cKDTree by KDTree
  see note here: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html#scipy.spatial.cKDTree
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `vpype/line_index.py`
  Content:
  ```
  1 from __future__ import annotations
  2 
  3 import logging
  4 from typing import Iterable
  5 
  6 import numpy as np
  7 from scipy.spatial import cKDTree as KDTree
  8 
  9 # REMINDER: anything added here must be added to docs/api.rst
  10 __all__ = ["LineIndex"]
  11 
  12 
  13 class LineIndex:
  14     """Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line
  15     collection.
  16 
  17     Implementation note: we use the `available` bool array because deleting stuff from the
  18     index is costly.
  19     """
  20 
  21     def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):
  22         self.lines = [line for line in lines if len(line) > 0]
  23         self.reverse = reverse
  24         self._make_index()
  25 
  26     def _make_index(self) -> None:
  27         logging.info(f"LineIndex: creating index for {len(self.lines)} lines")
  28         self.available = np.ones(shape=len(self.lines), dtype=bool)
  29 
  30         # create rtree index
  31         self.index = KDTree(
  32             np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)
  33         )
  34 
  35         # create reverse index
  36         if self.reverse:
  37             self.rindex = KDTree(
  38                 np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(
  39                     -1, 2
  40                 )
  41             )
  42 
  43     def _reindex(self) -> None:
  44         self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]
  45         self._make_index()
  46 
  47     def __len__(self) -> int:
  48         return np.count_nonzero(self.available)
  49 
  50     def __getitem__(self, item):
  51         return self.lines[item]
  52 
  53     def pop_front(self) -> np.ndarray:
  54         if len(self) == 0:
  55             raise RuntimeError
  56         idx = int(np.argmax(self.available))
  57         self.available[idx] = False
  58         return self.lines[idx]
  59 
  60     def pop(self, idx: int) -> np.ndarray | None:
  61         if not self.available[idx]:
  62             return None
  63         self.available[idx] = False
  64         return self.lines[idx]
  65 
  66     def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:
  67         """Find the closest line, assuming a maximum admissible distance.
  68         Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.
  69         `reverse` indicates whether or not a line ending has been matched instead of a start.
  70         False is always returned if index was created with `reverse=False`.s
  71         """
  72 
  73         ridx = None
  74         rdist: float | None = 0.0
  75 
  76         while True:
  77             reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)
  78             if reindex:
  79                 self._reindex()
  80                 continue
  81 
  82             if self.reverse:
  83                 reindex, ridx, rdist = self._find_nearest_within_in_index(
  84                     p, max_dist, self.rindex
  85                 )
  86                 if reindex:
  87                     self._reindex()
  88                     continue
  89             break
  90 
  91         if self.reverse:
  92             if idx is None and ridx is None:
  93                 return None, False
  94             elif idx is not None and ridx is None:
  95                 return idx, False
  96             elif idx is None and ridx is not None:
  97                 return ridx, True
  98             elif rdist < dist:  # type: ignore
  99                 return ridx, True
  100             else:
  101                 return idx, False
  102         else:
  103             return idx, False
  104 
  105     def _find_nearest_within_in_index(
  106         self, p: complex, max_dist: float, index: KDTree
  107     ) -> tuple[bool, int | None, float | None]:
  108         """Find nearest in specific index. Return (reindex, idx, dist) tuple, where
  109         reindex indicates if a reindex is needed.
  110         """
  111 
  112         # For performance reason, we query only a max of k candidates. In the special case
  113         # where all distances are not inf and none are available, we might have more than k
  114         # suitable candidate, so we reindex and loop. Otherwise, we check the query results
  115         # for availability and not inf and return anything found
  116         dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)
  117         dists = np.array(dists)
  118 
  119         not_inf = ~np.isinf(dists)
  120         if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):
  121             return True, None, 0
  122 
  123         candidates = self.available[idxs[not_inf]]
  124 
  125         if np.any(candidates):
  126             idx = np.argmax(candidates)
  127             return False, idxs[not_inf][idx], dists[not_inf][idx]
  128         else:
  129             return False, None, 0
  130 
  131     # noinspection PyUnboundLocalVariable
  132     def find_nearest(self, p: complex) -> tuple[int, bool]:
  133         while True:
  134             idx, dist = self._find_nearest_in_index(p, self.index)
  135             if self.reverse:
  136                 ridx, rdist = self._find_nearest_in_index(p, self.rindex)
  137 
  138                 if ridx is not None and idx is not None:
  139                     break
  140             elif idx is not None:
  141                 break
  142             self._reindex()
  143 
  144         if self.reverse:
  145             if rdist < dist:
  146                 return ridx, True  # type: ignore
  147             else:
  148                 return idx, False
  149         else:
  150             return idx, False
  151 
  152     def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:
  153         """Check the N nearest lines, hopefully find one that is active."""
  154 
  155         dists, idxs = index.query((p.real, p.imag), k=100)
  156         for dist, idx in zip(dists, idxs):
  157             if ~np.isinf(dist) and self.available[idx]:
  158                 return idx, dist
  159 
  160         return None, 0.0
  161
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/vpype/line_index.py b/vpype/line_index.py
  --- a/vpype/line_index.py
  +++ b/vpype/line_index.py
  @@ -4,14 +4,14 @@
   from typing import Iterable
   
   import numpy as np
  -from scipy.spatial import cKDTree as KDTree
  +from scipy.spatial import KDTree
   
   # REMINDER: anything added here must be added to docs/api.rst
   __all__ = ["LineIndex"]
   
   
   class LineIndex:
  -    """Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line
  +    """Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line
       collection.
   
       Implementation note: we use the `available` bool array because deleting stuff from the
  
verification_info: {"golden_diff": "diff --git a/vpype/line_index.py b/vpype/line_index.py\n--- a/vpype/line_index.py\n+++ b/vpype/line_index.py\n@@ -4,14 +4,14 @@\n from typing import Iterable\n \n import numpy as np\n-from scipy.spatial import cKDTree as KDTree\n+from scipy.spatial import KDTree\n \n # REMINDER: anything added here must be added to docs/api.rst\n __all__ = [\"LineIndex\"]\n \n \n class LineIndex:\n-    \"\"\"Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line\n+    \"\"\"Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line\n     collection.\n \n     Implementation note: we use the `available` bool array because deleting stuff from the\n", "issue": "Replace cKDTree by KDTree\nsee note here: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html#scipy.spatial.cKDTree\n", "before_files": [{"content": "from __future__ import annotations\n\nimport logging\nfrom typing import Iterable\n\nimport numpy as np\nfrom scipy.spatial import cKDTree as KDTree\n\n# REMINDER: anything added here must be added to docs/api.rst\n__all__ = [\"LineIndex\"]\n\n\nclass LineIndex:\n    \"\"\"Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line\n    collection.\n\n    Implementation note: we use the `available` bool array because deleting stuff from the\n    index is costly.\n    \"\"\"\n\n    def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):\n        self.lines = [line for line in lines if len(line) > 0]\n        self.reverse = reverse\n        self._make_index()\n\n    def _make_index(self) -> None:\n        logging.info(f\"LineIndex: creating index for {len(self.lines)} lines\")\n        self.available = np.ones(shape=len(self.lines), dtype=bool)\n\n        # create rtree index\n        self.index = KDTree(\n            np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)\n        )\n\n        # create reverse index\n        if self.reverse:\n            self.rindex = KDTree(\n                np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(\n                    -1, 2\n                )\n            )\n\n    def _reindex(self) -> None:\n        self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]\n        self._make_index()\n\n    def __len__(self) -> int:\n        return np.count_nonzero(self.available)\n\n    def __getitem__(self, item):\n        return self.lines[item]\n\n    def pop_front(self) -> np.ndarray:\n        if len(self) == 0:\n            raise RuntimeError\n        idx = int(np.argmax(self.available))\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def pop(self, idx: int) -> np.ndarray | None:\n        if not self.available[idx]:\n            return None\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:\n        \"\"\"Find the closest line, assuming a maximum admissible distance.\n        Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.\n        `reverse` indicates whether or not a line ending has been matched instead of a start.\n        False is always returned if index was created with `reverse=False`.s\n        \"\"\"\n\n        ridx = None\n        rdist: float | None = 0.0\n\n        while True:\n            reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)\n            if reindex:\n                self._reindex()\n                continue\n\n            if self.reverse:\n                reindex, ridx, rdist = self._find_nearest_within_in_index(\n                    p, max_dist, self.rindex\n                )\n                if reindex:\n                    self._reindex()\n                    continue\n            break\n\n        if self.reverse:\n            if idx is None and ridx is None:\n                return None, False\n            elif idx is not None and ridx is None:\n                return idx, False\n            elif idx is None and ridx is not None:\n                return ridx, True\n            elif rdist < dist:  # type: ignore\n                return ridx, True\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_within_in_index(\n        self, p: complex, max_dist: float, index: KDTree\n    ) -> tuple[bool, int | None, float | None]:\n        \"\"\"Find nearest in specific index. Return (reindex, idx, dist) tuple, where\n        reindex indicates if a reindex is needed.\n        \"\"\"\n\n        # For performance reason, we query only a max of k candidates. In the special case\n        # where all distances are not inf and none are available, we might have more than k\n        # suitable candidate, so we reindex and loop. Otherwise, we check the query results\n        # for availability and not inf and return anything found\n        dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)\n        dists = np.array(dists)\n\n        not_inf = ~np.isinf(dists)\n        if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):\n            return True, None, 0\n\n        candidates = self.available[idxs[not_inf]]\n\n        if np.any(candidates):\n            idx = np.argmax(candidates)\n            return False, idxs[not_inf][idx], dists[not_inf][idx]\n        else:\n            return False, None, 0\n\n    # noinspection PyUnboundLocalVariable\n    def find_nearest(self, p: complex) -> tuple[int, bool]:\n        while True:\n            idx, dist = self._find_nearest_in_index(p, self.index)\n            if self.reverse:\n                ridx, rdist = self._find_nearest_in_index(p, self.rindex)\n\n                if ridx is not None and idx is not None:\n                    break\n            elif idx is not None:\n                break\n            self._reindex()\n\n        if self.reverse:\n            if rdist < dist:\n                return ridx, True  # type: ignore\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:\n        \"\"\"Check the N nearest lines, hopefully find one that is active.\"\"\"\n\n        dists, idxs = index.query((p.real, p.imag), k=100)\n        for dist, idx in zip(dists, idxs):\n            if ~np.isinf(dist) and self.available[idx]:\n                return idx, dist\n\n        return None, 0.0\n", "path": "vpype/line_index.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport logging\nfrom typing import Iterable\n\nimport numpy as np\nfrom scipy.spatial import KDTree\n\n# REMINDER: anything added here must be added to docs/api.rst\n__all__ = [\"LineIndex\"]\n\n\nclass LineIndex:\n    \"\"\"Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line\n    collection.\n\n    Implementation note: we use the `available` bool array because deleting stuff from the\n    index is costly.\n    \"\"\"\n\n    def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):\n        self.lines = [line for line in lines if len(line) > 0]\n        self.reverse = reverse\n        self._make_index()\n\n    def _make_index(self) -> None:\n        logging.info(f\"LineIndex: creating index for {len(self.lines)} lines\")\n        self.available = np.ones(shape=len(self.lines), dtype=bool)\n\n        # create rtree index\n        self.index = KDTree(\n            np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)\n        )\n\n        # create reverse index\n        if self.reverse:\n            self.rindex = KDTree(\n                np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(\n                    -1, 2\n                )\n            )\n\n    def _reindex(self) -> None:\n        self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]\n        self._make_index()\n\n    def __len__(self) -> int:\n        return np.count_nonzero(self.available)\n\n    def __getitem__(self, item):\n        return self.lines[item]\n\n    def pop_front(self) -> np.ndarray:\n        if len(self) == 0:\n            raise RuntimeError\n        idx = int(np.argmax(self.available))\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def pop(self, idx: int) -> np.ndarray | None:\n        if not self.available[idx]:\n            return None\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:\n        \"\"\"Find the closest line, assuming a maximum admissible distance.\n        Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.\n        `reverse` indicates whether or not a line ending has been matched instead of a start.\n        False is always returned if index was created with `reverse=False`.s\n        \"\"\"\n\n        ridx = None\n        rdist: float | None = 0.0\n\n        while True:\n            reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)\n            if reindex:\n                self._reindex()\n                continue\n\n            if self.reverse:\n                reindex, ridx, rdist = self._find_nearest_within_in_index(\n                    p, max_dist, self.rindex\n                )\n                if reindex:\n                    self._reindex()\n                    continue\n            break\n\n        if self.reverse:\n            if idx is None and ridx is None:\n                return None, False\n            elif idx is not None and ridx is None:\n                return idx, False\n            elif idx is None and ridx is not None:\n                return ridx, True\n            elif rdist < dist:  # type: ignore\n                return ridx, True\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_within_in_index(\n        self, p: complex, max_dist: float, index: KDTree\n    ) -> tuple[bool, int | None, float | None]:\n        \"\"\"Find nearest in specific index. Return (reindex, idx, dist) tuple, where\n        reindex indicates if a reindex is needed.\n        \"\"\"\n\n        # For performance reason, we query only a max of k candidates. In the special case\n        # where all distances are not inf and none are available, we might have more than k\n        # suitable candidate, so we reindex and loop. Otherwise, we check the query results\n        # for availability and not inf and return anything found\n        dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)\n        dists = np.array(dists)\n\n        not_inf = ~np.isinf(dists)\n        if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):\n            return True, None, 0\n\n        candidates = self.available[idxs[not_inf]]\n\n        if np.any(candidates):\n            idx = np.argmax(candidates)\n            return False, idxs[not_inf][idx], dists[not_inf][idx]\n        else:\n            return False, None, 0\n\n    # noinspection PyUnboundLocalVariable\n    def find_nearest(self, p: complex) -> tuple[int, bool]:\n        while True:\n            idx, dist = self._find_nearest_in_index(p, self.index)\n            if self.reverse:\n                ridx, rdist = self._find_nearest_in_index(p, self.rindex)\n\n                if ridx is not None and idx is not None:\n                    break\n            elif idx is not None:\n                break\n            self._reindex()\n\n        if self.reverse:\n            if rdist < dist:\n                return ridx, True  # type: ignore\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:\n        \"\"\"Check the N nearest lines, hopefully find one that is active.\"\"\"\n\n        dists, idxs = index.query((p.real, p.imag), k=100)\n        for dist, idx in zip(dists, idxs):\n            if ~np.isinf(dist) and self.available[idx]:\n                return idx, dist\n\n        return None, 0.0\n", "path": "vpype/line_index.py"}]}