1 /**
2  * @file unit-SubarrayPartitioner-sparse.cc
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2017-2021 TileDB, Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * Tests the `SubarrayPartitioner` class for sparse arrays.
31  */
32 
33 #include "test/src/helpers.h"
34 #include "test/src/vfs_helpers.h"
35 #include "tiledb/sm/c_api/tiledb_struct_def.h"
36 #include "tiledb/sm/subarray/subarray_partitioner.h"
37 
38 #ifdef _WIN32
39 #include "tiledb/sm/filesystem/win.h"
40 #else
41 #include "tiledb/sm/filesystem/posix.h"
42 #endif
43 
44 #include <catch.hpp>
45 #include <iostream>
46 
47 using namespace tiledb::common;
48 using namespace tiledb::sm;
49 using namespace tiledb::test;
50 
51 /* ********************************* */
52 /*         STRUCT DEFINITION         */
53 /* ********************************* */
54 
55 struct SubarrayPartitionerSparseFx {
56   tiledb_ctx_t* ctx_;
57   tiledb_vfs_t* vfs_;
58   const std::vector<std::unique_ptr<SupportedFs>> fs_vec_;
59   std::string temp_dir_;
60   std::string array_name_;
61   const char* ARRAY_NAME = "subarray_partitioner_sparse";
62   tiledb_array_t* array_ = nullptr;
63   uint64_t memory_budget_ = 1024 * 1024 * 1024;
64   uint64_t memory_budget_var_ = 1024 * 1024 * 1024;
65 
66   SubarrayPartitionerSparseFx();
67   ~SubarrayPartitionerSparseFx();
68 
69   /**
70    * Helper function that creates a default 1D array, with the
71    * input tile and cell order.
72    */
73   void create_default_1d_array(
74       tiledb_layout_t tile_order, tiledb_layout_t cell_order);
75 
76   /**
77    * Helper function that creates a default 1D array, with float
78    * dimension and the input tile and cell order.
79    */
80   void create_default_1d_float_array(
81       tiledb_layout_t tile_order, tiledb_layout_t cell_order);
82 
83   /**
84    * Helper function that creates a default 2D array, with the
85    * input tile and cell order.
86    */
87   void create_default_2d_array(
88       tiledb_layout_t tile_order, tiledb_layout_t cell_order);
89 
90   /** Helper function that writes to the default 1D array. */
91   void write_default_1d_array();
92 
93   /** Helper function that writes to a second default 1D array. */
94   void write_default_1d_array_2();
95 
96   /** Helper function that writes to the default 1D float array. */
97   void write_default_1d_float_array();
98 
99   /** Helper function that writes to the default 2D array. */
100   void write_default_2d_array();
101 
102   /**
103    * Helper function to test the subarray partitioner for the given arguments.
104    */
105   template <class T>
106   void test_subarray_partitioner(
107       Layout subarray_layout,
108       const SubarrayRanges<T>& ranges,
109       const std::vector<SubarrayRanges<T>>& partitions,
110       const std::string& attr,  // Attribute to set the budget for
111       uint64_t budget,
112       bool unsplittable = false);
113 
114   /**
115    * Helper function to test the subarray partitioner for the given arguments.
116    */
117   template <class T>
118   void test_subarray_partitioner(
119       Layout subarray_layout,
120       const SubarrayRanges<T>& ranges,
121       const std::vector<SubarrayRanges<T>>& partitions,
122       const std::string& attr,  // Attribute to set the budget for
123       uint64_t result_budget,
124       uint64_t memory_budget,
125       uint64_t memory_budget_var,
126       bool unsplittable = false);
127 
128   /**
129    * Helper function to test the subarray partitioner for the given arguments.
130    * This is different from the above in that it tests the memory
131    * budget.
132    */
133   template <class T>
134   void test_subarray_partitioner(
135       Layout subarray_layout,
136       const SubarrayRanges<T>& ranges,
137       const std::vector<SubarrayRanges<T>>& partitions,
138       uint64_t budget,
139       uint64_t budget_var,
140       bool unsplittable = false);
141 };
142 
SubarrayPartitionerSparseFx()143 SubarrayPartitionerSparseFx::SubarrayPartitionerSparseFx()
144     : fs_vec_(vfs_test_get_fs_vec()) {
145   // Initialize vfs test
146   REQUIRE(vfs_test_init(fs_vec_, &ctx_, &vfs_).ok());
147 
148 // Create temporary directory based on the supported filesystem
149 #ifdef _WIN32
150   SupportedFsLocal windows_fs;
151   temp_dir_ = windows_fs.file_prefix() + windows_fs.temp_dir();
152 #else
153   SupportedFsLocal posix_fs;
154   temp_dir_ = posix_fs.file_prefix() + posix_fs.temp_dir();
155 #endif
156 
157   create_dir(temp_dir_, ctx_, vfs_);
158 
159   array_name_ = temp_dir_ + ARRAY_NAME;
160   int rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array_);
161   CHECK(rc == TILEDB_OK);
162 }
163 
~SubarrayPartitionerSparseFx()164 SubarrayPartitionerSparseFx::~SubarrayPartitionerSparseFx() {
165   tiledb_array_free(&array_);
166   remove_dir(temp_dir_, ctx_, vfs_);
167   tiledb_ctx_free(&ctx_);
168   tiledb_vfs_free(&vfs_);
169 }
170 
create_default_1d_array(tiledb_layout_t tile_order,tiledb_layout_t cell_order)171 void SubarrayPartitionerSparseFx::create_default_1d_array(
172     tiledb_layout_t tile_order, tiledb_layout_t cell_order) {
173   uint64_t domain[] = {1, 100};
174   uint64_t tile_extent = 10;
175   create_array(
176       ctx_,
177       array_name_,
178       TILEDB_SPARSE,
179       {"d"},
180       {TILEDB_UINT64},
181       {domain},
182       {&tile_extent},
183       {"a", "b"},
184       {TILEDB_INT32, TILEDB_INT32},
185       {1, TILEDB_VAR_NUM},
186       {tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1),
187        tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1)},
188       tile_order,
189       cell_order,
190       2);
191 }
192 
create_default_1d_float_array(tiledb_layout_t tile_order,tiledb_layout_t cell_order)193 void SubarrayPartitionerSparseFx::create_default_1d_float_array(
194     tiledb_layout_t tile_order, tiledb_layout_t cell_order) {
195   float domain[] = {1.0f, 100.0f};
196   float tile_extent = 100.0f;
197   create_array(
198       ctx_,
199       array_name_,
200       TILEDB_SPARSE,
201       {"d"},
202       {TILEDB_FLOAT32},
203       {domain},
204       {&tile_extent},
205       {"a", "b"},
206       {TILEDB_INT32, TILEDB_INT32},
207       {1, TILEDB_VAR_NUM},
208       {tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1),
209        tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1)},
210       tile_order,
211       cell_order,
212       2);
213 }
214 
create_default_2d_array(tiledb_layout_t tile_order,tiledb_layout_t cell_order)215 void SubarrayPartitionerSparseFx::create_default_2d_array(
216     tiledb_layout_t tile_order, tiledb_layout_t cell_order) {
217   uint64_t domain[] = {1, 10};
218   uint64_t tile_extent = 2;
219   create_array(
220       ctx_,
221       array_name_,
222       TILEDB_SPARSE,
223       {"d1", "d2"},
224       {TILEDB_UINT64, TILEDB_UINT64},
225       {domain, domain},
226       {&tile_extent, &tile_extent},
227       {"a", "b"},
228       {TILEDB_INT32, TILEDB_INT32},
229       {1, TILEDB_VAR_NUM},
230       {tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1),
231        tiledb::test::Compressor(TILEDB_FILTER_LZ4, -1)},
232       tile_order,
233       cell_order,
234       2);
235 }
236 
write_default_1d_array()237 void SubarrayPartitionerSparseFx::write_default_1d_array() {
238   tiledb::test::QueryBuffers buffers;
239   std::vector<uint64_t> coords = {2, 4, 5, 10, 12, 18};
240   uint64_t coords_size = coords.size() * sizeof(uint64_t);
241   std::vector<int> a = {1, 2, 3, 4, 5, 6};
242   uint64_t a_size = a.size() * sizeof(int);
243   std::vector<uint64_t> b_off = {0,
244                                  sizeof(int),
245                                  3 * sizeof(int),
246                                  6 * sizeof(int),
247                                  9 * sizeof(int),
248                                  11 * sizeof(int)};
249   uint64_t b_off_size = b_off.size() * sizeof(uint64_t);
250   std::vector<int> b_val = {1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 6};
251   uint64_t b_val_size = b_val.size() * sizeof(int);
252   buffers[TILEDB_COORDS] =
253       tiledb::test::QueryBuffer({&coords[0], coords_size, nullptr, 0});
254   buffers["a"] = tiledb::test::QueryBuffer({&a[0], a_size, nullptr, 0});
255   buffers["b"] =
256       tiledb::test::QueryBuffer({&b_off[0], b_off_size, &b_val[0], b_val_size});
257   write_array(ctx_, array_name_, TILEDB_UNORDERED, buffers);
258 }
259 
write_default_1d_array_2()260 void SubarrayPartitionerSparseFx::write_default_1d_array_2() {
261   tiledb::test::QueryBuffers buffers;
262   std::vector<uint64_t> coords = {2, 4, 5, 10, 12, 18, 25, 27, 33, 40};
263   uint64_t coords_size = coords.size() * sizeof(uint64_t);
264   std::vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
265   uint64_t a_size = a.size() * sizeof(int);
266   std::vector<uint64_t> b_off = {0,
267                                  sizeof(int),
268                                  3 * sizeof(int),
269                                  6 * sizeof(int),
270                                  9 * sizeof(int),
271                                  11 * sizeof(int),
272                                  15 * sizeof(int),
273                                  16 * sizeof(int),
274                                  17 * sizeof(int),
275                                  18 * sizeof(int)};
276   uint64_t b_off_size = b_off.size() * sizeof(uint64_t);
277   std::vector<int> b_val = {
278       1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 10};
279   uint64_t b_val_size = b_val.size() * sizeof(int);
280   buffers[TILEDB_COORDS] =
281       tiledb::test::QueryBuffer({&coords[0], coords_size, nullptr, 0});
282   buffers["a"] = tiledb::test::QueryBuffer({&a[0], a_size, nullptr, 0});
283   buffers["b"] =
284       tiledb::test::QueryBuffer({&b_off[0], b_off_size, &b_val[0], b_val_size});
285   write_array(ctx_, array_name_, TILEDB_UNORDERED, buffers);
286 }
287 
write_default_1d_float_array()288 void SubarrayPartitionerSparseFx::write_default_1d_float_array() {
289   tiledb::test::QueryBuffers buffers;
290   std::vector<float> coords = {2.0f, 4.0f, 5.0f, 10.0f, 12.0f, 18.0f};
291   uint64_t coords_size = coords.size() * sizeof(float);
292   std::vector<int> a = {1, 2, 3, 4, 5, 6};
293   uint64_t a_size = a.size() * sizeof(int);
294   std::vector<uint64_t> b_off = {0,
295                                  sizeof(int),
296                                  3 * sizeof(int),
297                                  6 * sizeof(int),
298                                  9 * sizeof(int),
299                                  11 * sizeof(int)};
300   uint64_t b_off_size = b_off.size() * sizeof(uint64_t);
301   std::vector<int> b_val = {1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 6};
302   uint64_t b_val_size = b_val.size() * sizeof(int);
303   buffers[TILEDB_COORDS] =
304       tiledb::test::QueryBuffer({&coords[0], coords_size, nullptr, 0});
305   buffers["a"] = tiledb::test::QueryBuffer({&a[0], a_size, nullptr, 0});
306   buffers["b"] =
307       tiledb::test::QueryBuffer({&b_off[0], b_off_size, &b_val[0], b_val_size});
308   write_array(ctx_, array_name_, TILEDB_UNORDERED, buffers);
309 }
310 
write_default_2d_array()311 void SubarrayPartitionerSparseFx::write_default_2d_array() {
312   tiledb::test::QueryBuffers buffers;
313   std::vector<uint64_t> coords = {1, 2, 2, 5, 3, 3, 3, 9, 4, 1, 4, 7};
314   uint64_t coords_size = coords.size() * sizeof(uint64_t);
315   std::vector<int> a = {1, 2, 3, 4, 5, 6};
316   uint64_t a_size = a.size() * sizeof(int);
317   std::vector<uint64_t> b_off = {0,
318                                  sizeof(int),
319                                  3 * sizeof(int),
320                                  6 * sizeof(int),
321                                  9 * sizeof(int),
322                                  11 * sizeof(int)};
323   uint64_t b_off_size = b_off.size() * sizeof(uint64_t);
324   std::vector<int> b_val = {1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, 6};
325   uint64_t b_val_size = b_val.size() * sizeof(int);
326   buffers[TILEDB_COORDS] =
327       tiledb::test::QueryBuffer({&coords[0], coords_size, nullptr, 0});
328   buffers["a"] = tiledb::test::QueryBuffer({&a[0], a_size, nullptr, 0});
329   buffers["b"] =
330       tiledb::test::QueryBuffer({&b_off[0], b_off_size, &b_val[0], b_val_size});
331   write_array(ctx_, array_name_, TILEDB_UNORDERED, buffers);
332 }
333 
334 template <class T>
test_subarray_partitioner(Layout subarray_layout,const SubarrayRanges<T> & ranges,const std::vector<SubarrayRanges<T>> & partitions,const std::string & attr,uint64_t budget,bool unsplittable)335 void SubarrayPartitionerSparseFx::test_subarray_partitioner(
336     Layout subarray_layout,
337     const SubarrayRanges<T>& ranges,
338     const std::vector<SubarrayRanges<T>>& partitions,
339     const std::string& attr,
340     uint64_t budget,
341     bool unsplittable) {
342   Subarray subarray;
343   create_subarray(array_->array_, ranges, subarray_layout, &subarray);
344 
345   ThreadPool tp;
346   CHECK(tp.init(4).ok());
347   Config config;
348   SubarrayPartitioner subarray_partitioner(
349       &config,
350       subarray,
351       memory_budget_,
352       memory_budget_var_,
353       0,
354       &tp,
355       &g_helper_stats,
356       g_helper_logger());
357   auto st = subarray_partitioner.set_result_budget(attr.c_str(), budget);
358   CHECK(st.ok());
359 
360   check_partitions(subarray_partitioner, partitions, unsplittable);
361 }
362 
363 template <class T>
test_subarray_partitioner(Layout subarray_layout,const SubarrayRanges<T> & ranges,const std::vector<SubarrayRanges<T>> & partitions,const std::string & attr,uint64_t result_budget,uint64_t memory_budget,uint64_t memory_budget_var,bool unsplittable)364 void SubarrayPartitionerSparseFx::test_subarray_partitioner(
365     Layout subarray_layout,
366     const SubarrayRanges<T>& ranges,
367     const std::vector<SubarrayRanges<T>>& partitions,
368     const std::string& attr,
369     uint64_t result_budget,
370     uint64_t memory_budget,
371     uint64_t memory_budget_var,
372     bool unsplittable) {
373   Subarray subarray;
374   create_subarray(array_->array_, ranges, subarray_layout, &subarray);
375 
376   ThreadPool tp;
377   CHECK(tp.init(4).ok());
378   Config config;
379   SubarrayPartitioner subarray_partitioner(
380       &config,
381       subarray,
382       memory_budget,
383       memory_budget_var,
384       0,
385       &tp,
386       &g_helper_stats,
387       g_helper_logger());
388   auto st = subarray_partitioner.set_result_budget(attr.c_str(), result_budget);
389   CHECK(st.ok());
390 
391   check_partitions(subarray_partitioner, partitions, unsplittable);
392 }
393 
394 template <class T>
test_subarray_partitioner(Layout subarray_layout,const SubarrayRanges<T> & ranges,const std::vector<SubarrayRanges<T>> & partitions,uint64_t budget,uint64_t budget_var,bool unsplittable)395 void SubarrayPartitionerSparseFx::test_subarray_partitioner(
396     Layout subarray_layout,
397     const SubarrayRanges<T>& ranges,
398     const std::vector<SubarrayRanges<T>>& partitions,
399     uint64_t budget,
400     uint64_t budget_var,
401     bool unsplittable) {
402   Subarray subarray;
403   create_subarray(array_->array_, ranges, subarray_layout, &subarray);
404 
405   ThreadPool tp;
406   CHECK(tp.init(4).ok());
407   Config config;
408   SubarrayPartitioner subarray_partitioner(
409       &config,
410       subarray,
411       memory_budget_,
412       memory_budget_var_,
413       0,
414       &tp,
415       &g_helper_stats,
416       g_helper_logger());
417 
418   // Note: this is necessary, otherwise the subarray partitioner does
419   // not check if the memory budget is exceeded for attributes whose
420   // result budget is not set.
421   auto st = subarray_partitioner.set_result_budget(TILEDB_COORDS, 1000000);
422   CHECK(st.ok());
423   st = subarray_partitioner.set_result_budget("a", 1000000);
424   CHECK(st.ok());
425   st = subarray_partitioner.set_result_budget("b", 1000000, 1000000);
426   CHECK(st.ok());
427 
428   st = subarray_partitioner.set_memory_budget(budget, budget_var, 0);
429   CHECK(st.ok());
430 
431   check_partitions(subarray_partitioner, partitions, unsplittable);
432 }
433 
434 /* ********************************* */
435 /*                TESTS              */
436 /* ********************************* */
437 
438 TEST_CASE_METHOD(
439     SubarrayPartitionerSparseFx,
440     "SubarrayPartitioner (Sparse): 1D, single-range, empty array",
441     "[SubarrayPartitioner][sparse][1D][1R][empty_array]") {
442   Layout subarray_layout;
443   SubarrayRanges<uint64_t> ranges = {};
444   std::vector<SubarrayRanges<uint64_t>> partitions = {{{1, 100}}};
445   uint64_t budget = 1000 * sizeof(uint64_t);
446   std::string attr = TILEDB_COORDS;
447   bool unsplittable = false;
448 
449   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
450   open_array(ctx_, array_, TILEDB_READ);
451 
452   // subarray: global
453   subarray_layout = Layout::GLOBAL_ORDER;
454   test_subarray_partitioner(
455       subarray_layout, ranges, partitions, attr, budget, unsplittable);
456 
457   // subarray: row
458   subarray_layout = Layout::ROW_MAJOR;
459   test_subarray_partitioner(
460       subarray_layout, ranges, partitions, attr, budget, unsplittable);
461 
462   // subarray: col
463   subarray_layout = Layout::COL_MAJOR;
464   test_subarray_partitioner(
465       subarray_layout, ranges, partitions, attr, budget, unsplittable);
466 
467   // subarray: unordered
468   subarray_layout = Layout::UNORDERED;
469   test_subarray_partitioner(
470       subarray_layout, ranges, partitions, attr, budget, unsplittable);
471 
472   close_array(ctx_, array_);
473 }
474 
475 TEST_CASE_METHOD(
476     SubarrayPartitionerSparseFx,
477     "SubarrayPartitioner (Sparse): 1D, single-range, whole subarray fits",
478     "[SubarrayPartitioner][sparse][1D][1R][whole_subarray_fits]") {
479   Layout subarray_layout;
480   SubarrayRanges<uint64_t> ranges = {};
481   std::vector<SubarrayRanges<uint64_t>> partitions = {{{1, 100}}};
482   uint64_t budget = 1000 * sizeof(uint64_t);
483   std::string attr = TILEDB_COORDS;
484   bool unsplittable = false;
485 
486   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
487   write_default_1d_array();
488   open_array(ctx_, array_, TILEDB_READ);
489 
490   // subarray: global
491   subarray_layout = Layout::GLOBAL_ORDER;
492   test_subarray_partitioner(
493       subarray_layout, ranges, partitions, attr, budget, unsplittable);
494 
495   // subarray: row
496   subarray_layout = Layout::ROW_MAJOR;
497   test_subarray_partitioner(
498       subarray_layout, ranges, partitions, attr, budget, unsplittable);
499 
500   // subarray: col
501   subarray_layout = Layout::COL_MAJOR;
502   test_subarray_partitioner(
503       subarray_layout, ranges, partitions, attr, budget, unsplittable);
504 
505   // subarray: unordered
506   subarray_layout = Layout::UNORDERED;
507   test_subarray_partitioner(
508       subarray_layout, ranges, partitions, attr, budget, unsplittable);
509 
510   close_array(ctx_, array_);
511 }
512 
513 TEST_CASE_METHOD(
514     SubarrayPartitionerSparseFx,
515     "SubarrayPartitioner (Sparse): 1D, single-range, split once",
516     "[SubarrayPartitioner][sparse][1D][1R][split_once]") {
517   Layout subarray_layout;
518   SubarrayRanges<uint64_t> ranges = {{3, 11}};
519   std::vector<SubarrayRanges<uint64_t>> partitions = {{{1, 100}}};
520   uint64_t budget = 3 * sizeof(int);
521   std::string attr = "a";
522   bool unsplittable = false;
523 
524   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
525   write_default_1d_array();
526   open_array(ctx_, array_, TILEDB_READ);
527 
528   // subarray: global
529   subarray_layout = Layout::GLOBAL_ORDER;
530   partitions = {{{3, 6}}, {{7, 10}}, {{11, 11}}};
531   test_subarray_partitioner(
532       subarray_layout, ranges, partitions, attr, budget, unsplittable);
533 
534   // subarray: row
535   subarray_layout = Layout::ROW_MAJOR;
536   partitions = {{{3, 7}}, {{8, 11}}};
537   test_subarray_partitioner(
538       subarray_layout, ranges, partitions, attr, budget, unsplittable);
539 
540   // subarray: col
541   subarray_layout = Layout::COL_MAJOR;
542   test_subarray_partitioner(
543       subarray_layout, ranges, partitions, attr, budget, unsplittable);
544 
545   // subarray: unordered
546   subarray_layout = Layout::UNORDERED;
547   test_subarray_partitioner(
548       subarray_layout, ranges, partitions, attr, budget, unsplittable);
549 
550   close_array(ctx_, array_);
551 }
552 
553 TEST_CASE_METHOD(
554     SubarrayPartitionerSparseFx,
555     "SubarrayPartitioner (Sparse): 1D, single-range, unsplittable at once",
556     "[SubarrayPartitioner][sparse][1D][1R][unsplittable_at_once]") {
557   Layout subarray_layout;
558   SubarrayRanges<uint64_t> ranges = {{4, 4}};
559   std::vector<SubarrayRanges<uint64_t>> partitions = {{{4, 4}}};
560   uint64_t budget = 1;
561   std::string attr = "a";
562   bool unsplittable = true;
563 
564   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
565   write_default_1d_array();
566   open_array(ctx_, array_, TILEDB_READ);
567 
568   // subarray: global
569   subarray_layout = Layout::GLOBAL_ORDER;
570   test_subarray_partitioner(
571       subarray_layout, ranges, partitions, attr, budget, unsplittable);
572 
573   // subarray: row
574   subarray_layout = Layout::ROW_MAJOR;
575   test_subarray_partitioner(
576       subarray_layout, ranges, partitions, attr, budget, unsplittable);
577 
578   // subarray: col
579   subarray_layout = Layout::COL_MAJOR;
580   test_subarray_partitioner(
581       subarray_layout, ranges, partitions, attr, budget, unsplittable);
582 
583   // subarray: unordered
584   subarray_layout = Layout::UNORDERED;
585   test_subarray_partitioner(
586       subarray_layout, ranges, partitions, attr, budget, unsplittable);
587 
588   close_array(ctx_, array_);
589 }
590 
591 TEST_CASE_METHOD(
592     SubarrayPartitionerSparseFx,
593     "SubarrayPartitioner (Sparse): 1D, single-range, split multiple",
594     "[SubarrayPartitioner][sparse][1D][1R][split_multiple]") {
595   Layout subarray_layout;
596   SubarrayRanges<uint64_t> ranges = {{2, 18}};
597   std::vector<SubarrayRanges<uint64_t>> partitions = {
598       {{2, 4}}, {{5, 6}}, {{7, 10}}, {{11, 18}}};
599   uint64_t budget = 2 * sizeof(int);
600   std::string attr = "a";
601   bool unsplittable = false;
602 
603   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
604   write_default_1d_array();
605   open_array(ctx_, array_, TILEDB_READ);
606 
607   // subarray: global
608   subarray_layout = Layout::GLOBAL_ORDER;
609   test_subarray_partitioner(
610       subarray_layout, ranges, partitions, attr, budget, unsplittable);
611 
612   // subarray: row
613   subarray_layout = Layout::ROW_MAJOR;
614   test_subarray_partitioner(
615       subarray_layout, ranges, partitions, attr, budget, unsplittable);
616 
617   // subarray: col
618   subarray_layout = Layout::COL_MAJOR;
619   test_subarray_partitioner(
620       subarray_layout, ranges, partitions, attr, budget, unsplittable);
621 
622   // subarray: unordered
623   subarray_layout = Layout::UNORDERED;
624   test_subarray_partitioner(
625       subarray_layout, ranges, partitions, attr, budget, unsplittable);
626 
627   close_array(ctx_, array_);
628 }
629 
630 TEST_CASE_METHOD(
631     SubarrayPartitionerSparseFx,
632     "SubarrayPartitioner (Sparse): 1D, single-range, unsplittable after "
633     "multiple",
634     "[SubarrayPartitioner][sparse][1D][1R][unsplittable_after_multiple]") {
635   Layout subarray_layout;
636   SubarrayRanges<uint64_t> ranges = {{2, 18}};
637   std::vector<SubarrayRanges<uint64_t>> partitions = {{{2, 2}}};
638   uint64_t budget = 1;
639   std::string attr = "a";
640   bool unsplittable = true;
641 
642   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
643   write_default_1d_array();
644   open_array(ctx_, array_, TILEDB_READ);
645 
646   // subarray: global
647   subarray_layout = Layout::GLOBAL_ORDER;
648   test_subarray_partitioner(
649       subarray_layout, ranges, partitions, attr, budget, unsplittable);
650 
651   // subarray: row
652   subarray_layout = Layout::ROW_MAJOR;
653   test_subarray_partitioner(
654       subarray_layout, ranges, partitions, attr, budget, unsplittable);
655 
656   // subarray: col
657   subarray_layout = Layout::COL_MAJOR;
658   test_subarray_partitioner(
659       subarray_layout, ranges, partitions, attr, budget, unsplittable);
660 
661   // subarray: unordered
662   subarray_layout = Layout::UNORDERED;
663   test_subarray_partitioner(
664       subarray_layout, ranges, partitions, attr, budget, unsplittable);
665 
666   close_array(ctx_, array_);
667 }
668 
669 TEST_CASE_METHOD(
670     SubarrayPartitionerSparseFx,
671     "SubarrayPartitioner (Sparse): 1D, single-range, unsplittable but ok after "
672     "budget reset",
673     "[SubarrayPartitioner][sparse][1D][1R][unsplittable_but_then_ok]") {
674   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
675   write_default_1d_array();
676   open_array(ctx_, array_, TILEDB_READ);
677 
678   Subarray subarray;
679   SubarrayRanges<uint64_t> ranges = {{2, 18}};
680   Layout subarray_layout = Layout::GLOBAL_ORDER;
681   std::vector<SubarrayRanges<uint64_t>> partitions = {{{2, 2}}};
682   std::vector<SubarrayRanges<uint64_t>> partitions_after = {
683       {{3, 3}}, {{4, 4}}, {{5, 6}}, {{7, 10}}, {{11, 18}}};
684 
685   create_subarray(array_->array_, ranges, subarray_layout, &subarray);
686 
687   ThreadPool tp;
688   CHECK(tp.init(4).ok());
689   Config config;
690   SubarrayPartitioner subarray_partitioner(
691       &config,
692       subarray,
693       memory_budget_,
694       memory_budget_var_,
695       0,
696       &tp,
697       &g_helper_stats,
698       g_helper_logger());
699   auto st = subarray_partitioner.set_result_budget("a", 100);
700   CHECK(st.ok());
701   st = subarray_partitioner.set_result_budget("b", 1, 1);
702   CHECK(st.ok());
703 
704   check_partitions(subarray_partitioner, partitions, true);
705 
706   st = subarray_partitioner.set_result_budget("b", 100, 100);
707   CHECK(st.ok());
708 
709   check_partitions(subarray_partitioner, partitions_after, false);
710 
711   close_array(ctx_, array_);
712 }
713 
714 TEST_CASE_METHOD(
715     SubarrayPartitionerSparseFx,
716     "SubarrayPartitioner (Sparse): 1D, single-range, float, split multiple",
717     "[SubarrayPartitioner][sparse][1D][1R][float][split_multiple]") {
718   Layout subarray_layout;
719   auto max = std::numeric_limits<float>::max();
720   SubarrayRanges<float> ranges = {{2.0f, 18.0f}};
721   std::vector<SubarrayRanges<float>> partitions = {
722       {{2.0f, 4.0f}},
723       {{std::nextafter(4.0f, max), 6.0f}},
724       {{std::nextafter(6.0f, max), 10.0f}},
725       {{std::nextafter(10.0f, max), 18.0f}}};
726   uint64_t budget = 2 * sizeof(int);
727   std::string attr = "a";
728   bool unsplittable = false;
729 
730   create_default_1d_float_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
731   write_default_1d_float_array();
732   open_array(ctx_, array_, TILEDB_READ);
733 
734   // subarray: global
735   subarray_layout = Layout::GLOBAL_ORDER;
736   test_subarray_partitioner(
737       subarray_layout, ranges, partitions, attr, budget, unsplittable);
738 
739   // subarray: row
740   subarray_layout = Layout::ROW_MAJOR;
741   test_subarray_partitioner(
742       subarray_layout, ranges, partitions, attr, budget, unsplittable);
743 
744   // subarray: col
745   subarray_layout = Layout::COL_MAJOR;
746   test_subarray_partitioner(
747       subarray_layout, ranges, partitions, attr, budget, unsplittable);
748 
749   // subarray: unordered
750   subarray_layout = Layout::UNORDERED;
751   test_subarray_partitioner(
752       subarray_layout, ranges, partitions, attr, budget, unsplittable);
753 
754   close_array(ctx_, array_);
755 }
756 
757 TEST_CASE_METHOD(
758     SubarrayPartitionerSparseFx,
759     "SubarrayPartitioner (Sparse): 1D, single-range, float, unsplittable after "
760     "multiple",
761     "[SubarrayPartitioner][sparse][1D][1R][float][unsplittable_after_"
762     "multiple]") {
763   Layout subarray_layout;
764   SubarrayRanges<float> ranges = {{2.0f, 18.0f}};
765   std::vector<SubarrayRanges<float>> partitions = {{{2.0f, 2.0f}}};
766   uint64_t budget = 0;
767   std::string attr = "a";
768   bool unsplittable = true;
769 
770   create_default_1d_float_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
771   write_default_1d_float_array();
772   open_array(ctx_, array_, TILEDB_READ);
773 
774   // subarray: global
775   subarray_layout = Layout::GLOBAL_ORDER;
776   test_subarray_partitioner(
777       subarray_layout, ranges, partitions, attr, budget, unsplittable);
778 
779   // subarray: row
780   subarray_layout = Layout::ROW_MAJOR;
781   test_subarray_partitioner(
782       subarray_layout, ranges, partitions, attr, budget, unsplittable);
783 
784   // subarray: col
785   subarray_layout = Layout::COL_MAJOR;
786   test_subarray_partitioner(
787       subarray_layout, ranges, partitions, attr, budget, unsplittable);
788 
789   // subarray: unordered
790   subarray_layout = Layout::UNORDERED;
791   test_subarray_partitioner(
792       subarray_layout, ranges, partitions, attr, budget, unsplittable);
793 
794   close_array(ctx_, array_);
795 }
796 
797 TEST_CASE_METHOD(
798     SubarrayPartitionerSparseFx,
799     "SubarrayPartitioner (Sparse): 1D, single-range, float, whole subarray "
800     "fits",
801     "[SubarrayPartitioner][sparse][1D][1R][float][whole_subarray_fits]") {
802   Layout subarray_layout;
803   SubarrayRanges<float> ranges = {{2.0f, 18.0f}};
804   std::vector<SubarrayRanges<float>> partitions = {{{2.0f, 18.0f}}};
805   uint64_t budget = 100000;
806   std::string attr = "a";
807   bool unsplittable = false;
808 
809   create_default_1d_float_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
810   write_default_1d_float_array();
811   open_array(ctx_, array_, TILEDB_READ);
812 
813   // subarray: global
814   subarray_layout = Layout::GLOBAL_ORDER;
815   test_subarray_partitioner(
816       subarray_layout, ranges, partitions, attr, budget, unsplittable);
817 
818   // subarray: row
819   subarray_layout = Layout::ROW_MAJOR;
820   test_subarray_partitioner(
821       subarray_layout, ranges, partitions, attr, budget, unsplittable);
822 
823   // subarray: col
824   subarray_layout = Layout::COL_MAJOR;
825   test_subarray_partitioner(
826       subarray_layout, ranges, partitions, attr, budget, unsplittable);
827 
828   // subarray: unordered
829   subarray_layout = Layout::UNORDERED;
830   test_subarray_partitioner(
831       subarray_layout, ranges, partitions, attr, budget, unsplittable);
832 
833   close_array(ctx_, array_);
834 }
835 
836 TEST_CASE_METHOD(
837     SubarrayPartitionerSparseFx,
838     "SubarrayPartitioner (Sparse): 1D, single-range, memory budget",
839     "[SubarrayPartitioner][sparse][1D][1R][memory_budget]") {
840   Layout subarray_layout;
841   SubarrayRanges<uint64_t> ranges = {};
842   std::vector<SubarrayRanges<uint64_t>> partitions = {};
843   uint64_t budget = 16;
844   uint64_t budget_var = 100000;
845   bool unsplittable = false;
846 
847   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
848   write_default_1d_array_2();
849   open_array(ctx_, array_, TILEDB_READ);
850 
851   // subarray: global
852   subarray_layout = Layout::GLOBAL_ORDER;
853   partitions = {
854       {{1, 3}},
855       {{4, 4}},
856       {{5, 5}},
857       {{6, 10}},
858       {{11, 20}},
859       {{21, 30}},
860       {{31, 40}},
861       {{41, 100}},
862   };
863   test_subarray_partitioner(
864       subarray_layout, ranges, partitions, budget, budget_var, unsplittable);
865 
866   // subarray: row
867   subarray_layout = Layout::ROW_MAJOR;
868   partitions = {
869       {{1, 4}},
870       {{5, 7}},
871       {{8, 10}},
872       {{11, 13}},
873       {{14, 19}},
874       {{20, 25}},
875       {{26, 32}},
876       {{33, 38}},
877       {{39, 50}},
878       {{51, 100}},
879   };
880   test_subarray_partitioner(
881       subarray_layout, ranges, partitions, budget, budget_var, unsplittable);
882 
883   // subarray: col
884   subarray_layout = Layout::COL_MAJOR;
885   test_subarray_partitioner(
886       subarray_layout, ranges, partitions, budget, budget_var, unsplittable);
887 
888   // subarray: unordered
889   subarray_layout = Layout::UNORDERED;
890   test_subarray_partitioner(
891       subarray_layout, ranges, partitions, budget, budget_var, unsplittable);
892 
893   close_array(ctx_, array_);
894 }
895 
896 TEST_CASE_METHOD(
897     SubarrayPartitionerSparseFx,
898     "SubarrayPartitioner (Sparse): 1D, multi-range, whole subarray fits",
899     "[SubarrayPartitioner][sparse][1D][MR][whole_subarray_fits]") {
900   Layout subarray_layout;
901   SubarrayRanges<uint64_t> ranges = {{5, 10, 25, 27, 33, 50}};
902   std::vector<SubarrayRanges<uint64_t>> partitions = {
903       {{5, 10, 25, 27, 33, 50}}};
904   uint64_t budget = 100000;
905   std::string attr = "a";
906   bool unsplittable = false;
907 
908   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
909   write_default_1d_array_2();
910   open_array(ctx_, array_, TILEDB_READ);
911 
912   // subarray: row
913   subarray_layout = Layout::ROW_MAJOR;
914   test_subarray_partitioner(
915       subarray_layout, ranges, partitions, attr, budget, unsplittable);
916 
917   // subarray: col
918   subarray_layout = Layout::COL_MAJOR;
919   test_subarray_partitioner(
920       subarray_layout, ranges, partitions, attr, budget, unsplittable);
921 
922   // subarray: unordered
923   subarray_layout = Layout::UNORDERED;
924   test_subarray_partitioner(
925       subarray_layout, ranges, partitions, attr, budget, unsplittable);
926 
927   close_array(ctx_, array_);
928 }
929 
930 TEST_CASE_METHOD(
931     SubarrayPartitionerSparseFx,
932     "SubarrayPartitioner (Sparse): 1D, multi-range, split once",
933     "[SubarrayPartitioner][sparse][1D][MR][split_once]") {
934   Layout subarray_layout;
935   SubarrayRanges<uint64_t> ranges = {{5, 10, 25, 27, 33, 50}};
936   std::vector<SubarrayRanges<uint64_t>> partitions = {{{5, 10, 25, 27}},
937                                                       {{33, 50}}};
938   uint64_t budget = 4 * sizeof(int);
939   std::string attr = "a";
940   bool unsplittable = false;
941 
942   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
943   write_default_1d_array_2();
944   open_array(ctx_, array_, TILEDB_READ);
945 
946   // subarray: row
947   subarray_layout = Layout::ROW_MAJOR;
948   test_subarray_partitioner(
949       subarray_layout, ranges, partitions, attr, budget, unsplittable);
950 
951   // subarray: col
952   subarray_layout = Layout::COL_MAJOR;
953   test_subarray_partitioner(
954       subarray_layout, ranges, partitions, attr, budget, unsplittable);
955 
956   // subarray: unordered
957   subarray_layout = Layout::UNORDERED;
958   test_subarray_partitioner(
959       subarray_layout, ranges, partitions, attr, budget, unsplittable);
960 
961   close_array(ctx_, array_);
962 }
963 
964 TEST_CASE_METHOD(
965     SubarrayPartitionerSparseFx,
966     "SubarrayPartitioner (Sparse): 1D, multi-range, split multiple",
967     "[SubarrayPartitioner][sparse][1D][MR][split_multiple]") {
968   Layout subarray_layout;
969   SubarrayRanges<uint64_t> ranges = {{5, 10, 25, 27, 33, 50}};
970   std::vector<SubarrayRanges<uint64_t>> partitions = {
971       {{5, 10}}, {{25, 27}}, {{33, 50}}};
972   uint64_t budget = 2 * sizeof(int);
973   std::string attr = "a";
974   bool unsplittable = false;
975 
976   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
977   write_default_1d_array_2();
978   open_array(ctx_, array_, TILEDB_READ);
979 
980   // subarray: row
981   subarray_layout = Layout::ROW_MAJOR;
982   test_subarray_partitioner(
983       subarray_layout, ranges, partitions, attr, budget, unsplittable);
984 
985   // subarray: col
986   subarray_layout = Layout::COL_MAJOR;
987   test_subarray_partitioner(
988       subarray_layout, ranges, partitions, attr, budget, unsplittable);
989 
990   // subarray: unordered
991   subarray_layout = Layout::UNORDERED;
992   test_subarray_partitioner(
993       subarray_layout, ranges, partitions, attr, budget, unsplittable);
994 
995   close_array(ctx_, array_);
996 }
997 
998 TEST_CASE_METHOD(
999     SubarrayPartitionerSparseFx,
1000     "SubarrayPartitioner (Sparse): 1D, multi-range, split multiple finer",
1001     "[SubarrayPartitioner][sparse][1D][MR][split_multiple_finer]") {
1002   Layout subarray_layout;
1003   SubarrayRanges<uint64_t> ranges = {{5, 10, 25, 27, 33, 40}};
1004   std::vector<SubarrayRanges<uint64_t>> partitions = {
1005       {{5, 7}},
1006       {{8, 10}},
1007       {{25, 26}},
1008       {{27, 27}},
1009       {{33, 36}},
1010       {{37, 40}},
1011   };
1012   uint64_t budget = 2 * sizeof(int) - 1;
1013   std::string attr = "a";
1014   bool unsplittable = false;
1015 
1016   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1017   write_default_1d_array_2();
1018   open_array(ctx_, array_, TILEDB_READ);
1019 
1020   // subarray: row
1021   subarray_layout = Layout::ROW_MAJOR;
1022   test_subarray_partitioner(
1023       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1024 
1025   // subarray: col
1026   subarray_layout = Layout::COL_MAJOR;
1027   test_subarray_partitioner(
1028       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1029 
1030   // subarray: unordered
1031   subarray_layout = Layout::UNORDERED;
1032   test_subarray_partitioner(
1033       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1034 
1035   close_array(ctx_, array_);
1036 }
1037 
1038 TEST_CASE_METHOD(
1039     SubarrayPartitionerSparseFx,
1040     "SubarrayPartitioner (Sparse): 1D, multi-range, unsplittable",
1041     "[SubarrayPartitioner][sparse][1D][MR][unsplittable]") {
1042   Layout subarray_layout;
1043   SubarrayRanges<uint64_t> ranges = {{5, 10, 25, 27, 33, 40}};
1044   std::vector<SubarrayRanges<uint64_t>> partitions = {{{5, 5}}};
1045   uint64_t budget = 1;
1046   std::string attr = "a";
1047   bool unsplittable = true;
1048 
1049   create_default_1d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1050   write_default_1d_array_2();
1051   open_array(ctx_, array_, TILEDB_READ);
1052 
1053   // subarray: row
1054   subarray_layout = Layout::ROW_MAJOR;
1055   test_subarray_partitioner(
1056       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1057 
1058   // subarray: col
1059   subarray_layout = Layout::COL_MAJOR;
1060   test_subarray_partitioner(
1061       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1062 
1063   // subarray: unordered
1064   subarray_layout = Layout::UNORDERED;
1065   test_subarray_partitioner(
1066       subarray_layout, ranges, partitions, attr, budget, unsplittable);
1067 
1068   close_array(ctx_, array_);
1069 }
1070 
1071 TEST_CASE_METHOD(
1072     SubarrayPartitionerSparseFx,
1073     "SubarrayPartitioner (Sparse): 2D, single-range, whole subarray fits",
1074     "[SubarrayPartitioner][sparse][2D][1R][whole_subarray_fits]") {
1075   Layout subarray_layout;
1076   SubarrayRanges<uint64_t> ranges = {{2, 10}, {2, 10}};
1077   std::vector<SubarrayRanges<uint64_t>> partitions = {{{2, 10}, {2, 10}}};
1078   uint64_t budget = 1000 * sizeof(uint64_t);
1079   std::string attr = TILEDB_COORDS;
1080   bool unsplittable = false;
1081 
1082   SECTION("# tile: row, cell: row") {
1083     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1084     write_default_2d_array();
1085     open_array(ctx_, array_, TILEDB_READ);
1086 
1087     // subarray: global
1088     subarray_layout = Layout::GLOBAL_ORDER;
1089     test_subarray_partitioner(
1090         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1091 
1092     // subarray: row
1093     subarray_layout = Layout::ROW_MAJOR;
1094     test_subarray_partitioner(
1095         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1096 
1097     // subarray: col
1098     subarray_layout = Layout::COL_MAJOR;
1099     test_subarray_partitioner(
1100         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1101 
1102     // subarray: unordered
1103     subarray_layout = Layout::UNORDERED;
1104     test_subarray_partitioner(
1105         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1106   }
1107 
1108   SECTION("# tile: row, cell: col") {
1109     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1110     write_default_2d_array();
1111     open_array(ctx_, array_, TILEDB_READ);
1112 
1113     // subarray: global
1114     subarray_layout = Layout::GLOBAL_ORDER;
1115     test_subarray_partitioner(
1116         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1117 
1118     // subarray: row
1119     subarray_layout = Layout::ROW_MAJOR;
1120     test_subarray_partitioner(
1121         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1122 
1123     // subarray: col
1124     subarray_layout = Layout::COL_MAJOR;
1125     test_subarray_partitioner(
1126         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1127 
1128     // subarray: unordered
1129     subarray_layout = Layout::UNORDERED;
1130     test_subarray_partitioner(
1131         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1132   }
1133 
1134   SECTION("# tile: col, cell: row") {
1135     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1136     write_default_2d_array();
1137     open_array(ctx_, array_, TILEDB_READ);
1138 
1139     // subarray: global
1140     subarray_layout = Layout::GLOBAL_ORDER;
1141     test_subarray_partitioner(
1142         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1143 
1144     // subarray: row
1145     subarray_layout = Layout::ROW_MAJOR;
1146     test_subarray_partitioner(
1147         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1148 
1149     // subarray: col
1150     subarray_layout = Layout::COL_MAJOR;
1151     test_subarray_partitioner(
1152         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1153 
1154     // subarray: unordered
1155     subarray_layout = Layout::UNORDERED;
1156     test_subarray_partitioner(
1157         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1158   }
1159 
1160   SECTION("# tile: col, cell: col") {
1161     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1162     write_default_2d_array();
1163     open_array(ctx_, array_, TILEDB_READ);
1164 
1165     // subarray: global
1166     subarray_layout = Layout::GLOBAL_ORDER;
1167     test_subarray_partitioner(
1168         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1169 
1170     // subarray: row
1171     subarray_layout = Layout::ROW_MAJOR;
1172     test_subarray_partitioner(
1173         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1174 
1175     // subarray: col
1176     subarray_layout = Layout::COL_MAJOR;
1177     test_subarray_partitioner(
1178         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1179 
1180     // subarray: unordered
1181     subarray_layout = Layout::UNORDERED;
1182     test_subarray_partitioner(
1183         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1184   }
1185 
1186   close_array(ctx_, array_);
1187 }
1188 
1189 TEST_CASE_METHOD(
1190     SubarrayPartitionerSparseFx,
1191     "SubarrayPartitioner (Sparse): 2D, single-range, split multiple",
1192     "[SubarrayPartitioner][sparse][2D][1R][split_multiple]") {
1193   Layout subarray_layout;
1194   SubarrayRanges<uint64_t> ranges = {{3, 4}, {1, 10}};
1195   std::vector<SubarrayRanges<uint64_t>> partitions = {};
1196   std::string attr = TILEDB_COORDS;
1197   uint64_t budget = 2 * sizeof(uint64_t);
1198 
1199   SECTION("# tile: row, cell: row") {
1200     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1201     write_default_2d_array();
1202     open_array(ctx_, array_, TILEDB_READ);
1203 
1204     // subarray: global
1205     subarray_layout = Layout::GLOBAL_ORDER;
1206     partitions = {
1207         {{3, 3}, {1, 2}},
1208         {{4, 4}, {1, 2}},
1209         {{3, 4}, {3, 4}},
1210         {{3, 4}, {5, 6}},
1211         {{3, 3}, {7, 8}},
1212         {{4, 4}, {7, 8}},
1213         {{3, 4}, {9, 10}},
1214     };
1215     test_subarray_partitioner(
1216         subarray_layout, ranges, partitions, attr, budget);
1217 
1218     // subarray: row
1219     subarray_layout = Layout::ROW_MAJOR;
1220     partitions = {
1221         {{3, 3}, {1, 5}},
1222         {{3, 3}, {6, 10}},
1223         {{4, 4}, {1, 5}},
1224         {{4, 4}, {6, 10}},
1225     };
1226     test_subarray_partitioner(
1227         subarray_layout, ranges, partitions, attr, budget);
1228 
1229     // subarray: col
1230     subarray_layout = Layout::COL_MAJOR;
1231     partitions = {
1232         {{3, 4}, {1, 1}},
1233         {{3, 4}, {2, 2}},
1234         {{3, 4}, {3, 3}},
1235         {{3, 4}, {4, 5}},
1236         {{3, 4}, {6, 7}},
1237         {{3, 4}, {8, 8}},
1238         {{3, 4}, {9, 10}},
1239     };
1240     test_subarray_partitioner(
1241         subarray_layout, ranges, partitions, attr, budget);
1242 
1243     // subarray: unordered
1244     subarray_layout = Layout::UNORDERED;
1245     partitions = {
1246         {{3, 3}, {1, 5}},
1247         {{3, 3}, {6, 10}},
1248         {{4, 4}, {1, 5}},
1249         {{4, 4}, {6, 10}},
1250     };
1251     test_subarray_partitioner(
1252         subarray_layout, ranges, partitions, attr, budget);
1253   }
1254 
1255   SECTION("# tile: row, cell: col") {
1256     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1257     write_default_2d_array();
1258     open_array(ctx_, array_, TILEDB_READ);
1259 
1260     // subarray: global
1261     subarray_layout = Layout::GLOBAL_ORDER;
1262     partitions = {
1263         {{3, 4}, {1, 1}},
1264         {{3, 4}, {2, 2}},
1265         {{3, 4}, {3, 4}},
1266         {{3, 4}, {5, 6}},
1267         {{3, 4}, {7, 7}},
1268         {{3, 4}, {8, 8}},
1269         {{3, 4}, {9, 10}},
1270     };
1271     test_subarray_partitioner(
1272         subarray_layout, ranges, partitions, attr, budget);
1273 
1274     // subarray: row
1275     subarray_layout = Layout::ROW_MAJOR;
1276     partitions = {
1277         {{3, 3}, {1, 5}},
1278         {{3, 3}, {6, 10}},
1279         {{4, 4}, {1, 5}},
1280         {{4, 4}, {6, 10}},
1281     };
1282     test_subarray_partitioner(
1283         subarray_layout, ranges, partitions, attr, budget);
1284 
1285     // subarray: col
1286     subarray_layout = Layout::COL_MAJOR;
1287     partitions = {
1288         {{3, 4}, {1, 1}},
1289         {{3, 4}, {2, 2}},
1290         {{3, 4}, {3, 3}},
1291         {{3, 4}, {4, 5}},
1292         {{3, 4}, {6, 7}},
1293         {{3, 4}, {8, 8}},
1294         {{3, 4}, {9, 10}},
1295     };
1296     test_subarray_partitioner(
1297         subarray_layout, ranges, partitions, attr, budget);
1298 
1299     // subarray: unordered
1300     subarray_layout = Layout::UNORDERED;
1301     partitions = {
1302         {{3, 4}, {1, 1}},
1303         {{3, 4}, {2, 2}},
1304         {{3, 4}, {3, 3}},
1305         {{3, 4}, {4, 5}},
1306         {{3, 4}, {6, 7}},
1307         {{3, 4}, {8, 8}},
1308         {{3, 4}, {9, 10}},
1309     };
1310     test_subarray_partitioner(
1311         subarray_layout, ranges, partitions, attr, budget);
1312   }
1313 
1314   SECTION("# tile: col, cell: row") {
1315     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1316     write_default_2d_array();
1317     open_array(ctx_, array_, TILEDB_READ);
1318 
1319     // subarray: global
1320     subarray_layout = Layout::GLOBAL_ORDER;
1321     partitions = {
1322         {{3, 4}, {1, 2}},
1323         {{3, 4}, {3, 4}},
1324         {{3, 4}, {5, 6}},
1325         {{3, 3}, {7, 8}},
1326         {{4, 4}, {7, 8}},
1327         {{3, 4}, {9, 10}},
1328     };
1329     test_subarray_partitioner(
1330         subarray_layout, ranges, partitions, attr, budget);
1331 
1332     // subarray: row
1333     subarray_layout = Layout::ROW_MAJOR;
1334     partitions = {
1335         {{3, 3}, {1, 3}},
1336         {{3, 3}, {4, 5}},
1337         {{3, 3}, {6, 10}},
1338         {{4, 4}, {1, 5}},
1339         {{4, 4}, {6, 10}},
1340     };
1341     test_subarray_partitioner(
1342         subarray_layout, ranges, partitions, attr, budget);
1343 
1344     // subarray: col
1345     subarray_layout = Layout::COL_MAJOR;
1346     partitions = {
1347         {{3, 4}, {1, 2}},
1348         {{3, 4}, {3, 3}},
1349         {{3, 4}, {4, 5}},
1350         {{3, 4}, {6, 7}},
1351         {{3, 4}, {8, 8}},
1352         {{3, 4}, {9, 10}},
1353     };
1354     test_subarray_partitioner(
1355         subarray_layout, ranges, partitions, attr, budget);
1356 
1357     // subarray: unordered
1358     subarray_layout = Layout::UNORDERED;
1359     partitions = {
1360         {{3, 3}, {1, 3}},
1361         {{3, 3}, {4, 5}},
1362         {{3, 3}, {6, 10}},
1363         {{4, 4}, {1, 5}},
1364         {{4, 4}, {6, 10}},
1365     };
1366     test_subarray_partitioner(
1367         subarray_layout, ranges, partitions, attr, budget);
1368   }
1369 
1370   SECTION("# tile: col, cell: col") {
1371     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1372     write_default_2d_array();
1373     open_array(ctx_, array_, TILEDB_READ);
1374 
1375     // subarray: global
1376     subarray_layout = Layout::GLOBAL_ORDER;
1377     partitions = {
1378         {{3, 4}, {1, 2}},
1379         {{3, 4}, {3, 4}},
1380         {{3, 4}, {5, 6}},
1381         {{3, 4}, {7, 7}},
1382         {{3, 4}, {8, 8}},
1383         {{3, 4}, {9, 10}},
1384     };
1385     test_subarray_partitioner(
1386         subarray_layout, ranges, partitions, attr, budget);
1387 
1388     // subarray: row
1389     subarray_layout = Layout::ROW_MAJOR;
1390     partitions = {
1391         {{3, 3}, {1, 3}},
1392         {{3, 3}, {4, 5}},
1393         {{3, 3}, {6, 10}},
1394         {{4, 4}, {1, 5}},
1395         {{4, 4}, {6, 10}},
1396     };
1397     test_subarray_partitioner(
1398         subarray_layout, ranges, partitions, attr, budget);
1399 
1400     // subarray: col
1401     subarray_layout = Layout::COL_MAJOR;
1402     partitions = {
1403         {{3, 4}, {1, 2}},
1404         {{3, 4}, {3, 3}},
1405         {{3, 4}, {4, 5}},
1406         {{3, 4}, {6, 7}},
1407         {{3, 4}, {8, 8}},
1408         {{3, 4}, {9, 10}},
1409     };
1410     test_subarray_partitioner(
1411         subarray_layout, ranges, partitions, attr, budget);
1412 
1413     // subarray: unordered
1414     subarray_layout = Layout::UNORDERED;
1415     partitions = {
1416         {{3, 4}, {1, 2}},
1417         {{3, 4}, {3, 3}},
1418         {{3, 4}, {4, 5}},
1419         {{3, 4}, {6, 7}},
1420         {{3, 4}, {8, 8}},
1421         {{3, 4}, {9, 10}},
1422     };
1423     test_subarray_partitioner(
1424         subarray_layout, ranges, partitions, attr, budget);
1425   }
1426 
1427   close_array(ctx_, array_);
1428 }
1429 
1430 TEST_CASE_METHOD(
1431     SubarrayPartitionerSparseFx,
1432     "SubarrayPartitioner (Sparse): 2D, single-range, unsplittable",
1433     "[SubarrayPartitioner][sparse][2D][1R][unsplittable]") {
1434   Layout subarray_layout;
1435   SubarrayRanges<uint64_t> ranges = {{2, 10}, {2, 10}};
1436   std::vector<SubarrayRanges<uint64_t>> partitions = {{{2, 2}, {2, 2}}};
1437   uint64_t budget = 0;
1438   std::string attr = TILEDB_COORDS;
1439   bool unsplittable = true;
1440 
1441   SECTION("# tile: row, cell: row") {
1442     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1443     write_default_2d_array();
1444     open_array(ctx_, array_, TILEDB_READ);
1445 
1446     // subarray: global
1447     subarray_layout = Layout::GLOBAL_ORDER;
1448     test_subarray_partitioner(
1449         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1450 
1451     // subarray: row
1452     subarray_layout = Layout::ROW_MAJOR;
1453     test_subarray_partitioner(
1454         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1455 
1456     // subarray: col
1457     subarray_layout = Layout::COL_MAJOR;
1458     test_subarray_partitioner(
1459         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1460 
1461     // subarray: unordered
1462     subarray_layout = Layout::UNORDERED;
1463     test_subarray_partitioner(
1464         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1465   }
1466 
1467   SECTION("# tile: row, cell: col") {
1468     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1469     write_default_2d_array();
1470     open_array(ctx_, array_, TILEDB_READ);
1471 
1472     // subarray: global
1473     subarray_layout = Layout::GLOBAL_ORDER;
1474     test_subarray_partitioner(
1475         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1476 
1477     // subarray: row
1478     subarray_layout = Layout::ROW_MAJOR;
1479     test_subarray_partitioner(
1480         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1481 
1482     // subarray: col
1483     subarray_layout = Layout::COL_MAJOR;
1484     test_subarray_partitioner(
1485         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1486 
1487     // subarray: unordered
1488     subarray_layout = Layout::UNORDERED;
1489     test_subarray_partitioner(
1490         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1491   }
1492 
1493   SECTION("# tile: col, cell: row") {
1494     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1495     write_default_2d_array();
1496     open_array(ctx_, array_, TILEDB_READ);
1497 
1498     // subarray: global
1499     subarray_layout = Layout::GLOBAL_ORDER;
1500     test_subarray_partitioner(
1501         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1502 
1503     // subarray: row
1504     subarray_layout = Layout::ROW_MAJOR;
1505     test_subarray_partitioner(
1506         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1507 
1508     // subarray: col
1509     subarray_layout = Layout::COL_MAJOR;
1510     test_subarray_partitioner(
1511         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1512 
1513     // subarray: unordered
1514     subarray_layout = Layout::UNORDERED;
1515     test_subarray_partitioner(
1516         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1517   }
1518 
1519   SECTION("# tile: col, cell: col") {
1520     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1521     write_default_2d_array();
1522     open_array(ctx_, array_, TILEDB_READ);
1523 
1524     // subarray: global
1525     subarray_layout = Layout::GLOBAL_ORDER;
1526     test_subarray_partitioner(
1527         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1528 
1529     // subarray: row
1530     subarray_layout = Layout::ROW_MAJOR;
1531     test_subarray_partitioner(
1532         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1533 
1534     // subarray: col
1535     subarray_layout = Layout::COL_MAJOR;
1536     test_subarray_partitioner(
1537         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1538 
1539     // subarray: unordered
1540     subarray_layout = Layout::UNORDERED;
1541     test_subarray_partitioner(
1542         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1543   }
1544 
1545   close_array(ctx_, array_);
1546 }
1547 
1548 TEST_CASE_METHOD(
1549     SubarrayPartitionerSparseFx,
1550     "SubarrayPartitioner (Sparse): 2D, multi-range, whole subarray fits",
1551     "[SubarrayPartitioner][sparse][2D][MR][whole_subarray_fits]") {
1552   Layout subarray_layout;
1553   SubarrayRanges<uint64_t> ranges = {{1, 2, 3, 3, 4, 4}, {2, 3, 4, 5}};
1554   std::vector<SubarrayRanges<uint64_t>> partitions = {
1555       {{1, 2, 3, 3, 4, 4}, {2, 3, 4, 5}}};
1556   uint64_t budget = 10000;
1557   std::string attr = "a";
1558 
1559   SECTION("# tile: row, cell: row") {
1560     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1561     write_default_2d_array();
1562     open_array(ctx_, array_, TILEDB_READ);
1563 
1564     // subarray: row
1565     subarray_layout = Layout::ROW_MAJOR;
1566     test_subarray_partitioner(
1567         subarray_layout, ranges, partitions, attr, budget);
1568 
1569     // subarray: col
1570     subarray_layout = Layout::COL_MAJOR;
1571     test_subarray_partitioner(
1572         subarray_layout, ranges, partitions, attr, budget);
1573 
1574     // subarray: unordered
1575     subarray_layout = Layout::UNORDERED;
1576     test_subarray_partitioner(
1577         subarray_layout, ranges, partitions, attr, budget);
1578   }
1579 
1580   SECTION("# tile: row, cell: col") {
1581     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1582     write_default_2d_array();
1583     open_array(ctx_, array_, TILEDB_READ);
1584 
1585     // subarray: row
1586     subarray_layout = Layout::ROW_MAJOR;
1587     test_subarray_partitioner(
1588         subarray_layout, ranges, partitions, attr, budget);
1589 
1590     // subarray: col
1591     subarray_layout = Layout::COL_MAJOR;
1592     test_subarray_partitioner(
1593         subarray_layout, ranges, partitions, attr, budget);
1594 
1595     // subarray: unordered
1596     subarray_layout = Layout::UNORDERED;
1597     test_subarray_partitioner(
1598         subarray_layout, ranges, partitions, attr, budget);
1599   }
1600 
1601   SECTION("# tile: col, cell: row") {
1602     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1603     write_default_2d_array();
1604     open_array(ctx_, array_, TILEDB_READ);
1605 
1606     // subarray: row
1607     subarray_layout = Layout::ROW_MAJOR;
1608     test_subarray_partitioner(
1609         subarray_layout, ranges, partitions, attr, budget);
1610 
1611     // subarray: col
1612     subarray_layout = Layout::COL_MAJOR;
1613     test_subarray_partitioner(
1614         subarray_layout, ranges, partitions, attr, budget);
1615 
1616     // subarray: unordered
1617     subarray_layout = Layout::UNORDERED;
1618     test_subarray_partitioner(
1619         subarray_layout, ranges, partitions, attr, budget);
1620   }
1621 
1622   SECTION("# tile: col, cell: col") {
1623     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1624     write_default_2d_array();
1625     open_array(ctx_, array_, TILEDB_READ);
1626 
1627     // subarray: row
1628     subarray_layout = Layout::ROW_MAJOR;
1629     test_subarray_partitioner(
1630         subarray_layout, ranges, partitions, attr, budget);
1631 
1632     // subarray: col
1633     subarray_layout = Layout::COL_MAJOR;
1634     test_subarray_partitioner(
1635         subarray_layout, ranges, partitions, attr, budget);
1636 
1637     // subarray: unordered
1638     subarray_layout = Layout::UNORDERED;
1639     test_subarray_partitioner(
1640         subarray_layout, ranges, partitions, attr, budget);
1641   }
1642 
1643   close_array(ctx_, array_);
1644 }
1645 
1646 TEST_CASE_METHOD(
1647     SubarrayPartitionerSparseFx,
1648     "SubarrayPartitioner (Sparse): 2D, multi-range, split once",
1649     "[SubarrayPartitioner][sparse][2D][MR][split_once]") {
1650   Layout subarray_layout;
1651   std::vector<SubarrayRanges<uint64_t>> partitions = {};
1652   SubarrayRanges<uint64_t> ranges;
1653   std::string attr = "a";
1654   uint64_t budget = 4 * sizeof(int);
1655 
1656   SECTION("# tile: row, cell: row") {
1657     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1658     write_default_2d_array();
1659     open_array(ctx_, array_, TILEDB_READ);
1660 
1661     // subarray: row
1662     subarray_layout = Layout::ROW_MAJOR;
1663     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1664     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1665     test_subarray_partitioner(
1666         subarray_layout, ranges, partitions, attr, budget);
1667 
1668     // subarray: col
1669     subarray_layout = Layout::COL_MAJOR;
1670     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1671     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1672     test_subarray_partitioner(
1673         subarray_layout, ranges, partitions, attr, budget);
1674 
1675     // subarray: unordered
1676     subarray_layout = Layout::UNORDERED;
1677     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1678     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1679     test_subarray_partitioner(
1680         subarray_layout, ranges, partitions, attr, budget);
1681   }
1682 
1683   SECTION("# tile: row, cell: col") {
1684     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1685     write_default_2d_array();
1686     open_array(ctx_, array_, TILEDB_READ);
1687 
1688     // subarray: row
1689     subarray_layout = Layout::ROW_MAJOR;
1690     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1691     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1692     test_subarray_partitioner(
1693         subarray_layout, ranges, partitions, attr, budget);
1694 
1695     // subarray: col
1696     subarray_layout = Layout::COL_MAJOR;
1697     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1698     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1699     test_subarray_partitioner(
1700         subarray_layout, ranges, partitions, attr, budget);
1701 
1702     // subarray: unordered
1703     subarray_layout = Layout::UNORDERED;
1704     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1705     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1706     test_subarray_partitioner(
1707         subarray_layout, ranges, partitions, attr, budget);
1708   }
1709 
1710   SECTION("# tile: col, cell: row") {
1711     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1712     write_default_2d_array();
1713     open_array(ctx_, array_, TILEDB_READ);
1714 
1715     // subarray: row
1716     subarray_layout = Layout::ROW_MAJOR;
1717     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1718     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1719     test_subarray_partitioner(
1720         subarray_layout, ranges, partitions, attr, budget);
1721 
1722     // subarray: col
1723     subarray_layout = Layout::COL_MAJOR;
1724     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1725     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1726     test_subarray_partitioner(
1727         subarray_layout, ranges, partitions, attr, budget);
1728 
1729     // subarray: unordered
1730     subarray_layout = Layout::UNORDERED;
1731     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1732     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1733     test_subarray_partitioner(
1734         subarray_layout, ranges, partitions, attr, budget);
1735   }
1736 
1737   SECTION("# tile: col, cell: col") {
1738     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1739     write_default_2d_array();
1740     open_array(ctx_, array_, TILEDB_READ);
1741 
1742     // subarray: row
1743     subarray_layout = Layout::ROW_MAJOR;
1744     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1745     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1746     test_subarray_partitioner(
1747         subarray_layout, ranges, partitions, attr, budget);
1748 
1749     // subarray: col
1750     subarray_layout = Layout::COL_MAJOR;
1751     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1752     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1753     test_subarray_partitioner(
1754         subarray_layout, ranges, partitions, attr, budget);
1755 
1756     // subarray: unordered
1757     subarray_layout = Layout::UNORDERED;
1758     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1759     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1760     test_subarray_partitioner(
1761         subarray_layout, ranges, partitions, attr, budget);
1762   }
1763 
1764   close_array(ctx_, array_);
1765 }
1766 
1767 /**
1768  * Tests subarray range calibration, such that the ranges involved
1769  * in the next partition fall in the same slab (or fall in a single
1770  * slab in case the subarray layout is UNORDERED).
1771  */
1772 TEST_CASE_METHOD(
1773     SubarrayPartitionerSparseFx,
1774     "SubarrayPartitioner (Sparse): 2D, multi-range, calibrate",
1775     "[SubarrayPartitioner][sparse][2D][MR][calibrate]") {
1776   Layout subarray_layout;
1777   std::vector<SubarrayRanges<uint64_t>> partitions = {};
1778   SubarrayRanges<uint64_t> ranges;
1779   std::string attr = "a";
1780   uint64_t budget = 5 * sizeof(int);
1781 
1782   SECTION("# tile: row, cell: row") {
1783     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1784     write_default_2d_array();
1785     open_array(ctx_, array_, TILEDB_READ);
1786 
1787     // subarray: row
1788     subarray_layout = Layout::ROW_MAJOR;
1789     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1790     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1791     test_subarray_partitioner(
1792         subarray_layout, ranges, partitions, attr, budget);
1793 
1794     // subarray: col
1795     subarray_layout = Layout::COL_MAJOR;
1796     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1797     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1798     test_subarray_partitioner(
1799         subarray_layout, ranges, partitions, attr, budget);
1800 
1801     // subarray: unordered
1802     subarray_layout = Layout::UNORDERED;
1803     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1804     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1805     test_subarray_partitioner(
1806         subarray_layout, ranges, partitions, attr, budget);
1807   }
1808 
1809   SECTION("# tile: row, cell: col") {
1810     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1811     write_default_2d_array();
1812     open_array(ctx_, array_, TILEDB_READ);
1813 
1814     // subarray: row
1815     subarray_layout = Layout::ROW_MAJOR;
1816     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1817     partitions = {{{1, 2, 3, 3}, {2, 5, 6, 9}}, {{4, 4}, {2, 5, 6, 9}}};
1818     test_subarray_partitioner(
1819         subarray_layout, ranges, partitions, attr, budget);
1820 
1821     // subarray: col
1822     subarray_layout = Layout::COL_MAJOR;
1823     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1824     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1825     test_subarray_partitioner(
1826         subarray_layout, ranges, partitions, attr, budget);
1827 
1828     // subarray: unordered
1829     subarray_layout = Layout::UNORDERED;
1830     ranges = {{1, 2, 3, 4}, {1, 2, 3, 5, 7, 9}};
1831     partitions = {{{1, 2, 3, 4}, {1, 2, 3, 5}}, {{1, 2, 3, 4}, {7, 9}}};
1832     test_subarray_partitioner(
1833         subarray_layout, ranges, partitions, attr, budget);
1834   }
1835 
1836   SECTION("# tile: col, cell: row") {
1837     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1838     write_default_2d_array();
1839     open_array(ctx_, array_, TILEDB_READ);
1840 
1841     // subarray: row
1842     subarray_layout = Layout::ROW_MAJOR;
1843     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1844     partitions = {{{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}}};
1845     test_subarray_partitioner(
1846         subarray_layout, ranges, partitions, attr, budget);
1847 
1848     // subarray: col
1849     subarray_layout = Layout::COL_MAJOR;
1850     ranges = {{1, 2, 3, 4}, {2, 5, 7, 9}};
1851     partitions = {{{1, 2, 3, 4}, {2, 5, 7, 9}}};
1852     test_subarray_partitioner(
1853         subarray_layout, ranges, partitions, attr, budget);
1854 
1855     // subarray: unordered
1856     subarray_layout = Layout::UNORDERED;
1857     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1858     partitions = {{{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}}};
1859     test_subarray_partitioner(
1860         subarray_layout, ranges, partitions, attr, budget);
1861   }
1862 
1863   SECTION("# tile: col, cell: col") {
1864     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1865     write_default_2d_array();
1866     open_array(ctx_, array_, TILEDB_READ);
1867 
1868     // subarray: row
1869     subarray_layout = Layout::ROW_MAJOR;
1870     ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1871     partitions = {{{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}}};
1872     test_subarray_partitioner(
1873         subarray_layout, ranges, partitions, attr, budget);
1874 
1875     // subarray: col
1876     subarray_layout = Layout::COL_MAJOR;
1877     ranges = {{1, 2, 3, 4}, {2, 5, 7, 9}};
1878     partitions = {{{1, 2, 3, 4}, {2, 5, 7, 9}}};
1879     test_subarray_partitioner(
1880         subarray_layout, ranges, partitions, attr, budget);
1881 
1882     // subarray: unordered
1883     subarray_layout = Layout::UNORDERED;
1884     ranges = {{1, 2, 3, 4}, {2, 5, 7, 9}};
1885     partitions = {{{1, 2, 3, 4}, {2, 5, 7, 9}}};
1886     test_subarray_partitioner(
1887         subarray_layout, ranges, partitions, attr, budget);
1888   }
1889 
1890   close_array(ctx_, array_);
1891 }
1892 
1893 TEST_CASE_METHOD(
1894     SubarrayPartitionerSparseFx,
1895     "SubarrayPartitioner (Sparse): 2D, multi-range, unsplittable",
1896     "[SubarrayPartitioner][sparse][2D][MR][unsplittable]") {
1897   Layout subarray_layout;
1898   std::vector<SubarrayRanges<uint64_t>> partitions = {{{1, 1}, {2, 2}}};
1899   SubarrayRanges<uint64_t> ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1900   std::string attr = "a";
1901   uint64_t budget = 0;
1902   bool unsplittable = true;
1903 
1904   SECTION("# tile: row, cell: row") {
1905     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
1906     write_default_2d_array();
1907     open_array(ctx_, array_, TILEDB_READ);
1908 
1909     // subarray: row
1910     subarray_layout = Layout::ROW_MAJOR;
1911     test_subarray_partitioner(
1912         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1913 
1914     // subarray: col
1915     subarray_layout = Layout::COL_MAJOR;
1916     test_subarray_partitioner(
1917         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1918 
1919     // subarray: unordered
1920     subarray_layout = Layout::UNORDERED;
1921     test_subarray_partitioner(
1922         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1923   }
1924 
1925   SECTION("# tile: row, cell: col") {
1926     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
1927     write_default_2d_array();
1928     open_array(ctx_, array_, TILEDB_READ);
1929 
1930     // subarray: row
1931     subarray_layout = Layout::ROW_MAJOR;
1932     test_subarray_partitioner(
1933         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1934 
1935     // subarray: col
1936     subarray_layout = Layout::COL_MAJOR;
1937     test_subarray_partitioner(
1938         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1939 
1940     // subarray: unordered
1941     subarray_layout = Layout::UNORDERED;
1942     test_subarray_partitioner(
1943         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1944   }
1945 
1946   SECTION("# tile: col, cell: row") {
1947     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
1948     write_default_2d_array();
1949     open_array(ctx_, array_, TILEDB_READ);
1950 
1951     // subarray: row
1952     subarray_layout = Layout::ROW_MAJOR;
1953     test_subarray_partitioner(
1954         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1955 
1956     // subarray: col
1957     subarray_layout = Layout::COL_MAJOR;
1958     test_subarray_partitioner(
1959         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1960 
1961     // subarray: unordered
1962     subarray_layout = Layout::UNORDERED;
1963     test_subarray_partitioner(
1964         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1965   }
1966 
1967   SECTION("# tile: col, cell: col") {
1968     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
1969     write_default_2d_array();
1970     open_array(ctx_, array_, TILEDB_READ);
1971 
1972     // subarray: row
1973     subarray_layout = Layout::ROW_MAJOR;
1974     test_subarray_partitioner(
1975         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1976 
1977     // subarray: col
1978     subarray_layout = Layout::COL_MAJOR;
1979     test_subarray_partitioner(
1980         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1981 
1982     // subarray: unordered
1983     subarray_layout = Layout::UNORDERED;
1984     test_subarray_partitioner(
1985         subarray_layout, ranges, partitions, attr, budget, unsplittable);
1986   }
1987 
1988   close_array(ctx_, array_);
1989 }
1990 
1991 TEST_CASE_METHOD(
1992     SubarrayPartitionerSparseFx,
1993     "SubarrayPartitioner (Sparse): 2D, multi-range, split multiple finer",
1994     "[SubarrayPartitioner][sparse][2D][MR][split_multiple_finer]") {
1995   Layout subarray_layout;
1996   std::vector<SubarrayRanges<uint64_t>> partitions = {};
1997   SubarrayRanges<uint64_t> ranges = {{1, 2, 3, 3, 4, 4}, {2, 5, 6, 9}};
1998   std::string attr = "a";
1999   uint64_t budget = sizeof(int);
2000 
2001   SECTION("# tile: row, cell: row") {
2002     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
2003     write_default_2d_array();
2004     open_array(ctx_, array_, TILEDB_READ);
2005 
2006     // subarray: row
2007     subarray_layout = Layout::ROW_MAJOR;
2008     partitions = {
2009         {{1, 1}, {2, 5, 6, 9}},
2010         {{2, 2}, {2, 5, 6, 9}},
2011         {{3, 3}, {2, 5}},
2012         {{3, 3}, {6, 9}},
2013         {{4, 4}, {2, 5}},
2014         {{4, 4}, {6, 9}},
2015     };
2016     test_subarray_partitioner(
2017         subarray_layout, ranges, partitions, attr, budget);
2018 
2019     // subarray: col
2020     subarray_layout = Layout::COL_MAJOR;
2021     partitions = {
2022         {{1, 2, 3, 3}, {2, 2}},
2023         {{4, 4}, {2, 2}},
2024         {{1, 2, 3, 3}, {3, 3}},
2025         {{4, 4}, {3, 3}},
2026         {{1, 2, 3, 3, 4, 4}, {4, 5}},
2027         {{1, 2, 3, 3, 4, 4}, {6, 7}},
2028         {{1, 2, 3, 3, 4, 4}, {8, 8}},
2029         {{1, 2, 3, 3, 4, 4}, {9, 9}},
2030     };
2031     test_subarray_partitioner(
2032         subarray_layout, ranges, partitions, attr, budget);
2033 
2034     // subarray: unordered
2035     subarray_layout = Layout::UNORDERED;
2036     partitions = {
2037         {{1, 1}, {2, 5}},
2038         {{2, 2}, {2, 5}},
2039         {{1, 2}, {6, 9}},
2040         {{3, 3}, {2, 5}},
2041         {{3, 3}, {6, 9}},
2042         {{4, 4}, {2, 5}},
2043         {{4, 4}, {6, 9}},
2044     };
2045     test_subarray_partitioner(
2046         subarray_layout, ranges, partitions, attr, budget);
2047   }
2048 
2049   SECTION("# tile: row, cell: col") {
2050     create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_COL_MAJOR);
2051     write_default_2d_array();
2052     open_array(ctx_, array_, TILEDB_READ);
2053 
2054     // subarray: row
2055     subarray_layout = Layout::ROW_MAJOR;
2056     partitions = {
2057         {{1, 1}, {2, 5, 6, 9}},
2058         {{2, 2}, {2, 5, 6, 9}},
2059         {{3, 3}, {2, 5}},
2060         {{3, 3}, {6, 9}},
2061         {{4, 4}, {2, 5}},
2062         {{4, 4}, {6, 9}},
2063     };
2064     test_subarray_partitioner(
2065         subarray_layout, ranges, partitions, attr, budget);
2066 
2067     // subarray: col
2068     subarray_layout = Layout::COL_MAJOR;
2069     partitions = {
2070         {{1, 2, 3, 3}, {2, 2}},
2071         {{4, 4}, {2, 2}},
2072         {{1, 2, 3, 3}, {3, 3}},
2073         {{4, 4}, {3, 3}},
2074         {{1, 2, 3, 3, 4, 4}, {4, 5}},
2075         {{1, 2, 3, 3, 4, 4}, {6, 7}},
2076         {{1, 2, 3, 3, 4, 4}, {8, 8}},
2077         {{1, 2, 3, 3, 4, 4}, {9, 9}},
2078     };
2079     test_subarray_partitioner(
2080         subarray_layout, ranges, partitions, attr, budget);
2081 
2082     // subarray: unordered
2083     subarray_layout = Layout::UNORDERED;
2084     partitions = {
2085         {{1, 2}, {2, 3}},
2086         {{1, 2}, {4, 5}},
2087         {{3, 3}, {2, 5}},
2088         {{4, 4}, {2, 5}},
2089         {{1, 2, 3, 3}, {6, 9}},
2090         {{4, 4}, {6, 9}},
2091     };
2092     test_subarray_partitioner(
2093         subarray_layout, ranges, partitions, attr, budget);
2094   }
2095 
2096   SECTION("# tile: col, cell: row") {
2097     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_ROW_MAJOR);
2098     write_default_2d_array();
2099     open_array(ctx_, array_, TILEDB_READ);
2100 
2101     // subarray: row
2102     subarray_layout = Layout::ROW_MAJOR;
2103     partitions = {
2104         {{1, 1}, {2, 5, 6, 9}},
2105         {{2, 2}, {2, 3}},
2106         {{2, 2}, {4, 5}},
2107         {{2, 2}, {6, 9}},
2108         {{3, 3}, {2, 3}},
2109         {{3, 3}, {4, 5}},
2110         {{3, 3}, {6, 9}},
2111         {{4, 4}, {2, 5}},
2112         {{4, 4}, {6, 9}},
2113     };
2114     test_subarray_partitioner(
2115         subarray_layout, ranges, partitions, attr, budget);
2116 
2117     // subarray: col
2118     subarray_layout = Layout::COL_MAJOR;
2119     partitions = {
2120         {{1, 2, 3, 3, 4, 4}, {2, 2}},
2121         {{1, 2, 3, 3, 4, 4}, {3, 3}},
2122         {{1, 2, 3, 3, 4, 4}, {4, 4}},
2123         {{1, 2, 3, 3, 4, 4}, {5, 5}},
2124         {{1, 2, 3, 3, 4, 4}, {6, 7}},
2125         {{1, 2, 3, 3, 4, 4}, {8, 8}},
2126         {{1, 2, 3, 3, 4, 4}, {9, 9}},
2127     };
2128     test_subarray_partitioner(
2129         subarray_layout, ranges, partitions, attr, budget);
2130 
2131     // subarray: unordered
2132     subarray_layout = Layout::UNORDERED;
2133     partitions = {
2134         {{1, 1}, {2, 5}},
2135         {{2, 2}, {2, 3}},
2136         {{2, 2}, {4, 5}},
2137         {{1, 2}, {6, 9}},
2138         {{3, 3}, {2, 3}},
2139         {{3, 3}, {4, 5}},
2140         {{3, 3}, {6, 9}},
2141         {{4, 4}, {2, 5}},
2142         {{4, 4}, {6, 9}},
2143     };
2144     test_subarray_partitioner(
2145         subarray_layout, ranges, partitions, attr, budget);
2146   }
2147 
2148   SECTION("# tile: col, cell: col") {
2149     create_default_2d_array(TILEDB_COL_MAJOR, TILEDB_COL_MAJOR);
2150     write_default_2d_array();
2151     open_array(ctx_, array_, TILEDB_READ);
2152 
2153     // subarray: row
2154     subarray_layout = Layout::ROW_MAJOR;
2155     partitions = {
2156         {{1, 1}, {2, 5, 6, 9}},
2157         {{2, 2}, {2, 3}},
2158         {{2, 2}, {4, 5}},
2159         {{2, 2}, {6, 9}},
2160         {{3, 3}, {2, 3}},
2161         {{3, 3}, {4, 5}},
2162         {{3, 3}, {6, 9}},
2163         {{4, 4}, {2, 5}},
2164         {{4, 4}, {6, 9}},
2165     };
2166     test_subarray_partitioner(
2167         subarray_layout, ranges, partitions, attr, budget);
2168 
2169     // subarray: col
2170     subarray_layout = Layout::COL_MAJOR;
2171     partitions = {
2172         {{1, 2, 3, 3, 4, 4}, {2, 2}},
2173         {{1, 2, 3, 3, 4, 4}, {3, 3}},
2174         {{1, 2, 3, 3, 4, 4}, {4, 4}},
2175         {{1, 2, 3, 3, 4, 4}, {5, 5}},
2176         {{1, 2, 3, 3, 4, 4}, {6, 7}},
2177         {{1, 2, 3, 3, 4, 4}, {8, 8}},
2178         {{1, 2, 3, 3, 4, 4}, {9, 9}},
2179     };
2180     test_subarray_partitioner(
2181         subarray_layout, ranges, partitions, attr, budget);
2182 
2183     // subarray: unordered
2184     subarray_layout = Layout::UNORDERED;
2185     partitions = {
2186         {{1, 2}, {2, 3}},
2187         {{1, 2}, {4, 5}},
2188         {{3, 3}, {2, 3}},
2189         {{3, 3}, {4, 5}},
2190         {{4, 4}, {2, 5}},
2191         {{1, 2, 3, 3}, {6, 9}},
2192         {{4, 4}, {6, 9}},
2193     };
2194     test_subarray_partitioner(
2195         subarray_layout, ranges, partitions, attr, budget);
2196   }
2197 
2198   close_array(ctx_, array_);
2199 }
2200 
2201 TEST_CASE_METHOD(
2202     SubarrayPartitionerSparseFx,
2203     "SubarrayPartitioner (Sparse): 1D, single-range, string dimension",
2204     "[SubarrayPartitioner][sparse][1D][SR][string-dims][basic]") {
2205   // Create array
2206   create_array(
2207       ctx_,
2208       array_name_,
2209       TILEDB_SPARSE,
2210       {"d"},
2211       {TILEDB_STRING_ASCII},
2212       {nullptr},
2213       {nullptr},
2214       {"a"},
2215       {TILEDB_INT32},
2216       {1},
2217       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
2218       TILEDB_ROW_MAJOR,
2219       TILEDB_ROW_MAJOR,
2220       2,
2221       false,
2222       false);
2223 
2224   // ####### WRITE #######
2225 
2226   // Open array
2227   tiledb_array_t* array;
2228   int rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
2229   CHECK(rc == TILEDB_OK);
2230   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
2231   CHECK(rc == TILEDB_OK);
2232 
2233   // Create and submit query
2234   tiledb_query_t* query;
2235   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
2236   REQUIRE(rc == TILEDB_OK);
2237 
2238   char d_data[] = "ccbbccddaa";
2239   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
2240   uint64_t d_off[] = {0, 2, 4, 8};
2241   uint64_t d_off_size = sizeof(d_off);
2242   int32_t a_data[] = {3, 2, 4, 1};
2243   uint64_t a_size = sizeof(a_data);
2244   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
2245   REQUIRE(rc == TILEDB_OK);
2246   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
2247   REQUIRE(rc == TILEDB_OK);
2248   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
2249   REQUIRE(rc == TILEDB_OK);
2250   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
2251   REQUIRE(rc == TILEDB_OK);
2252   rc = tiledb_query_submit(ctx_, query);
2253   REQUIRE(rc == TILEDB_OK);
2254 
2255   // Close array
2256   rc = tiledb_array_close(ctx_, array);
2257   CHECK(rc == TILEDB_OK);
2258   tiledb_array_free(&array);
2259 
2260   // #### PARTITIONER ####
2261 
2262   // Open array
2263   rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
2264   CHECK(rc == TILEDB_OK);
2265   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
2266   CHECK(rc == TILEDB_OK);
2267 
2268   Layout layout = Layout::ROW_MAJOR;
2269 
2270   SECTION("Global order") {
2271     layout = Layout::GLOBAL_ORDER;
2272   }
2273 
2274   SECTION("Row-major") {
2275     layout = Layout::ROW_MAJOR;
2276   }
2277 
2278   SECTION("Col-major") {
2279     layout = Layout::COL_MAJOR;
2280   }
2281 
2282   SECTION("Unordered") {
2283     layout = Layout::UNORDERED;
2284   }
2285 
2286   // Check unsplittable
2287   tiledb::sm::Subarray subarray(
2288       array->array_, layout, &g_helper_stats, g_helper_logger());
2289   tiledb::sm::Range r;
2290   r.set_str_range("bb", "bb");
2291   subarray.add_range(0, std::move(r), true);
2292   ThreadPool tp;
2293   CHECK(tp.init(4).ok());
2294   Config config;
2295   SubarrayPartitioner partitioner(
2296       &config,
2297       subarray,
2298       memory_budget_,
2299       memory_budget_var_,
2300       0,
2301       &tp,
2302       &g_helper_stats,
2303       g_helper_logger());
2304   auto st = partitioner.set_result_budget("d", 10);
2305   CHECK(!st.ok());
2306   uint64_t budget = 0;
2307   CHECK(!partitioner.get_result_budget("d", &budget).ok());
2308   st = partitioner.set_result_budget("d", 10, 1);
2309   CHECK(st.ok());
2310   uint64_t budget_off = 0, budget_val = 0;
2311   CHECK(partitioner.get_result_budget("d", &budget_off, &budget_val).ok());
2312   CHECK(budget_off == 10);
2313   CHECK(budget_val == 1);
2314   bool unsplittable = true;
2315   CHECK(partitioner.next(&unsplittable).ok());
2316   CHECK(unsplittable);
2317   auto partition = partitioner.current();
2318   CHECK(partition.range_num() == 1);
2319   const Range* range = nullptr;
2320   partition.get_range(0, 0, &range);
2321   CHECK(range != nullptr);
2322   CHECK(range->start_str() == std::string("bb", 2));
2323   CHECK(range->end_str() == std::string("bb", 2));
2324 
2325   // Check full
2326   tiledb::sm::Subarray subarray_full(
2327       array->array_, layout, &g_helper_stats, g_helper_logger());
2328   r.set_str_range("a", "bb");
2329   subarray_full.add_range(0, std::move(r), true);
2330   SubarrayPartitioner partitioner_full(
2331       &config,
2332       subarray_full,
2333       memory_budget_,
2334       memory_budget_var_,
2335       0,
2336       &tp,
2337       &g_helper_stats,
2338       g_helper_logger());
2339   st = partitioner_full.set_result_budget("d", 16, 4);
2340   CHECK(st.ok());
2341   CHECK(partitioner_full.get_result_budget("d", &budget_off, &budget_val).ok());
2342   CHECK(budget_off == 16);
2343   CHECK(budget_val == 4);
2344   CHECK(partitioner_full.next(&unsplittable).ok());
2345   CHECK(!unsplittable);
2346   partition = partitioner_full.current();
2347   CHECK(partition.range_num() == 1);
2348   partition.get_range(0, 0, &range);
2349   CHECK(range != nullptr);
2350   CHECK(range->start_str() == std::string("a", 1));
2351   CHECK(range->end_str() == std::string("bb", 2));
2352 
2353   // Check split
2354   tiledb::sm::Subarray subarray_split(
2355       array->array_, layout, &g_helper_stats, g_helper_logger());
2356   r.set_str_range("a", "bb");
2357   subarray_split.add_range(0, std::move(r), true);
2358   SubarrayPartitioner partitioner_split(
2359       &config,
2360       subarray_split,
2361       memory_budget_,
2362       memory_budget_var_,
2363       0,
2364       &tp,
2365       &g_helper_stats,
2366       g_helper_logger());
2367   st = partitioner_split.set_result_budget("d", 10, 4);
2368   CHECK(st.ok());
2369   CHECK(
2370       partitioner_split.get_result_budget("d", &budget_off, &budget_val).ok());
2371   CHECK(budget_off == 10);
2372   CHECK(budget_val == 4);
2373   CHECK(partitioner_split.next(&unsplittable).ok());
2374   CHECK(!unsplittable);
2375   partition = partitioner_split.current();
2376   CHECK(partition.range_num() == 1);
2377   partition.get_range(0, 0, &range);
2378   CHECK(range != nullptr);
2379   CHECK(range->start_str() == std::string("a", 1));
2380   CHECK(range->end_str() == std::string("a\x7F", 2));
2381   CHECK(partitioner_split.next(&unsplittable).ok());
2382   CHECK(!unsplittable);
2383   partition = partitioner_split.current();
2384   CHECK(partition.range_num() == 1);
2385   partition.get_range(0, 0, &range);
2386   CHECK(range != nullptr);
2387   CHECK(range->start_str() == std::string("b", 1));
2388   CHECK(range->end_str() == std::string("bb", 2));
2389   CHECK(partitioner_split.done());
2390 
2391   // Check no split 2 MBRs
2392   tiledb::sm::Subarray subarray_no_split(
2393       array->array_, layout, &g_helper_stats, g_helper_logger());
2394   r.set_str_range("bb", "cc");
2395   subarray_no_split.add_range(0, std::move(r), true);
2396   SubarrayPartitioner partitioner_no_split(
2397       &config,
2398       subarray_no_split,
2399       memory_budget_,
2400       memory_budget_var_,
2401       0,
2402       &tp,
2403       &g_helper_stats,
2404       g_helper_logger());
2405   st = partitioner_no_split.set_result_budget("d", 16, 10);
2406   CHECK(st.ok());
2407   CHECK(partitioner_no_split.get_result_budget("d", &budget_off, &budget_val)
2408             .ok());
2409   CHECK(budget_off == 16);
2410   CHECK(budget_val == 10);
2411   CHECK(partitioner_no_split.next(&unsplittable).ok());
2412   CHECK(partitioner_no_split.done());
2413   CHECK(!unsplittable);
2414   partition = partitioner_no_split.current();
2415   CHECK(partition.range_num() == 1);
2416   partition.get_range(0, 0, &range);
2417   CHECK(range != nullptr);
2418   CHECK(range->start_str() == std::string("bb", 2));
2419   CHECK(range->end_str() == std::string("cc", 2));
2420 
2421   // Check split 2 MBRs
2422   tiledb::sm::Subarray subarray_split_2(
2423       array->array_, layout, &g_helper_stats, g_helper_logger());
2424   r.set_str_range("bb", "cc");
2425   subarray_split_2.add_range(0, std::move(r), true);
2426   SubarrayPartitioner partitioner_split_2(
2427       &config,
2428       subarray_split_2,
2429       memory_budget_,
2430       memory_budget_var_,
2431       0,
2432       &tp,
2433       &g_helper_stats,
2434       g_helper_logger());
2435   st = partitioner_split_2.set_result_budget("d", 8, 10);
2436   CHECK(st.ok());
2437   CHECK(partitioner_split_2.get_result_budget("d", &budget_off, &budget_val)
2438             .ok());
2439   CHECK(budget_off == 8);
2440   CHECK(budget_val == 10);
2441   CHECK(partitioner_split_2.next(&unsplittable).ok());
2442   CHECK(!partitioner_split_2.done());
2443   CHECK(!unsplittable);
2444   partition = partitioner_split_2.current();
2445   CHECK(partition.range_num() == 1);
2446   partition.get_range(0, 0, &range);
2447   CHECK(range != nullptr);
2448   CHECK(range->start_str() == std::string("bb", 2));
2449   CHECK(range->end_str() == std::string("b\x7F", 2));
2450   CHECK(partitioner_split_2.next(&unsplittable).ok());
2451   CHECK(!unsplittable);
2452   partition = partitioner_split_2.current();
2453   CHECK(partition.range_num() == 1);
2454   partition.get_range(0, 0, &range);
2455   CHECK(range != nullptr);
2456   CHECK(range->start_str() == std::string("c", 1));
2457   CHECK(range->end_str() == std::string("cc", 2));
2458   CHECK(partitioner_split_2.done());
2459 
2460   // Close array
2461   rc = tiledb_array_close(ctx_, array);
2462   CHECK(rc == TILEDB_OK);
2463 
2464   // Clean up
2465   tiledb_array_free(&array);
2466   tiledb_query_free(&query);
2467 }
2468 
2469 TEST_CASE_METHOD(
2470     SubarrayPartitionerSparseFx,
2471     "SubarrayPartitioner (Sparse): 1D, single-range, string dimension, edge "
2472     "split",
2473     "[SubarrayPartitioner][sparse][1D][SR][string-dims][edge-split]") {
2474   // Create array
2475   create_array(
2476       ctx_,
2477       array_name_,
2478       TILEDB_SPARSE,
2479       {"d"},
2480       {TILEDB_STRING_ASCII},
2481       {nullptr},
2482       {nullptr},
2483       {"a"},
2484       {TILEDB_INT32},
2485       {1},
2486       {tiledb::test::Compressor(TILEDB_FILTER_NONE, -1)},
2487       TILEDB_ROW_MAJOR,
2488       TILEDB_ROW_MAJOR,
2489       4,
2490       false,
2491       false);
2492 
2493   // ####### WRITE #######
2494 
2495   // Open array
2496   tiledb_array_t* array;
2497   int rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
2498   CHECK(rc == TILEDB_OK);
2499   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
2500   CHECK(rc == TILEDB_OK);
2501 
2502   // Create and submit query
2503   tiledb_query_t* query;
2504   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
2505   REQUIRE(rc == TILEDB_OK);
2506 
2507   char d_data[] = "ccbbccddaa";
2508   uint64_t d_data_size = sizeof(d_data) - 1;  // Ignore '\0'
2509   uint64_t d_off[] = {0, 2, 4, 8};
2510   uint64_t d_off_size = sizeof(d_off);
2511   int32_t a_data[] = {3, 2, 4, 1};
2512   uint64_t a_size = sizeof(a_data);
2513   rc = tiledb_query_set_data_buffer(ctx_, query, "d", d_data, &d_data_size);
2514   REQUIRE(rc == TILEDB_OK);
2515   rc = tiledb_query_set_offsets_buffer(ctx_, query, "d", d_off, &d_off_size);
2516   REQUIRE(rc == TILEDB_OK);
2517   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a_data, &a_size);
2518   REQUIRE(rc == TILEDB_OK);
2519   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
2520   REQUIRE(rc == TILEDB_OK);
2521   rc = tiledb_query_submit(ctx_, query);
2522   REQUIRE(rc == TILEDB_OK);
2523 
2524   // Close array
2525   rc = tiledb_array_close(ctx_, array);
2526   CHECK(rc == TILEDB_OK);
2527 
2528   // Free array
2529   tiledb_array_free(&array);
2530 
2531   // #### PARTITIONER ####
2532 
2533   // Open array
2534   rc = tiledb_array_alloc(ctx_, array_name_.c_str(), &array);
2535   CHECK(rc == TILEDB_OK);
2536   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
2537   CHECK(rc == TILEDB_OK);
2538 
2539   // Unsplittable
2540   bool unsplittable = false;
2541   uint64_t budget_off = 0, budget_val = 0;
2542 
2543   Layout layout = Layout::ROW_MAJOR;
2544 
2545   SECTION("Global order") {
2546     layout = Layout::GLOBAL_ORDER;
2547   }
2548 
2549   SECTION("Row-major") {
2550     layout = Layout::ROW_MAJOR;
2551   }
2552 
2553   SECTION("Col-major") {
2554     layout = Layout::COL_MAJOR;
2555   }
2556 
2557   SECTION("Unordered") {
2558     layout = Layout::UNORDERED;
2559   }
2560 
2561   tiledb::sm::Subarray subarray(
2562       array->array_, layout, &g_helper_stats, g_helper_logger());
2563   tiledb::sm::Range r;
2564   r.set_str_range("cc", "ccd");
2565   subarray.add_range(0, std::move(r), true);
2566   ThreadPool tp;
2567   CHECK(tp.init(4).ok());
2568   Config config;
2569   SubarrayPartitioner partitioner(
2570       &config,
2571       subarray,
2572       memory_budget_,
2573       memory_budget_var_,
2574       0,
2575       &tp,
2576       &g_helper_stats,
2577       g_helper_logger());
2578   auto st = partitioner.set_result_budget("d", 10, 4);
2579   CHECK(st.ok());
2580   CHECK(partitioner.get_result_budget("d", &budget_off, &budget_val).ok());
2581   CHECK(budget_off == 10);
2582   CHECK(budget_val == 4);
2583   CHECK(partitioner.next(&unsplittable).ok());
2584   CHECK(unsplittable);
2585 
2586   // Close array
2587   rc = tiledb_array_close(ctx_, array);
2588   CHECK(rc == TILEDB_OK);
2589 
2590   // Clean up
2591   tiledb_array_free(&array);
2592   tiledb_query_free(&query);
2593 }
2594 
2595 TEST_CASE_METHOD(
2596     SubarrayPartitionerSparseFx,
2597     "SubarrayPartitioner (Sparse): 2D, multi-range, test memory budget "
2598     "calibration",
2599     "[SubarrayPartitioner][sparse][2D][MR][calibrate-memory_budget]") {
2600   Layout subarray_layout = Layout::ROW_MAJOR;
2601   std::string attr = "a";
2602 
2603   create_default_2d_array(TILEDB_ROW_MAJOR, TILEDB_ROW_MAJOR);
2604   write_default_2d_array();
2605   open_array(ctx_, array_, TILEDB_READ);
2606 
2607   subarray_layout = Layout::ROW_MAJOR;
2608   SubarrayRanges<uint64_t> ranges = {{1, 1}, {2, 2, 2, 2, 2, 2}};
2609   std::vector<SubarrayRanges<uint64_t>> partitions = {
2610       {{1, 1}, {2, 2, 2, 2, 2, 2}},
2611   };
2612 
2613   uint64_t result_budget = 3 * sizeof(int);
2614   uint64_t memory_budget = 2 * sizeof(int);
2615   uint64_t memory_budget_var = 20 * sizeof(int);
2616   test_subarray_partitioner(
2617       subarray_layout,
2618       ranges,
2619       partitions,
2620       attr,
2621       result_budget,
2622       memory_budget,
2623       memory_budget_var);
2624 
2625   close_array(ctx_, array_);
2626 }