746{
747 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
748
749 bool needs_resize = false;
750 for (const auto& span : target_data) {
751 if (target_size != span.size()) {
752 needs_resize = true;
753 break;
754 }
755 }
756
757 if (!needs_resize) {
758 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
759 }
760
761 for (size_t ch = 0; ch < target_data.size(); ++ch) {
762 const auto& data_span = target_data[ch];
763 working_buffer[ch].resize(target_size);
764
765 if (target_size <= 1 || data_span.size() <= 1) {
766 std::fill(working_buffer[ch].begin(), working_buffer[ch].end(),
767 data_span.empty() ? 0.0 : data_span[0]);
768 continue;
769 }
770
771 auto indices = std::views::iota(size_t { 0 }, target_size);
772 std::ranges::transform(indices, working_buffer[ch].begin(),
773 [&data_span, target_size](size_t i) {
774 double pos = static_cast<double>(i) * (data_span.size() - 1) / (target_size - 1);
775 int idx = static_cast<int>(pos);
776 double frac = pos - idx;
777
778 auto get_sample = [&data_span](int i) {
779 return data_span[std::clamp(i, 0, static_cast<int>(data_span.size() - 1))];
780 };
781
782 double y0 = get_sample(idx - 1);
783 double y1 = get_sample(idx);
784 double y2 = get_sample(idx + 1);
785 double y3 = get_sample(idx + 2);
786
787 double a = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
788 double b = y0 - 2.5 * y1 + 2.0 * y2 - 0.5 * y3;
789 double c = -0.5 * y0 + 0.5 * y2;
790 double d = y1;
791
792 return ((a * frac + b) * frac + c) * frac + d;
793 });
794 }
795
796 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
797}