///|
/// Result of comparing several deterministic construction strategies.
pub struct MultiStartRun {
  result : @core.SolveResult
  attempt_count : Int
  selected_strategy : @constructive.ConstructionStrategy
} derive(Debug)

///|
pub fn MultiStartRun::result(self : MultiStartRun) -> @core.SolveResult {
  self.result
}

///|
pub fn MultiStartRun::attempt_count(self : MultiStartRun) -> Int {
  self.attempt_count
}

///|
pub fn MultiStartRun::selected_strategy(
  self : MultiStartRun,
) -> @constructive.ConstructionStrategy {
  self.selected_strategy
}

///|
fn default_strategies() -> Array[@constructive.ConstructionStrategy] {
  [
    @constructive.ConstructionStrategy::ClarkeWright,
    @constructive.ConstructionStrategy::NearestNeighbor,
    @constructive.ConstructionStrategy::CheapestInsertion,
    @constructive.ConstructionStrategy::DemandFirst,
    @constructive.ConstructionStrategy::Radial,
    @constructive.ConstructionStrategy::Balanced,
  ]
}

///|
/// Solves the same instance from several independent constructive seeds.
pub fn solve_with_strategies(
  instance : @core.VrpInstance,
  strategies : Array[@constructive.ConstructionStrategy],
  config : @core.SolveConfig,
) -> MultiStartRun {
  let selected = if strategies.is_empty() {
    default_strategies()
  } else {
    strategies.copy()
  }
  let mut best_result : @core.SolveResult? = None
  let mut best_strategy = selected[0]
  for strategy in selected {
    let seed = @constructive.seed_by_strategy(instance, strategy, config)
    let result = solve_with_seed(instance, seed, config~)
    match best_result {
      None => {
        best_result = Some(result)
        best_strategy = strategy
      }
      Some(current) =>
        if result.best_solution.objective < current.best_solution.objective {
          best_result = Some(result)
          best_strategy = strategy
        } else {
          best_result = Some(current)
        }
    }
  }
  match best_result {
    None => {
      let result = solve(instance, config~)
      {
        result,
        attempt_count: 1,
        selected_strategy: @constructive.ConstructionStrategy::ClarkeWright,
      }
    }
    Some(result) =>
      {
        result,
        attempt_count: selected.length(),
        selected_strategy: best_strategy,
      }
  }
}