///|
/// Missing/excluded cells are distinct from nonrecurrent eligible cells.
pub struct Recurrence {
height : Int32
width : Int32
bits : Array[Bool]
mask : Array[Bool]
} derive(Debug, Eq)
///|
pub extend Recurrence with @debug.Debug::{to_repr}
///|
pub extend Recurrence with Eq::{equal, not_equal}
///|
fn cell_budget(rows : Int32, cols : Int32, max_cells : Int32) -> Bool {
rows > 0 &&
cols > 0 &&
max_cells > 0 &&
max_cells <= 1048576 &&
rows <= max_cells / cols
}
///|
pub fn recurrence(
a : Trajectory,
threshold : Double,
metric : Metric,
theiler : Int32,
max_cells : Int32,
) -> Result[Recurrence, RqaError] {
if !finite(threshold) || threshold < 0.0 {
return Err(Invalid("threshold"))
}
if theiler < 0 || theiler > 4096 {
return Err(Invalid("Theiler width"))
}
if !cell_budget(a.samples, a.samples, max_cells) {
return Err(Limit("recurrence cell budget"))
}
let n = a.samples
let mask = Array::makei(n * n, fn(p) { (p / n - p % n).abs() > theiler })
let bits = Array::makei(n * n, fn(p) {
mask[p] && point_distance(a, p / n, a, p % n, metric) <= threshold
})
Ok({ height: n, width: n, bits, mask, })
}
///|
pub fn Recurrence::shape(self : Recurrence) -> (Int32, Int32) {
(self.height, self.width)
}
///|
pub fn Recurrence::cell(
self : Recurrence,
i : Int32,
j : Int32,
) -> Result[Bool?, RqaError] {
if i < 0 || i >= self.height || j < 0 || j >= self.width {
return Err(Invalid("matrix index"))
}
let p = i * self.width + j
Ok(if self.mask[p] { Some(self.bits[p]) } else { None })
}
///|
pub fn Recurrence::counts(self : Recurrence) -> (Int32, Int32) {
let mut yes = 0
let mut eligible = 0
for i in 0.. Double? {
let (yes, all) = self.counts()
if all == 0 {
None
} else {
Some(yes.to_double() / all.to_double())
}
}