/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * Copyright (c) 2018 Kohei Yoshida * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * ************************************************************************/ #include "mdds/global.hpp" #include #include #include #include #include #include #include namespace mdds { namespace detail { namespace rtree { template using remove_cvref_t = typename std::remove_cv::type>::type; inline const char* to_string(node_type nt) { switch (nt) { case node_type::unspecified: return "unspecified"; case node_type::directory_leaf: return "directory-leaf"; case node_type::directory_nonleaf: return "directory-nonleaf"; case node_type::value: return "value"; } return "???"; } inline size_t calc_optimal_segment_size_for_pack(size_t init_size, size_t min_size, size_t max_size, size_t value_count) { // Keep increasing the size until the remainder becomes at least the min size. size_t final_size = init_size; for (; final_size < max_size; ++final_size) { size_t mod = value_count % final_size; if (!mod || mod >= min_size) return final_size; } // If increasing the size doesn't work, then decrease it. final_size = init_size; for (--final_size; min_size < final_size; --final_size) { size_t mod = value_count % final_size; if (!mod || mod >= min_size) return final_size; } // Nothing has worked. Use the original value. return init_size; } template auto calc_linear_intersection(size_t dim, const _Extent& bb1, const _Extent& bb2) -> remove_cvref_t { using key_type = remove_cvref_t; key_type start1 = bb1.start.d[dim], end1 = bb1.end.d[dim]; key_type start2 = bb2.start.d[dim], end2 = bb2.end.d[dim]; // Ensure that start1 < start2. if (start1 > start2) { std::swap(start1, start2); std::swap(end1, end2); } assert(start1 <= start2); if (end1 < start2) { // 1 : |------| // 2 : |-------| // These two are not intersected at all. Bail out. return key_type(); } if (end1 < end2) { // 1 : |---------| // 2 : |----------| return end1 - start2; } // 1 : |--------------| // 2 : |-----| return end2 - start2; } template bool intersects(const _Extent& bb1, const _Extent& bb2) { constexpr size_t dim_size = sizeof(bb1.start.d) / sizeof(bb1.start.d[0]); using key_type = remove_cvref_t; for (size_t dim = 0; dim < dim_size; ++dim) { key_type start1 = bb1.start.d[dim], end1 = bb1.end.d[dim]; key_type start2 = bb2.start.d[dim], end2 = bb2.end.d[dim]; // Ensure that start1 <= start2. if (start1 > start2) { std::swap(start1, start2); std::swap(end1, end2); } assert(start1 <= start2); if (end1 < start2) { // 1 : |------| // 2 : |-------| // These two are not intersected at all. Bail out. return false; } } return true; } template auto calc_intersection(const _Extent& bb1, const _Extent& bb2) -> remove_cvref_t { constexpr size_t dim_size = sizeof(bb1.start.d) / sizeof(bb1.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using key_type = remove_cvref_t; key_type total_volume = calc_linear_intersection<_Extent>(0, bb1, bb2); if (!total_volume) return key_type(); for (size_t dim = 1; dim < dim_size; ++dim) { key_type segment_len = calc_linear_intersection<_Extent>(dim, bb1, bb2); if (!segment_len) return key_type(); total_volume *= segment_len; } return total_volume; } template bool enlarge_extent_to_fit(_Extent& parent, const _Extent& child) { constexpr size_t dim_size = sizeof(parent.start.d) / sizeof(parent.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); bool enlarged = false; for (size_t dim = 0; dim < dim_size; ++dim) { if (child.start.d[dim] < parent.start.d[dim]) { parent.start.d[dim] = child.start.d[dim]; enlarged = true; } if (parent.end.d[dim] < child.end.d[dim]) { parent.end.d[dim] = child.end.d[dim]; enlarged = true; } } return enlarged; } template auto calc_area(const _Extent& bb) -> remove_cvref_t { constexpr size_t dim_size = sizeof(bb.start.d) / sizeof(bb.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using key_type = remove_cvref_t; key_type area = bb.end.d[0] - bb.start.d[0]; for (size_t dim = 1; dim < dim_size; ++dim) area *= bb.end.d[dim] - bb.start.d[dim]; return area; } template auto calc_square_distance(const _Pt& pt1, const _Pt& pt2) -> remove_cvref_t { constexpr size_t dim_size = sizeof(pt1.d) / sizeof(pt1.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using key_type = remove_cvref_t; key_type dist = key_type(); for (size_t dim = 0; dim < dim_size; ++dim) { key_type v1 = pt1.d[dim], v2 = pt2.d[dim]; if (v1 > v2) std::swap(v1, v2); // ensure that v1 <= v2. assert(v1 <= v2); key_type diff = v2 - v1; dist += diff * diff; } return dist; } /** * The margin here is defined as the sum of the lengths of the edges of a * bounding box, per the original paper on R*-tree. It's half-margin * because it only adds one of the two edges in each dimension. */ template auto calc_half_margin(const _Extent& bb) -> remove_cvref_t { constexpr size_t dim_size = sizeof(bb.start.d) / sizeof(bb.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using key_type = remove_cvref_t; key_type margin = bb.end.d[0] - bb.start.d[0]; for (size_t dim = 1; dim < dim_size; ++dim) margin += bb.end.d[dim] - bb.start.d[dim]; return margin; } /** * Area enlargement is calculated by calculating the area of the enlarged * box subtracted by the area of the original box prior to the enlargement. * * @param bb_host bounding box of the area receiving the new object. * @param bb_guest bounding of the new object being inserted. * * @return quantity of the area enlargement. */ template auto calc_area_enlargement(const _Extent& bb_host, const _Extent& bb_guest) -> remove_cvref_t { constexpr size_t dim_size = sizeof(bb_host.start.d) / sizeof(bb_host.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using key_type = remove_cvref_t; using extent = _Extent; // Calculate the original area. key_type original_area = calc_area<_Extent>(bb_host); // Enlarge the box for the new object if needed. extent bb_host_enlarged = bb_host; // make a copy. bool enlarged = enlarge_extent_to_fit<_Extent>(bb_host_enlarged, bb_guest); if (!enlarged) // Area enlargement did not take place. return key_type(); key_type enlarged_area = calc_area<_Extent>(bb_host_enlarged); return enlarged_area - original_area; } template auto calc_extent(_Iter it, _Iter it_end) -> decltype(it->extent) { auto bb = it->extent; for (++it; it != it_end; ++it) enlarge_extent_to_fit(bb, it->extent); return bb; } template auto get_center_point(const _Extent& extent) -> decltype(extent.start) { constexpr size_t dim_size = sizeof(extent.start.d) / sizeof(extent.start.d[0]); static_assert(dim_size > 0, "Dimension cannot be zero."); using point_type = decltype(extent.start); using key_type = decltype(extent.start.d[0]); point_type ret; static const key_type two = 2; for (size_t dim = 0; dim < dim_size; ++dim) ret.d[dim] = (extent.end.d[dim] + extent.start.d[dim]) / two; return ret; } template struct min_value_pos { _Key value = _Key(); size_t pos = 0; size_t count = 0; void assign(_Key new_value, size_t new_pos) { if (count) { // Assign only if it's less than the current value. if (new_value < value) { value = new_value; pos = new_pos; } } else { // The very first value. Just take it. value = new_value; pos = new_pos; } ++count; } }; template struct reinsertion_bucket { using key_type = _Key; key_type distance; size_t src_pos; }; template struct ptr_to_string { using node_ptr_type = _NodePtrT; using node_ptr_map_type = std::unordered_map; node_ptr_map_type node_ptr_map; std::string operator() (node_ptr_type np) const { auto it = node_ptr_map.find(np); return (it == node_ptr_map.end()) ? "(*, *)" : it->second; } ptr_to_string() { static_assert(std::is_pointer::value, "Node pointer type must be a real pointer type."); } ptr_to_string(const ptr_to_string&) = delete; ptr_to_string(ptr_to_string&& other) : node_ptr_map(std::move(other.node_ptr_map)) {} }; }} template rtree<_Key,_Value,_Trait>::point_type::point_type() { // Initialize the point values with the key type's default value. key_type* p = d; key_type* p_end = p + trait_type::dimensions; for (; p != p_end; ++p) *p = key_type{}; } template rtree<_Key,_Value,_Trait>::point_type::point_type(std::initializer_list vs) { // Initialize the point values with the key type's default value. key_type* dst = d; key_type* dst_end = dst + trait_type::dimensions; for (const key_type& v : vs) { if (dst == dst_end) throw std::range_error("number of elements exceeds the dimension size."); *dst = v; ++dst; } } template std::string rtree<_Key,_Value,_Trait>::point_type::to_string() const { std::ostringstream os; os << "("; for (size_t i = 0; i < trait_type::dimensions; ++i) { if (i > 0) os << ", "; os << d[i]; } os << ")"; return os.str(); } template bool rtree<_Key,_Value,_Trait>::point_type::operator== (const point_type& other) const { const key_type* left = d; const key_type* right = other.d; const key_type* left_end = left + trait_type::dimensions; for (; left != left_end; ++left, ++right) { if (*left != *right) return false; } return true; } template bool rtree<_Key,_Value,_Trait>::point_type::operator!= (const point_type& other) const { return !operator== (other); } template rtree<_Key,_Value,_Trait>::extent_type::extent_type() {} template rtree<_Key,_Value,_Trait>::extent_type::extent_type(const point_type& _start, const point_type& _end) : start(_start), end(_end) {} template std::string rtree<_Key,_Value,_Trait>::extent_type::to_string() const { std::ostringstream os; os << start.to_string(); if (!is_point()) os << " - " << end.to_string(); return os.str(); } template bool rtree<_Key,_Value,_Trait>::extent_type::is_point() const { return start == end; } template bool rtree<_Key,_Value,_Trait>::extent_type::operator== (const extent_type& other) const { return start == other.start && end == other.end; } template bool rtree<_Key,_Value,_Trait>::extent_type::operator!= (const extent_type& other) const { return !operator== (other); } template bool rtree<_Key,_Value,_Trait>::extent_type::contains(const point_type& pt) const { for (size_t dim = 0; dim < trait_type::dimensions; ++dim) { if (pt.d[dim] < start.d[dim] || end.d[dim] < pt.d[dim]) return false; } return true; } template bool rtree<_Key,_Value,_Trait>::extent_type::contains(const extent_type& bb) const { for (size_t dim = 0; dim < trait_type::dimensions; ++dim) { if (bb.start.d[dim] < start.d[dim] || end.d[dim] < bb.end.d[dim]) return false; } return true; } template bool rtree<_Key,_Value,_Trait>::extent_type::intersects(const extent_type& bb) const { return detail::rtree::intersects(bb, *this); } template bool rtree<_Key,_Value,_Trait>::extent_type::contains_at_boundary(const extent_type& bb) const { for (size_t dim = 0; dim < trait_type::dimensions; ++dim) { if (start.d[dim] == bb.start.d[dim] || bb.end.d[dim] == end.d[dim]) return true; } return false; } template rtree<_Key,_Value,_Trait>::node_store::node_store() : type(node_type::unspecified), parent(nullptr), node_ptr(nullptr), count(0), valid_pointer(true) {} template rtree<_Key,_Value,_Trait>::node_store::node_store(node_store&& r) : type(r.type), extent(r.extent), parent(r.parent), node_ptr(r.node_ptr), count(r.count), valid_pointer(r.valid_pointer) { r.type = node_type::unspecified; r.extent = extent_type(); r.parent = nullptr; r.node_ptr = nullptr; r.count = 0; r.valid_pointer = true; } template rtree<_Key,_Value,_Trait>::node_store::node_store(node_type _type, const extent_type& _extent, node* _node_ptr) : type(_type), extent(_extent), parent(nullptr), node_ptr(_node_ptr), count(0), valid_pointer(true) {} template rtree<_Key,_Value,_Trait>::node_store::~node_store() { if (node_ptr) { switch (type) { case node_type::directory_leaf: case node_type::directory_nonleaf: delete static_cast(node_ptr); break; case node_type::value: delete static_cast(node_ptr); break; case node_type::unspecified: default: assert(!"node::~node: unknown node type!"); } } } template typename rtree<_Key,_Value,_Trait>::node_store rtree<_Key,_Value,_Trait>::node_store::clone() const { auto func_copy_dir = [this](node_store& cloned, const directory_node* src) { directory_node* dir = cloned.get_directory_node(); assert(dir); for (const node_store& ns : src->children) dir->children.push_back(ns.clone()); cloned.count = count; cloned.extent = extent; }; switch (type) { case node_type::directory_leaf: { const directory_node* src = static_cast(node_ptr); node_store cloned = create_leaf_directory_node(); func_copy_dir(cloned, src); return cloned; } case node_type::directory_nonleaf: { const directory_node* src = static_cast(node_ptr); node_store cloned = create_nonleaf_directory_node(); func_copy_dir(cloned, src); return cloned; } case node_type::value: { const value_node* vn = static_cast(node_ptr); return create_value_node(extent, vn->value); } case node_type::unspecified: default: assert(!"node::~node: unknown node type!"); } } template typename rtree<_Key,_Value,_Trait>::node_store rtree<_Key,_Value,_Trait>::node_store::create_leaf_directory_node() { node_store ret(node_type::directory_leaf, extent_type(), new directory_node); ret.valid_pointer = false; return ret; } template typename rtree<_Key,_Value,_Trait>::node_store rtree<_Key,_Value,_Trait>::node_store::create_nonleaf_directory_node() { node_store ret(node_type::directory_nonleaf, extent_type(), new directory_node); ret.valid_pointer = false; return ret; } template typename rtree<_Key,_Value,_Trait>::node_store rtree<_Key,_Value,_Trait>::node_store::create_value_node(const extent_type& extent, value_type&& v) { node_store ret(node_type::value, extent, new value_node(std::move(v))); return ret; } template typename rtree<_Key,_Value,_Trait>::node_store rtree<_Key,_Value,_Trait>::node_store::create_value_node(const extent_type& extent, const value_type& v) { node_store ret(node_type::value, extent, new value_node(v)); return ret; } template typename rtree<_Key,_Value,_Trait>::node_store& rtree<_Key,_Value,_Trait>::node_store::operator= (node_store&& other) { node_store tmp(std::move(other)); swap(tmp); return *this; } template bool rtree<_Key,_Value,_Trait>::node_store::pack() { const directory_node* dir = get_directory_node(); if (!dir) return false; const dir_store_type& children = dir->children; if (children.empty()) { // This node has no children. Reset the bounding box to empty. extent_type new_box; bool changed = new_box != extent; extent = new_box; return changed; } extent_type new_box = dir->calc_extent(); bool changed = new_box != extent; extent = new_box; // update the bounding box. return changed; } template void rtree<_Key,_Value,_Trait>::node_store::pack_upward() { bool propagate = true; for (node_store* p = parent; propagate && p; p = p->parent) propagate = p->pack(); } template bool rtree<_Key,_Value,_Trait>::node_store::is_directory() const { switch (type) { case node_type::directory_leaf: case node_type::directory_nonleaf: return true; default: ; } return false; } template bool rtree<_Key,_Value,_Trait>::node_store::is_root() const { return parent == nullptr; } template bool rtree<_Key,_Value,_Trait>::node_store::exceeds_capacity() const { if (type != node_type::directory_leaf) return false; return count > trait_type::max_node_size; } template void rtree<_Key,_Value,_Trait>::node_store::swap(node_store& other) { std::swap(type, other.type); std::swap(extent, other.extent); std::swap(parent, other.parent); std::swap(node_ptr, other.node_ptr); std::swap(count, other.count); std::swap(valid_pointer, other.valid_pointer); } template void rtree<_Key,_Value,_Trait>::node_store::reset_parent_pointers_of_children() { if (valid_pointer) return; directory_node* dir = get_directory_node(); if (!dir) return; for (node_store& ns : dir->children) { ns.parent = this; ns.reset_parent_pointers_of_children(); } valid_pointer = true; } template void rtree<_Key,_Value,_Trait>::node_store::reset_parent_pointers() { valid_pointer = false; reset_parent_pointers_of_children(); } template typename rtree<_Key,_Value,_Trait>::directory_node* rtree<_Key,_Value,_Trait>::node_store::get_directory_node() { if (!is_directory()) return nullptr; return static_cast(node_ptr); } template const typename rtree<_Key,_Value,_Trait>::directory_node* rtree<_Key,_Value,_Trait>::node_store::get_directory_node() const { if (!is_directory()) return nullptr; return static_cast(node_ptr); } template bool rtree<_Key,_Value,_Trait>::node_store::erase_child(const node_store* p) { if (!is_directory()) return false; directory_node* dir = static_cast(node_ptr); bool erased = dir->erase(p); if (erased) --count; assert(count == dir->children.size()); return erased; } template rtree<_Key,_Value,_Trait>::node::node() {} template rtree<_Key,_Value,_Trait>::node::~node() {} template rtree<_Key,_Value,_Trait>::value_node::value_node(value_type&& _value) : value(std::move(_value)) {} template rtree<_Key,_Value,_Trait>::value_node::value_node(const value_type& _value) : value(_value) {} template rtree<_Key,_Value,_Trait>::value_node::~value_node() {} template rtree<_Key,_Value,_Trait>::directory_node::directory_node() {} template rtree<_Key,_Value,_Trait>::directory_node::~directory_node() {} template bool rtree<_Key,_Value,_Trait>::directory_node::erase(const node_store* ns) { auto it = std::find_if(children.begin(), children.end(), [ns](const node_store& this_ns) -> bool { return &this_ns == ns; } ); if (it == children.end()) return false; it = children.erase(it); // All nodes that occur after the erased node have their memory addresses // shifted. std::for_each(it, children.end(), [](node_store& this_ns) { this_ns.valid_pointer = false; } ); return true; } template typename rtree<_Key,_Value,_Trait>::node_store* rtree<_Key,_Value,_Trait>::directory_node::get_child_with_minimal_overlap(const extent_type& bb) { key_type min_overlap = key_type(); key_type min_area_enlargement = key_type(); key_type min_area = key_type(); node_store* dst = nullptr; for (node_store& ns : children) { directory_node* dir = static_cast(ns.node_ptr); key_type overlap = dir->calc_overlap_cost(bb); key_type area_enlargement = detail::rtree::calc_area_enlargement(ns.extent, bb); key_type area = detail::rtree::calc_area(ns.extent); bool pick_this = false; if (!dst) pick_this = true; else if (overlap < min_overlap) // Pick the entry with the smaller overlap cost increase. pick_this = true; else if (area_enlargement < min_area_enlargement) // Pick the entry with the smaller area enlargment. pick_this = true; else if (area < min_area) // Resolve ties by picking the one with on the smaller area // rectangle. pick_this = true; if (pick_this) { min_overlap = overlap; min_area_enlargement = area_enlargement; min_area = area; dst = &ns; } } return dst; } template typename rtree<_Key,_Value,_Trait>::node_store* rtree<_Key,_Value,_Trait>::directory_node::get_child_with_minimal_area_enlargement(const extent_type& bb) { // Compare the costs of area enlargements. key_type min_cost = key_type(); key_type min_area = key_type(); node_store* dst = nullptr; for (node_store& ns : children) { key_type cost = detail::rtree::calc_area_enlargement(ns.extent, bb); key_type area = detail::rtree::calc_area(ns.extent); bool pick_this = false; if (!dst) pick_this = true; else if (cost < min_cost) // Pick the entry with the smaller area enlargment. pick_this = true; else if (area < min_area) // Resolve ties by picking the one with on the smaller area // rectangle. pick_this = true; if (pick_this) { min_cost = cost; min_area = area; dst = &ns; } } return dst; } template typename rtree<_Key,_Value,_Trait>::extent_type rtree<_Key,_Value,_Trait>::directory_node::calc_extent() const { auto it = children.cbegin(), ite = children.cend(); extent_type box; if (it != ite) box = detail::rtree::calc_extent(it, ite); return box; } template typename rtree<_Key,_Value,_Trait>::key_type rtree<_Key,_Value,_Trait>::directory_node::calc_overlap_cost(const extent_type& bb) const { key_type overlap_cost = key_type(); for (const node_store& ns : children) overlap_cost += detail::rtree::calc_intersection(ns.extent, bb); return overlap_cost; } template bool rtree<_Key,_Value,_Trait>::directory_node::has_leaf_directory() const { for (const auto& ns : children) { if (ns.type == node_type::directory_leaf) return true; } return false; } template template void rtree<_Key,_Value,_Trait>::search_results_base<_NS>::add_node_store( node_store_type* ns, size_t depth) { m_store.emplace_back(ns, depth); } template template rtree<_Key,_Value,_Trait>::search_results_base<_NS>::entry::entry(node_store_type* _ns, size_t _depth) : ns(_ns), depth(_depth) {} template template rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::iterator_base(store_iterator_type pos) : m_pos(std::move(pos)) {} template template bool rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator== (const self_iterator_type& other) const { return m_pos == other.m_pos; } template template bool rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator!= (const self_iterator_type& other) const { return !operator==(other); } template template typename rtree<_Key,_Value,_Trait>::template iterator_base<_SelfIter,_StoreIter,_ValueT>::self_iterator_type& rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator++() { ++m_pos; return static_cast(*this); } template template typename rtree<_Key,_Value,_Trait>::template iterator_base<_SelfIter,_StoreIter,_ValueT>::self_iterator_type rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator++(int) { self_iterator_type ret(m_pos); ++m_pos; return ret; } template template typename rtree<_Key,_Value,_Trait>::template iterator_base<_SelfIter,_StoreIter,_ValueT>::self_iterator_type& rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator--() { --m_pos; return static_cast(*this); } template template typename rtree<_Key,_Value,_Trait>::template iterator_base<_SelfIter,_StoreIter,_ValueT>::self_iterator_type rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::operator--(int) { self_iterator_type ret(m_pos); --m_pos; return ret; } template template const typename rtree<_Key,_Value,_Trait>::extent_type& rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::extent() const { return m_pos->ns->extent; } template template size_t rtree<_Key,_Value,_Trait>::iterator_base<_SelfIter,_StoreIter,_ValueT>::depth() const { return m_pos->depth; } template typename rtree<_Key,_Value,_Trait>::const_iterator rtree<_Key,_Value,_Trait>::const_search_results::cbegin() const { return const_iterator(m_store.cbegin()); } template typename rtree<_Key,_Value,_Trait>::const_iterator rtree<_Key,_Value,_Trait>::const_search_results::cend() const { return const_iterator(m_store.cend()); } template typename rtree<_Key,_Value,_Trait>::const_iterator rtree<_Key,_Value,_Trait>::const_search_results::begin() const { return const_iterator(m_store.cbegin()); } template typename rtree<_Key,_Value,_Trait>::const_iterator rtree<_Key,_Value,_Trait>::const_search_results::end() const { return const_iterator(m_store.cend()); } template typename rtree<_Key,_Value,_Trait>::iterator rtree<_Key,_Value,_Trait>::search_results::begin() { return iterator(m_store.begin()); } template typename rtree<_Key,_Value,_Trait>::iterator rtree<_Key,_Value,_Trait>::search_results::end() { return iterator(m_store.end()); } template rtree<_Key,_Value,_Trait>::const_iterator::const_iterator(store_iterator_type pos) : base_type(std::move(pos)) {} template typename rtree<_Key,_Value,_Trait>::const_iterator::value_type& rtree<_Key,_Value,_Trait>::const_iterator::operator*() const { return static_cast(m_pos->ns->node_ptr)->value; } template typename rtree<_Key,_Value,_Trait>::const_iterator::value_type* rtree<_Key,_Value,_Trait>::const_iterator::operator->() const { return &operator*(); } template rtree<_Key,_Value,_Trait>::iterator::iterator(store_iterator_type pos) : base_type(std::move(pos)) {} template typename rtree<_Key,_Value,_Trait>::iterator::value_type& rtree<_Key,_Value,_Trait>::iterator::operator*() { return static_cast(m_pos->ns->node_ptr)->value; } template typename rtree<_Key,_Value,_Trait>::iterator::value_type* rtree<_Key,_Value,_Trait>::iterator::operator->() { return &operator*(); } template rtree<_Key,_Value,_Trait>::bulk_loader::bulk_loader() { } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert(const extent_type& extent, value_type&& value) { insert_impl(extent, std::move(value)); } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert(const extent_type& extent, const value_type& value) { insert_impl(extent, value); } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert(const point_type& position, value_type&& value) { insert_impl(extent_type({position, position}), std::move(value)); } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert(const point_type& position, const value_type& value) { insert_impl(extent_type({position, position}), value); } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert_impl(const extent_type& extent, value_type&& value) { node_store ns_value = node_store::create_value_node(extent, std::move(value)); m_store.emplace_back(std::move(ns_value)); } template void rtree<_Key,_Value,_Trait>::bulk_loader::insert_impl(const extent_type& extent, const value_type& value) { node_store ns_value = node_store::create_value_node(extent, value); m_store.emplace_back(std::move(ns_value)); } template rtree<_Key,_Value,_Trait> rtree<_Key,_Value,_Trait>::bulk_loader::pack() { size_t depth = 0; for (; m_store.size() > trait_type::max_node_size; ++depth) pack_level(m_store, depth); // By this point, the number of directory nodes should have been reduced // below the max node size. Create a root directory and store them there. assert(m_store.size() <= trait_type::max_node_size); node_store root = node_store::create_leaf_directory_node(); if (depth > 0) root.type = node_type::directory_nonleaf; directory_node* dir = root.get_directory_node(); assert(dir); dir->children.swap(m_store); root.count = dir->children.size(); root.pack(); rtree tree(std::move(root)); return tree; } template void rtree<_Key,_Value,_Trait>::bulk_loader::pack_level(dir_store_type& store, size_t depth) { assert(!store.empty()); float n_total_node = std::ceil(store.size() / float(trait_type::max_node_size)); float n_splits_per_dim = std::ceil( std::pow(n_total_node, 1.0f / float(trait_type::dimensions))); // The first dimension will start with one segment. std::vector segments; segments.emplace_back(store.begin(), store.end(), store.size()); for (size_t dim = 0; dim < trait_type::dimensions; ++dim) { if (segments[0].size <= trait_type::max_node_size) break; std::vector next_segments; for (dir_store_segment& seg : segments) { assert(seg.size == size_t(std::distance(seg.begin, seg.end))); if (seg.size <= trait_type::max_node_size) { next_segments.push_back(seg); continue; } // Sort by the current dimension key. std::sort(seg.begin, seg.end, [dim](const node_store& left, const node_store& right) -> bool { // Compare the middle points. float left_key = (left.extent.end.d[dim] + left.extent.start.d[dim]) / 2.0f; float right_key = (right.extent.end.d[dim] + right.extent.start.d[dim]) / 2.0f; return left_key < right_key; } ); // Size of each segment in this dimension splits. size_t segment_size = detail::rtree::calc_optimal_segment_size_for_pack( std::ceil(seg.size / n_splits_per_dim), trait_type::min_node_size, trait_type::max_node_size, seg.size); size_t n_cur_segment = 0; auto begin = seg.begin; for (auto it = begin; it != seg.end; ++it, ++n_cur_segment) { if (n_cur_segment == segment_size) { // Push a new segment. next_segments.emplace_back(begin, it, n_cur_segment); begin = it; n_cur_segment = 0; } } if (begin != seg.end) { size_t n = std::distance(begin, seg.end); next_segments.emplace_back(begin, seg.end, n); } } #ifdef MDDS_RTREE_DEBUG size_t test_total = 0; for (const auto& seg : next_segments) test_total += seg.size; if (test_total != store.size()) throw std::logic_error( "The total combined segment sizes must equal the size of the inserted values!"); #endif segments.swap(next_segments); } #ifdef MDDS_RTREE_DEBUG // Check the final segment. size_t test_total = 0; for (const auto& seg : segments) test_total += seg.size; if (test_total != store.size()) throw std::logic_error( "The total combined segment sizes must equal the size of the inserted values!"); #endif assert(!segments.empty()); // Create a set of directory nodes from the current segments. dir_store_type next_store; for (dir_store_segment& seg : segments) { node_store ns = node_store::create_leaf_directory_node(); if (depth > 0) ns.type = node_type::directory_nonleaf; directory_node* dir = ns.get_directory_node(); assert(dir); // this better not be null since we know it's a directory node. for (auto it = seg.begin; it != seg.end; ++it) dir->children.push_back(std::move(*it)); ns.count = dir->children.size(); ns.pack(); next_store.push_back(std::move(ns)); } store.swap(next_store); } template rtree<_Key,_Value,_Trait>::rtree() : m_root(node_store::create_leaf_directory_node()) { static_assert(trait_type::min_node_size <= trait_type::max_node_size / 2, "Minimum node size must be less than half of the maximum node size."); static_assert(trait_type::reinsertion_size <= (trait_type::max_node_size - trait_type::min_node_size + 1), "Reinsertion size is too large."); } template rtree<_Key,_Value,_Trait>::rtree(rtree&& other) : m_root(std::move(other.m_root)) { // The root node must be a valid directory at all times. other.m_root = node_store::create_leaf_directory_node(); // Since the moved root has its memory location changed, we need to update // the parent pointers in its child nodes. m_root.reset_parent_pointers(); } template rtree<_Key,_Value,_Trait>::rtree(const rtree& other) : m_root(other.m_root.clone()) { m_root.reset_parent_pointers(); } template rtree<_Key,_Value,_Trait>::rtree(node_store&& root) : m_root(std::move(root)) { m_root.reset_parent_pointers(); } template rtree<_Key,_Value,_Trait>::~rtree() { } template rtree<_Key,_Value,_Trait>& rtree<_Key,_Value,_Trait>::operator= (const rtree& other) { rtree tmp(other); tmp.swap(*this); return *this; } template rtree<_Key,_Value,_Trait>& rtree<_Key,_Value,_Trait>::operator= (rtree&& other) { rtree tmp(std::move(other)); tmp.swap(*this); return *this; } template void rtree<_Key,_Value,_Trait>::insert(const extent_type& extent, value_type&& value) { insert_impl(extent.start, extent.end, std::move(value)); } template void rtree<_Key,_Value,_Trait>::insert(const extent_type& extent, const value_type& value) { insert_impl(extent.start, extent.end, value); } template void rtree<_Key,_Value,_Trait>::insert(const point_type& position, value_type&& value) { insert_impl(position, position, std::move(value)); } template void rtree<_Key,_Value,_Trait>::insert(const point_type& position, const value_type& value) { insert_impl(position, position, value); } template void rtree<_Key,_Value,_Trait>::insert_impl(const point_type& start, const point_type& end, value_type&& value) { extent_type bb(start, end); node_store new_ns = node_store::create_value_node(bb, std::move(value)); std::unordered_set reinserted_depths; insert(std::move(new_ns), &reinserted_depths); } template void rtree<_Key,_Value,_Trait>::insert_impl(const point_type& start, const point_type& end, const value_type& value) { extent_type bb(start, end); node_store new_ns = node_store::create_value_node(bb, value); std::unordered_set reinserted_depths; insert(std::move(new_ns), &reinserted_depths); } template void rtree<_Key,_Value,_Trait>::insert(node_store&& ns, std::unordered_set* reinserted_depths) { extent_type ns_box = ns.extent; insertion_point insert_pt = find_leaf_directory_node_for_insertion(ns_box); node_store* dir_ns = insert_pt.ns; size_t depth = insert_pt.depth; assert(dir_ns); assert(dir_ns->type == node_type::directory_leaf); directory_node* dir = static_cast(dir_ns->node_ptr); // Insert the new value to this node. ns.parent = insert_pt.ns; dir->children.push_back(std::move(ns)); ++dir_ns->count; if (dir_ns->exceeds_capacity()) { if (trait_type::enable_forced_reinsertion) { if (reinserted_depths && !reinserted_depths->count(depth)) { // We perform forced re-insertion exactly once per depth level. reinserted_depths->insert(depth); perform_forced_reinsertion(dir_ns, *reinserted_depths); } else split_node(dir_ns); } else split_node(dir_ns); return; } if (dir_ns->count == 1) dir_ns->extent = ns_box; else detail::rtree::enlarge_extent_to_fit(dir_ns->extent, ns_box); extent_type bb = dir_ns->extent; // grab the parent bounding box. // Propagate the bounding box update up the tree all the way to the root. for (dir_ns = dir_ns->parent; dir_ns; dir_ns = dir_ns->parent) { assert(dir_ns->count > 0); detail::rtree::enlarge_extent_to_fit(dir_ns->extent, bb); } } template void rtree<_Key,_Value,_Trait>::insert_dir(node_store&& ns, size_t max_depth) { assert(ns.is_directory()); extent_type ns_box = ns.extent; node_store* dir_ns = find_nonleaf_directory_node_for_insertion(ns_box, max_depth); assert(dir_ns); assert(dir_ns->type == node_type::directory_nonleaf); directory_node* dir = static_cast(dir_ns->node_ptr); // Insert the new directory to this node. ns.parent = dir_ns; ns.valid_pointer = false; dir->children.push_back(std::move(ns)); ++dir_ns->count; dir->children.back().reset_parent_pointers_of_children(); if (dir_ns->exceeds_capacity()) { split_node(dir_ns); return; } if (dir_ns->count == 1) dir_ns->extent = ns_box; else detail::rtree::enlarge_extent_to_fit(dir_ns->extent, ns_box); extent_type bb = dir_ns->extent; // grab the parent bounding box. // Propagate the bounding box update up the tree all the way to the root. for (dir_ns = dir_ns->parent; dir_ns; dir_ns = dir_ns->parent) { assert(dir_ns->count > 0); detail::rtree::enlarge_extent_to_fit(dir_ns->extent, bb); } } template typename rtree<_Key,_Value,_Trait>::const_search_results rtree<_Key,_Value,_Trait>::search(const point_type& pt, search_type st) const { search_condition_type dir_cond, value_cond; switch (st) { case search_type::overlap: { dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); }; value_cond = dir_cond; break; } case search_type::match: { dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); }; value_cond = [&pt](const node_store& ns) -> bool { return ns.extent.start == pt && ns.extent.end == pt; }; break; } default: throw std::runtime_error("Unhandled search type."); } const_search_results ret; search_descend(0, dir_cond, value_cond, m_root, ret); return ret; } template typename rtree<_Key,_Value,_Trait>::search_results rtree<_Key,_Value,_Trait>::search(const point_type& pt, search_type st) { search_condition_type dir_cond, value_cond; switch (st) { case search_type::overlap: { dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); }; value_cond = dir_cond; break; } case search_type::match: { dir_cond = [&pt](const node_store& ns) -> bool { return ns.extent.contains(pt); }; value_cond = [&pt](const node_store& ns) -> bool { return ns.extent.start == pt && ns.extent.end == pt; }; break; } default: throw std::runtime_error("Unhandled search type."); } search_results ret; search_descend(0, dir_cond, value_cond, m_root, ret); return ret; } template typename rtree<_Key,_Value,_Trait>::const_search_results rtree<_Key,_Value,_Trait>::search(const extent_type& extent, search_type st) const { search_condition_type dir_cond, value_cond; switch (st) { case search_type::overlap: { dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.intersects(extent); }; value_cond = dir_cond; break; } case search_type::match: { dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.contains(extent); }; value_cond = [&extent](const node_store& ns) -> bool { return ns.extent == extent; }; break; } default: throw std::runtime_error("Unhandled search type."); } const_search_results ret; search_descend(0, dir_cond, value_cond, m_root, ret); return ret; } template typename rtree<_Key,_Value,_Trait>::search_results rtree<_Key,_Value,_Trait>::search(const extent_type& extent, search_type st) { search_condition_type dir_cond, value_cond; switch (st) { case search_type::overlap: { dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.intersects(extent); }; value_cond = dir_cond; break; } case search_type::match: { dir_cond = [&extent](const node_store& ns) -> bool { return ns.extent.contains(extent); }; value_cond = [&extent](const node_store& ns) -> bool { return ns.extent == extent; }; break; } default: throw std::runtime_error("Unhandled search type."); } search_results ret; search_descend(0, dir_cond, value_cond, m_root, ret); return ret; } template void rtree<_Key,_Value,_Trait>::erase(const const_iterator& pos) { const node_store* ns = pos.m_pos->ns; size_t depth = pos.m_pos->depth; erase_impl(ns, depth); } template void rtree<_Key,_Value,_Trait>::erase(const iterator& pos) { const node_store* ns = pos.m_pos->ns; size_t depth = pos.m_pos->depth; erase_impl(ns, depth); } template void rtree<_Key,_Value,_Trait>::erase_impl(const node_store* ns, size_t depth) { assert(ns->type == node_type::value); assert(ns->parent); extent_type bb_erased = ns->extent; // Move up to the parent and find its stored location. node_store* dir_ns = ns->parent; --depth; assert(dir_ns->type == node_type::directory_leaf); bool erased = dir_ns->erase_child(ns); assert(erased); (void)erased; // to avoid getting a warning on "variable set but not used". if (dir_ns->is_root()) { shrink_tree_upward(dir_ns, bb_erased); return; } if (dir_ns->count >= trait_type::min_node_size) { // The parent directory still contains enough nodes. No need to dissolve it. shrink_tree_upward(dir_ns, bb_erased); return; } // Dissolve the node the erased value node belongs to, and reinsert // all its siblings. assert(!dir_ns->is_root()); assert(dir_ns->count < trait_type::min_node_size); dir_store_type orphan_value_nodes; directory_node* dir = static_cast(dir_ns->node_ptr); dir->children.swap(orphan_value_nodes); // moves all the rest of the value node entries to the orphan store. // Move up one level, and remove this directory node from its parent directory node. node_store* child_ns = dir_ns; dir_ns = dir_ns->parent; --depth; erased = dir_ns->erase_child(child_ns); assert(erased); dir_ns->reset_parent_pointers(); dir_ns->pack(); orphan_node_entries_type orphan_dir_nodes; while (!dir_ns->is_root() && dir_ns->count < trait_type::min_node_size) { // This directory node is now underfilled. Move all its children out // for re-insertion and dissolve this node. dir = static_cast(dir_ns->node_ptr); while (!dir->children.empty()) { orphan_dir_nodes.emplace_back(); orphan_dir_nodes.back().ns.swap(dir->children.back()); orphan_dir_nodes.back().depth = depth + 1; // depth of the children. dir->children.pop_back(); } // Find and remove this node from its parent store. node_store* dir_ns_child = dir_ns; dir_ns = dir_ns->parent; --depth; erased = dir_ns->erase_child(dir_ns_child); assert(erased); dir_ns->reset_parent_pointers_of_children(); dir_ns->pack(); } while (!orphan_dir_nodes.empty()) { orphan_node_entry& entry = orphan_dir_nodes.back(); insert_dir(std::move(entry.ns), entry.depth); orphan_dir_nodes.pop_back(); } while (!orphan_value_nodes.empty()) { insert(std::move(orphan_value_nodes.back()), nullptr); orphan_value_nodes.pop_back(); } if (m_root.count == 1) { // If the root node only has one child, make that child the new root. // Be careful not to leak memory here... dir = static_cast(m_root.node_ptr); assert(dir->children.size() == 1); node_store new_root(std::move(dir->children.back())); dir->children.clear(); new_root.parent = nullptr; m_root.swap(new_root); m_root.reset_parent_pointers(); } } template const typename rtree<_Key,_Value,_Trait>::extent_type& rtree<_Key,_Value,_Trait>::extent() const { return m_root.extent; } template bool rtree<_Key,_Value,_Trait>::empty() const { return !m_root.count; } template size_t rtree<_Key,_Value,_Trait>::size() const { size_t n = 0; descend_with_func( [&n](const node_properties& np) { if (np.type == node_type::value) ++n; } ); return n; } template void rtree<_Key,_Value,_Trait>::swap(rtree& other) { m_root.swap(other.m_root); m_root.reset_parent_pointers(); other.m_root.reset_parent_pointers(); } template void rtree<_Key,_Value,_Trait>::clear() { node_store new_root = node_store::create_leaf_directory_node(); m_root.swap(new_root); } template template void rtree<_Key,_Value,_Trait>::walk(_Func func) const { descend_with_func(std::move(func)); } template void rtree<_Key,_Value,_Trait>::check_integrity(const integrity_check_properties& props) const { auto func_ptr_to_string = build_ptr_to_string_map(); switch (m_root.type) { case node_type::directory_leaf: case node_type::directory_nonleaf: // Good. break; default: throw integrity_error("The root node must be a directory node."); } if (m_root.parent) throw integrity_error("The root node should not have a non-null parent."); std::vector ns_stack; std::function func_descend = [&ns_stack,&func_descend,&func_ptr_to_string,&props](const node_store* ns, int level) -> bool { bool valid = true; std::string indent; for (int i = 0; i < level; ++i) indent += " "; const node_store* parent = nullptr; extent_type parent_bb; if (!ns_stack.empty()) { parent = ns_stack.back(); parent_bb = parent->extent; } if (!props.throw_on_first_error) { std::cout << indent << "node: " << func_ptr_to_string(ns) << "; parent: " << func_ptr_to_string(ns->parent) << "; type: " << to_string(ns->type) << "; extent: " << ns->extent.to_string() << std::endl; } if (parent) { if (ns->parent != parent) { std::ostringstream os; os << "The parent node pointer does not point to the real parent. (expected: " << parent << "; stored in node: " << ns->parent << ")"; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } if (!parent_bb.contains(ns->extent)) { std::ostringstream os; os << "The extent of the child " << ns->extent.to_string() << " is not within the extent of the parent " << parent_bb.to_string() << "."; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } switch (ns->type) { case node_type::directory_leaf: { if (parent->type != node_type::directory_nonleaf) { std::ostringstream os; os << "Parent of a leaf directory node must be non-leaf."; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } break; } case node_type::directory_nonleaf: { if (parent->type != node_type::directory_nonleaf) { std::ostringstream os; os << "Parent of a non-leaf directory node must also be non-leaf."; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } break; } case node_type::value: { if (parent->type != node_type::directory_leaf) { std::ostringstream os; os << "Parent of a value node must be a leaf directory node."; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } break; } default: throw integrity_error("Unexpected node type!"); } } ns_stack.push_back(ns); switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); if (ns->count != dir->children.size()) { std::ostringstream os; os << "Incorrect count of child nodes detected. (expected: " << dir->children.size() << "; actual: " << ns->count << ")"; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } bool node_underfill_allowed = false; if (ns->is_root() && ns->type == node_type::directory_leaf) // If the root directory is a leaf, it's allowed to be underfilled. node_underfill_allowed = true; if (!node_underfill_allowed && ns->count < trait_type::min_node_size) { std::ostringstream os; os << "The number of child nodes (" << ns->count << ") is less than the minimum allowed number of " << trait_type::min_node_size; if (props.error_on_min_node_size && props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; if (props.error_on_min_node_size) valid = false; } if (trait_type::max_node_size < ns->count) { std::ostringstream os; os << "The number of child nodes (" << ns->count << ") exceeds the maximum allowed number of " << trait_type::max_node_size; if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } // Check to make sure the bounding box of the current node is // tightly packed. extent_type bb_expected = dir->calc_extent(); if (bb_expected != ns->extent) { std::ostringstream os; os << "The extent of the node " << ns->extent.to_string() << " does not equal truly tight extent " << bb_expected.to_string(); if (props.throw_on_first_error) throw integrity_error(os.str()); std::cout << indent << "* " << os.str() << std::endl; valid = false; } for (const node_store& ns_child : dir->children) { bool valid_subtree = func_descend(&ns_child, level+1); if (!valid_subtree) valid = false; } break; } case node_type::value: // Do nothing. break; default: throw integrity_error("Unexpected node type!"); } ns_stack.pop_back(); return valid; }; bool valid = func_descend(&m_root, 0); if (!valid) throw integrity_error("Tree contains one or more errors."); } template std::string rtree<_Key,_Value,_Trait>::export_tree(export_tree_type mode) const { switch (mode) { case export_tree_type::formatted_node_properties: return export_tree_formatted(); case export_tree_type::extent_as_obj: return export_tree_extent_as_obj(); case export_tree_type::extent_as_svg: return export_tree_extent_as_svg(); default: throw std::runtime_error("unhandled export tree type."); } } template detail::rtree::ptr_to_string::node_store*> rtree<_Key,_Value,_Trait>::build_ptr_to_string_map() const { detail::rtree::ptr_to_string func; std::function func_build_node_ptr = [&func_build_node_ptr,&func](const node_store* ns, int level, int pos) { std::ostringstream os; os << "(" << level << ", " << pos << ")"; func.node_ptr_map.insert(std::make_pair(ns, os.str())); switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); int child_pos = 0; for (const node_store& ns_child : dir->children) func_build_node_ptr(&ns_child, level+1, child_pos++); break; } case node_type::value: // Do nothing. break; default: throw integrity_error("Unexpected node type!"); } }; func_build_node_ptr(&m_root, 0, 0); return func; } template std::string rtree<_Key,_Value,_Trait>::export_tree_formatted() const { auto func_ptr_to_string = build_ptr_to_string_map(); std::ostringstream os; std::function func_descend = [&func_descend,&os,&func_ptr_to_string](const node_store* ns, int level) { std::string indent; for (int i = 0; i < level; ++i) indent += " "; os << indent << "node: " << func_ptr_to_string(ns) << "; parent: " << func_ptr_to_string(ns->parent) << "; type: " << to_string(ns->type) << "; extent: " << ns->extent.to_string() << std::endl; switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); for (const node_store& ns_child : dir->children) func_descend(&ns_child, level+1); break; } case node_type::value: // Do nothing. break; default: throw integrity_error("Unexpected node type!"); } }; func_descend(&m_root, 0); return os.str(); } template std::string rtree<_Key,_Value,_Trait>::export_tree_extent_as_obj() const { if (trait_type::dimensions != 2u) throw size_error("Only 2-dimensional trees are supported."); float unit_height = ((m_root.extent.end.d[0] - m_root.extent.start.d[0]) + (m_root.extent.end.d[1] - m_root.extent.start.d[1])) / 5.0f; // Calculate the width to use for point data. float pt_width = std::min( m_root.extent.end.d[0] - m_root.extent.start.d[0], m_root.extent.end.d[1] - m_root.extent.start.d[1]); pt_width /= 400.0f; pt_width = std::min(pt_width, 1.0f); std::ostringstream os; size_t counter = 0; std::function func_descend = [&](const node_store* ns, int level) { size_t offset = counter * 4; point_type s = ns->extent.start; point_type e = ns->extent.end; if (s == e) { s.d[0] -= pt_width; s.d[1] -= pt_width; e.d[0] += pt_width; e.d[1] += pt_width; } os << "o extent " << counter << " (level " << level << ") " << s.to_string() << " - " << e.to_string() << std::endl; os << "v " << s.d[0] << ' ' << (level*unit_height) << ' ' << s.d[1] << std::endl; os << "v " << s.d[0] << ' ' << (level*unit_height) << ' ' << e.d[1] << std::endl; os << "v " << e.d[0] << ' ' << (level*unit_height) << ' ' << e.d[1] << std::endl; os << "v " << e.d[0] << ' ' << (level*unit_height) << ' ' << s.d[1] << std::endl; os << "f " << (offset+1) << ' ' << (offset+2) << ' ' << (offset+3) << ' ' << (offset+4) << std::endl; ++counter; switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); for (const node_store& ns_child : dir->children) func_descend(&ns_child, level+1); break; } case node_type::value: // Do nothing. break; default: throw integrity_error("Unexpected node type!"); } }; func_descend(&m_root, 0); return os.str(); } template std::string rtree<_Key,_Value,_Trait>::export_tree_extent_as_svg() const { if (trait_type::dimensions != 2u) throw size_error("Only 2-dimensional trees are supported."); constexpr float min_avg_root_length = 800.0f; constexpr float max_avg_root_length = 1000.0f; float root_w = m_root.extent.end.d[0] - m_root.extent.start.d[0]; float root_h = m_root.extent.end.d[1] - m_root.extent.start.d[1]; // Adjust zooming for optimal output size. We don't want it to be too // large or too small. float zoom_ratio = 1.0; float root_avg = (root_w + root_h) / 2.0f; if (root_avg >= max_avg_root_length) zoom_ratio = max_avg_root_length / root_avg; if (root_avg <= min_avg_root_length) zoom_ratio = min_avg_root_length / root_avg; root_w *= zoom_ratio; root_h *= zoom_ratio; float root_x = m_root.extent.start.d[0] * zoom_ratio; float root_y = m_root.extent.start.d[1] * zoom_ratio; float r = root_avg / 100.0f * zoom_ratio; float stroke_w = r / 10.0f; // stroke width const std::string indent = " "; // Uniform attributes to use for all drawing objects. std::string attrs_dir; { std::ostringstream os; os << " stroke=\"#999999\" stroke-width=\"" << stroke_w << "\" fill=\"green\" fill-opacity=\"0.05\""; attrs_dir = os.str(); } std::string attrs_value; { std::ostringstream os; os << " stroke=\"brown\" stroke-width=\"" << stroke_w << "\" fill=\"brown\" fill-opacity=\"0.2\""; attrs_value = os.str(); } std::ostringstream os; os << "\n"; os << "\n"; std::function func_descend = [&](const node_store* ns, int level) { const extent_type& ext = ns->extent; float w = ext.end.d[0] - ext.start.d[0]; float h = ext.end.d[1] - ext.start.d[1]; float x = ext.start.d[0]; float y = ext.start.d[1]; w *= zoom_ratio; h *= zoom_ratio; x *= zoom_ratio; y *= zoom_ratio; x -= root_x; y -= root_y; if (level > 0) { const char* attrs = (ns->type == node_type::value) ? attrs_value.data() : attrs_dir.data(); if (ext.is_point()) { os << indent << "\n"; } else { os << indent << "\n"; } } switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); for (const node_store& ns_child : dir->children) func_descend(&ns_child, level+1); break; } case node_type::value: // Do nothing. break; default: throw integrity_error("Unexpected node type!"); } }; os << "\n"; func_descend(&m_root, 0); os << ""; return os.str(); } template void rtree<_Key,_Value,_Trait>::split_node(node_store* ns) { directory_node* dir = ns->get_directory_node(); assert(dir); assert(ns->count == trait_type::max_node_size+1); dir_store_type& children = dir->children; sort_dir_store_by_split_dimension(children); size_t dist = pick_optimal_distribution(children); distribution dist_picked(dist, children); // Move the child nodes in group 2 into a brand-new sibling node. node_store node_g2 = node_store::create_leaf_directory_node(); node_g2.type = ns->type; directory_node* dir_sibling = static_cast(node_g2.node_ptr); for (auto it = dist_picked.g2.begin; it != dist_picked.g2.end; ++it) { assert(!it->valid_pointer); dir_sibling->children.push_back(std::move(*it)); } node_g2.count = dir_sibling->children.size(); node_g2.pack(); // Remove the nodes in group 2 from the original node by shrinking the node store. ns->count = dist_picked.g1.size; assert(dist_picked.g1.size < dir->children.size()); dir->children.resize(dist_picked.g1.size); ns->pack(); // Re-calculate the bounding box. if (ns->is_root()) { // Create a new root node and make it the parent of the original root // and the new sibling nodes. assert(ns == &m_root); node_store node_g1 = node_store::create_nonleaf_directory_node(); m_root.swap(node_g1); node_g1.parent = &m_root; node_g2.parent = &m_root; directory_node* dir_root = static_cast(m_root.node_ptr); dir_root->children.emplace_back(std::move(node_g1)); dir_root->children.emplace_back(std::move(node_g2)); m_root.count = 2; m_root.pack(); for (node_store& ns_child : dir_root->children) ns_child.reset_parent_pointers_of_children(); } else { // Place the new sibling (node_g2) under the same parent as ns. assert(ns->parent); node_g2.parent = ns->parent; node_store* ns_parent = ns->parent; assert(ns_parent->type == node_type::directory_nonleaf); directory_node* dir_parent = static_cast(ns_parent->node_ptr); dir_parent->children.emplace_back(std::move(node_g2)); ++ns_parent->count; bool parent_size_changed = ns_parent->pack(); // Update the parent pointer of the children _after_ the group 2 node // has been inserted into the buffer, as the pointer value of the node // changes after the insertion. ns->reset_parent_pointers(); dir_parent->children.back().reset_parent_pointers_of_children(); if (ns_parent->count > trait_type::max_node_size) // The parent node is overfilled. Split it and keep working upward. split_node(ns_parent); else if (parent_size_changed) // The extent of the parent node has changed. Propagate the change upward. ns_parent->pack_upward(); } } template void rtree<_Key,_Value,_Trait>::perform_forced_reinsertion( node_store* ns, std::unordered_set& reinserted_depth) { assert(ns->count == trait_type::max_node_size+1); // Compute the distance between the centers of the value extents and the // center of the extent of the parent directory. point_type center_of_dir = detail::rtree::get_center_point(ns->extent); directory_node* dir = ns->get_directory_node(); assert(dir); using buckets_type = std::vector>; buckets_type buckets; buckets.reserve(ns->count); size_t pos = 0; for (const node_store& ns_child : dir->children) { buckets.emplace_back(); buckets.back().src_pos = pos++; point_type center_of_child = detail::rtree::get_center_point(ns_child.extent); buckets.back().distance = detail::rtree::calc_square_distance(center_of_dir, center_of_child); } // Sort the value entries in decreasing order of their distances. std::sort(buckets.begin(), buckets.end(), [](const typename buckets_type::value_type& left, const typename buckets_type::value_type& right) -> bool { return left.distance < right.distance; } ); assert(trait_type::reinsertion_size < buckets.size()); // Remove the first set of entries from the parent directory. std::deque nodes_to_reinsert(trait_type::reinsertion_size); for (size_t i = 0; i < trait_type::reinsertion_size; ++i) { size_t this_pos = buckets[i].src_pos; dir->children[this_pos].swap(nodes_to_reinsert[i]); } // Erase the swapped out nodes from the directory. auto it = std::remove_if(dir->children.begin(), dir->children.end(), [](const node_store& this_ns) -> bool { return this_ns.type == node_type::unspecified; } ); dir->children.erase(it, dir->children.end()); ns->count -= nodes_to_reinsert.size(); assert(ns->count == dir->children.size()); // No need to invalidate pointers since they are all value nodes. if (ns->pack()) ns->pack_upward(); // Re-insert the values from the closest to farthest. while (!nodes_to_reinsert.empty()) { node_store ns_to_reinsert(std::move(nodes_to_reinsert.front())); nodes_to_reinsert.pop_front(); insert(std::move(ns_to_reinsert), &reinserted_depth); } } template void rtree<_Key,_Value,_Trait>::sort_dir_store_by_split_dimension(dir_store_type& children) { // Store the sum of margins for each dimension axis. detail::rtree::min_value_pos min_margin_dim; for (size_t dim = 0; dim < trait_type::dimensions; ++dim) { // Sort the entries by the lower then by the upper value of their // bounding boxes. This invalidates the pointers of the child nodes. sort_dir_store_by_dimension(dim, children); key_type sum_of_margins = key_type(); // it's actually the sum of half margins. for (size_t dist = 1; dist <= max_dist_size; ++dist) { // The first group contains m-1+dist entries, while the second // group contains the rest. auto it = children.begin(); auto it_end = it; std::advance(it_end, trait_type::min_node_size - 1 + dist); extent_type bb1 = detail::rtree::calc_extent(it, it_end); it = it_end; it_end = children.end(); assert(it != it_end); extent_type bb2 = detail::rtree::calc_extent(it, it_end); // Compute the half margins of the first and second groups. key_type margin1 = detail::rtree::calc_half_margin(bb1); key_type margin2 = detail::rtree::calc_half_margin(bb2); key_type margins = margin1 + margin2; sum_of_margins += margins; } min_margin_dim.assign(sum_of_margins, dim); } // Pick the dimension axis with the lowest sum of margins. size_t min_dim = min_margin_dim.pos; sort_dir_store_by_dimension(min_dim, children); } template void rtree<_Key,_Value,_Trait>::sort_dir_store_by_dimension(size_t dim, dir_store_type& children) { std::sort(children.begin(), children.end(), [dim](const node_store& a, const node_store& b) -> bool { if (a.extent.start.d[dim] != b.extent.start.d[dim]) return a.extent.start.d[dim] < b.extent.start.d[dim]; return a.extent.end.d[dim] < b.extent.end.d[dim]; } ); for (node_store& ns : children) ns.valid_pointer = false; } template size_t rtree<_Key,_Value,_Trait>::pick_optimal_distribution(dir_store_type& children) const { // Along the chosen dimension axis, pick the distribution with the minimum // overlap value. detail::rtree::min_value_pos min_overlap_dist; for (size_t dist = 1; dist <= max_dist_size; ++dist) { // The first group contains m-1+dist entries, while the second // group contains the rest. distribution dist_data(dist, children); extent_type bb1 = detail::rtree::calc_extent(dist_data.g1.begin, dist_data.g1.end); extent_type bb2 = detail::rtree::calc_extent(dist_data.g2.begin, dist_data.g2.end); key_type overlap = detail::rtree::calc_intersection(bb1, bb2); min_overlap_dist.assign(overlap, dist); } return min_overlap_dist.pos; } template typename rtree<_Key,_Value,_Trait>::insertion_point rtree<_Key,_Value,_Trait>::find_leaf_directory_node_for_insertion(const extent_type& bb) { insertion_point ret; ret.ns = &m_root; for (size_t i = 0; i <= trait_type::max_tree_depth; ++i) { if (ret.ns->type == node_type::directory_leaf) { ret.depth = i; return ret; } assert(ret.ns->type == node_type::directory_nonleaf); directory_node* dir = static_cast(ret.ns->node_ptr); // If this non-leaf directory contains at least one leaf directory, // pick the entry with minimum overlap increase. If all of its child // nodes are non-leaf directories, then pick the entry with minimum // area enlargement. if (dir->has_leaf_directory()) ret.ns = dir->get_child_with_minimal_overlap(bb); else ret.ns = dir->get_child_with_minimal_area_enlargement(bb); } throw std::runtime_error("Maximum tree depth has been reached."); } template typename rtree<_Key,_Value,_Trait>::node_store* rtree<_Key,_Value,_Trait>::find_nonleaf_directory_node_for_insertion( const extent_type& bb, size_t max_depth) { node_store* dst = &m_root; for (size_t i = 0; i <= trait_type::max_tree_depth; ++i) { assert(dst->is_directory()); if (!dst->count) // This node has no children. return dst; assert(dst->type == node_type::directory_nonleaf); if (i == max_depth) return dst; directory_node* dir = static_cast(dst->node_ptr); if (dir->has_leaf_directory()) return dst; assert(dst->type == node_type::directory_nonleaf); dst = dir->get_child_with_minimal_area_enlargement(bb); assert(dst); } throw std::runtime_error("Maximum tree depth has been reached."); } template template void rtree<_Key,_Value,_Trait>::descend_with_func(_Func func) const { std::function func_descend = [&](const node_store* ns) { node_properties np; np.type = ns->type; np.extent = ns->extent; func(np); switch (ns->type) { case node_type::directory_leaf: case node_type::directory_nonleaf: { const directory_node* dir = static_cast(ns->node_ptr); for (const node_store& ns_child : dir->children) func_descend(&ns_child); break; } case node_type::value: // Do nothing. break; default: assert(!"The tree should not contain node of this type!"); } }; func_descend(&m_root); } template template void rtree<_Key,_Value,_Trait>::search_descend( size_t depth, const search_condition_type& dir_cond, const search_condition_type& value_cond, typename _ResT::node_store_type& ns, _ResT& results) const { switch (ns.type) { case node_type::directory_nonleaf: case node_type::directory_leaf: { if (!dir_cond(ns)) return; auto* dir_node = ns.get_directory_node(); for (auto& child : dir_node->children) search_descend(depth+1, dir_cond, value_cond, child, results); break; } case node_type::value: { if (!value_cond(ns)) return; results.add_node_store(&ns, depth); break; } case node_type::unspecified: throw std::runtime_error("unspecified node type."); } } template void rtree<_Key,_Value,_Trait>::shrink_tree_upward(node_store* ns, const extent_type& bb_affected) { if (!ns) return; // Check if the affected bounding box is at a corner. if (!ns->extent.contains_at_boundary(bb_affected)) return; extent_type original_bb = ns->extent; // Store the original bounding box before the packing. bool updated = ns->pack(); if (!updated) // The extent hasn't changed. There is no point going upward. return; shrink_tree_upward(ns->parent, original_bb); } } // namespace mdds /* vim:set shiftwidth=4 softtabstop=4 expandtab: */