1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::common_simd::options::{validate_inputs, validate_options};
use crate::indicators::msw::{
dot_product_simd, min_data, multiplier, output_length, precompute_twiddles, IndicatorState,
State, INPUTS_WIDTH, OPTIONS_WIDTH,
};
use crate::indicators::simd_indicators::msw_simd::options::calc_sdft;
use crate::indicators::simd_indicators::msw_simd::SimdState;
use crate::indicators::simd_indicators::road_train::{Asset, Driver, PrimeMover};
use crate::types::IndicatorError;
use std::simd::Simd;
/// SIMD driver for the Mesa Sine Wave (MSW) indicator, processing `N` option-set lanes per
/// scheduling epoch using a vectorized Sliding DFT (SDFT) recurrence — O(1) per bar.
struct MswDriver;
impl Driver<State, (usize, f64)> for MswDriver {
fn next_run<const N: usize>(
&mut self,
inputs: Vec<Vec<&[f64]>>,
mut outputs: Vec<Vec<&mut [f64]>>,
mut states: Vec<&mut State>,
options: Vec<Option<&(usize, f64)>>,
) {
let len = outputs[0][0].len();
// Gather per-lane SDFT accumulators and rotation phasors into a single SimdState.
let mut simd_state = SimdState::new(&mut states);
// i[lane] = period[lane]; increments by 1 each bar (mirrors SMA driver).
let mut i = {
let mut arr = [0usize; N];
for (lane, opt) in options.iter().enumerate() {
if let Some(&(period, _)) = opt {
arr[lane] = period;
}
}
arr
};
let real_ptrs = crate::extract_input_ptrs!(inputs, N, real_ptrs);
let (sine_line_ptr, lead_line_ptr) =
crate::extract_output_ptrs!(outputs, N, sine_line_ptr, lead_line_ptr);
for j in 0..len {
// old_sample: bar leaving the window — uniform index j across all lanes.
let old_sample = crate::extract_simd_inputs_at_index!(j, N, old @ real_ptrs);
// new_sample: bar entering the window — per-lane index i[lane].
let new_sample = crate::extract_simd_inputs_at_index_array!(i, N, new @ real_ptrs);
// Vectorized SDFT update — O(1) per bar, no per-bar trig.
let (sine, lead) = calc_sdft(&mut simd_state, new_sample, old_sample);
crate::write_simd_at_indices!(N, j,
sine_line_ptr => sine,
lead_line_ptr => lead
);
for idx in i.iter_mut() {
*idx += 1;
}
}
// Scatter updated accumulators back to per-lane states.
simd_state.write_states(&mut states);
}
}
/// Calculates the Mesa Sine Wave (MSW) indicator for one asset with `N` different option sets
/// simultaneously using a vectorized Sliding DFT (SDFT) — O(1) per bar after seeding.
///
/// Applies each of the `N` period configurations to the same shared input series, computing
/// sine and lead wave lines for all option sets in a single SIMD-accelerated pass via
/// [`PrimeMover`]. Each lane's SDFT is seeded once upfront with a full DFT over the first
/// `period` bars (O(period) trig, one-time cost), then the hot loop runs at O(1) per bar.
///
/// # Arguments
/// * `inputs` - Shared input: `inputs[0]` is the `real` price series.
/// * `options` - An array of `N` option sets; `options[i][0]` is the `period` for lane `i`.
/// * `_optional_outputs` - Unused; MSW has no optional outputs.
///
/// # Returns
/// `Ok((outputs, states))` where `outputs[i][0]` is the `msw_sine` series and
/// `outputs[i][1]` is the `msw_lead` series for option set `i`, and `states[i]` is
/// the final [`IndicatorState`] for option set `i`.
/// Returns `Err(IndicatorError)` if any input slice is too short or options are invalid.
pub fn indicator_by_options<const N: usize>(
inputs: &[&[f64]; INPUTS_WIDTH],
options: &[&[f64; OPTIONS_WIDTH]; N],
_optional_outputs: Option<&[bool]>,
) -> Result<(Vec<Vec<Vec<f64>>>, Vec<IndicatorState>), IndicatorError> {
validate_inputs::<OPTIONS_WIDTH>(inputs, options, min_data)?;
validate_options(options, None)?;
let real = inputs[0];
let params: [(usize, f64); N] = std::array::from_fn(|i| {
let period = options[i][0] as usize;
(period, multiplier(period))
});
let mut road_train = PrimeMover::<N, State, (usize, f64)>::new();
let mut output_buffers = Vec::with_capacity(N);
for (i, &(period, mult)) in params.iter().enumerate() {
// Seed the SDFT once with a full DFT over the first `period` bars.
let (cos_tw, sin_tw) = precompute_twiddles(period, mult);
let (rp, ip) = dot_product_simd::<8>(&real[..period], &cos_tw, &sin_tw);
let state = State {
rp,
ip,
..State::new(mult)
};
let capacity = output_length(real.len(), options[i]);
let sine_line = crate::uninit_vec!(f64, capacity);
let lead_line = crate::uninit_vec!(f64, capacity);
let mut output_buffer = vec![sine_line, lead_line];
let mut asset_outputs = Vec::with_capacity(output_buffer.len());
for j in 0..output_buffer.len() {
unsafe {
let buf = &mut output_buffer[j];
asset_outputs.push(std::slice::from_raw_parts_mut(buf.as_mut_ptr(), buf.len()));
}
}
road_train.add_asset(Asset::new(
vec![real],
asset_outputs,
i,
period, // inputs_idx — road_train starts reading from here
period, // start_offset — lookback bars prepended to each slice
state,
Some(¶ms[i]),
));
output_buffers.push(output_buffer);
}
let mut driver = MswDriver;
road_train.drive(&mut driver);
let states = params
.iter()
.map(|&(period, mult)| IndicatorState::new(real, period, mult))
.collect();
Ok((output_buffers, states))
}