///|
pub struct Fold {
  training_indices : Array[Int]
  validation_indices : Array[Int]
}

///|
pub fn make_folds(sample_size : Int, folds : Int, seed : UInt64) -> Array[Fold] {
  let actual_folds = if folds < 2 {
    1
  } else if folds > sample_size {
    sample_size
  } else {
    folds
  }
  if sample_size <= 0 {
    return []
  }
  let order = Array::new(capacity=sample_size)
  for i in 0.. Double {
  let n = if predictions.length() < actual.length() {
    predictions.length()
  } else {
    actual.length()
  }
  if n == 0 {
    return 0.0
  }
  let mut correct = 0
  for i in 0.. Double {
  let partitions = make_folds(covariates.length(), folds, seed)
  let scores = Array::new()
  let labels = Array::new()
  for partition in partitions {
    let train_x : Array[Array[Double]] = Array::new()
    let train_t : Array[Bool] = Array::new()
    for index in partition.training_indices {
      train_x.push(covariates[index])
      train_t.push(treatment[index])
    }
    let model = fit_logistic_regression(train_x, train_t, max_iterations=200)
    for index in partition.validation_indices {
      scores.push(model_predict(model, covariates[index]))
      labels.push(treatment[index])
    }
  }
  let predicted = Array::new(capacity=scores.length())
  for score in scores {
    predicted.push(score >= 0.0)
  }
  accuracy_from_labels(predicted, labels)
}

///|
pub fn cross_validate_outcome_rmse(
  covariates : Array[Array[Double]],
  outcomes : Array[Double],
  folds : Int,
  seed : UInt64,
) -> Double {
  let partitions = make_folds(covariates.length(), folds, seed)
  let errors = Array::new()
  for partition in partitions {
    let train_x : Array[Array[Double]] = Array::new()
    let train_y : Array[Double] = Array::new()
    for index in partition.training_indices {
      train_x.push(covariates[index])
      train_y.push(outcomes[index])
    }
    let model = fit_linear_outcome_model(train_x, train_y)
    for index in partition.validation_indices {
      let prediction = model_predict(model, covariates[index])
      let difference = prediction - outcomes[index]
      errors.push(difference * difference)
    }
  }
  if errors.length() == 0 {
    0.0
  } else {
    mean(errors).sqrt()
  }
}

///|
pub fn auc_score(
  probabilities : Array[Double],
  treatment : Array[Bool],
) -> Double {
  let positive = Array::new()
  let negative = Array::new()
  let n = if probabilities.length() < treatment.length() {
    probabilities.length()
  } else {
    treatment.length()
  }
  for i in 0.. n {
        wins += 1.0
      } else if p == n {
        wins += 0.5
      }
    }
  }
  wins / (positive.length() * negative.length()).to_double()
}