<commit_before>#pragma once

#include <list>
#include <type_traits>

#include "base/ranges.hpp"
#include "numerics/hermite3.hpp"

namespace principia {
namespace numerics {
namespace internal_fit_hermite_spline {

using base::Range;
using geometry::Normed;
using quantities::Derivative;

template<typename Samples,
         typename GetArgument,
         typename GetValue,
         typename GetDerivative,
         typename ErrorType>
std::list<typename Samples::const_iterator> FitHermiteSpline(
    Samples const& samples,
    GetArgument const& get_argument,
    GetValue const& get_value,
    GetDerivative const& get_derivative,
    ErrorType const& tolerance) {
  using Iterator = typename Samples::const_iterator;
  using Argument = std::decay_t<decltype(get_argument(*samples.begin()))>;
  using Value = std::decay_t<decltype(get_value(*samples.begin()))>;
  using Derivative1 = std::decay_t<decltype(get_derivative(*samples.begin()))>;
  static_assert(
      std::is_same<Derivative1, Derivative<Value, Argument>>::value,
      "Inconsistent types for |get_argument|, |get_value|, and "
      "|get_derivative|");
  static_assert(
      std::is_same<ErrorType, typename Normed<Value>::NormType>::value,
      "|tolerance| must have the same type as a distance between values");

  if (samples.size() < 3) {
    // With 0 or 1 points there is nothing to interpolate, with 2 we cannot
    // estimate the error.
    return {};
  }

  auto interpolation_error = [get_argument, get_value, get_derivative](
                                 Iterator begin, Iterator last) {
    return Hermite3<Argument, Value>(
               {get_argument(*begin), get_argument(*last)},
               {get_value(*begin), get_value(*last)},
               {get_derivative(*begin), get_derivative(*last)})
        .LInfinityError(Range(begin, last + 1), get_argument, get_value);
  };

  auto const last = samples.end() - 1;
  if (interpolation_error(samples.begin(), last) < tolerance) {
    // A single polynomial fits the entire range, so we have no way of knowing
    // whether it is the largest polynomial that will fit the range.
    return {};
  } else {
    // Look for the longest polynomial that will fit the beginning within
    // |tolerance|.
    // This will find a locally longest such polynomial, but we hope that the
    // error often grows with the number of points fitted, so that it does not
    // matter much.  In any case, the chosen polynomial does fit the beginning
    // of |samples| within |tolerance|.
<commit_msg>reword comment<commit_after>#pragma once

#include <list>
#include <type_traits>

#include "base/ranges.hpp"
#include "numerics/hermite3.hpp"

namespace principia {
namespace numerics {
namespace internal_fit_hermite_spline {

using base::Range;
using geometry::Normed;
using quantities::Derivative;

template<typename Samples,
         typename GetArgument,
         typename GetValue,
         typename GetDerivative,
         typename ErrorType>
std::list<typename Samples::const_iterator> FitHermiteSpline(
    Samples const& samples,
    GetArgument const& get_argument,
    GetValue const& get_value,
    GetDerivative const& get_derivative,
    ErrorType const& tolerance) {
  using Iterator = typename Samples::const_iterator;
  using Argument = std::decay_t<decltype(get_argument(*samples.begin()))>;
  using Value = std::decay_t<decltype(get_value(*samples.begin()))>;
  using Derivative1 = std::decay_t<decltype(get_derivative(*samples.begin()))>;
  static_assert(
      std::is_same<Derivative1, Derivative<Value, Argument>>::value,
      "Inconsistent types for |get_argument|, |get_value|, and "
      "|get_derivative|");
  static_assert(
      std::is_same<ErrorType, typename Normed<Value>::NormType>::value,
      "|tolerance| must have the same type as a distance between values");

  if (samples.size() < 3) {
    // With 0 or 1 points there is nothing to interpolate, with 2 we cannot
    // estimate the error.
    return {};
  }

  auto interpolation_error = [get_argument, get_value, get_derivative](
                                 Iterator begin, Iterator last) {
    return Hermite3<Argument, Value>(
               {get_argument(*begin), get_argument(*last)},
               {get_value(*begin), get_value(*last)},
               {get_derivative(*begin), get_derivative(*last)})
        .LInfinityError(Range(begin, last + 1), get_argument, get_value);
  };

  auto const last = samples.end() - 1;
  if (interpolation_error(samples.begin(), last) < tolerance) {
    // A single polynomial fits the entire range, so we have no way of knowing
    // whether it is the largest polynomial that will fit the range.
    return {};
  } else {
    // Look for a cubic that fits the beginning within |tolerance| and
    // such the cubic fitting one more sample would not fit the samples within
    // |tolerance|.
    // Note that there may be more than one cubic satisfying this property;
    // ideally we would like to find the longest one, but this would be costly,
    // and we expect little gains from this in practice.

{'fraction_non_alphanumeric': 0.10570866141732284, 'fraction_numerical': 0.003937007874015748, 'mean_word_length': 3.5651392632524708, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7572000', 'n_tokens_mistral': 1521, 'n_tokens_neox': 1441, 'n_words': 456}