
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <system_error>

bool many() {
  std::string const input = "234532.3426362,7869234.9823,324562.645";
  double result;
  auto answer =
      fast_float::from_chars(input.data(), input.data() + input.size(), result);
  if (answer.ec != std::errc()) {
    return false;
  }
  if (result != 234532.3426362) {
    return false;
  }
  if (answer.ptr[0] != ',') {
    return false;
  }
  answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
                                  result);
  if (answer.ec != std::errc()) {
    return false;
  }
  if (result != 7869234.9823) {
    return false;
  }
  if (answer.ptr[0] != ',') {
    return false;
  }
  answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(),
                                  result);
  if (answer.ec != std::errc()) {
    return false;
  }
  if (result != 324562.645) {
    return false;
  }
  return true;
}

void many_loop() {
  std::string const input = "234532.3426362,7869234.9823,324562.645";
  double result;
  char const *pointer = input.data();
  char const *end_pointer = input.data() + input.size();

  while (pointer < end_pointer) {
    auto answer = fast_float::from_chars(pointer, end_pointer, result);
    if (answer.ec != std::errc()) {
      std::cerr << "error while parsing" << std::endl;
      break;
    }
    std::cout << "parsed: " << result << std::endl;
    pointer = answer.ptr;
    if ((answer.ptr < end_pointer) && (*pointer == ',')) {
      pointer++;
    }
  }
}

#if FASTFLOAT_IS_CONSTEXPR
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) {
  double result;
  auto answer =
      fast_float::from_chars(input.data(), input.data() + input.size(), result);
  if (answer.ec != std::errc()) {
    return -1.0;
  }
  return result;
}

// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest() { return parse("3.1415 input"); }
#endif

bool small() {
  double result = -1;
  std::string str = "3e-1000";
  auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
  if (r.ec != std::errc::result_out_of_range) {
    return false;
  }
  if (r.ptr != str.data() + 7) {
    return false;
  }
  if (result != 0) {
    return false;
  }
  printf("small values go to zero\n");
  return true;
}

bool large() {
  double result = -1;
  std::string str = "3e1000";
  auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
  if (r.ec != std::errc::result_out_of_range) {
    return false;
  }
  if (r.ptr != str.data() + 6) {
    return false;
  }
  if (result != std::numeric_limits<double>::infinity()) {
    return false;
  }
  printf("large values go to infinity\n");
  return true;
}

int main() {
  std::string input = "3.1416 xyz ";
  double result;
  auto answer =
      fast_float::from_chars(input.data(), input.data() + input.size(), result);
  if ((answer.ec != std::errc()) || ((result != 3.1416))) {
    std::cerr << "parsing failure\n";
    return EXIT_FAILURE;
  }
  std::cout << "parsed the number " << result << std::endl;
#ifdef __STDCPP_FLOAT16_T__
  printf("16-bit float\n");
  // Parse as 16-bit float
  std::float16_t parsed_16{};
  input = "10000e-1452";
  auto fast_float_r16 = fast_float::from_chars(
      input.data(), input.data() + input.size(), parsed_16);
  if (fast_float_r16.ec != std::errc() &&
      fast_float_r16.ec != std::errc::result_out_of_range) {
    std::cerr << "16-bit fast_float parsing failure for: " + input + "\n";
    return false;
  }
  std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl;
#endif
  if (!small()) {
    printf("Bug\n");
    return EXIT_FAILURE;
  }
  if (!large()) {
    printf("Bug\n");
    return EXIT_FAILURE;
  }

  if (!many()) {
    printf("Bug\n");
    return EXIT_FAILURE;
  }
  many_loop();
#if FASTFLOAT_IS_CONSTEXPR
  if constexpr (constexptest() != 3.1415) {
    return EXIT_FAILURE;
  }
#endif
  return EXIT_SUCCESS;
}
