///|
pub(all) struct Fit {
  coefficients : Array[Double]
  fitted : Array[Double]
  residuals : Array[Double]
  sse : Double
  dof : Int32
  variance : Double?
} derive(Debug, Eq)

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

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

///|
pub fn least_squares(
  a : Array[Array[Double]],
  responses : Array[Double],
  weights~ : Array[Double] = [],
  tol~ : Double = 1.0e-10,
) -> Fit raise DesignError {
  tolerance(tol)
  let (n, p) = matrix_shape(a)
  if p > 64 || n < p {
    raise Budget("least squares requires rows >= columns and <=64 terms")
  }
  if responses.length() != n || (weights.length() != 0 && weights.length() != n) {
    raise Invalid("response/weight count mismatch")
  }
  let work = zeros(n, p)
  let y = Array::make(n, 0.0)
  for i in 0.. scales[j] {
        scales[j] = work[i][j].abs()
      }
    }
    if scales[j] == 0.0 {
      raise Singular("zero model column")
    }
    for i in 0.. j ]
  let r = zeros(p, p)
  let qty = Array::make(p, 0.0)
  for k in 0..

best { best = norm pivot = j } } if best <= tol * tol { raise Singular("model is not identifiable at requested tolerance") } if pivot != k { for i in 0..= 0; k = k - 1 { let mut value = qty[k] for j in (k + 1)..

0 { Some(sse / dof.to_double()) } else { None }, } } ///| pub fn fit_plan( p : Plan, model : Array[Term], responses : Array[Double], ) -> Fit raise DesignError { least_squares(design_matrix(p, model), responses) }