]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/geometry/index/detail/rtree/pack_create.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / geometry / index / detail / rtree / pack_create.hpp
CommitLineData
7c673cae
FG
1// Boost.Geometry Index
2//
3// R-tree initial packing
4//
b32b8144 5// Copyright (c) 2011-2017 Adam Wulkiewicz, Lodz, Poland.
20effc67 6// Copyright (c) 2020 Caian Benedicto, Campinas, Brazil.
7c673cae 7//
92f5a8d4
TL
8// This file was modified by Oracle on 2019.
9// Modifications copyright (c) 2019 Oracle and/or its affiliates.
10// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
11//
7c673cae
FG
12// Use, modification and distribution is subject to the Boost Software License,
13// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
14// http://www.boost.org/LICENSE_1_0.txt)
15
16#ifndef BOOST_GEOMETRY_INDEX_DETAIL_RTREE_PACK_CREATE_HPP
17#define BOOST_GEOMETRY_INDEX_DETAIL_RTREE_PACK_CREATE_HPP
18
92f5a8d4
TL
19#include <boost/core/ignore_unused.hpp>
20
7c673cae
FG
21#include <boost/geometry/algorithms/expand.hpp>
22#include <boost/geometry/index/detail/algorithms/bounds.hpp>
b32b8144 23#include <boost/geometry/index/detail/algorithms/nth_element.hpp>
f67539c2 24#include <boost/geometry/index/detail/rtree/node/subtree_destroyer.hpp>
7c673cae
FG
25
26#include <boost/geometry/algorithms/detail/expand_by_epsilon.hpp>
27
28namespace boost { namespace geometry { namespace index { namespace detail { namespace rtree {
29
30namespace pack_utils {
31
32template <std::size_t Dimension>
33struct biggest_edge
34{
35 BOOST_STATIC_ASSERT(0 < Dimension);
36 template <typename Box>
37 static inline void apply(Box const& box, typename coordinate_type<Box>::type & length, std::size_t & dim_index)
38 {
39 biggest_edge<Dimension-1>::apply(box, length, dim_index);
40 typename coordinate_type<Box>::type curr
41 = geometry::get<max_corner, Dimension-1>(box) - geometry::get<min_corner, Dimension-1>(box);
42 if ( length < curr )
43 {
44 dim_index = Dimension - 1;
45 length = curr;
46 }
47 }
48};
49
50template <>
51struct biggest_edge<1>
52{
53 template <typename Box>
54 static inline void apply(Box const& box, typename coordinate_type<Box>::type & length, std::size_t & dim_index)
55 {
56 dim_index = 0;
57 length = geometry::get<max_corner, 0>(box) - geometry::get<min_corner, 0>(box);
58 }
59};
60
61template <std::size_t I>
62struct point_entries_comparer
63{
64 template <typename PointEntry>
65 bool operator()(PointEntry const& e1, PointEntry const& e2) const
66 {
67 return geometry::get<I>(e1.first) < geometry::get<I>(e2.first);
68 }
69};
70
71template <std::size_t I, std::size_t Dimension>
72struct nth_element_and_half_boxes
73{
74 template <typename EIt, typename Box>
75 static inline void apply(EIt first, EIt median, EIt last, Box const& box, Box & left, Box & right, std::size_t dim_index)
76 {
77 if ( I == dim_index )
78 {
b32b8144 79 index::detail::nth_element(first, median, last, point_entries_comparer<I>());
7c673cae
FG
80
81 geometry::convert(box, left);
82 geometry::convert(box, right);
83 typename coordinate_type<Box>::type edge_len
84 = geometry::get<max_corner, I>(box) - geometry::get<min_corner, I>(box);
85 typename coordinate_type<Box>::type median
86 = geometry::get<min_corner, I>(box) + edge_len / 2;
87 geometry::set<max_corner, I>(left, median);
88 geometry::set<min_corner, I>(right, median);
89 }
90 else
91 nth_element_and_half_boxes<I+1, Dimension>::apply(first, median, last, box, left, right, dim_index);
92 }
93};
94
95template <std::size_t Dimension>
96struct nth_element_and_half_boxes<Dimension, Dimension>
97{
98 template <typename EIt, typename Box>
99 static inline void apply(EIt , EIt , EIt , Box const& , Box & , Box & , std::size_t ) {}
100};
101
102} // namespace pack_utils
103
104// STR leafs number are calculated as rcount/max
105// and the number of splitting planes for each dimension as (count/max)^(1/dimension)
106// <-> for dimension==2 -> sqrt(count/max)
107//
108// The main flaw of this algorithm is that the resulting tree will have bad structure for:
109// 1. non-uniformly distributed elements
110// Statistic check could be performed, e.g. based on variance of lengths of elements edges for each dimension
111// 2. elements distributed mainly along one axis
112// Calculate bounding box of all elements and then number of dividing planes for a dimension
113// from the length of BB edge for this dimension (more or less assuming that elements are uniformly-distributed squares)
114//
115// Another thing is that the last node may have less elements than Max or even Min.
116// The number of splitting planes must be chosen more carefully than count/max
117//
118// This algorithm is something between STR and TGS
119// it is more similar to the top-down recursive kd-tree creation algorithm
120// using object median split and split axis of greatest BB edge
121// BB is only used as a hint (assuming objects are distributed uniformly)
122//
123// Implemented algorithm guarantees that the number of elements in nodes will be between Min and Max
124// and that nodes are packed as tightly as possible
125// e.g. for 177 values Max = 5 and Min = 2 it will construct the following tree:
126// ROOT 177
127// L1 125 52
128// L2 25 25 25 25 25 25 17 10
129// L3 5x5 5x5 5x5 5x5 5x5 5x5 3x5+2 2x5
130
f67539c2 131template <typename MembersHolder>
7c673cae
FG
132class pack
133{
f67539c2
TL
134 typedef typename MembersHolder::node node;
135 typedef typename MembersHolder::internal_node internal_node;
136 typedef typename MembersHolder::leaf leaf;
137
138 typedef typename MembersHolder::node_pointer node_pointer;
139 typedef typename MembersHolder::size_type size_type;
140 typedef typename MembersHolder::parameters_type parameters_type;
141 typedef typename MembersHolder::translator_type translator_type;
142 typedef typename MembersHolder::allocators_type allocators_type;
143
144 typedef typename MembersHolder::box_type box_type;
145 typedef typename geometry::point_type<box_type>::type point_type;
7c673cae 146 typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
f67539c2 147 typedef typename detail::default_content_result<box_type>::type content_type;
92f5a8d4 148 typedef typename detail::strategy_type<parameters_type>::type strategy_type;
7c673cae
FG
149 static const std::size_t dimension = geometry::dimension<point_type>::value;
150
151 typedef typename rtree::container_from_elements_type<
152 typename rtree::elements_type<leaf>::type,
f67539c2 153 size_type
7c673cae
FG
154 >::type values_counts_container;
155
156 typedef typename rtree::elements_type<internal_node>::type internal_elements;
157 typedef typename internal_elements::value_type internal_element;
158
f67539c2
TL
159 typedef rtree::subtree_destroyer<MembersHolder> subtree_destroyer;
160
7c673cae
FG
161public:
162 // Arbitrary iterators
163 template <typename InIt> inline static
f67539c2
TL
164 node_pointer apply(InIt first, InIt last,
165 size_type & values_count,
166 size_type & leafs_level,
167 parameters_type const& parameters,
168 translator_type const& translator,
169 allocators_type & allocators)
20effc67
TL
170 {
171 return apply(first, last, values_count, leafs_level, parameters, translator,
172 allocators, boost::container::new_allocator<void>());
173 }
174
175 template <typename InIt, typename TmpAlloc> inline static
176 node_pointer apply(InIt first, InIt last,
177 size_type & values_count,
178 size_type & leafs_level,
179 parameters_type const& parameters,
180 translator_type const& translator,
181 allocators_type & allocators,
182 TmpAlloc const& temp_allocator)
7c673cae
FG
183 {
184 typedef typename std::iterator_traits<InIt>::difference_type diff_type;
185
186 diff_type diff = std::distance(first, last);
187 if ( diff <= 0 )
188 return node_pointer(0);
189
190 typedef std::pair<point_type, InIt> entry_type;
20effc67
TL
191 typedef typename boost::container::allocator_traits<TmpAlloc>::
192 template rebind_alloc<entry_type> temp_entry_allocator_type;
193
194 temp_entry_allocator_type temp_entry_allocator(temp_allocator);
195 boost::container::vector<entry_type, temp_entry_allocator_type> entries(temp_entry_allocator);
7c673cae
FG
196
197 values_count = static_cast<size_type>(diff);
198 entries.reserve(values_count);
199
f67539c2 200 expandable_box<box_type, strategy_type> hint_box(detail::get_strategy(parameters));
7c673cae
FG
201 for ( ; first != last ; ++first )
202 {
203 // NOTE: support for iterators not returning true references adapted
204 // to Geometry concept and default translator returning true reference
205 // An alternative would be to dereference the iterator and translate
206 // in one expression each time the indexable was needed.
207 typename std::iterator_traits<InIt>::reference in_ref = *first;
f67539c2 208 typename translator_type::result_type indexable = translator(in_ref);
7c673cae
FG
209
210 // NOTE: added for consistency with insert()
211 // CONSIDER: alternative - ignore invalid indexable or throw an exception
212 BOOST_GEOMETRY_INDEX_ASSERT(detail::is_valid(indexable), "Indexable is invalid");
213
214 hint_box.expand(indexable);
215
216 point_type pt;
217 geometry::centroid(indexable, pt);
218 entries.push_back(std::make_pair(pt, first));
219 }
220
221 subtree_elements_counts subtree_counts = calculate_subtree_elements_counts(values_count, parameters, leafs_level);
222 internal_element el = per_level(entries.begin(), entries.end(), hint_box.get(), values_count, subtree_counts,
223 parameters, translator, allocators);
224
225 return el.second;
226 }
227
228private:
92f5a8d4 229 template <typename BoxType, typename Strategy>
7c673cae
FG
230 class expandable_box
231 {
232 public:
92f5a8d4
TL
233 explicit expandable_box(Strategy const& strategy)
234 : m_strategy(strategy), m_initialized(false)
7c673cae
FG
235 {}
236
237 template <typename Indexable>
92f5a8d4
TL
238 explicit expandable_box(Indexable const& indexable, Strategy const& strategy)
239 : m_strategy(strategy), m_initialized(true)
7c673cae 240 {
92f5a8d4 241 detail::bounds(indexable, m_box, m_strategy);
7c673cae
FG
242 }
243
244 template <typename Indexable>
245 void expand(Indexable const& indexable)
246 {
247 if ( !m_initialized )
248 {
249 // it's guaranteed that the Box will be initialized
250 // only for Points, Boxes and Segments but that's ok
251 // since only those Geometries can be stored
92f5a8d4 252 detail::bounds(indexable, m_box, m_strategy);
7c673cae
FG
253 m_initialized = true;
254 }
255 else
256 {
92f5a8d4 257 detail::expand(m_box, indexable, m_strategy);
7c673cae
FG
258 }
259 }
260
261 void expand_by_epsilon()
262 {
263 geometry::detail::expand_by_epsilon(m_box);
264 }
265
266 BoxType const& get() const
267 {
268 BOOST_GEOMETRY_INDEX_ASSERT(m_initialized, "uninitialized envelope accessed");
269 return m_box;
270 }
271
272 private:
7c673cae 273 BoxType m_box;
92f5a8d4
TL
274 Strategy m_strategy;
275 bool m_initialized;
7c673cae
FG
276 };
277
278 struct subtree_elements_counts
279 {
f67539c2
TL
280 subtree_elements_counts(size_type ma, size_type mi) : maxc(ma), minc(mi) {}
281 size_type maxc;
282 size_type minc;
7c673cae
FG
283 };
284
285 template <typename EIt> inline static
f67539c2
TL
286 internal_element per_level(EIt first, EIt last,
287 box_type const& hint_box,
288 size_type values_count,
289 subtree_elements_counts const& subtree_counts,
290 parameters_type const& parameters,
291 translator_type const& translator,
292 allocators_type & allocators)
7c673cae 293 {
f67539c2 294 BOOST_GEOMETRY_INDEX_ASSERT(0 < std::distance(first, last) && static_cast<size_type>(std::distance(first, last)) == values_count,
7c673cae
FG
295 "unexpected parameters");
296
297 if ( subtree_counts.maxc <= 1 )
298 {
299 // ROOT or LEAF
300 BOOST_GEOMETRY_INDEX_ASSERT(values_count <= parameters.get_max_elements(),
301 "too big number of elements");
302 // if !root check m_parameters.get_min_elements() <= count
303
304 // create new leaf node
f67539c2 305 node_pointer n = rtree::create_node<allocators_type, leaf>::apply(allocators); // MAY THROW (A)
7c673cae
FG
306 subtree_destroyer auto_remover(n, allocators);
307 leaf & l = rtree::get<leaf>(*n);
308
309 // reserve space for values
310 rtree::elements(l).reserve(values_count); // MAY THROW (A)
311
312 // calculate values box and copy values
313 // initialize the box explicitly to avoid GCC-4.4 uninitialized variable warnings with O2
f67539c2
TL
314 expandable_box<box_type, strategy_type> elements_box(translator(*(first->second)),
315 detail::get_strategy(parameters));
7c673cae
FG
316 rtree::elements(l).push_back(*(first->second)); // MAY THROW (A?,C)
317 for ( ++first ; first != last ; ++first )
318 {
319 // NOTE: push_back() must be called at the end in order to support move_iterator.
320 // The iterator is dereferenced 2x (no temporary reference) to support
321 // non-true reference types and move_iterator without boost::forward<>.
322 elements_box.expand(translator(*(first->second)));
323 rtree::elements(l).push_back(*(first->second)); // MAY THROW (A?,C)
324 }
325
326#ifdef BOOST_GEOMETRY_INDEX_EXPERIMENTAL_ENLARGE_BY_EPSILON
327 // Enlarge bounds of a leaf node.
328 // It's because Points and Segments are compared WRT machine epsilon
329 // This ensures that leafs bounds correspond to the stored elements
330 // NOTE: this is done only if the Indexable is a different kind of Geometry
331 // than the bounds (only Box for now). Spatial predicates are checked
332 // the same way for Geometry of the same kind.
333 if ( BOOST_GEOMETRY_CONDITION((
334 ! index::detail::is_bounding_geometry
335 <
f67539c2 336 typename indexable_type<translator_type>::type
7c673cae
FG
337 >::value )) )
338 {
339 elements_box.expand_by_epsilon();
340 }
341#endif
342
343 auto_remover.release();
344 return internal_element(elements_box.get(), n);
345 }
346
347 // calculate next max and min subtree counts
348 subtree_elements_counts next_subtree_counts = subtree_counts;
349 next_subtree_counts.maxc /= parameters.get_max_elements();
350 next_subtree_counts.minc /= parameters.get_max_elements();
351
352 // create new internal node
f67539c2 353 node_pointer n = rtree::create_node<allocators_type, internal_node>::apply(allocators); // MAY THROW (A)
7c673cae
FG
354 subtree_destroyer auto_remover(n, allocators);
355 internal_node & in = rtree::get<internal_node>(*n);
356
357 // reserve space for values
f67539c2 358 size_type nodes_count = calculate_nodes_count(values_count, subtree_counts);
7c673cae
FG
359 rtree::elements(in).reserve(nodes_count); // MAY THROW (A)
360 // calculate values box and copy values
f67539c2 361 expandable_box<box_type, strategy_type> elements_box(detail::get_strategy(parameters));
7c673cae
FG
362
363 per_level_packets(first, last, hint_box, values_count, subtree_counts, next_subtree_counts,
364 rtree::elements(in), elements_box,
365 parameters, translator, allocators);
366
367 auto_remover.release();
368 return internal_element(elements_box.get(), n);
369 }
370
371 template <typename EIt, typename ExpandableBox> inline static
f67539c2
TL
372 void per_level_packets(EIt first, EIt last,
373 box_type const& hint_box,
374 size_type values_count,
7c673cae
FG
375 subtree_elements_counts const& subtree_counts,
376 subtree_elements_counts const& next_subtree_counts,
f67539c2
TL
377 internal_elements & elements,
378 ExpandableBox & elements_box,
379 parameters_type const& parameters,
380 translator_type const& translator,
381 allocators_type & allocators)
7c673cae 382 {
f67539c2 383 BOOST_GEOMETRY_INDEX_ASSERT(0 < std::distance(first, last) && static_cast<size_type>(std::distance(first, last)) == values_count,
7c673cae
FG
384 "unexpected parameters");
385
386 BOOST_GEOMETRY_INDEX_ASSERT(subtree_counts.minc <= values_count,
387 "too small number of elements");
388
389 // only one packet
390 if ( values_count <= subtree_counts.maxc )
391 {
392 // the end, move to the next level
393 internal_element el = per_level(first, last, hint_box, values_count, next_subtree_counts,
394 parameters, translator, allocators);
395
396 // in case if push_back() do throw here
397 // and even if this is not probable (previously reserved memory, nonthrowing pairs copy)
398 // this case is also tested by exceptions test.
399 subtree_destroyer auto_remover(el.second, allocators);
400 // this container should have memory allocated, reserve() called outside
401 elements.push_back(el); // MAY THROW (A?,C) - however in normal conditions shouldn't
402 auto_remover.release();
403
404 elements_box.expand(el.first);
405 return;
406 }
407
f67539c2 408 size_type median_count = calculate_median_count(values_count, subtree_counts);
7c673cae
FG
409 EIt median = first + median_count;
410
411 coordinate_type greatest_length;
412 std::size_t greatest_dim_index = 0;
413 pack_utils::biggest_edge<dimension>::apply(hint_box, greatest_length, greatest_dim_index);
f67539c2 414 box_type left, right;
7c673cae
FG
415 pack_utils::nth_element_and_half_boxes<0, dimension>
416 ::apply(first, median, last, hint_box, left, right, greatest_dim_index);
417
418 per_level_packets(first, median, left,
419 median_count, subtree_counts, next_subtree_counts,
420 elements, elements_box,
421 parameters, translator, allocators);
422 per_level_packets(median, last, right,
423 values_count - median_count, subtree_counts, next_subtree_counts,
424 elements, elements_box,
425 parameters, translator, allocators);
426 }
427
428 inline static
f67539c2 429 subtree_elements_counts calculate_subtree_elements_counts(size_type elements_count, parameters_type const& parameters, size_type & leafs_level)
7c673cae 430 {
92f5a8d4 431 boost::ignore_unused(parameters);
7c673cae
FG
432
433 subtree_elements_counts res(1, 1);
434 leafs_level = 0;
435
f67539c2 436 size_type smax = parameters.get_max_elements();
7c673cae
FG
437 for ( ; smax < elements_count ; smax *= parameters.get_max_elements(), ++leafs_level )
438 res.maxc = smax;
439
440 res.minc = parameters.get_min_elements() * (res.maxc / parameters.get_max_elements());
441
442 return res;
443 }
444
445 inline static
f67539c2
TL
446 size_type calculate_nodes_count(size_type count,
447 subtree_elements_counts const& subtree_counts)
7c673cae 448 {
f67539c2
TL
449 size_type n = count / subtree_counts.maxc;
450 size_type r = count % subtree_counts.maxc;
7c673cae
FG
451
452 if ( 0 < r && r < subtree_counts.minc )
453 {
f67539c2 454 size_type count_minus_min = count - subtree_counts.minc;
7c673cae
FG
455 n = count_minus_min / subtree_counts.maxc;
456 r = count_minus_min % subtree_counts.maxc;
457 ++n;
458 }
459
460 if ( 0 < r )
461 ++n;
462
463 return n;
464 }
465
466 inline static
f67539c2
TL
467 size_type calculate_median_count(size_type count,
468 subtree_elements_counts const& subtree_counts)
7c673cae
FG
469 {
470 // e.g. for max = 5, min = 2, count = 52, subtree_max = 25, subtree_min = 10
471
f67539c2
TL
472 size_type n = count / subtree_counts.maxc; // e.g. 52 / 25 = 2
473 size_type r = count % subtree_counts.maxc; // e.g. 52 % 25 = 2
474 size_type median_count = (n / 2) * subtree_counts.maxc; // e.g. 2 / 2 * 25 = 25
7c673cae
FG
475
476 if ( 0 != r ) // e.g. 0 != 2
477 {
478 if ( subtree_counts.minc <= r ) // e.g. 10 <= 2 == false
479 {
480 //BOOST_GEOMETRY_INDEX_ASSERT(0 < n, "unexpected value");
481 median_count = ((n+1)/2) * subtree_counts.maxc; // if calculated ((2+1)/2) * 25 which would be ok, but not in all cases
482 }
483 else // r < subtree_counts.second // e.g. 2 < 10 == true
484 {
f67539c2 485 size_type count_minus_min = count - subtree_counts.minc; // e.g. 52 - 10 = 42
7c673cae
FG
486 n = count_minus_min / subtree_counts.maxc; // e.g. 42 / 25 = 1
487 r = count_minus_min % subtree_counts.maxc; // e.g. 42 % 25 = 17
488 if ( r == 0 ) // e.g. false
489 {
490 // n can't be equal to 0 because then there wouldn't be any element in the other node
491 //BOOST_GEOMETRY_INDEX_ASSERT(0 < n, "unexpected value");
492 median_count = ((n+1)/2) * subtree_counts.maxc; // if calculated ((1+1)/2) * 25 which would be ok, but not in all cases
493 }
494 else
495 {
496 if ( n == 0 ) // e.g. false
497 median_count = r; // if calculated -> 17 which is wrong!
498 else
499 median_count = ((n+2)/2) * subtree_counts.maxc; // e.g. ((1+2)/2) * 25 = 25
500 }
501 }
502 }
503
504 return median_count;
505 }
506};
507
508}}}}} // namespace boost::geometry::index::detail::rtree
509
510#endif // BOOST_GEOMETRY_INDEX_DETAIL_RTREE_PACK_CREATE_HPP