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