//! `Sub` — two-input f64 difference (input 0 minus input 1), e.g. a fast/slow //! spread. The walking skeleton's second worked node: it gives the sim loop a //! real fan-out + join to run (two SMAs joining into one node), exercising //! multi-input `Ctx` access inside a running graph. use aura_core::{Ctx, InputSpec, Node, NodeSchema, Scalar, ScalarKind}; /// Two-input f64 difference: input 0 minus input 1. Emits `None` until both /// inputs have a value. #[derive(Default)] pub struct Sub; impl Sub { /// Build a `Sub` node. pub fn new() -> Self { Self } } impl Node for Sub { fn schema(&self) -> NodeSchema { NodeSchema { inputs: vec![ InputSpec { kind: ScalarKind::F64, lookback: 1 }, InputSpec { kind: ScalarKind::F64, lookback: 1 }, ], output: ScalarKind::F64, } } fn eval(&mut self, ctx: Ctx<'_>) -> Option { let a = ctx.f64_in(0); let b = ctx.f64_in(1); if a.is_empty() || b.is_empty() { return None; } Some(Scalar::F64(a[0] - b[0])) } } #[cfg(test)] mod tests { use super::*; use aura_core::AnyColumn; #[test] fn sub_is_difference_once_both_inputs_present() { let mut sub = Sub::new(); let mut inputs = vec![ AnyColumn::with_capacity(ScalarKind::F64, 1), AnyColumn::with_capacity(ScalarKind::F64, 1), ]; // only input 0 present -> None inputs[0].push(Scalar::F64(10.0)).unwrap(); assert_eq!(sub.eval(Ctx::new(&inputs)), None); // both present -> a - b inputs[1].push(Scalar::F64(4.0)).unwrap(); assert_eq!(sub.eval(Ctx::new(&inputs)), Some(Scalar::F64(6.0))); } }