65 const RightColumns_ right_columns,
66 GetRightColumn_ get_right_column,
67 Output_*
const output,
70 const auto left_NR = left.
nrow();
71 const auto common_dim = left.
ncol();
74 std::optional<std::vector<std::optional<std::vector<Output_> > > > tmp_results;
76 tmp_results.emplace(sanisizer::cast<I<
decltype(tmp_results->size())> >(options.
num_threads - 1));
81 std::fill_n(output, sanisizer::product<std::size_t>(left_NR, right_columns), 0);
83 const auto num_used =
tatami::parallelize([&](
int t, LeftIndex_ start, LeftIndex_ length) ->
void {
86 std::optional<std::vector<Output_> > tmp_output;
88 if (!do_parallel || t == 0) {
91 tmp_output.emplace(sanisizer::product<I<
decltype(tmp_output->size())> >(left_NR, right_columns));
92 outptr = tmp_output->data();
98 for (LeftIndex_ cd = 0; cd < length; ++cd) {
99 const auto range = ext->fetch(vbuffer.data(), ibuffer.data());
100 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
101 const Output_ mult = get_right_column(rc)[start + cd];
102 for (LeftIndex_ x = 0; x < range.number; ++x) {
103 outptr[sanisizer::nd_offset<std::size_t>(range.index[x], left_NR, rc)] += mult *
static_cast<Output_
>(range.value[x]);
109 std::vector<std::vector<LeftValue_> > left_vbuffers;
110 std::vector<std::vector<LeftIndex_> > left_ibuffers;
111 std::vector<tatami::SparseRange<LeftValue_, LeftIndex_> > left_ranges;
112 std::vector<LeftIndex_> left_non_empty;
114 const LeftIndex_ max_block_cols = sanisizer::min(length, options.
block_size);
115 left_vbuffers.reserve(max_block_cols);
116 left_ibuffers.reserve(max_block_cols);
117 for (LeftIndex_ cd = 0; cd < max_block_cols; ++cd) {
121 sanisizer::resize(left_ranges, max_block_cols);
125 while (cd < length) {
129 const auto left_block_info = fetch_non_empty_sparse_block(
138 [&](
const LeftIndex_) ->
void {}
140 const auto cd_num = left_block_info.num_non_empty;
144 if (left_block_info.all_non_empty) {
145 const LeftIndex_ cd_base = cd + start;
146 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
147 const auto rightcol = get_right_column(rc);
148 for (LeftIndex_ cd_counter = 0; cd_counter < cd_num; ++cd_counter) {
149 const auto& currange = left_ranges[cd_counter];
150 const Output_ mult = rightcol[cd_base + cd_counter];
151 for (LeftIndex_ x = 0; x < currange.number; ++x) {
152 outptr[sanisizer::nd_offset<std::size_t>(currange.index[x], left_NR, rc)] += mult *
static_cast<Output_
>(currange.value[x]);
158 for (
auto& cdne : left_non_empty) {
161 for (RightColumns_ rc = 0; rc < right_columns; ++rc) {
162 const auto rightcol = get_right_column(rc);
163 for (LeftIndex_ cd_counter = 0; cd_counter < cd_num; ++cd_counter) {
164 const auto& currange = left_ranges[cd_counter];
165 const Output_ mult = rightcol[left_non_empty[cd_counter]];
166 for (LeftIndex_ x = 0; x < currange.number; ++x) {
167 outptr[sanisizer::nd_offset<std::size_t>(currange.index[x], left_NR, rc)] += mult *
static_cast<Output_
>(currange.value[x]);
173 cd = left_block_info.position;
177 if (do_parallel && t > 0) {
178 (*tmp_results)[t - 1] = std::move(tmp_output);
183 for (
int u = 1; u < num_used; ++u) {
184 const auto& tmp = *((*tmp_results)[u - 1]);
185 const auto N = tmp.size();
186 for (I<
decltype(N)> x = 0; x < N; ++x) {