///|
pub(all) struct RateSelection {
  threshold : Double
  achieved_rate : Double
  requested_rate : Double
  matrix : Recurrence
} derive(Debug, Eq)

///|
pub extend RateSelection with @debug.Debug::{to_repr}

///|
pub extend RateSelection with Eq::{equal, not_equal}

///|
/// Smallest observed distance whose inclusive threshold reaches the target.
/// Ties are never split; zero selects the smallest observed distance, not an empty mask.
pub fn recurrence_at_rate(
  t : Trajectory,
  target : Double,
  metric : Metric,
  theiler : Int32,
  max_cells : Int32,
) -> Result[RateSelection, RqaError] {
  if !finite(target) || target < 0.0 || target > 1.0 {
    return Err(Invalid("target rate"))
  }
  if theiler < 0 || theiler > 4096 {
    return Err(Invalid("Theiler width"))
  }
  if !cell_budget(t.samples, t.samples, max_cells) || max_cells > 262144 {
    return Err(Limit("rate selection budget <=262144"))
  }
  let observed : Array[Double] = []
  for i in 0.. theiler {
        observed.push(point_distance(t, i, t, j, metric))
      }
    }
  }
  if observed.length() == 0 {
    return Err(Invalid("no eligible rate-selection pair"))
  }
  observed.sort()
  let mut rank = 0
  while rank + 1 < observed.length() &&
        (rank + 1).to_double() / observed.length().to_double() < target {
    rank += 1
  }
  let threshold = observed[rank]
  let matrix = recurrence(t, threshold, metric, theiler, max_cells).unwrap()
  Ok({
    threshold,
    achieved_rate: matrix.rate().unwrap(),
    requested_rate: target,
    matrix,
  })
}