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

#include "functionsanitychecks.hh"

#include "findlinesegment.hh"

#include <matplot/matplot.h>
#include <matplot/util/colors.h>

namespace Ikarus::utils::Impl {
  double drawResultAndReturnSlope(std::string&& functionName, const std::function<double(double)>& ftfunc, bool draw,
                                  int slopeOfReference) {
    using namespace matplot;
    std::vector<double> t = logspace(-8, -2, 100);
    Eigen::Map<Eigen::VectorXd> data(t.data(), t.size());
    std::vector<double> ftevaluated = transform(t, ftfunc);
    Eigen::Map<Eigen::VectorXd> yE(ftevaluated.data(), ftevaluated.size());

    std::vector<double> fexpectedSlope
        = transform(t, [slopeOfReference](auto t_) { return Dune::power(t_, slopeOfReference); });
    const int rangeSize      = 10;
    const auto [poly, range] = Ikarus::utils::findLineSegment(data.array().log10(), yE.array().log10(), rangeSize);

    if (draw) {
      auto f   = figure(true);
      auto ax1 = gca();
      hold(ax1, true);
      std::vector<double> tOfRange(rangeSize);
      std::vector<double> fInRange(rangeSize);
      auto tET = data(range);
      auto yET = yE(range);

      for (int i = 0; auto r : tET) {
        tOfRange[i] = r;
        fInRange[i] = yET[i];
        ++i;
      }

      auto l0          = ax1->loglog(t, ftevaluated);
      auto lexpected   = ax1->loglog(t, fexpectedSlope, "--");
      auto lFoundRange = ax1->loglog(tOfRange, fInRange);
      l0->line_width(2);
      lexpected->line_width(2);
      lFoundRange->line_width(4);
      lFoundRange->color("magenta");
      l0->color("blue");
      lexpected->color("red");
      xlabel("h");
      ylabel("Approximation error ");
      title(functionName + "check");
      f->show();
    }
    if (yE(range).lpNorm<Eigen::Infinity>() < 1e-10)
      return std::numeric_limits<double>::infinity();  // If the error is zero everywhere the function is linear for
                                                       // this case we return infinity
    return poly.coefficients()[1];
  }

}  // namespace Ikarus::utils::Impl
