Короткие примеры C++ кода — дополнительные

Кувшинов Д.Р.

2016


Общее оглавление


Все примеры, представленные в данном наборе доступны в виде отдельных .cpp-файлов в папке cpp3.

2000-read_ints.cpp

// read_ints.cpp
// Чтение последовательности целых чисел неизвестного размера в динамический массив,
// организуемый средствами стандартной библиотеки C (malloc, realloc, free).
#include <cstdlib>
#include <iostream>
using namespace std;

// Возвращает число прочитанных чисел.
size_t read_ints(int **ints, size_t max_size = 1 << 20)
{
  size_t cap = max_size < 16 ? max_size : 16; // capacity
  auto a = (int*)malloc(cap * sizeof(int));
  *ints = a;

  if (!a)
    return 0;

  size_t i = 0; // size
  for (int x;; ++i)
  {
    if (i == cap)
    {
      if (cap == max_size)
        break;

      cap *= 2;
      if (max_size < cap)
        cap = max_size;

      if (auto r = (int*)realloc(a, cap * sizeof(int)))
        a = r;
      else
        break;
    }

    if (cin >> x)
      a[i] = x;
    else
      break;
  }

  return i;
}

/////////////////////////////////////////////////////////////////////
int main()
{
  while (true)
  {
    size_t mxs = 0;
    cin >> mxs;

    int *a = nullptr;
    size_t sz = read_ints(&a, mxs);
    cin.clear();
    cin.ignore(cin.rdbuf()->in_avail());
    cin.sync();

    for (size_t i = 0; i < sz; ++i)
      cout << a[i] << '\n';
    free(a);
  }
  
  return 0;
}


2010-pint.cpp

// pint.cpp
// Примитивный способ округления до целых.
// В каком диапазоне значений x эта функция работает корректно?
float pint(float x)
{
  x += 12582912.f; // 1.5f * (1 << 23)
  x –= 12582912.f;
  return x;
}


2015-ppow.cpp

// ppow.cpp
// Эффективный алгоритм возведения в целочисленную степень.
// Логарифмическое число умножений.
// std::pow(Float, Integer) реализуется аналогично.

// Неотрицательный показатель.
float ppow(float x, unsigned n)
{
  float s = 1.f;
  for (; n != 0; n /= 2, x *= x)
    if (n % 2 != 0)
      s *= x;
  return s;
}

// Отрицательный или неотрицательный показатель.
float ppow(float x, int n)
{
  return n < 0 ? 1.f / ppow(x, -n) 
    : ppow(x, n);
}


2020-hypot.cpp

// hypot.cpp
// Testing different hypot implementations (in float).
// V.0: standard implementation;
// V.1: just sqrt(v.v);
// V.2: usual non-overflowing code with division;
// V.3: (float)sqrt(double(v).double(v)).
#include <utility>
#include <cmath>

inline float hypot_v0(float x, float y)
{
  return std::hypotf(x, y);
}

inline float hypot_v0(float x, float y, float z)
{
  return std::hypotf(std::hypotf(x, y), z);
}

inline float hypot_v1(float x, float y)
{
  return std::sqrt(x * x + y * y);
}

inline float hypot_v1(float x, float y, float z)
{
  return std::sqrt(x * x + y * y + z * z);
}

inline float hypot_v2(float x, float y)
{
  x = std::abs(x);
  y = std::abs(y);
  
  const float
    min_ = x < y? x: y,
    max_ = x < y? y: x;

  if (max_ == 0.f || std::isinf(max_))
    return max_;
  
  const float q = min_ / max_;
  return max_ * std::sqrt(1.f + q * q);
}

inline float hypot_v2(float x, float y, float z)
{
  x = std::abs(x);
  y = std::abs(y);
  z = std::abs(z);

  if (y < x)
    std::swap(x, y);
  if (z < y)
    std::swap(z, y);

  // Now z is max.
  if (z == 0.f || std::isinf(z))
    return 0.f;

  const float qx = x / z, qy = y / z;
  return z * std::sqrt(1.f + (qx * qx + qy * qy));
}

inline float hypot_v3(float x, float y)
{
  const double x_ = x, y_ = y;
  return (float)std::sqrt(x_ * x_ + y_ * y_);
}

inline float hypot_v3(float x, float y, float z)
{
  const double x_ = x, y_ = y, z_ = z;
  return (float)std::sqrt(x_ * x_ + y_ * y_ + z_ * z_);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Testing
#include <cfloat>
#include <cstdint>
#include <iostream>
#include <chrono>
#include <vector>

/// Perform action and measure the time difference using Clock clock.
template <class Clock, class Action>
double timing(Action && action)
{
  const auto t0 = Clock::now();
  action();
  const std::chrono::duration<double> p = Clock::now() - t0;
  return p.count();
}

/// Perform timings and output the results.
template <class Clock = std::chrono::high_resolution_clock>
class Timings
{
  std::vector<std::pair<std::string, double>> results;

public:
  Timings() = default;

  template <class Action>
  Timings(std::string msg, Action && action)
  {
    (*this)(std::move(msg), std::forward<Action>(action));
  }

  template <class Action>
  Timings& operator()(std::string msg, Action && action)
  {
    results.emplace_back(std::move(msg),
      timing<Clock>(std::forward<Action>(action)));
    return *this;
  }

  /// Output the accumulated results.
  std::ostream& operator()(std::ostream & os) const
  {
    using namespace std;
    if (results.empty())
      return os << "No results" << endl;

    const auto npt = results.front().second;
    os << results.front().first << npt << "s\n";
    
    for (auto p = begin(results), p_end = end(results); ++p != p_end;)
      os << p->first << p->second << "s, " << npt / p->second << " times\n";
    return os << endl;
  }
};


std::uint32_t ulp_dist(float a, float b)
{
  const auto
    a_ = *reinterpret_cast<const uint32_t*>(&a),
    b_ = *reinterpret_cast<const uint32_t*>(&b);
  return a_ < b_? b_ - a_: a_ - b_;
}


int main()
{
  using namespace std;
  using Clock = chrono::high_resolution_clock;
  using Clock_period = Clock::period;
  cout << (double)Clock_period::den / Clock_period::num << " tps\n";
  cout.precision(4);
  
  // 2D versions.
  const int N = 12'000'000;
  vector<float> src(N);
  for (int i = 0; i < N; i += 2)
  {
    const double phi = 1e-5 * i;
    src[i] = (float)sin(phi);
    src[i + 1] = (float)cos(phi);
  }
  
  float s[4]{};
  #define HT(n)\
    float sum = 0.f;\
    for (int i = 0; i < N; i += 2)\
      sum += hypot_v##n(src[i], src[i+1]);\
    s[n] = sum;\
  // end define
  
  cout << "2D versions\n";
  Timings<Clock>
    ("v0: ", [&]{ HT(0) })
    ("v1: ", [&]{ HT(1) })
    ("v2: ", [&]{ HT(2) })
    ("v3: ", [&]{ HT(3) })
    ("v0: ", [&]{ HT(0) })
    (cout);
    
  cout << '\n';
  
  Timings<Clock>
    ("v0: ", [&]{ HT(0) })
    ("v3: ", [&]{ HT(3) })
    ("v0: ", [&]{ HT(0) })
    ("v2: ", [&]{ HT(2) })
    ("v1: ", [&]{ HT(1) })
    (cout);
    
  cout << '\n';
  for (int i = 0; i < 4; ++i)
    cout << '\t' << s[i];
  cout << '\n';
  
  // 3D versions.
  for (int i = 0; i < N; i += 3)
  {
    const double phi = 1e-5 * i;
    const double psi = 1.5e-5 * i;
    src[i] = (float)sin(phi);
    
    const auto cos_phi = cos(phi);
    src[i + 1] = (float)(cos_phi * sin(psi));
    src[i + 2] = (float)(cos_phi * cos(psi));
  }
  
  #undef HT
  #define HT(n)\
    float sum = 0.f;\
    for (int i = 0; i < N; i += 3)\
      sum += hypot_v##n(src[i], src[i+1], src[i+2]);\
    s[n] = sum;\
  // end define
  
  cout << endl << "3D versions\n";
  Timings<Clock>
    ("v0: ", [&]{ HT(0) })
    ("v1: ", [&]{ HT(1) })
    ("v2: ", [&]{ HT(2) })
    ("v3: ", [&]{ HT(3) })
    ("v0: ", [&]{ HT(0) })
    (cout);
    
  cout << '\n';
  
  Timings<Clock>
    ("v0: ", [&]{ HT(0) })
    ("v3: ", [&]{ HT(3) })
    ("v0: ", [&]{ HT(0) })
    ("v2: ", [&]{ HT(2) })
    ("v1: ", [&]{ HT(1) })
    (cout);
    
  cout << '\n';
  for (int i = 0; i < 4; ++i)
    cout << '\t' << s[i];
  cout << '\n';
  
  // Compare quality of hypot_v1, hypot_v2 against hypot_v3.
  float fixed[] { 1e-15f, 1e-6f, 1e-2f, 1.f, 1e+2f, 1e+6f, 1e+15f };
  for (auto y: fixed)
  {
    cout << "\nFor y = " << y << endl;
    
    float max_d1x = 0.f, max_d2x = 0.f;
    int i = 0;
    uint32_t max_ulp1 = 0, max_ulp2 = 0;
    
    const auto x_max = std::sqrt(FLT_MAX);
    for (float x = FLT_MIN; x != x_max; x = std::nextafter(x, FLT_MAX))
    {
      const float
        v1 = hypot_v1(x, y),
        v2 = hypot_v2(x, y),
        v3 = hypot_v3(x, y);
        
      const auto
        d1 = ulp_dist(v1, v3),
        d2 = ulp_dist(v2, v3);
        
      if (max_ulp1 < d1)
      {
        max_ulp1 = d1;
        max_d1x = x;
      }
      
      if (max_ulp2 < d2)
      {
        max_ulp2 = d2;
        max_d2x = x;
      }
      
      if (++i == 1 << 24)
      {
        cout.put('.').flush();
        i = 0;
      }
    }
    
    cout.precision(10);
    cout << "\nmax_d1 = " << max_ulp1 << " at x = " << max_d1x;
    cout << "\nmax_d2 = " << max_ulp2 << " at x = " << max_d2x;
  }
  
  cout << endl;
  cin.ignore();
  return 0;
}


2030-bisection_recursive.cpp

// bisection_recursive.cpp
// Решаем уравнение f(x) = 0, x in [a, b].
// Простейший вариант реализации метода бисекции:
// рекурсия, заданная где-то (зафиксированная) функция f.

float f(float);
float solve(float a, float b, float fa, float fb)
{
  if (fa == 0.f) return a;
  if (fb == 0.f) return b;
  const auto m = 0.5f * (a + b), fm = f(m);
  if (m == a || m == b) return m;
  if (signbit(fa) != signbit(fm))
    return solve(a, m, fa, fm);
  if (signbit(fb) != signbit(fm))
    return solve(m, b, fm, fb);
  return m;
}

// Удобный "вход" в рекурсию.
float solve(float a, float b)
{ return solve(a, b, f(a), f(b)); }


2031-bisection_recursive_functor.cpp

// bisection_recursive_functor.cpp
// Решаем уравнение f(x) = 0, x in [a, b].
// Функцию передаём в стиле C++ (как функциональный объект).
// Простейший вариант реализации метода бисекции:
// рекурсия, заданная где-то (зафиксированная) функция f.

template <class F>
float solve(F &f, float a, float b, float fa, float fb)
{
  if (fa == 0.f) return a;
  if (fb == 0.f) return b;
  const auto m = 0.5f * (a + b), fm = f(m);
  if (m == a || m == b) return m;
  if (signbit(fa) != signbit(fm))
    return solve(f, a, m, fa, fm);
  if (signbit(fb) != signbit(fm))
    return solve(f, m, b, fm, fb);
  return m;
}

// Удобный "вход" в рекурсию.
template <class F>
float solve(F f, float a, float b)
{ return solve(f, a, b, f(a), f(b)); }

// Сложный вопрос: передавать f по значению или по ссылке?
// Проще избавиться от рекурсии и необходимости передавать f повторно в рекурсивный вызов.
// (См. примеры далее.)


2040-nsolve.cpp

// nsolve.cpp
#include <cassert>
#include <cstddef>
#include <cmath>
#include <climits>

typedef float(*Function)(float);
struct Local_solve_task
{
  Function function;             // solving function(x) = 0
  float function_tolerance;      // maximal tolerable error on function value (divergence from zero)
  float interval_tolerance;      // interval span lower bound
  float left_bound, right_bound; // interval containing a root
  float left_value, right_value; // values of function in points left_bound and right_bound
};

typedef bool(*Interval_check)(Local_solve_task&); // returns true to stop the search
typedef bool(*Local_solve)(Local_solve_task&, Interval_check);


/// Uniform grid search.
/// Precomputes task.left_value and task.right_value.
/// @return total number of report invocations
std::size_t global_solve
  (
    Local_solve_task &task,
    Interval_check interval_check,
    Local_solve lsolve,
    float step
  )
{
  assert(step != 0);
  const auto a = task.left_bound, b = task.right_bound;
  const std::size_t max_step = (long)((b - a) / step);
  assert(max_step != 0);
  assert(max_step < LONG_MAX);

  std::size_t invocations = 0;

  task.left_value = task.function(task.left_bound);
  for (std::size_t i = 1; i <= max_step; ++i)
  {
    const auto next_bound = i == max_step ? b : a + i * step;
    const auto next_value = task.function(next_bound);

    task.right_bound = next_bound;
    task.right_value = next_value;

    if (std::signbit(task.left_value) != std::signbit(task.right_value))
    {
      ++invocations;
      if (lsolve(task, interval_check))
        break;
    }

    task.left_bound = next_bound;
    task.left_value = next_value;
  }

  return invocations;
}


// Метод деления пополам (бисекция).
bool bisection(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  while (true)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    const auto m = 0.5f * (a + b);
    if (m == a || m == b)
      break;

    const auto fm = f(m);
    if (std::signbit(fa) != std::signbit(fm))
    {
      b = m;
      fb = fm;
    }
    else
    {
      assert(std::signbit(fb) != std::signbit(fm));
      a = m;
      fa = fm;
    }
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Вилочный метод секущих (метод ложного положения).
bool bracket_secant(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  while (true)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    const auto m = (a*fb - b*fa) / (fb - fa); // the only difference with bisection
    if (m == a || m == b)
      break;

    const auto fm = f(m);
    if (std::signbit(fa) != std::signbit(fm))
    {
      b = m;
      fb = fm;
    }
    else
    {
      assert(std::signbit(fb) != std::signbit(fm));
      a = m;
      fa = fm;
    }
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Модифицированный вилочный метод секущих.
bool bracket_secant_illinois(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  for (int to_left = 0, to_right = 0;;)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    const auto m =
      to_left == 2?  (to_left = 0,   (a*fb - 0.5f*b*fa) / (fb - 0.5f*fa))
    : to_right == 2? (to_right = 0, ( 0.5f*a*fb - b*fa) / (0.5f*fb - fa))
    : (a*fb - b*fa) / (fb - fa);

    if (m == a || m == b)
      break;

    const auto fm = f(m);
    if (std::signbit(fa) != std::signbit(fm))
    {
      b = m;
      fb = fm;
      to_right = 0;
      ++to_left;
    }
    else
    {
      assert(std::signbit(fb) != std::signbit(fm));
      a = m;
      fa = fm;
      to_left = 0;
      ++to_right;
    }
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Метод Риддера.
bool ridders(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  while (true)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    const auto m = 0.5f * (a + b);
    if (m == a || m == b)
      break;

    const auto
      fm = f(m),
      add = (m - a) * fm / std::sqrt(fm * fm - fa * fb),
      p = m - (fa < fb? add: -add),
      fp = f(p);

    if (std::signbit(fp) != std::signbit(fm))
    {
      if (p < m)
      {
        a = p;
        b = m;
        fa = fp;
        fb = fm;
      }
      else
      {
        a = m;
        b = p;
        fa = fm;
        fb = fp;
      }
    }
    else if (std::signbit(fp) != std::signbit(fa))
    {
      b = p;
      fb = fp;
    }
    else
    {
      assert(std::signbit(fp) != std::signbit(fb));
      a = p;
      fa = fp;
    }
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Метод Риддера + метод секущих.
bool ridders_secant(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  while (true)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    const auto m = (a*fb - b*fa) / (fb - fa);
    if (m == a || m == b)
      break;

    const auto
      fm = f(m),
      add = (m - a) * fm / std::sqrt(fm * fm - fa * fb),
      p = m - (fa < fb? add: -add),
      fp = f(p);

    if (std::signbit(fp) != std::signbit(fm))
    {
      if (p < m)
      {
        a = p;
        b = m;
        fa = fp;
        fb = fm;
      }
      else
      {
        a = m;
        b = p;
        fa = fm;
        fb = fp;
      }
    }
    else if (std::signbit(fp) != std::signbit(fa))
    {
      b = p;
      fb = fp;
    }
    else
    {
      assert(std::signbit(fp) != std::signbit(fb));
      a = p;
      fa = fp;
    }
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Метод Брента-Деккера.
// https://en.wikipedia.org/wiki/Brent%27s_method
bool brent_dekker(Local_solve_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto
    itol = task.interval_tolerance,
    ftol = task.function_tolerance;

  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  if (std::abs(fa) < std::abs(fb))
  {
    auto t = a; a = b; b = t;
    t = fa; fa = fb; fb = t;
  }

  auto c = a, fc = fa, d = c;
  for (bool flag = true;;)
  {
    if (std::abs(fa) <= ftol
     || std::abs(fb) <= ftol
     || std::abs(b - a) <= itol)
      break;

    if (std::abs(fc) <= ftol)
    {
      a = c;
      fa = fc;
      break;
    }

    auto m =
      (fa != fc && fb != fc)?
        (a*fb*fc / ((fa - fb)*(fa - fc)) + b*fa*fc / ((fb - fa)*(fb - fc)) + c*fa*fb / ((fc - fa)*(fc - fb)))
      : (a*fb - b*fa) / (fb - fa); // secant fallback

    if // possible bisection fallback
      (
        (a < b && (b <= m || 4 * m < (3 * a + b)))
     || (b < a && (m <= b || (3 * a + b) < 4 * m))
     ||  flag && 2 * std::abs(m - b) >= std::abs(b - c)
     || !flag && 2 * std::abs(m - b) >= std::abs(d - c)
     ||  flag && std::abs(b - c) < itol // delta?
     || !flag && std::abs(c - d) < itol // delta?
      )
    {
      m = 0.5f * (a + b);
      flag = true;
    }
    else
      flag = false;

    if (m == a || m == b)
      break;

    const auto fm = f(m);
    d = c;
    c = b;
    fc = fb;

    if (std::signbit(fa) != std::signbit(fm))
    {
      b = m;
      fb = fm;
    }
    else
    {
      assert(std::signbit(fb) != std::signbit(fm));
      a = m;
      fa = fm;
    }
  }

  if (a < b)
  {
    task.left_bound = a;
    task.right_bound = b;
    task.left_value = fa;
    task.right_value = fb;
  }
  else
  {
    task.left_bound = b;
    task.right_bound = a;
    task.left_value = fb;
    task.right_value = fa;
  }
  return interval_check(task);
}


////////////////////////////////////////////////////////////////////////////////
// Testing.
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

// The counter, which is used to directly measure efficiency of different methods.
size_t f_invocations;

float f(float x)
{
  ++f_invocations;
  return 0.5f * sin(x) + sin(1.5f * x + 2.f)
    - 1.5f * sin(5.f / 17.f * x + 1.f) - atan(0.5f * x + 5.f);
}

bool print_interval(Local_solve_task &task)
{
  cout << '[' << task.left_bound << ", " << task.right_bound << "], bound values: ";
  cout << task.left_value << ", " << task.right_value << endl;
  return false; // proceed
}

int main()
{
  Local_solve_task task{ f };
  Local_solve methods[]
  {
    bisection,
    bracket_secant,
    bracket_secant_illinois,
    ridders,
    ridders_secant,
    brent_dekker
  };

  for (auto method : methods)
  {
    cout << '\n';
    task.left_bound = -20.f;
    task.right_bound = 20.f;
    f_invocations = 0;
    const auto n = global_solve(task, print_interval, method, 0.1f);
    cout << "Roots found: " << n << ", f invocations: " << f_invocations << endl;
  }

  cin.ignore();
  return 0;
}


2041-nsolve_functor.cpp

// nsolve_functor.cpp
// nsolve + шаблоны и функторы.
#include <cassert>
#include <cstddef>
#include <cmath>
#include <limits>
#include <utility>

template <class Argument>
inline Argument half(Argument x)
{
  return Argument(0.5) * x;
}

template <class Argument, class Value>
struct Local_solve_state
{
  Argument interval_tolerance;      // interval span lower bound
  Argument left_bound, right_bound; // interval containing a root
  Value function_tolerance;         // maximal tolerable error on function value (divergence from zero)
  Value left_value, right_value;    // values of function in points left_bound and right_bound
};


/// Uniform grid search.
/// Precomputes task.left_value and task.right_value.
/// @return total number of report invocations
template <
  class Function,
  class Argument,
  class Value,
  class Interval_check,
  class Local_solve>
std::size_t global_solve
  (
    Function function,
    Local_solve_state<Argument, Value> &task,
    Interval_check interval_check,
    Local_solve lsolve,
    Argument step
  )
{
  using std::signbit;

  assert(step != 0);
  const auto a = task.left_bound, b = task.right_bound;
  const std::size_t max_step = (long)((b - a) / step);
  assert(max_step != 0);
  assert(max_step < std::numeric_limits<long>::max());

  std::size_t invocations = 0;

  task.left_value = function(task.left_bound);
  for (std::size_t i = 1; i <= max_step; ++i)
  {
    const auto next_bound = i == max_step ? b : a + i * step;
    const auto next_value = function(next_bound);

    task.right_bound = next_bound;
    task.right_value = next_value;

    if (signbit(task.left_value) != signbit(task.right_value))
    {
      ++invocations;
      if (lsolve(function, task, interval_check))
        break;
    }

    task.left_bound = next_bound;
    task.left_value = next_value;
  }

  return invocations;
}


// Метод деления пополам (бисекция).
struct Bisection
{
  template <
    class Function,
    class Argument,
    class Value,
    class Interval_check>
  bool operator()
    (
      Function f,
      Local_solve_state<Argument, Value> &task,
      Interval_check interval_check
    ) const
  {
    using std::abs;
    using std::signbit;
    const auto itol = task.interval_tolerance;
    const auto ftol = task.function_tolerance;

    auto a = task.left_bound, b = task.right_bound;
    auto fa = task.left_value, fb = task.right_value;

    while (true)
    {
      if (abs(fa) <= ftol
       || abs(fb) <= ftol
       || abs(b - a) <= itol)
        break;

      const auto m = half(a + b);
      if (m == a || m == b)
        break;

      const auto fm = f(m);
      if (signbit(fa) != signbit(fm))
      {
        b = m;
        fb = fm;
      }
      else
      {
        assert(signbit(fb) != signbit(fm));
        a = m;
        fa = fm;
      }
    }

    task.left_bound = a;
    task.right_bound = b;
    task.left_value = fa;
    task.right_value = fb;
    return interval_check(task);
  }
} const bisection;


// Вилочный метод секущих (метод ложного положения).
struct Bracket_secant
{
  template <
    class Function,
    class Argument,
    class Value,
    class Interval_check>
  bool operator()
    (
      Function f,
      Local_solve_state<Argument, Value> &task,
      Interval_check interval_check
    ) const
  {
    using std::abs;
    using std::signbit;
    const auto itol = task.interval_tolerance;
    const auto ftol = task.function_tolerance;

    auto a = task.left_bound, b = task.right_bound;
    auto fa = task.left_value, fb = task.right_value;

    while (true)
    {
      if (abs(fa) <= ftol
       || abs(fb) <= ftol
       || abs(b - a) <= itol)
        break;

      const Argument m = (a*fb - b*fa) / (fb - fa); // the only difference with bisection
      if (m == a || m == b)
        break;

      const auto fm = f(m);
      if (signbit(fa) != signbit(fm))
      {
        b = m;
        fb = fm;
      }
      else
      {
        assert(signbit(fb) != signbit(fm));
        a = m;
        fa = fm;
      }
    }

    task.left_bound = a;
    task.right_bound = b;
    task.left_value = fa;
    task.right_value = fb;
    return interval_check(task);
  }
} const bracket_secant;


// Модифицированный вилочный метод секущих.
struct Bracket_secant_illinois
{
  template <
    class Function,
    class Argument,
    class Value,
    class Interval_check>
  bool operator()
    (
      Function f,
      Local_solve_state<Argument, Value> &task,
      Interval_check interval_check
    ) const
  {
    using std::abs;
    using std::signbit;
    const auto itol = task.interval_tolerance;
    const auto ftol = task.function_tolerance;

    auto a = task.left_bound, b = task.right_bound;
    auto fa = task.left_value, fb = task.right_value;

    for (int to_left = 0, to_right = 0;;)
    {
      if (abs(fa) <= ftol
       || abs(fb) <= ftol
       || abs(b - a) <= itol)
        break;

      const Argument m =
          to_left  == 2? (to_left = 0,  (a*fb - 0.5f*b*fa) / (fb - 0.5f*fa))
        : to_right == 2? (to_right = 0, (0.5f*a*fb - b*fa) / (0.5f*fb - fa))
        : (a*fb - b*fa) / (fb - fa);

      if (m == a || m == b)
        break;

      const auto fm = f(m);
      if (signbit(fa) != signbit(fm))
      {
        b = m;
        fb = fm;
        to_right = 0;
        ++to_left;
      }
      else
      {
        assert(signbit(fb) != signbit(fm));
        a = m;
        fa = fm;
        to_left = 0;
        ++to_right;
      }
    }

    task.left_bound = a;
    task.right_bound = b;
    task.left_value = fa;
    task.right_value = fb;
    return interval_check(task);
  }
} const bracket_secant_illinois;


// Метод Риддера.
struct Ridders
{
  template <
    class Function,
    class Argument,
    class Value,
    class Interval_check>
  bool operator()
    (
      Function f,
      Local_solve_state<Argument, Value> &task,
      Interval_check interval_check
    ) const
  {
    using std::abs;
    using std::signbit;
    using std::sqrt;
    const auto itol = task.interval_tolerance;
    const auto ftol = task.function_tolerance;

    auto a = task.left_bound, b = task.right_bound;
    auto fa = task.left_value, fb = task.right_value;

    while (true)
    {
      if (abs(fa) <= ftol
       || abs(fb) <= ftol
       || abs(b - a) <= itol)
        break;

      const auto m = half(a + b);
      if (m == a || m == b)
        break;

      const auto fm = f(m);
      const Argument
        add = (m - a) * fm / sqrt(fm * fm - fa * fb),
        p = m - (fa < fb? add: -add);

      const auto fp = f(p);

      if (signbit(fp) != signbit(fm))
      {
        if (p < m)
        {
          a = p;
          b = m;
          fa = fp;
          fb = fm;
        }
        else
        {
          a = m;
          b = p;
          fa = fm;
          fb = fp;
        }
      }
      else if (signbit(fp) != signbit(fa))
      {
        b = p;
        fb = fp;
      }
      else
      {
        assert(signbit(fp) != signbit(fb));
        a = p;
        fa = fp;
      }
    }

    task.left_bound = a;
    task.right_bound = b;
    task.left_value = fa;
    task.right_value = fb;
    return interval_check(task);
  }
} const ridders;



// Метод Брента-Деккера.
// https://en.wikipedia.org/wiki/Brent%27s_method
struct Brent_Dekker
{
  template <
    class Function,
    class Argument,
    class Value,
    class Interval_check>
  bool operator()
    (
      Function f,
      Local_solve_state<Argument, Value> &task,
      Interval_check interval_check
    ) const
  {
    using std::abs;
    using std::signbit;
    const auto itol = task.interval_tolerance;
    const auto ftol = task.function_tolerance;

    auto a = task.left_bound, b = task.right_bound;
    auto fa = task.left_value, fb = task.right_value;

    if (abs(fa) < abs(fb))
    {
      std::swap(a, b);
      std::swap(fa, fb);
    }

    auto c = a, d = a;
    auto fc = fa;

    for (bool flag = true;;)
    {
      if (abs(fa) <= ftol
       || abs(fb) <= ftol
       || abs(b - a) <= itol)
        break;

      if (abs(fc) <= ftol)
      {
        a = c;
        fa = fc;
        break;
      }

      Argument m =
        (fa != fc && fb != fc)?
          (a*fb*fc / ((fa - fb)*(fa - fc)) + b*fa*fc / ((fb - fa)*(fb - fc)) + c*fa*fb / ((fc - fa)*(fc - fb)))
        : (a*fb - b*fa) / (fb - fa); // secant fallback

      if // possible bisection fallback
        (
          (a < b && (b <= m || 4 * m < (3 * a + b)))
       || (b < a && (m <= b || (3 * a + b) < 4 * m))
       || ( flag && 2 * abs(m - b) >= abs(b - c))
       || (!flag && 2 * abs(m - b) >= abs(d - c))
       || ( flag && abs(b - c) < itol) // delta?
       || (!flag && abs(c - d) < itol) // delta?
        )
      {
        m = half(a + b);
        flag = true;
      }
      else
      {
        flag = false;
      }

      if (m == a || m == b)
        break;

      const auto fm = f(m);
      d = c;
      c = b;
      fc = fb;

      if (signbit(fa) != signbit(fm))
      {
        b = m;
        fb = fm;
      }
      else
      {
        assert(signbit(fb) != signbit(fm));
        a = m;
        fa = fm;
      }
    }

    if (a < b)
    {
      task.left_bound = a;
      task.right_bound = b;
      task.left_value = fa;
      task.right_value = fb;
    }
    else
    {
      task.left_bound = b;
      task.right_bound = a;
      task.left_value = fb;
      task.right_value = fa;
    }
    return interval_check(task);
  }
} const brent_dekker;


////////////////////////////////////////////////////////////////////////////////
// Testing.
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

// The counter, which is used to directly measure efficiency of different methods.
size_t f_invocations;

float f(float x)
{
  ++f_invocations;
  return 0.5f * sin(x) + sin(1.5f * x + 2.f)
    - 1.5f * sin(5.f / 17.f * x + 1.f) - atan(0.5f * x + 5.f);
}

template <class Argument, class Value>
bool print_interval(Local_solve_state<Argument, Value> &task)
{
  cout << '[' << task.left_bound << ", " << task.right_bound << "], bound values: ";
  cout << task.left_value << ", " << task.right_value << endl;
  return false; // proceed
}

template <class Method>
void test(Method method)
{
  cout << '\n';

  Local_solve_state<float, float> task;
  task.left_bound = -20.f;
  task.right_bound = 20.f;
  f_invocations = 0;

  const auto n = global_solve(f, task, print_interval<float, float>, method, 0.1f);
  cout << "Roots found: " << n << ", f invocations: " << f_invocations << endl;
}


int main()
{
#define TEST(m) \
  cout << '\n' << #m; \
  test(m)

  TEST(bisection);
  TEST(bracket_secant);
  TEST(bracket_secant_illinois);
  TEST(ridders);
  TEST(brent_dekker);

  cin.ignore();
  return 0;
}


2050-nmin.cpp

// nmin.cpp
#include <cassert>
#include <cstddef>
#include <cmath>
#include <climits>

typedef float(*Function)(float);
struct Local_min_task
{
  Function function;             // solving function(x) = 0
  float interval_tolerance;      // interval span lower bound
  float left_bound, right_bound, min_candidate; // interval containing a root + some "mid" point, which is the last minimum candidate
  float left_value, right_value, min_value;     // values of function in points left_bound, right_bound and min_candidate
};

typedef bool(*Interval_check)(Local_min_task&); // returns true to stop the search
typedef bool(*Local_minimize)(Local_min_task&, Interval_check);


/// Uniform grid search.
/// Precomputes task.left_value and task.right_value.
/// @return total number of report invocations
std::size_t global_minimize
  (
    Local_min_task &task,
    Interval_check interval_check,
    Local_minimize lmin,
    float step
  )
{
  assert(step != 0);
  const auto a = task.left_bound, b = task.right_bound;
  const std::size_t max_step = (long)((b - a) / step);
  assert(max_step != 0);
  assert(max_step < LONG_MAX);

  std::size_t invocations = 0;
  task.min_value = task.left_value = task.function(task.left_bound);
  task.min_candidate = task.left_bound;

  Local_min_task best_result = task;
  for (std::size_t i = 1; i <= max_step; ++i)
  {
    const auto next_bound = i == max_step ? b : a + i * step;
    const auto next_value = task.function(next_bound);

    task.right_bound = next_bound;
    task.right_value = next_value;

    ++invocations;
    const bool finish = lmin(task, interval_check);
    if (task.min_value < best_result.min_value)
      best_result = task;
    if (finish)
      break;

    task.left_bound = next_bound;
    task.left_value = next_value;
  }

  task = best_result;
  return invocations;
}


// Троичный поиск.
bool ternary_minimize(Local_min_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto itol = task.interval_tolerance;
  auto
    a = task.left_bound,
    b = task.right_bound,
    fa = task.left_value,
    fb = task.right_value;

  while (true)
  {
    if (b - a <= itol)
    {
      const auto m = 0.5f * (a + b);
      const auto fm = f(m);
      if (fa < fm)
      {
        task.min_candidate = a;
        task.min_value = fa;
      }
      else
      {
        task.min_candidate = m;
        task.min_value = fm;
      }
      break;
    }

    const auto
      third = (b - a) * (1.f/3.f),
      m1 = a + third,
      m2 = b - third;

    if (m1 == a || m2 == b || m2 <= m1)
    {
      task.min_candidate = a;
      task.min_value = fa;
      break;
    }

    const auto
      fm1 = f(m1),
      fm2 = f(m2);

    if (fm1 < fm2)
    {
      b = m2;
      fb = fm2;
    }
    else if (fm2 < fm1)
    {
      a = m1;
      fa = fm1;
    }
    else
    {
      assert(fm1 == fm2);
      a = m1;
      b = m2;
      fa = fm1;
      fb = fm2;
    }
  }

  if (fb < task.min_value)
  {
    task.min_candidate = b;
    task.min_value = fb;
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Метод золотого сечения.
bool golden_ratio_minimize(Local_min_task &task, Interval_check interval_check)
{
  // Golden ratio factor.
  static const auto phi = static_cast<float>(0.5 * (std::sqrt(5.0) - 1.));
  const auto f = task.function;
  const auto itol = task.interval_tolerance;
  auto
    a = task.left_bound,
    b = task.right_bound,
    c = b - (b - a) * phi,
    d = a + (b - a) * phi,
    fc = f(c),
    fd = f(d);

  while (true)
  {
    if (d - c <= itol || c <= a || b <= d)
    {
      const auto m = 0.5f * (a + b);
      const auto fm = f(m);
      if (fc < fm)
      {
        task.min_candidate = c;
        task.min_value = fc;
      }
      else
      {
        task.min_candidate = m;
        task.min_value = fm;
      }
      break;
    }

    if (fc < fd)
    {
      b = d;
      d = c;
      fd = fc;
      c = b - (b - a) * phi;
      fc = f(c);
    }
    else if (fd < fc)
    {
      a = c;
      c = d;
      fc = fd;
      d = a + (b - a) * phi;
      fd = f(d);
    }
    else
    {
      assert(fc == fd);
      a = c;
      b = d;
      c = b - (b - a) * phi;
      d = a + (b - a) * phi;
      fc = f(c);
      fd = f(d);
    }
  }

  if (fd < task.min_value)
  {
    task.min_candidate = d;
    task.min_value = fd;
  }

  task.left_bound = c;
  task.right_bound = d;
  task.left_value = fc;
  task.right_value = fd;
  return interval_check(task);
}


// Brent's method applied to minimization.
// Borrowed from Boost (see boost.org, brent_find_minima function): boost/math/tools/minima.hpp.
bool brent_minimize(Local_min_task &task, Interval_check interval_check)
{
  const auto f = task.function;
  const auto tolerance = std::fmax(task.interval_tolerance, std::ldexp(1.f, -23));
  auto
    min = task.left_bound,
    max = task.right_bound;

  // Original comments below.
  static const auto golden = 0.3819660f;  // golden ratio, don't need too much precision here!
  float
    x,  // minima so far
    w,  // second best point
    v,  // previous value of w
    u,  // most recent evaluation point
    delta,  // The distance moved in the last step
    delta2, // The distance moved in the step before last
    fu, fv, fw, fx,  // function evaluations at u, v, w, x
    mid, // midpoint of min and max
    fract1, fract2;  // minimal relative movement in x

  x = w = v = max;
  fw = fv = fx = task.right_value;
  delta2 = delta = 0;

  while (true)
  {
    // get midpoint
    mid = (min + max) / 2;
    // work out if we're done already:
    fract1 = tolerance * std::abs(x) + tolerance / 4;
    fract2 = 2 * fract1;
    if (std::abs(x - mid) <= (fract2 - (max - min) / 2))
      break;

    if (std::abs(delta2) > fract1)
    {
      // try and construct a parabolic fit:
      auto
        r = (x - w) * (fx - fv),
        q = (x - v) * (fx - fw),
        p = (x - v) * q - (x - w) * r;

      q = 2 * (q - r);
      if (q > 0)
        p = -p;
      q = std::abs(q);

      auto td = delta2;
      delta2 = delta;

      // determine whether a parabolic step is acceptible or not:
      if ((std::abs(p) >= std::abs(q * td / 2)) || (p <= q * (min - x)) || (p >= q * (max - x)))
      {
        // nope, try golden section instead
        delta2 = (x >= mid) ? min - x : max - x;
        delta = golden * delta2;
      }
      else
      {
        // whew, parabolic fit:
        delta = p / q;
        u = x + delta;
        if (((u - min) < fract2) || ((max- u) < fract2))
          delta = (mid - x) < 0 ? -std::abs(fract1) : std::abs(fract1);
      }
    }
    else
    {
      // golden section:
      delta2 = (x >= mid) ? min - x : max - x;
      delta = golden * delta2;
    }

    // update current position:
    u = (std::abs(delta) >= fract1) ? (x + delta) : (delta > 0 ? (x + std::abs(fract1)) : (x - std::abs(fract1)));
    fu = f(u);

    if (fu <= fx)
    {
      // good new point is an improvement!
      // update brackets:
      if(u >= x)
        min = x;
      else
        max = x;
      // update control points:
      v = w;
      w = x;
      x = u;
      fv = fw;
      fw = fx;
      fx = fu;
    }
    else
    {
      // Oh dear, point u is worse than what we have already,
      // even so it *must* be better than one of our endpoints:
      if (u < x)
        min = u;
      else
        max = u;
      if ((fu <= fw) || (w == x))
      {
        // however it is at least second best:
        v = w;
        w = u;
        fv = fw;
        fw = fu;
      }
      else if ((fu <= fv) || (v == x) || (v == w))
      {
        // third best:
        v = u;
        fv = fu;
      }
    }
  }

  task.min_candidate = x;
  task.min_value = fx;
  return interval_check(task);
}


////////////////////////////////////////////////////////////////////////////////
// Testing.
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;

// The counter, which is used to directly measure efficiency of different methods.
size_t f_invocations;
// "Global" minimum position and value.
float global_min, global_min_position;

float f(float x)
{
  ++f_invocations;
  return 0.5f * sin(x) + sin(1.5f * x + 2.f)
    - 1.5f * sin(5.f / 17.f * x + 1.f) - atan(0.5f * x + 5.f);
}

bool update_global_min(Local_min_task &task)
{
  if (task.min_value < global_min)
  {
    global_min = task.min_value;
    global_min_position = task.min_candidate;
  }
  return false; // proceed
}

int main()
{
  cout.precision(9);
  Local_min_task task{ f };

  static const int METHODS = 3;
  Local_minimize methods[METHODS]
  {
    ternary_minimize,
    golden_ratio_minimize,
    brent_minimize
  };

  const char *name[METHODS]
  {
    "Ternary search",
    "Golden ratio search",
    "Brent"
  };

  float gmin[METHODS], gminpos[METHODS];
  for (int i = 0; i < METHODS; ++i)
  {
    cout << name[i] << '\n';
    task.left_bound = -20.f;
    task.right_bound = 20.f;
    f_invocations = 0;
    global_min = 100.f;
    global_min_position = -100.f;
    const auto n = global_minimize(task, update_global_min, methods[i], 0.05f);
    cout << "Steps: " << n << ", f invocations: " << f_invocations << '\n';
    cout << "Global min = " << (gmin[i] = global_min) << ", at " << (gminpos[i] = global_min_position) << '\n' << endl;
  }

  auto best_i = min_element(begin(gmin), end(gmin)) - gmin;
  cout << "With min = " << gmin[best_i] << " winners are: ";
  for (int i = 0; i < METHODS; ++i)
    if (gmin[i] == gmin[best_i])
      cout << name[i] << ", ";
  cout << "\b\b." << endl;
  cin.ignore();
  return 0;
}


2051-nmin_functor.cpp

// nmin_functor.cpp
#include <cassert>
#include <cstddef>
#include <cmath>
#include <limits>

template <class Argument>
inline Argument half(Argument x)
{
  return Argument(0.5) * x;
}

template <class Argument>
inline Argument third(Argument x)
{
  return Argument(1./3.) * x;
}

template <class Argument, class Value>
struct Local_min_state
{
  // interval span lower bound
  Argument interval_tolerance;
  // interval containing a root + some "mid" point, which is the last minimum candidate
  Argument left_bound, right_bound, min_candidate;
  // values of function in points left_bound, right_bound and min_candidate
  Value left_value, right_value, min_value;
};


/// Uniform grid search.
/// Precomputes task.left_value and task.right_value.
/// @return total number of report invocations
template <
  class Function,
  class Argument,
  class Value,
  class Interval_check,
  class Local_minimize>
std::size_t global_minimize
  (
    Function f,
    Local_min_state<Argument, Value> &task,
    Interval_check interval_check,
    Local_minimize lmin,
    Argument step
  )
{
  step /= 2;
  assert(step != 0);

  const auto a = task.left_bound, b = task.right_bound;
  const std::size_t max_step = (long)((b - a) / step);

  assert(max_step != 0);
  assert(max_step < std::numeric_limits<long>::max());

  std::size_t invocations = 0;

  Argument x = a, y = a + step, z;
  Value fx = f(x), fy = f(y), fz;
  auto descending = fy < fx;
  task.left_bound = x;
  task.right_bound = y;
  task.left_value = fx;
  task.right_value = fy;
  task.min_candidate = descending? y: x;
  task.min_value = descending? fy: fx;

  auto best_result = task;
  for (std::size_t i = 2; i <= max_step; ++i)
  {
    z = i == max_step? b: a + i * step;
    fz = f(z);

    if (descending && fy < fz)
    {
      // A minimum is bracketed.
      task.left_bound = x;
      task.right_bound = z;
      task.left_value = fx;
      task.right_value = fz;

      ++invocations;
      const bool finish = lmin(f, task, interval_check);
      if (task.min_value < best_result.min_value)
        best_result = task;

      if (finish)
        break;

      descending = false;
    }
    else
    {
      descending = fz < fy;
    }

    x = y;
    y = z;
    fx = fy;
    fy = fz;
  }

  task = best_result;
  return invocations;
}


// Троичный поиск.
template <
  class Function,
  class Argument,
  class Value,
  class Interval_check>
bool ternary_minimize
  (
    Function f,
    Local_min_state<Argument, Value> &task,
    Interval_check interval_check
  )
{
  const auto itol = task.interval_tolerance;
  auto a = task.left_bound, b = task.right_bound;
  auto fa = task.left_value, fb = task.right_value;

  while (true)
  {
    if (b - a <= itol)
    {
      const auto m = half(a + b);
      const auto fm = f(m);
      if (fa < fm)
      {
        task.min_candidate = a;
        task.min_value = fa;
      }
      else
      {
        task.min_candidate = m;
        task.min_value = fm;
      }
      break;
    }

    const auto
      th = third(b - a),
      m1 = a + th,
      m2 = b - th;

    if (m1 == a || m2 == b || m2 <= m1)
    {
      task.min_candidate = a;
      task.min_value = fa;
      break;
    }

    const auto fm1 = f(m1), fm2 = f(m2);
    if (fm1 < fm2)
    {
      b = m2;
      fb = fm2;
    }
    else if (fm2 < fm1)
    {
      a = m1;
      fa = fm1;
    }
    else
    {
      assert(fm1 == fm2);
      a = m1;
      b = m2;
      fa = fm1;
      fb = fm2;
    }
  }

  if (fb < task.min_value)
  {
    task.min_candidate = b;
    task.min_value = fb;
  }

  task.left_bound = a;
  task.right_bound = b;
  task.left_value = fa;
  task.right_value = fb;
  return interval_check(task);
}


// Метод золотого сечения.
template <
  class Function,
  class Argument,
  class Value,
  class Interval_check>
bool golden_ratio_minimize
  (
    Function f,
    Local_min_state<Argument, Value> &task,
    Interval_check interval_check
  )
{
  using std::sqrt;
  // Golden ratio factor: (sqrt(5) - 1)/2.
  static const auto
    golden = static_cast<Argument>(half(sqrt(Argument(5)) - Argument(1)));
  const auto
    itol = task.interval_tolerance;
  auto
    a = task.left_bound,
    b = task.right_bound,
    c = b - (b - a) * golden,
    d = a + (b - a) * golden;
  auto
    fc = f(c),
    fd = f(d);

  while (true)
  {
    if (d - c <= itol || c <= a || b <= d)
    {
      const auto m = half(a + b);
      const auto fm = f(m);
      if (fc < fm)
      {
        task.min_candidate = c;
        task.min_value = fc;
      }
      else
      {
        task.min_candidate = m;
        task.min_value = fm;
      }
      break;
    }

    if (fc < fd)
    {
      b = d;
      d = c;
      fd = fc;
      c = b - (b - a) * golden;
      fc = f(c);
    }
    else if (fd < fc)
    {
      a = c;
      c = d;
      fc = fd;
      d = a + (b - a) * golden;
      fd = f(d);
    }
    else
    {
      assert(fc == fd);
      a = c;
      b = d;
      c = b - (b - a) * golden;
      d = a + (b - a) * golden;
      fc = f(c);
      fd = f(d);
    }
  }

  if (fd < task.min_value)
  {
    task.min_candidate = d;
    task.min_value = fd;
  }

  task.left_bound = c;
  task.right_bound = d;
  task.left_value = fc;
  task.right_value = fd;
  return interval_check(task);
}


// Brent's method applied to minimization.
// Borrowed from Boost (see boost.org, brent_find_minima function): boost/math/tools/minima.hpp.
template <
  class Function,
  class Argument,
  class Value,
  class Interval_check>
bool brent_minimize
  (
    Function f,
    Local_min_state<Argument, Value> &task,
    Interval_check interval_check
  )
{
  using std::fmax;
  using std::ldexp;
  using std::abs;

  const auto
    tolerance = static_cast<Argument>(
      fmax(task.interval_tolerance, static_cast<Argument>(ldexp(1., -23))));
  auto
    min = task.left_bound,
    max = task.right_bound;

  // Golden ratio factor: (sqrt(5) - 1)/2.
  static const auto
    golden = static_cast<Argument>(half(sqrt(Argument(5)) - Argument(1)));

  // Original Boost authors comments below.
  Argument
    x,  // minima so far
    w,  // second best point
    v,  // previous value of w
    u,  // most recent evaluation point
    delta,  // The distance moved in the last step
    delta2, // The distance moved in the step before last
    fract1, fract2,  // minimal relative movement in x
    mid; // midpoint of min and max

  Value
    fu, fv, fw, fx;  // function evaluations at u, v, w, x

  x = w = v = max;
  fw = fv = fx = task.right_value;
  delta2 = delta = 0;

  while (true)
  {
    // get midpoint
    mid = half(min + max);
    // work out if we're done already:
    fract1 = tolerance * abs(x) + tolerance * Argument(0.25);
    fract2 = 2 * fract1;
    if (abs(x - mid) <= (fract2 - half(max - min)))
      break;

    if (abs(delta2) > fract1)
    {
      // try and construct a parabolic fit:
      auto
        r = (x - w) * (fx - fv),
        q = (x - v) * (fx - fw),
        p = (x - v) * q - (x - w) * r;

      q = 2 * (q - r);
      if (q > 0)
        p = -p;
      q = abs(q);

      auto td = delta2;
      delta2 = delta;

      // determine whether a parabolic step is acceptible or not:
      if ((abs(p) >= abs(q * half(td))) || (p <= q * (min - x)) || (p >= q * (max - x)))
      {
        // nope, try golden section instead
        delta2 = (x >= mid) ? min - x : max - x;
        delta = golden * delta2;
      }
      else
      {
        // whew, parabolic fit:
        delta = p / q;
        u = x + delta;
        if (((u - min) < fract2) || ((max- u) < fract2))
          delta = (mid - x) < 0 ? -abs(fract1) : abs(fract1);
      }
    }
    else
    {
      // golden section:
      delta2 = (x >= mid) ? min - x : max - x;
      delta = golden * delta2;
    }

    // update current position:
    u = (abs(delta) >= fract1) ?
        (x + delta)
      : (delta > 0 ? (x + abs(fract1)) : (x - abs(fract1)));

    fu = f(u);

    if (fu <= fx)
    {
      // good new point is an improvement!
      // update brackets:
      if(u >= x)
        min = x;
      else
        max = x;
      // update control points:
      v = w;
      w = x;
      x = u;
      fv = fw;
      fw = fx;
      fx = fu;
    }
    else
    {
      // Oh dear, point u is worse than what we have already,
      // even so it *must* be better than one of our endpoints:
      if (u < x)
        min = u;
      else
        max = u;
      if ((fu <= fw) || (w == x))
      {
        // however it is at least second best:
        v = w;
        w = u;
        fv = fw;
        fw = fu;
      }
      else if ((fu <= fv) || (v == x) || (v == w))
      {
        // third best:
        v = u;
        fv = fu;
      }
    }
  }

  task.min_candidate = x;
  task.min_value = fx;
  return interval_check(task);
}


////////////////////////////////////////////////////////////////////////////////
// Testing.
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;

// The counter, which is used to directly measure efficiency of different methods.
size_t f_invocations;
// "Global" minimum position and value.
float global_min, global_min_position;

float f(float x)
{
  ++f_invocations;
  return 0.5f * sin(x) + sin(1.5f * x + 2.f)
    - 1.5f * sin(5.f / 17.f * x + 1.f) - atan(0.5f * x + 5.f);
}

bool update_global_min(Local_min_state<float, float> &task)
{
  if (task.min_value < global_min)
  {
    global_min = task.min_value;
    global_min_position = task.min_candidate;
  }
  return false; // proceed
}

int main()
{
  cout.precision(9);
  Local_min_state<float, float> task
  { // Interval tolerance:
    std::sqrt(std::numeric_limits<float>::epsilon())
  };

  static const int METHODS = 3;
  using F = float(*)(float);
  using IC = bool(*)(Local_min_state<float, float>&);

  bool (*methods[METHODS])(F, Local_min_state<float, float>&, IC)
  {
    ternary_minimize<F, float, float, IC>,
    golden_ratio_minimize<F, float, float, IC>,
    brent_minimize<F, float, float, IC>
  };

  const char *name[METHODS]
  {
    "Ternary search",
    "Golden ratio search",
    "Brent"
  };

  float gmin[METHODS], gminpos[METHODS];
  for (int i = 0; i < METHODS; ++i)
  {
    cout << name[i] << '\n';
    task.left_bound = -20.f;
    task.right_bound = 20.f;
    f_invocations = 0;
    global_min = 100.f;
    global_min_position = -100.f;
    const auto n = global_minimize(f, task, update_global_min, methods[i], 0.05f);
    cout << "Minima brackets found: " << n << ", f invocations: " << f_invocations << '\n';
    cout << "Global min = " << (gmin[i] = global_min) << ", at " << (gminpos[i] = global_min_position) << '\n' << endl;
  }

  auto best_i = min_element(begin(gmin), end(gmin)) - gmin;
  cout << "With min = " << gmin[best_i] << " winners are: ";
  for (int i = 0; i < METHODS; ++i)
    if (gmin[i] == gmin[best_i])
      cout << name[i] << ", ";
  cout << "\b\b." << endl;
  cin.ignore();
  return 0;
}


2100-bsearch.cpp

// bsearch.cpp
#include <iostream>
#include <iterator>
using namespace std;

// Двоичный поиск в массиве.
const int* bsearch(int item, const int *begin, const int *end)
{
  while (1 < end - begin)
  {
    const auto mid = begin + (end - begin) / 2;
    if (item < *mid)
      end = mid;
    else
      begin = mid;
  }
  return begin;
}

int main()
{
  int a[] { 0, 1, 2, 2, 2, 3, 4, 5, 6, 8, 40 };

  for (int i: a)
  {
    const auto pos = bsearch(i, begin(a), end(a));
    cout << "a[" << (pos - a) << "] == " << *pos << '\n';
  }

  auto pos = bsearch(7, begin(a), end(a));
  cout << "a[" << (pos - a) << "] == " << *pos << '\n';

  pos = bsearch(9, begin(a), end(a));
  cout << "a[" << (pos - a) << "] == " << *pos << '\n';

  pos = bsearch(-1, begin(a), end(a));
  cout << "a[" << (pos - a) << "] == " << *pos << '\n';

  pos = bsearch(100, begin(a), end(a));
  cout << "a[" << (pos - a) << "] == " << *pos << '\n';

  cin.ignore();
  return 0;
}


2110-eqrange.cpp

// eqrange.cpp
#include <iostream>
#include <iterator>
using namespace std;

// Двоичный поиск нижней границы.
const int* lbound(const int *begin, const int *end, int item)
{
  while (1 < end - begin)
  {
    const auto mid = begin + (end - begin) / 2;
    if (*mid < item)
      begin = mid + 1;
    else
      end = mid;
  }

  return begin == end || *begin < item? end: begin;
}

// Двоичный поиск верхней границы.
const int* ubound(const int *begin, const int *end, int item)
{
  while (1 < end - begin)
  {
    const auto mid = begin + (end - begin) / 2;
    if (item < *mid)
      end = mid;
    else
      begin = mid;
  }

  return begin == end || !(item < *begin)? end: begin;
}


struct Const_range
{
  const int *b, *e;
  auto begin() const { return b; }
  auto end() const { return e; }
};

// Поиск диапазона эквивалентных (равных) элементов.
Const_range eqrange(const int *begin, const int *end, int item)
{
  const auto lb = lbound(begin, end, item);
  return { lb, ubound(lb, end, item) };
}


int main()
{
  int a[] { 0, 1, 2, 2, 2, 3, 4, 5, 6, 8, 40 };

  for (int x : eqrange(begin(a), end(a), 1))
    cout << x << ' ';

  cout << endl;
  for (int x : eqrange(begin(a), end(a), 2))
    cout << x << ' ';

  cout << endl;
  for (int x : eqrange(begin(a), end(a), 7))
    cout << x << ' ';

  cin.ignore();
  return 0;
}


2150-vll_stack.cpp

// vll_stack.cpp
// void (intrusive) linked list
#include <cassert>
#include <cstdlib>
using namespace std;

// Type-agnostic (generic) code.
// Pure C.

void* list_bind(void *head, void *tail)
{
  assert(head);
  *(void**)head = tail;
  return head;
}

void* list_tail(void *head)
{
  assert(head);
  return *(void**)head;
}

void* head_value(void *head)
{
  return ((void**)head + 1);
}

void* alloc_head(size_t bytes)
{
  return calloc(1, bytes + sizeof(void*));
}

void free_head(void **head)
{
  void *tail = list_tail(*head);
  free(*head);
  *head = tail;
}

void free_list(void *head)
{
  while (head)
    free_head(&head);
}


// Use this to organize a stack.
// Uses C++ references, nullptr and IO.
// Still quite dirty at the edge (or behind?) of UB.
#include <iostream>

struct Int_stack_node
{
  void *link;
  int value;
};

Int_stack_node*& push(Int_stack_node *&stack, int value)
{
  auto new_head = alloc_head(sizeof(int));
  *(int*)head_value(new_head) = value;
  return stack = (Int_stack_node*)list_bind(new_head, stack);
}

int pop(Int_stack_node *&stack)
{
  assert(stack);
  const auto result = *(int*)head_value(stack);
  free_head((void**)&stack);
  return result;
}

void destroy(Int_stack_node *&stack)
{
  free_list(stack);
  stack = nullptr;
}

int main()
{
  Int_stack_node *stack = nullptr;
  for (int i; cin >> i;)
    push(stack, i);

  cin.clear();
  while (stack)
    cout << '\n' << pop(stack);

  cout << endl;
  return 0;
}


2160-vbst.cpp

// vbst.cpp
// void (intrusive) binary (search) tree
#include <cassert>
#include <cstdlib>
using namespace std;

// Type-agnostic (generic) code.
// Pure C.

void* tree_bind(void *parent, void *left, void *right)
{
  assert(parent);
  void **child = (void**)parent;

  child[0] = left;
  child[1] = right;
  return parent;
}

void* tree_left(void *parent)
{
  assert(parent);
  return ((void**)parent)[0];
}

void* tree_right(void *parent)
{
  assert(parent);
  return ((void**)parent)[1];
}

void* parent_value(void *parent)
{
  return ((void**)parent + 2);
}

void* alloc_tree_node(size_t bytes)
{
  return calloc(1, bytes + 2 * sizeof(void*));
}

void free_tree_node(void *parent)
{
  free(parent);
}

// May crash due to stack overflow!
void free_tree(void *parent)
{
  if (parent)
  {
    free_tree(tree_left(parent));
    free_tree(tree_right(parent));
    free_tree_node(parent);
  }
}

// In-order tree traversal.
// May crash due to stack overflow!
void tree_inorder(void *parent, void (*visit)(void*))
{
  if (parent)
  {
    tree_inorder(tree_left(parent), visit);
    visit(parent_value(parent));
    tree_inorder(tree_right(parent), visit);
  }
}


// Use this to organize a tree.
// Uses C++ references, nullptr and IO.
// Still quite dirty at the edge (or behind?) of UB.
#include <iostream>

struct Bst_node
{
  void *left, *right;
  int value;
};

void destroy(Bst_node *&tree)
{
  free_tree(tree);
  tree = nullptr;
}

Bst_node*& insert(Bst_node *&tree, int value)
{
  auto new_node = alloc_tree_node(sizeof(int));
  *(int*)parent_value(new_node) = value;

  if (!tree)
  {
    tree = (Bst_node*)new_node;
  }
  else // Binary search.
  {
    for (Bst_node *parent = tree;;)
    {
      // A reference to a pointer (this would be a "double-star" in C).
      void *&next = value < parent->value?
        parent->left: parent->right;

      if (!next)
      {
        next = new_node;
        break;
      }

      parent = (Bst_node*)next;
    }
  }

  return tree;
}


void print_int(void *data)
{
  cout << *(int*)data << ' ';
}

int main()
{
  Bst_node *tree = nullptr;
  for (int i; cin >> i;)
    insert(tree, i);

  cin.clear();
  tree_inorder(tree, print_int);

  cout << endl;
  return 0;
}


2170-trie_inorder.cpp

// trie_inorder.cpp
// Simplistic trie.
#include <cstddef>
#include <cassert>
#include <iostream>
#include <iterator>
#include <string>
#include <fstream>  // вывод в файл: ofstream
using namespace std;

/// Условное значение "позиция не найдена".
const size_t NOT_FOUND = -1;

/// Найти позицию нулевого символа в массиве символов.
size_t find_null(const char *word, size_t word_len)
{
  for (size_t i = 0; i < word_len; ++i)
    if (word[i] == '\0')
      return i;
  return NOT_FOUND;
}

/// Найти позицию заданного символ в массиве символов.
size_t find_char(const char *word, size_t word_len, char ch)
{
  for (size_t i = 0; i < word_len && word[i] != '\0'; ++i)
    if (word[i] == ch)
      return i;
  return NOT_FOUND;
}

/// Максимально возможное число потомков одного узла префиксного дерева.
const size_t MAX_TRIE_CHILDREN = 8;
/// Узел префиксного дерева.
struct Trie_node
{
  Trie_node *children[MAX_TRIE_CHILDREN];
  size_t label[MAX_TRIE_CHILDREN];
  char prefix[MAX_TRIE_CHILDREN];

  /// "Конструктор по умолчанию": вызывается при создании узла.
  Trie_node() { prefix[0] = '\0'; }

  /// Поиск свободной позиции в массиве потомков.
  size_t find_empty_slot()
  {
    return find_null(prefix, MAX_TRIE_CHILDREN);
  }
  /// Поиск номера потомка, отвечающего очередному символу.
  size_t find_char(char ch) const
  {
    return ::find_char(prefix, MAX_TRIE_CHILDREN, ch);
  }

  /// Рекурсивная операция вставки распознаваемой строки с заданной меткой lbl.
  void add_word(const char *word, size_t lbl)
  {
    if (*word == '\0')
      return;

    auto index = find_char(*word);
    if (index == NOT_FOUND)
    {
      index = find_empty_slot();
      if (index == NOT_FOUND)
      {
        assert(!"Trie: impossible to insert the next child: the node is full.");
        return;
      }

      prefix[index] = *word;
      if (index + 1 < MAX_TRIE_CHILDREN)
        prefix[index + 1] = '\0'; // The next empty slot.

      children[index] = new Trie_node;
      label[index] = NOT_FOUND; // Unassigned label at first.
    }

    if (word[1] == '\0')
      label[index] = lbl;
    else
      children[index]->add_word(word + 1, lbl);
    // Аналогично find_word: рекурсия -- хвостовой вызов,
    // как здесь заменить рекурсию на цикл?
  }

  /// Извлечение метки, отвечающей заданному слову
  /// (с которой оно было ранее вставлено add_word).
  size_t find_word(const char *word) const
  {
    if (*word == '\0')
      return NOT_FOUND;

    auto index = find_char(*word);
    if (index == NOT_FOUND)
      return NOT_FOUND;

    // Это был последний ненулевой символ слова
    // (== последний символ си-строки)?
    if (word[1] == '\0')
      return label[index];

    // Нет -- продолжим с "хвостом" слова (рекурсия -- хвостовой вызов).
    // (Как заменить этот вызов на цикл?)
    return children[index]->find_word(word + 1);
  }

  /// Обход поддерева слева-направо.
  void inorder(
    void (*open)(),
    void (*visit)(char, size_t),
    void (*close)())
  {
    if (prefix[0] == '\0')
      return;

    open();
    for (size_t i = 0; i < MAX_TRIE_CHILDREN && prefix[i] != '\0'; ++i)
    {
      visit(prefix[i], label[i]);
      children[i]->inorder(open, visit, close);
    }
    close();
  }
};

/// Префиксное дерево.
struct Trie
{
  Trie_node *root;

  void add_word(const char *word, size_t label)
  {
    assert(word);
    if (!root)
      root = new Trie_node;
    root->add_word(word, label);
  }

  size_t find_word(const char *word) const
  {
    assert(word);
    if (!root)
      return NOT_FOUND;
    return root->find_word(word);
  }

  void inorder(
    void (*open)(),
    void (*visit)(char ch, size_t label),
    void (*close)())
  {
    if (root)
      root->inorder(open, visit, close);
  }
};

/// Вспомогательный код для "распечатки" дерева.
ofstream file("test.cpp"); // Файл для вывода результата.
char tab[7]; // Строка текущих отступов.
bool start;  // Флаг "ожидается первый потомок".

void print_open()
{
  file << '\n' << tab << "(\n";
  auto pos = find_null(tab, sizeof(tab) - 1);
  if (pos != NOT_FOUND)
    tab[pos] = '\t';
  start = true;
}

void print_visit(char ch, size_t label)
{
  file << (start? tab: ", ") << ch;
  if (label != NOT_FOUND)
    file << " [" << label << ']';
  start = false;
}

void print_close()
{
  auto pos = find_null(tab, sizeof(tab));
  if (pos != 0)
    tab[pos-1] = '\0';
  file << '\n' << tab << ")\n";
  start = true;
}


int main()
{
  const char *words[]
  {
    "water",
    "was",
    "while",
    "when",
    "where",
    "why",
    "what",
    "whilst",
    "then",
    "there",
    "these",
    "today",
    "to",
    "the"
  };

  Trie trie{};

  // Поместить все строки из words в префиксное дерево.
  // В качестве метки -- индекс строки в массиве.
  for (size_t i = 0, iend = end(words) - begin(words); i < iend; ++i)
    trie.add_word(words[i], i);

  file << "trie\n{";
  // Вывести префиксное дерево в файл вместе с метками.
  trie.inorder(print_open, print_visit, print_close);
  file << "}\n";
  
  // Выводить метки слов, которые вводит пользователь.
  for (string word; cin >> word;)
  {
    const auto label = trie.find_word(word.c_str()); // token(word.c_str())
    if (label == NOT_FOUND)
      cout << "<not found>\n";
    else
      cout << label << ": \"" << words[label] << "\"\n";
  }
  
  return 0;
}


2200-csorts_i32.cpp

// csorts_i32.cpp
// Поразрядная сортировка для int32.
#include <cstddef>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <utility>
#include <algorithm>
using namespace std;


////////////////////////////////////////////////////////////////////////////////
// Варианты поразрядной сортировки.

// От младших к старшим.
void lsd_radix_sort(int32_t *from, int32_t *to)
{
  assert(to - from < INT_MAX);

  // 11-bit digits: 32/11 = 3 digits.
  static const unsigned
    digit_bits = 11,
    last_digit_bits = 10;

  static const int
    buckets = 1 << digit_bits,
    last_buckets = 1 << last_digit_bits;

  static const uint32_t
    mask = buckets - 1,
    last_mask = last_buckets - 1;

  using I = int;
  const I items = to - from;
  if (items < buckets)
    return sort(from, to);

  const auto bfrom = (uint32_t*)from;
  const auto buf = new uint32_t[items];

  // Three statically allocated sets of offsets:
  // all digits are counted in one scan.
  I counters[2*buckets + last_buckets]{};
  I *const offs_1 = counters;
  I *const offs_2 = offs_1 + buckets;
  I *const offs_3 = offs_2 + buckets;

  // Run #0: compute offs_1, offs_2, offs_3.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    offs_1[item & mask]++;
    offs_2[(item >> digit_bits) & mask]++;
    offs_3[((item >> 2*digit_bits) - last_buckets/2) & last_mask]++;
  }

  for (I i = 0, s1 = 0, s2 = 0; i < buckets; ++i)
  {
    const auto
      t1 = offs_1[i] + s1,
      t2 = offs_2[i] + s2;

    offs_1[i] = s1;
    offs_2[i] = s2;

    s1 = t1;
    s2 = t2;
  }

  for (I i = 0, s = 0; i < last_buckets; ++i)
  {
    const auto t = offs_3[i] + s;
    offs_3[i] = s;
    s = t;
  }

  // Additional optimization for better adaptivity:
  // Runs may be disabled if only one element of
  // the corresponding offs_x array is actually non-zero.
  // This may be checked in the summing loops above.

  // Run #1: first scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[item & mask]++] = item;
  }

  // Run #2: second scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    bfrom[offs_2[(item >> digit_bits) & mask]++] = item;
  }

  // Run #3: final scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_3[((item >> 2*digit_bits) - last_buckets/2) & last_mask]++] = item;
  }

  memcpy(bfrom, buf, items * sizeof(uint32_t));
  delete[] buf;
}


////////////////////////////////////////////////////////////////////////////////
// От старших к младшим: младший (самый правый) разряд.
void msd_radix_sort_2(uint32_t *from, uint32_t *to, uint32_t *buf)
{
  // 11-bit digits: 32/11 = 3 digits.
  static const unsigned digit_bits = 11;
  static const int buckets = 1 << digit_bits;
  static const uint32_t mask = buckets - 1;

  using I = int;
  const I items = to - from;
  if (items < buckets)
  {
    sort(from, to);
    memcpy(buf, from, items * sizeof(uint32_t));
    return;
  }

  // Three statically allocated sets of offsets:
  // all digits are counted in one scan.
  I offs[buckets]{};

  // Compute offs.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    offs[item & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    buf[offs[item & mask]++] = item;
  }
}


// От старших к младшим: средний разряд.
void msd_radix_sort_1(uint32_t *from, uint32_t *to, uint32_t *buf)
{
  // 11-bit digits: 32/11 = 3 digits.
  static const unsigned shift = 11, digit_bits = 11;
  static const int buckets = 1 << digit_bits;
  static const uint32_t mask = buckets - 1;

  using I = int;
  const I items = to - from;
  if (items < buckets)
  {
    sort(from, to);
    memcpy(buf, from, items * sizeof(uint32_t));
    return;
  }

  // Three statically allocated sets of offsets:
  // all digits are counted in one scan.
  I offs[buckets]{};

  // Compute offs.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    offs[(item >> shift) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    buf[offs[(item >> shift) & mask]++] = item;
  }

  // Sort blocks.
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    msd_radix_sort_2(buf + prev, buf + offs[i], from + prev);
    prev = offs[i];
  }
}


// От старших к младшим: старший разряд.
void msd_radix_sort_0(uint32_t *from, uint32_t *to, uint32_t *buf)
{
  // 11-bit digits: 32/11 = 3 digits.
  static const unsigned shift = 22, digit_bits = 10;
  static const int buckets = 1 << digit_bits;
  static const uint32_t mask = buckets - 1;

  using I = int;
  const I items = to - from;

  // Three statically allocated sets of offsets:
  // all digits are counted in one scan.
  I offs[buckets]{};

  // Compute offs.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    offs[((item >> shift) - buckets/2) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    buf[offs[((item >> shift) - buckets/2) & mask]++] = item;
  }

  // Sort blocks.
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    msd_radix_sort_1(buf + prev, buf + offs[i], from + prev);
    prev = offs[i];
  }
}


// От старших к младшим: основная процедура.
void msd_radix_sort(int32_t *from, int32_t *to)
{
  const auto items = to - from;
  assert(items < INT_MAX);
  if (items < 2048)
    return sort(from, to);

  const auto buf = new uint32_t[items];
  msd_radix_sort_0((uint32_t*)from, (uint32_t*)to, buf);
  memcpy(from, buf, items * sizeof(int32_t));
  delete[] buf;
}


////////////////////////////////////////////////////////////////////////////////
// Testing.

void std_stable_sort(int32_t *a, int32_t *ae)
{
  stable_sort(a, ae);
}


#include <ctime>
#include <cmath>
#include <iostream>
#include <random>
#include <iterator>
#include <vector>

double elapsed(clock_t t)
{
  return double(clock() - t) / CLOCKS_PER_SEC;
}


void do_tests(const vector<int32_t> &master)
{
  const auto mm = minmax_element(begin(master), end(master));
  cout << "items: " << master.size()*1e-6 << "M\t";
  cout << "min: " << *mm.first << "\tmax: " << *mm.second << '\n';

  auto sorted = master;
  auto t = clock();
  sort(begin(sorted), end(sorted));
  double std_time = elapsed(t);
  cout << "\nstd::sort\t" << std_time << "s\n\n";

  struct {
    void(*sort_alg)(int32_t*, int32_t*);
    const char *name;
  } algs[]
  {
    { std_stable_sort,     "std stable" },
    { lsd_radix_sort,      "lsd radix " },
    { msd_radix_sort,      "msd radix " },
  };

  unsigned lines = 0;
  for (auto &task : algs)
  {
    auto test = master;
    t = clock();
    task.sort_alg(test.data(), test.data() + test.size());
    double this_time = elapsed(t);
    cout << task.name << '\t' << this_time << "s,\t";
    cout << std_time / this_time << '\t';
    cout << (test == sorted) << endl;

    if (++lines == 6)
    {
      lines = 0;
      cout << '\n';
    }
  }
}


int main()
{
  cout.precision(3);
  mt19937_64 rng(14456);
  vector<int32_t> master(1 << 24);

  cout << "Uniform: non-negatives\n";
  {
    uniform_int_distribution<int32_t> vg;
    for (auto &val : master)
      val = vg(rng);
  }

  do_tests(master);
  cout << endl;

  cout << "Uniform: full range\n";
  {
    uniform_int_distribution<int32_t> vg(INT32_MIN, INT32_MAX);
    for (auto &val : master)
      val = vg(rng);
  }

  do_tests(master);
  cout << endl;
  return 0;
}


2210-csorts_f64.cpp

// csorts_f64.cpp
// Блочная и поразрядная сортировки для массивов значений типа double.
// Два примера с разными распределениями.
#include <cstddef>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <utility>
#include <algorithm>
using namespace std;


////////////////////////////////////////////////////////////////////////////////
// Варианты блочной сортировки.

// Нерекурсивная блочная сортировка.
void nonrec_bucket_sort(double *from, double *to)
{
  using I = ptrdiff_t;

  // Размер блока -- 32кб.
  static const I bucket_size = 32768 / sizeof(double);
  const I items = to - from;
  if (items < 2 * bucket_size)
    return sort(from, to);

  // Определить минимум и максимум.
  // g++ векторизует этот цикл, если указан параметр -ffast-math.
  auto minv = *from, maxv = *from;
  for (I i = 1; i < items; ++i)
  {
    minv = from[i] < minv ? from[i] : minv;
    maxv = maxv < from[i] ? from[i] : maxv;
  }

  if (minv == maxv)
    return;

  // Количество блоков.
  const auto buckets = 1 + items / bucket_size;
  // Коэффициент аффинной функции, вычисляющей номер блока.
  const auto factor = (buckets - 1) / (maxv - minv);

  // Выделить вспомогательную память:
  // буфер для копирования значений: items * sizeof(double),
  // счётчики блоков: buckets * sizeof(I).
  assert(items < PTRDIFF_MAX / (2 * sizeof(double)));
  const I storage_size = items * sizeof(double) + buckets * sizeof(I);
  const auto storage = malloc(storage_size);
  if (!storage)
    return sort(from, to);

  const auto buf = (double*)storage;
  const auto off = (I*)(buf + items); // Размеры блоков ("смещения" = offsets)
  memset(off, 0, buckets * sizeof(I)); // Обнулить размеры.

  // Вычислить размеры блоков.
  for (I i = 0; i < items; ++i)
    off[(I)(factor * (from[i] - minv))]++;

  // Вычислить смещения и заполнить начальные значения позиций записи.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = off[i] + s;
    off[i] = s;
    s = t;
  }

  // Раскидать значения по блокам.
  for (I i = 0; i < items; ++i)
    buf[off[(I)(factor * (from[i] - minv))]++] = from[i];

  // Отсортировать блоки.
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    sort(buf + prev, buf + off[i]);
    prev = off[i];
  }

  // Скопировать конкатенацию блоков в исходный массив.
  memcpy(from, buf, items * sizeof(double));
  free(storage);
}




// Рекурсивная блочная сортировка.
// Использует две функции: внешнюю, создающую буфер, и
// внутреннюю -- со статическим массивом счётчиков и ограниченной глубиной рекурсии.

// Внутренняя функция рекурсивной блочной сортировки.
void rec_bucket_sort_inner(double *from, double *to, double *buf, int depth)
{
  using I = ptrdiff_t;

  // Размер блока -- 32кб.
  static const I bucket_size = 32768 / sizeof(double);
  static const I max_buckets = 2048; // Максимальное количество блоков.

  const I items = to - from;
  if (0 == depth-- || items <= 2 * bucket_size)
    return sort(from, to);

  // Определить минимум и максимум.
  // g++ векторизует этот цикл, если указан параметр -ffast-math.
  auto minv = *from, maxv = *from;
  for (I i = 1; i < items; ++i)
  {
    minv = from[i] < minv ? from[i] : minv;
    maxv = maxv < from[i] ? from[i] : maxv;
  }

  if (minv == maxv)
    return;

  // Количество блоков.
  const auto possible_buckets = 1 + items / bucket_size;
  const auto buckets = possible_buckets < max_buckets ? possible_buckets : max_buckets;

  // Коэффициент аффинной функции, вычисляющей номер блока.
  const auto factor = (buckets - 1) / (maxv - minv);

  I off[max_buckets]{};

  // Вычислить размеры блоков.
  for (I i = 0; i < items; ++i)
    off[(I)(factor * (from[i] - minv))]++;

  // Вычислить начальные значения позиций записи.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = off[i] + s;
    off[i] = s;
    s = t;
  }

  // Раскидать значения по блокам.
  for (I i = 0; i < items; ++i)
    buf[off[(I)(factor * (from[i] - minv))]++] = from[i];

  // Скопировать конкатенацию блоков в исходный массив.
  memcpy(from, buf, items * sizeof(double));

  // Отсортировать блоки "на месте" (уже в исходном массиве).
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    rec_bucket_sort_inner(from + prev, from + off[i], buf, depth);
    prev = off[i];
  }
}


// Внешняя функция рекурсивной блочной сортировки.
void rec_bucket_sort(double *from, double *to)
{
  const auto items = to - from;
  // Выделить вспомогательную память: буфер для копирования значений.
  const auto buf = new double[items];
  // Вызвать "вложенную" рекурсивную функцию.
  rec_bucket_sort_inner(from, to, buf, 4);
  delete[] buf;
}


// Рекурсивная блочная сортировка: челночное использование буфера и основного массива.
// Использует две функции: внешнюю, создающую буфер, и
// внутреннюю -- со статическим массивом счётчиков и ограниченной глубиной рекурсии.

// Внутренняя функция рекурсивной блочной сортировки.
void rec_bucket_sort_inner2(double *from, double *to, double *buf, int depth)
{
  using I = ptrdiff_t;

  // Размер блока -- 32кб.
  static const I bucket_size = 32768 / sizeof(double);
  static const I max_buckets = 2048; // Максимальное количество блоков.

  const I items = to - from;
  if (0 == depth || items <= 2 * bucket_size)
  {
    sort(from, to);
    if (depth % 2)
      memcpy(buf, from, items * sizeof(double));
    return;
  }

  // Определить минимум и максимум.
  // g++ векторизует этот цикл, если указан параметр -ffast-math.
  auto minv = *from, maxv = *from;
  for (I i = 1; i < items; ++i)
  {
    minv = from[i] < minv ? from[i] : minv;
    maxv = maxv < from[i] ? from[i] : maxv;
  }

  if (minv == maxv)
    return;

  // Количество блоков.
  const auto possible_buckets = 1 + items / bucket_size;
  const auto buckets = possible_buckets < max_buckets ? possible_buckets : max_buckets;

  // Коэффициент аффинной функции, вычисляющей номер блока.
  const auto factor = (buckets - 1) / (maxv - minv);

  I off[max_buckets]{};

  // Вычислить размеры блоков.
  for (I i = 0; i < items; ++i)
    off[(I)(factor * (from[i] - minv))]++;

  // Вычислить начальные значения позиций записи.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = off[i] + s;
    off[i] = s;
    s = t;
  }

  // Раскидать значения по блокам.
  for (I i = 0; i < items; ++i)
    buf[off[(I)(factor * (from[i] - minv))]++] = from[i];

  // Отсортировать блоки "на месте" (уже в исходном массиве).
  --depth;
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    rec_bucket_sort_inner2(buf + prev, buf + off[i], from + prev, depth);
    prev = off[i];
  }
}


// Внешняя функция рекурсивной блочной сортировки.
void rec_bucket_sort2(double *from, double *to)
{
  const auto items = to - from;
  // Выделить вспомогательную память: буфер для копирования значений.
  const auto buf = new double[items];
  // Вызвать "вложенную" рекурсивную функцию.
  rec_bucket_sort_inner2(from, to, buf, 4);
  delete[] buf;
}


////////////////////////////////////////////////////////////////////////////////
// Варианты поразрядной сортировки.

// От младших к старшим.
void lsd_radix_sort(double *from, double *to)
{
  static_assert(sizeof(double) == sizeof(uint64_t));
  // 11-bit digits: 64/11 = 6 digits.
  static const unsigned digit_bits = 11, buckets = 1 << digit_bits;
  static const uint64_t mask = buckets - 1;

  using I = size_t;
  const I items = to - from;
  if (items < buckets)
    return sort(from, to);

  const auto bfrom = (uint64_t*)from;
  const auto buf = new uint64_t[items];

  // Two statically allocated sets of offsets:
  // one in use, other being computed while the scan.
  I counters[2 * buckets]{};
  I *const offs_1 = counters;
  I *const offs_2 = offs_1 + buckets;

  // Run #0: negatives-positives transform, compute offs_1.
  // Negatives-positives problem: inverse negatives, flip sign bit of positives.
  for (I i = 0; i < items; ++i)
  {
    const auto raw = bfrom[i];
    const auto item = raw ^ ((0 - (raw >> 63)) | (uint64_t(1) << 63));
    bfrom[i] = item;
    offs_1[item & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs_1[i] + s;
    offs_1[i] = s;
    s = t;
  }

  // Run #1: first scatter, compute offs_2.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[item & mask]++] = item;
    offs_2[(item >> digit_bits) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    offs_1[i] = 0;
    const auto t = offs_2[i] + s;
    offs_2[i] = s;
    s = t;
  }

  // Run #2: second scatter, compute offs_1.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    bfrom[offs_2[(item >> digit_bits) & mask]++] = item;
    offs_1[(item >> 2 * digit_bits) & mask]++;
  }

  // Now more copy-paste en route.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    offs_2[i] = 0;
    const auto t = offs_1[i] + s;
    offs_1[i] = s;
    s = t;
  }

  // Run #3: third scatter, compute offs_2.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[(item >> 2 * digit_bits) & mask]++] = item;
    offs_2[(item >> 3 * digit_bits) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    offs_1[i] = 0;
    const auto t = offs_2[i] + s;
    offs_2[i] = s;
    s = t;
  }

  // Run #4: fourth scatter, compute offs_1.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    bfrom[offs_2[(item >> 3 * digit_bits) & mask]++] = item;
    offs_1[(item >> 4 * digit_bits) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    offs_2[i] = 0;
    const auto t = offs_1[i] + s;
    offs_1[i] = s;
    s = t;
  }

  // Run #5: fifth scatter, compute offs_2.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[(item >> 4 * digit_bits) & mask]++] = item;
    offs_2[(item >> 5 * digit_bits) & mask]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs_2[i] + s;
    offs_2[i] = s;
    s = t;
  }

  // Run #6: final scatter, negatives-positives inverse transform.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    const auto raw = item ^ (((item >> 63) - 1) | (uint64_t(1) << 63));
    bfrom[offs_2[item >> 5 * digit_bits]++] = raw;
  }

  delete[] buf;
}


// От младших к старшим: вариант 2.
void lsd_radix_sort2(double *from, double *to)
{
  static_assert(sizeof(double) == sizeof(uint64_t));
  // 11-bit digits: 64/11 = 6 digits.
  static const unsigned digit_bits = 11, buckets = 1 << digit_bits;
  static const uint64_t mask = buckets - 1;

  using I = size_t;
  const I items = to - from;
  if (items < buckets)
    return sort(from, to);

  const auto bfrom = (uint64_t*)from;
  const auto buf = new uint64_t[items];

  // Two statically allocated sets of offsets:
  // one in use, other being computed while the scan.
  I counters[2 * buckets]{};
  const auto offs_1 = &counters[0];
  const auto offs_2 = &counters[buckets];

  // Run #0: negatives-positives transform, compute offs_1 & offs_2.
  // Negatives-positives problem: inverse negatives, flip sign bit of positives.
  for (I i = 0; i < items; ++i)
  {
    const auto raw = bfrom[i];
    const auto item = raw ^ ((0 - (raw >> 63)) | (uint64_t(1) << 63));
    bfrom[i] = item;
    offs_1[item & mask]++;
    offs_2[(item >> digit_bits) & mask]++;
  }

  for (I i = 0, s1 = 0, s2 = 0; i < buckets; ++i)
  {
    const auto
      t1 = offs_1[i] + s1,
      t2 = offs_2[i] + s2;

    offs_1[i] = s1;
    offs_2[i] = s2;
    s1 = t1;
    s2 = t2;
  }

  // Run #1: first scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[item & mask]++] = item;
  }

  // Run #2: second scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    bfrom[offs_2[(item >> digit_bits) & mask]++] = item;
  }

  // Compute offs_1 & offs_2 for the next two runs.
  memset(counters, 0, sizeof(counters));
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    offs_1[(item >> 2 * digit_bits) & mask]++;
    offs_2[(item >> 3 * digit_bits) & mask]++;
  }

  for (I i = 0, s1 = 0, s2 = 0; i < buckets; ++i)
  {
    const auto
      t1 = offs_1[i] + s1,
      t2 = offs_2[i] + s2;

    offs_1[i] = s1;
    offs_2[i] = s2;
    s1 = t1;
    s2 = t2;
  }

  // Run #3: third scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[(item >> 2 * digit_bits) & mask]++] = item;
  }

  // Run #4: fourth scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    bfrom[offs_2[(item >> 3 * digit_bits) & mask]++] = item;
  }

  // Compute offs_1 & offs_2 for the next two runs.
  memset(counters, 0, sizeof(counters));
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    offs_1[(item >> 4 * digit_bits) & mask]++;
    offs_2[(item >> 5 * digit_bits) & mask]++;
  }

  for (I i = 0, s1 = 0, s2 = 0; i < buckets; ++i)
  {
    const auto
      t1 = offs_1[i] + s1,
      t2 = offs_2[i] + s2;

    offs_1[i] = s1;
    offs_2[i] = s2;
    s1 = t1;
    s2 = t2;
  }

  // Run #5: fifth scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs_1[(item >> 4 * digit_bits) & mask]++] = item;
  }

  // Run #6: final scatter, negatives-positives inverse transform.
  for (I i = 0; i < items; ++i)
  {
    const auto item = buf[i];
    const auto raw = item ^ (((item >> 63) - 1) | (uint64_t(1) << 63));
    bfrom[offs_2[item >> 5 * digit_bits]++] = raw;
  }

  delete[] buf;
}


// От старших к младшим: вложенные разряды (рекурсивная функция).
void msd_radix_sort_inner(uint64_t *from, uint64_t *to, uint64_t *buf, unsigned shift)
{
  // 11-bit digits: 64/11 = 6 digits.
  static const unsigned digit_bits = 11, buckets = 1 << digit_bits;
  static const uint64_t mask = buckets - 1;

  using I = size_t;
  const I items = to - from;
  if (items < 2 * buckets)
  {
    sort(from, to);
    // Negative-positive inverse transform.
    const auto dest = shift / digit_bits % 2 ? from : buf;
    for (I i = 0; i < items; ++i)
    {
      const auto item = from[i];
      const auto raw = item ^ (((item >> 63) - 1) | (uint64_t(1) << 63));
      dest[i] = raw;
    }

    return;
  }

  // Compute offsets.
  I offs[buckets]{};
  for (I i = 0; i < items; ++i)
    offs[(from[i] >> shift) & mask]++;

  // Prepare positions.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Scatter.
  if (shift != 0)
  {
    for (I i = 0; i < items; ++i)
    {
      const auto item = from[i];
      buf[offs[(item >> shift) & mask]++] = item;
    }

    // Sort blocks.
    shift -= digit_bits;
    for (I i = 0, prev = 0; i < buckets; ++i)
    {
      msd_radix_sort_inner(buf + prev, buf + offs[i], from + prev, shift);
      prev = offs[i];
    }
  }
  else
  {
    for (I i = 0; i < items; ++i)
    {
      const auto item = from[i];
      const auto raw = item ^ (((item >> 63) - 1) | (uint64_t(1) << 63));
      buf[offs[item & mask]++] = raw;
    }
  }
}


// От старших к младшим: внешняя функция.
void msd_radix_sort(double *from, double *to)
{
  static_assert(sizeof(double) == sizeof(uint64_t));
  // 11-bit digits: 64/11 = 6 digits.
  static const unsigned
    digit_bits = 11,
    shift = 55,
    buckets = 1 << (sizeof(double) * CHAR_BIT - shift); // 1 << 9

  using I = size_t;
  const I items = to - from;
  if (items < buckets)
    return sort(from, to);

  const auto bfrom = (uint64_t*)from;
  const auto buf = new uint64_t[items];

  // Offsets.
  I offs[buckets]{};

  // Run #0: negatives-positives transform, compute offs.
  // Negatives-positives problem: inverse negatives, flip sign bit of positives.
  for (I i = 0; i < items; ++i)
  {
    const auto raw = bfrom[i];
    const auto item = raw ^ ((0 - (raw >> 63)) | (uint64_t(1) << 63));
    bfrom[i] = item;
    offs[item >> shift]++;
  }

  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Run #1: first scatter.
  for (I i = 0; i < items; ++i)
  {
    const auto item = bfrom[i];
    buf[offs[item >> shift]++] = item;
  }

  // Sort recursively.
  for (I i = 0, prev = 0; i < buckets; ++i)
  {
    msd_radix_sort_inner(buf + prev, buf + offs[i], bfrom + prev, shift - digit_bits);
    prev = offs[i];
  }

  delete[] buf;
}


////////////////////////////////////////////////////////////////////////////////
// Testing.

void std_stable_sort(double *a, double *ae)
{
  stable_sort(a, ae);
}


#include <ctime>
#include <cmath>
#include <iostream>
#include <random>
#include <iterator>
#include <vector>

double elapsed(clock_t t)
{
  return double(clock() - t) / CLOCKS_PER_SEC;
}


void do_tests(const vector<double> &master)
{
  const auto mm = minmax_element(begin(master), end(master));
  cout << "items: " << master.size()*1e-6 << "M\t";
  cout << "min: " << *mm.first << "\tmax: " << *mm.second << '\n';

  auto sorted = master;
  auto t = clock();
  sort(begin(sorted), end(sorted));
  double std_time = elapsed(t);
  cout << "\nstd::sort\t" << std_time << "s\n\n";

  struct {
    void(*sort_alg)(double*, double*);
    const char *name;
  } algs[]
  {
    { std_stable_sort,     "std stable" },
    { nonrec_bucket_sort,  "bucket    " },
    { rec_bucket_sort,     "rbucket   " },
    { rec_bucket_sort2,    "rbucket2  " },
    { lsd_radix_sort,      "lsd radix " },
    { lsd_radix_sort2,     "lsd radix2" },
    { msd_radix_sort,      "msd radix " },
  };

  unsigned lines = 0;
  for (auto &task : algs)
  {
    auto test = master;
    t = clock();
    task.sort_alg(test.data(), test.data() + test.size());
    double this_time = elapsed(t);
    cout << task.name << '\t' << this_time << "s,\t";
    cout << std_time / this_time << '\t';
    cout << (test == sorted) << endl;

    if (++lines == 4)
    {
      lines = 0;
      cout << '\n';
    }
  }
}


int main()
{
  cout.precision(3);
  mt19937_64 rng(14456);
  vector<double> master(1 << 24);

  cout << "Normal(0, 10) + x2\n";
  {
    normal_distribution<double> vg(0., 10.);

    double x = -10., dx = 20. / master.size();
    for (auto &val : master)
    {
      val = vg(rng) + x * x;
      x += dx;
    }
  }

  do_tests(master);
  cout << endl;

  cout << "------------------------\n"
    "Uniform in [-1e+6, 1e+6]\n";
  {
    uniform_real_distribution<double> vg(-1e+6, 1e+6);
    for (auto &val : master)
      val = vg(rng);
  }

  do_tests(master);
  cout << endl;
  return 0;
}


2220-csorts_s8.cpp

// csorts_s8.cpp
// Поразрядная сортировка 7-битных (ANSI) си-строк.
// Лексикографический порядок, индуцированный порядком кодов символов.
// (Впрочем, поведение strcmp, используемого для "досортировки" малых
//  блоков, не так просто и зависит от настройки локализации.)
#include <cstddef>
#include <cstring>
#include <algorithm>

// Стандартная сортировка.
void std_sort(const char **from, const char **to)
{
  std::sort(from, to,
    [](const char *a, const char *b)
    { return std::strcmp(a, b) < 0; });
}

// Стандартная устойчивая сортировка.
void std_stable_sort(const char **from, const char **to)
{
  std::stable_sort(from, to,
    [](const char *a, const char *b)
    { return std::strcmp(a, b) < 0; });
}


// Внешняя функция.
void ansi_msd_radix_sort(const char **from, const char **to)
{
  // Объявление внутренней (рекурсивной) функции сортировки.
  void ansi_msd_radix_sort_inner
    (const char **from, const char **to, const char **buf, int depth);

  // Маленькие наборы сортируем стандартной сортировкой.
  const auto items = to - from;
  if (items < 256)
    return std_sort(from, to);

  // Создать буфер и отсортировать рекурсивно.
  const auto buf = new const char*[items];
  ansi_msd_radix_sort_inner(from, to, buf, 0);
  delete[] buf;
}


// Внутренняя функция.
void ansi_msd_radix_sort_inner
  (const char **from, const char **to, const char **buf, int depth)
{
  static const int max_depth = 10;
  static const unsigned buckets = 128;
  using I = std::size_t;

  const I items = to - from;
  // Если блок маленький, или достигнута макс. глубина
  // -- "досортировать" стандартной функцией "хвосты" и закончить.
  if (items < buckets || depth == max_depth)
  {
    std_sort(from, to);

    // Скопировать элементы "обратно", поправив указатели.
    const auto dest = depth % 2? buf: from;
    for (I i = 0; i < items; ++i)
      dest[i] = from[i] - depth;

    return;
  }

  // Вычислить размеры блоков.
  I offs[buckets]{};
  for (I i = 0; i < items; ++i)
  {
    const unsigned char bucket = *from[i];
    offs[bucket]++;
  }

  // Вычислить стартовые позиции.
  for (I i = 0, s = 0; i < buckets; ++i)
  {
    const auto t = offs[i] + s;
    offs[i] = s;
    s = t;
  }

  // Разбросать по блокам в буфер и
  // сдвинуть указатели на непустые строки на один символ вперёд.
  for (I i = 0; i < items; ++i)
  {
    const auto item = from[i];
    const unsigned char bucket = *item;
    buf[offs[bucket]++] = item + (bucket != 0);
  }

  // Вернуть указатели на "пустые" строки в исходный массив и на исходные позиции.
  {
    const auto dest = depth % 2? buf: from;
    for (I i = 0, sz = offs[0]; i < sz; ++i)
      dest[i] = buf[i] - depth;
  }

  // Обработать рекурсивно непустые строки.
  ++depth;
  for (I i = 1; i < buckets; ++i)
    ansi_msd_radix_sort_inner
      (buf + offs[i-1], buf + offs[i], from + offs[i-1], depth);
}


////////////////////////////////////////////////////////////////////////////////
// Тестирование.
#include <ctime>
#include <vector>
#include <string>
#include <random>
#include <iostream>
using namespace std;


double elapsed(clock_t t)
{
  return double(clock() - t) / CLOCKS_PER_SEC;
}


vector<string> random_string_vector(size_t sz)
{
  // Инициализация генератора псевдослучайных чисел.
  mt19937_64 rng(19'08'1991);
  uniform_int_distribution<char> cg('\x20', '\x7e'); // неестественное распределение символов (равномерное).
  binomial_distribution<unsigned> lg(40, 0.5); // распределение длин строк.

  // Результирующий вектор строк.
  vector<string> strings(sz);
  for (auto &s: strings)
  {
    // Размер строки.
    s.resize(min(150u, lg(rng)));
    // Коды символов строки.
    for (auto &ch: s)
      ch = cg(rng);
  }

  return strings;
}


int main()
{
  cout.precision(3);
  cout << "An example of random strings being sorted:\n\n";
  for (auto &s: random_string_vector(12))
    cout << s << '\n';
  cout << endl;

  const auto storage = random_string_vector(1 << 22);
  vector<const char*> master(storage.size());
  for (size_t i = 0, sz = storage.size(); i < sz; ++i)
    master[i] = storage[i].c_str();

  auto sorted = master;
  auto t = clock();
  std_sort(sorted.data(), sorted.data() + sorted.size());
  double std_time = elapsed(t);
  cout << "\nstd::sort\t" << std_time << "s\n\n";

  struct {
    void (*sort_alg)(const char**, const char**);
    const char *name;
  } algs[]
  {
    { std_stable_sort,     "std stable" },
    { ansi_msd_radix_sort, "msd radix " },
  };

  unsigned lines = 0;
  for (auto &task : algs)
  {
    auto test = master;
    t = clock();
    task.sort_alg(test.data(), test.data() + test.size());
    double this_time = elapsed(t);
    cout << task.name << '\t' << this_time << "s,\t";
    cout << std_time / this_time << '\t';
    cout << (test == sorted) << endl;

    if (++lines == 4)
    {
      lines = 0;
      cout << '\n';
    }
  }

  return 0;
}


2250-subshashs.cpp

// subshashs.cpp
// substrings hashing search
#include <cstdint>
#include <cstring>
using namespace std;

/// Вычислить хэш префикса s.
uint32_t prefix_hash(const char *s)
{
  // Расширение нулями.
  char bytes[sizeof(uint32_t)]{};
  strncpy(bytes, s, sizeof(uint32_t));

  // Интерпретировать как число.
  uint32_t result;
  memcpy(&result, bytes, sizeof(uint32_t));
  return result;
}

/// Выбрать возможный префикс из набора по хэшу.
/// Возвращает успех выборки.
bool select_prefix(uint32_t hash, const char **prefix)
{
  static const char *words[]
  {
    "delta",
    "alpha",
    "beta",
    "gamma",
  };

  static const uint32_t hashes[]
  {
    0x746c'6564,
    0x6870'6c61,
    0x6174'6562,
    0x6d6d'6167,
  };

  if (hashes[hash % 4] != hash)
    return false;

  *prefix = words[hash % 4];
  return true;
}


/// Найти все вхождения искомых строк (alpha, beta, gamma, delta).
void find_all_matches(const char *text,
  void (*report_match)(const char *prefix, size_t pos))
{
  const char *prefix = nullptr;
  for (size_t pos = 0; *text != '\0'; ++text, ++pos)
  {
    if (select_prefix(prefix_hash(text), &prefix)
        && strncmp(text, prefix, strlen(prefix)) == 0)
      report_match(prefix, pos);
  }
}


////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <iomanip>

const char *text = "There are some alphas and betas or gammas but probably no deltas, bita is not a proper spelling.\n";
int main()
{
  cout << "Text:\n" << text;
  find_all_matches(text,
    [](const char *prefix, size_t pos)
    {
      cout << "\nfound: " << prefix << " at " << pos << '\n' << (text + pos);
    });

  return 0;
}


2260-rabinkarp.cpp

// rabinkarp.cpp
// Rabin-Karp algorithm
// алгоритм поиска подстроки Рабина-Карпа
// кольцевой хэш -- аналог вычисления значения числа, записанного в системе счисления с базой RK_HASH_RADIX
#include <cstring> // memcmp
using namespace std;

/// Стандарт, увы, не определяет целочисленных вариантов pow.
size_t ipow(size_t x, size_t y)
{
  size_t p = 1;
  while (y != 0)
  {
    p *= y & 1? x: 1;
    x *= x;
    y >>= 1;
  }

  return p;
}


/// Основание системы счисления для rk_hash.
const size_t RK_HASH_RADIX = 39989; // Простое число.

/// Вычислить хэш строки.
size_t rk_hash(const char *text, size_t text_size)
{
  size_t s = 0; // сумма
  while (text_size--)
  {
    s *= RK_HASH_RADIX;
    s += (unsigned char)*text++;
  }
  return s;
}

/// Выполнить поиск подстроки алгоритмом Рабина-Карпа.
size_t rk_search(const char *where, size_t where_len,
                 const char *what,  size_t what_len)
{
  if (where_len < what_len)
    return where_len;
  if (what_len == 0)
    return 0;

  // Вычислить хэш what.
  const auto
    what_hash = rk_hash(what, what_len),
    radix_pow = ipow(RK_HASH_RADIX, what_len - 1);

  // Подготовить хэш окна поиска, обработав префикс where.
  auto window_hash = rk_hash(where, what_len);

  // Идти по строке, сдвигая окно и проверяя хэш.
  // i и j -- индексы крайних символов окна.
  for (size_t i = what_len, j = 0;; ++i, ++j)
  {
    if (what_hash == window_hash
     && memcmp(where + j, what, what_len) == 0)
      return j;

    if (i == where_len)
      return where_len;

    // сдвинуть окно и поправить хэш
    const unsigned char
      old_byte = where[j],
      new_byte = where[i];

    ((window_hash -= old_byte * radix_pow)
                  *= RK_HASH_RADIX)
                  += new_byte;
  }
}


////////////////////////////////////////////////////////////////////////////////
// Тестирование.
#include <iostream>
#include <string>

int main()
{
  string line, sub;
  while (true)
  {
    getline(cin, line);
    getline(cin, sub);
    if (!cin)
      return 0;

    for (size_t p = 0; p < line.size(); p += sub.size())
    {
      p += rk_search(line.c_str() + p, line.size() - p, sub.c_str(), sub.size());
      if (p < line.size())
        cout << p << '\n';
    }
  }
}


2270-rabinkarp2.cpp

// rabinkarp2.cpp
// Поиск всех вхождений заданного набора подстрок в тексте.
// Организует простейшую хэш-таблицу.
#include <cstring> // memcmp, strlen
#include <cassert>
using namespace std;

/// Стандарт, увы, не определяет целочисленных вариантов pow.
size_t ipow(size_t x, size_t y)
{
  size_t p = 1;
  while (y != 0)
  {
    p *= y & 1? x: 1;
    x *= x;
    y >>= 1;
  }

  return p;
}


/// Основание системы счисления для rk_hash.
const size_t RK_HASH_RADIX = 39989; // Простое число.

/// Вычислить хэш строки.
size_t rk_hash(const char *text, size_t text_size)
{
  size_t s = 0; // сумма
  while (text_size--)
  {
    s *= RK_HASH_RADIX;
    s += (unsigned char)*text++;
  }
  return s;
}


/// Набор искомых строк с дополнительной информацией.
struct RK_subs
{
  /// Записи, соответствующие искомым подстрокам.
  struct Entry
  {
    const char *value;
    size_t len;
    size_t hash;
    size_t radix_pow;
    size_t window_hash;
  };

  Entry *entries = nullptr;
  size_t size = 0, active = 0;

  /// Инициализировать записи.
  RK_subs(const char *subs[], size_t subs_size)
    : entries(new Entry[subs_size]),
      size(subs_size),
      active(subs_size)
  {
    for (size_t i = 0; i < subs_size; ++i)
    {
      const auto len = strlen(subs[i]);
      entries[i] = Entry{ subs[i], len, rk_hash(subs[i], len) };
    }
  }

  ~RK_subs() { delete[] entries; }

  /// Инициализировать окна.
  void init_windows(const char *text, size_t text_len)
  {
    for (size_t i = 0; i < size; ++i)
    {
      auto &e = entries[i];
      if (text_len < e.len)
      {
        e.value = nullptr; // "деактивировать" запись.
        --active;
      }
      else
      {
        e.window_hash = rk_hash(text, e.len); // неэффективно...
        e.radix_pow = ipow(RK_HASH_RADIX, e.len-1);
      }
    }
  }

  /// Поправить окна: шаг вперёд на один символ.
  void shift_windows(const char *text, size_t text_len, size_t old_pos)
  {
    for (size_t i = 0; i < size; ++i)
    {
      auto &e = entries[i];
      if (e.value) // активна?
      {
        if (text_len <= old_pos + e.len)
        {
          e.value = nullptr; // деактивировать
          --active;
        }
        else
        {
          const unsigned char
            old_byte = text[old_pos],
            new_byte = text[old_pos + e.len];

          ((e.window_hash -= old_byte * e.radix_pow)
                          *= RK_HASH_RADIX)
                          += new_byte;
        }
      }
    }
  }

  /// Найти все совпадения.
  void find_matches(const char *text, size_t cur_pos,
    void (*report_match)(const char*, size_t)) const
  {
    const auto start = text + cur_pos;
    for (size_t i = 0; i < size; ++i)
    {
      const auto &e = entries[i];
      if (e.value && e.hash == e.window_hash
       && memcmp(start, e.value, e.len) == 0)
         report_match(e.value, cur_pos);
    }
  }
};


/// Выполнить поиск всех подстрок алгоритмом Рабина-Карпа.
void rk_search_all
  (const char *text,
   const char *what[], size_t what_size,
   void (*report_match)(const char*, size_t))
{
  const auto text_len = strlen(text);
  if (text_len == 0 || what_size == 0)
    return;

  // Создать окна поиска.
  RK_subs searcher(what, what_size);
  searcher.init_windows(text, text_len);

  // Искать, пока есть "помещающиеся" окна.
  for (size_t pos = 0; searcher.active != 0 && pos != text_len; ++pos)
  {
    searcher.find_matches(text, pos, report_match);
    searcher.shift_windows(text, text_len, pos);
  }
}



////////////////////////////////////////////////////////////////////////////////
// Тестирование.
#include <iostream>
#include <string>

const char *text = "alpha bet beta to send gamma into delta, that's all about alpha and beta";
int main()
{
  const char *subs[]
  {
    "alpha",
    "beta",
    "gamma",
    "delta",
  };

  rk_search_all(text, subs, 4,
    [](const char *sub, size_t pos)
    {
      cout << "found: " << sub << " at " << pos << '\n';
      cout << (text + pos) << '\n';
    });

  return 0;
}


2280-lcs.cpp

// lcs.cpp
// longest common substring
// самя длинная общая подстрока
#include <cassert>
#include <cstring>
#include <string>
#include <algorithm>
#include <utility>
using namespace std;

/// Решение: позиции начала в первой и второй строках и длина.
struct Lcs_solution
{
  size_t pos1, pos2, len;
};

inline bool operator==(const Lcs_solution &a, const Lcs_solution &b)
{
  return a.pos1 == b.pos1 && a.pos2 == b.pos2 && a.len == b.len;
}

inline bool operator!=(const Lcs_solution &a, const Lcs_solution &b)
{
  return !(a == b);
}


/// Наивный переборный алгоритм: O(N3)-время, O(1)-память.
/// (На самом деле O(asz*bsz + sum(match(i, j), 0<=i<asz, 0<=j<bsz)),
///  что на практике близко O(N2).)
Lcs_solution lcs_naive(const string &a, const string &b)
{
  Lcs_solution result {};

  const size_t asz = a.size(), bsz = b.size();
  for (size_t i = 0; i < asz; ++i)
  {
    for (size_t j = 0; j < bsz; ++j)
    {
      const size_t max_match = min(asz - i, bsz - j);
      if (max_match <= result.len)
        break;

      size_t match = 0;
      while (match < max_match && a[i + match] == b[j + match])
        ++match;

      if (result.len < match)
        result = Lcs_solution { i, j, match };
    }

    if (asz - i <= result.len)
      break;
  }

  return result;
}


/// Переборный алгоритм + мемоизация: O(N2)-время, O(N2)-память.
/// "Словарь".
struct Lcs_memo
{
  const string &a, &b;
  size_t **m, asz, bsz;

  // Конструктор объектов Memo.
  Lcs_memo(const string &a, const string &b)
    : a(a), b(b), asz(a.size()), bsz(b.size())
  {
    // Создать таблицу в динамической памяти.
    assert(asz && bsz && asz < SIZE_MAX/bsz/sizeof(size_t));
    m = new size_t*[asz];
    m[0] = new size_t[asz * bsz];
    memset(m[0], -1, asz * bsz * sizeof(size_t)); // -1 == "not stored"
    for (size_t j = 1; j < asz; ++j)
      m[j] = m[j-1] + bsz;
  }

  // Деструктор объектов Memo. Освобождает память.
  ~Lcs_memo() { delete[] m[0]; delete[] m; }

  // Проверить, вычислена ли ячейка таблицы с индексами i, j.
  bool stored(size_t i, size_t j) const
  {
    return m[i][j] != size_t(-1);
  }

  // Вычислить совпадение, начиная с индексов i, j.
  size_t match(size_t i, size_t j)
  {
    if (i == asz || j == bsz)
      return 0;

    if (stored(i, j))
      return m[i][j];

    return m[i][j] = a[i] != b[j]? 0: match(i+1, j+1) + 1;
  }
};

Lcs_solution lcs_memo(const string &a, const string &b)
{
  Lcs_solution result {};
  Lcs_memo memo(a, b);

  const size_t asz = a.size(), bsz = b.size();
  for (size_t i = 0; i < asz; ++i)
  {
    for (size_t j = 0; j < bsz; ++j)
    {
      const size_t match = memo.match(i, j);
      if (result.len < match)
        result = Lcs_solution { i, j, match };
    }

    if (asz - i <= result.len)
      break;
  }

  return result;
}


/// Мемоизация с запоминанием только двух строк таблицы: O(N2)-время, O(N)-память.
Lcs_solution lcs_memo2(const string &a, const string &b)
{
  Lcs_solution result{};
  const size_t asz = a.size(), bsz = b.size();
  auto m_prev = new size_t[bsz + 1]{}; // в начале нули.
  auto m_next = new size_t[bsz + 1];
  m_next[bsz] = 0;

  // С конца к началу.
  for (size_t i = asz; i-- != 0;)
  {
    const auto ai = a[i];
    for (size_t j = bsz; j-- != 0;)
    {
      if (ai != b[j])
      {
        m_next[j] = 0;
      }
      else
      {
        const auto val = m_next[j] = m_prev[j + 1] + 1;
        if (result.len <= val)
          result = Lcs_solution{ i, j, val };
      }
    }

    swap(m_next, m_prev);
  }

  delete[] m_next;
  delete[] m_prev;
  return result;
}


/// Мемоизация с запоминанием только двух строк таблицы: O(N2)-время, O(N)-память.
Lcs_solution lcs_memo3(const string &a, const string &b)
{
  Lcs_solution result {};
  const size_t asz = a.size(), bsz = b.size();
  auto m_prev = new size_t[bsz+1]{}; // в начале нули.
  auto m_next = new size_t[bsz+1];
  m_next[bsz] = 0;

  // С конца к началу.
  for (size_t i = asz; i-- != 0;)
  {
    const auto ai = a[i];
    for (size_t j = 0; j < bsz; ++j)
    {
      const auto nz = m_prev[j+1] + 1;
      m_next[j] = ai != b[j]? 0: nz;
    }

    const auto max_match = max_element(m_next, m_next + bsz);
    if (result.len <= *max_match)
      result = Lcs_solution{ i, size_t(max_match - m_next), *max_match };

    swap(m_next, m_prev);
  }

  delete[] m_next;
  delete[] m_prev;
  return result;
}


////////////////////////////////////////////////////////////////////////////////
// Максимальная общая подпоследовательность.

/// Мемоизация с запоминанием только двух строк таблицы: O(N3)-время, O(N2)-память.
/// Из-за копирования строк получается O(N3)-время, можно улучшить до O(N2)-времени.
string longest_common_subsequence(const string &a, const string &b)
{
  const size_t asz = a.size(), bsz = b.size();
  auto m_prev = new string[bsz + 1]; // Строки будут изначально пустые,
  auto m_next = new string[bsz + 1]; // благодаря тому, что для string определён "конструктор по умолчанию".
  string result;

  // С конца к началу.
  for (size_t i = asz; i-- != 0;)
  {
    const auto ai = a[i];
    for (size_t j = bsz; j-- != 0;)
    {
      if (ai != b[j])
        m_next[j] = m_next[j+1].size() < m_prev[j].size()?
          m_prev[j]: m_next[j+1];
      else
        m_next[j] = ai + m_prev[j + 1];
    }

    const auto &candidate = *max_element(m_next, m_next+bsz,
      [](const string &a, const string &b) // Найти самую длинную строку.
      { return a.size() < b.size(); });
    if (result.size() < candidate.size())
      result = candidate;

    swap(m_next, m_prev);
  }

  delete[] m_next;
  delete[] m_prev;
  return result;
}


/// Мемоизация с запоминанием только двух строк таблицы: O(N3)-время, O(N2)-память.
/// Из-за копирования строк получается O(N3)-время, можно улучшить до O(N2)-времени.
/// Другой порядок: с начала в конец.
string longest_common_subsequence2(const string &a, const string &b)
{
  if (a.size() < b.size())
    return longest_common_subsequence2(b, a);
    
  const size_t asz = a.size(), bsz = b.size();
  const auto storage = new string[2*bsz + 2];
  auto m_prev = storage + 1, m_next = m_prev + bsz + 1;
  
  string result;

  for (size_t i = 0; i < asz; ++i)
  {
    const auto ai = a[i];
    for (size_t j = 0; j < bsz; ++j)
    {
      if (ai != b[j])
        m_next[j] = m_next[j-1].size() < m_prev[j].size()?
          m_prev[j]: m_next[j-1];
      else
        m_next[j] = m_prev[j-1] + ai;
    }

    const auto &candidate = *max_element(m_next, m_next+bsz,
      [](const string &a, const string &b) // Найти самую длинную строку.
      { return a.size() < b.size(); });
    if (result.size() < candidate.size())
      result = candidate;

    swap(m_next, m_prev);
  }

  delete[] storage;
  return result;
}


////////////////////////////////////////////////////////////////////////////////
// Тестирование.
#include <iostream>
#include <random>
#include <chrono>


/// Генерирует псевдослучайную строку.
string random_string(size_t len, unsigned seed = 421129)
{
  string result(len, '\0');
  mt19937_64 rng(seed);
  for (auto &ch: result)
  {
    unsigned code = rng() % 32;
    ch = code < 26? 'a'+code: ' '; // латинские буквы или пробелы.
  }
  return result;
}

/// Перемешивает строки (чтобы появилось много совпадений).
void crosscopy(string &a, string &b, unsigned seed = 2017)
{
  mt19937_64 rng(seed);
  const auto sz = min(a.size(), b.size());
  bool first = false;
  for (size_t i = 0, j = 0; i < sz; ++i)
  {
    if (j == 0)
    {
      j = (rng() % sz) / 16;
      first = rng() % 2 == 0;
    }
    else
    {
      (first? a: b)[i] = (first? b: a)[i];
      --j;
    }
  }

  rotate(a.begin(), a.begin() + (rng() % sz), a.end());
}


/// Тестирование на заранее заданных строках.
int test_lcs(Lcs_solution (*lcs)(const string&, const string&))
{
  if (lcs("", "") != Lcs_solution{})
    return 1;
  if (lcs("", "abc") != Lcs_solution{})
    return 2;
  if (lcs("abc", "") != Lcs_solution{})
    return 3;
  if (lcs("abcdef", "ghik") != Lcs_solution{})
    return 4;
  if (lcs("abcd", "efga") != Lcs_solution{0, 3, 1})
    return 5;
  if (lcs("abra cadabra", "mabra fabra") != Lcs_solution{0, 1, 5})
    return 6;
  if (lcs("this is a description of another situation",
          "that situation has been described already") != Lcs_solution{32, 4, 10})
    return 7;

  return 0;
}


/// Тестирование производительности поиска общей подстроки.
void time_test(Lcs_solution (*lcs)(const string&, const string&), size_t len = 1 << 14)
{
  using namespace chrono;
  auto
    a = random_string(len, 2011),
    b = random_string(len, 2017);
  crosscopy(a, b);

  const auto t0 = high_resolution_clock::now();
  const auto p = lcs(a, b);
  const auto t1 = high_resolution_clock::now();
  cout << (int)duration<double, std::milli>(t1 - t0).count() << " ms  "
       << p.pos1 << ' ' << p.pos2 << ' ' << p.len << endl;
}


int main()
{
  cout << test_lcs(lcs_naive);
  cout << test_lcs(lcs_memo);
  cout << test_lcs(lcs_memo2);
  cout << test_lcs(lcs_memo3) << '\n';

  cout << "LCsubsequence: ";
  cout << longest_common_subsequence("abcd bcd ab", "dbca cda") << '\n';
  cout << longest_common_subsequence("this is a description of another situation",
          "that situation has been described already") << '\n';
  cout << longest_common_subsequence2("this is a description of another situation",
          "that situation has been described already") << '\n';

  cout << "\nRandom string: " << random_string(20) << '\n';

  size_t len = 4'000;
  cout << "\n2 x " << len << " characters:";

  cout << "\nNaive: ";
  time_test(lcs_naive, len);

  cout << "Memo:  ";
  time_test(lcs_memo, len);

  cout << "Memo2: ";
  time_test(lcs_memo2, len);

  cout << "Memo3: ";
  time_test(lcs_memo3, len);

  len = 20'000;
  cout << "\n2 x " << len << " characters:";

  cout << "\nNaive: ";
  time_test(lcs_naive, len);

  cout << "Memo2: ";
  time_test(lcs_memo2, len);

  cout << "Memo3: ";
  time_test(lcs_memo3, len);

  cout << endl;
  return 0;
}


2300-vt_println.cpp

// vt_println.cpp
#include <iostream>

inline void print() {} // Нечего печатать.

template <class Ch, class Tr>
void print(std::basic_ostream<Ch, Tr>&) {} // Нечего печатать.

template <class Ch, class Tr, class First, class... Other>
void print(std::basic_ostream<Ch, Tr> &os,
  const First &first, const Other&... other)
{
  os << first;
  print(os, other...);
}

template <class... Other>
void print(const Other&... other)
{
  print(std::cout, other...);
}

template <class Ch, class Tr, class... Other>
void println(std::basic_ostream<Ch, Tr> &os, const Other&... other)
{
  print(os, other..., '\n');
}

template <class... Args>
void println(const Args&... args)
{
  println(std::cout, args...);
}


int main()
{
  println(42, " is the new ", 23);
  println(3.1415926535897932);
  return 0;
}


2310-vt_format.cpp

// vt_format.cpp
#include <string>
#include <cstddef>
#include <cctype>

namespace impl
{
  template <class T>
  std::string to_str(const T &val)
  {
    using std::to_string;
    return to_string(val);
  }

  inline std::string to_str(const char *p)
  {
    return p;
  }

  inline const std::string& to_str(const std::string &s)
  {
    return s;
  }
}


template <class... Params>
std::string format(const std::string &model, const Params&... params)
{
  using namespace std;
  // Конвертировать params в массив строк.
  string param[] { "", impl::to_str(params)... };

  // Подготовить место для результата.
  size_t result_possible_size = model.size();
  for (auto &p: param)
    result_possible_size += p.size();

  string result;
  result.reserve(result_possible_size);

  // Выполнить подстановку (конечный автомат).
  size_t param_index;
  auto p = model.c_str();
  auto stored_p = p;

outer:
  while (auto ch = *p++)
  {
    if (ch == '%')
      goto percent;
    result += ch;
  }

  return result;

percent:
  stored_p = p;
  switch (auto ch = *p++)
  {
  case '\0':
    result += '%';
    return result;

  case '%':
    result += '%';
    goto outer;

  default:
    if (!isdigit(ch))
    {
      (result += '%') += ch;
      goto outer;
    }

    param_index = ch - '0';
  }

  while (auto ch = *p++)
  {
    if (ch == '%' && param_index <= sizeof...(params))
    {
      result += param[param_index];
      goto outer;
    }

    if (!isdigit(ch))
      break;

    param_index = param_index * 10 + (ch - '0');
  }

  result += '%';
  p = stored_p;
  goto outer;
}


#include <iostream>
int main()
{
  std::cout << format(
      "%2% %3% of %1%\n"
      "%20%\n"
      "%2broken%\n"
      "%2%% of %1%, percent-sign: %%\n"
      "not closed %\n",
    "acid",
    37.5,
    "ounces",
    "not used?");
  return 0;
}


2315-vt_format_regex.cpp

// vt_format_regex.cpp
#include <string>
#include <cstddef>
#include <regex>

namespace impl
{
  template <class T>
  std::string to_str(const T &val)
  {
    using std::to_string;
    return to_string(val);
  }

  inline std::string to_str(const char *p)
  {
    return p;
  }

  inline const std::string& to_str(const std::string &s)
  {
    return s;
  }
}

template <class... Params>
std::string format(std::string model, const Params&... params)
{
  using namespace std;
  // Конвертировать params в массив строк.
  string param[] { "", impl::to_str(params)... };

  // Выполнить подстановку (конечный автомат через std::regex).
  string result;

  regex preg("%(\\d*)%");
  smatch match;
  size_t i;
  while (regex_search(model, match, preg))
  {
    result += match.prefix();
    if (match.size() == 1 || match[1].length() == 0)
      result += '%';
    else if ((i = stoi(match[1])) <= sizeof...(params))
      result += param[i];
    else
      result += match[0];

    model = match.suffix();
  }

  result += match.suffix();
  return result;
}


#include <iostream>
int main()
{
  std::cout << format(
      "%2% %3% of %1%\n"
      "%20%\n"
      "%2broken%\n"
      "%2%% of %1%, percent-sign: %%\n"
      "not closed %\n",
    "acid",
    37.5,
    "ounces",
    "not used?");
  return 0;
}


2320-vt_type_list.cpp

// vt_type_list.cpp
#include <type_traits>
#include <cstddef>

template <class... Types>
struct Type_list
{
  static constexpr auto size = sizeof...(Types);
};


#define APPLY(meta, ...) \
  typename meta::template apply<__VA_ARGS__>::type


struct T_prepend
{
  template <class A, class B>
  struct apply {};

  template <class A, class... Other>
  struct apply<A, Type_list<Other...>>
  {
    using type = Type_list<A, Other...>;
  };
};


struct T_map
{
  template <class Map, class List>
  struct apply { using type = List; };

  template <class Map, class T, class... Tail>
  struct apply<Map, Type_list<T, Tail...>>
  {
    using type =
    APPLY(T_prepend, \
      APPLY(Map, T), \
      APPLY(T_map, Map, Type_list<Tail...>));
  };
};


struct T_fold
{
  template <class Fold, class List> struct apply {};

  template <class Fold, class T>
  struct apply<Fold, Type_list<T>>
  {
    using type = T;
  };

  template <class Fold, class T, class... Tail>
  struct apply<Fold, Type_list<T, Tail...>>
  {
    using type = APPLY(Fold, T, APPLY(T_fold, Fold, Type_list<Tail...>));
  };
};


struct T_sizeof
{
  template <class T>
  struct apply { using type = std::integral_constant<size_t, sizeof(T)>; };
};


struct T_add
{
  template <class A, class B>
  struct apply
  {
    using type = std::integral_constant<
      std::common_type_t<typename A::value_type, typename B::value_type>,
      A::value + B::value>;
  };
};

struct T_max
{
  template <class A, class B>
  struct apply
  {
    using type = std::integral_constant<
      std::common_type_t<typename A::value_type, typename B::value_type>,
      (A::value < B::value? B::value: A::value)>;
  };
};


#include <iostream>

template <class BinOp, class Types>
using F = APPLY(T_fold, \
  BinOp, \
  APPLY(T_map, T_sizeof, Types));

int main()
{
  using Ts = Type_list<int, char, bool, char>;
  std::cout
    << "Type list has " << Ts::size << " types in it\n"
    << "Type list max sizeof = "
    << F<T_max, Ts>{}
    << "\nType list sizeof sum = "
    << F<T_add, Ts>{} << '\n';

  return 0;
}


2330-vt_variant.cpp

// vt_variant.cpp
#include <algorithm>
#include <cstdint>
#include <typeinfo>

template <class... Types>
struct Type_list
{
  static constexpr auto size = sizeof...(Types);
};

template <class T, class L>
constexpr size_t t_count = 0;

template <class T, class... Tail>
constexpr size_t t_count<T, Type_list<T, Tail...>> = 1 + t_count<T, Type_list<Tail...>>;

template <class T, class X, class... Tail>
constexpr size_t t_count<T, Type_list<X, Tail...>> = t_count<T, Type_list<Tail...>>;


template <class T, class L>
constexpr size_t t_index = -1;

template <class T, class... Tail>
constexpr size_t t_index<T, Type_list<T, Tail...>> = 0;

template <class T, class X, class... Tail>
constexpr size_t t_index<T, Type_list<X, Tail...>> = 1 + t_index<T, Type_list<Tail...>>;


template <size_t i, class L>
struct t_at {};

template <size_t i, class T, class... Tail>
struct t_at<i, Type_list<T, Tail...>>
{
  using type =
    typename t_at<i - 1, Type_list<Tail...>>
    ::type;
};

template <class T, class... Tail>
struct t_at<0, Type_list<T, Tail...>>
{
  using type = T;
};

template <size_t i, class L>
using t_at_t = typename t_at<i, L>::type;


template <class L>
constexpr size_t max_sizeof = 0;

template <class T, class... Tail>
constexpr size_t max_sizeof<Type_list<T, Tail...>> =
  std::max(sizeof(T), max_sizeof<Type_list<Tail...>>);

template <class L>
constexpr size_t max_alignof = 1;

template <class T, class... Tail>
constexpr size_t max_alignof<Type_list<T, Tail...>> =
  std::max(alignof(T), max_alignof<Type_list<Tail...>>);


template <class L1, class L2>
constexpr bool t_subset = true; // for empty list L1.

template <class T, class... Types1, class... Types2>
constexpr bool t_subset<
    Type_list<T, Types1...>,
    Type_list<Types2...>> =
  t_count<T, Type_list<Types2...>> != 0 &&
  t_subset<Type_list<Types1...>, Type_list<Types2...>>;


constexpr size_t ti_prop1(size_t a)
{
  return a != size_t(-1)? a + 1: -1;
}

template <class... Args>
constexpr size_t t_first_constructible = -1;

template <class T, class... Types, class... Args>
constexpr size_t t_first_constructible<
    Type_list<T, Types...>, Args...> =
  std::is_constructible<T, Args...>::value? 0
  : ti_prop1(t_first_constructible<Type_list<Types...>, Args...>);


/// Вариантный тип.
template <class... Types>
class Variant
{
public:
  static_assert(sizeof...(Types) > 0,
    "Variant: at least one type needed.");
  static_assert(sizeof...(Types) < 64,
    "Variant: too many possible types.");
  using types = Type_list<Types...>;

private:
  using Storage = std::aligned_storage_t<
    max_sizeof<types>, max_alignof<types>>;

  Storage _stor;
  uint8_t _type = -1;


  template <class T>
  static void dtor(void *p)
  {
    static_cast<T*>(p)->~T();
  }

  static auto fetch_dtor(uint8_t t)
  {
    static void (*d[])(void*)
    { &dtor<Types>... }; // Destructor table.
    return d[t];
  }


  template <class T>
  static void copy_ctor(void *p, const void *val)
  {
    new (p) T(*static_cast<const T*>(val));
  }

  static auto fetch_copy_ctor(uint8_t t)
  {
    static void (*cc[])(void*, const void*)
    { &copy_ctor<Types>... }; // Copy constructor table.
    return cc[t];
  }


  template <class T, class Visitor>
  static void do_visit(Visitor &&visitor, void *val)
  {
    visitor(*static_cast<T*>(val));
  }

  template <class Visitor>
  static auto fetch_do_visit(uint8_t t)
  {
    using DVT = void(Visitor&&, void*);
    static DVT* const vt[]
    { &do_visit<Types, Visitor>... };
    return vt[t];
  }

public:
  /// Извлечь индекс хранимого типа.
  uint8_t get_type() const noexcept { return _type; }

  /// Получить адрес хранилища.
  const void* data() const noexcept { return &_stor; }

  /// Проверить, "пуст" ли вариант (содержит ли он какое-либо значение).
  bool empty() const noexcept { return _type == -1; }

  /// Очистить содержимое, вернув к состоянию "пусто".
  void clear()
  {
    if (!empty())
    {
      // Dispatch destructor call:
      fetch_dtor(_type)(&_stor);
      _type = -1;
    }
  }

  // Деструктор, вызывает clear().
  ~Variant() { clear(); }

  // Конструктор из произвольного набора аргументов.
  // Выбирает первый подходящий тип из Types (слева направо) для инициализации.
  template <class... Args>
  Variant(Args&&... args)
  {
    static constexpr size_t i = t_first_constructible<types, Args&&...>;
    static_assert(i != -1, "Variant constructor: invalid arguments.");
    new (static_cast<void*>(&_stor))
      t_at_t<i, types>(std::forward<Args>(args)...);
    _type = uint8_t(i);
  }

  // Конструктор по умолчанию, создаёт пустой объект варианта.
  Variant() {}

  // Копирующий конструктор.
  Variant(const Variant &other) { *this = other; }

  // Копирующий конструктор (вариант для не const-ссылки).
  Variant(Variant &other) { *this = other; }
  
  // Перемещающий конструктор.
  // (По сути, не реализован -- вызывает копирование.)
  Variant(Variant&& other) { *this = other; }

  // Копирующий оператор присваивания.
  Variant& operator=(const Variant &other)
  {
    if (this != &other)
    {
      clear();
      // Dispatch copy constructor call:
      fetch_copy_ctor(other._type)(&_stor, &other._stor);
      _type = other._type;
    }
    return *this;
  }

  // Присваивание варианта с более узким множеством возможных типов значений.
  template <class... Subset>
  std::enable_if_t<
    t_subset<Type_list<Subset...>, types>,
  Variant<Types...>&> operator=(const Variant<Subset...> &other)
  {
    clear();
    if (other.empty())
      return *this;

    // Таблица перевода индексов other._type в this->_type.
    static const uint8_t tt[]
    { t_index<Subset, types>... };

    fetch_copy_ctor(tt[other.get_type()])(&_stor, other.data());
    _type = tt[other.get_type()];
    return *this;
  }

  // Оператор присваивания произвольного значения.
  // Доступен только если тип T есть в Types.
  template <class T>
  std::enable_if_t<
    t_count<T, types> != 0,
  Variant<Types...>&> operator=(const T &val)
  {
    return *this = Variant<Types...>(val); // possibly suboptimal.
  }

  // Получить значение type_info для хранимого типа.
  const std::type_info& get_typeid() const
  {
    if (empty())
      return typeid(void);

    static const std::type_info* const ti[]
    { &typeid(Types)... };

    return *ti[_type];
  }

  // Обработать значение варианта с помощью паттерна "посетитель".
  template <class Visitor>
  void visit(Visitor &&visitor)
  {
    fetch_do_visit<Visitor>(_type)
      (std::forward<Visitor>(visitor), &_stor);
  }
};


#include <iostream>
#include <string>

using namespace std;

struct Twice
{
  template <class T>
  void operator()(T &x) const
  {
    x += x;
  }
} const twice;

struct To_cout
{
  template <class T>
  ostream& operator()(const T &x) const
  {
    return cout << x;
  }
} const to_cout;


int main()
{
  Variant<int, string> v = 3;
  v.visit(twice);
  v.visit(to_cout);
  cout << ' ' << v.get_typeid().name();
  cout << '\n';

  v = "da";
  v.visit(twice);
  v.visit(to_cout);
  cout << ' ' << v.get_typeid().name();
  cout << '\n';

  return 0;
}



Общее оглавление

Кувшинов Д.Р. © 2016