473{
474 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
475
476 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
477 throw std::invalid_argument("Transformation matrix dimensions must match number of channels");
478 }
479
480 if (target_data.size() != num_channels) {
481 throw std::invalid_argument("Data channel count must match specified number of channels");
482 }
483
484 size_t min_frames = std::ranges::min(target_data | std::views::transform([](const auto& span) { return span.size(); }));
485
486 auto frame_indices = std::views::iota(size_t { 0 }, min_frames);
487
488 std::ranges::for_each(frame_indices, [&](size_t frame) {
489 Eigen::VectorXd frame_vector(num_channels);
490
491 for (size_t channel = 0; channel < num_channels; ++channel) {
492 frame_vector[channel] = target_data[channel][frame];
493 }
494
495 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
496
497 for (size_t channel = 0; channel < num_channels; ++channel) {
498 target_data[channel][frame] = transformed[channel];
499 }
500 });
501
502 auto reconstructed_data = target_data
503 | std::views::transform([](const auto& span) {
504 return std::vector<double>(span.begin(), span.end());
505 })
506 | std::ranges::to<std::vector>();
507
508 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
509}