diff --git a/include/tatami/chunked/CustomChunkManager.hpp b/include/tatami/chunked/CustomChunkManager.hpp index fd91587c..faa97fed 100644 --- a/include/tatami/chunked/CustomChunkManager.hpp +++ b/include/tatami/chunked/CustomChunkManager.hpp @@ -44,17 +44,45 @@ class CustomChunkManager { } } + template + std::pair get_chunk_offset_and_stride(size_t primary_chunk_index) const { + size_t offset, increment; + if (row_major) { + if constexpr(accrow_) { + offset = primary_chunk_index * num_chunks_per_row; + increment = 1; + } else { + offset = primary_chunk_index; + increment = num_chunks_per_row; + } + } else { + if constexpr(accrow_) { + offset = primary_chunk_index; + increment = num_chunks_per_column; + } else { + offset = primary_chunk_index * num_chunks_per_column; + increment = 1; + } + } + return std::make_pair(offset, increment); + } + + static size_t get_primary_upper_bound(size_t primary_chunk_index, size_t primary_maxdim, size_t primary_chunkdim) { + return std::min(primary_chunkdim, primary_maxdim - primary_chunk_index * primary_chunkdim); + } + public: static constexpr bool sparse_chunk = Chunk_::sparse; + typedef typename Chunk_::index_type Chunkdex; struct SparseCache { SparseCache(size_t primary_dim) : cache_indices(primary_dim), cache_values(primary_dim) {} - std::vector > cache_indices; + std::vector > cache_indices; std::vector > cache_values; std::vector buffer_indptrs; - std::vector buffer_indices; + std::vector buffer_indices; std::vector buffer_values; }; @@ -73,13 +101,13 @@ class CustomChunkManager { if constexpr(sparse_chunk) { return SparseCache(exact_ ? 1 : get_primary_chunkdim()); } else { - return DenseCache(exact_ ? 1 : length, get_primary_chunkdim()); + return DenseCache(length, exact_ ? 1 : get_primary_chunkdim()); } } public: template - void extract_block(size_t i, size_t start, size_t length, Cache& cache) { + void extract(size_t primary_chunk_index, size_t primary_chunk_offset, size_t primary_maxdim, size_t start, size_t length, Cache& cache) const { if (!length) { return; } @@ -88,19 +116,15 @@ class CustomChunkManager { size_t primary_chunkdim = get_primary_chunkdim(); size_t secondary_chunkdim = get_secondary_chunkdim(); - size_t primary_chunk_index = (i / primary_chunkdim); - size_t shift = (row_major ? num_chunks_per_row : num_chunks_per_column); - size_t increment = (row_major == accrow_ ? 1 : shift); - size_t offset = primary_chunk_index * shift; + size_t primary_end_pos = (exact_ ? 0 : get_primary_upper_bound(primary_chunk_index, primary_maxdim, primary_chunkdim)); + + auto chunk_stride = get_chunk_offset_and_stride(primary_chunk_index); + auto offset = chunk_stride.first; + auto increment = chunk_stride.second; size_t start_chunk_index = start / secondary_chunkdim; size_t end_chunk_index = (start + length + secondary_chunkdim - 1) / secondary_chunkdim; // i.e., ceiling of the integer division. - - size_t primary_start_pos = 0, primary_end_pos = primary_chunkdim; - if constexpr(exact_) { - primary_start_pos = i - primary_chunk_index * primary_chunkdim; - primary_end_pos = primary_start_pos + 1; - } + offset += increment * start_chunk_index; size_t dense_cache_offset = 0; size_t secondary_start_pos = start_chunk_index * secondary_chunkdim; @@ -112,40 +136,59 @@ class CustomChunkManager { if constexpr(sparse_chunk) { chunk.inflate(cache.buffer_values, cache.buffer_indices, cache.buffer_indptrs); - typename Chunk_::index_type secondary_offset = secondary_start_pos; + Chunkdex secondary_offset = secondary_start_pos; if (chunk.row_major == accrow_) { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { - auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; - if (start == end) { - continue; - } - + auto refine_start_and_end = [&](size_t start, size_t end) -> void { if (from) { - start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, from) - cache.buffer_indices.begin(); + auto it = cache.buffer_indices.begin(); + start = std::lower_bound(it + start, it + end, static_cast(from)) - it; } if (to != secondary_chunkdim) { - end = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, to) - cache.buffer_indices.begin(); + auto it = cache.buffer_indices.begin(); + end = std::lower_bound(it + start, it + end, static_cast(to)) - it; } + }; - auto pout = (exact_ ? 0 : p); - cache.cache_values[pout].insert(cache.cache_values[p].end(), cache.buffer_values.begin() + start, cache.buffer_values.begin() + end); - for (size_t i = start; i < end; ++i) { - cache.cache_indices[pout].push_back(cache.buffer_indices[i] + secondary_offset); + if constexpr(exact_) { + auto start = cache.buffer_indptrs[primary_chunk_offset], end = cache.buffer_indptrs[primary_chunk_offset + 1]; + if (start < end) { + refine_start_and_end(start, end); + cache.cache_values[0].insert(cache.cache_values[0].end(), cache.buffer_values.begin() + start, cache.buffer_values.begin() + end); + for (size_t i = start; i < end; ++i) { + cache.cache_indices[0].push_back(cache.buffer_indices[i] + secondary_offset); + } + } + + } else { + for (size_t p = 0; p < primary_end_pos; ++p) { + auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; + if (start < end) { + refine_start_and_end(start, end); + cache.cache_values[p].insert(cache.cache_values[p].end(), cache.buffer_values.begin() + start, cache.buffer_values.begin() + end); + for (size_t i = start; i < end; ++i) { + cache.cache_indices[p].push_back(cache.buffer_indices[i] + secondary_offset); + } + } } } } else { - for (size_t s = from; s < to; ++s) { - auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; - if constexpr(exact_) { - start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, primary_start_pos) - cache.buffer_indices.begin(); - if (start != end && cache.buffer_indices[start] == primary_start_pos) { + if constexpr(exact_) { + Chunkdex target = primary_chunk_offset; + for (size_t s = from; s < to; ++s) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; + auto it = cache.buffer_indices.begin(); + start = std::lower_bound(it + start, it + end, target) - it; + if (start != end && cache.buffer_indices[start] == target) { cache.cache_values[0].push_back(cache.buffer_values[start]); cache.cache_indices[0].push_back(s + secondary_offset); } + } - } else { + } else { + for (size_t s = from; s < to; ++s) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; for (size_t i = start; i < end; ++i) { auto p = cache.buffer_indices[i]; cache.cache_values[p].push_back(cache.buffer_values[i]); @@ -161,23 +204,40 @@ class CustomChunkManager { auto cptr = cache.cache.data() + dense_cache_offset; if (chunk.row_major == accrow_) { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { + if constexpr(exact_) { + bptr += primary_chunk_offset * secondary_chunkdim; std::copy(bptr + from, bptr + to, cptr); - bptr += secondary_chunkdim; - cptr += length; + + } else { + for (size_t p = 0; p < primary_end_pos; ++p) { + std::copy(bptr + from, bptr + to, cptr); + bptr += secondary_chunkdim; + cptr += length; + } } } else { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { - auto copy_bptr = bptr; - auto copy_cptr = cptr; + if constexpr(exact_) { + bptr += from * primary_chunkdim + primary_chunk_offset; for (size_t s = from; s < to; ++s) { - *copy_cptr = *copy_bptr; - ++copy_cptr; - copy_bptr += primary_chunkdim; + *cptr = *bptr; + ++cptr; + bptr += primary_chunkdim; + } + + } else { + bptr += from * primary_chunkdim; + for (size_t p = 0; p < primary_end_pos; ++p) { + auto copy_bptr = bptr; + auto copy_cptr = cptr; + for (size_t s = from; s < to; ++s) { + *copy_cptr = *copy_bptr; + ++copy_cptr; + copy_bptr += primary_chunkdim; + } + ++bptr; + cptr += length; } - ++bptr; - cptr += length; } } @@ -191,7 +251,7 @@ class CustomChunkManager { public: template - void extract_dense_block(size_t i, const std::vector& indices, Cache& cache) { + void extract(size_t primary_chunk_index, size_t primary_chunk_offset, size_t primary_maxdim, const std::vector& indices, Cache& cache) const { if (indices.empty()) { return; } @@ -200,20 +260,16 @@ class CustomChunkManager { size_t primary_chunkdim = get_primary_chunkdim(); size_t secondary_chunkdim = get_secondary_chunkdim(); - size_t primary_chunk_index = (i / primary_chunkdim); - size_t shift = (row_major ? num_chunks_per_row : num_chunks_per_column); - size_t increment = (row_major == accrow_ ? 1 : shift); - size_t offset = primary_chunk_index * shift; + size_t primary_end_pos = (exact_ ? 0 : get_primary_upper_bound(primary_chunk_index, primary_maxdim, primary_chunkdim)); + + auto chunk_stride = get_chunk_offset_and_stride(primary_chunk_index); + auto offset = chunk_stride.first; + auto increment = chunk_stride.second; size_t start_chunk_index = indices.front() / secondary_chunkdim; std::vector collected; auto iIt = indices.begin(); - - size_t primary_start_pos = 0, primary_end_pos = primary_chunkdim; - if constexpr(exact_) { - primary_start_pos = i - primary_chunk_index * primary_chunkdim; - primary_end_pos = primary_start_pos + 1; - } + offset += start_chunk_index * increment; size_t dense_cache_offset = 0; size_t secondary_start_pos = start_chunk_index * secondary_chunkdim; @@ -231,48 +287,63 @@ class CustomChunkManager { if constexpr(sparse_chunk) { chunk.inflate(cache.buffer_values, cache.buffer_indices, cache.buffer_indptrs); - typename Chunk_::index_type secondary_offset = secondary_start_pos; + Chunkdex secondary_offset = secondary_start_pos; - if (chunk.row_major == accrow_) { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { - auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; - if (start == end) { - continue; + auto collect_sparse = [&](size_t start, size_t end, size_t pout) -> void { + if (collected.front()) { + auto it = cache.buffer_indices.begin(); + start = std::lower_bound(it + start, it + end, static_cast(collected.front())) - it; + } + + auto cIt = collected.begin(); + for (size_t i = start; i < end; ++i) { + Index_ target = cache.buffer_indices[i]; + while (cIt != collected.end() && *cIt < target) { + ++cIt; + } + if (cIt == collected.end()) { + break; + } + if (*cIt == target) { + cache.cache_values[pout].push_back(cache.buffer_values[i]); + cache.cache_indices[pout].push_back(cache.buffer_indices[i] + secondary_offset); + ++cIt; } + } + }; - if (collected.front()) { - start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, collected.front()) - cache.buffer_indices.begin(); + if (chunk.row_major == accrow_) { + if constexpr(exact_) { + auto start = cache.buffer_indptrs[primary_chunk_offset], end = cache.buffer_indptrs[primary_chunk_offset + 1]; + if (start < end) { + collect_sparse(start, end, 0); } - auto pout = (exact_ ? 0 : p); - auto cIt = collected.begin(); - for (size_t i = start; i < end; ++i) { - Index_ target = cache.buffer_indices[i]; - while (cIt != collected.end() && *cIt < target) { - ++cIt; - } - if (cIt == collected.end()) { - break; - } - if (*cIt == target) { - cache.cache_values[pout].push_back(cache.buffer_values[i]); - cache.cache_indices[pout].push_back(cache.buffer_indices[i] + secondary_offset); - ++cIt; + } else { + for (size_t p = 0; p < primary_end_pos; ++p) { + auto start = cache.buffer_indptrs[p], end = cache.buffer_indptrs[p + 1]; + if (start < end) { + collect_sparse(start, end, p); } } } } else { - for (auto s : collected) { - auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; - if constexpr(exact_) { - start = std::lower_bound(cache.buffer_indices.begin() + start, cache.buffer_indices.begin() + end, primary_start_pos) - cache.buffer_indices.begin(); - if (start != end && cache.buffer_indices[start] == primary_start_pos) { + if constexpr(exact_) { + Chunkdex target = primary_chunk_offset; + for (auto s : collected) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; + auto it = cache.buffer_indices.begin(); + start = std::lower_bound(it + start, it + end, target) - it; + if (start != end && cache.buffer_indices[start] == target) { cache.cache_values[0].push_back(cache.buffer_values[start]); cache.cache_indices[0].push_back(s + secondary_offset); } + } - } else { + } else { + for (auto s : collected) { + auto start = cache.buffer_indptrs[s], end = cache.buffer_indptrs[s + 1]; for (size_t i = start; i < end; ++i) { auto p = cache.buffer_indices[i]; cache.cache_values[p].push_back(cache.buffer_values[i]); @@ -288,25 +359,43 @@ class CustomChunkManager { auto cptr = cache.cache.data() + dense_cache_offset; if (chunk.row_major == accrow_) { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { - auto copy_cptr = cptr; + if constexpr(exact_) { + bptr += primary_chunk_offset * secondary_chunkdim; for (auto x : collected) { - *copy_cptr = bptr[x]; - ++copy_cptr; + *cptr = bptr[x]; + ++cptr; + } + + } else { + for (size_t p = 0; p < primary_end_pos; ++p) { + auto copy_cptr = cptr; + for (auto x : collected) { + *copy_cptr = bptr[x]; + ++copy_cptr; + } + bptr += secondary_chunkdim; + cptr += indices.size(); } - bptr += secondary_chunkdim; - cptr += indices.size(); } } else { - for (size_t p = primary_start_pos; p < primary_end_pos; ++p) { - auto copy_cptr = cptr; + if constexpr(exact_) { + bptr += primary_chunk_offset; for (auto x : collected) { - *copy_cptr = bptr[x * primary_chunkdim]; - ++copy_cptr; + *cptr = bptr[x * primary_chunkdim]; + ++cptr; + } + + } else { + for (size_t p = 0; p < primary_end_pos; ++p) { + auto copy_cptr = cptr; + for (auto x : collected) { + *copy_cptr = bptr[x * primary_chunkdim]; + ++copy_cptr; + } + ++bptr; + cptr += indices.size(); } - ++bptr; - cptr += indices.size(); } } diff --git a/tests/src/chunked/CustomChunkManager.cpp b/tests/src/chunked/CustomChunkManager.cpp index 7bc0dcdc..e4d2e062 100644 --- a/tests/src/chunked/CustomChunkManager.cpp +++ b/tests/src/chunked/CustomChunkManager.cpp @@ -1,3 +1,361 @@ #include +#include "tatami/dense/DenseMatrix.hpp" #include "tatami/chunked/CustomChunkManager.hpp" +#include "tatami_test/tatami_test.hpp" + +class CustomDenseChunkManagerMethods { +protected: + struct Chunk { + static constexpr bool sparse = false; + typedef int index_type; + typedef double value_type; + + bool row_major; + std::vector contents; + + void inflate(std::vector& buffer) const { + buffer.resize(contents.size()); + std::copy(contents.begin(), contents.end(), buffer.begin()); + } + }; + + std::unique_ptr > ref; + tatami::CustomChunkManager manager; + + void assemble(std::pair matdim, std::pair chunkdim, bool rowmajor, bool chunkrowmajor) { + auto full = tatami_test::simulate_dense_vector(matdim.first * matdim.second, -10, 10, + /* seed = */ matdim.first * matdim.second + chunkdim.first * chunkdim.second + rowmajor - chunkrowmajor); + ref.reset(new tatami::DenseRowMatrix(matdim.first, matdim.second, std::move(full))); + + manager.chunk_nrow = chunkdim.first; + manager.chunk_ncol = chunkdim.second; + manager.num_chunks_per_row = (matdim.second + chunkdim.second - 1) / chunkdim.second; + manager.num_chunks_per_column = (matdim.first + chunkdim.first - 1) / chunkdim.first; + manager.row_major = rowmajor; + manager.chunks.resize(manager.num_chunks_per_row * manager.num_chunks_per_column); + + for (int r = 0; r < manager.num_chunks_per_column; ++r) { + for (int c = 0; c < manager.num_chunks_per_row; ++c) { + auto cstart = c * manager.chunk_ncol; + auto cend = std::min(cstart + manager.chunk_ncol, static_cast(matdim.second)); + auto clen = cend - cstart; + + auto rstart = r * manager.chunk_nrow; + auto rend = std::min(rstart + manager.chunk_nrow, static_cast(matdim.first)); + auto rlen = rend - rstart; + + auto& current = manager.chunks[rowmajor ? r * manager.num_chunks_per_row + c : c * manager.num_chunks_per_column + r]; + current.row_major = chunkrowmajor; + current.contents.resize(chunkdim.first * chunkdim.second); + + if (chunkrowmajor) { + auto ext = ref->dense_row(cstart, clen); + auto ccptr = current.contents.data(); + for (int r2 = 0; r2 < rlen; ++r2) { + ext->fetch_copy(r2 + rstart, ccptr); + ccptr += chunkdim.second; + } + } else { + auto ext = ref->dense_column(rstart, rlen); + auto ccptr = current.contents.data(); + for (int c2 = 0; c2 < clen; ++c2) { + ext->fetch_copy(c2 + cstart, ccptr); + ccptr += chunkdim.first; + } + } + } + } + } +}; + +/*******************************************************/ + +class CustomDenseChunkManagerFullTest : + public ::testing::TestWithParam, std::pair, bool, bool> >, + public CustomDenseChunkManagerMethods {}; + +TEST_P(CustomDenseChunkManagerFullTest, Row) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<2>(param), std::get<3>(param)); + + auto cache = manager.create_chunk_cache(ref->ncol()); + auto ecache = manager.create_chunk_cache(ref->ncol()); + std::vector tmp1(ref->ncol()), tmp2(ref->ncol()); + auto ref_ext = ref->dense_row(); + int lastr = -1; + const double* ccptr = NULL; + + for (int r = 0; r < ref->nrow(); ++r) { + int requiredr = r / manager.chunk_nrow; + if (requiredr != lastr) { + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), 0, ref->ncol(), cache); + lastr = requiredr; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(r, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += ref->ncol(); + + // Testing with exact. + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), 0, ref->ncol(), ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +TEST_P(CustomDenseChunkManagerFullTest, Column) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<2>(param), std::get<3>(param)); + + auto cache = manager.create_chunk_cache(ref->nrow()); + auto ecache = manager.create_chunk_cache(ref->nrow()); + std::vector tmp1(ref->nrow()), tmp2(ref->nrow()); + auto ref_ext = ref->dense_column(); + int lastc = -1; + const double* ccptr = NULL; + + for (int c = 0; c < ref->ncol(); ++c) { + int requiredc = c / manager.chunk_ncol; + if (requiredc != lastc) { + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), 0, ref->nrow(), cache); + lastc = requiredc; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(c, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += ref->nrow(); + + // Testing with exact. + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), 0, ref->nrow(), ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +INSTANTIATE_TEST_SUITE_P( + CustomChunkManager, + CustomDenseChunkManagerFullTest, + ::testing::Combine( + ::testing::Values( // matrix dimensions + std::make_pair(200, 50), + std::make_pair(100, 300), + std::make_pair(152, 211), + std::make_pair(512, 32) + ), + ::testing::Values( // chunk dimensions + std::make_pair(1, 20), + std::make_pair(20, 1), + std::make_pair(10, 10), + std::make_pair(11, 13) // odd numbers + ), + ::testing::Values(true, false), // row major + ::testing::Values(true, false) // chunk is row major + ) +); + +/*******************************************************/ + +class CustomDenseChunkManagerBlockTest : + public ::testing::TestWithParam, std::pair, std::pair, bool, bool> >, + public CustomDenseChunkManagerMethods {}; + +TEST_P(CustomDenseChunkManagerBlockTest, Row) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<3>(param), std::get<4>(param)); + + auto bounds = std::get<2>(param); + int start = bounds.first * ref->ncol(); + int len = bounds.second * ref->ncol() - start; + + auto cache = manager.create_chunk_cache(len); + auto ecache = manager.create_chunk_cache(len); + std::vector tmp1(len), tmp2(len); + auto ref_ext = ref->dense_row(start, len); + int lastr = -1; + const double* ccptr = NULL; + + for (int r = 0; r < ref->nrow(); ++r) { + int requiredr = r / manager.chunk_nrow; + if (requiredr != lastr) { + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), start, len, cache); + lastr = requiredr; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(r, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += len; + + // Testing with exact. + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), start, len, ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +TEST_P(CustomDenseChunkManagerBlockTest, Column) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<3>(param), std::get<4>(param)); + + auto bounds = std::get<2>(param); + int start = bounds.first * ref->nrow(); + int len = bounds.second * ref->nrow() - start; + + auto cache = manager.create_chunk_cache(len); + auto ecache = manager.create_chunk_cache(len); + std::vector tmp1(len), tmp2(len); + auto ref_ext = ref->dense_column(start, len); + int lastc = -1; + const double* ccptr = NULL; + + for (int c = 0; c < ref->ncol(); ++c) { + int requiredc = c / manager.chunk_ncol; + if (requiredc != lastc) { + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), start, len, cache); + lastc = requiredc; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(c, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += len; + + // Testing with exact. + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), start, len, ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +INSTANTIATE_TEST_SUITE_P( + CustomChunkManager, + CustomDenseChunkManagerBlockTest, + ::testing::Combine( + ::testing::Values( // matrix dimensions + std::make_pair(200, 50), + std::make_pair(100, 300), + std::make_pair(152, 211) + ), + ::testing::Values( // chunk dimensions + std::make_pair(1, 20), + std::make_pair(20, 1), + std::make_pair(10, 10) + ), + ::testing::Values( // block boundaries + std::make_pair(0.0, 0.35), + std::make_pair(0.15, 0.87), + std::make_pair(0.38, 1.0) + ), + ::testing::Values(true, false), // row major + ::testing::Values(true, false) // chunk is row major + ) +); + +/*******************************************************/ + +class CustomDenseChunkManagerIndexTest : + public ::testing::TestWithParam, std::pair, std::pair, bool, bool> >, + public CustomDenseChunkManagerMethods +{ +protected: + static std::vector get_indices(std::pair bounds, int range) { + int start = bounds.first * range; + int jump = bounds.second; + std::vector indices; + while (start < range) { + indices.push_back(start); + start += jump; + } + return indices; + } +}; + +TEST_P(CustomDenseChunkManagerIndexTest, Row) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<3>(param), std::get<4>(param)); + auto indices = get_indices(std::get<2>(param), ref->ncol()); + + auto cache = manager.create_chunk_cache(indices.size()); + auto ecache = manager.create_chunk_cache(indices.size()); + std::vector tmp1(indices.size()), tmp2(indices.size()); + auto ref_ext = ref->dense_row(indices); + int lastr = -1; + const double* ccptr = NULL; + + for (int r = 0; r < ref->nrow(); ++r) { + int requiredr = r / manager.chunk_nrow; + if (requiredr != lastr) { + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), indices, cache); + lastr = requiredr; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(r, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += indices.size(); + + // Testing with exact. + manager.extract(requiredr, r % manager.chunk_nrow, ref->nrow(), indices, ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +TEST_P(CustomDenseChunkManagerIndexTest, Column) { + auto param = GetParam(); + assemble(std::get<0>(param), std::get<1>(param), std::get<3>(param), std::get<4>(param)); + auto indices = get_indices(std::get<2>(param), ref->nrow()); + + auto cache = manager.create_chunk_cache(indices.size()); + auto ecache = manager.create_chunk_cache(indices.size()); + std::vector tmp1(indices.size()), tmp2(indices.size()); + auto ref_ext = ref->dense_column(indices); + int lastc = -1; + const double* ccptr = NULL; + + for (int c = 0; c < ref->ncol(); ++c) { + int requiredc = c / manager.chunk_ncol; + if (requiredc != lastc) { + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), indices, cache); + lastc = requiredc; + ccptr = cache.cache.data(); + } + + ref_ext->fetch_copy(c, tmp1.data()); + std::copy(ccptr, ccptr + tmp2.size(), tmp2.data()); + EXPECT_EQ(tmp1, tmp2); + ccptr += indices.size(); + + // Testing with exact. + manager.extract(requiredc, c % manager.chunk_ncol, ref->ncol(), indices, ecache); + EXPECT_EQ(tmp1, ecache.cache); + } +} + +INSTANTIATE_TEST_SUITE_P( + CustomChunkManager, + CustomDenseChunkManagerIndexTest, + ::testing::Combine( + ::testing::Values( // matrix dimensions + std::make_pair(200, 50), + std::make_pair(100, 300), + std::make_pair(152, 211) + ), + ::testing::Values( // chunk dimensions + std::make_pair(1, 20), + std::make_pair(20, 1), + std::make_pair(10, 10) + ), + ::testing::Values( // index information. + std::make_pair(0.0, 10), + std::make_pair(0.2, 5), + std::make_pair(0.7, 3) + ), + ::testing::Values(true, false), // row major + ::testing::Values(true, false) // chunk is row major + ) +); +