// Small numeric helpers shared by every indicator package.
///|
/// Quiet NaN used to mark positions where an indicator has no value yet.
pub fn nan() -> Double {
let zero = 0.0
zero / zero
}
///|
/// Returns true when the value is NaN.
pub fn is_nan(x : Double) -> Bool {
x != x
}
///|
/// Integer square root (floor of the square root), used by HMA to size its
/// final WMA window.
pub fn isqrt(n : Int32) -> Int32 {
if n <= 0 {
return 0
}
let mut x = n
let mut y = (x + 1) / 2
while y < x {
x = y
y = (x + n / x) / 2
}
x
}
///|
/// Shared validation for a single price series plus a window length: the
/// input must be non-empty and the period positive and not larger than the
/// input. Indicators are otherwise graceful — positions without a full
/// window are simply NaN in the output.
pub fn validate_series(
data : Array[Double],
period : Int32,
) -> Unit raise TaError {
if data.length() == 0 {
raise EmptyInput
}
if period <= 0 {
raise InvalidPeriod("period must be positive, got \{period}")
}
if period > data.length() {
raise NotEnoughData(
"period \{period} exceeds input length \{data.length()}",
)
}
}
///|
/// Shared validation for a `Candles` series: non-empty and equal-length
/// columns.
pub fn validate_candles(candles : Candles) -> Unit raise TaError {
let n = candles.close.length()
if n == 0 {
raise EmptyInput
}
if candles.open.length() != n {
raise MismatchedLength("open column length differs from close")
}
if candles.high.length() != n {
raise MismatchedLength("high column length differs from close")
}
if candles.low.length() != n {
raise MismatchedLength("low column length differs from close")
}
if candles.volume.length() != n {
raise MismatchedLength("volume column length differs from close")
}
}