1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #include "common/test.h"
18 
19 #include "tbb/blocked_range.h"
20 
21 #include <cmath>
22 #include <vector>
23 
24 #include "common/utils_report.h"
25 
26 namespace test_partitioner_utils {
27 
28 struct RangeStatisticData {
29     // denotes the number of range objects
30     size_t m_rangeNum;
31 
32     // store minimal and maximal range sizes (in terms of number of iterations)
33     size_t m_minRangeSize;
34     size_t m_maxRangeSize;
35 
36     bool m_wasMinRangeSizeWritten; // shows whether relevant field was written or not
37 };
38 
39 using tbb::split;
40 using tbb::proportional_split;
41 using tbb::blocked_range;
42 
43 // helper for calculating number of range objects created before balancing phase is started
44 // and for finding maximum and minimum number of iterations among all such ranges
45 // Note: class does not provide exclusive access to members
46 class RangeStatisticCollector {
47 public:
RangeStatisticCollector(RangeStatisticData * statisticData)48     RangeStatisticCollector(RangeStatisticData *statisticData) :
49         m_statData(statisticData)
50     {
51         m_called = false;
52         if (m_statData)
53             m_statData->m_rangeNum = 1;
54     }
55 
56     // constructor is called from non-proportional split constructor of derived Range
RangeStatisticCollector(RangeStatisticCollector & sc,size_t rangeSize)57     RangeStatisticCollector(RangeStatisticCollector& sc, size_t rangeSize) {
58         if (!sc.m_called) {
59             // this is the first time non-proportional split constructor is called
60             // it means that work distribution phase has been completed and
61             // work balancing phase has been just started
62             sc.m_called = true;
63 
64             if (sc.m_statData) {
65                 size_t *minRangeSize = &sc.m_statData->m_minRangeSize;
66                 if (*minRangeSize > rangeSize || !sc.m_statData->m_wasMinRangeSizeWritten) { // if minimum is not an actual minimum
67                     *minRangeSize = rangeSize;
68                     sc.m_statData->m_wasMinRangeSizeWritten = true;
69                 }
70                 size_t *maxRangeSize = &sc.m_statData->m_maxRangeSize;
71                 if (*maxRangeSize < rangeSize) { // if maximum is not an actual maximum
72                     *maxRangeSize = rangeSize;
73                 }
74             }
75         }
76         *this = sc;
77         // constructor is used on work balancing phase only, so no need to increment
78         // number of range objects created
79     }
80 
RangeStatisticCollector(RangeStatisticCollector & sc,proportional_split &)81     RangeStatisticCollector(RangeStatisticCollector& sc, proportional_split&) {
82         if (sc.m_statData)
83             sc.m_statData->m_rangeNum++;
84         *this = sc;
85     }
86 
87 private:
88     RangeStatisticData *m_statData;
89 
90     // turns to 'true' when non-proportional split constructor is called first time
91     bool m_called;
92 };
93 
94 // Base class for fake ranges used in various tests for parallel
95 // algorithms as well as for partitioner
96 template <typename DerivedRange, typename T>
97 class RangeBase: public RangeStatisticCollector {
98 protected:
99     size_t my_begin, my_end;
100     bool m_provide_feedback;
101     bool m_ensure_non_empty_size;
102 public:
RangeBase(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)103     RangeBase(size_t _begin, size_t _end, RangeStatisticData *statData,
104               bool provide_feedback, bool ensure_non_empty_size)
105         : RangeStatisticCollector(statData)
106         , my_begin(_begin), my_end(_end)
107         , m_provide_feedback(provide_feedback)
108         , m_ensure_non_empty_size(ensure_non_empty_size)
109         { }
RangeBase(RangeBase & r,tbb::split)110     RangeBase(RangeBase& r, tbb::split) : RangeStatisticCollector(r, r.size()) {
111         *this = r;
112         size_t middle = r.my_begin + (r.my_end - r.my_begin) / 2u;
113         r.my_end = my_begin = middle;
114     }
115 
RangeBase(RangeBase & r,proportional_split & p)116     RangeBase(RangeBase& r, proportional_split& p) : RangeStatisticCollector(r, p) {
117         *this = r;
118         size_t original_size = r.size();
119         T right = self().compute_right_part(r, p);
120         size_t right_part = self().round(right);
121         if( m_ensure_non_empty_size ) {
122             right_part = (original_size == right_part) ? (original_size - 1) : right_part;
123             right_part = (right_part != 0) ? right_part : 1;
124         }
125         r.my_end = my_begin = r.my_end - right_part;
126         if( m_ensure_non_empty_size )
127             CHECK_MESSAGE((r.my_end != r.my_begin && my_end != my_begin), "Incorrect range split");
128     }
129 
begin()130     size_t begin() const { return my_begin; }
end()131     size_t end() const { return my_end; }
is_divisible()132     bool is_divisible() const { return (my_end - my_begin) > 1; }
empty()133     bool empty() const { return my_end == my_begin; }
size()134     size_t size() const { return my_end - my_begin; }
135 
136     // helper methods (not part of the range concept)
self()137     DerivedRange& self() { return static_cast<DerivedRange&>(*this); }
round(T part)138     size_t round(T part) { return size_t(part); }
compute_right_part(RangeBase & r,proportional_split & p)139     T compute_right_part(RangeBase& r, proportional_split& p) {
140         return T(r.size() * T(p.right())) / T(p.left() + p.right());
141     }
is_ensure_non_emptiness()142     bool is_ensure_non_emptiness() { return m_ensure_non_empty_size; }
143 };
144 
145 namespace TestRanges {
146 /*
147  * RoundedUpRange rounds result up
148  * RoundedDownRange rounds result down
149  * Range1_2 forces proportion always to be 1:2 and rounds up
150  * Range1_999 uses weird proportion 1:999 and rounds up
151  * Range1_999 uses weird proportion 999:1 and rounds up
152  * BlockedRange uses tbb::blocked_range formula for proportion calculation
153  * InvertedProportionRange inverts proportion suggested by partitioner (e.g. 1:3 --> 3:1)
154  * ExactSplitRange uses integer arithmetic for accurate splitting
155  */
156 
157 class RoundedDownRange: public RangeBase<RoundedDownRange, float> {
158 public:
RoundedDownRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)159     RoundedDownRange(size_t _begin, size_t _end, RangeStatisticData *statData,
160                      bool provide_feedback, bool ensure_non_empty_size)
161         : RangeBase<RoundedDownRange, float>(_begin, _end, statData, provide_feedback,
162                                              ensure_non_empty_size) { }
RoundedDownRange(RoundedDownRange & r,tbb::split)163     RoundedDownRange(RoundedDownRange& r, tbb::split)
164         : RangeBase<RoundedDownRange, float>(r, tbb::split()) { }
RoundedDownRange(RoundedDownRange & r,proportional_split & p)165     RoundedDownRange(RoundedDownRange& r, proportional_split& p)
166         : RangeBase<RoundedDownRange, float>(r, p) { }
167     // uses default implementation of RangeBase::round() which rounds down
168 };
169 
170 class RoundedUpRange: public RangeBase<RoundedUpRange, float> {
171 public:
RoundedUpRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)172     RoundedUpRange(size_t _begin, size_t _end, RangeStatisticData *statData,
173                    bool provide_feedback, bool ensure_non_empty_size)
174         : RangeBase<RoundedUpRange, float>(_begin, _end, statData, provide_feedback,
175                                            ensure_non_empty_size) { }
RoundedUpRange(RoundedUpRange & r,tbb::split)176     RoundedUpRange(RoundedUpRange& r, tbb::split)
177         : RangeBase<RoundedUpRange, float>(r, tbb::split()) { }
RoundedUpRange(RoundedUpRange & r,proportional_split & p)178     RoundedUpRange(RoundedUpRange& r, proportional_split& p)
179         : RangeBase<RoundedUpRange, float>(r, p) { }
round(float part)180     size_t round(float part) { return size_t(std::ceil(part)); }
181 };
182 
183 class Range1_2: public RangeBase<Range1_2, float> {
184 public:
Range1_2(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)185     Range1_2(size_t _begin, size_t _end, RangeStatisticData *statData,
186              bool provide_feedback, bool ensure_non_empty_size)
187         : RangeBase<Range1_2, float>(_begin, _end, statData, provide_feedback,
188                                      ensure_non_empty_size) { }
Range1_2(Range1_2 & r,tbb::split)189     Range1_2(Range1_2& r, tbb::split) : RangeBase<Range1_2, float>(r, tbb::split()) { }
Range1_2(Range1_2 & r,proportional_split & p)190     Range1_2(Range1_2& r, proportional_split& p) : RangeBase<Range1_2, float>(r, p) { }
compute_right_part(RangeBase<Range1_2,float> & r,proportional_split &)191     float compute_right_part(RangeBase<Range1_2, float>& r, proportional_split&) {
192         return float(r.size() * 2) / 3.0f;
193     }
194     // uses default implementation of RangeBase::round() which rounds down
195 };
196 
197 class Range1_999: public RangeBase<Range1_999, float> {
198 public:
Range1_999(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)199     Range1_999(size_t _begin, size_t _end, RangeStatisticData *statData,
200                bool provide_feedback, bool ensure_non_empty_size)
201         : RangeBase<Range1_999, float>(_begin, _end, statData, provide_feedback,
202                                        ensure_non_empty_size) { }
Range1_999(Range1_999 & r,tbb::split)203     Range1_999(Range1_999& r, tbb::split) : RangeBase<Range1_999, float>(r, tbb::split()) { }
Range1_999(Range1_999 & r,proportional_split & p)204     Range1_999(Range1_999& r, proportional_split& p) : RangeBase<Range1_999, float>(r, p) { }
compute_right_part(RangeBase<Range1_999,float> & r,proportional_split &)205     float compute_right_part(RangeBase<Range1_999, float>& r, proportional_split&) {
206         return float(r.size() * 999) / 1000.0f;
207     }
208     // uses default implementation of RangeBase::round() which rounds down
209 };
210 
211 class Range999_1: public RangeBase<Range999_1, float> {
212 public:
Range999_1(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)213     Range999_1(size_t _begin, size_t _end, RangeStatisticData *statData,
214                bool provide_feedback, bool ensure_non_empty_size)
215         : RangeBase<Range999_1, float>(_begin, _end, statData, provide_feedback,
216                                        ensure_non_empty_size) { }
Range999_1(Range999_1 & r,tbb::split)217     Range999_1(Range999_1& r, tbb::split) : RangeBase<Range999_1, float>(r, tbb::split()) { }
Range999_1(Range999_1 & r,proportional_split & p)218     Range999_1(Range999_1& r, proportional_split& p) : RangeBase<Range999_1, float>(r, p) { }
compute_right_part(RangeBase<Range999_1,float> & r,proportional_split &)219     float compute_right_part(RangeBase<Range999_1, float>& r, proportional_split&) {
220         return float(r.size()) / 1000.0f;
221     }
222     // uses default implementation of RangeBase::round() which rounds down
223 };
224 
225 class BlockedRange: public RangeStatisticCollector, public blocked_range<size_t>  {
226 public:
BlockedRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool,bool)227     BlockedRange(size_t _begin, size_t _end, RangeStatisticData *statData, bool, bool)
228         : RangeStatisticCollector(statData), blocked_range<size_t>(_begin, _end) { }
BlockedRange(BlockedRange & r,split)229     BlockedRange(BlockedRange& r, split)
230         : RangeStatisticCollector(r, r.size()), blocked_range<size_t>(r, split()) { }
BlockedRange(BlockedRange & r,proportional_split & p)231     BlockedRange(BlockedRange& r, proportional_split& p)
232         : RangeStatisticCollector(r, p), blocked_range<size_t>(r, p) { }
is_ensure_non_emptiness()233     bool is_ensure_non_emptiness() { return false; }
234 };
235 
236 class InvertedProportionRange: public RangeBase<InvertedProportionRange, float> {
237 public:
InvertedProportionRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)238     InvertedProportionRange(size_t _begin, size_t _end, RangeStatisticData *statData,
239                             bool provide_feedback, bool ensure_non_empty_size)
240         : RangeBase<InvertedProportionRange, float>(_begin, _end, statData, provide_feedback,
241                                                     ensure_non_empty_size) { }
InvertedProportionRange(InvertedProportionRange & r,split)242     InvertedProportionRange(InvertedProportionRange& r, split)
243         : RangeBase<InvertedProportionRange, float>(r, split()) { }
InvertedProportionRange(InvertedProportionRange & r,proportional_split & p)244     InvertedProportionRange(InvertedProportionRange& r, proportional_split& p)
245         : RangeBase<InvertedProportionRange, float>(r, p) { }
compute_right_part(RangeBase<InvertedProportionRange,float> & r,proportional_split & p)246     float compute_right_part(RangeBase<InvertedProportionRange, float>& r,
247                              proportional_split& p) {
248         return float(r.size() * float(p.left())) / float(p.left() + p.right());
249     }
250 };
251 
252 class ExactSplitRange: public RangeBase<ExactSplitRange, size_t> {
253 public:
ExactSplitRange(size_t _begin,size_t _end,RangeStatisticData * statData,bool provide_feedback,bool ensure_non_empty_size)254     ExactSplitRange(size_t _begin, size_t _end, RangeStatisticData *statData,
255                     bool provide_feedback, bool ensure_non_empty_size)
256         : RangeBase<ExactSplitRange, size_t>(_begin, _end, statData, provide_feedback,
257                                              ensure_non_empty_size) { }
ExactSplitRange(ExactSplitRange & r,split)258     ExactSplitRange(ExactSplitRange& r, split)
259         : RangeBase<ExactSplitRange, size_t>(r, split()) { }
ExactSplitRange(ExactSplitRange & r,proportional_split & p)260     ExactSplitRange(ExactSplitRange& r, proportional_split& p)
261         : RangeBase<ExactSplitRange, size_t>(r, p) { }
compute_right_part(RangeBase<ExactSplitRange,size_t> & r,proportional_split & p)262     size_t compute_right_part(RangeBase<ExactSplitRange, size_t>& r, proportional_split& p) {
263         size_t parts = size_t(p.left() + p.right());
264         size_t currSize = r.size();
265         size_t int_part = currSize / parts * p.right();
266         size_t remainder = currSize % parts * p.right();
267         int_part += remainder / parts;
268         remainder %= parts;
269         size_t right_part = int_part + (remainder > parts/2 ? 1 : 0);
270         return right_part;
271     }
272 };
273 
274 } // namespace TestRanges
275 
276 struct TreeNode {
277     size_t m_affinity;
278     size_t m_range_begin, m_range_end;
279     TreeNode *m_left, *m_right;
280 private:
TreeNodeTreeNode281     TreeNode(size_t range_begin, size_t range_end, size_t affinity,
282              TreeNode* left, TreeNode* right)
283         : m_affinity(affinity), m_range_begin(range_begin), m_range_end(range_end),
284           m_left(left), m_right(right) { }
285 
286     friend TreeNode* make_node(size_t range_begin, size_t range_end, size_t affinity,
287                                TreeNode *left, TreeNode *right);
288 };
289 
290 TreeNode* make_node(size_t range_begin, size_t range_end, size_t affinity,
291                     TreeNode* left = NULL, TreeNode* right = NULL) {
292     CHECK_MESSAGE((range_begin <= range_end), "Incorrect range interval");
293     return new TreeNode(range_begin, range_end, affinity, left, right);
294 }
295 
296 // Class stores nodes as a binary tree
297 // (marshals TreeNode objects in accordance with values of range intervals)
298 // Note: BinaryTree deletes all TreeNode objects pushed into it in a destruction phase
299 class BinaryTree {
300 public:
BinaryTree()301     BinaryTree() : m_root(NULL) { }
~BinaryTree()302     ~BinaryTree() {
303         if (m_root)
304             remove_node_recursively(m_root);
305     }
306 
307     // pushed node must be within subrange of the parent nodes
push_node(TreeNode * node)308     void push_node(TreeNode* node) {
309         if (!node)
310             return;
311 
312         if (m_root) {
313             CHECK_MESSAGE((node->m_range_begin >= m_root->m_range_begin && node->m_range_end <= m_root->m_range_end),
314                 "Cannot push node not from subrange");
315         }
316 
317         push_subnode(m_root, node);
318     }
319 
visualize()320     void visualize() {
321         if (!m_root) { // nothing to visualize
322             REPORT("Tree is empty\n");
323             return;
324         }
325         visualize_node(m_root);
326     }
327 
328     bool operator ==(const BinaryTree& other_tree) const { return compare_nodes(m_root, other_tree.m_root); }
fill_leafs(std::vector<TreeNode * > & leafs)329     void fill_leafs(std::vector<TreeNode*>& leafs) const { fill_leafs_impl(m_root, leafs); }
330 
331 private:
332     TreeNode *m_root;
333 
push_subnode(TreeNode * & root_node,TreeNode * node)334     void push_subnode(TreeNode *&root_node, TreeNode *node) {
335         if (!root_node) {
336             root_node = node;
337             return;
338         } else if (are_nodes_equal(root_node, node)) {
339             // no need to push the same node
340             return;
341         }
342 
343         if (!has_children(root_node)) {
344             // if current root_node does not have children passed node
345             // should has one of the interval bounds to be equal to
346             // the same bound in the root_node
347             if (is_look_like_left_sibling(root_node, node))
348                 push_subnode(root_node->m_left, node);
349             else
350                 push_subnode(root_node->m_right, node);
351             return;
352         }
353 
354         if (has_left_child(root_node)) {
355             if (is_subnode(root_node->m_left, node)) {
356                 push_subnode(root_node->m_left, node);
357                 return;
358             }
359             push_subnode(root_node->m_right, node);
360             return;
361         }
362 
363         CHECK_MESSAGE(root_node->m_right != nullptr, "Right child is NULL but must be present");
364         if (is_subnode(root_node->m_right, node)) {
365             push_subnode(root_node->m_right, node);
366             return;
367         }
368         push_subnode(root_node->m_left, node);
369         return;
370     }
371 
has_children(TreeNode * node)372     bool has_children(TreeNode *node) { return node->m_left || node->m_right; }
373 
is_look_like_left_sibling(TreeNode * root_node,TreeNode * node)374     bool is_look_like_left_sibling(TreeNode *root_node, TreeNode *node) {
375         if (root_node->m_range_begin == node->m_range_begin)
376             return true;
377         CHECK_MESSAGE(root_node->m_range_end == node->m_range_end, NULL);
378         return false;
379     }
380 
has_left_child(TreeNode * node)381     bool has_left_child(TreeNode *node) { return node->m_left != NULL; }
382 
is_subnode(TreeNode * root_node,TreeNode * node)383     bool is_subnode(TreeNode *root_node, TreeNode *node) {
384         return root_node->m_range_begin <= node->m_range_begin &&
385             node->m_range_end <= root_node->m_range_end;
386     }
387 
are_nodes_equal(TreeNode * node1,TreeNode * node2)388     bool are_nodes_equal(TreeNode *node1, TreeNode *node2) const {
389         return node1->m_range_begin == node2->m_range_begin &&
390             node1->m_range_end == node2->m_range_end;
391     }
392 
remove_node_recursively(TreeNode * node)393     void remove_node_recursively(TreeNode *node) {
394         if (node->m_left)
395             remove_node_recursively(node->m_left);
396         if (node->m_right)
397             remove_node_recursively(node->m_right);
398         delete node;
399     }
400 
401     static void visualize_node(const TreeNode* node, unsigned indent = 0) {
402         // respecting indent
403         const char *indentStep = "    ";
404         for (unsigned i = 0; i < indent; ++i)
405             REPORT("%s", indentStep);
406 
407         size_t rangeSize = node->m_range_end - node->m_range_begin;
408         REPORT("[%llu, %llu)%%%llu@%llu\n", uint64_t(node->m_range_begin), uint64_t(node->m_range_end),
409                uint64_t(rangeSize), uint64_t(node->m_affinity));
410 
411         if (node->m_left)
412             visualize_node(node->m_left, indent + 1);
413         if (node->m_right)
414             visualize_node(node->m_right, indent + 1);
415     }
416 
compare_nodes(TreeNode * node1,TreeNode * node2)417     bool compare_nodes(TreeNode* node1, TreeNode* node2) const {
418         if (node1 == NULL && node2 == NULL) return true;
419         if (node1 == NULL || node2 == NULL) return false;
420         return are_nodes_equal(node1, node2) && compare_nodes(node1->m_left, node2->m_left)
421             && compare_nodes(node1->m_right, node2->m_right);
422     }
423 
fill_leafs_impl(TreeNode * node,std::vector<TreeNode * > & leafs)424     void fill_leafs_impl(TreeNode* node, std::vector<TreeNode*>& leafs) const {
425         if (node->m_left == NULL && node->m_right == NULL)
426             leafs.push_back(node);
427         if (node->m_left != NULL) fill_leafs_impl(node->m_left, leafs);
428         if (node->m_right != NULL) fill_leafs_impl(node->m_right, leafs);
429     }
430 };
431 
432 class SimpleBody {
433 public:
SimpleBody()434     SimpleBody() { }
435     template <typename Range>
operator()436     void operator()(Range&) const { }
437 };
438 
439 class SimpleReduceBody {
440 public:
SimpleReduceBody()441     SimpleReduceBody() { }
SimpleReduceBody(SimpleReduceBody &,tbb::split)442     SimpleReduceBody(SimpleReduceBody&, tbb::split) { }
443     template <typename Range>
operator()444     void operator()(Range&) { }
join(SimpleReduceBody &)445     void join(SimpleReduceBody&) { }
446 };
447 
448 namespace interaction_with_range_and_partitioner {
449 
450 class SplitConstructorAssertedRange {
451     mutable bool is_divisible_called;
452     mutable bool is_empty_called;
453     bool my_assert_in_nonproportional, my_assert_in_proportional;
454 public:
SplitConstructorAssertedRange(bool assert_in_nonproportional,bool assert_in_proportional)455     SplitConstructorAssertedRange(bool assert_in_nonproportional, bool assert_in_proportional)
456         : is_divisible_called(false),
457           is_empty_called(false),
458           my_assert_in_nonproportional(assert_in_nonproportional),
459           my_assert_in_proportional(assert_in_proportional) { }
SplitConstructorAssertedRange(SplitConstructorAssertedRange & r,tbb::split)460     SplitConstructorAssertedRange(SplitConstructorAssertedRange& r, tbb::split) {
461         *this = r;
462         CHECK_MESSAGE( !my_assert_in_nonproportional, "Disproportional splitting constructor was called but should not been" );
463     }
SplitConstructorAssertedRange(SplitConstructorAssertedRange & r,proportional_split &)464     SplitConstructorAssertedRange(SplitConstructorAssertedRange& r, proportional_split&) {
465         *this = r;
466         CHECK_MESSAGE( !my_assert_in_proportional, "Proportional splitting constructor was called but should not been" );
467     }
is_divisible()468     bool is_divisible() const {
469         if (!is_divisible_called) {
470             is_divisible_called = true;
471             return true;
472         }
473         return false;
474     }
empty()475     bool empty() const {
476         if (!is_empty_called) {
477             is_empty_called = true;
478             return false;
479         }
480         return true;
481     }
482 };
483 
484 /*
485  * Possible use cases are:
486  * -------------------------------------------------------------------------------------------------------------
487  * Range#  is_splittable_in_proportion   Range proportional ctor      Used partitioner          Result Effect
488  * -------------------------------------------------------------------------------------------------------------
489  *   1           true                       available                proportional             pMN, r(p), part(p)
490  * -------------------------------------------------------------------------------------------------------------
491  *   2           false                      available                proportional             p11, r(p), part(p)
492  * -------------------------------------------------------------------------------------------------------------
493  *   3        not defined                   available                proportional             p11, r(p), part(p)
494  * -------------------------------------------------------------------------------------------------------------
495  *   4           true                     not available              proportional             pMN, r(s), part(p)  *
496  * -------------------------------------------------------------------------------------------------------------
497  *   5           false                    not available              proportional             p11, r(s), part(p)
498  * -------------------------------------------------------------------------------------------------------------
499  *   6        not defined                 not available              proportional             p11, r(s), part(p)
500  * -------------------------------------------------------------------------------------------------------------
501  *   1           true                       available                   simple                s, r(s), part(s)
502  * -------------------------------------------------------------------------------------------------------------
503  *   2           false                      available                   simple                s, r(s), part(s)
504  * -------------------------------------------------------------------------------------------------------------
505  *   3        not defined                   available                   simple                s, r(s), part(s)
506  * -------------------------------------------------------------------------------------------------------------
507  *   4           true                     not available                 simple                s, r(s), part(s)
508  * -------------------------------------------------------------------------------------------------------------
509  *   5           false                    not available                 simple                s, r(s), part(s)
510  * -------------------------------------------------------------------------------------------------------------
511  *   6        not defined                 not available                 simple                s, r(s), part(s)
512  * -------------------------------------------------------------------------------------------------------------
513  *
514  * Legend:
515  *   proportional - with proportional splits (e.g. affinity_partitioner)
516  *   simple  - without proportional splits (e.g. simple_partitioner, auto_partitioner)
517  *   pMN     - proportional_split object with proportion M to N is created. (p11 - proportion 1 to 1)
518  *   s       - split object is created
519  *   r(p)    - range's proportional split constructor is called
520  *   r(s)    - range's ordinary split constructor is called
521  *   part(p) - partitioner's proportional split constructor is called
522  *   part(s) - partitioner's ordinary split constructor is called
523  *      *    - incorrect split behavior is possible (e.g. partitioner divides at an arbitrary ratio while
524  *             range divides into halves)
525  */
526 
527 
528 // proportional_split ctor defined
529 class Range1: public SplitConstructorAssertedRange {
530 public:
Range1(bool assert_in_nonproportional,bool assert_in_proportional)531     Range1(bool assert_in_nonproportional, bool assert_in_proportional)
532         : SplitConstructorAssertedRange(assert_in_nonproportional, assert_in_proportional) { }
Range1(Range1 & r,tbb::split)533     Range1( Range1& r, tbb::split ) : SplitConstructorAssertedRange(r, tbb::split()) { }
Range1(Range1 & r,proportional_split & proportion)534     Range1( Range1& r, proportional_split& proportion ) : SplitConstructorAssertedRange(r, proportion) { }
535 };
536 
537 // proportional_split ctor is not defined
538 class Range6: public SplitConstructorAssertedRange {
539 public:
Range6(bool assert_in_nonproportional,bool assert_in_proportional)540     Range6(bool assert_in_nonproportional, bool assert_in_proportional)
541         : SplitConstructorAssertedRange(assert_in_nonproportional, assert_in_proportional) { }
Range6(Range6 & r,tbb::split)542     Range6(Range6& r, tbb::split) : SplitConstructorAssertedRange(r, tbb::split()) { }
543 };
544 
545 } // namespace interaction_with_range_and_partitioner
546 
547 } // namespace test_partitioner_utils
548