MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MathematicalHelper.hpp
Go to the documentation of this file.
1#pragma once
2
5
6/**
7 * @file MathematicalTransforms.hpp
8 * @brief Mathematical transformation functions leveraging existing ecosystem
9 *
10 * Provides mathematical transformation functions that can be used by any class.
11 * Leverages existing infrastructure:
12 * - Uses OperationHelper for data conversion/reconstruction
13 * - Uses existing Generator::Polynomial for polynomial operations
14 * - Uses StatisticalAnalyzer for normalization statistics
15 * - Follows DataUtils in-place vs copy patterns
16 * - Uses C++20 ranges/views for efficient processing
17 *
18 * Philosophy: **Function-based helpers** that compose existing capabilities.
19 */
20
21namespace MayaFlux::Yantra {
22
23/**
24 * @brief Linear transformation y = ax + b using C++20 ranges (IN-PLACE)
25 * @tparam DataType OperationReadyData type
26 * @param input Input data - WILL BE MODIFIED
27 * @param a Scale factor
28 * @param b Offset factor
29 * @return Transformed data
30 */
31template <OperationReadyData DataType>
32DataType transform_linear(DataType& input, double a, double b)
33{
34 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
35
36 for (auto& span : target_data) {
37 std::ranges::transform(span, span.begin(),
38 [a, b](double x) { return a * x + b; });
39 }
40
41 auto reconstructed_data = target_data
42 | std::views::transform([](const auto& span) {
43 return std::vector<double>(span.begin(), span.end());
44 })
45 | std::ranges::to<std::vector>();
46
47 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
48}
49
50/**
51 * @brief Linear transformation y = ax + b using C++20 ranges (OUT-OF-PLACE)
52 * @tparam DataType OperationReadyData type
53 * @param input Input data - will NOT be modified
54 * @param a Scale factor
55 * @param b Offset factor
56 * @param working_buffer Buffer for operations (will be resized if needed)
57 * @return Transformed data
58 */
59template <OperationReadyData DataType>
60DataType transform_linear(DataType& input, double a, double b, std::vector<std::vector<double>>& working_buffer)
61{
62 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
63
64 for (auto& span : target_data) {
65 std::ranges::transform(span, span.begin(),
66 [a, b](double x) { return a * x + b; });
67 }
68
69 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
70}
71
72/**
73 * @brief Power transformation y = x^exponent (IN-PLACE)
74 * @tparam DataType OperationReadyData type
75 * @param input Input data - WILL BE MODIFIED
76 * @param exponent Power exponent
77 * @return Transformed data
78 */
79template <OperationReadyData DataType>
80DataType transform_power(DataType& input, double exponent)
81{
82 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
83
84 for (auto& span : target_data) {
85 std::ranges::transform(span, span.begin(),
86 [exponent](double x) { return std::pow(x, exponent); });
87 }
88
89 auto reconstructed_data = target_data
90 | std::views::transform([](const auto& span) {
91 return std::vector<double>(span.begin(), span.end());
92 })
93 | std::ranges::to<std::vector>();
94
95 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
96}
97
98/**
99 * @brief Power transformation y = x^exponent (OUT-OF-PLACE)
100 * @tparam DataType OperationReadyData type
101 * @param input Input data - will NOT be modified
102 * @param exponent Power exponent
103 * @param working_buffer Buffer for operations (will be resized if needed)
104 * @return Transformed data
105 */
106template <OperationReadyData DataType>
107DataType transform_power(DataType& input, double exponent, std::vector<std::vector<double>>& working_buffer)
108{
109 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
110
111 for (auto& span : target_data) {
112 std::ranges::transform(span, span.begin(),
113 [exponent](double x) { return std::pow(x, exponent); });
114 }
115
116 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
117}
118
119/**
120 * @brief Polynomial transformation using existing Generator::Polynomial (IN-PLACE)
121 * @tparam DataType OperationReadyData type
122 * @param input Input data - WILL BE MODIFIED
123 * @param coefficients Polynomial coefficients [a0, a1, a2, ...]
124 * @return Transformed data
125 */
126template <OperationReadyData DataType>
127DataType transform_polynomial(DataType& input, const std::vector<double>& coefficients)
128{
129 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
130
131 auto polynomial_gen = std::make_shared<Nodes::Generator::Polynomial>(coefficients);
132
133 for (auto& span : target_data) {
134 std::ranges::transform(span, span.begin(),
135 [&polynomial_gen](double x) {
136 return polynomial_gen->process_sample(x);
137 });
138 }
139
140 auto reconstructed_data = target_data
141 | std::views::transform([](const auto& span) {
142 return std::vector<double>(span.begin(), span.end());
143 })
144 | std::ranges::to<std::vector>();
145
146 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
147}
148
149/**
150 * @brief Polynomial transformation using existing Generator::Polynomial (OUT-OF-PLACE)
151 * @tparam DataType OperationReadyData type
152 * @param input Input data - will NOT be modified
153 * @param coefficients Polynomial coefficients [a0, a1, a2, ...]
154 * @param working_buffer Buffer for operations (will be resized if needed)
155 * @return Transformed data
156 */
157template <OperationReadyData DataType>
158DataType transform_polynomial(DataType& input, const std::vector<double>& coefficients, std::vector<std::vector<double>>& working_buffer)
159{
160 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
161
162 auto polynomial_gen = std::make_shared<Nodes::Generator::Polynomial>(coefficients);
163
164 for (auto& span : target_data) {
165 std::ranges::transform(span, span.begin(),
166 [&polynomial_gen](double x) {
167 return polynomial_gen->process_sample(x);
168 });
169 }
170
171 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
172}
173
174/**
175 * @brief Exponential transformation y = a * base^(b * x) (IN-PLACE)
176 * @tparam DataType OperationReadyData type
177 * @param input Input data - WILL BE MODIFIED
178 * @param a Scale factor
179 * @param b Exponential rate
180 * @param base Exponential base (default: e for natural exponential)
181 * @return Transformed data
182 */
183template <OperationReadyData DataType>
184DataType transform_exponential(DataType& input, double a, double b, double base = std::numbers::e)
185{
186 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
187
188 for (auto& span : target_data) {
189 std::ranges::transform(span, span.begin(),
190 [a, b, base](double x) {
191 if (base == std::numbers::e) {
192 return a * std::exp(b * x);
193 }
194 return a * std::pow(base, b * x);
195 });
196 }
197
198 auto reconstructed_data = target_data
199 | std::views::transform([](const auto& span) {
200 return std::vector<double>(span.begin(), span.end());
201 })
202 | std::ranges::to<std::vector>();
203
204 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
205}
206
207/**
208 * @brief Exponential transformation y = a * base^(b * x) (OUT-OF-PLACE)
209 * @tparam DataType OperationReadyData type
210 * @param input Input data - will NOT be modified
211 * @param a Scale factor
212 * @param b Exponential rate
213 * @param working_buffer Buffer for operations (will be resized if needed)
214 * @param b Exponential rate
215 * @return Transformed data
216 */
217template <OperationReadyData DataType>
218DataType transform_exponential(DataType& input, double a, double b, std::vector<std::vector<double>>& working_buffer, double base = std::numbers::e)
219{
220 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
221
222 for (auto& span : target_data) {
223 std::ranges::transform(span, span.begin(),
224 [a, b, base](double x) {
225 if (base == std::numbers::e) {
226 return a * std::exp(b * x);
227 }
228 return a * std::pow(base, b * x);
229 });
230 }
231
232 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
233}
234
235/**
236 * @brief Logarithmic transformation y = a * log_base(b * x + c) (IN-PLACE)
237 * @tparam DataType OperationReadyData type
238 * @param input Input data - WILL BE MODIFIED
239 * @param a Scale factor
240 * @param b Input scale factor
241 * @param c Offset to ensure positive argument
242 * @param base Logarithm base (default: e for natural log)
243 * @return Transformed data
244 */
245template <OperationReadyData DataType>
246DataType transform_logarithmic(DataType& input, double a, double b, double c = 1.0, double base = std::numbers::e)
247{
248 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
249
250 double log_base_factor = (base == std::numbers::e) ? 1.0 : (1.0 / std::log(base));
251
252 for (auto& span : target_data) {
253 std::ranges::transform(span, span.begin(),
254 [a, b, c, log_base_factor](double x) {
255 double arg = b * x + c;
256 if (arg <= 0.0)
257 return 0.0;
258 return a * std::log(arg) * log_base_factor;
259 });
260 }
261
262 auto reconstructed_data = target_data
263 | std::views::transform([](const auto& span) {
264 return std::vector<double>(span.begin(), span.end());
265 })
266 | std::ranges::to<std::vector>();
267
268 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
269}
270
271/**
272 * @brief Logarithmic transformation y = a * log_base(b * x + c) (OUT-OF-PLACE)
273 * @tparam DataType OperationReadyData type
274 * @param input Input data - will NOT be modified
275 * @param a Scale factor
276 * @param b Input scale factor
277 * @param c Offset to ensure positive argument
278 * @param working_buffer Buffer for operations (will be resized if needed)
279 * @return Transformed data
280 */
281template <OperationReadyData DataType>
282DataType transform_logarithmic(DataType& input, double a, double b, double c, std::vector<std::vector<double>>& working_buffer, double base = std::numbers::e)
283{
284 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
285
286 double log_base_factor = (base == std::numbers::e) ? 1.0 : (1.0 / std::log(base));
287
288 for (auto& span : target_data) {
289 std::ranges::transform(span, span.begin(),
290 [a, b, c, log_base_factor](double x) {
291 double arg = b * x + c;
292 if (arg <= 0.0)
293 return 0.0;
294 return a * std::log(arg) * log_base_factor;
295 });
296 }
297
298 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
299}
300
301/**
302 * @brief Trigonometric transformation using specified function (IN-PLACE)
303 * @tparam DataType OperationReadyData type
304 * @tparam TrigFunc Trigonometric function type
305 * @param input Input data - WILL BE MODIFIED
306 * @param trig_func Trigonometric function (sin, cos, tan, etc.)
307 * @param frequency Frequency scaling factor
308 * @param amplitude Amplitude scaling factor
309 * @param phase Phase offset
310 * @return Transformed data
311 */
312template <OperationReadyData DataType, typename TrigFunc>
313 requires std::invocable<TrigFunc, double>
314DataType transform_trigonometric(DataType& input,
315 TrigFunc trig_func,
316 double frequency = 1.0,
317 double amplitude = 1.0,
318 double phase = 0.0)
319{
320 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
321
322 for (auto& span : target_data) {
323 std::ranges::transform(span, span.begin(),
324 [trig_func, frequency, amplitude, phase](double x) {
325 return amplitude * trig_func(frequency * x + phase);
326 });
327 }
328
329 auto reconstructed_data = target_data
330 | std::views::transform([](const auto& span) {
331 return std::vector<double>(span.begin(), span.end());
332 })
333 | std::ranges::to<std::vector>();
334
335 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
336}
337
338/**
339 * @brief Trigonometric transformation using specified function (OUT-OF-PLACE)
340 * @tparam DataType OperationReadyData type
341 * @tparam TrigFunc Trigonometric function type
342 * @param input Input data - will NOT be modified
343 * @param trig_func Trigonometric function (sin, cos, tan, etc.)
344 * @param frequency Frequency scaling factor
345 * @param amplitude Amplitude scaling factor
346 * @param phase Phase offset
347 * @param working_buffer Buffer for operations (will be resized if needed)
348 * @return Transformed data
349 */
350template <OperationReadyData DataType, typename TrigFunc>
351 requires std::invocable<TrigFunc, double>
352DataType transform_trigonometric(DataType& input,
353 TrigFunc trig_func,
354 double frequency,
355 double amplitude,
356 double phase,
357 std::vector<std::vector<double>>& working_buffer)
358{
359 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
360
361 for (auto& span : target_data) {
362 std::ranges::transform(span, span.begin(),
363 [trig_func, frequency, amplitude, phase](double x) {
364 return amplitude * trig_func(frequency * x + phase);
365 });
366 }
367
368 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
369}
370
371/**
372 * @brief Quantization transformation (bit reduction) using C++20 features (IN-PLACE)
373 * @tparam DataType OperationReadyData type
374 * @param input Input data - WILL BE MODIFIED
375 * @param bits Number of bits for quantization
376 * @return Quantized data
377 */
378template <OperationReadyData DataType>
379DataType transform_quantize(DataType& input, uint8_t bits)
380{
381 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
382
383 const double levels = std::pow(2.0, bits) - 1.0;
384
385 for (auto& span : target_data) {
386 std::ranges::transform(span, span.begin(),
387 [levels](double x) {
388 double clamped = std::clamp(x, -1.0, 1.0);
389 return std::round(clamped * levels) / levels;
390 });
391 }
392
393 auto reconstructed_data = target_data
394 | std::views::transform([](const auto& span) {
395 return std::vector<double>(span.begin(), span.end());
396 })
397 | std::ranges::to<std::vector>();
398
399 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
400}
401
402/**
403 * @brief Quantization transformation (bit reduction) using C++20 features (OUT-OF-PLACE)
404 * @tparam DataType OperationReadyData type
405 * @param input Input data - will NOT be modified
406 * @param bits Number of bits for quantization
407 * @param working_buffer Buffer for operations (will be resized if needed)
408 * @return Quantized data
409 */
410template <OperationReadyData DataType>
411DataType transform_quantize(DataType& input, uint8_t bits, std::vector<std::vector<double>>& working_buffer)
412{
413 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
414
415 const double levels = std::pow(2.0, bits) - 1.0;
416
417 for (auto& span : target_data) {
418 std::ranges::transform(span, span.begin(),
419 [levels](double x) {
420 double clamped = std::clamp(x, -1.0, 1.0);
421 return std::round(clamped * levels) / levels;
422 });
423 }
424
425 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
426}
427
428/**
429 * @brief Clamp transformation using C++20 ranges (IN-PLACE)
430 * @tparam DataType OperationReadyData type
431 * @param input Input data - WILL BE MODIFIED
432 * @param min_val Minimum value
433 * @param max_val Maximum value
434 * @return Clamped data
435 */
436template <OperationReadyData DataType>
437DataType transform_clamp(DataType& input, double min_val, double max_val)
438{
439 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
440
441 for (auto& span : target_data) {
442 std::ranges::transform(span, span.begin(),
443 [min_val, max_val](double x) { return std::clamp(x, min_val, max_val); });
444 }
445
446 auto reconstructed_data = target_data
447 | std::views::transform([](const auto& span) {
448 return std::vector<double>(span.begin(), span.end());
449 })
450 | std::ranges::to<std::vector>();
451
452 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
453}
454
455/**
456 * @brief Clamp transformation using C++20 ranges (OUT-OF-PLACE)
457 * @tparam DataType OperationReadyData type
458 * @param input Input data - will NOT be modified
459 * @param min_val Minimum value
460 * @param max_val Maximum value
461 * @param working_buffer Buffer for operations (will be resized if needed)
462 * @return Clamped data
463 */
464template <OperationReadyData DataType>
465DataType transform_clamp(DataType& input, double min_val, double max_val, std::vector<std::vector<double>>& working_buffer)
466{
467 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
468
469 for (auto& span : target_data) {
470 std::ranges::transform(span, span.begin(),
471 [min_val, max_val](double x) { return std::clamp(x, min_val, max_val); });
472 }
473
474 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
475}
476
477/**
478 * @brief Wrap transformation (modulo operation) using C++20 ranges (IN-PLACE)
479 * @tparam DataType OperationReadyData type
480 * @param input Input data - WILL BE MODIFIED
481 * @param wrap_range Wrap range [0, wrap_range)
482 * @return Wrapped data
483 */
484template <OperationReadyData DataType>
485DataType transform_wrap(DataType& input, double wrap_range)
486{
487 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
488
489 for (auto& span : target_data) {
490 std::ranges::transform(span, span.begin(),
491 [wrap_range](double x) {
492 return x - wrap_range * std::floor(x / wrap_range);
493 });
494 }
495
496 auto reconstructed_data = target_data
497 | std::views::transform([](const auto& span) {
498 return std::vector<double>(span.begin(), span.end());
499 })
500 | std::ranges::to<std::vector>();
501
502 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
503}
504
505/**
506 * @brief Wrap transformation (modulo operation) using C++20 ranges (OUT-OF-PLACE)
507 * @tparam DataType OperationReadyData type
508 * @param input Input data - will NOT be modified
509 * @param wrap_range Wrap range [0, wrap_range)
510 * @param working_buffer Buffer for operations (will be resized if needed)
511 * @return Wrapped data
512 */
513template <OperationReadyData DataType>
514DataType transform_wrap(DataType& input, double wrap_range, std::vector<std::vector<double>>& working_buffer)
515{
516 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
517
518 for (auto& span : target_data) {
519 std::ranges::transform(span, span.begin(),
520 [wrap_range](double x) {
521 return x - wrap_range * std::floor(x / wrap_range);
522 });
523 }
524
525 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
526}
527
528/**
529 * @brief Normalize transformation using existing infrastructure (IN-PLACE)
530 * @tparam DataType OperationReadyData type
531 * @param input Input data - WILL BE MODIFIED
532 * @param target_range Target range [min, max]
533 * @return Normalized data
534 */
535template <OperationReadyData DataType>
536DataType transform_normalize(DataType& input, const std::pair<double, double>& target_range = { -1.0, 1.0 })
537{
538 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
539
540 for (auto& span : target_data) {
541 auto [min_it, max_it] = std::ranges::minmax_element(span);
542 double current_min = *min_it;
543 double current_max = *max_it;
544
545 if (current_max == current_min)
546 return input;
547
548 double current_range = current_max - current_min;
549 double target_min = target_range.first;
550 double target_max = target_range.second;
551 double target_span = target_max - target_min;
552
553 std::ranges::transform(span, span.begin(),
554 [current_min, current_range, target_span, target_min](double x) {
555 return ((x - current_min) / current_range) * target_span + target_min;
556 });
557 }
558
559 auto reconstructed_data = target_data
560 | std::views::transform([](const auto& span) {
561 return std::vector<double>(span.begin(), span.end());
562 })
563 | std::ranges::to<std::vector>();
564
565 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
566}
567
568/**
569 * @brief Normalize transformation using existing infrastructure (OUT-OF-PLACE)
570 * @tparam DataType OperationReadyData type
571 * @param input Input data - will NOT be modified
572 * @param target_range Target range [min, max]
573 * @param working_buffer Buffer for operations (will be resized if needed)
574 * @return Normalized data
575 */
576template <OperationReadyData DataType>
577DataType transform_normalize(DataType& input, const std::pair<double, double>& target_range, std::vector<std::vector<double>>& working_buffer)
578{
579 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
580
581 for (auto& span : target_data) {
582 auto [min_it, max_it] = std::ranges::minmax_element(span);
583 double current_min = *min_it;
584 double current_max = *max_it;
585
586 if (current_max == current_min)
587 return input;
588
589 double current_range = current_max - current_min;
590 double target_min = target_range.first;
591 double target_max = target_range.second;
592 double target_span = target_max - target_min;
593
594 std::ranges::transform(span, span.begin(),
595 [current_min, current_range, target_span, target_min](double x) {
596 return ((x - current_min) / current_range) * target_span + target_min;
597 });
598 }
599
600 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
601}
602
603inline void interpolate(std::span<double> input, std::vector<double>& output, uint32_t target_size)
604{
605 auto indices = std::views::iota(size_t { 0 }, target_size);
606
607 std::ranges::transform(indices, output.begin(),
608 [&input, target_size](size_t i) {
609 double pos = static_cast<double>(i) * double(input.size() - 1) / (target_size - 1);
610 auto idx = static_cast<size_t>(pos);
611 double frac = pos - (double)idx;
612
613 if (idx + 1 < input.size()) {
614 return input[idx] * (1.0 - frac) + input[idx + 1] * frac;
615 }
616
617 return input[idx];
618 });
619}
620
621/**
622 * @brief Linear interpolation between data points using C++20 ranges (IN-PLACE)
623 * @tparam DataType OperationReadyData type
624 * @param input Input data - WILL BE MODIFIED/RESIZED
625 * @param target_size Target size after interpolation
626 * @return Interpolated data
627 */
628template <OperationReadyData DataType>
629DataType interpolate_linear(DataType& input, size_t target_size)
630{
631 auto [data_span, structure_info] = OperationHelper::extract_structured_double(input);
632
633 std::vector<std::vector<double>> interpolated;
634 for (auto& span : data_span) {
635 if (target_size != span.size()) {
636 std::vector<double> sub_data(target_size);
637 interpolate(span, std::ref(sub_data), target_size);
638 interpolated.push_back(std::move(sub_data));
639 } else {
640 interpolated.emplace_back(span.begin(), span.end());
641 }
642 }
643
644 input = OperationHelper::reconstruct_from_double<DataType>(interpolated, structure_info);
645 return input;
646}
647
648/**
649 * @brief Linear interpolation between data points using C++20 ranges (OUT-OF-PLACE)
650 * @tparam DataType OperationReadyData type
651 * @param input Input data - will NOT be modified
652 * @param target_size Target size after interpolation
653 * @param working_buffer Buffer for operations (will be resized if needed)
654 * @return Interpolated data
655 */
656template <OperationReadyData DataType>
657DataType interpolate_linear(DataType& input, size_t target_size, std::vector<std::vector<double>>& working_buffer)
658{
659 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
660
661 for (size_t i = 0; i < target_data.size(); i++) {
662 if (target_size != target_data[i].size()) {
663 working_buffer[i].resize(target_size);
664 interpolate(target_data[i], working_buffer[i], target_size);
665 }
666 }
667
668 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
669}
670
671/**
672 * @brief Cubic interpolation between data points using C++20 ranges (IN-PLACE)
673 * @tparam DataType OperationReadyData type
674 * @param input Input data - WILL BE MODIFIED/RESIZED
675 * @param target_size Target size after interpolation
676 * @return Interpolated data
677 */
678template <OperationReadyData DataType>
679DataType interpolate_cubic(DataType& input, size_t target_size)
680{
681 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
682
683 bool needs_resize = false;
684 for (const auto& span : target_data) {
685 if (target_size != span.size()) {
686 needs_resize = true;
687 break;
688 }
689 }
690
691 if (!needs_resize) {
692 return input;
693 }
694
695 std::vector<std::vector<double>> result(target_data.size());
696
697 for (size_t ch = 0; ch < target_data.size(); ++ch) {
698 const auto& data_span = target_data[ch];
699 auto& interpolated = result[ch];
700 interpolated.resize(target_size);
701
702 if (target_size <= 1 || data_span.size() <= 1) {
703 std::fill(interpolated.begin(), interpolated.end(),
704 data_span.empty() ? 0.0 : data_span[0]);
705 continue;
706 }
707
708 auto indices = std::views::iota(size_t { 0 }, target_size);
709 std::ranges::transform(indices, interpolated.begin(),
710 [&data_span, target_size](size_t i) {
711 double pos = static_cast<double>(i) * (data_span.size() - 1) / (target_size - 1);
712 int idx = static_cast<int>(pos);
713 double frac = pos - idx;
714
715 auto get_sample = [&data_span](int i) {
716 return data_span[std::clamp(i, 0, static_cast<int>(data_span.size() - 1))];
717 };
718
719 double y0 = get_sample(idx - 1);
720 double y1 = get_sample(idx);
721 double y2 = get_sample(idx + 1);
722 double y3 = get_sample(idx + 2);
723
724 double a = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;
725 double b = y0 - 2.5 * y1 + 2.0 * y2 - 0.5 * y3;
726 double c = -0.5 * y0 + 0.5 * y2;
727 double d = y1;
728
729 return ((a * frac + b) * frac + c) * frac + d;
730 });
731 }
732
733 return OperationHelper::reconstruct_from_double<DataType>(result, structure_info);
734}
735
736/**
737 * @brief Cubic interpolation between data points using C++20 ranges (OUT-OF-PLACE)
738 * @tparam DataType OperationReadyData type
739 * @param input Input data - will NOT be modified
740 * @param target_size Target size after interpolation
741 * @param working_buffer Buffer for operations (will be resized if needed)
742 * @return Interpolated data
743 */
744template <OperationReadyData DataType>
745DataType interpolate_cubic(DataType& input, size_t target_size, std::vector<std::vector<double>>& working_buffer)
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}
798
799} // namespace MayaFlux::Yantra
static std::tuple< std::vector< std::span< double > >, DataStructureInfo > extract_structured_double(T &compute_data)
Extract structured double data from IO container or direct ComputeData with automatic container handl...
static auto setup_operation_buffer(T &input, std::vector< std::vector< double > > &working_buffer)
Setup operation buffer from IO or ComputeData type.
void interpolate(std::span< double > input, std::vector< double > &output, uint32_t target_size)
DataType transform_wrap(DataType &input, double wrap_range)
Wrap transformation (modulo operation) using C++20 ranges (IN-PLACE)
DataType interpolate_cubic(DataType &input, size_t target_size)
Cubic interpolation between data points using C++20 ranges (IN-PLACE)
DataType interpolate_linear(DataType &input, size_t target_size)
Linear interpolation between data points using C++20 ranges (IN-PLACE)
DataType transform_polynomial(DataType &input, const std::vector< double > &coefficients)
Polynomial transformation using existing Generator::Polynomial (IN-PLACE)
DataType transform_linear(DataType &input, double a, double b)
Linear transformation y = ax + b using C++20 ranges (IN-PLACE)
DataType transform_logarithmic(DataType &input, double a, double b, double c=1.0, double base=std::numbers::e)
Logarithmic transformation y = a * log_base(b * x + c) (IN-PLACE)
DataType transform_normalize(DataType &input, const std::pair< double, double > &target_range={ -1.0, 1.0 })
Normalize transformation using existing infrastructure (IN-PLACE)
DataType transform_exponential(DataType &input, double a, double b, double base=std::numbers::e)
Exponential transformation y = a * base^(b * x) (IN-PLACE)
DataType transform_power(DataType &input, double exponent)
Power transformation y = x^exponent (IN-PLACE)
DataType transform_quantize(DataType &input, uint8_t bits)
Quantization transformation (bit reduction) using C++20 features (IN-PLACE)
DataType transform_clamp(DataType &input, double min_val, double max_val)
Clamp transformation using C++20 ranges (IN-PLACE)
DataType transform_trigonometric(DataType &input, TrigFunc trig_func, double frequency=1.0, double amplitude=1.0, double phase=0.0)
Trigonometric transformation using specified function (IN-PLACE)