Repository: atksh/python_prtree (language: python)

Issue:
generalization to 3 dimensions
Hi, 
Thank you for this nice work. 
I love the fact it support batch query (unlike rtree https://github.com/Toblerity/rtree/issues/178). 
It would be great if it could support 3 dimensions (or maybe even n dimensions if that does not impact the speed). Does that seem easily feasible ? 
This could then potentially be used in the trimesh library to speed thing up and solve issue https://github.com/mikedh/trimesh/issues/1116



Discussion / hints:
Hi,

It is feasible, but not easy due to the implementation of this library. This is because the four real numbers representing a two-dimensional rectangle are stored as fixed-length vectors in order to use SIMD. Plus, the building method of the Priority R-tree depends on the dimensions. 

Another way to build a 3d-prtree may be to use three 2d-prtrees, like xy, yz, zx projection planes. The query result could be the intersection of the three results. This can be implemented by c++.
ok, thank for the explanation! I believe it would need to be a real 3d rtree to allow efficient queries

Fix patch:
diff --git a/.gitignore b/.gitignore
index 3fcc660..a84b318 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,18 @@
-/cmake-build-*/
-/docker/
-/ldata/
-/build/
-/dist/
-/_build/
-/_generate/
+cmake-build-*/
+docker/
+ldata/
+build/
+dist/
+_build/
+_generate/
 *.so
 *.so.*
 *.py[cod]
 *.egg-info
-/.eggs/
-/.idea/
-/input/*
-!/input/.gitkeep
+.eggs/
+.idea/
+input/*
+!input/.gitkeep
 __pycache__/
 .ipynb_checkpoints/
-/.vscode/
+.vscode/
diff --git a/.gitmodules b/.gitmodules
index 6e3306b..172da95 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
 [submodule "pybind11"]
 	path = third/pybind11
 	url = https://github.com/pybind/pybind11.git
-[submodule "third/xsimd"]
-	path = third/xsimd
-	url = https://github.com/xtensor-stack/xsimd
diff --git a/.travis.yml b/.travis.yml
index ddb944d..a11edc3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,11 +5,11 @@ jobs:
       services: docker
     - os: osx
       language: shell
-      osx_image: xcode11.6
+      osx_image: xcode12.5
     - os: windows
       language: shell
       before_install:
-        - choco install python --version 3.8.1
+        - choco install python --version 3.8.10
         - export PATH="/c/Python38:/c/Python38/Scripts:$PATH"
         - ln -s /c/Python38/python.exe /c/Python38/python3.exe
 env:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fd6b85d..efe19cf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,8 +18,7 @@ file(GLOB MYCPP "cpp/*")
 
 add_subdirectory(./third/pybind11/)
 add_subdirectory(./third/cereal/)
-add_subdirectory(./third/xsimd/)
 
 pybind11_add_module(PRTree ${MYCPP})
-target_link_libraries(PRTree PRIVATE cereal xsimd)
+target_link_libraries(PRTree PRIVATE cereal)
 
diff --git a/README.md b/README.md
index 1ede5e5..69be06e 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,8 @@
 "python_prtree" is a python implementation of Priority R-Tree (see reference below).
 Supported futures are as follows:
 
-- Construct Priority R-Tree(PRTree) from rectangles; an array of (xmin, xmax, ymin, ymax)
+- Construct Priority R-Tree(PRTree) from rectangles; an array of (xmin, xmax, ymin, ymax) / (xmin, xmax, ymin, ymax, zmin, zmax)
+  - Now supports 3D PRTree with python_prtree>=0.4.0
 - query and batch query with a rectangle(s)
 - insert and erase(delete) (but not optimized yet)
 
@@ -13,13 +14,13 @@ Supported futures are as follows:
 # Usage 
 ```python
 import numpy as np
-from python_prtree import PRTree
+from python_prtree import PRTree2D
 
 idxes = np.array([1, 2])  # must be unique because it uses idx as key for hash map
 rects = np.array([[0.0, 1.0, 0.0, 0.5],
                   [1.0, 1.2, 2.5, 3.0]])  # (xmin, xmax, ymin, ymax)
 
-prtree = PRTree(idxes, rects)  # initial construction
+prtree = PRTree2D(idxes, rects)  # initial construction
 
 q = np.array([[0.5, 0.6, 0.2, 0.3],
               [0.8, 1.5, 0.5, 3.5]])
@@ -28,7 +29,28 @@ print(result)
 # [[1], [1, 2]]
 ```
 
-## New features(`python-prtree>=0.3.0`)
+## New features (`python-prtree>=0.4.0`)
+You can use PRTree3D:
+
+```python
+import numpy as np
+from python_prtree import PRTree3D
+
+idxes = np.array([1, 2])  # must be unique because it uses idx as key for hash map
+rects = np.array([[0.0, 1.0, 0.0, 0.5, 0.0, 0.5],
+                  [1.0, 1.2, 2.5, 3.0, 2.5, 3.0]])  # (xmin, xmax, ymin, ymax, zmin, zmax)
+
+prtree = PRTree3D(idxes, rects)  # initial construction
+
+q = np.array([[0.5, 0.6, 0.2, 0.3, 0.2, 0.3],
+              [0.8, 1.5, 0.5, 3.5, 0.5, 3.5]])
+result = prtree.batch_query(q)
+print(result)
+# [[1], [1, 2]]
+```
+
+
+## New features (`python-prtree>=0.3.0`)
 You can save and load a binary file as follows:
 
 ```python
@@ -85,13 +107,33 @@ This installation needs cmake.
 
 # Performance
 ## Construction
-![fig1](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/fig1.png)
+### 2d
+
+![2d_fig1](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/2d_fig1.png)
+
+### 3d
+
+![3d_fig1](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/3d_fig1.png)
 
 ## Query and batch query
-![fig2](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/fig2.png)
+
+### 2d
+
+![2d_fig2](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/2d_fig2.png)
+
+### 3d
+
+![3d_fig2](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/2d_fig2.png)
 
 ## Delete and insert
-![fig3](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/fig3.png)
+
+### 2d
+
+![2d_fig3](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/2d_fig3.png)
+
+### 3d
+
+![3d_fig3](https://raw.githubusercontent.com/atksh/python_prtree/master/docs/images/2d_fig3.png)
 
 # Requirement
 - numpy
diff --git a/cpp/.vscode/c_cpp_properties.json b/cpp/.vscode/c_cpp_properties.json
deleted file mode 100644
index d13d95a..0000000
--- a/cpp/.vscode/c_cpp_properties.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-    "configurations": [
-        {
-            "name": "Linux",
-            "includePath": [
-                "${workspaceFolder}/**"
-            ],
-            "defines": [],
-            "compilerPath": "/usr/bin/gcc",
-            "cStandard": "c11",
-            "cppStandard": "c++17",
-            "intelliSenseMode": "gcc-x64"
-        }
-    ],
-    "version": 4
-}
\ No newline at end of file
diff --git a/cpp/function_ref.h b/cpp/function_ref.h
deleted file mode 100644
index a038b0f..0000000
--- a/cpp/function_ref.h
+++ /dev/null
@@ -1,239 +0,0 @@
-///
-// function_ref - A low-overhead non-owning function
-// Written in 2017 by Simon Brand (@TartanLlama)
-//
-// To the extent possible under law, the author(s) have dedicated all
-// copyright and related and neighboring rights to this software to the
-// public domain worldwide. This software is distributed without any warranty.
-//
-// You should have received a copy of the CC0 Public Domain Dedication
-// along with this software. If not, see
-// <http://creativecommons.org/publicdomain/zero/1.0/>.
-///
-
-#pragma once
-#ifndef TL_FUNCTION_REF_HPP
-#define TL_FUNCTION_REF_HPP
-
-#define TL_FUNCTION_REF_VERSION_MAJOR 1
-#define TL_FUNCTION_REF_VERSION_MINOR 0
-#define TL_FUNCTION_REF_VERSION_PATCH 0
-
-#if (defined(_MSC_VER) && _MSC_VER == 1900)
-/// \exclude
-#define TL_FUNCTION_REF_MSVC2015
-#endif
-
-#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
-     !defined(__clang__))
-/// \exclude
-#define TL_FUNCTION_REF_GCC49
-#endif
-
-#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \
-     !defined(__clang__))
-/// \exclude
-#define TL_FUNCTION_REF_GCC54
-#endif
-
-#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
-     !defined(__clang__))
-// GCC < 5 doesn't support overloading on const&& for member functions
-/// \exclude
-#define TL_FUNCTION_REF_NO_CONSTRR
-#endif
-
-#if __cplusplus > 201103L
-/// \exclude
-#define TL_FUNCTION_REF_CXX14
-#endif
-
-// constexpr implies const in C++11, not C++14
-#if (__cplusplus == 201103L || defined(TL_FUNCTION_REF_MSVC2015) || \
-     defined(TL_FUNCTION_REF_GCC49)) &&                             \
-    !defined(TL_FUNCTION_REF_GCC54)
-/// \exclude
-#define TL_FUNCTION_REF_11_CONSTEXPR
-#else
-/// \exclude
-#define TL_FUNCTION_REF_11_CONSTEXPR constexpr
-#endif
-
-#include <functional>
-#include <utility>
-
-namespace tl
-{
-  namespace detail
-  {
-    namespace fnref
-    {
-      // C++14-style aliases for brevity
-      template <class T>
-      using remove_const_t = typename std::remove_const<T>::type;
-      template <class T>
-      using remove_reference_t = typename std::remove_reference<T>::type;
-      template <class T>
-      using decay_t = typename std::decay<T>::type;
-      template <bool E, class T = void>
-      using enable_if_t = typename std::enable_if<E, T>::type;
-      template <bool B, class T, class F>
-      using conditional_t = typename std::conditional<B, T, F>::type;
-
-      // std::invoke from C++17
-      // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
-      template <typename Fn, typename... Args,
-                typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>,
-                int = 0>
-      constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
-          noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
-          -> decltype(std::mem_fn(f)(std::forward<Args>(args)...))
-      {
-        return std::mem_fn(f)(std::forward<Args>(args)...);
-      }
-
-      template <typename Fn, typename... Args,
-                typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>{}>>
-      constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
-          noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
-          -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...))
-      {
-        return std::forward<Fn>(f)(std::forward<Args>(args)...);
-      }
-
-      // std::invoke_result from C++17
-      template <class F, class, class... Us>
-      struct invoke_result_impl;
-
-      template <class F, class... Us>
-      struct invoke_result_impl<
-          F, decltype(tl::detail::fnref::invoke(std::declval<F>(), std::declval<Us>()...), void()),
-          Us...>
-      {
-        using type = decltype(tl::detail::fnref::invoke(std::declval<F>(), std::declval<Us>()...));
-      };
-
-      template <class F, class... Us>
-      using invoke_result = invoke_result_impl<F, void, Us...>;
-
-      template <class F, class... Us>
-      using invoke_result_t = typename invoke_result<F, Us...>::type;
-
-      template <class, class R, class F, class... Args>
-      struct is_invocable_r_impl : std::false_type
-      {
-      };
-
-      template <class R, class F, class... Args>
-      struct is_invocable_r_impl<
-          typename std::is_convertible<invoke_result_t<F, Args...>, R>::type, R, F, Args...>
-          : std::true_type
-      {
-      };
-
-      template <class R, class F, class... Args>
-      using is_invocable_r = is_invocable_r_impl<std::true_type, R, F, Args...>;
-
-    } // namespace fnref
-  }   // namespace detail
-
-  /// A lightweight non-owning reference to a callable.
-  ///
-  /// Example usage:
-  ///
-  /// ```cpp
-  /// void foo (function_ref<int(int)> func) {
-  ///     std::cout << "Result is " << func(21); //42
-  /// }
-  ///
-  /// foo([](int i) { return i*2; });
-  template <class F>
-  class function_ref;
-
-  /// Specialization for function types.
-  template <class R, class... Args>
-  class function_ref<R(Args...)>
-  {
-  public:
-    constexpr function_ref() noexcept = delete;
-
-    /// Creates a `function_ref` which refers to the same callable as `rhs`.
-    constexpr function_ref(const function_ref<R(Args...)> &rhs) noexcept = default;
-
-    /// Constructs a `function_ref` referring to `f`.
-    ///
-    /// \synopsis template <typename F> constexpr function_ref(F &&f) noexcept
-    template <typename F,
-              detail::fnref::enable_if_t<
-                  !std::is_same<detail::fnref::decay_t<F>, function_ref>::value &&
-                  detail::fnref::is_invocable_r<R, F &&, Args...>::value> * = nullptr>
-    TL_FUNCTION_REF_11_CONSTEXPR function_ref(F &&f) noexcept
-        : obj_(const_cast<void *>(reinterpret_cast<const void *>(std::addressof(f))))
-    {
-      callback_ = [](void *obj, Args... args) -> R {
-        return detail::fnref::invoke(
-            *reinterpret_cast<typename std::add_pointer<F>::type>(obj),
-            std::forward<Args>(args)...);
-      };
-    }
-
-    /// Makes `*this` refer to the same callable as `rhs`.
-    TL_FUNCTION_REF_11_CONSTEXPR function_ref<R(Args...)> &
-    operator=(const function_ref<R(Args...)> &rhs) noexcept = default;
-
-    /// Makes `*this` refer to `f`.
-    ///
-    /// \synopsis template <typename F> constexpr function_ref &operator=(F &&f) noexcept;
-    template <typename F,
-              detail::fnref::enable_if_t<detail::fnref::is_invocable_r<R, F &&, Args...>::value>
-                  * = nullptr>
-    TL_FUNCTION_REF_11_CONSTEXPR function_ref<R(Args...)> &operator=(F &&f) noexcept
-    {
-      obj_ = reinterpret_cast<void *>(std::addressof(f));
-      callback_ = [](void *obj, Args... args) {
-        return detail::fnref::invoke(
-            *reinterpret_cast<typename std::add_pointer<F>::type>(obj),
-            std::forward<Args>(args)...);
-      };
-
-      return *this;
-    }
-
-    /// Swaps the referred callables of `*this` and `rhs`.
-    constexpr void swap(function_ref<R(Args...)> &rhs) noexcept
-    {
-      std::swap(obj_, rhs.obj_);
-      std::swap(callback_, rhs.callback_);
-    }
-
-    /// Call the stored callable with the given arguments.
-    R operator()(Args... args) const
-    {
-      return callback_(obj_, std::forward<Args>(args)...);
-    }
-
-  private:
-    void *obj_ = nullptr;
-    R (*callback_)
-    (void *, Args...) = nullptr;
-  };
-
-  /// Swaps the referred callables of `lhs` and `rhs`.
-  template <typename R, typename... Args>
-  constexpr void swap(function_ref<R(Args...)> &lhs,
-                      function_ref<R(Args...)> &rhs) noexcept
-  {
-    lhs.swap(rhs);
-  }
-
-#if __cplusplus >= 201703L
-  template <typename R, typename... Args>
-  function_ref(R (*)(Args...))->function_ref<R(Args...)>;
-
-// TODO, will require some kind of callable traits
-// template <typename F>
-// function_ref(F) -> function_ref</* deduced if possible */>;
-#endif
-} // namespace tl
-
-#endif
diff --git a/cpp/main.cc b/cpp/main.cc
index 1721ce5..02324d6 100644
--- a/cpp/main.cc
+++ b/cpp/main.cc
@@ -1,22 +1,51 @@
-#include <pybind11/pybind11.h>
+#include "prtree.h"
 #include <pybind11/numpy.h>
+#include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
-#include "prtree.h"
 
 namespace py = pybind11;
 
-using T = int64_t; // is a temporary type of template. You can change it and recompile this.
+using T = int64_t; // is a temporary type of template. You can change it and
+                   // recompile this.
 const int B = 6;   // the number of children of tree.
 
-PYBIND11_MODULE(PRTree, m)
-{
+PYBIND11_MODULE(PRTree, m) {
   m.doc() = R"pbdoc(
         INCOMPLETE Priority R-Tree
         Only supports for construct and find
         insert and delete are not supported.
     )pbdoc";
 
-  py::class_<PRTree<T, B>>(m, "PRTree")
+  py::class_<PRTree<T, B, 2>>(m, "PRTree2D")
+      .def(py::init<py::array_t<T>, py::array_t<float>>(), R"pbdoc(
+          Construct PRTree with init.
+        )pbdoc")
+      .def(py::init<>(), R"pbdoc(
+          Construct PRTree with .
+        )pbdoc")
+      .def(py::init<std::string>(), R"pbdoc(
+          Construct PRTree with load.
+        )pbdoc")
+      .def("query", &PRTree<T, B, 2>::find_one, R"pbdoc(
+          Find all indexes which has intersect with given bounding box.
+        )pbdoc")
+      .def("batch_query", &PRTree<T, B, 2>::find_all, R"pbdoc(
+          parallel query with openmp
+        )pbdoc")
+      .def("erase", &PRTree<T, B, 2>::erase, R"pbdoc(
+          Delete from prtree
+        )pbdoc")
+      .def("insert", &PRTree<T, B, 2>::insert, R"pbdoc(
+          Insert one to prtree
+        )pbdoc")
+      .def("save", &PRTree<T, B, 2>::save, R"pbdoc(
+          cereal save
+        )pbdoc")
+      .def("load", &PRTree<T, B, 2>::load, R"pbdoc(
+          cereal load
+        )pbdoc");
+
+  py::class_<PRTree<T, B, 3>>(m, "PRTree3D")
       .def(py::init<py::array_t<T>, py::array_t<float>>(), R"pbdoc(
           Construct PRTree with init.
         )pbdoc")
@@ -26,22 +55,22 @@ PYBIND11_MODULE(PRTree, m)
       .def(py::init<std::string>(), R"pbdoc(
           Construct PRTree with load.
         )pbdoc")
-      .def("query", &PRTree<T, B>::find_one, R"pbdoc(
+      .def("query", &PRTree<T, B, 3>::find_one, R"pbdoc(
           Find all indexes which has intersect with given bounding box.
         )pbdoc")
-      .def("batch_query", &PRTree<T, B>::find_all, R"pbdoc(
+      .def("batch_query", &PRTree<T, B, 3>::find_all, R"pbdoc(
           parallel query with openmp
         )pbdoc")
-      .def("erase", &PRTree<T, B>::erase, R"pbdoc(
+      .def("erase", &PRTree<T, B, 3>::erase, R"pbdoc(
           Delete from prtree
         )pbdoc")
-      .def("insert", &PRTree<T, B>::insert, R"pbdoc(
+      .def("insert", &PRTree<T, B, 3>::insert, R"pbdoc(
           Insert one to prtree
         )pbdoc")
-      .def("save", &PRTree<T, B>::save, R"pbdoc(
+      .def("save", &PRTree<T, B, 3>::save, R"pbdoc(
           cereal save
         )pbdoc")
-      .def("load", &PRTree<T, B>::load, R"pbdoc(
+      .def("load", &PRTree<T, B, 3>::load, R"pbdoc(
           cereal load
         )pbdoc");
 
diff --git a/cpp/prtree.h b/cpp/prtree.h
index 99916a7..35321b5 100644
--- a/cpp/prtree.h
+++ b/cpp/prtree.h
@@ -1,56 +1,49 @@
 #pragma once
+#include <algorithm>
 #include <array>
 #include <cmath>
+#include <cstdlib>
+#include <fstream>
+#include <future>
+#include <iostream>
+#include <iterator>
 #include <limits>
-#include <random>
-#include <vector>
-#include <unordered_map>
-#include <queue>
-#include <stack>
-#include <algorithm>
-#include <numeric>
 #include <memory>
-#include <iterator>
-#include <iostream>
-#include <fstream>
 #include <mutex>
-#include <thread>
-#include <future>
-#include <cstdlib>
-#include <pybind11/pybind11.h>
+#include <numeric>
 #include <pybind11/numpy.h>
+#include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
+#include <queue>
+#include <random>
+#include <stack>
+#include <thread>
+#include <unordered_map>
+#include <vector>
 
+#include <cereal/archives/json.hpp>
+#include <cereal/archives/portable_binary.hpp>
 #include <cereal/cereal.hpp>
+#include <cereal/types/array.hpp>
 #include <cereal/types/memory.hpp> //for smart pointers
 #include <cereal/types/string.hpp>
-#include <cereal/archives/json.hpp>
-#include <cereal/archives/portable_binary.hpp>
-#include <cereal/types/vector.hpp>
 #include <cereal/types/unordered_map.hpp>
+#include <cereal/types/vector.hpp>
 
-#include "xsimd/xsimd.hpp"
 #include "parallel.h"
-#include "function_ref.h"
-
-namespace xs = xsimd;
 
 using Real = float;
 
 namespace py = pybind11;
 using std::swap;
 
-template <class T, class U>
-using pair = std::pair<T, U>;
+template <class T, class U> using pair = std::pair<T, U>;
 
-template <class T>
-using vec = std::vector<T>;
+template <class T> using vec = std::vector<T>;
 
-template <class T>
-using deque = std::deque<T>;
+template <class T> using deque = std::deque<T>;
 
-template <class T>
-using queue = std::queue<T, deque<T>>;
+template <class T> using queue = std::queue<T, deque<T>>;
 
 static std::mt19937 rand_src(42);
 
@@ -62,184 +55,175 @@ static std::mt19937 rand_src(42);
 #define unlikely(x) (x)
 #endif
 
-template <typename Iter>
-inline Iter select_randomly(Iter start, Iter end)
-{
+template <typename Iter> Iter select_randomly(Iter start, Iter end) {
   std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1);
   std::advance(start, dis(rand_src));
   return start;
 };
 
-static const auto zero_values = xs::batch<Real, 4>(-1e100, -1e100, -1e100, -1e100);
-class BB
-{
+template <int D = 2> class BB {
 private:
-  xs::batch<Real, 4> values;
+  std::array<Real, 2 * D> values;
 
 public:
-  BB()
-  {
-    clear();
-  }
+  BB() { clear(); }
 
-  BB(const Real &_xmin, const Real &_xmax, const Real &_ymin, const Real &_ymax)
-  {
-    if (unlikely(_xmin > _xmax || _ymin > _ymax))
-    {
-      throw std::runtime_error("Impossible rectange was given. xmin < xmax and ymin < ymax must be satisfied.");
+  template <typename Iter> BB(const Iter &minima, const Iter &maxima) {
+    std::array<Real, 2 * D> v;
+    if (unlikely(minima.size() != maxima.size())) {
+      throw std::runtime_error("Invalid size");
+    }
+    const int n = minima.size();
+    if (unlikely(n != D)) {
+      throw std::runtime_error("Invalid size");
+    }
+    for (int i = 0; i < n; ++i) {
+      v[2 * i] = -minima[i];
+      v[2 * i + 1] = maxima[i];
     }
-    values = xs::batch<Real, 4>(-_xmin, _xmax, -_ymin, _ymax);
+    validate(v);
+    values = v;
   }
 
-  BB(const xs::batch<Real, 4> &v)
-  {
-    if (unlikely(-v[0] > v[1] || -v[2] > v[3]))
-    {
-      throw std::runtime_error("Impossible rectange was given. xmin < xmax and ymin < ymax must be satisfied.");
-    }
+  BB(const std::array<Real, 2 * D> v) {
+    validate(v);
     values = v;
   }
 
-  inline Real xmin() const { return -values[0]; }
-  inline Real xmax() const { return values[1]; }
-  inline Real ymin() const { return -values[2]; }
-  inline Real ymax() const { return values[3]; }
+  Real min(const int dim) const {
+    if (unlikely(dim < 0 || D <= dim)) {
+      throw std::runtime_error("Invalid dim");
+    }
+    return -values[2 * dim];
+  }
+  Real max(const int dim) const {
+    if (unlikely(dim < 0 || D <= dim)) {
+      throw std::runtime_error("Invalid dim");
+    }
+    return values[2 * dim + 1];
+  }
 
-  inline void clear()
-  {
-    values += zero_values;
+  bool validate(const std::array<Real, 2 * D> &v) const {
+    bool flag = false;
+    for (int i = 0; i < 2; ++i) {
+      if (-v[2 * i] > v[2 * i + 1]) {
+        flag = true;
+        break;
+      }
+    }
+    if (flag) {
+      throw std::runtime_error("Invalid bb");
+    }
+    return flag;
   }
+  void clear() { std::fill_n(values.begin(), 2 * D, -1e100); }
 
-  inline BB operator+(const BB &rhs) const
-  {
-    return BB(xs::fmax(values, rhs.values));
+  BB operator+(const BB &rhs) const {
+    std::array<Real, 2 * D> result;
+    for (int i = 0; i < 2 * D; ++i) {
+      result[i] = std::max(values[i], rhs.values[i]);
+    }
+    return BB<D>(result);
   }
 
-  inline BB operator+=(const BB &rhs)
-  {
-    values = xs::fmax(values, rhs.values);
+  BB operator+=(const BB &rhs) {
+    for (int i = 0; i < 2 * D; ++i) {
+      values[i] = std::max(values[i], rhs.values[i]);
+    }
     return *this;
   }
 
-  inline void expand(const Real &dx, const Real &dy)
-  {
-    auto d = xs::batch<Real, 4>(dx, dx, dy, dy);
-    values += d;
+  void expand(const std::array<Real, D> &delta) {
+    for (int i = 0; i < D; ++i) {
+      values[2 * i] += delta[i];
+      values[2 * i + 1] += delta[i];
+    }
   }
 
-  inline bool operator()(const BB &target) const
-  { // whether this and target has any intersect
-    const auto m = xs::fmin(values, target.values);
-    //const bool c1 = unlikely(std::max(xmin(), target.xmin()) <= std::min(xmax(), target.xmax()));
-    //const bool c2 = unlikely(std::max(ymin(), target.ymin()) <= std::min(ymax(), target.ymax()));
-    //return unlikely(c1 && c2);
-    return (-m[0] <= m[1]) && (-m[2] <= m[3]);
+  bool operator()(
+      const BB &target) const { // whether this and target has any intersect
+    std::array<Real, 2 * D> result;
+    for (int i = 0; i < 2 * D; ++i) {
+      result[i] = std::min(values[i], target.values[i]);
+    }
+    bool flag = true;
+    for (int i = 0; i < D; ++i) {
+      flag = flag && (-result[2 * i] <= result[2 * i + 1]);
+    }
+    return flag;
   }
 
-  inline Real operator[](const int &i) const
-  {
-    return values[i];
-  }
-  template <class Archive>
-  void serialize(Archive &ar)
-  {
-    ar(values[0], values[1], values[2], values[3]);
-  }
+  Real operator[](const int i) const { return values[i]; }
+  template <class Archive> void serialize(Archive &ar) { ar(values); }
 };
 
-template <class T>
-class DataType
-{
+template <class T, int D = 2> class DataType {
 public:
   T first;
-  BB second;
+  BB<D> second;
   DataType(){};
 
-  DataType(const T &f, const BB &s)
-  {
+  DataType(const T &f, const BB<D> &s) {
     first = f;
     second = s;
   }
 
-  DataType(T &&f, BB &&s)
-  {
+  DataType(T &&f, BB<D> &&s) noexcept {
     first = std::move(f);
     second = std::move(s);
   }
 
-  template <class Archive>
-  void serialize(Archive &ar)
-  {
-    ar(first, second);
-  }
+  template <class Archive> void serialize(Archive &ar) { ar(first, second); }
 };
 
-template <class T, int B = 6>
-class Leaf
-{
+template <class T, int B = 6, int D = 2> class Leaf {
 public:
   int axis = 0;
-  BB mbb;
-  vec<DataType<T>> data; // You can swap when filtering
+  BB<D> mbb;
+  vec<DataType<T, D>> data; // You can swap when filtering
   // T is type of keys(ids) which will be returned when you post a query.
-  Leaf()
-  {
-    mbb = BB();
+  Leaf() {
+    mbb = BB<D>();
     data.reserve(B);
   }
-  Leaf(int &_axis)
-  {
+  Leaf(int &_axis) {
     axis = _axis;
-    mbb = BB();
+    mbb = BB<D>();
     data.reserve(B);
   }
 
-  ~Leaf()
-  {
+  ~Leaf() {
     data.clear();
-    vec<DataType<T>>().swap(data);
+    vec<DataType<T, D>>().swap(data);
   }
 
-  template <class Archive>
-  void serialize(Archive &ar)
-  {
-    ar(axis, mbb, data);
-  }
+  template <class Archive> void serialize(Archive &ar) { ar(axis, mbb, data); }
 
-  inline void set_axis(const int &_axis)
-  {
-    axis = _axis;
-  }
+  void set_axis(const int &_axis) { axis = _axis; }
 
-  inline void push(const T &key, const BB &target)
-  {
+  void push(const T &key, const BB<D> &target) {
     data.emplace_back(key, target);
     update_mbb();
   }
 
-  inline void update_mbb()
-  {
+  void update_mbb() {
     mbb.clear();
-    for (const auto &datum : data)
-    {
+    for (const auto &datum : data) {
       mbb += datum.second;
     }
   }
 
   template <typename F>
-  inline bool filter(DataType<T> &value, const F &comp)
-  { // false means given value is ignored
-    if (unlikely(data.size() < B))
-    { // if there is room, just push the candidate
+  bool filter(DataType<T, D> &value,
+              const F &comp) { // false means given value is ignored
+    if (unlikely(data.size() <
+                 B)) { // if there is room, just push the candidate
       mbb += value.second;
       data.push_back(std::move(value));
       return true;
-    }
-    else
-    { // if there is no room, check the priority and swap if needed
-      auto iter = upper_bound(data.begin(), data.end(), value, comp);
-      if (unlikely(iter != data.end()))
-      {
+    } else { // if there is no room, check the priority and swap if needed
+      auto iter = std::upper_bound(data.begin(), data.end(), value, comp);
+      if (unlikely(iter != data.end())) {
         swap(*iter, value);
         update_mbb();
       }
@@ -247,82 +231,64 @@ class Leaf
     }
   }
 
-  void operator()(const BB &target, vec<T> &out) const
-  {
-    if (unlikely(mbb(target)))
-    {
-      for (const auto &x : data)
-      {
-        if (unlikely(x.second(target)))
-        {
+  void operator()(const BB<D> &target, vec<T> &out) const {
+    if (unlikely(mbb(target))) {
+      for (const auto &x : data) {
+        if (unlikely(x.second(target))) {
           out.emplace_back(x.first);
         }
       }
     }
   }
 
-  void del(const T &key, const BB &target)
-  {
-    if (unlikely(mbb(target)))
-    {
-      auto remove_it = std::remove_if(data.begin(), data.end(), [&](auto &datum) {
-        return datum.second(target) && datum.first == key;
-      });
+  void del(const T &key, const BB<D> &target) {
+    if (unlikely(mbb(target))) {
+      auto remove_it =
+          std::remove_if(data.begin(), data.end(), [&](auto &datum) {
+            return datum.second(target) && datum.first == key;
+          });
       data.erase(remove_it, data.end());
     }
   }
 };
 
-template <class T, int B = 6>
-class PseudoPRTreeNode
-{
+template <class T, int B = 6, int D = 2> class PseudoPRTreeNode {
 public:
-  std::array<Leaf<T, B>, 4> leaves;
+  std::array<Leaf<T, B, D>, 2 * D> leaves;
   std::unique_ptr<PseudoPRTreeNode> left, right;
 
-  PseudoPRTreeNode()
-  {
-    for (int i = 0; i < 4; i++)
-    {
+  PseudoPRTreeNode() {
+    for (int i = 0; i < 2 * D; i++) {
       leaves[i].set_axis(i);
     }
   }
 
-  template <class Archive>
-  void serialize(Archive &archive)
-  {
-    //archive(cereal::(left), cereal::defer(right), leaves);
+  template <class Archive> void serialize(Archive &archive) {
+    // archive(cereal::(left), cereal::defer(right), leaves);
     archive(left, right, leaves);
   }
 
-  inline void address_of_leaves(vec<Leaf<T, B> *> &out)
-  {
-    for (auto &leaf : leaves)
-    {
-      if (likely(leaf.data.size() > 0))
-      {
+  void address_of_leaves(vec<Leaf<T, B, D> *> &out) {
+    for (auto &leaf : leaves) {
+      if (likely(leaf.data.size() > 0)) {
         out.emplace_back(&leaf);
       }
     }
   }
 
-  template <class iterator>
-  inline auto filter(iterator &b, iterator &e)
-  {
-    vec<tl::function_ref<bool(const DataType<T> &, const DataType<T> &)>> comps;
-    for (int axis = 0; axis < 4; axis++)
-    {
+  template <class iterator> auto filter(iterator &b, iterator &e) {
+    vec<std::function<bool(const DataType<T, D> &, const DataType<T, D> &)>>
+        comps;
+    for (int axis = 0; axis < 2 * D; ++axis) {
       comps.emplace_back(
-          [axis](const DataType<T> &lhs, const DataType<T> &rhs) noexcept {
+          [axis](const DataType<T, D> &lhs, const DataType<T, D> &rhs) {
             return lhs.second[axis] < rhs.second[axis];
           });
     }
 
     auto out = std::remove_if(b, e, [&](auto &x) {
-      for (auto &l : leaves)
-      {
-        if (unlikely(l.filter(x, comps[l.axis])))
-        {
+      for (auto &l : leaves) {
+        if (unlikely(l.filter(x, comps[l.axis]))) {
           return true;
         }
       }
@@ -332,113 +298,94 @@ class PseudoPRTreeNode
   }
 };
 
-template <class T, int B = 6>
-class PseudoPRTree
-{
+template <class T, int B = 6, int D = 2> class PseudoPRTree {
 public:
-  std::unique_ptr<PseudoPRTreeNode<T, B>> root;
-  vec<Leaf<T, B> *> cache_children;
+  std::unique_ptr<PseudoPRTreeNode<T, B, D>> root;
+  vec<Leaf<T, B, D> *> cache_children;
   const int nthreads = std::max(1, (int)std::thread::hardware_concurrency());
 
-  PseudoPRTree()
-  {
-    root = std::make_unique<PseudoPRTreeNode<T, B>>();
-  }
+  PseudoPRTree() { root = std::make_unique<PseudoPRTreeNode<T, B, D>>(); }
 
-  ~PseudoPRTree()
-  {
+  ~PseudoPRTree() {
     cache_children.clear();
-    vec<Leaf<T, B> *>().swap(cache_children);
+    vec<Leaf<T, B, D> *>().swap(cache_children);
   }
 
-  template <class iterator>
-  PseudoPRTree(iterator &b, iterator &e)
-  {
-    if (likely(!root))
-    {
-      root = std::make_unique<PseudoPRTreeNode<T, B>>();
+  template <class iterator> PseudoPRTree(iterator &b, iterator &e) {
+    if (likely(!root)) {
+      root = std::make_unique<PseudoPRTreeNode<T, B, D>>();
     }
     construct(root.get(), b, e, 0);
 
-    for (DataType<T> *it = e - 1; it >= b; --it)
-    {
-      it->~DataType<T>();
+    for (DataType<T, D> *it = e - 1; it >= b; --it) {
+      it->~DataType<T, D>();
     }
     b = nullptr;
     e = nullptr;
   }
 
-  template <class Archive>
-  void serialize(Archive &archive)
-  {
+  template <class Archive> void serialize(Archive &archive) {
     archive(root);
-    //archive.serializeDeferments();
+    // archive.serializeDeferments();
   }
 
   template <class iterator>
-  inline void construct(PseudoPRTreeNode<T, B> *node, iterator &b, iterator &e, const size_t depth)
-  {
-    if (e - b > 0 && node != nullptr)
-    {
+  void construct(PseudoPRTreeNode<T, B, D> *node, iterator &b, iterator &e,
+                 const size_t depth) {
+    if (e - b > 0 && node != nullptr) {
       bool use_recursive_threads = std::pow(2, depth + 1) <= nthreads;
 
       vec<std::thread> threads;
       threads.reserve(2);
-      PseudoPRTreeNode<T, B> *node_left, *node_right;
+      PseudoPRTreeNode<T, B, D> *node_left, *node_right;
 
-      const int axis = depth % 4;
+      const int axis = depth % (2 * D);
       auto ee = node->filter(b, e);
       auto m = b;
       std::advance(m, (ee - b) / 2);
       auto mm = m;
-      std::nth_element(b, m, ee, [axis](const DataType<T> &lhs, const DataType<T> &rhs) noexcept {
-        return lhs.second[axis] < rhs.second[axis];
-      });
-
-      if (m - b > 0)
-      {
-        node->left = std::make_unique<PseudoPRTreeNode<T, B>>();
+      std::nth_element(b, m, ee,
+                       [axis](const DataType<T, D> &lhs,
+                              const DataType<T, D> &rhs) noexcept {
+                         return lhs.second[axis] < rhs.second[axis];
+                       });
+
+      if (m - b > 0) {
+        node->left = std::make_unique<PseudoPRTreeNode<T, B, D>>();
         node_left = node->left.get();
-        if (use_recursive_threads)
-        {
-          threads.push_back(std::thread([&]() { construct(node_left, b, m, depth + 1); }));
-        }
-        else
-        {
+        if (use_recursive_threads) {
+          threads.push_back(
+              std::thread([&]() { construct(node_left, b, m, depth + 1); }));
+        } else {
           construct(node_left, b, m, depth + 1);
         }
       }
-      if (ee - mm > 0)
-      {
-        node->right = std::make_unique<PseudoPRTreeNode<T, B>>();
+      if (ee - mm > 0) {
+        node->right = std::make_unique<PseudoPRTreeNode<T, B, D>>();
         node_right = node->right.get();
-        if (use_recursive_threads)
-        {
-          threads.push_back(std::thread([&]() { construct(node_right, mm, ee, depth + 1); }));
-        }
-        else
-        {
+        if (use_recursive_threads) {
+          threads.push_back(
+              std::thread([&]() { construct(node_right, mm, ee, depth + 1); }));
+        } else {
           construct(node_right, mm, ee, depth + 1);
         }
       }
-      std::for_each(threads.begin(), threads.end(), [&](std::thread &x) { x.join(); });
+      std::for_each(threads.begin(), threads.end(),
+                    [&](std::thread &x) { x.join(); });
       threads.clear();
       vec<std::thread>().swap(threads);
     }
   }
 
-  inline auto get_all_leaves(const int hint)
-  {
-    if (cache_children.empty())
-    {
-      using U = PseudoPRTreeNode<T, B>;
+  auto get_all_leaves(const int hint) {
+    if (cache_children.empty()) {
+      using U = PseudoPRTreeNode<T, B, D>;
       cache_children.reserve(hint);
       auto node = root.get();
       queue<U *> que;
       que.emplace(node);
 
-      while (likely(!que.empty()))
-      {
+      while (likely(!que.empty())) {
         node = que.front();
         que.pop();
         node->address_of_leaves(cache_children);
@@ -451,126 +398,101 @@ class PseudoPRTree
     return cache_children;
   }
 
-  std::pair<DataType<int> *, DataType<int> *> as_X(void *placement, const int hint)
-  {
-    DataType<int> *b, *e;
+  std::pair<DataType<int, D> *, DataType<int, D> *> as_X(void *placement,
+                                                         const int hint) {
+    DataType<int, D> *b, *e;
     auto children = get_all_leaves(hint);
     int total = children.size();
-    b = (DataType<int> *)placement;
+    b = (DataType<int, D> *)placement;
     e = b + total;
     parallel_for_each(b, e, [&](auto &p) {
       int i = &p - b;
-      new (b + i) DataType<int>{i, children[i]->mbb};
+      new (b + i) DataType<int, D>{i, children[i]->mbb};
     });
     return {b, e};
   }
 };
 
-template <class T, int B = 6>
-class PRTreeNode
-{
+template <class T, int B = 6, int D = 2> class PRTreeNode {
 public:
-  BB mbb;
-  std::unique_ptr<Leaf<T, B>> leaf;
-  std::unique_ptr<PRTreeNode<T, B>> head, next;
+  BB<D> mbb;
+  std::unique_ptr<Leaf<T, B, D>> leaf;
+  std::unique_ptr<PRTreeNode<T, B, D>> head, next;
 
   PRTreeNode() {}
-  PRTreeNode(const BB &_mbb)
-  {
-    mbb = _mbb;
-  }
+  PRTreeNode(const BB<D> &_mbb) { mbb = _mbb; }
 
-  PRTreeNode(BB &&_mbb)
-  {
-    mbb = std::move(_mbb);
-  }
+  PRTreeNode(BB<D> &&_mbb) { mbb = std::move(_mbb); }
 
-  PRTreeNode(Leaf<T, B> *l)
-  {
-    leaf = std::make_unique<Leaf<T, B>>();
+  PRTreeNode(Leaf<T, B, D> *l) {
+    leaf = std::make_unique<Leaf<T, B, D>>();
     mbb = l->mbb;
     leaf->mbb = std::move(l->mbb);
     leaf->data = std::move(l->data);
   }
 
-  inline bool operator()(const BB &target)
-  {
-    return mbb(target);
-  }
+  bool operator()(const BB<D> &target) { return mbb(target); }
 
-  template <class Archive>
-  void serialize(Archive &archive)
-  {
-    //archive(cereal::defer(leaf), cereal::defer(head), cereal::defer(next));
+  template <class Archive> void serialize(Archive &archive) {
+    // archive(cereal::defer(leaf), cereal::defer(head), cereal::defer(next));
     archive(mbb, leaf, head, next);
   }
 };
 
-template <class T, int B = 6>
-class PRTree
-{
+template <class T, int B = 6, int D = 2> class PRTree {
 private:
-  std::unique_ptr<PRTreeNode<T, B>> root;
-  std::unordered_map<T, BB> umap;
+  std::unique_ptr<PRTreeNode<T, B, D>> root;
+  std::unordered_map<T, BB<D>> umap;
 
 public:
-  template <class Archive>
-  void serialize(Archive &archive)
-  {
+  template <class Archive> void serialize(Archive &archive) {
     archive(root, umap);
-    //archive.serializeDeferments();
+    // archive.serializeDeferments();
   }
 
-  void save(std::string fname)
-  {
+  void save(std::string fname) {
     {
       {
         std::ofstream ofs(fname, std::ios::binary);
         cereal::PortableBinaryOutputArchive o_archive(ofs);
-        //cereal::JSONOutputArchive o_archive(ofs);
-        o_archive(cereal::make_nvp("root", root), cereal::make_nvp("umap", umap));
+        o_archive(cereal::make_nvp("root", root),
+                  cereal::make_nvp("umap", umap));
       }
     }
   }
 
   PRTree() {}
 
-  ~PRTree()
-  {
+  ~PRTree() {
     umap.clear();
-    std::unordered_map<T, BB>().swap(umap);
+    std::unordered_map<T, BB<D>>().swap(umap);
   }
 
-  PRTree(std::string fname)
-  {
-    load(fname);
-  }
+  PRTree(std::string fname) { load(fname); }
 
-  void load(std::string fname)
-  {
+  void load(std::string fname) {
     {
       {
         std::ifstream ifs(fname, std::ios::binary);
         cereal::PortableBinaryInputArchive i_archive(ifs);
-        //cereal::JSONInputArchive i_archive(ifs);
-        i_archive(cereal::make_nvp("root", root), cereal::make_nvp("umap", umap));
+        // cereal::JSONInputArchive i_archive(ifs);
+        i_archive(cereal::make_nvp("root", root),
+                  cereal::make_nvp("umap", umap));
       }
     }
   }
 
-  PRTree(const py::array_t<T> &idx, const py::array_t<float> &x)
-  {
+  PRTree(const py::array_t<T> &idx, const py::array_t<float> &x) {
     const auto &buff_info_idx = idx.request();
     const auto &shape_idx = buff_info_idx.shape;
     const auto &buff_info_x = x.request();
     const auto &shape_x = buff_info_x.shape;
-    if (shape_idx[0] != shape_x[0])
-    {
-      throw std::runtime_error("Both index and boudning box must have the same length");
-    }
-    else if (shape_x[1] != 4)
-    {
-      throw std::runtime_error("Bounding box must have the shape (length, 4)");
+    if (shape_idx[0] != shape_x[0]) {
+      throw std::runtime_error(
+          "Both index and boudning box must have the same length");
+    } else if (shape_x[1] != 2 * D) {
+      throw std::runtime_error(
+          "Bounding box must have the shape (length, 2 * dim)");
     }
 
     auto ri = idx.template unchecked<1>();
@@ -578,68 +500,90 @@ class PRTree
     size_t length = shape_idx[0];
     umap.reserve(length);
 
-    DataType<T> *b, *e;
-    void *placement = std::malloc(std::max(sizeof(DataType<T>), sizeof(DataType<int>)) * length);
-    b = reinterpret_cast<DataType<T> *>(placement);
+    DataType<T, D> *b, *e;
+    void *placement = std::malloc(
+        std::max(sizeof(DataType<T, D>), sizeof(DataType<int, D>)) * length);
+    b = reinterpret_cast<DataType<T, D> *>(placement);
     e = b + length;
 
-    parallel_for_each(b, e,
-                      [&](auto &it) {
-                        int i = &it - b;
-                        auto bb = BB(rx(i, 0), rx(i, 1), rx(i, 2), rx(i, 3));
-                        new (b + i) DataType<T>{ri(i), std::move(bb)};
-                      });
+    parallel_for_each(b, e, [&](auto &it) {
+      int i = &it - b;
+      std::array<Real, D> minima;
+      std::array<Real, D> maxima;
+      for (int j = 0; j < D; ++j) {
+        minima[j] = rx(i, 2 * j);
+        maxima[j] = rx(i, 2 * j + 1);
+      }
+      auto bb = BB<D>(minima, maxima);
+      new (b + i) DataType<T, D>{ri(i), std::move(bb)};
+    });
 
-    auto build_umap = [&] {for (size_t i = 0; i < length; i++){
-        auto bb = BB(rx(i, 0), rx(i, 1), rx(i, 2), rx(i, 3));
+    auto build_umap = [&] {
+      for (size_t i = 0; i < length; i++) {
+        std::array<Real, D> minima;
+        std::array<Real, D> maxima;
+        for (int j = 0; j < D; ++j) {
+          minima[j] = rx(i, 2 * j);
+          maxima[j] = rx(i, 2 * j + 1);
+        }
+        auto bb = BB<D>(minima, maxima);
         umap.emplace_hint(umap.end(), ri(i), std::move(bb));
-      } };
+      }
+    };
 
-    //ProfilerStart("construct.prof");
+    // ProfilerStart("construct.prof");
     auto t2 = std::thread([&] { build(b, e, placement); });
     auto t1 = std::thread(std::move(build_umap));
     t1.join();
     t2.join();
-    //ProfilerStop();
+    // ProfilerStop();
     std::free(placement);
   }
 
-  void insert(const T &idx, const py::array_t<float> &x)
-  {
-    vec<Leaf<T, B> *> cands;
-    queue<PRTreeNode<T, B> *> que;
-    BB bb;
-    PRTreeNode<T, B> *p, *q;
+  void insert(const T &idx, const py::array_t<float> &x) {
+    vec<Leaf<T, B, D> *> cands;
+    queue<PRTreeNode<T, B, D> *> que;
+    BB<D> bb;
+    PRTreeNode<T, B, D> *p, *q;
 
     const auto &buff_info_x = x.request();
     const auto &shape_x = buff_info_x.shape;
     const auto &ndim = buff_info_x.ndim;
-    if (shape_x[0] != 4 || ndim != 1)
-    {
+    if (shape_x[0] != 2 * D || ndim != 1) {
       throw std::runtime_error("invalid shape.");
     }
     auto it = umap.find(idx);
-    if (unlikely(it != umap.end()))
-    {
+    if (unlikely(it != umap.end())) {
       throw std::runtime_error("Given index is already included.");
     }
+    {
+      std::array<Real, D> minima;
+      std::array<Real, D> maxima;
+      for (int i = 0; i < D; ++i) {
+        minima[i] = *x.data(2 * i);
+        maxima[i] = *x.data(2 * i + 1);
+      }
+      bb = BB<D>(minima, maxima);
+    }
 
-    bb = BB(*x.data(0), *x.data(1), *x.data(2), *x.data(3));
-    Real dx = bb.xmax() - bb.xmin() + 0.000000001;
-    Real dy = bb.ymax() - bb.ymin() + 0.000000001;
+    std::array<Real, D> delta;
+    for (int i = 0; i < D; ++i) {
+      delta[i] = bb.max(i) - bb.min(i) + 0.00000001;
+    }
     Real c = 0.0;
-    std::stack<PRTreeNode<T, B> *> sta;
-    while (likely(cands.size() == 0))
-    {
-      while (likely(!sta.empty()))
-      {
+    std::stack<PRTreeNode<T, B, D> *> sta;
+    while (likely(cands.size() == 0)) {
+      while (likely(!sta.empty())) {
         sta.pop();
       }
-      bb.expand(dx * c, dy * c);
+      std::array<Real, D> d;
+      for (int i = 0; i < D; ++i) {
+        d[i] = delta[i] * c;
+      }
+      bb.expand(d);
       c = (c + 1) * 2;
-      auto qpush = [&](PRTreeNode<T, B> *r) {
-        if (unlikely((*r)(bb)))
-        {
+      auto qpush = [&](PRTreeNode<T, B, D> *r) {
+        if (unlikely((*r)(bb))) {
           que.emplace(r);
           sta.push(r);
         }
@@ -647,23 +591,17 @@ class PRTree
 
       p = root.get();
       qpush(p);
-      while (likely(!que.empty()))
-      {
+      while (likely(!que.empty())) {
         p = que.front();
         que.pop();
 
-        if (unlikely(p->leaf))
-        {
+        if (unlikely(p->leaf)) {
           cands.push_back(p->leaf.get());
-        }
-        else
-        {
-          if (likely(p->head))
-          {
+        } else {
+          if (likely(p->head)) {
             q = p->head.get();
             qpush(q);
-            while (likely(q->next))
-            {
+            while (likely(q->next)) {
               q = q->next.get();
               qpush(q);
             }
@@ -672,24 +610,27 @@ class PRTree
       }
     }
     auto tg = *select_randomly(cands.begin(), cands.end());
-    bb = BB(*x.data(0), *x.data(1), *x.data(2), *x.data(3));
+    {
+      std::array<Real, D> minima;
+      std::array<Real, D> maxima;
+      for (int i = 0; i < D; ++i) {
+        minima[i] = *x.data(2 * i);
+        maxima[i] = *x.data(2 * i + 1);
+      }
+      bb = BB<D>(minima, maxima);
+    }
     tg->push(idx, bb);
     umap[idx] = bb;
-    while (likely(!sta.empty()))
-    {
+    while (likely(!sta.empty())) {
       p = sta.top();
       sta.pop();
-      if (unlikely(p->leaf))
-      {
+      if (unlikely(p->leaf)) {
         p->mbb = p->leaf->mbb;
-      }
-      else if (likely(p->head))
-      {
-        BB mbb;
+      } else if (likely(p->head)) {
+        BB<D> mbb;
         q = p->head.get();
         mbb += q->mbb;
-        while (likely(q->next))
-        {
+        while (likely(q->next)) {
           q = q->next.get();
           mbb += q->mbb;
         }
@@ -699,55 +640,47 @@ class PRTree
   }
 
   template <class iterator>
-  void build(iterator &b, iterator &e, void *placement)
-  {
-    vec<std::unique_ptr<PRTreeNode<T, B>>> prev_nodes;
-    std::unique_ptr<PRTreeNode<T, B>> p, q, r;
+  void build(iterator &b, iterator &e, void *placement) {
+    vec<std::unique_ptr<PRTreeNode<T, B, D>>> prev_nodes;
+    std::unique_ptr<PRTreeNode<T, B, D>> p, q, r;
 
-    auto first_tree = PseudoPRTree<T, B>(b, e);
+    auto first_tree = PseudoPRTree<T, B, D>(b, e);
     auto first_leaves = first_tree.get_all_leaves(e - b);
     parallel_for_each(first_leaves.begin(), first_leaves.end(), prev_nodes,
                       [&](auto &leaf, auto &o) {
-                        auto pp = std::make_unique<PRTreeNode<T, B>>(leaf);
+                        auto pp = std::make_unique<PRTreeNode<T, B, D>>(leaf);
                         o.push_back(std::move(pp));
                       });
-
     auto [bb, ee] = first_tree.as_X(placement, e - b);
-    while (likely(prev_nodes.size() > 1))
-    {
-      auto tree = PseudoPRTree<int, B>(bb, ee);
-      vec<Leaf<int, B> *> leaves = tree.get_all_leaves(ee - bb);
+    while (likely(prev_nodes.size() > 1)) {
+      auto tree = PseudoPRTree<int, B, D>(bb, ee);
+      vec<Leaf<int, B, D> *> leaves = tree.get_all_leaves(ee - bb);
       auto leaves_size = leaves.size();
 
-      vec<std::unique_ptr<PRTreeNode<T, B>>> tmp_nodes;
+      vec<std::unique_ptr<PRTreeNode<T, B, D>>> tmp_nodes;
       tmp_nodes.reserve(leaves_size);
 
-      parallel_for_each(leaves.begin(), leaves.end(), tmp_nodes,
-                        [&](auto &leaf, auto &o) {
-                          int idx, jdx;
-                          int len = leaf->data.size();
-                          auto pp = std::make_unique<PRTreeNode<T, B>>(leaf->mbb);
-                          if (likely(!leaf->data.empty()))
-                          {
-                            for (int i = 1; i < len; i++)
-                            {
-                              idx = leaf->data[len - i - 1].first; // reversed way
-                              jdx = leaf->data[len - i].first;
-                              prev_nodes[idx]->next = std::move(prev_nodes[jdx]);
-                            }
-                            idx = leaf->data[0].first;
-                            pp->head = std::move(prev_nodes[idx]);
-                            if (!pp->head)
-                            {
-                              throw std::runtime_error("ppp");
-                            }
-                            o.push_back(std::move(pp));
-                          }
-                          else
-                          {
-                            throw std::runtime_error("what????");
-                          }
-                        });
+      parallel_for_each(
+          leaves.begin(), leaves.end(), tmp_nodes, [&](auto &leaf, auto &o) {
+            int idx, jdx;
+            int len = leaf->data.size();
+            auto pp = std::make_unique<PRTreeNode<T, B, D>>(leaf->mbb);
+            if (likely(!leaf->data.empty())) {
+              for (int i = 1; i < len; i++) {
+                idx = leaf->data[len - i - 1].first; // reversed way
+                jdx = leaf->data[len - i].first;
+                prev_nodes[idx]->next = std::move(prev_nodes[jdx]);
+              }
+              idx = leaf->data[0].first;
+              pp->head = std::move(prev_nodes[idx]);
+              if (!pp->head) {
+                throw std::runtime_error("ppp");
+              }
+              o.push_back(std::move(pp));
+            } else {
+              throw std::runtime_error("what????");
+            }
+          });
 
       /*size_t c = 0;
         for (const auto& n : prev_nodes){
@@ -759,10 +692,10 @@ class PRTree
           throw std::runtime_error("eee");
         }*/
       leaves.clear();
-      vec<Leaf<int, B> *>().swap(leaves);
+      vec<Leaf<int, B, D> *>().swap(leaves);
       prev_nodes.swap(tmp_nodes);
       tmp_nodes.clear();
-      vec<std::unique_ptr<PRTreeNode<T, B>>>().swap(tmp_nodes);
+      vec<std::unique_ptr<PRTreeNode<T, B, D>>>().swap(tmp_nodes);
 
       {
         auto tmp = tree.as_X(placement, ee - bb);
@@ -770,100 +703,103 @@ class PRTree
         ee = std::move(tmp.second);
       }
     }
-    if (unlikely(prev_nodes.size() != 1))
-    {
+    if (unlikely(prev_nodes.size() != 1)) {
       throw std::runtime_error("#roots is not 1.");
     }
     root = std::move(prev_nodes[0]);
     prev_nodes.clear();
-    vec<std::unique_ptr<PRTreeNode<T, B>>>().swap(prev_nodes);
+    vec<std::unique_ptr<PRTreeNode<T, B, D>>>().swap(prev_nodes);
   }
 
-  auto find_all(const py::array_t<float> &x)
-  {
+  auto find_all(const py::array_t<float> &x) {
     const auto &buff_info_x = x.request();
     const auto &ndim = buff_info_x.ndim;
     const auto &shape_x = buff_info_x.shape;
-    if (ndim == 1 && shape_x[0] != 4)
-    {
-      throw std::runtime_error("Bounding box must have the shape 4 with (xmin, xmax, ymin, ymax)");
-    }
-    else if (ndim == 2 && shape_x[1] != 4)
-    {
-      throw std::runtime_error("Bounding box must have the shape (length, 4)");
-    }
-    else if (ndim > 3)
-    {
+    if (ndim == 1 && shape_x[0] != 2 * D) {
+      throw std::runtime_error("Invalid Bounding box size");
+    } else if (ndim == 2 && shape_x[1] != 2 * D) {
+      throw std::runtime_error(
+          "Bounding box must have the shape (length, 2 * dim)");
+    } else if (ndim > 3) {
       throw std::runtime_error("invalid shape");
     }
-    vec<BB> X;
+    vec<BB<D>> X;
     X.reserve(ndim == 1 ? 1 : shape_x[0]);
-    BB bb;
-    if (ndim == 1)
-    {
-      bb = BB(*x.data(0), *x.data(1), *x.data(2), *x.data(3));
+    BB<D> bb;
+    if (ndim == 1) {
+      {
+        std::array<Real, D> minima;
+        std::array<Real, D> maxima;
+        for (int i = 0; i < D; ++i) {
+          minima[i] = *x.data(2 * i);
+          maxima[i] = *x.data(2 * i + 1);
+        }
+        bb = BB<D>(minima, maxima);
+      }
       X.push_back(std::move(bb));
-    }
-    else
-    {
+    } else {
       X.reserve(shape_x[0]);
-      for (long int i = 0; i < shape_x[0]; i++)
-      {
-        bb = BB(*x.data(i, 0), *x.data(i, 1), *x.data(i, 2), *x.data(i, 3));
+      for (long int i = 0; i < shape_x[0]; i++) {
+        {
+          std::array<Real, D> minima;
+          std::array<Real, D> maxima;
+          for (int j = 0; j < D; ++j) {
+            minima[j] = *x.data(i, 2 * j);
+            maxima[j] = *x.data(i, 2 * j + 1);
+          }
+          bb = BB<D>(minima, maxima);
+        }
         X.push_back(std::move(bb));
       }
     }
     size_t length = X.size();
     vec<vec<T>> out;
     out.reserve(length);
-    parallel_for_each(X.begin(), X.end(), out, [&](const BB &x, auto &o) { o.push_back(find(x)); });
+    parallel_for_each(X.begin(), X.end(), out,
+                      [&](const BB<D> &x, auto &o) { o.push_back(find(x)); });
     return out;
   }
 
-  vec<T> find_one(const py::array_t<float> &x)
-  {
+  vec<T> find_one(const py::array_t<float> &x) {
     const auto &buff_info_x = x.request();
     const auto &ndim = buff_info_x.ndim;
     const auto &shape_x = buff_info_x.shape;
-    if (unlikely(ndim != 1 || shape_x[0] != 4))
-    {
+    if (unlikely(ndim != 1 || shape_x[0] != 2 * D)) {
       throw std::runtime_error("invalid shape");
     }
-    const BB bb = BB(*x.data(0), *x.data(1), *x.data(2), *x.data(3));
+    std::array<Real, D> minima;
+    std::array<Real, D> maxima;
+    for (int i = 0; i < D; ++i) {
+      minima[i] = *x.data(2 * i);
+      maxima[i] = *x.data(2 * i + 1);
+    }
+    const auto bb = BB<D>(minima, maxima);
     return find(bb);
   }
 
-  vec<T> find(const BB &target)
-  {
+  vec<T> find(const BB<D> &target) {
     vec<T> out;
-    queue<PRTreeNode<T, B> *> que;
-    PRTreeNode<T, B> *p, *q;
-    auto qpush = [&](PRTreeNode<T, B> *r) {
-      if (unlikely((*r)(target)))
-      {
+    queue<PRTreeNode<T, B, D> *> que;
+    PRTreeNode<T, B, D> *p, *q;
+    auto qpush = [&](PRTreeNode<T, B, D> *r) {
+      if (unlikely((*r)(target))) {
         que.emplace(r);
       }
     };
 
     p = root.get();
     qpush(p);
-    while (likely(!que.empty()))
-    {
+    while (likely(!que.empty())) {
       p = que.front();
       que.pop();
 
-      if (unlikely(p->leaf))
-      {
+      if (unlikely(p->leaf)) {
         (*p->leaf)(target, out);
-      }
-      else
-      {
-        if (likely(p->head))
-        {
+      } else {
+        if (likely(p->head)) {
           q = p->head.get();
           qpush(q);
-          while (likely(q->next))
-          {
+          while (likely(q->next)) {
             q = q->next.get();
             qpush(q);
           }
@@ -873,42 +809,33 @@ class PRTree
     return out;
   }
 
-  void erase(const T idx)
-  {
+  void erase(const T idx) {
     auto it = umap.find(idx);
-    if (unlikely(it == umap.end()))
-    {
+    if (unlikely(it == umap.end())) {
       throw std::runtime_error("Given index is not found.");
     }
-    BB target = it->second;
-    queue<PRTreeNode<T, B> *> que;
-    PRTreeNode<T, B> *p, *q;
-    auto qpush = [&](PRTreeNode<T, B> *r) {
-      if (unlikely((*r)(target)))
-      {
+    BB<D> target = it->second;
+    queue<PRTreeNode<T, B, D> *> que;
+    PRTreeNode<T, B, D> *p, *q;
+    auto qpush = [&](PRTreeNode<T, B, D> *r) {
+      if (unlikely((*r)(target))) {
         que.emplace(r);
       }
     };
 
     p = root.get();
     qpush(p);
-    while (likely(!que.empty()))
-    {
+    while (likely(!que.empty())) {
       p = que.front();
       que.pop();
 
-      if (unlikely(p->leaf))
-      {
+      if (unlikely(p->leaf)) {
         p->leaf->del(idx, target);
-      }
-      else
-      {
-        if (likely(p->head))
-        {
+      } else {
+        if (likely(p->head)) {
           q = p->head.get();
           qpush(q);
-          while (likely(q->next))
-          {
+          while (likely(q->next)) {
             q = q->next.get();
             qpush(q);
           }
diff --git a/docs/experiment.ipynb b/docs/experiment.ipynb
index 1367387..3c3d721 100644
--- a/docs/experiment.ipynb
+++ b/docs/experiment.ipynb
@@ -6,7 +6,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "from python_prtree import PRTree\n",
+    "from python_prtree import PRTree2D, PRTree3D\n",
     "import numpy as np\n",
     "import time\n",
     "import matplotlib.pyplot as plt\n",
@@ -23,18 +23,18 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def f(N):\n",
+    "def f(N, PRTree, dim):\n",
     "    idx = np.arange(N)\n",
-    "    x = np.random.rand(N, 4).astype(np.float32)\n",
+    "    x = np.random.rand(N, 2*dim).astype(np.float32)\n",
     "    print(x.nbytes // 1024 // 1024) # mb\n",
-    "    x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100\n",
-    "    x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100\n",
+    "    for i in range(dim):\n",
+    "        x[:, 2*i+1] = x[:, 2*i] + x[:, 2*i+1] / np.sqrt(N) / 100\n",
     "    s = time.time()\n",
     "    prtree = PRTree(idx, x)\n",
     "    t = time.time()\n",
-    "    x = np.random.rand(100_000, 4).astype(np.float32)\n",
-    "    x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100\n",
-    "    x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100\n",
+    "    x = np.random.rand(100_000, 2*dim).astype(np.float32)\n",
+    "    for i in range(dim):\n",
+    "        x[:, 2*i+1] = x[:, 2*i] + x[:, 2*i+1] / np.sqrt(N) / 100\n",
     "    t2 = time.time()\n",
     "    out = prtree.batch_query(x)\n",
     "    u = time.time()\n",
@@ -46,9 +46,9 @@
     "        prtree.erase(k)\n",
     "    v = time.time()\n",
     "    \n",
-    "    x = np.random.rand(min(N, 100_000), 4).astype(np.float32)\n",
-    "    x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100\n",
-    "    x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100\n",
+    "    x = np.random.rand(min(N, 100_000), 2*dim).astype(np.float32)\n",
+    "    for i in range(dim):\n",
+    "        x[:, 2*i+1] = x[:, 2*i] + x[:, 2*i+1] / np.sqrt(N) / 100\n",
     "    v2 = time.time()\n",
     "    for i, k in enumerate(del_list):\n",
     "        prtree.insert(k, x[i])\n",
@@ -58,68 +58,6 @@
   {
    "cell_type": "code",
    "execution_count": 3,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "0\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(0.01875448226928711,\n",
-       " 0.041174888610839844,\n",
-       " 0.19428801536560059,\n",
-       " 0.00861501693725586,\n",
-       " 0.027135610580444336)"
-      ]
-     },
-     "execution_count": 3,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "f(10_000)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 4,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "15\n"
-     ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "(0.1822516918182373,\n",
-       " 0.04976153373718262,\n",
-       " 0.34719419479370117,\n",
-       " 0.27179813385009766,\n",
-       " 0.3984971046447754)"
-      ]
-     },
-     "execution_count": 4,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "f(1_000_000)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 5,
    "metadata": {
     "scrolled": false
    },
@@ -128,9 +66,6 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "188924888, 2882\n",
-      "125949925, 1921\n",
-      "83966617, 1281\n",
       "55977744, 854\n",
       "37318496, 569\n",
       "24878997, 379\n",
@@ -145,125 +80,130 @@
       "647159, 9\n",
       "431439, 6\n",
       "287626, 4\n",
-      "191751, 2\n"
+      "191751, 2\n",
+      "55977744, 1281\n",
+      "37318496, 854\n",
+      "24878997, 569\n",
+      "16585998, 379\n",
+      "11057332, 253\n",
+      "7371554, 168\n",
+      "4914369, 112\n",
+      "3276246, 74\n",
+      "2184164, 49\n",
+      "1456109, 33\n",
+      "970739, 22\n",
+      "647159, 14\n",
+      "431439, 9\n",
+      "287626, 6\n",
+      "191751, 4\n"
      ]
-    }
-   ],
-   "source": [
-    "x = []\n",
-    "y1 = []\n",
-    "y2 = []\n",
-    "y22 = []\n",
-    "y3 = []\n",
-    "y4 = []\n",
-    "for i in list(range(7, 25))[::-1]:\n",
-    "    n = int(1.5 ** (23 + i))\n",
-    "    print(n, end=', ')\n",
-    "    x.append(n)\n",
-    "    s, t, t2, u,v = f(n)\n",
-    "    y1.append(s)\n",
-    "    y2.append(t)\n",
-    "    y22.append(t2)\n",
-    "    y3.append(u)\n",
-    "    y4.append(v)\n",
-    "    gc.collect()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 6,
-   "metadata": {},
-   "outputs": [
+    },
     {
      "data": {
-      "image/png": "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\n",
       "text/plain": [
-       "<Figure size 576x288 with 1 Axes>"
+       "<Figure size 576x288 with 0 Axes>"
       ]
      },
-     "metadata": {
-      "needs_background": "light"
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<Figure size 576x288 with 0 Axes>"
+      ]
      },
+     "metadata": {},
      "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "plt.figure(figsize=(8, 4))\n",
-    "plt.plot(x, y1)\n",
-    "plt.xscale('log')\n",
-    "plt.xlabel('#rectangles constructed with initial PRTree')\n",
-    "plt.ylabel('time[sec]')\n",
-    "plt.title('construction time')\n",
-    "plt.savefig('./images/fig1.png')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 7,
-   "metadata": {},
-   "outputs": [
+    },
     {
      "data": {
-      "image/png": "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JSKy56IW8JPufnQriOVE4I/uPiDNoYg0G92kQERSxt2LgbQncJG2St3pIiIiGUrd6SIiIhlKLXEREZEMpSQuIiKSoTJuYFuvXr18yJAh6Q5DRESkxcycOXO9u3/qiX0Zl8SHDBlCUVFRusMQERFpMWaW9LbB6k4XERHJUEriIiIiGUpJXEREJENl3DnxZMrLyykuLmbnzp3pDqXdys/PZ8CAAeTk6ImKIiItpU0k8eLiYgoKChgyZAjhSZPSktydDRs2UFxczNChQ9MdjohIu9EmutN37txJz549lcDTxMzo2bOnekJERFpYm0jigBJ4munnLyIC7NwCc+t6xH3zaxPd6SIiImlVWQGzHoX//BR2bIT+R0D3ISnfbZtpiafbsmXLOOiggxpdftKkSaxatarBMtdee+2ehiYiIqm05CV44DPw7Leh1wj46sstksBBLfG0mTRpEgcddBB77713ukOpl7vj7mRl6f89EZFa1i6AF34Qknj3IXDRY3DAOdCCpxfbXBK/7Zl5zF+1pVnrHLl3F24558AGy1VUVPClL32JWbNmMWLECB599FF+8Ytf8Mwzz7Bjxw6OOeYYHnzwQf72t79RVFTE5ZdfTocOHZg+fTpz587l+uuvp6SkhLy8PF5++WUAVq1axbhx4/jggw8YP348d955Z537/8Mf/sDtt99Ov379GDFiBHl5edx7771cccUVnH322Vx44YUAdO7cmW3btgFw11138Ze//IXS0lLGjx/PbbfdxrJlyzjjjDM46aSTmD59Oueffz6bNm3iV7/6FQAPPfQQCxYs4Je//OWe/mhFRDLPtnUw9WcwcxLkFsDpP4XRV0F2XouHouZVM1q4cCETJkxgzpw5dOnShfvvv59rr72WGTNmMHfuXHbs2MGzzz7LhRdeSGFhIX/605+YPXs2sViMiy++mLvvvpv33nuPl156iQ4dOgAwe/ZsnnjiCd5//32eeOIJVqxYkXTfH3/8MbfccgtvvPEGL774IvPnz28w3hdeeIHFixfzzjvvMHv2bGbOnMlrr71W81m++MUvMmvWLG644QamTJlCeXk5EP5Z+PKXv9xMPzURkQxRvhNe/xX85nCY+QgceRVcNwuOuTYtCRzaYEu8MS3mVBk4cCDHHnssAJ///Of5zW9+w9ChQ7nzzjvZvn07Gzdu5MADD+Scc86ptd3ChQvp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\n",
       "text/plain": [
-       "<Figure size 576x288 with 1 Axes>"
+       "<Figure size 576x288 with 0 Axes>"
       ]
      },
-     "metadata": {
-      "needs_background": "light"
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<Figure size 576x288 with 0 Axes>"
+      ]
      },
+     "metadata": {},
      "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "plt.figure(figsize=(8, 4))\n",
-    "plt.plot(x, y2, label='batch_query')\n",
-    "plt.plot(x, y22, label='query')\n",
-    "plt.xscale('log')\n",
-    "plt.xlabel('#rectangles constructed with initial PRTree')\n",
-    "plt.legend()\n",
-    "plt.ylabel('time[sec]')\n",
-    "plt.title('total time of 100,000 queries')\n",
-    "plt.savefig('./images/fig2.png')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [
+    },
     {
      "data": {
-      "image/png": "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\n",
       "text/plain": [
-       "<Figure size 576x288 with 1 Axes>"
+       "<Figure size 576x288 with 0 Axes>"
       ]
      },
-     "metadata": {
-      "needs_background": "light"
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<Figure size 576x288 with 0 Axes>"
+      ]
      },
+     "metadata": {},
      "output_type": "display_data"
     }
    ],
    "source": [
-    "plt.figure(figsize=(8, 4))\n",
-    "plt.plot(x, y3, label='erase')\n",
-    "plt.plot(x, y4, label='insert')\n",
-    "plt.xscale('log')\n",
-    "plt.legend()\n",
-    "plt.xlabel('#rectangles constructed with initial PRTree')\n",
-    "plt.ylabel('time[sec]')\n",
-    "plt.title('total time of 100,000 delete/insert events')\n",
-    "plt.savefig('./images/fig3.png')"
+    "for PRTree, name, dim in zip([PRTree2D, PRTree3D], ['2d', '3d'], [2, 3]):\n",
+    "    x = []\n",
+    "    y1 = []\n",
+    "    y2 = []\n",
+    "    y22 = []\n",
+    "    y3 = []\n",
+    "    y4 = []\n",
+    "    for i in list(range(7, 22))[::-1]:\n",
+    "        n = int(1.5 ** (23 + i))\n",
+    "        print(n, end=', ')\n",
+    "        x.append(n)\n",
+    "        s, t, t2, u,v = f(n, PRTree, dim)\n",
+    "        y1.append(s)\n",
+    "        y2.append(t)\n",
+    "        y22.append(t2)\n",
+    "        y3.append(u)\n",
+    "        y4.append(v)\n",
+    "        gc.collect()\n",
+    "    plt.figure(figsize=(8, 4))\n",
+    "    plt.plot(x, y1)\n",
+    "    plt.xscale('log')\n",
+    "    plt.xlabel('#rectangles constructed with initial PRTree')\n",
+    "    plt.ylabel('time[sec]')\n",
+    "    plt.title(f'[{name}] construction time')\n",
+    "    plt.savefig(f'./images/{name}_fig1.png')\n",
+    "    plt.clf()\n",
+    "    \n",
+    "    plt.figure(figsize=(8, 4))\n",
+    "    plt.plot(x, y2, label='batch_query')\n",
+    "    plt.plot(x, y22, label='query')\n",
+    "    plt.xscale('log')\n",
+    "    plt.xlabel('#rectangles constructed with initial PRTree')\n",
+    "    plt.legend()\n",
+    "    plt.ylabel('time[sec]')\n",
+    "    plt.title(f'[{name}] total time of 100,000 queries')\n",
+    "    plt.savefig(f'./images/{name}_fig2.png')\n",
+    "    plt.clf()\n",
+    "    \n",
+    "    plt.figure(figsize=(8, 4))\n",
+    "    plt.plot(x, y3, label='erase')\n",
+    "    plt.plot(x, y4, label='insert')\n",
+    "    plt.xscale('log')\n",
+    "    plt.legend()\n",
+    "    plt.xlabel('#rectangles constructed with initial PRTree')\n",
+    "    plt.ylabel('time[sec]')\n",
+    "    plt.title(f'[{name}] total time of 100,000 delete/insert events')\n",
+    "    plt.savefig(f'./images/{name}_fig3.png')\n",
+    "    plt.clf()"
    ]
   },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -288,7 +228,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.7"
+   "version": "3.8.5"
   }
  },
  "nbformat": 4,
diff --git a/docs/images/2d_fig1.png b/docs/images/2d_fig1.png
new file mode 100644
index 0000000..d2afcdb
Binary files /dev/null and b/docs/images/2d_fig1.png differ
diff --git a/docs/images/2d_fig2.png b/docs/images/2d_fig2.png
new file mode 100644
index 0000000..f45d8e1
Binary files /dev/null and b/docs/images/2d_fig2.png differ
diff --git a/docs/images/2d_fig3.png b/docs/images/2d_fig3.png
new file mode 100644
index 0000000..b8bf990
Binary files /dev/null and b/docs/images/2d_fig3.png differ
diff --git a/docs/images/3d_fig1.png b/docs/images/3d_fig1.png
new file mode 100644
index 0000000..3ea9081
Binary files /dev/null and b/docs/images/3d_fig1.png differ
diff --git a/docs/images/3d_fig2.png b/docs/images/3d_fig2.png
new file mode 100644
index 0000000..20abe73
Binary files /dev/null and b/docs/images/3d_fig2.png differ
diff --git a/docs/images/3d_fig3.png b/docs/images/3d_fig3.png
new file mode 100644
index 0000000..86ecaff
Binary files /dev/null and b/docs/images/3d_fig3.png differ
diff --git a/docs/images/fig1.png b/docs/images/fig1.png
deleted file mode 100644
index 84a4267..0000000
Binary files a/docs/images/fig1.png and /dev/null differ
diff --git a/docs/images/fig2.png b/docs/images/fig2.png
deleted file mode 100644
index 31b87bd..0000000
Binary files a/docs/images/fig2.png and /dev/null differ
diff --git a/docs/images/fig3.png b/docs/images/fig3.png
deleted file mode 100644
index dbbe65c..0000000
Binary files a/docs/images/fig3.png and /dev/null differ
diff --git a/setup.py b/setup.py
index 44ace56..db26571 100644
--- a/setup.py
+++ b/setup.py
@@ -80,7 +80,7 @@ def build_extension(self, ext):
 
 setup(
     name='python_prtree',
-    version='0.3.5',
+    version='0.4.0',
     license='MIT',
     description='Python implementation of Priority R-Tree',
     author='atksh',
diff --git a/src/python_prtree/__init__.py b/src/python_prtree/__init__.py
index 8eacf00..b35084d 100644
--- a/src/python_prtree/__init__.py
+++ b/src/python_prtree/__init__.py
@@ -1,2 +1,2 @@
-from .PRTree import PRTree
+from .PRTree import PRTree2D, PRTree3D
 
diff --git a/third/pybind11 b/third/pybind11
index 7e0a4fb..f61855b 160000
--- a/third/pybind11
+++ b/third/pybind11
@@ -1,1 +1,1 @@
-Subproject commit 7e0a4fb7d0c548231b2dc104054170cb6bebea96
+Subproject commit f61855b9d8821e2576960109a2a67379a6c2366f
diff --git a/third/xsimd b/third/xsimd
deleted file mode 160000
index 518e10e..0000000
--- a/third/xsimd
+++ /dev/null
@@ -1,1 +0,0 @@
-Subproject commit 518e10ee161caf7fac72141e3ff9fd68c87533c0


Test patch:
diff --git a/test.sh b/test.sh
new file mode 100644
index 0000000..aa25acc
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,5 @@
+clear
+rm -rf build dist .pytest_cache
+CXX=/usr/bin/g++ pip install .
+pytest tests -vv --capture=no
+
diff --git a/tests/test_PRTree.py b/tests/test_PRTree.py
index f8dc1d0..74160b9 100644
--- a/tests/test_PRTree.py
+++ b/tests/test_PRTree.py
@@ -1,70 +1,80 @@
-import unittest
-from python_prtree import PRTree
+import pytest
+from python_prtree import PRTree2D, PRTree3D
 import numpy as np
-import os
-import time
-
-
-class TestPRTree(unittest.TestCase):
-    def test_result(self):
-        def has_intersect(x, y):
-            b1 = max(x[0], y[0]) <= min(x[1], y[1])
-            b2 = max(x[2], y[2]) <= min(x[3], y[3])
-            return b1 and b2
-
-        idx = np.arange(100)
-        x = np.random.rand(len(idx), 4)
-        x[:, 1] += x[:, 0]
-        x[:, 3] += x[:, 2]
-
-        prtree = PRTree(idx, x)
-        out = prtree.batch_query(x)
-        for i in range(len(idx)):
-            tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k])]
-            self.assertEqual(set(out[i]), set(tmp))
-
-        prtree.save('tree.bin')
-        time.sleep(.3)
-        prtree = PRTree("tree.bin")
-
-        out = prtree.batch_query(x)
-        for i in range(len(idx)):
-            tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k])]
-            self.assertEqual(set(out[i]), set(tmp))
-
-        prtree = PRTree()
-        prtree.load('tree.bin')
-
-        out = prtree.batch_query(x)
-        for i in range(len(idx)):
-            tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k])]
-            self.assertEqual(set(out[i]), set(tmp))
-
-        os.remove('tree.bin')
-        N = 100000
-        idx = np.arange(N)
-        x = np.random.rand(N, 4)
-        x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100
-        x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100
-        prtree1 = PRTree(idx, x)
-
-        prtree2 = PRTree(idx[:N//2], x[:N//2])
-        for i in range(N//2, N):
-            prtree2.insert(idx[i], x[i])
-
-        x = np.random.rand(N, 4)
-        x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100
-        x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100
-        for i in range(N):
-            self.assertEqual(
-                set(prtree1.query(x[i])), set(prtree2.query(x[i])))
-
-        for i in range(N-1):
-            prtree1.erase(i)
-            prtree2.erase(i)
-
-
-def suite():
-    suite = unittest.TestSuite()
-    suite.addTests(unittest.makeSuite(TestPRTree))
-    return suite
+
+
+def has_intersect(x, y, dim):
+    return all(
+        [max(x[2 * i], y[2 * i]) <= min(x[2 * i + 1], y[2 * i + 1]) for i in range(dim)]
+    )
+
+
+@pytest.mark.parametrize("PRTree, dim", [(PRTree2D, 2), (PRTree3D, 3)])
+def test_result(PRTree, dim):
+    idx = np.arange(100)
+    x = np.random.rand(len(idx), 2 * dim)
+    for i in range(dim):
+        x[:, 2 * i + 1] += x[:, 2 * i]
+
+    prtree = PRTree(idx, x)
+    out = prtree.batch_query(x)
+    for i in range(len(idx)):
+        tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k], dim)]
+        assert set(out[i]) == set(tmp)
+
+
+@pytest.mark.parametrize("PRTree, dim", [(PRTree2D, 2), (PRTree3D, 3)])
+def test_io(PRTree, dim, tmp_path):
+    idx = np.arange(100)
+    x = np.random.rand(len(idx), 2 * dim)
+    for i in range(dim):
+        x[:, 2 * i + 1] += x[:, 2 * i]
+
+    prtree = PRTree(idx, x)
+
+    fname = tmp_path / "tree.bin"
+    fname = str(fname)
+    prtree.save(fname)
+    prtree = PRTree(fname)
+
+    out = prtree.batch_query(x)
+    for i in range(len(idx)):
+        tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k], dim)]
+        assert set(out[i]) == set(tmp)
+
+    prtree = PRTree()
+    prtree.load(fname)
+
+    out = prtree.batch_query(x)
+    for i in range(len(idx)):
+        tmp = [k for k in range(len(idx)) if has_intersect(x[i], x[k], dim)]
+        assert set(out[i]) == set(tmp)
+
+
+@pytest.mark.parametrize("PRTree, dim", [(PRTree2D, 2), (PRTree3D, 3)])
+def test_insert_erase(PRTree, dim):
+    N = 10000
+    idx = np.arange(N)
+    x = np.random.rand(N, 2 * dim)
+    for i in range(dim):
+        x[:, 2 * i + 1] = x[:, 2 * i] + x[:, 2 * i + 1] / np.sqrt(N) / 100
+    prtree1 = PRTree(idx, x)
+
+    prtree2 = PRTree(idx[: N // 2], x[: N // 2])
+    for i in range(N // 2, N):
+        prtree2.insert(idx[i], x[i])
+
+    x = np.random.rand(100, 2 * dim)
+    for i in range(dim):
+        x[:, 2 * i + 1] = x[:, 2 * i] + x[:, 2 * i + 1] / np.sqrt(N) / 100
+    for i in range(x.shape[0]):
+        assert set(prtree1.query(x[i])) == set(prtree2.query(x[i]))
+
+    for i in range(N // 2):
+        prtree1.erase(i)
+        prtree2.erase(i)
+
+    for i in range(dim):
+        x[:, 2 * i + 1] = x[:, 2 * i] + x[:, 2 * i + 1] / np.sqrt(N) / 100
+    for i in range(x.shape[0]):
+        assert set(prtree1.query(x[i])) == set(prtree2.query(x[i]))
diff --git a/tests/tmptest.py b/tests/tmptest.py
deleted file mode 100644
index afbb88d..0000000
--- a/tests/tmptest.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import gc
-import random
-from python_prtree import PRTree
-import numpy as np
-import time
-import gc
-
-
-def f(N):
-    idx = np.arange(N)
-    x = np.random.rand(N, 4)
-    x[:, 1] = x[:, 0] + x[:, 1] / np.sqrt(N) / 100
-    x[:, 3] = x[:, 2] + x[:, 3] / np.sqrt(N) / 100
-    s = time.time()
-    print(x.nbytes // 1024 // 1024, 'mb')
-    prtree = PRTree(idx, x)
-    #prtree.save(f'{N}.bin')
-    del prtree
-    gc.collect()
-    return time.time() - s
-
-
-for _ in range(1000):
-    f(10_000_000)