shape_runtime/intrinsics/
rolling.rs1use crate::context::ExecutionContext;
23use crate::marshal::register_typed_fn_2;
24use crate::module_exports::ModuleExports;
25use crate::simd_rolling;
26use crate::typed_module_exports::{ConcreteReturn, ConcreteType, TypedReturn};
27use shape_ast::error::{Result, ShapeError};
28use shape_value::KindedSlot;
29use std::sync::Arc;
30
31pub fn create_rolling_intrinsics_module() -> ModuleExports {
39 let mut module = ModuleExports::new("std::core::intrinsics::rolling");
40 module.description =
41 "Rolling-window intrinsics (typed entries; polymorphic-input intrinsics stay as legacy bodies pending M1-split sub-decision extension)"
42 .to_string();
43
44 register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
45 &mut module,
46 "__intrinsic_rolling_mean",
47 "Rolling mean (Simple Moving Average) over a fixed-size window",
48 [("series", "Array<number>"), ("window", "int")],
49 ConcreteType::ArrayNumber,
50 |series, window, _ctx| {
51 let data = series.as_slice();
52 if data.is_empty() {
53 return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
54 }
55 if window <= 0 {
56 return Err("Window size must be greater than 0".to_string());
57 }
58 let result = simd_rolling::rolling_mean(data, window as usize);
59 Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
60 },
61 );
62
63 register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
64 &mut module,
65 "__intrinsic_rolling_std",
66 "Rolling standard deviation (Welford's algorithm) over a fixed-size window",
67 [("series", "Array<number>"), ("window", "int")],
68 ConcreteType::ArrayNumber,
69 |series, window, _ctx| {
70 let data = series.as_slice();
71 if data.is_empty() {
72 return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
73 }
74 if window <= 0 {
75 return Err("Window size must be greater than 0".to_string());
76 }
77 let window = window as usize;
78 if window > data.len() {
79 return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![
80 f64::NAN;
81 data.len()
82 ])));
83 }
84 let result = simd_rolling::rolling_std_welford(data, window);
85 Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
86 },
87 );
88
89 register_typed_fn_2::<_, Arc<Vec<f64>>, i64>(
90 &mut module,
91 "__intrinsic_ema",
92 "Exponential Moving Average with smoothing alpha = 2 / (period + 1)",
93 [("series", "Array<number>"), ("period", "int")],
94 ConcreteType::ArrayNumber,
95 |series, period, _ctx| {
96 let data = series.as_slice();
97 if data.is_empty() {
98 return Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(vec![])));
99 }
100 if period <= 0 {
101 return Err("EMA period must be greater than 0".to_string());
102 }
103 let alpha = 2.0 / (period as f64 + 1.0);
104 let mut result = Vec::with_capacity(data.len());
105 let mut ema = data[0];
106 result.push(ema);
107 for &price in &data[1..] {
108 ema = alpha * price + (1.0 - alpha) * ema;
109 result.push(ema);
110 }
111 Ok(TypedReturn::Concrete(ConcreteReturn::ArrayF64(result)))
112 },
113 );
114
115 module
116}
117
118pub fn intrinsic_rolling_sum(
128 _args: &[KindedSlot],
129 _ctx: &mut ExecutionContext,
130) -> Result<KindedSlot> {
131 Err(ShapeError::RuntimeError {
132 message: "intrinsic_rolling_sum: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
133 location: None,
134 })
135}
136
137pub fn intrinsic_rolling_min(
140 _args: &[KindedSlot],
141 _ctx: &mut ExecutionContext,
142) -> Result<KindedSlot> {
143 Err(ShapeError::RuntimeError {
144 message: "intrinsic_rolling_min: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
145 location: None,
146 })
147}
148
149pub fn intrinsic_rolling_max(
152 _args: &[KindedSlot],
153 _ctx: &mut ExecutionContext,
154) -> Result<KindedSlot> {
155 Err(ShapeError::RuntimeError {
156 message: "intrinsic_rolling_max: pending Phase 2c intrinsic kind threading + M1-split — see ADR-006 §2.7.4".to_string(),
157 location: None,
158 })
159}