// To build, define one of the following macros to chose an algorithm to test:
//    REMOVE_CARRIAGE_RETURNS
//    BYTE_AT_A_TIME
//    REMOVE_IF
// 
// Time by defining the TIMING macro during compilation to enable self-timing.
// Alternatively using an external program like unix time.

#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <iostream>
#include <ratio>

static std::size_t RemoveCarriageReturns(char *buffer, std::size_t bytes) {
  std::size_t wrote{0};
  char *p{buffer};
  while (bytes > 0) {
    void *vp{static_cast<void *>(p)};
    void *crvp{std::memchr(vp, '\r', bytes)};
    char *crcp{static_cast<char *>(crvp)};
    if (!crcp) {
      std::memmove(buffer + wrote, p, bytes);
      wrote += bytes;
      break;
    }
    std::size_t chunk = crcp - p;
    std::memmove(buffer + wrote, p, chunk);
    wrote += chunk;
    p += chunk + 1;
    bytes -= chunk + 1;
  }
  return wrote;
}

static std::size_t ByteAtATime(char *buffer, std::size_t bytes) {
  char *q{buffer};
  for (char *p{buffer}; bytes-- > 0; ++p) {
    if (*p != '\r') {
      *q++ = *p;
    }
  }
  return q - buffer;
}

int main() {
  using namespace std::chrono;
  int n{1 << 20};
  int linelen{20};
  char *src{static_cast<char *>(std::malloc(n))};
  int left;
  int max_reps = 10000;
#ifdef TIMING
  double cumulative_time_span = 0.0;
#endif // TIMING
  for (int reps{0}; reps < max_reps; ++reps) {
    std::memset(src, 0, n);
    for (int j{0}; j < n; j += linelen) {
      src[j] = '\r';
    }
#ifdef TIMING
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
#endif // TIMING
#if REMOVE_CARRIAGE_RETURNS
    int left = RemoveCarriageReturns(src, n);
#elif BYTE_AT_A_TIME
    int left = ByteAtATime(src, n);
#elif REMOVE_IF
    int left = std::remove_if(src, src + n, [](char c){ return c == '\r'; }) - src;
#else
    #error "Plese define one of REMOVE_CARRIAGE_RETURNS, BYTE_AT_A_TIME or REMOVE_IF to select algorithm"
#endif
#ifdef TIMING
    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
    cumulative_time_span += time_span.count();
#endif // TIMING
    
    if (reps == 0) {
      std::cout << left << '\n';
    }
  }
#ifdef TIMING
  double avg_time_per_rep = cumulative_time_span / max_reps;
  std::cout << "Avg time for " << max_reps << " reps: " << avg_time_per_rep << std::endl;
#endif // TIMING
  return 0;
}
