///|
/// Maintenance policy for preventive and corrective actions.
pub(all) enum MaintenanceAction {
  Inspect
  PreventiveReplace
  CorrectiveRepair
  NoAction
} derive(Debug, Eq)

///|
pub struct MaintenanceSchedule {
  ages : Array[Double]
  actions : Array[MaintenanceAction]
  expected_costs : Array[Double]
  expected_availability : Array[Double]
  selected_age : Double
}

///|
pub fn maintenance_schedule(
  ages~ : Array[Double],
  actions~ : Array[MaintenanceAction],
  expected_costs~ : Array[Double],
  expected_availability~ : Array[Double],
  selected_age~ : Double,
) -> MaintenanceSchedule {
  { ages, actions, expected_costs, expected_availability, selected_age }
}

///|
pub fn age_replacement_schedule(
  model : ReliabilityModel,
  policy_cost : Double,
  repair_cost : Double,
  start_age : Double,
  end_age : Double,
  steps : Int,
) -> MaintenanceSchedule {
  let ages = linspace(start_age, end_age, steps)
  let costs = ages.map(age => {
    let failure_probability = model.cdf(age)
    policy_cost / age + repair_cost * failure_probability / age
  })
  let availability = ages.map(age => model.survival(age))
  let mut best = 0
  for i in 1.. Double {
  let mut best_interval = 1.0
  let mut best_cost = 1.0e300
  for interval in linspace(0.1, 100.0, 200) {
    let availability = model.survival(interval)
    if availability >= target_availability {
      let cost = cost_of_inspection / interval +
        cost_of_failure * (1.0 - availability) / interval
      if cost < best_cost {
        best_cost = cost
        best_interval = interval
      }
    }
  }
  best_interval
}

///|
pub fn renewal_reward_rate(
  model : ReliabilityModel,
  replacement_cost : Double,
  reward_per_time : Double,
  age : Double,
) -> Double {
  if age <= 0.0 {
    abort("renewal age must be positive")
  }
  let survival = model.survival(age)
  let expected_cycle = age * survival + model_mean(model) * (1.0 - survival)
  (reward_per_time * expected_cycle - replacement_cost) / expected_cycle
}

///|
pub fn optimum_renewal_age(
  model : ReliabilityModel,
  replacement_cost : Double,
  reward_per_time : Double,
  start : Double,
  stop : Double,
  steps : Int,
) -> Double {
  let ages = linspace(start, stop, steps)
  let rates = ages.map(age => {
    renewal_reward_rate(model, replacement_cost, reward_per_time, age)
  })
  let mut best = 0
  for i in 1.. rates[best] {
      best = i
    }
  }
  ages[best]
}

///|
pub fn repair_queue_utilization(
  arrival_rate : Double,
  service_rate : Double,
) -> Double {
  if arrival_rate < 0.0 || service_rate <= 0.0 || arrival_rate >= service_rate {
    1.0
  } else {
    arrival_rate / service_rate
  }
}

///|
pub fn expected_repair_queue_length(
  arrival_rate : Double,
  service_rate : Double,
) -> Double {
  let utilization = repair_queue_utilization(arrival_rate, service_rate)
  if utilization >= 1.0 {
    1.0e300
  } else {
    utilization * utilization / (1.0 - utilization)
  }
}