#' A multi-linear model
#'
#' @export
gmlm_mlm <- function(X, F, sample.axis) {
    # get (set) problem (observation) dimensions
    modes <- seq_along(dim(X))[-sample.axis]
    dimX <- dim(X)[modes]
    sample.size <- dim(X)[sample.axis]
    if (!is.array(F)) {
        dim(F) <- c(1L, sample.size)[(seq_along(dim(X)) == sample.axis) + 1L]
    }
    dimF <- dim(F)[modes]

    # vectorize the tensor valued data (columns are the vectorized samples)
    matX <- mat(X, modes)
    matF <- mat(F, modes)

    # center
    matX <- matX - (meanX <- rowMeans(matX))
    matF <- matF - rowMeans(matF)

    # solve vectorized linear model
    B <- tcrossprod(matX, matF) %*% pinv(tcrossprod(matF))

    # decompose linear model solution as Kronecker product
    betas <- approx.kron(B, dimX, dimF)

    # reshape centered vectorized `X` and `F` into tensors (now, sample axis
    # is last axis)
    X <- `dim<-`(matX, c(dimX, sample.size))
    F <- `dim<-`(matF, c(dimF, sample.size))

    # and estimate covariances (sample axis is last axis)
    Sigmas <- mcov(X - mlm(F, betas), sample.axis = length(dim(X)))

    # finaly, invert covariances to get the scatter matrices `Omegas`
    Omegas <- Map(solve, Sigmas)

    list(
        eta1 = array(meanX, dim = dim(X)[-sample.axis]),
        betas = betas,
        Omegas = Omegas
    )
}
