#include "fast_float/fast_float.h"

#include <cctype>
#include <cstdio>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>

#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) ||     \
    defined(sun) || defined(__sun)
// Anything at all that is related to cygwin, msys and so forth will
// always use this fallback because we cannot rely on it behaving as normal
// gcc.
#include <locale>
#include <sstream>

// workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) {
  double d;
  std::stringstream ss;
  ss.imbue(std::locale::classic());
  ss << start;
  ss >> d;
  if (ss.fail()) {
    *end = nullptr;
  }
  if (ss.eof()) {
    ss.clear();
  }
  auto nread = ss.tellg();
  *end = const_cast<char *>(start) + nread;
  return d;
}

float cygwin_strtof_l(char const *start, char **end) {
  float d;
  std::stringstream ss;
  ss.imbue(std::locale::classic());
  ss << start;
  ss >> d;
  if (ss.fail()) {
    *end = nullptr;
  }
  if (ss.eof()) {
    ss.clear();
  }
  auto nread = ss.tellg();
  *end = const_cast<char *>(start) + nread;
  return d;
}
#endif

inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) ||     \
    defined(sun) || defined(__sun)
  if (!Assertion) {
    std::cerr << "Omitting hard failure on msys/cygwin/sun systems.";
  }
#else
  if (!Assertion) {
    throw std::runtime_error("bug");
  }
#endif
}

template <typename T> std::string to_string(T d) {
  std::string s(64, '\0');
  auto written = std::snprintf(&s[0], s.size(), "%.*e",
                               std::numeric_limits<T>::max_digits10 - 1, d);
  s.resize(size_t(written));
  return s;
}

template <typename T> bool test() {
  std::string input =
      "0.1 1e1000 100000 3.14159265359  -1e-500 001    1e01  1e0000001  -inf";
  std::vector<T> answers = {T(0.1),
                            std::numeric_limits<T>::infinity(),
                            100000,
                            T(3.14159265359),
                            -0.0,
                            1,
                            10,
                            10,
                            -std::numeric_limits<T>::infinity()};
  std::vector<std::errc> expected_ec = {std::errc(),
                                        std::errc::result_out_of_range,
                                        std::errc(),
                                        std::errc(),
                                        std::errc::result_out_of_range,
                                        std::errc(),
                                        std::errc(),
                                        std::errc(),
                                        std::errc()};
  char const *begin = input.data();
  char const *end = input.data() + input.size();
  for (size_t i = 0; i < answers.size(); i++) {
    T result_value;
    while ((begin < end) && (std::isspace(*begin))) {
      begin++;
    }
    auto result = fast_float::from_chars(begin, end, result_value);
    if (result.ec != expected_ec[i]) {
      printf("parsing %.*s\n", int(end - begin), begin);
      std::cerr << " I could not parse " << std::endl;
      return false;
    }
    if (result_value != answers[i]) {
      printf("parsing %.*s\n", int(end - begin), begin);
      std::cerr << " Mismatch " << std::endl;
      return false;
    }
    begin = result.ptr;
  }
  if (begin != end) {
    std::cerr << " bad ending " << std::endl;
    return false;
  }
  return true;
}

template <typename T> void strtod_from_string(std::string const &st, T &d);

template <> void strtod_from_string(std::string const &st, double &d) {
  char *pr = (char *)st.c_str();
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) ||     \
    defined(sun) || defined(__sun)
  d = cygwin_strtod_l(pr, &pr);
#elif defined(_WIN32)
  static _locale_t c_locale = _create_locale(LC_ALL, "C");
  d = _strtod_l(st.c_str(), &pr, c_locale);
#else
  static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL);
  d = strtod_l(st.c_str(), &pr, c_locale);
#endif
  if (pr == st.c_str()) {
    throw std::runtime_error("bug in strtod_from_string");
  }
}

template <> void strtod_from_string(std::string const &st, float &d) {
  char *pr = (char *)st.c_str();
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) ||     \
    defined(sun) || defined(__sun)
  d = cygwin_strtof_l(st.c_str(), &pr);
#elif defined(_WIN32)
  static _locale_t c_locale = _create_locale(LC_ALL, "C");
  d = _strtof_l(st.c_str(), &pr, c_locale);
#else
  static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL);
  d = strtof_l(st.c_str(), &pr, c_locale);
#endif
  if (pr == st.c_str()) {
    throw std::runtime_error("bug in strtod_from_string");
  }
}

template <typename T> bool partow_test() {
  // credit:
  // https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568
  // MIT license
  std::string const strint_list[] = {
      "9007199254740993",
      "9007199254740994",
      "9007199254740995",
      "0",
      "1",
      "2",
      "3",
      "4",
      "5",
      "6",
      "7",
      "8",
      "9",
      "917049",
      "4931205",
      "6768064",
      "6884243",
      "5647132",
      "7371203",
      "-8629878",
      "4941840",
      "4543268",
      "1075600",
      "290",
      "823",
      "111",
      "715",
      "-866",
      "367",
      "666",
      "-706",
      "850",
      "-161",
      "9922547",
      "6960207",
      "1883152",
      "2300759",
      "-279294",
      "4187292",
      "3699841",
      "8386395",
      "-1441129",
      "-887892",
      "-635422",
      "9742573",
      "2326186",
      "-5903851",
      "5648486",
      "3057647",
      "2980079",
      "2957468",
      "7929158",
      "1925615",
      "879",
      "130",
      "292",
      "705",
      "817",
      "446",
      "576",
      "750",
      "523",
      "-527",
      "4365041",
      "5624958",
      "8990205",
      "2652177",
      "3993588",
      "-298316",
      "2901599",
      "3887387",
      "-5202979",
      "1196268",
      "5968501",
      "7619928",
      "3565643",
      "1885272",
      "-749485",
      "2961381",
      "2982579",
      "2387454",
      "4250081",
      "5958205",
      "00000",
      "00001",
      "00002",
      "00003",
      "00004",
      "00005",
      "00006",
      "00007",
      "00008",
      "00009",
      "4907034",
      "2592882",
      "3269234",
      "549815",
      "6256292",
      "9721039",
      "-595225",
      "5587491",
      "4596297",
      "-3885009",
      "673",
      "-899",
      "174",
      "354",
      "870",
      "147",
      "898",
      "-510",
      "369",
      "859",
      "6518423",
      "5149762",
      "8834164",
      "-8085586",
      "3233120",
      "8166948",
      "4172345",
      "6735549",
      "-934295",
      "9481935",
      "-430406",
      "6932717",
      "4087292",
      "4047263",
      "3236400",
      "-3863050",
      "4312079",
      "6956261",
      "5689446",
      "3871332",
      "535",
      "691",
      "326",
      "-409",
      "704",
      "-568",
      "301",
      "951",
      "121",
      "384",
      "4969414",
      "9378599",
      "7971781",
      "5380630",
      "5001363",
      "1715827",
      "6044615",
      "9118925",
      "9956168",
      "-8865496",
      "5962464",
      "7408980",
      "6646513",
      "-634564",
      "4188330",
      "9805948",
      "5625691",
      "7641113",
      "-4212929",
      "7802447",
      "0",
      "1",
      "2",
      "3",
      "4",
      "5",
      "6",
      "7",
      "8",
      "9",
      "2174248",
      "7449361",
      "9896659",
      "-25961",
      "1706598",
      "2412368",
      "-4617035",
      "6314554",
      "2225957",
      "7521434",
      "-9530566",
      "3914164",
      "2394759",
      "7157744",
      "9919392",
      "6406949",
      "-744004",
      "9899789",
      "8380325",
      "-1416284",
      "3402833",
      "2150043",
      "5191009",
      "8979538",
      "9565778",
      "3750211",
      "7304823",
      "2829359",
      "6544236",
      "-615740",
      "363",
      "-627",
      "129",
      "656",
      "135",
      "113",
      "381",
      "646",
      "198",
      "38",
      "8060564",
      "-176752",
      "1184717",
      "-666343",
      "-1273292",
      "-485827",
      "6241066",
      "6579411",
      "8093119",
      "7481306",
      "-4924485",
      "7467889",
      "9813178",
      "7927100",
      "3614859",
      "7293354",
      "9232973",
      "4323115",
      "1133911",
      "9511638",
      "4443188",
      "2289448",
      "5639726",
      "9073898",
      "8540394",
      "5389992",
      "1397726",
      "-589230",
      "1017086",
      "1852330",
      "-840",
      "267",
      "201",
      "533",
      "-675",
      "494",
      "315",
      "706",
      "-920",
      "784",
      "9097353",
      "6002251",
      "-308780",
      "-3830169",
      "4340467",
      "2235284",
      "3314444",
      "1085967",
      "4152107",
      "5431117",
      "-0000",
      "-0001",
      "-0002",
      "-0003",
      "-0004",
      "-0005",
      "-0006",
      "-0007",
      "-0008",
      "-0009",
      "-444999",
      "2136400",
      "6925907",
      "6990614",
      "3588271",
      "8422028",
      "-4034772",
      "5804039",
      "-6740545",
      "9381873",
      "-924923",
      "1652367",
      "2302616",
      "6776663",
      "2567821",
      "-248935",
      "2587688",
      "7076742",
      "-6461467",
      "1562896",
      "-768116",
      "2338768",
      "9887307",
      "9992184",
      "2045182",
      "2797589",
      "9784597",
      "9696554",
      "5113329",
      "1067216",
      "-76247763",
      "58169007",
      "29408062",
      "85342511",
      "42092201",
      "-95817703",
      "-1912517",
      "-26275135",
      "54656606",
      "-58188878",
      "473",
      "74",
      "374",
      "-64",
      "266",
      "715",
      "937",
      "-249",
      "249",
      "780",
      "3907360",
      "-23063423",
      "59062754",
      "83711047",
      "-95221044",
      "34894840",
      "-38562139",
      "-82018330",
      "14226223",
      "-10799717",
      "8529722",
      "88961903",
      "25608618",
      "-39988247",
      "33228241",
      "38598533",
      "21161480",
      "-33723784",
      "8873948",
      "96505557",
      "-47385048",
      "-79413272",
      "-85904404",
      "87791158",
      "49194195",
      "13051222",
      "57773302",
      "31904423",
      "3142966",
      "27846156",
      "7420011",
      "-72376922",
      "-68873971",
      "23765361",
      "4040725",
      "-22359806",
      "85777219",
      "10099223",
      "-90364256",
      "-40158172",
      "-7948696",
      "-64344821",
      "34404238",
      "84037448",
      "-85084788",
      "-42078409",
      "-56550310",
      "96898389",
      "-595829",
      "-73166703",
      "-0",
      "-1",
      "-2",
      "-3",
      "-4",
      "-5",
      "-6",
      "-7",
      "-8",
      "-9",
      "2147483647",
      "31",
      "2147483610",
      "33",
      "2147483573",
      "37",
      "2147483536",
      "-82838342",
      "64441808",
      "43641062",
      "-64419642",
      "-44421934",
      "75232413",
      "-75773725",
      "-89139509",
      "12812089",
      "-97633526",
      "36090916",
      "-57706234",
      "17804655",
      "4189936",
      "-4100124",
      "38803710",
      "-39735126",
      "-62397437",
      "75801648",
      "51302332",
      "73433906",
      "13015224",
      "-12624818",
      "91360377",
      "11576319",
      "-54467535",
      "8892431",
      "36319780",
      "38832042",
      "50172572",
      "-317",
      "109",
      "-888",
      "302",
      "-463",
      "716",
      "916",
      "665",
      "826",
      "513",
      "42423473",
      "41078812",
      "40445652",
      "-76722281",
      "95092224",
      "12075234",
      "-4045888",
      "-74396490",
      "-57304222",
      "-21726885",
      "92038121",
      "-31899682",
      "21589254",
      "-30260046",
      "56000244",
      "69686659",
      "93327838",
      "96882881",
      "-91419389",
      "77529147",
      "43288506",
      "1192435",
      "-74095920",
      "76756590",
      "-31184683",
      "-35716724",
      "9451980",
      "-63168350",
      "62864002",
      "26283194",
      "37188395",
      "29151634",
      "99343471",
      "-69450330",
      "-55680090",
      "-64957599",
      "47577948",
      "47107924",
      "2490477",
      "48633003",
      "-82740809",
      "-24122215",
      "67301713",
      "-63649610",
      "75499016",
      "82746620",
      "17052193",
      "4602244",
      "-32721165",
      "20837836",
      "674",
      "467",
      "706",
      "889",
      "172",
      "282",
      "-795",
      "188",
      "87",
      "153",
      "64501793",
      "53146328",
      "5152287",
      "-9674493",
      "68105580",
      "57245637",
      "39740229",
      "-74071854",
      "86777268",
      "86484437",
      "-86962508",
      "12644427",
      "-62944073",
      "59539680",
      "43340539",
      "30661534",
      "20143968",
      "-68183731",
      "-48250926",
      "42669063",
      "000",
      "001",
      "002",
      "003",
      "004",
      "005",
      "006",
      "007",
      "008",
      "009",
      "2147483499",
      "71",
      "2147483462",
      "73",
      "2147483425",
      "77",
      "2147483388",
      "87736852",
      "-4444906",
      "-48094147",
      "54774735",
      "54571890",
      "-22473078",
      "95053418",
      "393654",
      "-33229960",
      "32276798",
      "-48361110",
      "44295939",
      "-79813406",
      "11630865",
      "38544571",
      "70972830",
      "-9821748",
      "-60965384",
      "-13096675",
      "-24569041",
      "708",
      "-467",
      "-794",
      "610",
      "929",
      "766",
      "152",
      "482",
      "397",
      "-191",
      "97233152",
      "51028396",
      "-13796948",
      "95437272",
      "71352512",
      "-83233730",
      "-68517318",
      "61832742",
      "-42667174",
      "-18002395",
      "-92239407",
      "12701336",
      "-63830875",
      "41514172",
      "-5726049",
      "18668677",
      "69555144",
      "-13737009",
      "-22626233",
      "-55078143",
      "00",
      "11",
      "22",
      "33",
      "44",
      "-00",
      "-11",
      "-22",
      "-33",
      "-44",
      "000",
      "111",
      "222",
      "333",
      "444",
      "-000",
      "-111",
      "-222",
      "-333",
      "-444",
      "0000",
      "1111",
      "2222",
      "3333",
      "4444",
      "-0000",
      "-1111",
      "-2222",
      "-3333",
      "-4444",
      "00000",
      "11111",
      "22222",
      "33333",
      "44444",
      "-00000",
      "-11111",
      "-22222",
      "-33333",
      "-44444",
      "000000",
      "111111",
      "222222",
      "333333",
      "444444",
      "-000000",
      "-111111",
      "-222222",
      "-333333",
      "-444444",
      "0000000",
      "1111111",
      "2222222",
      "3333333",
      "4444444",
      "-0000000",
      "-1111111",
      "-2222222",
      "-3333333",
      "-4444444",
      "00000000",
      "11111111",
      "22222222",
      "33333333",
      "44444444",
      "-00000000",
      "-11111111",
      "-22222222",
      "-33333333",
      "-44444444",
      "000000000",
      "111111111",
      "222222222",
      "333333333",
      "444444444",
      "-000000000",
      "-111111111",
      "-222222222",
      "-333333333",
      "-444444444",
      "2147483351",
      "51",
      "2147483314",
      "53",
      "-2147483648",
      "57",
      "-2147483611",
      "55",
      "66",
      "77",
      "88",
      "99",
      "-55",
      "-66",
      "-77",
      "-88",
      "-99",
      "555",
      "666",
      "777",
      "888",
      "999",
      "-555",
      "-666",
      "-777",
      "-888",
      "-999",
      "5555",
      "6666",
      "7777",
      "8888",
      "9999",
      "-5555",
      "-6666",
      "-7777",
      "-8888",
      "-9999",
      "55555",
      "66666",
      "77777",
      "88888",
      "99999",
      "-55555",
      "-66666",
      "-77777",
      "-88888",
      "-99999",
      "555555",
      "666666",
      "777777",
      "888888",
      "999999",
      "-555555",
      "-666666",
      "-777777",
      "-888888",
      "-999999",
      "5555555",
      "6666666",
      "7777777",
      "8888888",
      "9999999",
      "-5555555",
      "-6666666",
      "-7777777",
      "-8888888",
      "-9999999",
      "55555555",
      "66666666",
      "77777777",
      "88888888",
      "99999999",
      "-55555555",
      "-66666666",
      "-77777777",
      "-88888888",
      "-99999999",
      "555555555",
      "666666666",
      "777777777",
      "888888888",
      "999999999",
      "-555555555",
      "-666666666",
      "-777777777",
      "-888888888",
      "-999999999",
      "-2147483574",
      "91",
      "-2147483537",
      "93",
      "-2147483500",
      "97",
      "-2147483463",
      "0000000011",
      "0000000022",
      "0000000033",
      "0000000044",
      "-000000011",
      "-000000022",
      "-000000033",
      "-000000044",
      "-000000088",
      "0000000111",
      "0000000222",
      "0000000333",
      "0000000444",
      "-000000111",
      "-000000222",
      "-000000333",
      "-000000444",
      "-000000888",
      "0000001111",
      "0000002222",
      "0000003333",
      "0000004444",
      "-000001111",
      "-000002222",
      "-000003333",
      "-000004444",
      "-000008888",
      "0000011111",
      "0000022222",
      "0000033333",
      "0000044444",
      "-000011111",
      "-000022222",
      "-000033333",
      "-000044444",
      "-000088888",
      "0000111111",
      "0000222222",
      "0000333333",
      "0000444444",
      "-000111111",
      "-000222222",
      "-000333333",
      "-000444444",
      "-000888888",
      "0001111111",
      "0002222222",
      "0003333333",
      "0004444444",
      "-001111111",
      "-002222222",
      "-003333333",
      "-004444444",
      "-008888888",
      "0011111111",
      "0022222222",
      "0033333333",
      "0044444444",
      "-011111111",
      "-022222222",
      "-033333333",
      "-044444444",
      "-088888888",
      "0111111111",
      "0222222222",
      "0333333333",
      "0444444444",
      "-111111111",
      "-222222222",
      "-333333333",
      "-444444444",
      "-888888888",
      "0000000055",
      "0000000066",
      "0000000077",
      "0000000088",
      "0000000099",
      "-000000055",
      "-000000066",
      "-000000077",
      "-000000099",
      "0000000555",
      "0000000666",
      "0000000777",
      "0000000888",
      "0000000999",
      "-000000555",
      "-000000666",
      "-000000777",
      "-000000999",
      "0000005555",
      "0000006666",
      "0000007777",
      "0000008888",
      "0000009999",
      "-000005555",
      "-000006666",
      "-000007777",
      "-000009999",
      "0000055555",
      "0000066666",
      "0000077777",
      "0000088888",
      "0000099999",
      "-000055555",
      "-000066666",
      "-000077777",
      "-000099999",
      "0000555555",
      "0000666666",
      "0000777777",
      "0000888888",
      "0000999999",
      "-000555555",
      "-000666666",
      "-000777777",
      "-000999999",
      "0005555555",
      "0006666666",
      "0007777777",
      "0008888888",
      "0009999999",
      "-005555555",
      "-006666666",
      "-007777777",
      "-009999999",
      "0055555555",
      "0066666666",
      "0077777777",
      "0088888888",
      "0099999999",
      "-055555555",
      "-066666666",
      "-077777777",
      "-099999999",
      "0555555555",
      "0666666666",
      "0777777777",
      "0888888888",
      "0999999999",
      "-555555555",
      "-666666666",
      "-777777777",
      "-999999999",
      "-2147483426",
      "101",
      "-2147483389",
      "103",
      "-2147483352",
      "105",
      "-2147483315",
      "0000001234567890",
      "0000001234567890",
      "-0000001234567890",
      "000001234567890",
      "000001234567890",
      "-000001234567890",
      "00001234567890",
      "00001234567890",
      "-00001234567890",
      "0001234567890",
      "0001234567890",
      "-0001234567890",
      "001234567890",
      "001234567890",
      "-001234567890",
      "01234567890",
      "01234567890",
      "-01234567890",
      "1234567890",
      "1234567890",
      "-1234567890",
  };
  for (std::string const &st : strint_list) {
    T expected_value;
    strtod_from_string(st, expected_value);
    T result_value;
    auto result =
        fast_float::from_chars(st.data(), st.data() + st.size(), result_value);
    if (result.ec != std::errc() &&
        result.ec != std::errc::result_out_of_range) {
      printf("parsing %.*s\n", int(st.size()), st.data());
      std::cerr << " I could not parse " << std::endl;
      return false;
    }
    if (result.ptr != st.data() + st.size()) {
      printf("parsing %.*s\n", int(st.size()), st.data());
      std::cerr << " Did not get to the end " << std::endl;
      return false;
    }
    if (result_value != expected_value) {
      printf("parsing %.*s\n", int(st.size()), st.data());
      std::cerr << "expected value : " << to_string(expected_value)
                << std::endl;
      std::cerr << "result   value : " << to_string(result_value) << std::endl;
      std::cerr << " Mismatch " << std::endl;
      return false;
    }
  }
  return true;
}

int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) ||     \
    defined(sun) || defined(__sun)
  std::cout << "Warning: msys/cygwin or solaris detected." << std::endl;
#endif
  std::cout << "32 bits checks" << std::endl;
  Assert(partow_test<float>());
  Assert(test<float>());

  std::cout << "64 bits checks" << std::endl;
  Assert(partow_test<double>());
  Assert(test<double>());

  std::cout << "All ok" << std::endl;
  return EXIT_SUCCESS;
}
