///|
pub fn class_weight(
  label : Int,
  value : Double,
) -> Result[ClassWeight, SvmError] {
  if !finite_double(value) || value <= 0.0 {
    return Err(InvalidClassWeight(label, value))
  }
  Ok({ weighted_label: label, weight_value: value })
}

///|
pub fn ClassWeight::label(self : ClassWeight) -> Int {
  self.weighted_label
}

///|
pub fn ClassWeight::value(self : ClassWeight) -> Double {
  self.weight_value
}

///|
fn class_multiplier(label : Int, weights : Array[ClassWeight]) -> Double {
  for weight in weights {
    if weight.weighted_label == label {
      return weight.weight_value
    }
  }
  1.0
}

///|
fn validate_class_weights(
  classes : Array[Int],
  weights : Array[ClassWeight],
) -> Result[Unit, SvmError] {
  for position, weight in weights {
    let mut known = false
    for label in classes {
      if label == weight.weighted_label {
        known = true
      }
    }
    if !known {
      return Err(UnknownClassLabel(weight.weighted_label))
    }
    for previous = 0; previous < position; previous = previous + 1 {
      if weights[previous].weighted_label == weight.weighted_label {
        return Err(DuplicateClassWeight(weight.weighted_label))
      }
    }
  }
  Ok(())
}

///|
/// Fits binary C-SVC with positive sample and optional class multipliers.
pub fn train_binary_weighted(
  data : Dataset,
  sample_weights : Array[Double],
  class_weights : Array[ClassWeight],
  config : BinaryConfig,
) -> Result[BinaryModel, SvmError] {
  if sample_weights.length() != data.row_count() {
    return Err(WeightLengthMismatch(data.row_count(), sample_weights.length()))
  }
  for index, value in sample_weights {
    if !finite_double(value) || value <= 0.0 {
      return Err(InvalidWeight(index, value))
    }
  }
  let classes = data.classes()
  match validate_class_weights(classes, class_weights) {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  let labels = data.labels()
  let bounds = Array::make(data.row_count(), 0.0)
  for index, sample_weight in sample_weights {
    let effective = config.c() *
      sample_weight *
      class_multiplier(labels[index], class_weights)
    if !finite_double(effective) || effective <= 0.0 {
      return Err(ZeroEffectiveWeight)
    }
    bounds[index] = effective
  }
  train_binary_bounds(data, config, bounds)
}