// SPDX-FileCopyrightText: 2021-2024 The Ikarus Developers mueller@ibb.uni-stuttgart.de
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "polyfit.hh"
namespace Ikarus::utils {

  /*
   * This function returns the polynom fitted onto the data passed in the least square sense.
   * It also returns the least square error.
   */
  std::tuple<Dune::Functions::Polynomial<double>, double> polyfit(const Eigen::Ref<const Eigen::VectorXd>& x,
                                                                  const Eigen::Ref<const Eigen::VectorXd>& y, int deg) {
    Eigen::MatrixXd A = Eigen::MatrixXd::Ones(x.size(), deg + 1);
    for (int j = 1; j < deg + 1; ++j)
      A.col(j) = A.col(j - 1).cwiseProduct(x);

    Eigen::FullPivHouseholderQR<Eigen::MatrixXd> qr(A.rows(), A.cols());
    qr.compute(A);
    Eigen::VectorXd coeffs = qr.solve(y);

    std::vector<double> coeffsSTD(coeffs.begin(), coeffs.end());
    Dune::Functions::Polynomial<double> poly(std::move(coeffsSTD));

    // Compute the residual and return the result
    return std::make_tuple(poly, (A * coeffs - y).norm());
  }
}  // namespace Ikarus::utils
