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
//! R11-6 — `ARR(x)` is C's `toArray(ps,1)` (`aCalcPerform.c:1516-1518`), the same
//! promotion every other operator uses, applied IN PLACE.
//!
//! The port popped the operand as an `f64` and rebuilt a buffer from it, which broke
//! the operator at both ends:
//!
//! * an ARRAY operand was collapsed to its `a[0]` and rebroadcast — C leaves it alone,
//! window and all, because `toArray` tests `isDouble` first (`:145`).
//! * a NaN scalar was copied verbatim — C's `to_array` fills 0 for a NaN
//! (`:135-137`), so an aCalcout could not publish an all-NaN AVAL.
//!
//! Every expectation below is the output of a driver compiled from
//! `/home/stevek/work/epics-modules/calc/calcApp/src/{aCalcPerform,aCalcPostfix,calcUtil}.c`.
use epics_base_rs::calc::{ArrayInputs, ArrayStackValue, acalc};
fn inputs() -> ArrayInputs {
let mut i = ArrayInputs::new(4);
i.arrays[0] = vec![1.0, 2.0, 3.0, 4.0];
i
}
fn buf(expr: &str, inputs: &mut ArrayInputs) -> Vec<f64> {
match acalc(expr, inputs).expect("status 0") {
ArrayStackValue::Array(cell) => cell.buf().to_vec(),
other => panic!("ARR always yields an array, got {other:?}"),
}
}
/// C's `to_array` fills 0 for a NaN scalar (`:135-137`) — the ONE place aCalc keeps a
/// NaN out of an array. Compiled C, arraySize 4, A=NaN: `ARR(A)` is [0,0,0,0].
#[test]
fn r11_6_arr_of_a_nan_scalar_fills_zero() {
let mut i = inputs();
i.num_vars[0] = f64::NAN;
assert_eq!(buf("ARR(A)", &mut i), vec![0.0, 0.0, 0.0, 0.0]);
}
/// The negative control: an ordinary scalar is broadcast, not zeroed. Compiled C, A=5:
/// `ARR(A)` is [5,5,5,5].
#[test]
fn r11_6_arr_of_an_ordinary_scalar_broadcasts_it() {
let mut i = inputs();
i.num_vars[0] = 5.0;
assert_eq!(buf("ARR(A)", &mut i), vec![5.0, 5.0, 5.0, 5.0]);
}
/// An ARRAY operand passes straight through — `toArray` is a no-op on it. Compiled C,
/// AA=[1,2,3,4]: `ARR(AA)` is [1,2,3,4]. The port answered [1,1,1,1].
#[test]
fn r11_6_arr_of_an_array_is_a_no_op() {
assert_eq!(buf("ARR(AA)", &mut inputs()), vec![1.0, 2.0, 3.0, 4.0]);
}
/// And the window survives it, because `toArray` never reaches an array's numEl.
/// Compiled C: `ARR(AA[1,2])` is [2,3,0,0] and `AVG(ARR(AA[1,2]))` is 2.5 — the same
/// answer as `AVG(AA[1,2])`, which is the point: ARR must not flatten the window into
/// the buffer's zero tail (that would average to 1.25).
#[test]
fn r11_6_arr_preserves_the_window() {
assert_eq!(buf("ARR(AA[1,2])", &mut inputs()), vec![2.0, 3.0, 0.0, 0.0]);
assert_eq!(
acalc("AVG(ARR(AA[1,2]))", &mut inputs()).unwrap(),
ArrayStackValue::Double(2.5)
);
}