1#ifndef TATAMI_TEST_TEST_ACCESS_HPP
2#define TATAMI_TEST_TEST_ACCESS_HPP
4#include <gtest/gtest.h>
83 output.
use_row = std::get<0>(x);
85 output.
order = std::get<2>(x);
86 output.
jump = std::get<3>(x);
95 return ::testing::Combine(
96 ::testing::Values(
true,
false),
97 ::testing::Values(
true,
false),
98 ::testing::Values(TestAccessOrder::FORWARD, TestAccessOrder::REVERSE, TestAccessOrder::RANDOM),
99 ::testing::Values(1, 3)
108template<
typename Value_>
109void compare_vectors(
const std::vector<Value_>& expected,
const std::vector<Value_>& observed,
const std::string& context) {
110 const auto n_expected = expected.size();
111 ASSERT_EQ(n_expected, observed.size()) <<
"mismatch in vector length (" << context <<
")";
112 for (I<
decltype(n_expected)> i = 0; i < n_expected; ++i) {
113 auto expected_val = expected[i], observed_val = observed[i];
114 if (std::isnan(expected_val)) {
115 EXPECT_EQ(std::isnan(expected_val), std::isnan(observed_val)) <<
"mismatching NaNs at position " << i <<
" (" << context <<
")";
117 EXPECT_EQ(expected_val, observed_val) <<
"different values at position " << i <<
" (" << context <<
")";
122template<
typename Value_>
123void compare_vectors(
const std::vector<Value_>& expected,
const std::vector<Value_>& observed,
const char* context) {
124 compare_vectors(expected, observed, std::string(context));
127template<
typename Index_>
128SeedType create_seed(
const Index_ NR,
const Index_ NC,
const TestAccessOptions& options) {
130 seed += 13 *
static_cast<SeedType>(options.use_row);
131 seed += 57 *
static_cast<SeedType>(options.order);
132 seed += 101 *
static_cast<SeedType>(options.jump);
136template<
typename Index_>
137std::vector<Index_> simulate_test_access_sequence(
const Index_ NR,
const Index_ NC,
const TestAccessOptions& options) {
138 std::vector<Index_> sequence;
139 const auto limit = (options.use_row ? NR : NC);
141 RngEngine rng(create_seed(NR, NC, options));
142 Index_ start = rng() % options.jump;
145 sequence.push_back(start);
146 const Index_ remainder = limit - start;
148 if (sanisizer::is_less_than_or_equal(remainder, options.jump)) {
151 start += options.jump;
155 if (options.order == TestAccessOrder::REVERSE) {
156 std::reverse(sequence.begin(), sequence.end());
157 }
else if (options.order == TestAccessOrder::RANDOM) {
158 std::shuffle(sequence.begin(), sequence.end(), rng);
164template<
bool use_oracle_,
typename Index_>
166 if constexpr(use_oracle_) {
167 std::shared_ptr<tatami::Oracle<Index_> > oracle;
168 if (options.jump == 1 && options.order == TestAccessOrder::FORWARD) {
179template<
bool use_oracle_,
typename Value_,
typename Index_,
class SparseExpand_,
typename ...Args_>
180void test_access_base(
183 const TestAccessOptions& options,
185 const SparseExpand_ sparse_expand,
188 const auto NR = matrix.
nrow();
189 ASSERT_EQ(NR, reference.
nrow());
190 const auto NC = matrix.
ncol();
191 ASSERT_EQ(NC, reference.
ncol());
195 auto sequence = simulate_test_access_sequence(NR, NC, options);
196 auto oracle = create_oracle<use_oracle_>(sequence, options);
208 sanisizer::as_size_type<std::vector<Value_> >(extent);
209 std::vector<Value_> mat_dense_buffer(extent), ref_dense_buffer(extent), mat_vbuffer(extent), mat_vstore1(extent), mat_vstore2(extent);
210 sanisizer::as_size_type<std::vector<Index_> >(extent);
211 std::vector<Index_> mat_ibuffer(extent), mat_istore1(extent), mat_istore2(extent);
212 bool has_sparse =
false;
214 for (
const auto i : sequence) {
216 std::fill(ref_dense_buffer.begin(), ref_dense_buffer.end(), 0);
217 const auto ref_buf = ref_dense_buffer.data();
218 const auto ref_ptr = refwork->fetch(Fix(i), ref_buf);
224 std::fill(mat_dense_buffer.begin(), mat_dense_buffer.end(), 0);
225 const auto mat_buf = mat_dense_buffer.data();
226 const auto mat_ptr = [&]() {
227 if constexpr(use_oracle_) {
228 return pwork->fetch(mat_buf);
230 return pwork->fetch(Fix(i), mat_buf);
234 compare_vectors(ref_dense_buffer, mat_dense_buffer,
"dense retrieval");
239 std::fill(mat_vbuffer.begin(), mat_vbuffer.end(), 0);
240 std::fill(mat_ibuffer.begin(), mat_ibuffer.end(), 0);
241 const auto vbuf = mat_vbuffer.data();
242 const auto ibuf = mat_ibuffer.data();
244 const auto observed = [&]() {
245 if constexpr(use_oracle_) {
246 return swork->fetch(vbuf, ibuf);
248 return swork->fetch(Fix(i), vbuf, ibuf);
251 compare_vectors(ref_dense_buffer, sparse_expand(observed),
"sparse retrieval");
253 if (!has_sparse && sanisizer::is_less_than(observed.number, extent)) {
257 bool is_increasing =
true;
258 for (I<
decltype(observed.number)> i = 1; i < observed.number; ++i) {
259 if (observed.index[i] <= observed.index[i-1]) {
260 is_increasing =
false;
264 ASSERT_TRUE(is_increasing);
267 mat_vstore1.insert(mat_vstore1.end(), observed.value, observed.value + observed.number);
269 mat_istore1.insert(mat_istore1.end(), observed.index, observed.index + observed.number);
274 std::fill(mat_ibuffer.begin(), mat_ibuffer.end(), 0);
275 const auto ibuf = mat_ibuffer.data();
277 auto observed_i = [&]() {
278 if constexpr(use_oracle_) {
279 return swork_i->fetch(NULL, ibuf);
281 return swork_i->fetch(Fix(i), NULL, ibuf);
285 ASSERT_TRUE(observed_i.value == NULL);
287 mat_istore2.insert(mat_istore2.end(), observed_i.index, observed_i.index + observed_i.number);
288 ASSERT_EQ(mat_istore1, mat_istore2);
293 std::fill(mat_vbuffer.begin(), mat_vbuffer.end(), 0);
294 const auto vbuf = mat_vbuffer.data();
296 auto observed_v = [&]() {
297 if constexpr(use_oracle_) {
298 return swork_v->fetch(vbuf, NULL);
300 return swork_v->fetch(Fix(i), vbuf, NULL);
304 ASSERT_TRUE(observed_v.index == NULL);
306 mat_vstore2.insert(mat_vstore2.end(), observed_v.value, observed_v.value + observed_v.number);
307 compare_vectors(mat_vstore1, mat_vstore2,
"sparse retrieval with values only");
312 auto observed_n = [&]() {
313 if constexpr(use_oracle_) {
314 return swork_n->fetch(NULL, NULL);
316 return swork_n->fetch(Fix(i), NULL, NULL);
320 ASSERT_TRUE(observed_n.value == NULL);
321 ASSERT_TRUE(observed_n.index == NULL);
322 ASSERT_EQ(mat_vstore1.size(), observed_n.number);
326 if (options.check_sparse && matrix.
is_sparse()) {
327 EXPECT_TRUE(has_sparse);
331template<
bool use_oracle_,
typename Value_,
typename Index_>
335 const TestAccessOptions& options
337 const Index_ nsecondary = (options.use_row ? reference.
ncol() : reference.
nrow());
338 auto expected = sanisizer::create<std::vector<Value_> >(nsecondary);
340 test_access_base<use_oracle_>(
346 std::fill(expected.begin(), expected.end(), 0);
347 for (I<
decltype(svec.
number)> i = 0; i < svec.
number; ++i) {
355template<
bool use_oracle_,
typename Value_,
typename Index_>
359 const double relative_start,
360 const double relative_length,
361 const TestAccessOptions& options
363 const Index_ nsecondary = (options.use_row ? reference.
ncol() : reference.
nrow());
364 const Index_ start = nsecondary * relative_start;
365 const Index_ length = nsecondary * relative_length;
366 auto expected = sanisizer::create<std::vector<Value_> >(length);
368 test_access_base<use_oracle_>(
374 std::fill(expected.begin(), expected.end(), 0);
375 for (I<
decltype(svec.
number)> i = 0; i < svec.
number; ++i) {
376 expected[svec.
index[i] - start] = svec.
value[i];
385template<
bool use_oracle_,
typename Value_,
typename Index_>
389 const double relative_start,
390 const double probability,
391 const TestAccessOptions& options
393 const Index_ nsecondary = (options.use_row ? reference.
ncol() : reference.
nrow());
399 create_seed(matrix.
nrow(), matrix.
ncol(), options)
400 +
static_cast<SeedType>(999 * probability)
401 +
static_cast<SeedType>(85 * relative_start)
405 const Index_ num_indices = index_ptr->size();
406 constexpr std::size_t placeholder = -1;
407 auto reposition = sanisizer::create<std::vector<std::size_t> >(nsecondary, placeholder);
409 const auto& indices = *index_ptr;
410 for (Index_ i = 0; i < num_indices; ++i) {
411 reposition[indices[i]] = i;
415 auto expected = sanisizer::create<std::vector<Value_> >(num_indices);
417 test_access_base<use_oracle_>(
423 std::fill(expected.begin(), expected.end(), 0);
424 for (I<
decltype(svec.
number)> i = 0; i < svec.
number; ++i) {
425 expected[reposition[svec.
index[i]]] = svec.
value[i];
450template<
typename Value_,
typename Index_>
457 internal::test_full_access<true>(matrix, reference, options);
459 internal::test_full_access<false>(matrix, reference, options);
481template<
typename Value_,
typename Index_>
485 double relative_start,
486 double relative_length,
490 internal::test_block_access<true>(matrix, reference, relative_start, relative_length, options);
492 internal::test_block_access<false>(matrix, reference, relative_start, relative_length, options);
514template<
typename Value_,
typename Index_>
518 double relative_start,
523 internal::test_indexed_access<true>(matrix, reference, relative_start, probability, options);
525 internal::test_indexed_access<false>(matrix, reference, relative_start, probability, options);
541template<
typename Value_,
typename Index_>
560template<
typename Value_,
typename Index_>
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense_row(const Options &opt) const
virtual Index_ ncol() const=0
std::unique_ptr< MyopicDenseExtractor< Value_, Index_ > > dense_column(const Options &opt) const
virtual Index_ nrow() const=0
virtual bool is_sparse() const=0
Create an indexed subset of dimension elements.
Utilities for testing tatami libraries.
Definition create_indexed_subset.hpp:16
void test_block_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, double relative_start, double relative_length, const TestAccessOptions &options)
Definition test_access.hpp:482
tatami::VectorPtr< Index_ > create_indexed_subset(const Index_ extent, const double relative_start, const double probability, const SeedType seed)
Definition create_indexed_subset.hpp:36
std::mt19937_64 RngEngine
Definition utils.hpp:34
RngEngine::result_type SeedType
Definition utils.hpp:39
std::tuple< bool, bool, TestAccessOrder, int > StandardTestAccessOptions
Definition test_access.hpp:72
TestAccessOrder
Definition test_access.hpp:35
void test_indexed_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, double relative_start, double probability, const TestAccessOptions &options)
Definition test_access.hpp:515
void test_simple_row_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference)
Definition test_access.hpp:561
void test_simple_column_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference)
Definition test_access.hpp:542
TestAccessOptions convert_test_access_options(const StandardTestAccessOptions &x)
Definition test_access.hpp:81
auto standard_test_access_options_combinations()
Definition test_access.hpp:94
void test_full_access(const tatami::Matrix< Value_, Index_ > &matrix, const tatami::Matrix< Value_, Index_ > &reference, const TestAccessOptions &options)
Definition test_access.hpp:451
auto new_extractor(const Matrix< Value_, Index_ > &matrix, const bool row, MaybeOracle< oracle_, Index_ > oracle, Args_ &&... args)
typename std::conditional< oracle_, std::shared_ptr< const Oracle< Index_ > >, bool >::type MaybeOracle
Value_ * copy_n(const Value_ *const input, const Size_ n, Value_ *const output)
bool sparse_extract_index
bool sparse_extract_value
Options for test_full_access() and friends.
Definition test_access.hpp:40
bool use_oracle
Definition test_access.hpp:44
int jump
Definition test_access.hpp:60
bool use_row
Definition test_access.hpp:50
bool check_sparse
Definition test_access.hpp:65
TestAccessOrder order
Definition test_access.hpp:55