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