API Documentation
Loading...
Searching...
No Matches
Median.hpp
Go to the documentation of this file.
1/*--------------------------------------------------------------------------------------------
2Copyright (c) 2019, NDEVR LLC
3tyler.parke@ndevr.org
4 __ __ ____ _____ __ __ _______
5 | \ | | | __ \ | ___|\ \ / / | __ \
6 | \ | | | | \ \ | |___ \ \ / / | |__) |
7 | . \| | | |__/ / | |___ \ V / | _ /
8 | |\ |_|_____/__|_____|___\_/____| | \ \
9 |__| \__________________________________| \__\
10
11Subject to the terms of the Enterprise+ Agreement, NDEVR hereby grants
12Licensee a limited, non-exclusive, non-transferable, royalty-free license
13(without the right to sublicense) to use the API solely for the purpose of
14Licensee's internal development efforts to develop applications for which
15the API was provided.
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
23FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
26
27Library: Base
28File: Median
29Included in API: True
30Author(s): Tyler Parke
31 *-----------------------------------------------------------------------------------------**/
32#pragma once
33#include <NDEVR/Buffer.h>
34#include <NDEVR/BinaryHeap.h>
35namespace NDEVR
36{
37 /**--------------------------------------------------------------------------------------------------
38 \brief A dummy class for including optimized median functions
39 **/
40 class Median
41 {};
42 // return the median value in a vector
43 template<class t_container_type>
44 uint04 median(const t_container_type& elements, Buffer<uint04>& indices, const uint04 start, const uint04 end, uint01 dimension)
45 {
46 uint04* middle = indices.begin(start + ((end - start) / 2));
47 std::nth_element(indices.begin(start), middle, indices.begin(end), [&elements, dimension](uint04 i, uint04 j)
48 {
49 return elements[i].center()[dimension] < elements[j].center()[dimension];
50 });
51 return start + ((end - start) / 2);
52 }
53
54 // return the median value in a vector
55 template<class t_node_type, class t_container_type>
56 uint04 dynamicHeapMedian(uint04 size, const Buffer<t_node_type>& points, const t_container_type& indices, const uint04 start, const uint04 step, uint01 dimension)
57 {
58 const sint04 half_heap = cast<sint04>(size / 2);
59 const sint04 max_heap = cast<sint04>(size / 2);
62 sint04 nLeft = 1;
63 sint04 nRight = 1;
64 sint04 index = start;
65 // pick first value as median candidate
66 sint04 median = index;
67 index += step;
68
69 for (;;)
70 {
71 // get next value
72 sint04 val = index;
73 sint04 child = nLeft++, parent = (child - 1) / 2;
74 index += step;
75 // if value is smaller than median, append to left heap
76 if (points[indices[val]][dimension] < points[indices[median]][dimension])
77 {
78 // move biggest value to the heap top
79 left.insert(points[indices[val]][dimension], val);
80
81 // if left heap is full
82 if (left.size() == max_heap)
83 {
84 // for each remaining value
85 for (; nRight != max_heap; nRight++)
86 {
87 // get next value
88 val = index;
89 index += step;
90 // if value is to be inserted in the left heap
91 if (points[indices[val]][dimension] < points[indices[median]][dimension])
92 {
93
94 child = points[indices[left[2]]][dimension] > points[indices[left[1]]][dimension] ? 2 : 1;
95 if (points[indices[val]][dimension] >= points[indices[left[child]]][dimension])
96 median = val;
97 else
98 {
99 median = left[child];
100 parent = child;
101 child = parent * 2 + 1;
102 while (child < max_heap)
103 {
104 if (child < half_heap
105 && points[indices[left[child + 1]]][dimension] > points[indices[left[child]]][dimension])
106 ++child;
107 if (points[indices[val]][dimension] >= points[indices[left[child]]][dimension])
108 break;
109 left[parent] = left[child];
110 parent = child;
111 child = parent * 2 + 1;
112 }
113 left[parent] = val;
114 }
115 }
116 }
117 return median;
118 }
119 }
120
121 // else append to right heap
122 else
123 {
124 // move smallest value to the heap top
125 sint04 child = nRight++, parent = (child - 1) / 2;
126 while (parent && points[indices[val]][dimension] < points[indices[right[parent]]][dimension])
127 {
128 right[child] = right[parent];
129 child = parent;
130 parent = (parent - 1) / 2;
131 }
132 right[child] = val;
133
134 // if right heap is full
135 if (nRight == max_heap)
136 {
137 // for each remaining value
138 for (; nLeft != max_heap; nLeft++)
139 {
140 // get next value
141 val = index;
142 index += step;
143 // if value is to be inserted in the right heap
144 if (points[indices[val]][dimension] > points[indices[median]][dimension])
145 {
146 child = points[indices[right[2]]][dimension] < points[indices[right[1]]][dimension] ? 2 : 1;
147 if (points[indices[val]][dimension] <= points[indices[right[child]]][dimension])
148 median = val;
149 else
150 {
151 median = right[child];
152 parent = child;
153 child = parent * 2 + 1;
154 while (child < max_heap)
155 {
156 if (child < half_heap
157 && points[indices[right[child + 1]]][dimension] < points[indices[right[child]]][dimension])
158 ++child;
159 if (points[indices[val]][dimension] <= points[indices[right[child]]][dimension])
160 break;
161 right[parent] = right[child];
162 parent = child;
163 child = parent * 2 + 1;
164 }
165 right[parent] = val;
166 }
167 }
168 }
169 return median;
170 }
171 }
172 }
173 }
174 /**--------------------------------------------------------------------------------------------------
175 \brief Return the median value in a Buffer of points based on a given dimension.
176 **/
177 template<uint01 t_bucket_size, class t_buffer_type, class t_container_type>
178 uint04 StaticHeapMedian(const t_buffer_type& points, const t_container_type& indices, uint04 start, uint04 step, uint01 dimension)
179 {
180 const uint04 half_heap = t_bucket_size / 2;
181 const uint04 max_heap = t_bucket_size / 2 + 1;
182 uint04 left[t_bucket_size / 2 + 1];
183 uint04 right[t_bucket_size / 2 + 1];
184 sint04 nLeft = 1;
185 sint04 nRight = 1;
186 sint04 index = start;
187 // pick first value as median candidate
188 sint04 median = index;
189 index += step;
190
191 for (;;)
192 {
193 // get next value
194 sint04 val = index;
195 index += step;
196 // if value is smaller than median, append to left heap
197 if (points[indices[val]][dimension] < points[indices[median]][dimension])
198 {
199 // move biggest value to the heap top
200 sint04 child = nLeft++, parent = (child - 1) / 2;
201 while (parent && points[indices[val]][dimension] > points[indices[left[parent]]][dimension])
202 {
203 left[child] = left[parent];
204 child = parent;
205 parent = (parent - 1) / 2;
206 }
207 left[child] = val;
208
209 // if left heap is full
210 if (nLeft == max_heap)
211 {
212 // for each remaining value
213 for (; nRight != max_heap; nRight++)
214 {
215 // get next value
216 val = index;
217 index += step;
218 // if value is to be inserted in the left heap
219 if (points[indices[val]][dimension] < points[indices[median]][dimension])
220 {
221 child = points[indices[left[2]]][dimension] > points[indices[left[1]]][dimension] ? 2 : 1;
222 if (points[indices[val]][dimension] >= points[indices[left[child]]][dimension])
223 median = val;
224 else
225 {
226 median = left[child];
227 parent = child;
228 child = parent * 2 + 1;
229 while (child < max_heap)
230 {
231 if (child < half_heap
232 && points[indices[left[child + 1]]][dimension] > points[indices[left[child]]][dimension])
233 ++child;
234 if (points[indices[val]][dimension] >= points[indices[left[child]]][dimension])
235 break;
236 left[parent] = left[child];
237 parent = child;
238 child = parent * 2 + 1;
239 }
240 left[parent] = val;
241 }
242 }
243 }
244 return median;
245 }
246 }
247
248 // else append to right heap
249 else
250 {
251 // move smallest value to the heap top
252 sint04 child = nRight++, parent = (child - 1) / 2;
253 while (parent && points[indices[val]][dimension] < points[indices[right[parent]]][dimension])
254 {
255 right[child] = right[parent];
256 child = parent;
257 parent = (parent - 1) / 2;
258 }
259 right[child] = val;
260
261 // if right heap is full
262 if (nRight == max_heap)
263 {
264 // for each remaining value
265 for (; nLeft != max_heap; nLeft++)
266 {
267 // get next value
268 val = index;
269 index += step;
270 // if value is to be inserted in the right heap
271 if (points[indices[val]][dimension] > points[indices[median]][dimension])
272 {
273 child = points[indices[right[2]]][dimension] < points[indices[right[1]]][dimension] ? 2 : 1;
274 if (points[indices[val]][dimension] <= points[indices[right[child]]][dimension])
275 median = val;
276 else
277 {
278 median = right[child];
279 parent = child;
280 child = parent * 2 + 1;
281 while (child < max_heap)
282 {
283 if (child < half_heap
284 && points[indices[right[child + 1]]][dimension] < points[indices[right[child]]][dimension])
285 ++child;
286 if (points[indices[val]][dimension] <= points[indices[right[child]]][dimension])
287 break;
288 right[parent] = right[child];
289 parent = child;
290 child = parent * 2 + 1;
291 }
292 right[parent] = val;
293 }
294 }
295 }
296 return median;
297 }
298 }
299 }
300 }
301
302 /**--------------------------------------------------------------------------------------------------
303 \brief Returns the aproximate median, or value that is close to the median, significantly faster than
304 it would take to calculate the true median.
305 **/
306 template<class t_buffer_type>
307 uint04 ApxMedian(const t_buffer_type& elements, const Buffer<uint04>& indices, uint04 start, uint04 end, uint01 dimension)
308 {
309 uint04 skip = (end - start) / 64;
310 if (skip == 0)
311 {
312 if (end - start > 32) return StaticHeapMedian<31>(elements, indices, start, 1, dimension);
313 if (end - start > 16) return StaticHeapMedian<15>(elements, indices, start, 1, dimension);
314 if (end - start > 8) return StaticHeapMedian<7>(elements, indices, start, 1, dimension);
315 if (end - start > 4) return StaticHeapMedian<3>(elements, indices, start, 1, dimension);
316 else
317 return indices[start];
318 }
319 uint04 number_value;
320 if (skip > 64) number_value = 64;
321 else if (skip > 32) number_value = 32;
322 else if (skip > 16) number_value = 16;
323 else if (skip > 8) number_value = 8;
324 else return StaticHeapMedian<63>(elements, indices, start, skip, dimension);
325 uint04 median_locations[64];
326 uint04 median_values[64];
327 for (uint04 i = 0; i < number_value; i++)
328 {
329 uint04 median_loc = StaticHeapMedian<63>(elements, indices, start + i, skip, dimension);
330 median_locations[i] = (indices[median_loc]);
331 median_values[i] = median_loc;
332 }
333 switch (number_value)
334 {
335 case 64: return median_values[StaticHeapMedian<63>(elements, median_locations, 0, 1, dimension)];
336 case 32: return median_values[StaticHeapMedian<31>(elements, median_locations, 0, 1, dimension)];
337 case 16: return median_values[StaticHeapMedian<15>(elements, median_locations, 0, 1, dimension)];
338 case 8: return median_values[StaticHeapMedian<7>(elements, median_locations, 0, 1, dimension)];
339 default: lib_assert(false, "Unknown number_value in roughHeapMedian");
340 }
342 }
343
344 /**--------------------------------------------------------------------------------------------------
345 \brief Sorts all incoming indices within the given range such that the resulting index matrix will
346 have values less than the index first, and greater than the index second.
347 **/
348 template<class t_type, class t_node_type>
349 uint04 SortAboutValue(uint04 value_index, const Buffer<t_node_type>& points, Buffer<uint04>& indices, uint04 start, uint04 end, uint01 dimension)
350 {
351 const t_type value = points[indices[value_index]][dimension];
352
353 std::swap(indices[--end], indices[value_index]);
354 uint04 store = start;
355 for (uint04 idx = start; idx < end; idx++)
356 {
357 if (points[indices[idx]][dimension] <= value)
358 {
359 std::swap(indices[idx], indices[store]);
360 store++;
361 }
362 }
363 std::swap(indices[end], indices[store]);
364 return store;
365 }
366
367 /**--------------------------------------------------------------------------------------------------
368 \brief Returns the aproximate Nth element, or value that is close to the Nth Element, significantly faster than
369 it would take to calculate the true Nth element of the sorted series
370 **/
371 template<class t_type, class t_node_type>
372 uint04 ApxNthElement(const Buffer<t_node_type>& points, Buffer<uint04>& indices, uint04 start, uint04 end, uint01 dimension)
373 {
374 const uint04 median_loc = ApxMedian(points, indices, start, end, dimension);
375 return SortAboutValue<t_type>(median_loc, points, indices, start, end, dimension);
376 }
377
378 /**--------------------------------------------------------------------------------------------------
379 \brief Sorts all incoming indices within the given range such that the resulting index matrix will
380 have values less than the index first, and greater than the index second.
381 **/
382 template<class t_type, class t_buffer_type, class t_bounds_type>
383 uint04 SortAboutValue(uint04 value_index, const t_buffer_type& points, Buffer<uint04>& indices, uint04 start, uint04 end, uint01 dimension, t_bounds_type& bounds_left, t_bounds_type& bounds_right)
384 {
385 const t_type value = points[indices[value_index]].center()[dimension];
386 bounds_right.addToBounds(points[indices[value_index]]);
387
388 std::swap(indices[--end], indices[value_index]);
389 uint04 store = start;
390 for (uint04 idx = start; idx < end; idx++)
391 {
392 auto element = points[indices[idx]];
393 if (element.center()[dimension] == value)
394 {
395 if ((store - start) <= (idx - start) / 2)
396 {
397 bounds_left.addToBounds(element);
398 std::swap(indices[idx], indices[store]);
399 store++;
400 }
401 else
402 {
403 bounds_right.addToBounds(element);
404 }
405 }
406 else if (element.center()[dimension] < value)
407 {
408 bounds_left.addToBounds(element);
409 std::swap(indices[idx], indices[store]);
410 store++;
411 }
412 else
413 {
414 bounds_right.addToBounds(element);
415 }
416 }
417 std::swap(indices[end], indices[store]);
418 return store;
419 }
420};
421
#define lib_assert(expression, message)
Definition LibAssert.h:61
A heap data structure that takes the form of a binary tree which is a common way of implementing prio...
Definition BinaryHeap.hpp:42
void insert(t_comp_type comparison, t_value_type value)
Definition BinaryHeap.hpp:47
uint04 size() const
Definition BinaryHeap.hpp:74
The equivelent of std::vector but with a bit more control. The basic array unit of the library.
Definition Buffer.hpp:56
decltype(auto) begin()
Definition Buffer.hpp:402
A dummy class for including optimized median functions.
Definition Median.hpp:41
Definition ACIColor.h:37
int32_t sint04
-Defines an alias representing a 4 byte, signed integer. -Can represent exact integer values -2147483...
Definition BaseValues.hpp:64
uint04 ApxNthElement(const Buffer< t_node_type > &points, Buffer< uint04 > &indices, uint04 start, uint04 end, uint01 dimension)
Returns the aproximate Nth element, or value that is close to the Nth Element, significantly faster t...
Definition Median.hpp:372
uint8_t uint01
-Defines an alias representing a 1 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:80
uint04 SortAboutValue(uint04 value_index, const Buffer< t_node_type > &points, Buffer< uint04 > &indices, uint04 start, uint04 end, uint01 dimension)
Sorts all incoming indices within the given range such that the resulting index matrix will have valu...
Definition Median.hpp:349
uint04 ApxMedian(const t_buffer_type &elements, const Buffer< uint04 > &indices, uint04 start, uint04 end, uint01 dimension)
Returns the aproximate median, or value that is close to the median, significantly faster than it wou...
Definition Median.hpp:307
uint04 StaticHeapMedian(const t_buffer_type &points, const t_container_type &indices, uint04 start, uint04 step, uint01 dimension)
Return the median value in a Buffer of points based on a given dimension.
Definition Median.hpp:178
uint04 dynamicHeapMedian(uint04 size, const Buffer< t_node_type > &points, const t_container_type &indices, const uint04 start, const uint04 step, uint01 dimension)
Definition Median.hpp:56
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
Definition BaseValues.hpp:96
constexpr t_to cast(const Angle< t_from > &value)
Definition Angle.h:375
uint04 median(const t_container_type &elements, Buffer< uint04 > &indices, const uint04 start, const uint04 end, uint01 dimension)
Definition Median.hpp:44
Defines for a given type (such as sint04, fltp08, UUID, etc) a maximum, minimum, and reserved 'invali...
Definition BaseValues.hpp:233