use crate::context::ExecutionContext;
use crate::marshal::register_typed_fn_2;
use crate::module_exports::ModuleExports;
use crate::simd_rolling;
use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
use shape_ast::error::{Result, ShapeError};
use shape_value::KindedSlot;
use std::sync::Arc;
pub fn create_rolling_intrinsics_module() -> ModuleExports {
let mut module = ModuleExports::new("std::core::intrinsics::rolling");
module.description =
"Rolling-window intrinsics (typed entries; polymorphic-input intrinsics stay as legacy bodies pending M1-split sub-decision extension)"
.to_string();
register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
&mut module,
"__intrinsic_rolling_mean",
"Rolling mean (Simple Moving Average) over a fixed-size window",
[("series", "Array<number>"), ("window", "int")],
ConcreteType::ArrayNumber,
|series, window, _ctx| {
let data = series.as_slice();
if data.is_empty() {
return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
}
if window <= 0 {
return Err("Window size must be greater than 0".to_string());
}
let result = simd_rolling::rolling_mean(data, window as usize);
Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
},
);
register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
&mut module,
"__intrinsic_rolling_std",
"Rolling standard deviation (Welford's algorithm) over a fixed-size window",
[("series", "Array<number>"), ("window", "int")],
ConcreteType::ArrayNumber,
|series, window, _ctx| {
let data = series.as_slice();
if data.is_empty() {
return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
}
if window <= 0 {
return Err("Window size must be greater than 0".to_string());
}
let window = window as usize;
if window > data.len() {
return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![
f64::NAN;
data.len()
])));
}
let result = simd_rolling::rolling_std_welford(data, window);
Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
},
);
register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
&mut module,
"__intrinsic_ema",
"Exponential Moving Average with smoothing alpha = 2 / (period + 1)",
[("series", "Array<number>"), ("period", "int")],
ConcreteType::ArrayNumber,
|series, period, _ctx| {
let data = series.as_slice();
if data.is_empty() {
return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
}
if period <= 0 {
return Err("EMA period must be greater than 0".to_string());
}
let alpha = 2.0 / (period as f64 + 1.0);
let mut result = Vec::with_capacity(data.len());
let mut ema = data[0];
result.push(ema);
for &price in &data[1..] {
ema = alpha * price + (1.0 - alpha) * ema;
result.push(ema);
}
Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
},
);
module
}
pub fn intrinsic_rolling_sum(
_args: &[KindedSlot],
_ctx: &mut ExecutionContext,
) -> Result<KindedSlot> {
Err(ShapeError::RuntimeError {
message: "intrinsic_rolling_sum: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
location: None,
})
}
pub fn intrinsic_rolling_min(
_args: &[KindedSlot],
_ctx: &mut ExecutionContext,
) -> Result<KindedSlot> {
Err(ShapeError::RuntimeError {
message: "intrinsic_rolling_min: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
location: None,
})
}
pub fn intrinsic_rolling_max(
_args: &[KindedSlot],
_ctx: &mut ExecutionContext,
) -> Result<KindedSlot> {
Err(ShapeError::RuntimeError {
message: "intrinsic_rolling_max: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
location: None,
})
}